@rxxuzi/gumi
Version:
Clean & minimal design system with delightful interactions
376 lines • 12.9 kB
JavaScript
// gumi.ts
// Gumi.js v1.0.0 - Main Entry Point
// Core utilities
import * as dom from './core/dom';
import * as animation from './core/animation';
// Components
import { ThemeManager } from './components/theme';
import { Modal } from './components/modal';
import { Toast } from './components/toast';
import { Tabs } from './components/tabs';
import { Accordion } from './components/accordion';
import { Dropdown } from './components/dropdown';
import { Progress } from './components/progress';
import { FormValidator } from './components/form';
import { Sidebar } from './components/sidebar';
import { CodeCopy } from './components/code-copy';
// Utils
import * as helpers from './utils/helpers';
import { icons } from './utils/icons';
// Main Gumi object
class Gumi {
constructor() {
this.version = '1.0.0';
this.modals = new Map();
this.dropdowns = new Map();
this.tabs = new Map();
this.accordions = new Map();
this.sidebars = new Map();
// DOM utilities
this.$ = dom.$;
this.$$ = dom.$$;
this.ready = dom.ready;
this.on = dom.on;
this.off = dom.off;
this.trigger = dom.trigger;
this.createElement = dom.createElement;
// Animation methods
this.animate = animation.animate;
this.fadeIn = animation.fadeIn;
this.fadeOut = animation.fadeOut;
this.slideUp = animation.slideUp;
this.slideDown = animation.slideDown;
this.scaleIn = animation.scaleIn;
this.scaleOut = animation.scaleOut;
this.slideIn = animation.slideIn;
this.bounce = animation.bounce;
this.shake = animation.shake;
this.pulse = animation.pulse;
// Utility methods
this.show = dom.show;
this.hide = dom.hide;
this.toggle = dom.toggle;
// Helpers
this.debounce = helpers.debounce;
this.throttle = helpers.throttle;
this.copyToClipboard = helpers.copyToClipboard;
// Icons
this.icons = icons;
this.theme = new ThemeManager();
this.init();
}
/**
* Initialize Gumi.js
*/
init() {
dom.ready(() => {
// Setup smooth scroll
this.setupSmoothScroll();
// Setup focus rings
this.setupFocusRings();
// Setup preloaded animations
this.setupPreloadedAnimations();
// Initialize components
this.initializeComponents();
console.log(`🍬 Gumi.js v${this.version} initialized`);
});
}
/**
* Setup smooth scrolling for anchor links
*/
setupSmoothScroll() {
dom.on(document, 'click', 'a[href^="#"]', (e) => {
e.preventDefault();
const target = e.target;
const link = target.closest('a[href^="#"]');
if (!link)
return;
const href = link.getAttribute('href');
if (!href)
return;
const targetEl = dom.$(href);
if (targetEl) {
targetEl.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
}
/**
* Setup enhanced focus rings
*/
setupFocusRings() {
let isKeyboard = false;
dom.on(document, 'keydown', () => {
isKeyboard = true;
});
dom.on(document, 'mousedown', () => {
isKeyboard = false;
});
dom.on(document, 'focusin', (e) => {
if (isKeyboard) {
dom.addClass(e.target, 'gumi-focus-visible');
}
});
dom.on(document, 'focusout', (e) => {
dom.removeClass(e.target, 'gumi-focus-visible');
});
}
/**
* Setup animations triggered by CSS classes
*/
setupPreloadedAnimations() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const el = entry.target;
if (el.classList.contains('gumi-fade-in')) {
animation.fadeIn(el);
}
if (el.classList.contains('gumi-slide-up')) {
el.style.transform = 'translateY(30px)';
el.style.opacity = '0';
animation.animate(el, [
{ transform: 'translateY(30px)', opacity: 0 },
{ transform: 'translateY(0)', opacity: 1 }
], { duration: 600 });
}
if (el.classList.contains('gumi-slide-down')) {
animation.slideDown(el);
}
if (el.classList.contains('gumi-scale-in')) {
animation.scaleIn(el);
}
observer.unobserve(el);
}
});
}, { threshold: 0.1 });
const animatedElements = dom.$$('.gumi-fade-in, .gumi-slide-up, .gumi-slide-down, .gumi-scale-in');
animatedElements.forEach(el => observer.observe(el));
}
/**
* Initialize all components
*/
initializeComponents() {
// Initialize modals
Modal.initFromTriggers('[data-modal]').forEach(modal => {
const id = modal.element.id;
if (id)
this.modals.set(id, modal);
});
// Initialize dropdowns
Dropdown.initFromAttributes('[data-dropdown]').forEach(dropdown => {
const id = helpers.generateId('dropdown');
this.dropdowns.set(id, dropdown);
});
// Initialize tabs
Tabs.initAll('.tabs').forEach(tabs => {
const id = helpers.generateId('tabs');
this.tabs.set(id, tabs);
});
// Initialize accordions
Accordion.initAll('.accordion').forEach(accordion => {
const id = helpers.generateId('accordion');
this.accordions.set(id, accordion);
});
// Initialize sidebars
Sidebar.initFromAttributes('[data-sidebar]').forEach(sidebar => {
const id = helpers.generateId('sidebar');
this.sidebars.set(id, sidebar);
});
// Initialize code copy buttons
CodeCopy.initAll('pre, .code-block');
// Setup switch listeners
this.setupSwitches();
}
/**
* Setup switch/toggle functionality
*/
setupSwitches() {
const switches = dom.$$('.switch input');
switches.forEach(switchInput => {
dom.on(switchInput, 'change', () => {
const event = new CustomEvent('gumi-switch-change', {
detail: { checked: switchInput.checked }
});
switchInput.dispatchEvent(event);
});
});
}
// Theme methods
setTheme(theme) {
this.theme.setTheme(theme);
}
toggleTheme() {
this.theme.toggleTheme();
}
getTheme() {
return this.theme.getTheme();
}
// Modal methods
modal(selector, options) {
const el = dom.$(selector);
if (!el)
return;
const modal = new Modal(el, options);
this.modals.set(el.id || helpers.generateId('modal'), modal);
const triggers = dom.$$(`[data-modal="${selector}"]`);
triggers.forEach(trigger => {
dom.on(trigger, 'click', (e) => {
e.preventDefault();
modal.open();
});
});
}
openModal(selector) {
const el = dom.$(selector);
if (!el)
return;
let modal = Array.from(this.modals.values()).find(m => m.element === el);
if (!modal) {
modal = new Modal(el);
this.modals.set(el.id || helpers.generateId('modal'), modal);
}
modal.open();
}
closeModal(selector) {
const el = dom.$(selector);
if (!el)
return;
const modal = Array.from(this.modals.values()).find(m => m.element === el);
if (modal) {
modal.close();
}
}
// Toast methods
toast(message, options) {
return Toast.show(message, options);
}
// Progress methods
setProgress(selector, value) {
Progress.setProgress(selector, value);
// Update data attribute for color progression
const element = dom.$(selector);
if (element) {
element.setAttribute('data-progress', Math.round(value).toString());
}
}
// Form validation
validateForm(selector) {
return FormValidator.validateForm(selector);
}
// Dropdown methods
dropdown(trigger, menu, options) {
const triggerEl = dom.$(trigger);
if (!triggerEl)
return;
const menuSelector = menu || triggerEl.getAttribute('data-dropdown');
if (!menuSelector)
return;
const menuEl = dom.$(menuSelector);
if (!menuEl)
return;
const dropdown = new Dropdown(triggerEl, menuEl, options);
this.dropdowns.set(helpers.generateId('dropdown'), dropdown);
}
// Lazy loading
lazyLoad(selector = 'img[data-src]') {
const images = dom.$$(selector);
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
const src = img.getAttribute('data-src');
if (src) {
img.src = src;
img.removeAttribute('data-src');
dom.addClass(img, 'gumi-loaded');
}
observer.unobserve(img);
}
});
});
images.forEach(img => observer.observe(img));
}
// Parallax effect
parallax(selector, options = {}) {
const elements = dom.$$(selector);
const speed = options.speed || 0.5;
const updateParallax = () => {
const scrollY = window.pageYOffset;
elements.forEach(el => {
const rect = el.getBoundingClientRect();
const elementTop = rect.top + scrollY;
const windowHeight = window.innerHeight;
if (rect.bottom >= 0 && rect.top <= windowHeight) {
const yPos = (scrollY - elementTop) * speed;
el.style.transform = `translateY(${yPos}px)`;
}
});
};
dom.on(window, 'scroll', helpers.debounce(updateParallax, 10));
updateParallax();
}
// Ripple effect
ripple(selector) {
const buttons = dom.$$(selector);
buttons.forEach(button => {
dom.on(button, 'click', (e) => {
animation.ripple(e, button);
});
});
}
// Loading state
loading(element, isLoading = true) {
const el = dom.$(element);
if (!el)
return;
if (isLoading) {
el.disabled = true;
el.setAttribute('data-loading', 'true');
const originalContent = el.innerHTML;
el.setAttribute('data-original-content', originalContent);
el.innerHTML = `<span class="spinner"></span> Loading...`;
}
else {
el.disabled = false;
el.removeAttribute('data-loading');
const originalContent = el.getAttribute('data-original-content');
if (originalContent) {
el.innerHTML = originalContent;
el.removeAttribute('data-original-content');
}
}
}
// Sidebar methods
sidebar(selector) {
return Sidebar.init(selector);
}
openSidebar(selector) {
const sidebar = Array.from(this.sidebars.values()).find(s => s.element === dom.$(selector));
if (sidebar)
sidebar.open();
}
closeSidebar(selector) {
const sidebar = Array.from(this.sidebars.values()).find(s => s.element === dom.$(selector));
if (sidebar)
sidebar.close();
}
toggleSidebar(selector) {
const sidebar = Array.from(this.sidebars.values()).find(s => s.element === dom.$(selector));
if (sidebar)
sidebar.toggle();
}
}
// Create global instance
const gumi = new Gumi();
// Export for module usage
export default gumi;
// Export components for advanced usage
export { ThemeManager, Modal, Toast, Tabs, Accordion, Dropdown, Progress, FormValidator, Sidebar, CodeCopy };
// Make available globally for browser usage
if (typeof window !== 'undefined') {
window.gumi = gumi;
}
//# sourceMappingURL=gumi.js.map