@feugene/granularity-chrono
Записи релизов — авторский текст библиотеки, и он английский. Портал его не переводит: журнал изменений — история чужого репозитория, а переписанная история перестаёт быть историей.
v0.10.02026-08-27
Changed
-
Peer floors on
@feugene/*raised to the current minor. Every peer this package declares on the ecosystem now starts at the version the monorepo actually ships:@feugene/fint-i18n→>=0.7.0 <1.0.0@feugene/granularity→>=0.36.0 <1.0.0@feugene/unocss-preset-granular→>=0.13.0 <1.0.0@feugene/unplugin-granularity→>=0.7.0 <1.0.0
The floors had drifted far behind — some still admitted releases from a year of development ago — and a range that claims support it was never tested against is worse than a narrow one: the install succeeds and the breakage surfaces later, in the consumer’s app.
This is breaking for anyone below a floor. Installing against an older
@feugene/granularitynow produces a peer conflict instead of silence. The fix is to move the core up; nothing in this package’s own API changed.
v0.9.12026-08-23
Fixed
- The i18n docblock named the wrong block. It said keys live under
gr, «like the core», while the package declaresgrChrono(GR_CHRONO_I18N_BLOCK) and everything else — README, loaders, the integration gate — agrees with that. A consumer who trusted the comment registeredgrand got no chrono translations, with a failure quiet enough to miss: the calendar grid still reads correctly because it comes fromIntl, only panel labels andaria-labels stay English.
v0.9.02026-08-20
Added
-
Manual input in
GrDateTimePickerandGrDateRangePicker.editablenow works on all four pickers. One string describes two values here — a date with a time, or two bounds — and that is a parser of its own, which is why these two came last.Parsing is driven by
Intl, not by a pattern string. Which half of the string is the date and which is the time, in what order the parts come and what separates them is known by the locale:viputs the time first (15:30 12/8/26),koputs the day period before the hour (오후 3:30),en-USafter it. Separators themselves are never matched — digit groups are counted instead. That is what makesen-CA, where the date is itself written with hyphens (2026-08-12 - 2026-08-14), split correctly; a list of range separators would have broken on the first such locale.For the range, the string is split in half by digit-group count and each half is parsed as a bound. Reversed order is normalised, exactly as clicking backwards is. A single date is rejected: one date is not a period, and inventing the other bound would make up something the user never entered. Time is accepted precisely when the picker shows it — required on both bounds with
enable-time, refused without it. A parsed but disallowed period (disabledDates,min,max,minRange,maxRange) is not applied and is announced: the input was well formed, and silence would read as a lostEnter.For the datetime picker, a bare date keeps the model’s time — what was not typed does not change. Typed text commits the model directly, bypassing
autoApply: that prop governs the panel, where selection is multi-step, whileEnterin the field is already a finished action.Neither field masks input: a mask would have to guess where the date ends and the time begins, and fight the caret on deletion.
-
The panel follows what is being typed. In all four editable pickers, a fully typed date is highlighted in the grid and the grid moves to its month; a typed hour is highlighted in the hours column, minutes in the minutes column. The model is untouched until
Enteror blur — this is a preview, not a commit. Parts that were not typed keep their current value.Typing into a panel that still shows the previous value is exactly the blind spot a text field was supposed to remove, so the draft is parsed on every keystroke: completely for the date, part by part for the time.
Fixed
- An editable field now shows what it can parse back. With
editable, values are rendered as digits (08/12/2026) instead ofAug 12, 2026. Editing a number in place used to leave the parser two digit groups instead of three, so the edit was silently rolled back although the user had done nothing wrong. An explicitformatstill wins. This also affectsGrDatePicker, where the same defect had been present sinceeditablewas introduced. - A disallowed date is no longer accepted as text.
disabledDates,minandmaxnow apply toEnterin the field the same way they apply to a click, in all three editable pickers. The grid refuses such a day; the field used to take it. - A time column now scrolls to a selection that changed while it was open. Scrolling happened
only when the panel opened, so a value set from anywhere else — typing in the field, an external
v-modelwrite — stayed off-screen and the highlight was useless. Jumping to a new value also centres it instead of stopping at the nearest edge: an hour pinned to the bottom of the column reads as “nothing below”. Stepping with arrow keys still stops at the edge, so the list does not jump under the hand. - Clicking an editable field no longer moves focus into the panel. The panel opens, as before,
but focus stays where the user put it: they came to type. Keyboard opening (
↓) still moves into the grid — that is what it means. - Quick ranges honour
enable-time. A footer preset committed two midnights while the same two dates picked by hand got 00:00 and the end of day, so “last 7 days” quietly dropped the final day.
v0.8.02026-08-20
Added
-
enable-timeonGrDateRangePicker— a window, not two midnights. A shift from 8:00 Monday to 20:00 Wednesday, a maintenance window, a two-day room booking: until now this had to be assembled from separate fields, because the range picker gave two midnights and the datetime picker a single moment.A freshly picked pair gets 00:00 and 23:59 — the whole span of days. The end lands on the same grid as the columns, so
minute-step="15"gives 23:45: otherwise the minutes column would have no selected option and the end could not be read where it is edited. At the default step of a minute it is exactly 23:59. Two midnights would look symmetrical, but “1 to 3 August” means all of the third to a person, and[1 Aug 00:00, 3 Aug 00:00)quietly drops almost all of it: the classic reporting bug. Withenable-secondsthe end gets 23:59:59. Both values are visible and editable.No four-step wizard, which is what the spec had assumed this feature would need. Dates are picked with the same two clicks as before and the times are edited whenever, in any order. Stepping would introduce hidden “which step are we on” state that a person who mis-clicks can only escape by walking the whole thing again.
The panel no longer closes on the second date when time is on: selection is not finished there, and closing would take away the very columns the prop was enabled for.
Inside a single day only the time keeps the ends in order. An edit that would put the end before the start is not applied and is announced — the same way a disallowed length is refused today: the user simply mis-aimed, and there is nothing to reset.
minRangeandmaxRangestill count days. Changing the unit of existing props would break consumers; a minute-level limit isminDuration/maxDuration, and those arrive when asked for.minuteStep,secondStep,enableSecondsanduse12Hourscarry the same names they have onGrDateTimePicker, so the two pickers do not drift apart.
v0.7.02026-08-20
Added
-
multipleonGrDatePicker— a set of dates, not a range. Class schedules, exception days, booked dates: an arbitrary set where adjacency means nothing. Until now this had no expression at all —GrDatePickerheld one date andGrDateRangePickertwo ends of a continuous span, leaving the application with its own grid or a list of fields.Clicking a selected date removes it: a set is a toggle, not an accumulator, or there would be no way to undo a mistaken pick. The panel stays open — a set is accumulated, whereas a single date is chosen once, and closing after the first click would turn ten dates into ten openings.
The model always arrives sorted ascending, wherever you clicked. It has to be comparable: reordering must not read as a change, or “unsaved edits” fires on nothing.
Manual entry is off in this mode even with
editable. One string describing N dates is a separate parser with its own behaviour on partial input, and the package does not have it yet.The field shows the first three dates and the rest as a count (“and 2 more”) — without a cap the label overflows by the fifth. The form receives one hidden field per date, sharing a name, as
FormData.getAllreads them.min,maxanddisabledDatesapply per date. -
selectedDatesonGrCalendar— painting only, exactly likerangeStart/rangeEnd. The grid answers one question, how to colour a cell; adding, removing, sorting and length rules belong to the picker. Membership is tested against aSetrebuilt once per set change: walking the array for each of the forty-two cells would make highlighting quadratic. -
separatoronGrDatePicker— what joins the dates of a set in the field.
v0.6.02026-08-20
Added
-
GrTimePicker’s footer hands the selection inward. Thefooterslot now receivesselect,canSelectandclose— the same trio the date pickers already pass — so an application can finally put a “now” button there. Until now the slot took no props at all: there was neither a way to pick a time nor a way to ask whether one was allowed.Time snaps up to the step: 14:37 with a 15-minute step gives 14:45, not 14:30. A time in a picker almost always means “starting from this moment” — a booking, a reminder, an appointment — and a value rounded down has already passed. This is the mirror of the columns, which round down when displaying an existing value; there the task is the opposite, not to invent a slot the column does not have.
The coarsest declared step wins and finer units zero out, so
minuteStep: 15yields14:45:00rather than14:45:37. With seconds switched off they stay out of the value too — otherwise the model would carry what was never on screen.Bounds are checked after snapping, not before. With
maxat 14:40 and a 15-minute step, a “now” of 14:37 snaps to 14:45 — already past the bound — andcanSelectreturnsfalse. The button arrives disabled instead of silently doing nothing; checking before the snap would have let it through. -
quartermode inGrCalendarandGrDatePicker— four cells in two columns (three would leave one cell alone on the second row). The value is the first day of the quarter. Labels come from the package’s own locale strings:Intldoes not name quarters at all, andQ1vs1 кв.is interface text rather than locale-dependent data. -
weekmode — drawn with the day grid, not the period grid. Twelve cells in three columns would be twelve weeks on screen: a quarter of a year without a single month label, with nothing to aim at. Instead, clicking any day selects the week it belongs to and the whole row highlights.The value is the start of the week, so the shape of the model stays the same across all five modes and
valueAdapterkeeps working as before. The first day comes from the locale, as everywhere else — in the US the same date falls in a week starting on Sunday. Week numbers are not shown: ISO and US number them differently andIntldoes not provide them. -
startOfWeek(date, firstDayOfWeek)andceilToStep(time, stepSeconds)are exported — the twins ofleadingOffset(which was internal) andfloorToStep.
Fixed
GrTimePicker’s footer had no container of its own. Slot content sat flush against the columns and read as their continuation rather than a separate action. It now gets the same wrapper the date pickers’ footer uses — a hairline rule and spacing — so the two pickers look alike, which was the point of giving the slot the same props.
v0.5.02026-08-19
Added
- Quick ranges live in the picker panel.
GrDateRangePickerandGrDatePickergained apresetsprop — a row of shortcuts in the panel footer: «Today», «Last 7 days», «This month». Bounds may be given as a function, so «last 7 days» counts from today rather than from the day the prop was declared. A shortcut whose range falls outsidemin/max, touchesdisabledDatesor breaksminRange/maxRangearrives disabled: a button that does nothing lies about what it does. The docs used to redirect this toGrSegmentednext to the field, and for a single date that still holds — but a range shortcut has to set both bounds and respect the length limits, which is knowledge only the picker has. - The
footerslot now carries the selection.GrDateRangePickerpassessetRange,canSetRangeandclose;GrDatePickerandGrCalendarpassselect,canSelect(andclosefor the picker). A custom footer replaces the preset row entirely and gets exactly the same rules, so it cannot select a value the grid would refuse. GrDuration— how long it lasted, not when it happened. «2 h 30 min» from a number of seconds, from a pair of dates, or counted live from a starting moment. Unit names and numeral agreement come fromIntl, so the component ships no strings of its own; the markup is<time>with an exact ISO 8601 duration indatetimeeven when the text is shortened.- Duration arithmetic is public:
selectDurationParts,formatDuration,durationToIsoandresetDurationFormatCache. Units stop at days — months and years are calendar-bound, and deriving them from a count of seconds cannot be done without lying.maxUnitsis a ceiling rather than a quota (exactly two hours reads «2 h», not «2 h 0 min»), and the smallest shown unit is truncated rather than rounded, so the display never runs ahead of the time that actually passed. WhereIntl.DurationFormatis missing — the package declares Node>=22, which has no such API, and the SSR harness runs in Node — the same string is assembled fromIntl.NumberFormatwithstyle: 'unit'.
Changed
- The panel footer moved out of the calendar grid into the picker panel. Rendered inside
[data-gr-calendar]it had no padding of its own: the panel hands its background and spacing to the calendar. Anything already using thefooterslot ofGrDatePickerorGrDateRangePickernow sits one level higher in the DOM.
v0.4.02026-08-19
Changed
- Control-scale font sizes now ship a paired line height. The calendar, the time picker and
the shared picker field set the matching
leading-*next to every control font size, from the core’s new--gr-control-leading-*steps. Before this the line height came from the host application’sbodyas an absolute value, so a 12px picker caption inherited whatever the host had set. Requires core>=0.27.0.
Added
weekStartcan now be set once for the whole application through<GrConfigProvider :component-defaults="{ GrCalendar: { weekStart: 7 } }">. The prop existed on the calendar and on all three pickers, but only per instance — an application whose working week starts on Sunday regardless of interface language had to repeat it at every call site, and miss one to get two different calendars on one screen. The key isGrCalendarand there is deliberately no per-picker one: the pickers render that same calendar, so a second key would be two names for one setting. Resolution order is the usual one — a prop on the spot beats the config, the config beats the locale — and with neither setIntlstill decides, which is what makes the default right in every country without anybody configuring anything.
Fixed
showWeekNumberswas typed as configurable and was not.GrCalendardeclared it in its defaults registry, so the key type-checked and the IDE suggested it, but the component readprops.showWeekNumbers ?? falseand never looked at the provider. Nothing failed: the setting was simply ignored, silently, which is the worst way for a contract to be broken. The gate did not catch it either —defineComponentDefaultsGatechecks that a declaration exists, not that the component reads it.- The pickers overrode that setting even once it worked.
GrDatePicker,GrDateRangePickerandGrDateTimePickerdefaultedshowWeekNumberstofalserather thanundefinedand passed that value straight down, so an explicit “no” from the picker beat the application’s “yes” from the provider. They now pass the prop through untouched and let the calendar resolve it.
Более ранние версии
Тела этих релизов не показываются: лента целиком весит больше, чем страница имеет право весить. Файл в репозитории всегда полон.
v0.3.12026-08-18v0.3.02026-08-18