@rastr/controls 0.0.0-stage → 0.1.0-beta.2
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.
- package/LICENSE +21 -0
- package/README.md +19 -2
- package/dist/index.cjs +789 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +120 -0
- package/dist/index.d.ts +120 -0
- package/dist/index.js +753 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +387 -0
- package/package.json +41 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dom.ts","../src/popover.ts","../src/select.ts","../src/i18n.ts","../src/date-text.ts","../src/calendar.ts","../src/date-input.ts"],"sourcesContent":["type AttrValue = string | number | boolean | null | undefined;\ntype Child = Node | string | null | undefined | false;\n\n/** Creates an element; strings are text nodes, markup is never parsed. */\nexport function h<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n props: { readonly class?: string; readonly attrs?: Readonly<Record<string, AttrValue>>; readonly text?: string } = {},\n ...children: Child[]\n): HTMLElementTagNameMap[K] {\n const el: HTMLElementTagNameMap[K] = document.createElement(tag);\n if (props.class) el.className = props.class;\n for (const [name, value] of Object.entries(props.attrs ?? {})) setAttr(el, name, value);\n if (props.text !== undefined) el.textContent = props.text;\n for (const child of children) {\n if (child !== null && child !== undefined && child !== false) el.append(child);\n }\n return el;\n}\n\n/** Sets or removes an attribute: false, null and undefined remove it, true sets it empty. */\nexport function setAttr(el: Element, name: string, value: AttrValue): void {\n if (value === false || value === null || value === undefined) el.removeAttribute(name);\n else el.setAttribute(name, value === true ? '' : String(value));\n}\n\nlet ids: number = 0;\n/** A document-unique id for ARIA references. */\nexport const uniqueId = (prefix: string): string => `${prefix}-${++ids}`;\n","import { h } from './dom';\n\nexport interface ControlPopover {\n readonly element: HTMLElement;\n readonly anchor: HTMLElement;\n close(): void;\n}\n\nconst GAP_PX: number = 4;\nconst VIEWPORT_MARGIN_PX: number = 8;\n/** Announced on document when a Rastr popover opens; the others (grid menus, controls) close. */\nexport const POPOVER_OPEN_EVENT: string = 'rastr:popover-open';\n\nlet current: ControlPopover | null = null;\n\n/** Closes the open popover when it belongs to `within` (or when none is given), e.g. when a grid is destroyed. */\nexport function closeControlPopover(within?: Element): void {\n if (current && (!within || within.contains(current.anchor) || within.contains(current.element))) current.close();\n}\n\n/**\n * A floating panel under `anchor` (above it when it fits better), at least as wide as the anchor and\n * kept inside the viewport (scrolling inside when it is taller). One at a time: opening another closes\n * it. A pointer down outside, focus moving away, scrolling around it, a resize, its anchor leaving the\n * page or close() closes it and calls onClose once.\n */\nexport function openControlPopover(anchor: HTMLElement, content: HTMLElement, options: { readonly onClose: () => void }): ControlPopover {\n current?.close();\n // The nearest dialog or grid hosts the panel: a modal dialog makes the rest of the page inert, and a\n // grid gives it its theme tokens and closes it when destroyed.\n const host: HTMLElement = anchor.closest<HTMLElement>('dialog, .rs-grid') ?? document.body;\n const element: HTMLDivElement = h('div', { class: 'rs-control-popover' }, content);\n host.append(element);\n\n const position = (): void => {\n const rect: DOMRect = anchor.getBoundingClientRect();\n element.style.minWidth = `${rect.width}px`;\n element.style.removeProperty('max-height');\n const box: DOMRect = element.getBoundingClientRect();\n const below: number = window.innerHeight - VIEWPORT_MARGIN_PX - (rect.bottom + GAP_PX);\n const above: number = rect.top - GAP_PX - VIEWPORT_MARGIN_PX;\n let top: number;\n if (box.height <= below || (box.height > above && below >= above)) {\n top = rect.bottom + GAP_PX;\n if (box.height > below) element.style.maxHeight = `${Math.max(below, 0)}px`;\n } else {\n const height: number = Math.min(box.height, above);\n if (box.height > above) element.style.maxHeight = `${Math.max(above, 0)}px`;\n top = rect.top - GAP_PX - height;\n }\n const left: number = Math.max(VIEWPORT_MARGIN_PX, Math.min(rect.left, window.innerWidth - Math.max(box.width, rect.width) - VIEWPORT_MARGIN_PX));\n element.style.top = `${top}px`;\n element.style.left = `${left}px`;\n };\n\n let closed: boolean = false;\n const onPointerDown = (event: Event): void => {\n const target: Node = event.target as Node;\n if (!anchor.isConnected || (!element.contains(target) && !anchor.contains(target))) popover.close();\n };\n const onKeyDown = (): void => {\n if (!anchor.isConnected) popover.close();\n };\n // A scroll anywhere but inside the panel would leave it floating away from its anchor.\n const onScroll = (event: Event): void => {\n if (!(event.target instanceof Node && element.contains(event.target))) popover.close();\n };\n const onResize = (): void => popover.close();\n // Focus moving to another control (Tab out of the calendar) closes it; a click on nothing focusable does not.\n const onFocusOut = (event: FocusEvent): void => {\n const next: EventTarget | null = event.relatedTarget;\n if (next instanceof Node && !element.contains(next) && !anchor.contains(next)) popover.close();\n };\n const onOtherOpen = (event: Event): void => {\n if ((event as CustomEvent<unknown>).detail !== element) popover.close();\n };\n\n const popover: ControlPopover = {\n element,\n anchor,\n close(): void {\n if (closed) return;\n closed = true;\n if (current === popover) current = null;\n document.removeEventListener('pointerdown', onPointerDown, true);\n document.removeEventListener('keydown', onKeyDown, true);\n document.removeEventListener('scroll', onScroll, true);\n document.removeEventListener(POPOVER_OPEN_EVENT, onOtherOpen);\n window.removeEventListener('resize', onResize);\n element.removeEventListener('focusout', onFocusOut);\n element.remove();\n options.onClose();\n },\n };\n position();\n document.dispatchEvent(new CustomEvent(POPOVER_OPEN_EVENT, { detail: element }));\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('scroll', onScroll, true);\n document.addEventListener(POPOVER_OPEN_EVENT, onOtherOpen);\n window.addEventListener('resize', onResize);\n element.addEventListener('focusout', onFocusOut);\n current = popover;\n return popover;\n}\n","import { h, setAttr, uniqueId } from './dom';\nimport { openControlPopover } from './popover';\nimport type { ControlPopover } from './popover';\n\nexport interface SelectOption {\n readonly value: string;\n readonly label: string;\n /** Shown but not choosable (e.g. a saved view that cannot be read). */\n readonly disabled?: boolean;\n /** Tooltip, e.g. why the option is disabled. */\n readonly title?: string;\n}\n\nexport interface SelectOptions {\n readonly options: readonly SelectOption[];\n readonly value: string;\n /** Accessible name (aria-label). */\n readonly label: string;\n /** Called when the user picks a different value. */\n readonly onChange: (value: string) => void;\n /** Called on every choice, also of the current value (an editor commits on it). */\n readonly onChoose?: (value: string) => void;\n /** Extra classes on the button (e.g. the grid's editor class). */\n readonly className?: string;\n /** Shown when the value matches no option. */\n readonly placeholder?: string;\n}\n\nexport interface SelectControl {\n readonly element: HTMLButtonElement;\n readonly value: string;\n setValue(value: string): void;\n setOptions(options: readonly SelectOption[]): void;\n /** Changes the accessible name (e.g. after a language switch). */\n setLabel(label: string): void;\n focus(): void;\n destroy(): void;\n}\n\n/** Characters typed within this time form one search (\"Dok\" finds \"Dokončená\"). */\nconst TYPEAHEAD_MS: number = 600;\n/** Options PageUp and PageDown move by. */\nconst PAGE: number = 10;\n\n/**\n * A select-only combobox (WAI-ARIA APG): a button that shows the chosen option and opens a listbox.\n * Focus stays on the button; the active option is announced through aria-activedescendant.\n */\nexport function createSelect(init: SelectOptions): SelectControl {\n let options: readonly SelectOption[] = init.options;\n let value: string = init.value;\n let popover: ControlPopover | null = null;\n let optionEls: HTMLElement[] = [];\n let active: number = -1;\n let typed: string = '';\n let typedAt: number = 0;\n const listId: string = uniqueId('rs-select-list');\n\n const label: HTMLSpanElement = h('span', { class: 'rs-select__label' });\n const element: HTMLButtonElement = h(\n 'button',\n {\n class: init.className ? `rs-select ${init.className}` : 'rs-select',\n attrs: { type: 'button', role: 'combobox', 'aria-haspopup': 'listbox', 'aria-expanded': 'false', 'aria-label': init.label },\n },\n label,\n );\n\n const render = (): void => {\n const chosen: SelectOption | undefined = options.find(o => o.value === value);\n label.textContent = chosen?.label ?? init.placeholder ?? '';\n setAttr(element, 'data-value', value);\n setAttr(element, 'data-placeholder', chosen === undefined);\n };\n\n /** The nearest enabled option from `index` in direction `step` (the index itself when enabled). */\n const enabledFrom = (index: number, step: 1 | -1): number => {\n for (let i: number = Math.max(0, Math.min(index, options.length - 1)); i >= 0 && i < options.length; i += step) {\n if (!options[i]?.disabled) return i;\n }\n return active;\n };\n\n const setActive = (index: number, step: 1 | -1 = 1): void => {\n if (optionEls.length === 0) return;\n active = enabledFrom(index, step);\n optionEls.forEach((el, i) => setAttr(el, 'data-active', i === active));\n const el: HTMLElement | undefined = optionEls[active];\n setAttr(element, 'aria-activedescendant', el?.id);\n el?.scrollIntoView?.({ block: 'nearest' });\n };\n\n const close = (): void => popover?.close();\n\n const choose = (index: number): void => {\n const option: SelectOption | undefined = options[index];\n if (option?.disabled) return;\n close();\n element.focus();\n if (!option) return;\n const changed: boolean = option.value !== value;\n value = option.value;\n render();\n if (changed) init.onChange(value);\n init.onChoose?.(value);\n };\n\n const open = (): void => {\n if (popover) return;\n const list: HTMLDivElement = h('div', { class: 'rs-select__list', attrs: { role: 'listbox', id: listId, 'aria-label': init.label } });\n optionEls = options.map((option, index) => {\n const el: HTMLDivElement = h('div', {\n class: 'rs-select__option',\n attrs: {\n role: 'option',\n id: `${listId}-${index}`,\n 'aria-selected': String(option.value === value),\n 'aria-disabled': option.disabled ? 'true' : null,\n 'data-value': option.value,\n title: option.title,\n },\n text: option.label,\n });\n // pointerdown would move the focus away from the button.\n el.addEventListener('pointerdown', event => event.preventDefault());\n el.addEventListener('mousemove', () => {\n if (!option.disabled) setActive(index);\n });\n el.addEventListener('click', () => choose(index));\n return el;\n });\n list.append(...optionEls);\n popover = openControlPopover(element, list, {\n onClose: () => {\n popover = null;\n optionEls = [];\n setAttr(element, 'aria-expanded', 'false');\n setAttr(element, 'aria-controls', null);\n setAttr(element, 'aria-activedescendant', null);\n },\n });\n setAttr(element, 'aria-expanded', 'true');\n setAttr(element, 'aria-controls', listId);\n setActive(Math.max(options.findIndex(o => o.value === value), 0));\n };\n\n /** The option a typed search finds (-1: none); a letter that does not continue the search starts a new one. */\n const search = (char: string): number => {\n const now: number = Date.now();\n typed = now - typedAt > TYPEAHEAD_MS ? char : typed + char;\n typedAt = now;\n const find = (text: string): number => options.findIndex(o => !o.disabled && o.label.toLocaleLowerCase().startsWith(text.toLocaleLowerCase()));\n let index: number = find(typed);\n if (index < 0 && typed.length > 1) {\n typed = char;\n index = find(char);\n }\n return index;\n };\n const searching = (): boolean => typed !== '' && Date.now() - typedAt <= TYPEAHEAD_MS;\n\n element.addEventListener('click', () => (popover ? close() : open()));\n element.addEventListener('keydown', event => {\n const opened: boolean = popover !== null;\n let handled: boolean = true;\n // Space continues a typed search (\"Čeká na…\") instead of choosing.\n const key: string = event.key === ' ' && opened && searching() ? 'Char' : event.key;\n switch (key) {\n case 'ArrowDown':\n if (opened) setActive(active + 1);\n else open();\n break;\n case 'ArrowUp':\n if (opened && event.altKey) choose(active);\n else if (opened) setActive(active - 1, -1);\n else open();\n break;\n case 'PageDown':\n if (opened) setActive(active + PAGE);\n else handled = false;\n break;\n case 'PageUp':\n if (opened) setActive(active - PAGE, -1);\n else handled = false;\n break;\n case 'Home':\n if (opened) setActive(0);\n else handled = false;\n break;\n case 'End':\n if (opened) setActive(optionEls.length - 1, -1);\n else handled = false;\n break;\n case 'Enter':\n case ' ':\n if (opened) choose(active);\n else open();\n break;\n case 'Escape':\n if (opened) close();\n else handled = false;\n break;\n case 'Tab':\n close();\n handled = false;\n break;\n default:\n handled = false;\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {\n const index: number = search(event.key);\n // Open: the found option becomes active; closed: it is chosen, like a native select.\n if (index >= 0 && opened) setActive(index);\n else if (index >= 0 && options[index]!.value !== value) {\n value = options[index]!.value;\n render();\n init.onChange(value);\n }\n handled = true;\n }\n }\n if (handled) {\n // The grid and dialogs around must not act on keys the select used.\n event.preventDefault();\n event.stopPropagation();\n }\n });\n\n render();\n return {\n element,\n get value(): string {\n return value;\n },\n setValue(next: string): void {\n value = next;\n render();\n },\n setOptions(next: readonly SelectOption[]): void {\n options = next;\n close();\n render();\n },\n setLabel(label: string): void {\n setAttr(element, 'aria-label', label);\n },\n focus: () => element.focus(),\n destroy(): void {\n close();\n element.remove();\n },\n };\n}\n","export interface ControlsMessages {\n readonly chooseDate: string;\n readonly chooseDateTime: string;\n readonly calendar: string;\n readonly previousMonth: string;\n readonly nextMonth: string;\n readonly today: string;\n readonly clear: string;\n readonly time: string;\n readonly invalidDate: string;\n}\n\nconst cs: ControlsMessages = {\n chooseDate: 'Vybrat datum',\n chooseDateTime: 'Vybrat datum a čas',\n calendar: 'Kalendář',\n previousMonth: 'Předchozí měsíc',\n nextMonth: 'Další měsíc',\n today: 'Dnes',\n clear: 'Vymazat',\n time: 'Čas',\n invalidDate: 'Neplatné datum',\n};\n\nconst en: ControlsMessages = {\n chooseDate: 'Choose date',\n chooseDateTime: 'Choose date and time',\n calendar: 'Calendar',\n previousMonth: 'Previous month',\n nextMonth: 'Next month',\n today: 'Today',\n clear: 'Clear',\n time: 'Time',\n invalidDate: 'Invalid date',\n};\n\nexport const CONTROLS_MESSAGES: Readonly<Record<'cs' | 'en', ControlsMessages>> = { cs, en };\n\n/** Texts for a locale ('en…' → English, otherwise Czech) with overrides. */\nexport function resolveControlsMessages(locale: string, overrides: Partial<ControlsMessages> = {}): ControlsMessages {\n return { ...(locale.toLowerCase().startsWith('en') ? en : cs), ...overrides };\n}\n","export type DateKind = 'date' | 'datetime';\n\n/** Calendar day without a time zone. */\nexport interface CalendarDay {\n readonly year: number;\n readonly month: number;\n readonly day: number;\n}\n\nconst ISO: RegExp = /^(\\d{4})-(\\d{1,2})-(\\d{1,2})(?:[ T](\\d{1,2}):(\\d{2}))?$/;\n/** Three numbers with one kind of separator (. / -) and spaces around it, an optional dot, an optional time. */\nconst LOCAL: RegExp = /^(\\d{1,4})\\s*([./-])\\s*(\\d{1,4})\\s*\\2\\s*(\\d{1,4})\\.?(?:\\s+(\\d{1,2}):(\\d{2}))?$/;\n\nconst pad = (n: number): string => String(n).padStart(2, '0');\n\n/** True when the numbers make a real day (31 February does not). */\nexport function isValidDay(year: number, month: number, day: number): boolean {\n const date: Date = new Date(Date.UTC(year, month - 1, day));\n return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;\n}\n\n/** \"yyyy-mm-dd\" of a calendar day. */\nexport const toIsoDay = (d: CalendarDay): string => `${d.year}-${pad(d.month)}-${pad(d.day)}`;\n\n/** The calendar day of a stored value: a \"yyyy-mm-dd\" date as written, a date-time in local time. */\nexport function dayOf(value: string | null, kind: DateKind): CalendarDay | null {\n if (!value) return null;\n if (kind === 'date') {\n const match: RegExpExecArray | null = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(value);\n return match ? { year: Number(match[1]), month: Number(match[2]), day: Number(match[3]) } : null;\n }\n const date: Date = new Date(value);\n return Number.isNaN(date.getTime()) ? null : { year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() };\n}\n\n/** \"HH:mm\" of a date-time value in local time (\"00:00\" without one). */\nexport function timeOf(value: string | null): string {\n const date: Date | null = value ? new Date(value) : null;\n return date && !Number.isNaN(date.getTime()) ? `${pad(date.getHours())}:${pad(date.getMinutes())}` : '00:00';\n}\n\n/** \"9:05\" or \"09:05\" → minutes of the day; anything else → undefined. */\nexport function parseTime(text: string): { readonly hours: number; readonly minutes: number } | undefined {\n const match: RegExpExecArray | null = /^\\s*(\\d{1,2}):(\\d{2})\\s*$/.exec(text);\n if (!match) return undefined;\n const hours: number = Number(match[1]);\n const minutes: number = Number(match[2]);\n return hours < 24 && minutes < 60 ? { hours, minutes } : undefined;\n}\n\n/** A value from a calendar day and a local time: \"yyyy-mm-dd\" for dates, an ISO instant for date-times. */\nexport function valueOf(day: CalendarDay, kind: DateKind, time: { readonly hours: number; readonly minutes: number } = { hours: 0, minutes: 0 }): string {\n return kind === 'date' ? toIsoDay(day) : new Date(day.year, day.month - 1, day.day, time.hours, time.minutes).toISOString();\n}\n\ntype DateField = 'day' | 'month' | 'year';\n\n/** The order of day, month and year in the locale's numeric date (cs d.m.y, en-US m/d/y, hu y.m.d). */\nexport function dateOrder(locale: string): readonly DateField[] {\n const parts: Intl.DateTimeFormatPart[] = new Intl.DateTimeFormat(locale, { day: 'numeric', month: 'numeric', year: 'numeric', timeZone: 'UTC' }).formatToParts(\n new Date(Date.UTC(2026, 1, 1)),\n );\n return parts.filter((p): p is Intl.DateTimeFormatPart & { type: DateField } => p.type === 'day' || p.type === 'month' || p.type === 'year').map(p => p.type);\n}\n\n/** A two-digit year: in the next 20 years, otherwise in the past century (\"40\" → 2040, \"85\" → 1985 in 2026). */\nfunction fullYear(twoDigits: number): number {\n const year: number = 2000 + twoDigits;\n return year <= new Date().getFullYear() + 20 ? year : year - 100;\n}\n\n/**\n * Reads typed text: the locale's order of day, month and year (cs d. m. yyyy, en-US m/d/yyyy, en-GB\n * d/m/yyyy, hu yyyy. m. d.), one kind of separator (. / -, spaces around it), two-digit years, ISO\n * yyyy-mm-dd, and for date-times an optional \" HH:mm\". Empty text is null; text that is not a real\n * date is undefined.\n */\nexport function parseDateText(text: string, locale: string, kind: DateKind): string | null | undefined {\n const trimmed: string = text.trim();\n if (trimmed === '') return null;\n let year: number;\n let month: number;\n let day: number;\n let hours: string | undefined;\n let minutes: string | undefined;\n const iso: RegExpExecArray | null = ISO.exec(trimmed);\n if (iso) {\n [year, month, day] = [Number(iso[1]), Number(iso[2]), Number(iso[3])];\n [hours, minutes] = [iso[4], iso[5]];\n } else {\n const local: RegExpExecArray | null = LOCAL.exec(trimmed);\n if (!local) return undefined;\n const numbers: string[] = [local[1]!, local[3]!, local[4]!];\n const fields: Partial<Record<DateField, string>> = {};\n dateOrder(locale).forEach((field, i) => (fields[field] = numbers[i] ?? ''));\n const rawYear: string = fields.year ?? '';\n if ((fields.day ?? '').length > 2 || (fields.month ?? '').length > 2 || (rawYear.length !== 2 && rawYear.length !== 4)) return undefined;\n [day, month] = [Number(fields.day), Number(fields.month)];\n year = rawYear.length === 2 ? fullYear(Number(rawYear)) : Number(rawYear);\n [hours, minutes] = [local[5], local[6]];\n }\n if (!isValidDay(year, month, day)) return undefined;\n if (kind === 'date') return hours === undefined ? toIsoDay({ year, month, day }) : undefined;\n const time: { readonly hours: number; readonly minutes: number } | undefined = hours === undefined ? { hours: 0, minutes: 0 } : parseTime(`${hours}:${minutes}`);\n return time ? valueOf({ year, month, day }, kind, time) : undefined;\n}\n\n/** A value as text: dates in the locale's numeric format, date-times with the local time \" HH:mm\". */\nexport function formatDateText(value: string | null, locale: string, kind: DateKind): string {\n const day: CalendarDay | null = dayOf(value, kind);\n if (!day) return '';\n const text: string = new Intl.DateTimeFormat(locale, { day: 'numeric', month: 'numeric', year: 'numeric', timeZone: 'UTC' }).format(\n new Date(Date.UTC(day.year, day.month - 1, day.day)),\n );\n return kind === 'date' ? text : `${text} ${timeOf(value)}`;\n}\n\n/** What to type, as a placeholder, in the locale's order (\"d. m. rrrr\" in Czech, \"m/d/yyyy\" in US English). */\nexport function datePattern(locale: string, kind: DateKind): string {\n const czech: boolean = /^(cs|sk)\\b/i.test(locale);\n const letters: Readonly<Record<string, string>> = { day: 'd', month: 'm', year: czech ? 'rrrr' : 'yyyy' };\n const date: string = new Intl.DateTimeFormat(locale, { day: 'numeric', month: 'numeric', year: 'numeric', timeZone: 'UTC' })\n .formatToParts(new Date(Date.UTC(2026, 1, 1)))\n .map(part => letters[part.type] ?? part.value)\n .join('')\n // Some locales use narrow no-break spaces; a placeholder reads the same with plain ones.\n .replace(/[\\u00a0\\u202f]/g, ' ');\n return kind === 'date' ? date : `${date} hh:mm`;\n}\n","import { h, setAttr, uniqueId } from './dom';\nimport type { CalendarDay, DateKind } from './date-text';\nimport { parseTime } from './date-text';\nimport type { ControlsMessages } from './i18n';\n\nexport interface CalendarOptions {\n readonly kind: DateKind;\n readonly selected: CalendarDay | null;\n readonly today: CalendarDay;\n /** \"HH:mm\" for date-times. */\n readonly time: string;\n readonly locale: string;\n readonly messages: ControlsMessages;\n readonly label: string;\n readonly onPick: (day: CalendarDay, time: { readonly hours: number; readonly minutes: number }) => void;\n readonly onClear: () => void;\n readonly onCancel: () => void;\n}\n\nexport interface Calendar {\n readonly element: HTMLElement;\n /** Focuses the selected day (or today) after the calendar is placed. */\n focus(): void;\n}\n\nconst sameDay = (a: CalendarDay | null, b: CalendarDay): boolean => a !== null && a.year === b.year && a.month === b.month && a.day === b.day;\n\n/** The day `days` after a day (negative: before). */\nfunction addDays(day: CalendarDay, days: number): CalendarDay {\n const date: Date = new Date(Date.UTC(day.year, day.month - 1, day.day + days));\n return { year: date.getUTCFullYear(), month: date.getUTCMonth() + 1, day: date.getUTCDate() };\n}\n\n/** The same day `months` later, clamped to the month's last day (31 Jan + 1 month = 28/29 Feb). */\nfunction addMonths(day: CalendarDay, months: number): CalendarDay {\n const first: Date = new Date(Date.UTC(day.year, day.month - 1 + months, 1));\n const year: number = first.getUTCFullYear();\n const month: number = first.getUTCMonth() + 1;\n const last: number = new Date(Date.UTC(year, month, 0)).getUTCDate();\n return { year, month, day: Math.min(day.day, last) };\n}\n\n/** The locale's first day of the week (0 = Sunday), Monday when the runtime does not know it. */\nfunction firstWeekday(locale: string): number {\n try {\n const intl: Intl.Locale & { getWeekInfo?(): { firstDay: number }; weekInfo?: { firstDay: number } } = new Intl.Locale(locale);\n const firstDay: number | undefined = (intl.getWeekInfo?.() ?? intl.weekInfo)?.firstDay;\n if (firstDay !== undefined) return firstDay % 7;\n } catch {\n // An unknown locale tag falls through.\n }\n return /^en(-US)?$/i.test(locale) ? 0 : 1;\n}\n\n/**\n * A month grid (WAI-ARIA APG date picker dialog): arrows move by a day or a week, PageUp/PageDown by a\n * month, Home/End to the week's ends, Enter picks, Escape cancels. Date-times have a time field.\n */\nexport function createCalendar(options: CalendarOptions): Calendar {\n const m: ControlsMessages = options.messages;\n let focused: CalendarDay = options.selected ?? options.today;\n let time: string = options.time;\n\n const titleId: string = uniqueId('rs-calendar-title');\n const title: HTMLSpanElement = h('span', { class: 'rs-calendar__title', attrs: { id: titleId, 'aria-live': 'polite' } });\n const nav = (step: number, label: string, glyph: string): HTMLButtonElement => {\n const button: HTMLButtonElement = h('button', { class: 'rs-calendar__nav', attrs: { type: 'button', 'aria-label': label, 'data-step': step }, text: glyph });\n button.addEventListener('click', () => {\n focused = addMonths(focused, step);\n render();\n });\n return button;\n };\n // The weekday names are the grid's column headers.\n const weekdays: HTMLDivElement = h('div', { class: 'rs-calendar__weekdays', attrs: { role: 'row' } });\n const grid: HTMLDivElement = h('div', { class: 'rs-calendar__grid', attrs: { role: 'grid', 'aria-labelledby': titleId } });\n const element: HTMLDivElement = h(\n 'div',\n { class: 'rs-calendar', attrs: { role: 'dialog', 'aria-modal': 'false', 'aria-label': `${m.calendar}: ${options.label}` } },\n h('div', { class: 'rs-calendar__head' }, title, nav(-1, m.previousMonth, '‹'), nav(1, m.nextMonth, '›')),\n grid,\n );\n\n // Short weekday names in the locale, from a known week (5 January 2026 is a Monday).\n const start: number = firstWeekday(options.locale);\n const weekdayFormat: Intl.DateTimeFormat = new Intl.DateTimeFormat(options.locale, { weekday: 'short', timeZone: 'UTC' });\n const weekdayName: Intl.DateTimeFormat = new Intl.DateTimeFormat(options.locale, { weekday: 'long', timeZone: 'UTC' });\n for (let i: number = 0; i < 7; i++) {\n // 4 January 2026 is a Sunday.\n const date: Date = new Date(Date.UTC(2026, 0, 4 + start + i));\n weekdays.append(h('span', { attrs: { role: 'columnheader', 'aria-label': weekdayName.format(date) }, text: weekdayFormat.format(date) }));\n }\n\n const monthFormat: Intl.DateTimeFormat = new Intl.DateTimeFormat(options.locale, { month: 'long', year: 'numeric', timeZone: 'UTC' });\n const fullFormat: Intl.DateTimeFormat = new Intl.DateTimeFormat(options.locale, { dateStyle: 'full', timeZone: 'UTC' });\n\n let timeInput: HTMLInputElement | null = null;\n const pick = (day: CalendarDay): void => {\n const parsed: { readonly hours: number; readonly minutes: number } | undefined = options.kind === 'datetime' ? parseTime(time) : { hours: 0, minutes: 0 };\n if (!parsed) {\n // A day with a time that is no time would silently become midnight: ask for the time first.\n if (timeInput) {\n setAttr(timeInput, 'aria-invalid', 'true');\n timeInput.focus();\n }\n return;\n }\n options.onPick(day, parsed);\n };\n\n const dayButtons: Map<string, HTMLButtonElement> = new Map();\n const keyOf = (d: CalendarDay): string => `${d.year}-${d.month}-${d.day}`;\n\n const render = (): void => {\n title.textContent = monthFormat.format(new Date(Date.UTC(focused.year, focused.month - 1, 1)));\n const first: CalendarDay = { year: focused.year, month: focused.month, day: 1 };\n const weekday: number = new Date(Date.UTC(first.year, first.month - 1, 1)).getUTCDay();\n let cursor: CalendarDay = addDays(first, -((weekday - start + 7) % 7));\n dayButtons.clear();\n const rows: HTMLElement[] = [];\n for (let week: number = 0; week < 6; week++) {\n const row: HTMLDivElement = h('div', { attrs: { role: 'row' } });\n for (let i: number = 0; i < 7; i++) {\n const day: CalendarDay = cursor;\n const isFocused: boolean = sameDay(focused, day);\n const button: HTMLButtonElement = h('button', {\n class: 'rs-calendar__day',\n attrs: {\n type: 'button',\n role: 'gridcell',\n tabindex: isFocused ? 0 : -1,\n 'aria-label': fullFormat.format(new Date(Date.UTC(day.year, day.month - 1, day.day))),\n 'aria-selected': String(sameDay(options.selected, day)),\n 'data-selected': sameDay(options.selected, day),\n 'data-today': sameDay(options.today, day),\n 'data-outside': day.month !== focused.month,\n },\n text: String(day.day),\n });\n button.addEventListener('click', () => pick(day));\n dayButtons.set(keyOf(day), button);\n row.append(button);\n cursor = addDays(cursor, 1);\n }\n rows.push(row);\n }\n grid.replaceChildren(weekdays, ...rows);\n };\n\n const moveFocus = (next: CalendarDay): void => {\n focused = next;\n render();\n dayButtons.get(keyOf(focused))?.focus();\n };\n\n grid.addEventListener('keydown', event => {\n const weekday: number = (new Date(Date.UTC(focused.year, focused.month - 1, focused.day)).getUTCDay() - start + 7) % 7;\n let next: CalendarDay | null = null;\n switch (event.key) {\n case 'ArrowLeft':\n next = addDays(focused, -1);\n break;\n case 'ArrowRight':\n next = addDays(focused, 1);\n break;\n case 'ArrowUp':\n next = addDays(focused, -7);\n break;\n case 'ArrowDown':\n next = addDays(focused, 7);\n break;\n case 'PageUp':\n next = addMonths(focused, event.shiftKey ? -12 : -1);\n break;\n case 'PageDown':\n next = addMonths(focused, event.shiftKey ? 12 : 1);\n break;\n case 'Home':\n next = addDays(focused, -weekday);\n break;\n case 'End':\n next = addDays(focused, 6 - weekday);\n break;\n case 'Enter':\n case ' ':\n event.preventDefault();\n event.stopPropagation();\n pick(focused);\n return;\n default:\n return;\n }\n event.preventDefault();\n event.stopPropagation();\n moveFocus(next);\n });\n\n element.addEventListener('keydown', event => {\n if (event.key !== 'Escape') return;\n event.preventDefault();\n event.stopPropagation();\n options.onCancel();\n });\n\n const action = (name: string, label: string, run: () => void): HTMLButtonElement => {\n const button: HTMLButtonElement = h('button', { class: 'rs-calendar__action', attrs: { type: 'button', 'data-action': name }, text: label });\n button.addEventListener('click', run);\n return button;\n };\n const foot: HTMLDivElement = h('div', { class: 'rs-calendar__foot' }, action('clear', m.clear, () => options.onClear()));\n if (options.kind === 'datetime') {\n const field: HTMLInputElement = h('input', { class: 'rs-calendar__time', attrs: { type: 'text', inputmode: 'numeric', 'aria-label': m.time, placeholder: 'hh:mm' } });\n field.value = time;\n field.addEventListener('input', () => {\n time = field.value;\n setAttr(field, 'aria-invalid', parseTime(time) ? null : 'true');\n });\n // Enter in the time field picks the focused day with that time.\n field.addEventListener('keydown', event => {\n if (event.key !== 'Enter') return;\n event.preventDefault();\n event.stopPropagation();\n pick(focused);\n });\n timeInput = field;\n foot.append(field);\n }\n foot.append(action('today', m.today, () => pick(options.today)));\n element.append(foot);\n\n render();\n return {\n element,\n focus: () => dayButtons.get(keyOf(focused))?.focus(),\n };\n}\n","import { createCalendar } from './calendar';\nimport type { Calendar } from './calendar';\nimport { h, setAttr } from './dom';\nimport { datePattern, dayOf, formatDateText, parseDateText, timeOf, valueOf } from './date-text';\nimport type { CalendarDay, DateKind } from './date-text';\nimport { resolveControlsMessages } from './i18n';\nimport type { ControlsMessages } from './i18n';\nimport { openControlPopover } from './popover';\nimport type { ControlPopover } from './popover';\n\nexport interface DateInputOptions {\n readonly kind: DateKind;\n /** \"yyyy-mm-dd\" for dates, an ISO instant for date-times, null when empty. */\n readonly value: string | null;\n readonly locale: string;\n /** Accessible name of the text field. */\n readonly label: string;\n /** Called when the value changes by typing (blur or Enter) or by the calendar. */\n readonly onChange: (value: string | null) => void;\n readonly messages?: Partial<ControlsMessages>;\n /** Extra classes on the wrapper. */\n readonly className?: string;\n}\n\nexport interface DateInputControl {\n readonly element: HTMLElement;\n /** The text field, e.g. to focus it or to read what was typed. */\n readonly input: HTMLInputElement;\n readonly value: string | null;\n setValue(value: string | null): void;\n focus(): void;\n destroy(): void;\n}\n\nconst CALENDAR_ICON: string = 'M7 2v2H5a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-2V2h-2v2H9V2zM5 9h14v10H5z';\n\nfunction calendarIcon(): SVGSVGElement {\n const svg: SVGSVGElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n svg.setAttribute('viewBox', '0 0 24 24');\n svg.setAttribute('width', '14');\n svg.setAttribute('height', '14');\n svg.setAttribute('aria-hidden', 'true');\n const path: SVGPathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path');\n path.setAttribute('d', CALENDAR_ICON);\n path.setAttribute('fill', 'currentColor');\n svg.append(path);\n return svg;\n}\n\n/** Same local day and minute (stored seconds and milliseconds do not count). */\nfunction sameMinute(a: string, b: string): boolean {\n const x: Date = new Date(a);\n const y: Date = new Date(b);\n return Math.floor(x.getTime() / 60_000) === Math.floor(y.getTime() / 60_000);\n}\n\nconst todayOf = (): CalendarDay => {\n const now: Date = new Date();\n return { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() };\n};\n\n/**\n * A text field for a date (or date and time) in the locale's format with a calendar button. Typed text\n * is read on blur or Enter; text that is no date is marked invalid and leaves the value as it was.\n */\nexport function createDateInput(options: DateInputOptions): DateInputControl {\n const m: ControlsMessages = resolveControlsMessages(options.locale, options.messages);\n let value: string | null = options.value;\n let popover: ControlPopover | null = null;\n\n const input: HTMLInputElement = h('input', {\n class: 'rs-date__input',\n attrs: {\n type: 'text',\n autocomplete: 'off',\n 'aria-label': options.label,\n 'aria-keyshortcuts': 'Alt+ArrowDown',\n placeholder: datePattern(options.locale, options.kind),\n },\n });\n const button: HTMLButtonElement = h(\n 'button',\n {\n class: 'rs-date__button',\n attrs: { type: 'button', tabindex: -1, 'aria-haspopup': 'dialog', 'aria-expanded': 'false', 'aria-label': options.kind === 'date' ? m.chooseDate : m.chooseDateTime },\n },\n calendarIcon(),\n );\n const element: HTMLDivElement = h('div', { class: options.className ? `rs-date ${options.className}` : 'rs-date' }, input, button);\n\n /** The text show() wrote last: leaving the field with it untouched changes nothing. */\n let shown: string = '';\n const show = (): void => {\n shown = formatDateText(value, options.locale, options.kind);\n input.value = shown;\n setAttr(input, 'aria-invalid', null);\n setAttr(input, 'title', null);\n };\n\n const set = (next: string | null): void => {\n const changed: boolean = next !== value;\n value = next;\n show();\n if (changed) options.onChange(value);\n };\n\n /** Reads the typed text; true when it was a date (or empty). */\n const commitText = (): boolean => {\n // The text as shown is the value (a date-time keeps its seconds, a locale its order).\n if (input.value === shown) {\n setAttr(input, 'aria-invalid', null);\n return true;\n }\n const parsed: string | null | undefined = parseDateText(input.value, options.locale, options.kind);\n if (parsed === undefined) {\n setAttr(input, 'aria-invalid', 'true');\n setAttr(input, 'title', m.invalidDate);\n return false;\n }\n setAttr(input, 'title', null);\n // The same day and minute as the value: keep the value (its seconds and its exact form).\n if (options.kind === 'datetime' && parsed !== null && value !== null && sameMinute(parsed, value)) {\n show();\n return true;\n }\n set(parsed);\n return true;\n };\n\n const close = (): void => popover?.close();\n\n const open = (): void => {\n if (popover) return;\n // What is typed and valid is where the calendar opens.\n const typed: string | null | undefined = parseDateText(input.value, options.locale, options.kind);\n const shown: string | null = typed === undefined ? value : typed;\n const calendar: Calendar = createCalendar({\n kind: options.kind,\n selected: dayOf(shown, options.kind),\n today: todayOf(),\n time: timeOf(options.kind === 'datetime' ? shown : null),\n locale: options.locale,\n messages: m,\n label: options.label,\n onPick: (day, time) => {\n close();\n set(valueOf(day, options.kind, time));\n input.focus();\n },\n onClear: () => {\n close();\n set(null);\n input.focus();\n },\n onCancel: () => {\n close();\n input.focus();\n },\n });\n popover = openControlPopover(element, calendar.element, {\n onClose: () => {\n popover = null;\n setAttr(button, 'aria-expanded', 'false');\n },\n });\n setAttr(button, 'aria-expanded', 'true');\n calendar.focus();\n };\n\n input.addEventListener('blur', () => {\n commitText();\n });\n input.addEventListener('keydown', event => {\n if (event.key === 'Enter') {\n // Text that is no date stays in the field, marked; Enter must not reach an editor that would save the old value.\n if (!commitText()) {\n event.preventDefault();\n event.stopPropagation();\n }\n } else if (event.key === 'ArrowDown' && event.altKey) {\n // Alt+↓ opens the calendar, like a native date field.\n event.preventDefault();\n event.stopPropagation();\n open();\n }\n });\n // pointerdown would blur the field and commit half-typed text first.\n button.addEventListener('pointerdown', event => event.preventDefault());\n button.addEventListener('click', () => (popover ? close() : open()));\n\n show();\n return {\n element,\n input,\n get value(): string | null {\n return value;\n },\n setValue(next: string | null): void {\n value = next;\n show();\n },\n focus: () => input.focus(),\n destroy(): void {\n close();\n element.remove();\n },\n };\n}\n"],"mappings":"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","names":["el","label","shown"]}
|
package/dist/styles.css
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Rastr controls: select and date picker. Same rules as the grid: one cascade layer, zero-specificity
|
|
3
|
+
* selectors, colors only in --rs-* tokens. The tokens have defaults on :root because the controls also
|
|
4
|
+
* live outside a grid (filter builder dialogs); inside a grid they take the grid theme.
|
|
5
|
+
*/
|
|
6
|
+
@layer rastr {
|
|
7
|
+
:where(:root) {
|
|
8
|
+
--rs-font: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
|
|
9
|
+
--rs-font-size: 13px;
|
|
10
|
+
--rs-accent: oklch(0.58 0.17 255);
|
|
11
|
+
--rs-accent-fg: #ffffff;
|
|
12
|
+
--rs-accent-soft: color-mix(in oklch, var(--rs-accent) 12%, transparent);
|
|
13
|
+
--rs-accent-softer: color-mix(in oklch, var(--rs-accent) 6%, transparent);
|
|
14
|
+
--rs-bg: #ffffff;
|
|
15
|
+
--rs-surface: #f7f8fa;
|
|
16
|
+
--rs-surface-2: #eff1f4;
|
|
17
|
+
--rs-border: #e6e8ec;
|
|
18
|
+
--rs-border-strong: #d5d9df;
|
|
19
|
+
--rs-text: #1b1f24;
|
|
20
|
+
--rs-text-muted: #6b7280;
|
|
21
|
+
--rs-danger: #d93f3f;
|
|
22
|
+
--rs-radius: 10px;
|
|
23
|
+
--rs-radius-sm: 6px;
|
|
24
|
+
--rs-focus-ring: 0 0 0 3px var(--rs-accent-soft);
|
|
25
|
+
--rs-popover-shadow: 0 8px 28px rgb(0 0 0 / 0.12), 0 2px 6px rgb(0 0 0 / 0.06);
|
|
26
|
+
--rs-transition: 120ms ease;
|
|
27
|
+
--rs-z-popover: 1000;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@media (prefers-color-scheme: dark) {
|
|
31
|
+
:where(:root:not([data-theme])) {
|
|
32
|
+
--rs-bg: #15181d;
|
|
33
|
+
--rs-surface: #1b1f25;
|
|
34
|
+
--rs-surface-2: #232830;
|
|
35
|
+
--rs-border: #2a3039;
|
|
36
|
+
--rs-border-strong: #39414c;
|
|
37
|
+
--rs-text: #e7eaee;
|
|
38
|
+
--rs-text-muted: #8d96a3;
|
|
39
|
+
--rs-accent: oklch(0.7 0.14 255);
|
|
40
|
+
--rs-accent-fg: #0b0d10;
|
|
41
|
+
--rs-accent-soft: color-mix(in oklch, var(--rs-accent) 18%, transparent);
|
|
42
|
+
--rs-popover-shadow: 0 8px 28px rgb(0 0 0 / 0.45), 0 2px 6px rgb(0 0 0 / 0.3);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* ---------- Popover ---------- */
|
|
47
|
+
:where(.rs-control-popover) {
|
|
48
|
+
position: fixed;
|
|
49
|
+
z-index: var(--rs-z-popover);
|
|
50
|
+
box-sizing: border-box;
|
|
51
|
+
max-width: calc(100vw - 16px);
|
|
52
|
+
max-height: min(360px, calc(100vh - 16px));
|
|
53
|
+
overflow: auto;
|
|
54
|
+
padding: 4px;
|
|
55
|
+
border: 1px solid var(--rs-border);
|
|
56
|
+
border-radius: var(--rs-radius-sm);
|
|
57
|
+
background: var(--rs-bg);
|
|
58
|
+
color: var(--rs-text);
|
|
59
|
+
box-shadow: var(--rs-popover-shadow);
|
|
60
|
+
font-family: var(--rs-font);
|
|
61
|
+
font-size: var(--rs-font-size);
|
|
62
|
+
line-height: 1.35;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* ---------- Select ---------- */
|
|
66
|
+
:where(.rs-select) {
|
|
67
|
+
all: unset;
|
|
68
|
+
box-sizing: border-box;
|
|
69
|
+
position: relative;
|
|
70
|
+
display: flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
width: 100%;
|
|
73
|
+
min-width: 0;
|
|
74
|
+
height: 100%;
|
|
75
|
+
min-height: 26px;
|
|
76
|
+
padding: 0 26px 0 8px;
|
|
77
|
+
border: 1px solid var(--rs-border);
|
|
78
|
+
border-radius: var(--rs-radius-sm);
|
|
79
|
+
background: var(--rs-bg);
|
|
80
|
+
color: var(--rs-text);
|
|
81
|
+
font-family: var(--rs-font);
|
|
82
|
+
font-size: 12px;
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
transition:
|
|
85
|
+
border-color var(--rs-transition),
|
|
86
|
+
box-shadow var(--rs-transition);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Chevron pointing down; up while the list is open. */
|
|
90
|
+
:where(.rs-select)::after {
|
|
91
|
+
content: "";
|
|
92
|
+
position: absolute;
|
|
93
|
+
right: 10px;
|
|
94
|
+
top: 50%;
|
|
95
|
+
width: 5px;
|
|
96
|
+
height: 5px;
|
|
97
|
+
border-right: 1.5px solid var(--rs-text-muted);
|
|
98
|
+
border-bottom: 1.5px solid var(--rs-text-muted);
|
|
99
|
+
transform: translateY(-70%) rotate(45deg);
|
|
100
|
+
transition: transform var(--rs-transition);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
:where(.rs-select[aria-expanded="true"])::after {
|
|
104
|
+
transform: translateY(-20%) rotate(-135deg);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
:where(.rs-select:hover) {
|
|
108
|
+
border-color: var(--rs-border-strong);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
:where(.rs-select:focus-visible, .rs-select[aria-expanded="true"]) {
|
|
112
|
+
border-color: var(--rs-accent);
|
|
113
|
+
box-shadow: var(--rs-focus-ring);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
:where(.rs-select[data-active]) {
|
|
117
|
+
border-color: var(--rs-accent);
|
|
118
|
+
background: var(--rs-accent-softer);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
:where(.rs-select__label) {
|
|
122
|
+
overflow: hidden;
|
|
123
|
+
white-space: nowrap;
|
|
124
|
+
text-overflow: ellipsis;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
:where(.rs-select[data-placeholder] .rs-select__label) {
|
|
128
|
+
color: var(--rs-text-muted);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
:where(.rs-select__list) {
|
|
132
|
+
display: flex;
|
|
133
|
+
flex-direction: column;
|
|
134
|
+
gap: 1px;
|
|
135
|
+
outline: none;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
:where(.rs-select__option) {
|
|
139
|
+
position: relative;
|
|
140
|
+
padding: 6px 10px 6px 28px;
|
|
141
|
+
border-radius: 4px;
|
|
142
|
+
white-space: nowrap;
|
|
143
|
+
cursor: pointer;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
:where(.rs-select__option[data-active]) {
|
|
147
|
+
background: var(--rs-surface-2);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
:where(.rs-select__option[aria-disabled="true"]) {
|
|
151
|
+
color: var(--rs-text-muted);
|
|
152
|
+
opacity: 0.6;
|
|
153
|
+
cursor: not-allowed;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* Check mark at the chosen option. */
|
|
157
|
+
:where(.rs-select__option[aria-selected="true"]) {
|
|
158
|
+
color: var(--rs-accent);
|
|
159
|
+
font-weight: 600;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
:where(.rs-select__option[aria-selected="true"])::before {
|
|
163
|
+
content: "";
|
|
164
|
+
position: absolute;
|
|
165
|
+
left: 11px;
|
|
166
|
+
top: 50%;
|
|
167
|
+
width: 4px;
|
|
168
|
+
height: 8px;
|
|
169
|
+
border-right: 2px solid currentColor;
|
|
170
|
+
border-bottom: 2px solid currentColor;
|
|
171
|
+
transform: translateY(-60%) rotate(45deg);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* ---------- Date input ---------- */
|
|
175
|
+
:where(.rs-date) {
|
|
176
|
+
position: relative;
|
|
177
|
+
display: flex;
|
|
178
|
+
align-items: stretch;
|
|
179
|
+
width: 100%;
|
|
180
|
+
min-width: 0;
|
|
181
|
+
height: 100%;
|
|
182
|
+
min-height: 26px;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
:where(.rs-date__input) {
|
|
186
|
+
all: unset;
|
|
187
|
+
box-sizing: border-box;
|
|
188
|
+
flex: 1;
|
|
189
|
+
width: 100%;
|
|
190
|
+
min-width: 0;
|
|
191
|
+
padding: 0 26px 0 8px;
|
|
192
|
+
border: 1px solid var(--rs-border);
|
|
193
|
+
border-radius: var(--rs-radius-sm);
|
|
194
|
+
background: var(--rs-bg);
|
|
195
|
+
color: var(--rs-text);
|
|
196
|
+
font-family: var(--rs-font);
|
|
197
|
+
font-size: 12px;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
:where(.rs-date__input::placeholder) {
|
|
201
|
+
color: var(--rs-text-muted);
|
|
202
|
+
opacity: 0.75;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
:where(.rs-date__input:focus) {
|
|
206
|
+
border-color: var(--rs-accent);
|
|
207
|
+
box-shadow: var(--rs-focus-ring);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
:where(.rs-date__input[aria-invalid="true"]) {
|
|
211
|
+
border-color: var(--rs-danger);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
:where(.rs-date[data-active] .rs-date__input) {
|
|
215
|
+
border-color: var(--rs-accent);
|
|
216
|
+
background: var(--rs-accent-softer);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
:where(.rs-date__button) {
|
|
220
|
+
all: unset;
|
|
221
|
+
position: absolute;
|
|
222
|
+
right: 3px;
|
|
223
|
+
top: 50%;
|
|
224
|
+
display: grid;
|
|
225
|
+
place-items: center;
|
|
226
|
+
width: 20px;
|
|
227
|
+
height: 20px;
|
|
228
|
+
border-radius: 4px;
|
|
229
|
+
color: var(--rs-text-muted);
|
|
230
|
+
cursor: pointer;
|
|
231
|
+
transform: translateY(-50%);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
:where(.rs-date__button:hover, .rs-date__button[aria-expanded="true"]) {
|
|
235
|
+
background: var(--rs-surface-2);
|
|
236
|
+
color: var(--rs-text);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
:where(.rs-date__button:focus-visible) {
|
|
240
|
+
box-shadow: var(--rs-focus-ring);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* ---------- Calendar ---------- */
|
|
244
|
+
:where(.rs-calendar) {
|
|
245
|
+
display: flex;
|
|
246
|
+
flex-direction: column;
|
|
247
|
+
gap: 6px;
|
|
248
|
+
padding: 4px;
|
|
249
|
+
width: 244px;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
:where(.rs-calendar__head) {
|
|
253
|
+
display: flex;
|
|
254
|
+
align-items: center;
|
|
255
|
+
gap: 4px;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
:where(.rs-calendar__title) {
|
|
259
|
+
flex: 1;
|
|
260
|
+
font-weight: 600;
|
|
261
|
+
text-transform: capitalize;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
:where(.rs-calendar__nav, .rs-calendar__action) {
|
|
265
|
+
all: unset;
|
|
266
|
+
box-sizing: border-box;
|
|
267
|
+
display: inline-grid;
|
|
268
|
+
place-items: center;
|
|
269
|
+
min-width: 26px;
|
|
270
|
+
height: 26px;
|
|
271
|
+
padding: 0 8px;
|
|
272
|
+
border-radius: var(--rs-radius-sm);
|
|
273
|
+
color: var(--rs-text);
|
|
274
|
+
cursor: pointer;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
:where(.rs-calendar__nav:hover, .rs-calendar__action:hover) {
|
|
278
|
+
background: var(--rs-surface-2);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
:where(.rs-calendar__nav:focus-visible, .rs-calendar__action:focus-visible, .rs-calendar__day:focus-visible) {
|
|
282
|
+
box-shadow: var(--rs-focus-ring);
|
|
283
|
+
outline: none;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
:where(.rs-calendar__action) {
|
|
287
|
+
color: var(--rs-accent);
|
|
288
|
+
font-weight: 600;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/* Week rows are ARIA rows only; the days sit in the grid's seven columns. */
|
|
292
|
+
:where(.rs-calendar__grid > [role="row"]) {
|
|
293
|
+
display: contents;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
:where(.rs-calendar__grid) {
|
|
297
|
+
display: grid;
|
|
298
|
+
grid-template-columns: repeat(7, 1fr);
|
|
299
|
+
gap: 2px;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/* The weekday row sits in the grid's columns like the week rows. */
|
|
303
|
+
:where(.rs-calendar__weekdays) {
|
|
304
|
+
display: contents;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
:where(.rs-calendar__weekdays > *) {
|
|
308
|
+
padding-bottom: 2px;
|
|
309
|
+
color: var(--rs-text-muted);
|
|
310
|
+
font-size: 11px;
|
|
311
|
+
font-weight: 600;
|
|
312
|
+
text-align: center;
|
|
313
|
+
text-transform: uppercase;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
:where(.rs-calendar__day) {
|
|
317
|
+
all: unset;
|
|
318
|
+
box-sizing: border-box;
|
|
319
|
+
display: grid;
|
|
320
|
+
place-items: center;
|
|
321
|
+
height: 30px;
|
|
322
|
+
border-radius: var(--rs-radius-sm);
|
|
323
|
+
color: var(--rs-text);
|
|
324
|
+
font-variant-numeric: tabular-nums;
|
|
325
|
+
cursor: pointer;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
:where(.rs-calendar__day:hover) {
|
|
329
|
+
background: var(--rs-surface-2);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
:where(.rs-calendar__day[data-outside]) {
|
|
333
|
+
color: var(--rs-text-muted);
|
|
334
|
+
opacity: 0.6;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
:where(.rs-calendar__day[data-today]) {
|
|
338
|
+
box-shadow: inset 0 0 0 1px var(--rs-accent);
|
|
339
|
+
font-weight: 600;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
:where(.rs-calendar__day[data-selected]) {
|
|
343
|
+
background: var(--rs-accent);
|
|
344
|
+
color: var(--rs-accent-fg);
|
|
345
|
+
font-weight: 600;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
:where(.rs-calendar__foot) {
|
|
349
|
+
display: flex;
|
|
350
|
+
align-items: center;
|
|
351
|
+
justify-content: space-between;
|
|
352
|
+
gap: 6px;
|
|
353
|
+
padding-top: 4px;
|
|
354
|
+
border-top: 1px solid var(--rs-border);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
:where(.rs-calendar__time) {
|
|
358
|
+
all: unset;
|
|
359
|
+
box-sizing: border-box;
|
|
360
|
+
flex: none;
|
|
361
|
+
width: 64px;
|
|
362
|
+
height: 26px;
|
|
363
|
+
padding: 0 6px;
|
|
364
|
+
border: 1px solid var(--rs-border);
|
|
365
|
+
border-radius: var(--rs-radius-sm);
|
|
366
|
+
background: var(--rs-bg);
|
|
367
|
+
color: var(--rs-text);
|
|
368
|
+
font-family: var(--rs-font);
|
|
369
|
+
font-size: 12px;
|
|
370
|
+
text-align: center;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
:where(.rs-calendar__time:focus) {
|
|
374
|
+
border-color: var(--rs-accent);
|
|
375
|
+
box-shadow: var(--rs-focus-ring);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
:where(.rs-calendar__time[aria-invalid="true"]) {
|
|
379
|
+
border-color: var(--rs-danger);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
@media (prefers-reduced-motion: reduce) {
|
|
383
|
+
:where(.rs-select, .rs-select::after) {
|
|
384
|
+
transition: none;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rastr/controls",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0-beta.2",
|
|
4
|
+
"description": "Select and date picker in the Rastr look, used by the grid and the filter builder.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": [
|
|
8
|
+
"*.css"
|
|
9
|
+
],
|
|
10
|
+
"main": "./dist/index.cjs",
|
|
11
|
+
"module": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"require": {
|
|
20
|
+
"types": "./dist/index.d.cts",
|
|
21
|
+
"default": "./dist/index.cjs"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"./styles.css": "./dist/styles.css"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^22",
|
|
31
|
+
"jsdom": "^25",
|
|
32
|
+
"tsup": "^8",
|
|
33
|
+
"vitest": "^2"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsup",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"lint": "eslint src test",
|
|
42
|
+
"typecheck": "tsc --noEmit"
|
|
43
|
+
}
|
|
6
44
|
}
|