@feugene/granularity-chrono

Calendar, date and time components for the @feugene/granularity design system — no third-party date widget, no date library.
2,790 1
Install
npm install @feugene/granularity-chrono
Latest Version:2.0.2
Vue:3
License:LicenseRef-Granularity-EUC-1.1
Last Updated:Oct 22, 2026
Links: GitHub  ·  npm
Maintainer: efureev

@feugene/granularity-chrono

Calendar, date and time components for the @feugene/granularity design system.

A first-class part of the design system rather than a themed wrapper: the date grid, the keyboard and the formatting are ours, so GrConfigProvider, GrFormField, the size scale, the overlay stack and the i18n adapter all apply the way they do to every other control.

No runtime dependencies. Date arithmetic is a few hundred lines of plain integer maths; everything locale-dependent — the first day of the week, month and weekday names, the 12/24-hour default — comes from Intl. Installing this package pulls in no date library and no third-party widget.

Granular imports, in numbers

What you import gzip of the barrel
the whole package from the root 42.1 kB 100 %
the lightest component — GrDuration 3.0 kB 7 %
the median component — GrTimePicker 14.1 kB 33 %
the 5 heaviest together 33.9 kB 81 %

These numbers do not add up: shared code is counted again in every row but paid for once, which is why the set is shown as a union rather than a sum. They are an upper bound — the gzip of everything a subpath pulls out of dist, before the application bundler shakes it further and minifies it again.

The weight of every component — docs/entry-sizes.md.

Documentation

  • docs/model.md — the value: why Date lives only at the boundary, how to pick a valueAdapter, what the public arithmetic offers, how relative time picks its unit and its tick
  • docs/keyboard.md — keyboard contract per component: the grid, the time columns, the field, the range
  • docs/theming.md — own --gr-* tokens, what may and may not be overridden, how the CSS is assembled
  • docs/ssr.md — server rendering: the single source of mismatch and how to remove it

Why not wrap an existing picker

The predecessor (@feugene/granularity-datepicker) wrapped @vuepic/vue-datepicker, and its failures were structural rather than incidental: the theme never reached the widget because the library redefines the same CSS variables one level deeper; id, name and the accessible name could not be set because the <input> belonged to the library; and the prop contract drifted on every major of the dependency without anything failing the build.

Each of the three is structural: none could be fixed without owning the widget, which is what this package does.

Status

Six components — GrCalendar, GrDatePicker, GrTimePicker, GrDateTimePicker, GrDateRangePicker and GrRelativeTime — plus the public arithmetic and the shared clock (useChronoNow). What each one is for, and where the line to its neighbour runs, is in docs/components.md; what has shipped so far is in CHANGELOG.md.

Install

yarn add @feugene/granularity-chrono
# peers you already have with granularity:
#   @feugene/granularity  @feugene/granum  vue

Localization

The package ships UI strings only — labels, aria-labels, panel titles. Month and weekday names, the first day of the week, the 12/24-hour default and the parsing order all come from Intl, so the components speak every language the engine knows, not the ones we listed.

Strings live in their own block (grChrono) and plug into the same fint-i18n instance as the core's — one i18n layer for the whole app:

import { createFintI18n } from '@feugene/fint-i18n/core'
import { installI18n } from '@feugene/fint-i18n/vue'
import { GRANULARITY_I18N_BLOCK, en as coreEn, ru as coreRu } from '@feugene/granularity/i18n'
import { GR_CHRONO_I18N_BLOCK, en, ru } from '@feugene/granularity-chrono/i18n'

const i18n = createFintI18n({
  locale: 'ru',
  fallbackLocale: 'en',
  loaders: [coreEn, coreRu, en, ru],
})

i18n.registerBlocks([GRANULARITY_I18N_BLOCK, GR_CHRONO_I18N_BLOCK])
await i18n.loadUsedBlocks('ru')
installI18n(app, i18n)

Need every language at once (demos, e2e) — import the aggregate instead: @feugene/granularity-chrono/i18n/all. Skip the wiring entirely and the components still work: every key has an English fallback compiled into the component.

granum provider

List the package alongside granularity so its classes, CSS and tokens reach the application's CSS:

// granum.config.ts
import { defineGranumConfig } from '@feugene/granum/vite'
import { windEngine } from '@feugene/granum-engine-wind'

export default defineGranumConfig({
  engine: windEngine(),
  providers: ['@feugene/granularity', '@feugene/granularity-chrono'],
  components: ['@feugene/granularity-chrono:GrCalendar'],
  appSources: { dirs: ['src'] },
})

Both packages go in by name, not as imported objects: each one ships its own granum.manifest.json, and granum reads it instead of scanning the package's dist. The core arrives as a donor of the selected components anyway — the chrono manifest names it — but the application still has to list it to get its theme and base layers.

Auto-import (unplugin-vue-components)

// vite.config.ts
import Components from 'unplugin-vue-components/vite'
import { GranularityResolver } from '@feugene/unplugin-granularity'

export default defineConfig({
  plugins: [
    Components({ resolvers: GranularityResolver() }),
  ],
})

GranularityResolver() (@feugene/unplugin-granularity >=1.1.0) finds this package in the application's package.json and reads its exports: the core and every companion resolve through one resolver, in any order. The package's own GranularityChronoResolver() from ./resolver still works for existing configs.

Registries

The component lists in src/index.ts, package.json#exports, src/granular-provider/shared.ts and src/componentNames.ts are generated:

yarn generate:registry          # synchronise
yarn generate:registry --check  # report drift, write nothing

Never edit inside a // <granularity:components> block — the next run overwrites it. The machinery is shared across provider packages and lives in @feugene/granum/codegen.

Component entries are not on that list: granumProvider() builds them from the same provider registry, so vite.config.ts has one fewer place to fall out of sync.

Adding the very first component takes one manual step. package.json carries no markers, so the generator replaces the contiguous run of ./components/* keys in place — and with no such key yet it cannot decide where the run belongs. Add one entry by hand alongside the first component; every one after that is generated. The generator says as much when it stops.

yarn test:run runs the same --check and fails on drift, so a forgotten generate:registry never reaches CI.

License

SEE LICENSE IN LICENSE (inherits the monorepo license).

Related Packages

@composy/datepicker-vue

LDesign DatePicker Vue Component

0
vue-material-date-time-picker

A simple Vue.js date and time picker made with material design in mind

1,424 7
tedir-calendar

Tedir Calendar is a Vue 3 & Web Component Event Calendar

647 2
@custom_okoshko/vue

Vue 3 wrapper for @custom_okoshko/core calendar.

163
@tiaohsun/vue-datepicker

A Vue 3 datepicker with multi-calendar support and a self-contained theme (no Ta...

1,933 0

Version History

Version Released Vue License
2.0.2 ^3.5.0 LicenseRef-Granularity-EUC-1.1
2.0.1 ^3.5.0 LicenseRef-Granularity-EUC-1.1
2.0.0 ^3.5.0 LicenseRef-Granularity-EUC-1.1
1.1.0 ^3.5.0 LicenseRef-Granularity-EUC-1.1
1.0.6 ^3.5.0 LicenseRef-Granularity-EUC-1.1