API dokumentace
Kompletní reference knihovny Gregory ve verzi 0.3.0. Všechno, co je tady popsané, je součástí veřejného API.
Instalace
Rovnou z GitHubu — do npm registru se knihovna vydávat nebude:
npm install github:svatekr70/gregory # poslední main npm install github:svatekr70/gregory#v0.3.0 # konkrétní verze
Balíček je ESM i UMD, typy jsou součástí:
import { Gregory } from '@svatekr70/gregory' import '@svatekr70/gregory/style.css'
Bez build kroku, přímo v HTML:
<link rel="stylesheet" href="…/gregory/dist/gregory.css"> <script src="…/gregory/dist/gregory.umd.cjs"></script> <script> new Gregory.Gregory('#vstup', { mode: 'range' }) </script>
Knihovna do stránky sama nic nevkládá. Buď načti gregory/style.css,
nebo si napiš vlastní — komponenta používá jen třídy s prefixem gr-.
Rychlý start
Jako třída
const picker = new Gregory('#vstup', { mode: 'range', locale: 'cs', }) picker.on('apply', ({ value }) => { // value je { from: Date, to: Date } odesliFiltr(value.from, value.to) })
Jako custom element
import { defineElement } from '@svatekr70/gregory' defineElement()
<gregory-picker mode="range" locale="cs" months="2"></gregory-picker>
Konstruktor
target je CSS selektor nebo přímo prvek:
- je-li to
<input>, picker se na něj napojí jako popover a zapisuje do něj naformátovanou hodnotu, - je-li to jakýkoli jiný prvek a je zapnuté
inline: true, vykreslí se dovnitř, - jiný prvek bez
inlinese stane spouštěčem — viz níž, - když selektor nic nenajde, konstruktor vyhodí
Error.
Existuje i tovární funkce, když ti vadí new:
Picker na jiném prvku než inputu
Datum bývá i mimo formulář — v odznaku, v buňce tabulky, ve
<span>. Takový prvek se stane spouštěčem:
klik nebo Enter otevře panel a potvrzená hodnota se do něj vypíše.
<span id="termin" data-value="2026-08-13" data-placeholder="Nezadáno"> 📅 <b data-gr-value>13. 8. 2026</b> </span>
new Gregory('#termin', { mode: 'date' })
| Atribut | K čemu je |
|---|---|
data-value | Strojová podoba hodnoty. Odsud se čte počáteční stav a sem se po potvrzení zapisuje — ISO datum, u rozsahu od/do. Text prvku je pro lidi, ten se neparsuje. |
data-gr-value | Nepovinný potomek, do kterého se vypíše text. Bez něj se přepíše obsah celého prvku, takže by ikona nebo popisek zmizely. |
data-placeholder | Co se vypíše, když je hodnota prázdná. |
Prvek dostane tabindex="0" a role="button", pokud
je sám nemá — u <button> se nesahá na nic. Po potvrzení
prvek vyšle bublající gregory:change s hodnotou v
event.detail, takže na něj jde reagovat i bez reference na picker.
destroy() přidané atributy zase odebere.
Přehled voleb
| Volba | Typ | Výchozí | Popis |
|---|---|---|---|
mode | Mode | 'date' | Režim výběru. |
className | string | — | Vlastní třídy pro kořen panelu; takhle se aplikují motivy. |
value | DateLike | DateRange | [DateLike, DateLike] | null | Počáteční hodnota. |
locale | LocaleInput | jazyk prohlížeče | BCP 47 tag nebo přebití částí locale. |
min | DateLike | null | Nejstarší volitelný den. |
max | DateLike | null | Nejmladší volitelný den. |
firstDayOfWeek | 0–6 | dle locale | 0 = neděle … 6 = sobota. |
months | number | 2 v range, jinak 1 | Počet panelů vedle sebe. |
linkedCalendars | boolean | false | Listovat všemi panely najednou místo každým zvlášť. |
weekNumbers | boolean | false | Sloupec s ISO čísly týdnů. |
showOutsideDays | boolean | true | Zobrazovat dny přesahující ze sousedních měsíců. |
weekSelection | 'off' | 'number' | 'day' | 'both' | 'off' | Výběr celého týdne jedním klikem. |
dropdowns | boolean | 'menu' | false | Výběr měsíce a roku: nativní <select>, nebo seznam otevřený klikem na caption. |
endInput | string | HTMLInputElement | — | Druhé pole pro konec rozsahu. |
allowTyping | boolean | true | Číst datum napsané rukou do pole. |
submitName | string | { from, to } | — | Skrytá pole s ISO hodnotou pro odeslání formuláře. |
disabled | boolean | false | Zamkne picker bez ohledu na stav pole. |
lockOnReadonly | boolean | false | Zamknout i nad polem s readonly. Bez toho se picker nad readonly polem normálně otevře. |
inline | boolean | false | Vykreslit na místo místo popoveru. |
autoApply | boolean | true jen v režimu date | Potvrdit hned, bez tlačítek. |
presets | RangePreset[] | boolean | vestavěné v range režimech | Postranní zkratky. |
maxSpan | number | null | null | Nejdelší rozsah ve dnech (včetně obou konců). |
minSpan | number | null | null | Nejkratší rozsah ve dnech (včetně obou konců). |
stopAtDisabled | boolean | false | Rozsah nesmí přeskočit den zakázaný přes isDisabled. |
allowOpenRange | boolean | false | Povolí rozsah otevřený na jednom konci. |
compare | boolean | CompareKind | (range) => [from, to] | false | Srovnávací období k vybranému rozsahu. Dopočítává se, nevybírá. |
timeStep | number | 5 | Krok minut v časových režimech. |
timeUi | 'select' | 'slider' | 'input' | 'select' | Podoba ovládání času. |
minTime | string | null | null | Nejdřívější čas dne, 'HH:MM'. Včetně. |
maxTime | string | null | null | Nejpozdější čas dne, 'HH:MM'. Včetně. |
timeWindow | (date: Date) => { min?, max? } | null | — | Časové okno pro konkrétní den; přebíjí minTime a maxTime. |
fullscreenBelow | number | null | 480 | Pod touto šířkou okna se panel otevře přes celou obrazovku. |
opens | 'left' | 'right' | 'center' | 'right' | Vodorovné zarovnání popoveru. |
drops | 'down' | 'up' | 'auto' | 'auto' | Směr otevření popoveru. |
isDisabled | (date: Date) => boolean | — | Zakáže konkrétní dny. |
dayClass | (date: Date) => string | null | — | Přidá vlastní třídu dni. |
dayBadge | (date: Date) => string | null | — | Značka pod číslem dne — tečka nebo počet. |
format | (value, locale) => string | — | Vlastní text do inputu. |
summary | boolean | (value, locale) => string | false | Řádek v panelu s právě vybranými daty. |
Volby podrobně
mode
Určuje tvar hodnoty i výchozí hodnoty ostatních voleb.
| Hodnota | Vrací | Výchozí months | Výchozí autoApply |
|---|---|---|---|
'date' | Date | null | 1 | true |
'range' | DateRange | 2 | false |
'datetime' | Date | null | 1 | false |
'datetime-range' | DateRange | 2 | false |
'multiple' | Date[] | 1 | false |
'month' | Date | null | 1 | true |
'quarter' | Date | null | 1 | true |
'year' | Date | null | 1 | true |
Tlačítka v patičce
Vlevo jsou pomocné akce, vpravo potvrzení — stejné rozdělení jako v nativním kalendáři prohlížeče.
| Tlačítko | Co udělá |
|---|---|
| Dnes | Přeskočí na dnešek a vybere ho — stejně, jako by se kliklo na jeho buňku. Když je dnešek mimo min/max, je zašedlé. |
| Nyní | Totéž v režimech s časem (datetime, datetime-range), akorát nastaví i aktuální čas — srovnaný po timeStep do okna minTime/maxTime. Popisek je labels.now. |
| Vymazat | Vyprázdní hodnotu a rovnou ji potvrdí: vyšle apply s null, vyprázdní input a zavře panel. Takhle se odesílá „nic". Zašedlé, dokud není co mazat. |
| Zrušit | Zahodí rozpracovaný výběr a vrátí poslední potvrzenou hodnotu. Jen mimo autoApply. |
| Použít | Povýší výběr na hodnotu. Jen mimo autoApply. |
Prázdná hodnota má tvar null v single režimech a
{ from: null, to: null } v rozsazích.
Režimy month a year
Na filtry v přehledech („vyber měsíc") je denní kalendář zbytečný. Tyhle režimy místo dnů ukážou mřížku dvanácti měsíců, respektive dvanácti let.
new Gregory('#obdobi', { mode: 'month' }) picker.getValue() // Date(2026-08-01) — první den období
Hodnotou je první den vybraného období, takže se s ní dá dál počítat jako s běžným datem. V poli se píše „srpen 2026", respektive „2026".
Šipky listují po celých obdobích — u měsíců po rocích, u roků po stránkách po dvanácti. Stránky jsou ukotvené na násobky dvanácti, takže listování tam a zpět skončí, kde začalo.
min a max zakážou období, které do rozmezí
vůbec nezasahuje: s min: '2026-03-15' je únor zakázaný,
ale březen ne. Potvrzuje se hned, bez tlačítek, a postranní zkratky ani
čísla týdnů nedávají smysl, takže se nevykreslují.
Režim multiple
Seznam samostatných dnů, ne rozsah — rozvrhy, směny, termíny školení. Klik den přidá, druhý klik ho zase odebere.
new Gregory('#vstup', { mode: 'multiple', maxSelected: 5, }) picker.getValue() // [Date, Date, Date] — vždy vzestupně
Hodnota je pole seřazené vzestupně, bez ohledu na pořadí klikání.
Vybrané dny se malují každý zvlášť — mezi nimi nevzniká rozsah,
to je celý rozdíl proti range.
Do pole se vypíše seznam a za třetím dnem se zkrátí:
5. 8. 2026, 13. 8. 2026, 20. 8. 2026 +1. Souhrn místo toho
hlásí počet („4 dny"). Na vstupu se čeká pole:
value: ['2026-08-05', '2026-08-13'].
maxSelected omezí počet. Po dosažení limitu další den
nepřibude, ale odebrat už vybraný jde pořád — jinak by se uživatel zasekl.
Seznam se pořád doplňuje, takže „hotovo" nemá kdy nastat.
I s autoApply se hodnota průběžně potvrzuje, ale panel
zůstane otevřený, dokud ho uživatel nezavře.
autoApply
Rozhoduje o tom, kdy se rozpracovaný výběr stane hodnotou.
true— poslední klik rovnou potvrdí, patička nemá Použít / Zrušit, kliknutí mimo panel jen zavře.false— výběr žije odděleně, dokud uživatel nedá Použít. Escape, Zrušit i kliknutí mimo vrátí předchozí hodnotu.
Rozdíl je vidět na návratových hodnotách: getSelection() ukazuje
rozpracovaný stav, getValue() až potvrzený.
Tlačítko Použít se odemkne už po prvním kliknutí. Potvrzení jediného dne
uloží jednodenní rozsah — { from: 10. 8., to: 10. 8. }.
Klikat dvakrát na stejné datum není potřeba. Se zapnutým
allowOpenRange se místo toho uloží otevřený rozsah.
Zapnuté je jen v režimu date, protože jinde jeden klik hodnotu
nedokončí: rozsah čeká na druhý konec a datetime na čas. Kdyby
se datetime potvrzoval hned, panel by se zavřel dřív, než by šel
čas vůbec nastavit.
months a linkedCalendars
Ve výchozím stavu má každý panel vlastní šipky a lze jím listovat nezávisle. Panely přitom musí zůstat v chronologickém pořadí, takže ten posunutý si v případě potřeby odsune sousedy:
| Akce | Výsledek |
|---|---|
| první panel vpřed | druhý ustoupí, pokud by se překryly |
| první panel zpět | druhý zůstává, mezera se zvětší |
| druhý panel zpět | první ustoupí, pokud by se překryly |
| druhý panel vpřed | první zůstává, mezera se zvětší |
Odsun je vždy nejmenší možný. U sousedních měsíců (srpen / září) to vyjde na stejný posun, jaký jsi provedl. Když si ale mezi panely vytvoříš mezeru (srpen / listopad), zůstane zachovaná, dokud se panely nedotknou.
linkedCalendars: true vrátí spřažené chování: panely tvoří jeden
blok, listuje se jedinou dvojicí šipek na krajích a mezera mezi měsíci je
vždy nulová.
Že by from vyšlo později než to, hlídá už samotný
výběr — konce se v takovém případě prohodí. Pořadí panelů je věc přehlednosti:
nemá se stát, aby levý kalendář ukazoval pozdější měsíc než pravý.
maxSpan
Platí jen v range režimech a měří se včetně obou konců:
maxSpan: 7 dovolí sedmidenní rozsah, ne osmidenní. Jakmile je
vybraný první konec, dny mimo okno se automaticky zakážou — a to na obě
strany, protože druhý klik může jít i dozadu.
minSpan
Protějšek maxSpan, měří se stejně — včetně obou konců.
Po prvním kliknutí zešednou dny, které jsou blíž, než kolik je potřeba.
Samotný počáteční den zůstává klikatelný: je to způsob, jak výběr začít
jinde, ne pokus o nulový rozsah.
stopAtDisabled
Bez něj může rozsah zakázané dny přeskočit — kdo zašedil víkendy, chce přes ně dovolenou normálně vybrat. Zapnutý naopak hlídá, že mezi konci rozsahu není ani jeden zakázaný den: po prvním kliknutí se výběr zastaví na nejbližším zakázaném dni na obě strany. To je chování rezervačních kalendářů, kde obsazený den rozsah přerušuje.
new Gregory('#vstup', { mode: 'range', isDisabled: (d) => jeObsazeno(d), stopAtDisabled: true, })
disabled, readonly
Pole s disabled picker zamkne: panel se neotevře a napsaný
text se nečte. Volba disabled udělá totéž bez ohledu na pole
— hodí se na inline panel, který žádné pole nemá. Programové
setValue() funguje dál, zamčené je jen ovládání.
Nad readonly polem se picker otevře a datum do něj zapíše
— pole, do kterého se hodnota dostane jen z kalendáře, je běžný vzor.
Z pole se pak nic nečte, psát do něj stejně nejde. Když má být zamčené
i to, je na to lockOnReadonly: true; když má jít jen
o zákaz psaní u obyčejného pole, stačí allowTyping: false.
isDisabled a dayClass
Obě dostanou Date nastavené na půlnoc daného dne. isDisabled
se ptá až po kontrole min, max a maxSpan —
vrácené false tedy dřívější zákaz nepřebije.
new Gregory('#vstup', { isDisabled: (d) => d.getDay() === 0 || d.getDay() === 6, dayClass: (d) => jeSvatek(d) ? 'je-svatek' : null, })
fullscreenBelow
Na telefonu je popover u pole nepohodlný a u pole při kraji obrazovky se navíc nemá kam vejít. Pod zadanou šířkou okna se proto panel otevře jako dialog uprostřed obrazovky i s podkladem, na který jde kliknout.
// výchozí je 480; vypnout jde null new Gregory('#vstup', { fullscreenBelow: null })
Panel v tomhle režimu nese data-fullscreen, podklad má
třídu .gr-backdrop. Přepíná se i za chodu, takže otočení
telefonu ho přehodí bez zavření.
Nezávisle na tom se popover drží uvnitř okna — hlídá se levý i pravý okraj, takže u pole na kraji obrazovky panel nevyteče ven.
submitName
Ve viditelném poli je 13. 8. 2026 — datum pro lidi, které
server spolehlivě nepřečte. submitName vyrobí vedle něj
skryté pole s ISO hodnotou, které se odešle s formulářem:
new Gregory('#termin', { mode: 'date', submitName: 'termin', }) // odešle se termin=2026-08-13
V range režimech vzniknou dvě pole, termin_from a
termin_to. Vlastní jména jim dá dvojice:
submitName: { from: 'od', to: 'do' }
Hodnota je ISO datum, v datetime režimech
2026-08-13T14:30. Prázdný konec otevřeného rozsahu zůstane
prázdný řetězec. Pole se vkládají hned za viditelný prvek, takže spadnou
do stejného formuláře, a destroy() je zase odebere.
allowTyping
Datum se dá napsat rovnou do pole — zkušený uživatel je napíše rychleji, než naklikne. Čte se při opuštění pole nebo po Enteru.
| Napsané | Přečte se jako |
|---|---|
13. 8. 2026 | Plné datum v pořadí podle locale — en-US čeká 8/13/2026. |
1.9.2026 | Mezery a tvar oddělovače nehrají roli; pole se pak přepíše na kanonický tvar. |
13. 8. | Bez roku — doplní se letošní. |
13 | Jen den — doplní se aktuální měsíc a rok. |
13. 8. 26 | Dvojciferný rok se rozvine na 2026. |
2026-08-13 | ISO tvar projde v každém jazyce. |
13. 8. 2026 14:30 | Čas na konci se v datetime režimech použije. |
10. 8. – 14. 8. | Rozsah v jednom poli; jako oddělovač projde pomlčka, „až" i „to". |
Co se přečíst nedá, se zahodí a pole se vrátí na poslední platnou
hodnotu — nesmyslný text tedy nikdy nepřepíše to, co bylo předtím.
Nesmysl je i 31. 2., datum se nepřetáčí na březen.
Vymazání pole hodnotu vymaže.
U rozděleného pickeru (endInput) se každé pole čte zvlášť,
takže do nich jde psát nezávisle. Vypnout jde volbou
allowTyping: false, čímž se pole stane jen výstupem.
Samotné čtení je i na locale: locale.parseInput(text).
endInput
Rozsah rozdělený do dvou polí — jedno drží „od", druhé „do". Cíl
konstruktoru je to první, endInput to druhé:
<input id="od"> <input id="do">
new Gregory('#od', { mode: 'range', endInput: '#do', })
Panel otevře kterékoli z polí a pověsí se pod to, kterým se otevřel.
Vybírá se přitom jeden rozsah — teprve potvrzení ho
rozdělí: from do prvního pole, to do druhého.
Každé pole tedy nese jedno datum, ne celý rozsah s oddělovačem.
Počáteční hodnota se čte z obou polí, Vymazat obě vyprázdní a u otevřeného rozsahu zůstane druhé pole prázdné. Kliknutí do druhého pole panel nezavírá.
Mimo range a datetime-range se volba ignoruje,
stejně jako v kombinaci s inline. Volba format
se u rozděleného pickeru volá pro každý konec zvlášť, ne pro celý rozsah.
showOutsideDays
Mřížka má vždy šest řádků, takže první a poslední týden obvykle přetéká do
sousedních měsíců. Ve výchozím stavu jsou tyhle dny vidět zašedle a jdou
vybrat. showOutsideDays: false je skryje:
new Gregory('#vstup', { showOutsideDays: false, })
Buňka přitom zůstane, jen prázdná — mřížka si drží šest
řádků a panel při listování neposkakuje. Prázdná místa nesou třídu
.gr-day-empty, nejsou to tlačítka a nereagují na myš.
dropdowns
Bez této volby je v hlavičce jen text „srpen 2026". Zapnout jde dvěma způsoby:
| Hodnota | Hlavička |
|---|---|
false | Výchozí. Jen titulek a šipky. |
true | Dva nativní <select>y, trvale viditelné. |
'menu' | Titulek zůstane, ale měsíc a rok jsou tlačítka — klik na ně vyroluje seznam. |
new Gregory('#vstup', { dropdowns: 'menu', })
Seznam roků se řídí min a max, jinak sahá deset let
na obě strany, a otevře se rovnou na zobrazeném roce. Zavře ho druhý klik na
stejné tlačítko, klik kamkoli jinam v panelu, nebo Escape — ten nejdřív
zavře seznam a teprve podruhé celý picker.
Výběr ze seznamu jde stejnou cestou jako šipky, takže respektuje
linkedCalendars i pořadí panelů. Každý panel má vlastní seznam.
weekSelection
Udělá z pickeru výběr týdnů — jeden klik vybere celých sedm dní jako rozsah. Platí jen v range režimech.
| Hodnota | Chování |
|---|---|
'off' | Výchozí. Klikání po dnech jako obvykle. |
'number' | Čísla týdnů se stanou tlačítky. Vyžaduje weekNumbers. Dny se vybírají normálně. |
'day' | Klik na kterýkoli den vybere jeho týden. Čísla týdnů nejsou potřeba. |
'both' | Obojí zároveň. |
new Gregory('#vstup', { mode: 'range', weekSelection: 'day', })
V režimu 'day' se týden pod kurzorem rovnou předsvítí, takže je
před kliknutím vidět, co se vybere.
Vybraný týden se řídí zobrazeným řádkem, tedy volbou
firstDayOfWeek. V české locale to je pondělí až neděle, v
en-US neděle až sobota — číslo vlevo přitom zůstává ISO týdnem,
který se počítá vždy od pondělí.
Omezení platí i tady: týden se ořízne podle min a
max, takže z částečně dostupného týdne vybere jen použitelnou
část. Když je maxSpan menší než 7, klik se ignoruje — sedm dní
by se do limitu nevešlo.
allowOpenRange
Někdy je hodnota „od 1. 8. 2026 dál" — bez konce. S touto volbou smí jeden
konec rozsahu zůstat null:
const picker = new Gregory('#vstup', { mode: 'range', allowOpenRange: true, }) picker.getValue() // { from: Date(2026-08-01), to: null }
Chování se tím mění na třech místech:
- Potvrzení jediného vybraného dne uloží otevřený rozsah
{ from, to: null }. Bez této volby by se stejný klik uložil jako jednodenní rozsah. - V patičce přibudou tlačítka Bez začátku a
Bez konce, která příslušný konec zahodí. Zbylý konec se
přesune na správnou stranu, takže z „1. 8." udělá Bez začátku
hodnotu
{ from: null, to: 1. 8. }. - Kalendář vybarví celý zbytek směrem, kterým je rozsah otevřený — je tak vidět, co by potvrzení právě teď znamenalo.
V inputu se otevřená hodnota píše s předponou z locale:
od 1. 8. 2026, respektive do 14. 8. 2026.
Vlastní format to samozřejmě přebije.
I se zapnutým allowOpenRange se panel sám zavře až po
vybrání obou konců. Kdyby se zavíral po prvním kliknutí — které
je teď „hotové" — nešel by druhý konec vybrat vůbec. Otevřený rozsah se
proto potvrzuje výhradně tlačítkem.
Jako vstup projde { from, to: null }, dvojice
[from, null] i řetězec s prázdnou stranou:
'2026-08-01/' nebo '/2026-08-14'. Ve stejném tvaru
se hodnota zrcadlí do atributu value custom elementu.
compare
Reporty se skoro vždycky dívají na zvolené období a na to
předchozí. compare ho dopočítá z hlavního rozsahu — nevybírá
se, v mřížce se jen vyznačí pruhem pod dny:
const picker = new Gregory('#obdobi', { mode: 'range', summary: true, compare: 'previous', }) picker.on('apply', ({ value, compare }) => { nacti(value.from, value.to) if (compare) nacti(compare.from, compare.to) })
| Hodnota | Co spočítá |
|---|---|
'previous' (nebo true) | Stejně dlouhé období těsně před začátkem. |
'year' | Stejná data o rok zpět. |
'year-weekday' | Posun o 364 dní, tedy o 52 celých týdnů — sedí dny v týdnu. |
(range) => [from, to] | Vlastní výpočet. Dostane rozsah setříděný; null znamená neporovnávat. |
false | Vypnuto. Výchozí stav. |
Únor je kratší než leden, takže počítáno po dnech by „předchozí
období“ k 1.–28. 2. vyšlo na 4.–31. 1. Když výběr přesně odpovídá
celému kalendářnímu měsíci, čtvrtletí nebo roku, vrací se celý
předchozí celek. U 'year' vychází totéž z ořezu dne:
celý přestupný únor 2024 dá celý únor 2023.
Hodnota je v getCompare() a v payloadu událostí
change a apply. Se zapnutým
summary přibude i do souhrnu:
18. – 31. 8. 2026 vs. 4. – 17. 8. 2026 · 14 dní.
Spojku nese locale.labels.comparedTo.
Porovnávat jde jen v režimech rozsahu; jinde je volba bez efektu.
Srovnávací období navíc nepodléhá min, max ani
maxSpan — jsou to meze výběru, a tohle se nevybírá.
Období může vyjít i mimo ně; je na aplikaci, jak s tím naloží.
Samotný výpočet je čistá funkce a je exportovaný, takže se dá použít i mimo picker — třeba když stejné období potřebuje i dotaz na serveru. Viz Datové utility.
Čas: timeStep, timeUi, minTime, maxTime
V režimech datetime a datetime-range je v patičce
ovládání času — v range režimu zvlášť pro každý konec, s popiskem Od / Do.
timeUi | Ovládání |
|---|---|
'select' | Výchozí. Dva selecty — hodiny a minuty. Minuty jdou po timeStep, takže při kroku 15 nabídnou 00, 15, 30, 45. |
'slider' | Posuvník na hodiny a na minuty, nad nimi výsledný čas. Pohodlné na dotyku. |
'input' | Nativní <input type="time"> s odpovídajícím step, min a max. |
Posuvníky se přepočítávají za tahu a panel se přitom nepřestavuje — vyměnit
uzel pod táhlem by tažení přerušilo. Když hodina dojede na kraj okna, rozsah
minut se sám zúží; při maxTime: '18:00' tak v osmnácté hodině
zbude jediná poloha.
minTime a maxTime vymezí použitelné okno dne.
Obě hranice jsou inkluzivní:
new Gregory('#vstup', { mode: 'datetime-range', timeStep: 30, minTime: '08:00', maxTime: '18:00', })
Select hodin pak nabídne 8 až 18. V osmnácté hodině zbude jediná volba
:00, protože 18:30 by už bylo za hranicí — seznam minut se
počítá pro právě zvolenou hodinu.
Čerstvě vybraný den startuje na začátku okna, ne o půlnoci. Hodnota
vložená přes setValue() nebo value se do okna
zatáhne a zarovná na krok. Hranice zadané obráceně (minTime
pozdější než maxTime) picker prohodí místo toho, aby
znepřístupnil celý den.
timeWindow
minTime a maxTime platí pro celý picker, což
nestačí tam, kde se v sobotu otevírá jinak než ve všední den.
timeWindow dostane den a vrátí meze pro něj:
new Gregory('#vstup', { mode: 'datetime', minTime: '08:00', maxTime: '18:00', timeWindow: (d) => d.getDay() === 6 ? { min: '09:00', max: '12:00' } : null, })
Co funkce neuvede, zůstává na globálních mezích: { max: '12:00' }
zkrátí jen konec dne, začátek si drží minTime. Explicitní
null naopak mez pro ten den ruší, null místo
celého okna znamená „nic zvláštního". Každý konec rozsahu se řídí oknem
svého vlastního dne a čas, který se přesunem dostane mimo okno, se
srovná dovnitř.
summary
Přidá do panelu řádek, který slovy říká, co je právě vybrané. Hodí se hlavně u inline pickeru, kde není input, do kterého by se hodnota psala.
new Gregory('#kalendar', { mode: 'range', inline: true, summary: true, }) // „10. 8. 2026 – 16. 8. 2026 · 7 dní"
Řádek sleduje rozpracovaný výběr, ne potvrzenou hodnotu —
mění se hned při klikání. Prázdný stav hlásí „Nic nevybráno" a nese třídu
.gr-summary.is-empty. Otevřený rozsah se píše jako
od 10. 8. 2026.
Počet dní se skloňuje přes Intl.PluralRules, takže česky
vyjde 1 den, 2 dny i 7 dní správně. Skloňování
vlastního jazyka jde dodat přes locale.formatDayCount.
Vlastní znění nahradí to vestavěné:
summary: (value) => value?.to ? `Rezervace na ${dny(value)} dní` : 'Vyber odjezd'
Text je dostupný i programově přes picker.summaryText().
dayBadge
Pod číslo dne se vejde krátká značka z vlastních dat — kolik je ten den rezervací, jestli je někdo na směně, že tam něco je:
dayBadge: (date) => {
const count = rezervace.get(formatISODate(date))
return count ? String(count) : null
}
null značku nevykreslí, prázdný řetězec vykreslí jen tečku —
na „tady něco je" bez čísla. Volá se i pro dny přesahující ze sousedních
měsíců (stejně jako dayClass); jejich značka zdědí ztlumení
z .is-outside. Kdo je nechce, vrátí pro ně null.
Dny v takové mřížce se přepnou na sloupec — číslo nahoře, značka pod
ním. Řádek pro značku si drží i den, který žádnou nedostal, takže čísla
v řádku sedí na jedné lince. Značka nereaguje na myš, den zůstává
klikatelný celý. Stylovat jde přes .gr-day-badge, den se
značkou nese .has-badge.
Do výchozího čtverce 24 × 24 px se číslo se značkou vejde jen tak tak.
Když má být značka čitelnější, zvyš jen výšku buňky —
šířku sloupců drží --gr-day-size, takže kalendář zůstane
stejně široký:
.gr {
--gr-day-height: 38px;
--gr-day-badge-gap: 3px;
--gr-day-badge-size: 11px;
}
--gr-day-badge-size je zároveň velikost písma ve značce
a výška jejího řádku — tou se řídí i prázdné místo pod dny bez značky,
takže se čísla nikdy nerozejdou.
format
Přebíjí text zapsaný do inputu. Dostane potvrzenou hodnotu a rozřešené locale.
format: (value, locale) => {
if (!value || value instanceof Date) return ''
return `od ${locale.formatDate(value.from, false)}`
}
Hodnoty a formáty
Všude, kde se dá zadat datum, přijímá Gregory typ DateLike:
| Vstup | Chování |
|---|---|
Date | Použije se kopie, neplatné datum dá null. |
'2026-08-13' | Parsuje se jako lokální půlnoc. |
'2026-08-13T14:30' | Volitelná časová část, sekundy se ignorují. |
'2026-08-01/2026-08-13' | V range režimech rozsah zapsaný lomítkem. |
number | Unixové milisekundy. |
null, undefined, '' | Prázdná hodnota. |
nesmysl ('2026-02-31') | null — datum se nepřetáčí na březen. |
new Date('2026-08-13') je podle specifikace UTC půlnoc,
takže v našem časovém pásmu vyjde 13. srpna 02:00 — ale kdekoli západně od
Greenwiche to spadne na 12. srpna. Gregory proto ISO tvary rozebírá sám
a staví lokální datum.
Metody
getValue()
Potvrzená hodnota. V single režimech Date (nebo null),
v range režimech vždy objekt { from, to }, jehož položky mohou být
null.
getSelection()
Rozpracovaný výběr včetně stavu „mám první konec, čekám na druhý".
Užitečné při autoApply: false.
getCompare()
Srovnávací období k potvrzené hodnotě. null,
když se neporovnává, rozsah nemá oba konce, nebo picker není v režimu
rozsahu. Oba konce jsou vždy vyplněné — otevřený rozsah se neporovnává.
setValue()
Nastaví a rovnou potvrdí hodnotu, přesune pohled na příslušný měsíc a zapíše
do inputu. Se silent: true nevyvolá change ani apply.
clear()
Vyprázdní hodnotu i input.
setOptions()
Sloučí nové volby se stávajícími a překreslí. Hodí se na přepnutí jazyka nebo
rozšíření min/max za běhu.
goTo()
Přesune pohled tak, aby zadané datum bylo v prvním viditelném měsíci.
Výběr nemění. Vyvolá month-change.
openPanel(), close(), toggle()
Ovládání popoveru. V inline režimu nedělají nic — panel je vždy viditelný. Otevření obnoví panel do stavu poslední potvrzené hodnoty.
apply(), cancel()
Programová obdoba tlačítek v patičce. apply() povýší výběr na
hodnotu a zavře, cancel() výběr zahodí a vrátí poslední potvrzený.
formatValue()
Text, který by se zapsal do inputu — buď z format, nebo z locale.
on(), once(), off()
on() i once() vracejí funkci pro odhlášení, takže
nemusíš držet referenci na posluchače.
const odhlasit = picker.on('change', handler) odhlasit()
destroy()
Odstraní panel z DOM, odpojí posluchače na inputu i na dokumentu a zahodí všechny odběratele událostí. Volat při odstranění komponenty ze stránky.
element
Kořenový prvek panelu. Existuje i když je panel zavřený (má hidden).
Události
| Událost | Payload | Kdy |
|---|---|---|
change | { value, complete, compare } | Při každém kliknutí na den, i na první konec rozsahu. value je rozpracovaný výběr, ne potvrzená hodnota. |
apply | { value, compare } | Při potvrzení — tlačítkem, nebo automaticky při autoApply. |
cancel | { value } | Zrušit, Escape nebo kliknutí mimo panel. |
open | { value } | Otevření popoveru. |
close | { value } | Zavření popoveru. |
invalid | { value, reason } | Hodnota neprošla omezeními a picker ji nepřijal. reason je 'min' | 'max' | 'disabled' | 'maxSpan' | 'minSpan' | 'unreadable'. |
month-change | { year, month, index } | Změna zobrazeného měsíce. month je 0–11, index je pořadí panelu. |
Na apply, pokud tě zajímá jen hotový výsledek — v range režimu se
change ozve i s polovičním rozsahem. Příznak complete
říká, jestli je hodnota kompletní.
compare je srovnávací období
(viz volba compare), nebo
null — bez té volby vždycky. V change se
počítá z rozpracovaného výběru, takže po prvním kliknutí je ještě
null.
Pozor na rozdíl mezi událostmi: change nese
rozpracovaný výběr (totéž co getSelection()), zatímco
apply, cancel, open a close
nesou potvrzenou hodnotu (totéž co getValue()). Při zapnutém
autoApply je to jedno a totéž.
Custom element
Registrace (bezpečně opakovatelná, volitelně pod jiným jménem):
Element si vyrobí vnitřní <input>, nebo převezme ten,
který do něj vložíš.
Atributy
| Atribut | Odpovídá volbě |
|---|---|
mode | mode |
value | value — u rozsahu od/do |
locale | locale |
min, max | min, max |
months | months |
max-span | maxSpan |
min-span | minSpan |
stop-at-disabled | stopAtDisabled |
disabled | disabled |
first-day-of-week | firstDayOfWeek |
placeholder | placeholder vnitřního inputu |
inline | inline (přítomnost atributu) |
week-numbers | weekNumbers |
dropdowns | dropdowns |
auto-apply | autoApply |
presets | presets; presets="false" je skryje |
compare | compare; atribut bez hodnoty znamená 'previous', chybějící atribut porovnávání vypíná |
Všechny uvedené atributy jsou sledované — změna za běhu se projeví okamžitě.
Vlastnosti a události
element.value čte i zapisuje hodnotu stejně jako
getValue() / setValue(). Po potvrzení se hodnota
zrcadlí zpět do atributu value.
Události se přeposílají jako bublající CustomEvent s prefixem:
gregory:change, gregory:apply, gregory:open,
gregory:close. Data jsou v event.detail.
document.addEventListener('gregory:apply', (e) => { console.log(e.target.id, e.detail.value) })
Lokalizace
Vše jde přes Intl — knihovna nenese žádné jazykové balíčky.
Stačí tag:
new Gregory('#vstup', { locale: 'de-AT' })
Neplatný tag picker neshodí — degraduje na angličtinu.
Co jde z Intl a co ne
Názvy měsíců, zkratky dnů, první den v týdnu i formát data řeší prohlížeč, takže fungují pro jakýkoli jazyk. Slova jako „Použít" nebo „Posledních 7 dní" ale odnikud vzít nejdou — ty knihovna nese sama a má je pro:
Rozhoduje jazyk, ne region — de-AT i de-CH
dostanou němčinu. Pro ostatní jazyky se měsíce a formát data lokalizují
dál, ale popisky spadnou na angličtinu. Seznam vrací
availableTranslations().
Doplnění jazyka
Chybějící jazyk se dodá zvenčí, není potřeba forkovat:
import { registerTranslation } from '@svatekr70/gregory' registerTranslation('ja', { labels: { apply: '適用', cancel: 'キャンセル', today: '今日', now: '現在', /* … */ }, presets: { today: '今日', yesterday: '昨日', /* … */ }, days: { other: '日' }, })
Volá se před vytvořením pickeru a přepíše i jazyk, který knihovna nese.
Tvary v days se vybírají přes Intl.PluralRules,
takže čeština potřebuje one, few,
many i other, kdežto němčině stačí
one a other.
Na jednorázovou úpravu jednoho popisku stačí objekt v
locale, viz níž — registerTranslation je pro
celý jazyk napříč aplikací.
Přebití částí locale
Objekt se sloučí přes rozřešené locale, takže můžeš změnit jedinou položku:
locale: {
code: 'cs',
rangeSeparator: ' až ',
labels: { apply: 'OK' },
}
rangeSeparator platí pro obě podoby rozsahu — plnou
i zkrácenou se společným měsícem — a zároveň se podle něj rozsah čte
zpátky, když ho někdo napíše do pole.
Rozhraní Locale
| Položka | Typ | Popis |
|---|---|---|
code | string | BCP 47 tag. |
firstDayOfWeek | 0–6 | Z Intl.Locale, s tabulkovou zálohou. |
monthLabel(date) | string | Titulek panelu, např. „srpen 2026". |
monthNames() | string[] | 12 názvů měsíců pro dropdown. |
weekdayNames(first) | string[] | 7 zkratek, už otočených. |
formatDate(date, withTime) | string | Text do inputu. |
rangeSeparator | string | Oddělovač konců rozsahu. |
labels | object | Popisky tlačítek a ARIA. Mimo jiné today a now — druhý se použije v režimech s časem. |
Přímé rozřešení mimo picker: resolveLocale(input?).
Presety
V range režimech se vlevo zobrazí zkratky. Vestavěná sada: Dnes, Včera, Posledních 7 dní, Posledních 30 dní, Tento měsíc, Minulý měsíc, Letos.
range je funkce, ne hodnota — vyhodnotí se až při kliknutí.
Díky tomu picker otevřený přes půlnoc nevrátí včerejší „dnes".
presets: [
{ label: 'Tento týden', range: () => [pondeli(), nedele()] },
{ label: 'Q1', range: () => [new Date(2026, 0, 1), new Date(2026, 3, 0)] },
]
presets: false panel skryje, presets: true vynutí
vestavěné i v single režimu. Vestavěnou sadu si můžeš vyžádat i sám:
defaultPresets(locale).
Datové utility
Knihovna exportuje i funkce, které používá vnitřně. Všechny pracují v lokálním čase a porovnávají na úrovni dne.
| Funkce | Popis |
|---|---|
today() | Dnešek na půlnoci. |
startOfDay(date) | Ořízne čas. |
createDate(y, m, d, h?, min?) | Bezpečné sestavení data (řeší roky 0–99). |
parseDate(value) | DateLike → Date | null. |
formatISODate(date) | '2026-08-13' |
formatISOTime(date) | '14:30' |
addDays(date, n) | Posun o dny. |
addMonths(date, n) | Posun o měsíce s ořezem dne. |
isSameDay(a, b) | Stejný kalendářní den. |
compareDay(a, b) | -1 | 0 | 1, ignoruje čas. |
isWithinDay(d, from, to) | Inkluzivní na obou koncích. |
startOfWeek(date, first) | Začátek týdne dle prvního dne. |
isoWeekNumber(date) | Číslo týdne podle ISO 8601. |
parseTimeOfDay(value) | '08:30' → 510 minut od půlnoci. |
formatTimeOfDay(minutes) | 510 → '08:30'. |
minutesOfDay(date) | Čas data jako minuty od půlnoci. |
withTimeOfDay(date, minutes) | Přenese čas na kalendářní den. |
hourOptions(step, min, max) | Použitelné hodiny v okně. |
minuteOptions(hour, step, min, max) | Použitelné minuty dané hodiny. |
comparePeriod(range, kind?) | Srovnávací období k rozsahu; kind je 'previous' (výchozí), 'year' nebo 'year-weekday'. |
wholePeriodOf(range) | Odpovídá rozsah přesně celému měsíci, čtvrtletí, nebo roku? 'month' | 'quarter' | 'year' | null. |
resolveCompare(range, compare) | Vyhodnotí volbu compare v její plné podobě, včetně vlastní funkce. |
buildMonth(year, month, ctx) | Mřížka měsíce bez DOM — pro vlastní vykreslení. |
import { comparePeriod } from '@svatekr70/gregory' comparePeriod({ from: new Date(2026, 1, 1), to: new Date(2026, 1, 28) }) // { from: 1. 1. 2026, to: 31. 1. 2026 } — celý měsíc, ne posledních 28 dní
CSS proměnné
Motiv se ladí výhradně proměnnými na .gr. Není potřeba přepisovat
jediný selektor.
| Proměnná | Výchozí (světlá) | Význam |
|---|---|---|
--gr-font | system-ui… | Písmo panelu. |
--gr-radius | 10px | Zaoblení panelu. |
--gr-radius-day | 6px | Zaoblení dne a tlačítek. |
--gr-gap | 2px | Mezera v mřížce. |
--gr-day-size | 24px | Velikost políčka dne — čtverce, do kterého se vykreslí číslo. Určuje šířku celého panelu, takže je to hlavní páka na hustotu. |
--gr-day-height | var(--gr-day-size) | Výška políčka dne. Ve výchozím stavu kopíruje šířku, takže den je čtverec. Zvýšit se dá zvlášť — kalendář kvůli tomu nenaroste do šířky. |
--gr-day-badge-gap | 2px | Mezera mezi číslem dne a značkou pod ním (dayBadge). |
--gr-day-badge-size | 9px | Písmo ve značce a výška jejího řádku. Řádek drží i dny bez značky, aby čísla v řádku seděla. |
--gr-pad | 10px | Odsazení uvnitř panelu. Odvozuje se z něj i odsazení patičky, zkratek a tlačítek. |
--gr-font-size | 14px | Písmo zbytku panelu — hlavička, tlačítka, souhrn. Čísel dnů se netýká. |
--gr-day-font-size | 50 % políčka | Velikost čísel dnů. Odvozuje se z políčka, ne ze základního písma. |
--gr-weekday-font-size | 11px | Zkratky dnů v týdnu (Po, Út…) a čísla týdnů. |
--gr-bg | #fff | Pozadí panelu. |
--gr-fg | #1c1f23 | Text. |
--gr-muted | #8b9199 | Sekundární text. |
--gr-border | #e3e6ea | Linky. |
--gr-hover | #f1f3f5 | Pozadí pod myší. |
--gr-accent | #2f6fed | Vybraný den, primární tlačítko. |
--gr-accent-fg | #fff | Text na akcentu. |
--gr-range-bg | #e7efff | Výplň mezi konci rozsahu. |
--gr-today | #e8590c | Obrys dnešku. |
--gr-compare | #7048e8 | Pruh pod dny srovnávacího období (compare). Třetí barva schválně — musí být rozeznatelná od výběru i od dneška. |
--gr-compare-bar | 2px | Tloušťka toho pruhu. |
--gr-shadow | … | Stín popoveru. |
.gr {
--gr-accent: #0f766e;
--gr-range-bg: #ccfbf1;
--gr-radius-day: 999px;
}
Panel je vždy jen tak široký, jak potřebuje — šířku určuje
--gr-day-size, ne dostupné místo. Kompaktnější kalendář se
tedy dělá zmenšením políčka dne, ne přebíjením rozměrů panelu:
Hustotu drží čtyři proměnné: velikost dne, mezera v mřížce, odsazení
a velikost písma. Ostatní odsazení se z --gr-pad dopočítává,
takže stačí přenastavit je:
/* hustší než výchozí */ .gr { --gr-day-size: 21px; --gr-gap: 0; --gr-pad: 7px; --gr-font-size: 13px; } /* jen větší čísla dnů, zbytek beze změny */ .gr { --gr-day-font-size: 16px; }
Do kontejneru užšího, než je jeden měsíc, se panel nevejde — a protože
mřížka má pevnou velikost políčka, není co zmenšovat. Takový panel se
proto odroluje vodorovně, ne ořízne: poslední sloupce
dnů zůstanou dosažitelné. Nejmenší rozumná šířka je zhruba
7 × --gr-day-size + 2 × --gr-pad, u dvou měsíců dvojnásobek
a u zapnutých presets ještě postranní panel navrch.
themes.css nese třídy gr-density-compact a
gr-density-comfortable. Barvy neřeší, takže se s motivy
kombinují: className: 'gr-theme-riso gr-density-compact'.
Výchozí hustota je kompaktní — jeden měsíc vyjde na 196 × 268 px, tedy
znatelně méně než nativní kalendář prohlížeče. Rozsah se dvěma měsíci
a čísly týdnů má 443 × 219 px, v compact 379 × 188 px a
v comfortable 622 × 302 px.
24 px je pod doporučenými 44 px pro dotykové cíle. Na mobilu tedy
buď zvyš --gr-day-size, nebo použij
gr-density-comfortable.
Šířku sloupců řídí --gr-day-size, výšku buňky
--gr-day-height — a ta z první ve výchozím stavu vychází,
takže den je čtverec. Kdo pod čísla vykresluje značky
(dayBadge), zvýší jen --gr-day-height
a panel si nechá stejně široký.
Den nemá žádný vnitřní padding — číslice je vycentrovaná ve čtverci
o hraně --gr-day-size. Kolik okolo ní zbude místa, tedy
určuje poměr písma k políčku. Zmenšit políčko a zároveň zmenšit písmo
proto prázdno nezmenší.
Proto se --gr-day-font-size odvozuje
z velikosti políčka (50 %) a ne ze základního písma. Plnější dojem se dělá vyšším
poměrem, vzdušnější nižším:
/* plnější */ .gr { --gr-day-font-size: calc(var(--gr-day-size) * 0.6); } /* vzdušnější */ .gr { --gr-day-font-size: calc(var(--gr-day-size) * 0.42); }
Pozor jen na to, že buňka má pevný rozměr — poměr nad zhruba 0,6 už z ní dvojciferná čísla vytlačí.
Hotové motivy
Volitelný soubor themes.css nese čtyři připravené motivy.
Načítá se až po základních stylech:
import '@svatekr70/gregory/style.css' import '@svatekr70/gregory/themes.css' new Gregory('#vstup', { className: 'gr-theme-riso' })
| Třída | Charakter |
|---|---|
gr-theme-blueprint | Technický výkres — tmavě modrá, monospace číslice, hustá mřížka. |
gr-theme-riso | Dvoubarevný tisk — papír, fluorescentní růžová, posunutý stín místo rozostřeného. |
gr-theme-clinic | Objednávkový systém — vzdušná bílá, modrozelená, vybrané dny jako pilulky. |
gr-theme-nocturne | Noční provoz — skoro černá s teplým jantarem. |
Motivy nepřepisují jediný selektor komponenty — mění jen proměnné
--gr-*. Vlastní motiv se dělá stejně: zkopíruj kterýkoli z nich
a přebarvi. Posunutý stín u riso je vidět jen u popoveru; inline
panel stín zásadně nemá, protože sedí uvnitř cizího rámečku.
className
Panel si vyrábí knihovna sama a v režimu popoveru ho věší na
<body>, takže se k němu nedá dostat přes obalový prvek.
className je proto jediná cesta, jak mu přidat vlastní třídu —
motivy toho využívají, ale hodí se na jakékoli cílení.
Tmavý motiv naskočí sám podle prefers-color-scheme. Natvrdo se
přepíná atributem na kořenovém prvku pickeru:
data-theme="dark" nebo data-theme="light".
CSS třídy
| Třída | Prvek |
|---|---|
.gr | Kořen panelu. Nese data-mode, data-inline / data-popover. |
.gr-presets, .gr-preset | Postranní panel a jeho tlačítka. |
.gr-calendars, .gr-calendar | Obal měsíců a jeden měsíc. |
.gr-head, .gr-nav, .gr-caption | Hlavička s šipkami a titulkem. |
.gr-select | Nativní dropdown měsíce a roku (dropdowns: true). |
.gr-caption-btn | Měsíc a rok jako tlačítka (dropdowns: 'menu'). |
.gr-menu, .gr-menu-item | Vyrolovaný seznam; aktuální položka má .is-current. |
.gr-grid | Mřížka dnů, role="grid". |
.gr-weekday, .gr-weeknum | Záhlaví dnů a sloupec čísel týdnů. |
.gr-weeknum-button | Číslo týdne při zapnutém selectableWeeks. |
.gr-day | Jeden den. Stavy: .is-outside, .is-today, .is-selected, .is-in-range, .is-start, .is-end, .is-weekend, .is-compare se .is-compare-start a .is-compare-end. |
.gr-foot, .gr-times | Patička a blok s časem. |
.gr-time-group, .gr-time-label | Jeden konec rozsahu a jeho popisek (nese data-bound). |
.gr-time, .gr-time-select, .gr-time-sep | Obal času, selecty hodin/minut a dvojtečka mezi nimi. |
.gr-time-sliders, .gr-slider, .gr-time-readout | Posuvníky a vypsaný čas nad nimi. |
.gr-summary | Informační řádek; prázdný stav má .is-empty. |
.gr-actions, .gr-btn | Tlačítka Vymazat / Zrušit / Použít. |
Klávesnice
| Klávesa | Akce |
|---|---|
↓ na inputu, Enter | Otevře panel. |
← → | O den zpět / vpřed. |
↑ ↓ | O týden zpět / vpřed. |
PageUp PageDown | O měsíc zpět / vpřed. |
Enter, Mezerník | Vybere zaměřený den. |
Esc | Zruší rozpracovaný výběr, zavře panel a vrátí fokus do inputu. |
Pohyb fokusu respektuje min a max a sám odroluje na
měsíc, do kterého fokus utekl.
TypeScript typy
Exportované typy: Mode, WeekDay, DateLike,
DateRange, GregoryValue, GregoryOptions,
ResolvedOptions, GregoryEvents, Locale,
LocaleInput,
RangePreset, DayCell, WeekRow,
MonthView, MonthContext,
ClosedDateRange, CompareKind,
CompareInput, CompareResolver,
WholePeriod.
import type { DateRange, GregoryOptions } from '@svatekr70/gregory' const volby: GregoryOptions = { mode: 'range', maxSpan: 31 }
Posluchače událostí typuje GregoryEvents, takže payload zná
i editor:
picker.on('month-change', ({ year, month }) => { /* number, number */ })
Okrajové chování
Inkluzivní konce
Rozsah 10.–12. srpna jsou tři dny. Platí to i pro maxSpan
a pro isWithinDay().
Pozpátku vybraný rozsah
Když uživatel klikne nejdřív na pozdější den, Gregory konce sám otočí —
from je vždy dřívější.
Přetečení měsíce
addMonths(31. ledna, 1) je 28. února (v přestupném roce 29.),
ne 2. nebo 3. března.
Celek se porovnává s celkem
Výběr, který přesně odpovídá kalendářnímu měsíci, čtvrtletí nebo roku,
má u compare: 'previous' za protějšek celý předchozí celek,
ne posledních n dní. Tři měsíce, které nesedí na hranice
čtvrtletí, jsou pořád jen úsek dnů.
Čas při změně dne
V datetime režimech se čas váže ke konci rozsahu a přežije
překliknutí na jiný den.
Stálá výška panelu
Každý měsíc se vykresluje na šest řádků, takže popover při listování
neposkakuje. Přebytečné dny mají .is-outside.
Vykreslování
Panel se při změně stavu staví znovu přes replaceChildren().
Žádný diffing — u 42 buněk na měsíc je to rychlejší než ho udržovat.
Výjimkou je náhled pod myší, který jen přepíná třídy na existujících
tlačítkách: přestavět DOM pod kurzorem znamená ztracené kliknutí, protože
prohlížeč vyvolá click jen tehdy, když stisk i puštění skončí
na tomtéž prvku.
Odolnost vůči stylům stránky
Panel používá běžné značky (section, header,
footer, aside, button…), takže si
je hned na začátku vynuluje pravidlem s nulovou specificitou přes
:where(). Nuluje se typografie i rozvržení:
globální section { padding: 52px 0 } ani
header { flex-direction: column } panel nerozhodí — to
druhé jinak postaví šipky nad a pod název měsíce, protože
.gr-head si sám nastavuje jen display: flex.
Přitom pořád stačí přepsat kteroukoli vlastnost bez souboje
o specificitu.
Dvě vlastnosti se schválně nenulují: display, protože
přepsat ho pro všechny značky najednou by rozbilo tlačítka i inputy,
a text-align, protože čísla dnů mají vycentrování
od prohlížeče.