UNPKG

@jay-js/ui

Version:

A library of UI components for Jay JS with Tailwind CSS and daisyUI.

452 lines (451 loc) 15.9 kB
function _define_property(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _object_spread(target) { for(var i = 1; i < arguments.length; i++){ var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === "function") { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function(key) { _define_property(target, key, source[key]); }); } return target; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function(sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _object_spread_props(target, source) { source = source != null ? source : {}; if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function(key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } function _object_without_properties(source, excluded) { if (source == null) return {}; var target = _object_without_properties_loose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for(i = 0; i < sourceSymbolKeys.length; i++){ key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _object_without_properties_loose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for(i = 0; i < sourceKeys.length; i++){ key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } import { Base, Box, Button, Typography } from "@jay-js/elements"; import { Effect, render, State } from "@jay-js/system"; import { cn } from "../../utils/cn.js"; const LOCALES = { "pt-BR": { months: [ "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro" ], weekDays: [ "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb" ], back: "Voltar", confirm: "Confirmar" }, "en-US": { months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], weekDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], back: "Back", confirm: "Confirm" }, "es-ES": { months: [ "Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre" ], weekDays: [ "Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb" ], back: "Volver", confirm: "Confirmar" } }; export function DatePicker(_param = { tag: "div" }) { var { label, defaultDate = new Date(), value, onSelect, withTime = false, minDate, maxDate, color = "btn-primary", size = "btn-sm", disabled = false, locale = "pt-BR", showToday = true, rangeStart, rangeEnd } = _param, props = _object_without_properties(_param, [ "label", "defaultDate", "value", "onSelect", "withTime", "minDate", "maxDate", "color", "size", "disabled", "locale", "showToday", "rangeStart", "rangeEnd" ]); const strings = LOCALES[locale] || LOCALES["pt-BR"]; const currentDate = State(new Date(defaultDate)); const selectedDate = State(value ? new Date(value) : null); const currentView = State("calendar"); const selectedHour = State(value ? value.getHours() : new Date().getHours()); const selectedMinute = State(value ? value.getMinutes() : new Date().getMinutes()); const containerId = `picker-${Math.random().toString(36).slice(2, 11)}`; const monthYearId = `${containerId}-month-year`; const calendarDaysId = `${containerId}-calendar-days`; const container = Base(_object_spread_props(_object_spread({ tag: "div" }, props), { className: cn("w-full max-w-sm", props.className), onunmount: ()=>{ currentDate.clear(); selectedDate.clear(); currentView.clear(); selectedHour.clear(); selectedMinute.clear(); } })); function previousMonth() { currentDate.set((current)=>{ const newDate = new Date(current); newDate.setMonth(newDate.getMonth() - 1); return newDate; }); } function nextMonth() { currentDate.set((current)=>{ const newDate = new Date(current); newDate.setMonth(newDate.getMonth() + 1); return newDate; }); } function selectDate(day) { const newDate = new Date(currentDate.value.getFullYear(), currentDate.value.getMonth(), day); if (minDate && newDate < minDate) return; if (maxDate && newDate > maxDate) return; selectedDate.set(newDate); if (withTime) { currentView.set("time"); } else { if (onSelect) { onSelect(new Date(newDate)); } } } function createTimeDisplay(value, isActive) { return Box({ className: cn("h-12 flex items-center justify-center transition-all duration-200", isActive ? "font-mono text-3xl font-bold text-primary" : "font-mono text-sm opacity-40"), style: { userSelect: "none" }, children: String(value).padStart(2, "0") }); } function createTimeColumn(type, currentValue) { const max = type === "hour" ? 24 : 60; const items = []; for(let i = -1; i <= 1; i++){ const value = (currentValue + i + max) % max; items.push(createTimeDisplay(value, i === 0)); } return Box({ className: "flex flex-col items-center w-20", children: [ Button({ type: "button", className: cn("btn btn-ghost btn-xs mb-1", size), children: "▲", disabled, onclick: ()=>{ if (type === "hour") { selectedHour.set((h)=>(h - 1 + 24) % 24); } else { selectedMinute.set((m)=>(m - 1 + 60) % 60); } } }), Box({ className: "flex flex-col", children: items }), Button({ type: "button", className: cn("btn btn-ghost btn-xs mt-1", size), children: "▼", disabled, onclick: ()=>{ if (type === "hour") { selectedHour.set((h)=>(h + 1) % 24); } else { selectedMinute.set((m)=>(m + 1) % 60); } } }) ] }); } function createTimeSelector() { const timeContainer = Box({ className: "time-display-container flex items-center justify-center gap-1 mb-6" }); Effect(()=>{ const hour = selectedHour.value; const minute = selectedMinute.value; const hourColumn = createTimeColumn("hour", hour); const minuteColumn = createTimeColumn("minute", minute); const separator = Box({ className: "flex items-center justify-center px-2", children: Typography({ tag: "span", children: ":", className: "font-mono text-2xl font-bold", style: { userSelect: "none" } }) }); render(timeContainer, [ hourColumn, separator, minuteColumn ]); }); return Box({ className: "border border-base-300 rounded-lg bg-base-100 shadow-sm p-6", children: [ timeContainer, Box({ className: "flex justify-center gap-3", children: [ Button({ type: "button", children: strings.back, className: cn("btn btn-ghost", size), disabled, onclick: ()=>{ currentView.set("calendar"); } }), Button({ type: "button", children: strings.confirm, className: cn("btn", color, size), disabled, onclick: ()=>{ const selected = selectedDate.value; if (selected) { selected.setHours(selectedHour.value, selectedMinute.value, 0, 0); if (onSelect) { onSelect(new Date(selected)); } } } }) ] }) ] }); } function createCalendar() { const monthYearElement = Typography({ id: monthYearId, className: "font-semibold text-base" }); const calendarDaysElement = Box({ id: calendarDaysId, className: "grid grid-cols-7 p-2 gap-1" }); Effect(()=>{ const current = currentDate.value; monthYearElement.textContent = `${strings.months[current.getMonth()]} ${current.getFullYear()}`; }); Effect(()=>{ const current = currentDate.value; const selected = selectedDate.value; const firstDay = new Date(current.getFullYear(), current.getMonth(), 1); const lastDay = new Date(current.getFullYear(), current.getMonth() + 1, 0); const days = []; for(let i = 0; i < firstDay.getDay(); i++){ days.push(Box({ className: "p-2" })); } for(let day = 1; day <= lastDay.getDate(); day++){ const dayDate = new Date(current.getFullYear(), current.getMonth(), day); const isDisabled = minDate && dayDate < minDate || maxDate && dayDate > maxDate || disabled; const isSelected = selected && selected.getDate() === day && selected.getMonth() === current.getMonth() && selected.getFullYear() === current.getFullYear(); const isToday = showToday && new Date().toDateString() === dayDate.toDateString(); const isRangeEdge = rangeStart && dayDate.toDateString() === rangeStart.toDateString() || rangeEnd && dayDate.toDateString() === rangeEnd.toDateString(); const isInRange = rangeStart && rangeEnd && dayDate > rangeStart && dayDate < rangeEnd && !isSelected; const dayElement = Button({ type: "button", className: cn("btn btn-square", size, isSelected || isRangeEdge ? color : isInRange ? "btn-ghost bg-primary/10" : "btn-ghost", isToday && !isSelected && !isRangeEdge && "border border-primary"), children: String(day), disabled: isDisabled, onclick: ()=>selectDate(day) }); days.push(dayElement); } render(calendarDaysElement, days); }); return Box({ className: "border border-base-300 rounded-lg bg-base-100 shadow-sm", children: [ Box({ className: "flex items-center justify-between p-3 border-b border-base-300", children: [ Button({ type: "button", className: cn("btn btn-ghost btn-square", size), children: "‹", disabled, onclick: previousMonth }), monthYearElement, Button({ type: "button", className: cn("btn btn-ghost btn-square", size), children: "›", disabled, onclick: nextMonth }) ] }), Box({ className: "grid grid-cols-7 border-b border-base-300 bg-base-200", children: strings.weekDays.map((day)=>Box({ className: "p-2 text-center text-xs font-medium", children: day })) }), calendarDaysElement ] }); } const contentContainer = Box({ className: "w-full" }); Effect(()=>{ const view = currentView.value; if (view === "calendar") { render(contentContainer, createCalendar()); } else { render(contentContainer, createTimeSelector()); } }); const children = [ contentContainer ]; if (label) { const labelElement = Typography({ tag: "label", className: "label", children: Base({ tag: "span", className: "label-text font-semibold", children: label }) }); children.unshift(labelElement); } render(container, children); return container; }