(function () {
'use strict';
/* ----- Helpers ----- */
function $(sel, ctx) { return (ctx || document).querySelector(sel); }
function $$(sel, ctx) { return Array.from((ctx || document).querySelectorAll(sel)); }
function on(el, evt, sel, fn) {
if (typeof sel === 'function') { fn = sel; sel = null; }
el.addEventListener(evt, function (e) {
if (!sel) return fn.call(el, e);
var t = e.target.closest(sel);
if (t && el.contains(t)) fn.call(t, e);
});
}
function toggle(el, attr, forceOpen) {
var open = forceOpen !== undefined ? forceOpen : el.getAttribute(attr) !== 'open';
el.setAttribute(attr, open ? 'open' : 'closed');
return open;
}
var Ranju = window.Ranju = {};
Ranju.theme = {
current: function () {
return document.documentElement.getAttribute('data-theme') || 'light';
},
set: function (t) {
document.documentElement.setAttribute('data-theme', t);
try { localStorage.setItem('ranju-theme', t); } catch (e) {}
},
toggle: function () {
this.set(this.current() === 'dark' ? 'light' : 'dark');
},
init: function () {
try {
var saved = localStorage.getItem('ranju-theme');
if (saved) { this.set(saved); return; }
} catch (e) {}
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
this.set('dark');
}
}
};
function initAccordion() {
on(document, 'click', '.accordion-trigger', function () {
var item = this.closest('.accordion-item');
if (!item) return;
var accordion = item.closest('.accordion');
var isOpen = item.getAttribute('data-state') === 'open';
if (accordion && !accordion.hasAttribute('data-multiple')) {
$$('.accordion-item', accordion).forEach(function (i) {
i.setAttribute('data-state', 'closed');
});
}
item.setAttribute('data-state', isOpen ? 'closed' : 'open');
});
}
function initAlert() {
on(document, 'click', '.alert-close', function () {
var alert = this.closest('.alert');
if (alert) {
alert.style.opacity = '0';
alert.style.transform = 'translateY(-4px)';
alert.style.transition = 'opacity .2s, transform .2s';
setTimeout(function () { alert.remove(); }, 200);
}
});
}
function initCollapsible() {
on(document, 'click', '.collapsible-trigger', function () {
var col = this.closest('.collapsible');
if (col) toggle(col, 'data-state');
});
}
function openDialog(id) {
var overlay = document.getElementById(id);
if (!overlay) return;
overlay.setAttribute('data-state', 'open');
document.body.style.overflow = 'hidden';
var sheet = overlay.nextElementSibling;
if (sheet && (sheet.classList.contains('sheet') || sheet.classList.contains('drawer'))) {
sheet.setAttribute('data-state', 'open');
}
var first = $('[autofocus], input, button:not(.dialog-close)', overlay);
if (first) setTimeout(function () { first.focus(); }, 100);
}
function closeDialog(id) {
var overlay = document.getElementById(id);
if (!overlay) return;
overlay.setAttribute('data-state', 'closed');
document.body.style.overflow = '';
var sheet = overlay.nextElementSibling;
if (sheet && (sheet.classList.contains('sheet') || sheet.classList.contains('drawer'))) {
sheet.setAttribute('data-state', 'closed');
}
}
Ranju.dialog = { open: openDialog, close: closeDialog };
function initDialog() {
on(document, 'click', '[data-dialog-open]', function (e) {
e.preventDefault();
openDialog(this.getAttribute('data-dialog-open'));
});
on(document, 'click', '[data-dialog-close]', function (e) {
e.preventDefault();
var overlay = this.closest('.dialog-overlay');
if (overlay) closeDialog(overlay.id);
});
on(document, 'click', '.dialog-overlay', function (e) {
if (e.target === this) closeDialog(this.id);
});
on(document, 'keydown', function (e) {
if (e.key === 'Escape') {
var open = $$('.dialog-overlay[data-state="open"]');
if (open.length) closeDialog(open[open.length - 1].id);
var cmd = $('.command-overlay[data-state="open"]');
if (cmd) cmd.setAttribute('data-state', 'closed');
}
});
}
function initDropdown() {
on(document, 'click', '[data-dropdown-trigger]', function (e) {
e.stopPropagation();
var dd = this.closest('.dropdown');
if (!dd) return;
var isOpen = dd.getAttribute('data-state') === 'open';
closeAllDropdowns();
if (!isOpen) dd.setAttribute('data-state', 'open');
});
on(document, 'click', function () { closeAllDropdowns(); });
on(document, 'click', '.dropdown-item', function () { closeAllDropdowns(); });
}
function closeAllDropdowns() {
$$('.dropdown[data-state="open"]').forEach(function (d) {
d.setAttribute('data-state', 'closed');
});
}
function initContextMenu() {
on(document, 'contextmenu', '[data-context-menu]', function (e) {
e.preventDefault();
$$('.context-menu[data-state="open"]').forEach(function (m) {
m.setAttribute('data-state', 'closed');
});
var id = this.getAttribute('data-context-menu');
var menu = document.getElementById(id);
if (!menu) return;
menu.style.left = e.clientX + 'px';
menu.style.top = e.clientY + 'px';
menu.setAttribute('data-state', 'open');
});
on(document, 'click', function () {
$$('.context-menu[data-state="open"]').forEach(function (m) {
m.setAttribute('data-state', 'closed');
});
});
}
function initTabs() {
on(document, 'click', '.tabs-trigger', function () {
var tabsRoot = this.closest('.tabs');
if (!tabsRoot) return;
var target = this.getAttribute('data-tab');
$$('.tabs-trigger', tabsRoot).forEach(function (t) {
t.classList.remove('active');
t.setAttribute('aria-selected', 'false');
});
this.classList.add('active');
this.setAttribute('aria-selected', 'true');
$$('.tab-panel', tabsRoot).forEach(function (p) {
p.classList.remove('active');
});
var panel = tabsRoot.querySelector('[data-tab-panel="' + target + '"]');
if (panel) panel.classList.add('active');
});
}
var toastDefaults = { duration: 4000, position: 'bottom-right' };
function getContainer(pos) {
var existing = $('.toast-container.' + pos);
if (existing) return existing;
var c = document.createElement('div');
c.className = 'toast-container ' + pos;
document.body.appendChild(c);
return c;
}
Ranju.toast = function (opts) {
if (typeof opts === 'string') opts = { title: opts };
opts = Object.assign({}, toastDefaults, opts);
var container = getContainer(opts.position);
var toast = document.createElement('div');
toast.className = 'toast' + (opts.variant ? ' toast-' + opts.variant : '');
var iconSvg = '';
if (opts.variant === 'success') iconSvg = '';
else if (opts.variant === 'destructive') iconSvg = '';
else if (opts.variant === 'warning') iconSvg = '';
toast.innerHTML =
iconSvg +
'
' +
(opts.title ? '
' + opts.title + '
' : '') +
(opts.description ? '
' + opts.description + '
' : '') +
'
' +
'';
container.appendChild(toast);
var closeBtn = toast.querySelector('.toast-close');
closeBtn.addEventListener('click', function () { removeToast(toast); });
if (opts.duration > 0) {
setTimeout(function () { removeToast(toast); }, opts.duration);
}
return toast;
};
function removeToast(t) {
if (t._removing) return;
t._removing = true;
t.classList.add('toast-leaving');
setTimeout(function () { t.remove(); }, 200);
}
function initTooltip() {
$$('[data-tooltip]').forEach(function (el) {
el.addEventListener('mouseenter', function () {
var text = this.getAttribute('data-tooltip');
if (!text || this._tip) return;
var tip = document.createElement('div');
tip.className = 'tooltip-content';
tip.style.opacity = '1';
tip.style.pointerEvents = 'none';
tip.style.position = 'absolute';
tip.textContent = text;
this.style.position = 'relative';
this.appendChild(tip);
this._tip = tip;
});
el.addEventListener('mouseleave', function () {
if (this._tip) { this._tip.remove(); this._tip = null; }
});
});
}
function initPopover() {
on(document, 'click', '[data-popover-trigger]', function (e) {
e.stopPropagation();
var pop = this.closest('.popover');
if (!pop) return;
toggle(pop, 'data-state');
});
on(document, 'click', function (e) {
$$('.popover[data-state="open"]').forEach(function (p) {
if (!p.contains(e.target)) p.setAttribute('data-state', 'closed');
});
});
}
function initCombobox() {
on(document, 'focusin', '.combobox-input', function () {
var cb = this.closest('.combobox');
if (cb) cb.setAttribute('data-state', 'open');
});
on(document, 'input', '.combobox-input', function () {
var cb = this.closest('.combobox');
if (!cb) return;
var val = this.value.toLowerCase();
$$('.combobox-option', cb).forEach(function (opt) {
var text = opt.textContent.toLowerCase();
opt.style.display = text.includes(val) ? '' : 'none';
});
});
on(document, 'click', '.combobox-option', function () {
var cb = this.closest('.combobox');
if (!cb) return;
var inp = $('.combobox-input', cb);
if (inp) inp.value = this.textContent.trim();
$$('.combobox-option', cb).forEach(function (o) { o.removeAttribute('data-selected'); });
this.setAttribute('data-selected', '');
cb.setAttribute('data-state', 'closed');
});
on(document, 'click', function (e) {
$$('.combobox[data-state="open"]').forEach(function (cb) {
if (!cb.contains(e.target)) cb.setAttribute('data-state', 'closed');
});
});
}
Ranju.command = {
open: function (id) {
var el = document.getElementById(id);
if (el) {
el.setAttribute('data-state', 'open');
var inp = $('.command-input', el);
if (inp) setTimeout(function () { inp.focus(); }, 100);
}
},
close: function (id) {
var el = document.getElementById(id);
if (el) el.setAttribute('data-state', 'closed');
}
};
function initCommand() {
on(document, 'click', '.command-overlay', function (e) {
if (e.target === this) this.setAttribute('data-state', 'closed');
});
on(document, 'input', '.command-input', function () {
var overlay = this.closest('.command-overlay');
if (!overlay) return;
var val = this.value.toLowerCase();
$$('.command-item', overlay).forEach(function (item) {
var text = item.textContent.toLowerCase();
item.style.display = text.includes(val) ? '' : 'none';
});
$$('.command-group-label', overlay).forEach(function (lbl) {
var group = lbl.parentElement;
var visible = $$('.command-item', group).some(function (i) {
return i.style.display !== 'none';
});
lbl.style.display = visible ? '' : 'none';
});
});
on(document, 'keydown', function (e) {
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
var cmd = $('.command-overlay');
if (cmd) {
var isOpen = cmd.getAttribute('data-state') === 'open';
cmd.setAttribute('data-state', isOpen ? 'closed' : 'open');
if (!isOpen) {
var inp = $('.command-input', cmd);
if (inp) setTimeout(function () { inp.focus(); }, 100);
}
}
}
});
}
function initCarousel() {
$$('.carousel').forEach(function (carousel) {
var track = $('.carousel-track', carousel);
var slides = $$('.carousel-slide', carousel);
var dots = $$('.carousel-dot', carousel);
var idx = 0;
function goTo(i) {
idx = Math.max(0, Math.min(i, slides.length - 1));
track.style.transform = 'translateX(-' + (idx * 100) + '%)';
dots.forEach(function (d, j) { d.classList.toggle('active', j === idx); });
}
var prev = $('.carousel-prev', carousel);
var next = $('.carousel-next', carousel);
if (prev) prev.addEventListener('click', function () { goTo(idx - 1); });
if (next) next.addEventListener('click', function () { goTo(idx + 1); });
dots.forEach(function (dot, j) {
dot.addEventListener('click', function () { goTo(j); });
});
if (dots.length) dots[0].classList.add('active');
if (carousel.hasAttribute('data-autoplay')) {
var ms = parseInt(carousel.getAttribute('data-autoplay')) || 5000;
setInterval(function () { goTo((idx + 1) % slides.length); }, ms);
}
});
}
function initDataTable() {
on(document, 'click', 'th[data-sortable]', function () {
var table = this.closest('table');
if (!table) return;
var colIdx = Array.from(this.parentElement.children).indexOf(this);
var tbody = table.querySelector('tbody') || table;
var rows = Array.from(tbody.querySelectorAll('tr'));
var dir = this.getAttribute('data-sort') === 'asc' ? 'desc' : 'asc';
$$('th[data-sortable]', table).forEach(function (h) { h.removeAttribute('data-sort'); });
this.setAttribute('data-sort', dir);
rows.sort(function (a, b) {
var cellA = (a.children[colIdx] || {}).textContent || '';
var cellB = (b.children[colIdx] || {}).textContent || '';
var numA = parseFloat(cellA);
var numB = parseFloat(cellB);
if (!isNaN(numA) && !isNaN(numB)) {
return dir === 'asc' ? numA - numB : numB - numA;
}
return dir === 'asc' ? cellA.localeCompare(cellB) : cellB.localeCompare(cellA);
});
rows.forEach(function (r) { tbody.appendChild(r); });
});
}
Ranju.calendar = function (el, opts) {
opts = opts || {};
var today = new Date();
var month = today.getMonth();
var year = today.getFullYear();
var selected = null;
var days = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
function render() {
var first = new Date(year, month, 1);
var last = new Date(year, month + 1, 0);
var startDay = first.getDay();
var monthName = first.toLocaleString('default', { month: 'long', year: 'numeric' });
var html = '';
days.forEach(function (d) { html += ''; });
var prevLast = new Date(year, month, 0).getDate();
for (var i = startDay - 1; i >= 0; i--) {
html += '
' + (prevLast - i) + '
';
}
for (var d = 1; d <= last.getDate(); d++) {
var cls = 'calendar-day';
if (d === today.getDate() && month === today.getMonth() && year === today.getFullYear()) cls += ' today';
if (selected && d === selected.getDate() && month === selected.getMonth() && year === selected.getFullYear()) cls += ' selected';
html += '
' + d + '
';
}
var rem = 42 - (startDay + last.getDate());
for (var i = 1; i <= rem; i++) {
html += '
' + i + '
';
}
html += '
';
el.innerHTML = html;
el.querySelector('[data-cal-prev]').addEventListener('click', function () {
month--; if (month < 0) { month = 11; year--; } render();
});
el.querySelector('[data-cal-next]').addEventListener('click', function () {
month++; if (month > 11) { month = 0; year++; } render();
});
$$('.calendar-day:not(.other-month)', el).forEach(function (dayEl) {
dayEl.addEventListener('click', function () {
selected = new Date(year, month, parseInt(this.getAttribute('data-day')));
render();
if (opts.onSelect) opts.onSelect(selected);
});
});
}
render();
};
function initSidebar() {
on(document, 'click', '[data-sidebar-toggle]', function () {
var id = this.getAttribute('data-sidebar-toggle');
var sidebar = document.getElementById(id) || $('.sidebar');
if (sidebar) sidebar.classList.toggle('sidebar-collapsed');
});
}
function initResizable() {
$$('.resizable-handle').forEach(function (handle) {
var dragging = false;
var prev, startX, startY, startW, startH;
handle.addEventListener('mousedown', function (e) {
dragging = true;
prev = handle.previousElementSibling;
startX = e.clientX;
startY = e.clientY;
var rect = prev.getBoundingClientRect();
startW = rect.width;
startH = rect.height;
document.body.style.cursor = handle.closest('.resizable-vertical') ? 'row-resize' : 'col-resize';
document.body.style.userSelect = 'none';
e.preventDefault();
});
document.addEventListener('mousemove', function (e) {
if (!dragging) return;
if (handle.closest('.resizable-vertical')) {
prev.style.height = (startH + (e.clientY - startY)) + 'px';
prev.style.flex = 'none';
} else {
prev.style.width = (startW + (e.clientX - startX)) + 'px';
prev.style.flex = 'none';
}
});
document.addEventListener('mouseup', function () {
if (dragging) {
dragging = false;
document.body.style.cursor = '';
document.body.style.userSelect = '';
}
});
});
}
function initToggle() {
on(document, 'click', '.toggle', function () {
var pressed = this.getAttribute('aria-pressed') === 'true';
this.setAttribute('aria-pressed', !pressed);
this.classList.toggle('active');
var group = this.closest('.toggle-group');
if (group && !group.hasAttribute('data-multiple')) {
$$('.toggle', group).forEach(function (t) {
if (t !== this) {
t.setAttribute('aria-pressed', 'false');
t.classList.remove('active');
}
}.bind(this));
}
});
}
function init() {
Ranju.theme.init();
initAccordion();
initAlert();
initCollapsible();
initDialog();
initDropdown();
initContextMenu();
initTabs();
initTooltip();
initPopover();
initCombobox();
initCommand();
initCarousel();
initDataTable();
initSidebar();
initResizable();
initToggle();
$$('.calendar').forEach(function (el) {
if (!el.hasAttribute('data-manual')) Ranju.calendar(el);
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();