@featherk/composables
Vue 3 composables that improve Kendo UI for Vue behavior and accessibility.
Composables Overview
- Address:
useUSAddress— Orchestrates full US address form behavior, including delayed validation (viauseFieldsetValidationKit), state options, ZIP integration, and normalized submit payloads.- Guide: docs/address/useUSAddress.md
- Address:
useZipTextBox— Dynamic multi-mask ZIP code input (5-digit and ZIP+4) for Kendo VueTextBox, with caret preservation, submit-gated validation, and focus-on-error support. - Date:
useMaskedDateInput— Masked single-date input with steppers and validation. - Form (Low-level):
useFieldsetTouchTracker— Primitive focus-leave touch tracker that powers the validation kit. - Form:
useFieldsetValidationKit— Delayed fieldset validation kit with submit/touch gating, field helpers, invalid-focus targeting, and reset handling. - Grid:
useGridA11y— Accessible row-level navigation and column menu keyboard support.- Guide: docs/grid/useGridA11y.md
- Grid:
useGridActiveFilter— Reactive header class resolver that marks filtered columns as"active". - Grid:
useGridRowAction— Safe, accessible row click and keyboard activation handlers for grid rows with interaction and selection guards. - ID:
useCompositeId— SSR-safe component-scoped DOM ID generation for related controls and ARIA relationships.- Guide: docs/id/useCompositeId.md
- Menu:
usePopupMenu— Accessible Kendo Popup + Menu action-menu toggling, selection modality, and contextual focus restoration.- Guide: docs/menu/usePopupMenu.md
- Grid orchestration overview: docs/menu/usePopupMenuGridOrchestration.md
- Activation anchors: docs/menu/usePopupMenuActivationAnchor.md
- Shared instances: docs/menu/usePopupMenuSharedInstance.md
- Active-ID registry lifecycle: docs/menu/usePopupMenuAndActiveIdRegistry.md
- Focus and exclusivity: docs/menu/usePopupMenuFocusAndExclusivity.md
- Upgrade to 0.13.0: docs/menu/usePopupMenuUpgrade0130.md
- Observer:
useIntersectionObserver— Reactive intersection observation with optional clipping-root tracking and SSR-safe no-op behavior. - Range:
useMaskedDateRangeInput— Masked date range input with validation, clamp, and optional popup coordination. - Registry:
useActiveIdRegistry— Shared active-id bookkeeping for single-open patterns across many grid rows/buttons. - Registry:
useExclusiveGroup— Lifecycle-managed mutual exclusivity for active-id registries. - Time:
useMaskedTimeInput— Masked single-time input with AM/PM conveniences and bounds. - Trap:
usePopupTrap— Focus trap + Escape/Outside click close behavior for popups.- Guide: docs/trap/usePopupTrap.md
- Window:
useWindow— Zero/low-config KendoWindowwrapper: viewport-clamped dragging and resizing, stage transitions, and an internal pointerdown-capture resize/text-selection workaround.- Guide: docs/window/useWindow.md
- Window:
useWindowDrawer— KendoWindowas a drawer docked to the right edge: full viewport height, covers the page, modal with a focus trap by default, slides in and out, resizes only from its left edge.
Utility Export
computeCollisionOffset is a pure utility export, not a Vue composable. It applies per-axis fit or flip collision behavior and is available from @featherk/composables/geometry or the package root.
Importing Composables
Import from the feature subpath. Each subpath is a separate entry that loads only its own composables, which also keeps the dev server fast. The package root re-exports every feature; see What Gets Installed vs. What Ships. Don't import internal dist paths.
Subpath imports (recommended):
// Grid
import {
useGridA11y,
useGridActiveFilter,
useGridRowAction,
type RowActionContext,
} from "@featherk/composables/grid";
// Date input
import {
useMaskedDateInput,
type ChangePayload,
} from "@featherk/composables/date";
// Date range input
import {
useMaskedDateRangeInput,
type RangeChangePayload,
} from "@featherk/composables/range";
// Time input
import {
useMaskedTimeInput,
type ChangePayload,
} from "@featherk/composables/time";
// Popup focus trap utilities
import { usePopupTrap } from "@featherk/composables/trap";
// Kendo Popup + Menu action-menu utilities
import {
usePopupMenu,
type KendoMenuSelectEvent,
} from "@featherk/composables/menu";
// Shared active-id bookkeeping for single-open grid or list patterns
import {
useActiveIdRegistry,
useExclusiveGroup,
} from "@featherk/composables/registry";
// Intersection observer utilities
import { useIntersectionObserver } from "@featherk/composables/observer";
// Geometry utilities
import { computeCollisionOffset } from "@featherk/composables/geometry";
// Component-scoped DOM ID utilities
import { useCompositeId } from "@featherk/composables/id";
// Address utilities
import {
useZipTextBox,
useUSAddress,
type USAddressSubmitPayload,
} from "@featherk/composables/address";
// Form utilities
import {
useFieldsetValidationKit,
useFieldsetTouchTracker,
} from "@featherk/composables/form";
// Kendo Window wrappers
import { useWindow, useWindowDrawer } from "@featherk/composables/window";
The package root re-exports every feature. Date and time payload types are aliased there to avoid name collisions:
import {
useGridA11y,
useGridActiveFilter,
useGridRowAction,
type RowActionContext,
useMaskedDateInput,
type DateChangePayload,
useMaskedDateRangeInput,
type RangeChangePayload,
useMaskedTimeInput,
type TimeChangePayload,
usePopupTrap,
usePopupMenu,
type KendoMenuSelectEvent,
useActiveIdRegistry,
useExclusiveGroup,
useIntersectionObserver,
useCompositeId,
useZipTextBox,
useUSAddress,
useFieldsetValidationKit,
useFieldsetTouchTracker,
useWindow,
useWindowDrawer,
} from "@featherk/composables";
For complete setup, behavior, and examples, use the guide listed in Composables Overview.
What Gets Installed vs. What Ships
FeatherK packages should just work once installed: you should never have to track down a missing dependency or silence a peer warning. When that conflicts with install size, FeatherK accepts a larger node_modules and keeps the browser bundle small.
| You import | Installed in node_modules | Shipped to the browser (production build) |
|---|---|---|
import { useX } from "@featherk/composables/<feature>" | Every runtime dependency (VueUse, focus-trap), installed automatically | useX and the dependencies it imports |
import { useX } from "@featherk/composables" | Same | Same as the subpath import |
| Nothing from a feature | Its dependencies are still installed | Nothing from that feature |
During vite dev, a root import loads every feature. This affects only the dev server, not what users download.
Kendo UI Compatibility
Built and tested against Kendo UI for Vue 6.4.1. The composables do not import Kendo at runtime, but they depend on the DOM structure and k-* classes rendered by specific Kendo versions, and several public types come from Kendo packages. Those packages are declared as optional peer dependencies (^6.4.1): none is required, but any you install must be in the supported range.
| Composables | Kendo package |
|---|---|
useGridA11y, useGridActiveFilter, useGridRowAction | @progress/kendo-vue-grid, @progress/kendo-data-query |
useWindow, useWindowDrawer | @progress/kendo-vue-dialogs |
usePopupMenu | @progress/kendo-vue-layout, @progress/kendo-vue-popup |
usePopupTrap | @progress/kendo-vue-popup |
useMaskedDateInput, useMaskedDateRangeInput, useMaskedTimeInput | @progress/kendo-vue-dateinputs |
useZipTextBox, useUSAddress | @progress/kendo-vue-inputs |
Dependencies
-
Required peers:
vue(3.5+)npm and pnpm install it automatically; with Yarn Berry, install it yourself.
-
Optional peers: the Kendo packages listed under Kendo UI Compatibility, for the composables you use. They're used only for types, and your app already installs them for the Kendo components it renders.
-
Runtime dependencies:
@vueuse/core,@vueuse/integrationsandfocus-trap, installed with this package.usePopupTrapuses VueUse integrations and focus-trap internally.
Keep all @featherk/* packages on the same version. They release together, and each @featherk/ui release requires at least the matching @featherk/composables. With Renovate or Dependabot, group them into one update.
Styling
The composables ship no CSS. Some, such as useWindow and useGridA11y, add fk-* classes to the Kendo components they manage, and @featherk/styles themes those classes.
Testing with Vitest
To mock a composable, mock its subpath, and import it from that same subpath in the code under test. Each subpath is a separate module, and a mock replaces only the module it names. A root mock doesn't reach code that imports a subpath, which includes every @featherk/ui component, and a subpath mock doesn't reach code that imports the root. Neither case raises an error; the test simply runs the real composable.
// Before 0.18.0 every subpath was one module, so either specifier worked.
vi.mock("@featherk/composables", …);
// Now: mock the subpath the code imports.
vi.mock("@featherk/composables/trap", …);
Build
Run from repository root:
npm run build -- --filter=@featherk/composables
Or run package-local:
npm run build
Maintainer Notes
- Every feature folder has an
index.tsbarrel, is re-exported fromsrc/index.ts, and has a matchingexportssubpath. For example,useGridA11y,useGridActiveFilteranduseGridRowActionlive insrc/grid/and ship from@featherk/composables/grid. vite.config.tsbuilds one ES and one CommonJS entry for the root and for eachpackage.jsonexportssubpath. A new feature folder only needs its subpath added toexports.package.jsonsets"sideEffects": false, so a root import tree-shakes to the same code as the subpath. Keep modules free of import-time side effects. A module that must run on import has to be listed insideEffects, or bundlers will drop it.- Keep shared state out of module scope: no module-level refs, Maps or
provide/injectkeys. An app can load both the ES and CommonJS builds, and the two copies don't share state. Create state in the composable and pass the instance, asuseActiveIdRegistrydoes. - Add each new source file to
coverage.includeinvitest.config.ts. The list is explicit, so a file left out is missing from the coverage report and escapes the coverage thresholds, even when it has its own tests. - The repo's
scripts/check-entries.mjsruns at the end of the build. It fails if an export target is missing, an entry imports a package that isn't a dependency or peer, or any entry imports an optional peer (npm doesn't install those). Kendo packages are optional peers, so this keeps them type-only. - The repo's
scripts/check-peers.mjsdoesn't run here, because this package has no@featherk/*peers. If it ever gains one, add the sibling as afile:dev dependency and the peer check to thebuildscript, as@featherk/uidoes.
Changelog
Release history for all @featherk/* packages is maintained in a single CHANGELOG.md.