najm-kit 2.11.16 → 2.11.18

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/CHANGELOG.md CHANGED
@@ -1,421 +1,437 @@
1
- # Changelog
2
-
3
- ## 2.11.16 - 2026-09-04
4
-
5
- - `NBrandingPayload` accepts a `slots` registry, so `NajmAppProvider` takes what
6
- `najm-theme`'s `loadServerBranding()` returned as `initialBranding` unchanged
7
- instead of renaming `sidebarLogoExpanded` and `sidebarLogoCollapsed` at the
8
- call site. Structural the kit still does not depend on `najm-theme`.
9
- Precedence is explicit marks, then flat payload fields, then slots, and only
10
- the two sidebar keys are read: an auth logo, a hero, and a consumer's own
11
- slots stay out of the context the sidebar sees.
12
-
13
- ## 2.11.15 - 2026-09-04
14
-
15
- - Fixed `ComboboxInput` keyboard interaction. Enter and Space now open its
16
- focusable trigger, and Enter commits the active filtered result instead of
17
- doing nothing when the previously active CmdK item was filtered out. Disabled
18
- comboboxes remain non-activatable.
19
-
20
- ## 2.11.14 - 2026-09-04
21
-
22
- - Added `defineNajmPreferences` to `najm-kit/server`: the language, theme, and
23
- time-zone cookie contract every Najm application otherwise hand-writes across
24
- three route handlers and a root layout. `handlers.language`, `handlers.theme`,
25
- and `handlers.timeZone` export directly as Next.js `POST` handlers, and
26
- `resolve(cookieStore, { languageFallback })` seeds the root layout. Pure Web
27
- `Request`/`Response` and a structural cookie reader no React, no Next import.
28
- - Made it convention-first. A new application passes `{ i18n }` and nothing
29
- else: `light` default theme, `light | dark` the only accepted modes, `UTC`
30
- default zone, the canonical `TimeZoneInput` zones, `najm-ui-*` cookie names,
31
- and `HttpOnly; SameSite=Lax; Path=/; Max-Age=31536000` cookies are all
32
- defaults. Applications with published cookie names or another product default
33
- override only those keys. No consumer-side theme guard or normalizer.
34
- - Fixed the time-zone contract having two sources of truth. `TimeZoneInput`'s
35
- values moved out of the component into `NAJM_TIME_ZONES`, a pure module now
36
- read by the input, the resolver, and the POST handler alike, so a zone the
37
- control offers can no longer be rejected by server validation. Applications
38
- that pass custom `items` pass the same values as `timeZones`.
39
- - Exported `NAJM_TIME_ZONES`, `NAJM_DEFAULT_TIME_ZONE`, `NajmTimeZone`, and
40
- `NajmMode` from `najm-kit/server`, plus `NajmPreferenceLanguage<T>` and
41
- `NajmPreferenceTimeZone<T>` inferred from a configured definition, so
42
- consumers declare no language, theme, or time-zone alias of their own.
43
- - Validated before normalizing in every handler: an unsupported value is a
44
- `400` with a generic message and no `Set-Cookie`, never the default silently
45
- written into a cookie. Malformed JSON, a non-object body, and a missing field
46
- answer the same way, and no request content reaches the response.
47
-
48
- ## 2.11.13 - 2026-09-04
49
-
50
- - Fixed the documented `common.feedback.<field>` convention so all shared
51
- loading, empty, error, forbidden, and not-found states resolve localized
52
- labels without an application-owned `feedbackDefaults` key map. Missing
53
- convention keys still fall back to the packaged English labels.
54
- - Forwarded Najm i18n's missing-key fallback and language-direction resolver
55
- through `NajmAppProvider`, keeping language changes reactive for partial
56
- catalogs and non-Arabic RTL languages.
57
-
58
- ## 2.11.10 - 2026-09-03
59
-
60
- - Fixed the selected numbered `NTable` pagination button inheriting the regular
61
- foreground colour over its filled primary style. The active page now keeps
62
- the primary button foreground token, which is white in the light theme.
63
- - Fixed focused invalid inputs combining a destructive border with the normal
64
- theme focus colour. `BaseInput` now keeps the visible keyboard-focus ring but
65
- colours it from the destructive token for both directly focused composite
66
- controls and wrappers whose child has `:focus-visible`.
67
- - Made the desktop and mobile sidebar use logical inline edges, spacing, border
68
- utilities, chevrons, and drag direction so the complete rail behaves
69
- correctly in both LTR and RTL layouts.
70
-
71
- ## 2.11.9 - 2026-08-30
72
-
73
- - Fixed card grids that paginate client-side rendering their entire dataset on
74
- one page. `useTable` pinned an uncontrolled paged card list to `data.length`,
75
- a branch that predates the container measurement added in 2.2.0 and was never
76
- reconciled with it: `calculatedCardPageSize` was measured and published, but
77
- only ever applied to a server-paginated grid. A 500-row list rendered 500
78
- cards on page one, reported a single page, and showed 500 as the current
79
- Rows/page value. The measured size is now applied to an uncontrolled card grid
80
- too, through the guards that already keep a server-paginated one stable —
81
- geometry bucketing, the per-container report budget, and the deferral until
82
- real cards have been measured. That loop is a property of the measurement
83
- rather than of fetching: page size decides which cards render, and rendered
84
- cards are what card height is measured from. Rendering every supplied row
85
- stays the behavior wherever there is nothing to measure against, so
86
- `dynamicHeight={false}` and a container that has not yet reported a layout are
87
- unchanged, and a controlled `pagination` prop or an explicit Rows/page choice
88
- still wins.
89
-
90
- ## 2.11.8 - 2026-08-28
91
-
92
- - Fixed `NContextMenu` leaving keyboard focus on its opener after the menu
93
- appeared. The first enabled action now receives focus, disabled actions are
94
- skipped, Arrow Up/Down wrap, Home/End jump to the bounds, Tab dismisses the
95
- menu, and Escape restores focus to the opener. Focused actions also receive
96
- the same visual treatment as hovered actions.
97
-
98
- ## 2.11.7 - 2026-08-17
99
-
100
- - Fixed date, combobox, and multiselect `FormInput` controls reaching the
101
- accessibility tree with no accessible name. These types render their trigger
102
- as a styled `div`, so the `<label for>` that `FormControl` wires up pointed at
103
- a non-labelable element and named nothing: screen readers announced an unnamed
104
- control, and `getByRole(..., { name })` could not resolve one. The form label
105
- now becomes the trigger's `aria-label`, and `DateInput` and `ComboboxInput`
106
- accept an explicit `ariaLabel` to override it. `select` keeps naming itself
107
- from `ariaLabel || placeholder`; renaming it would break consumers that select
108
- it by placeholder today.
109
- - Fixed `DateInput` rendering its popover trigger as a `div`. Radix only merges
110
- trigger props onto the child, so the control had no role, no tab stop, and no
111
- keyboard activation it was unreachable without a mouse. It is now a real
112
- `<button type="button">`, which also keeps it from submitting the surrounding
113
- form when the calendar opens.
114
-
115
- ## 2.11.6 - 2026-08-15
116
-
117
- - Fixed `FormInput` dropping React Hook Form's native field binding for text,
118
- number, password, textarea, and time controls. These controls now forward
119
- their field name, composed blur handler, and focus ref to the actual native
120
- element, preserving browser form semantics, touched-state tracking, and
121
- programmatic focus without changing composite-control contracts.
122
-
123
- ## 2.11.5 - 2026-08-14
124
-
125
- - Fixed numbered lists staying on an invalid final page after a deletion or
126
- another mutation reduced the server total. Paged lists now clamp before
127
- slicing their row buffer, render the previous valid page immediately, and
128
- reconcile the controlled pagination state without showing a false empty
129
- state. A genuinely empty result still remains on page one.
130
- - Fixed panel feedback states stretching their icon, copy, and action across
131
- separate grid rows in tall table and dialog bodies. Panel content now stays
132
- together in one centered vertical stack with consistent spacing.
133
-
134
- ## 2.11.4 - 2026-08-12
135
-
136
- - Fixed server-paginated card grids briefly rendering at a placeholder-derived
137
- page size before correcting to the real card height. Card mode now waits for
138
- real rows and publishes the measured fit before their first paint; fixed-row
139
- table sizing remains immediate during loading.
140
-
141
- ## 2.11.3 - 2026-08-12
142
-
143
- - Fixed `NTable` replacing or clipping an explicit Rows/page choice when
144
- `dynamicHeight` was enabled. Automatic fit sizing still owns the initial page
145
- size; after the reader chooses a value, the table preserves it and scrolls
146
- its bounded body when the requested rows exceed the measured fit.
147
-
148
- ## 2.11.2 - 2026-08-11
149
-
150
- - Fixed `PasswordInput` forwarding native input attributes such as generated
151
- form IDs and ARIA metadata to the actual `<input>`, restoring programmatic
152
- label association for assistive technology and browser automation.
153
-
154
- ## 2.11.1 - 2026-08-11
155
-
156
- - Fixed `NajmThemeProvider` precedence so an explicit runtime `mode` overrides
157
- a preset stored in the design config. An explicit `preset` prop still wins,
158
- allowing deliberately fixed-preset subtrees without producing mixed light
159
- and dark application surfaces.
160
-
161
- ## 2.11.0 - 2026-08-10
162
-
163
- - Gave every keyboard-focusable primitive a visible focus ring, from one shared
164
- source: `focusRingClasses` and `focusRingWithinClasses`, exported from the
165
- root barrel so consumers stop hand-rolling app-level focus CSS. `TabsContent`
166
- is keyboard-focusable (Radix sets `tabIndex={0}`) and previously showed no
167
- indicator at all; `CollapsibleTrigger` now carries its own ring rather than
168
- relying on whatever it wraps. `BaseInput` paints the ring on the wrapper via
169
- `has-[:focus-visible]:`, which is what finally gives `NumberInput` and the
170
- multi-select trigger an indicator — both suppress the inner control's ring,
171
- so focusing the child element used to change only the border colour.
172
- - Fixed `Dialog` focus restoration. The opener is captured in
173
- `onOpenAutoFocus` and refocused on close when focus would otherwise land on
174
- `<body>`, so dismissing a dialog with Escape returns the keyboard to the
175
- control that opened it (WCAG 2.4.3) instead of stranding it at the top of
176
- the document. A caller that calls `preventDefault()` on the close event still
177
- owns focus entirely.
178
- - Added `NCredentialsCard`, a one-time handover surface for freshly generated
179
- secrets. Renders a description list of fields with monospaced, break-all
180
- values, an optional header icon that defaults to a check mark, an optional
181
- consumer `actions` slot, and a built-in Copy button. The Copy action resolves
182
- text through `copyText` when supplied or joins `${label}: ${value}` with
183
- `\n` in field order otherwise. The button is disabled while a clipboard
184
- write is pending; on success it swaps to a check icon and `copiedLabel`,
185
- on failure to a warning icon and `copyErrorLabel`, and either state reverts
186
- to idle after roughly two seconds. Header and field icons are marked
187
- decorative, status is announced through a polite `aria-live` region, and
188
- the Copy button renders before consumer actions so a Done-style dismiss
189
- stays the last tab stop. The component never rethrows: a missing
190
- `navigator.clipboard`, a rejected or synchronously thrown `writeText`, and
191
- a synchronously thrown `copyText` all land in the error state with
192
- `onCopyError` invoked. State updates and revert timers are guarded so
193
- unmounting during a pending copy, or starting a second copy while the
194
- first success state is still showing, never fire stale setters. Logical
195
- spacing properties keep the layout correct under `dir="rtl"` without a
196
- consumer override, and each value carries `dir="auto"` so it resolves its own
197
- text direction: credentials are weak-directionality strings, and inheriting an
198
- ambient `rtl` paints `+1 555 0100` as `0100 555 1+` and `p@ssw0rd!` as
199
- `!p@ssw0rd` while the DOM and the copied text stay correct. A genuinely Arabic
200
- value still renders right-to-left. Packaged English fallbacks ship for the three action
201
- labels only; every field label, title, and description is the consumer's
202
- text. Exported as `NCredentialsCard`, `NCredentialField`,
203
- `NCredentialsCardProps`, and `NCredentialsCardClassNames` from the root
204
- barrel and from `packages/najm-kit/src/components/data-display`.
205
- - Added a shared `surface` contract across `NLoadingState`, `NErrorState`, and
206
- `NEmptyState`. `inline` (default) preserves existing behavior; `panel` adds a
207
- centered minimum-height frame for table, card, dialog, and sheet bodies
208
- without a page gutter or a landmark; `page` uses the configured page
209
- spacing through `NPageLayout as="div"` so it never introduces a nested
210
- `<main>`. `NLoadingState.fullScreen` keeps precedence over `surface`.
211
- - Added `NForbiddenState` and `NNotFoundState` as first-class generic Kit
212
- components. Both default to the page surface, ship token-backed default
213
- icons (`ShieldOff`, `Compass`), and resolve title and description through
214
- the new provider defaults. Neither knows the dashboard URL, renders a Next
215
- `Link`, redirects, or writes route metadata.
216
- - Added `feedbackDefaults` to `NajmUIProvider`. It is inherited by
217
- `NajmNextUIProvider` and `NajmAppProvider` through TypeScript no second
218
- adapter prop or translation source. Labels resolve through the existing
219
- structural `t` (most specific: explicit prop > literal default > translated
220
- key > packaged English). Generic error body copy and empty description
221
- deliberately have no packaged fallback so the no-provider render is
222
- unchanged.
223
- - Exported the new feedback components and props (`NLoadingStateProps`,
224
- `NErrorStateProps`, `NEmptyStateProps`, `NForbiddenStateProps`,
225
- `NNotFoundStateProps`, `NFeedbackSurface`, `NFeedbackDefaults`,
226
- `NFeedbackLabels`, `NFeedbackLabelKeys`, `NFeedbackIconSize`) from the root
227
- barrel and re-exported them directly from `najm-kit/app` so a Next Server
228
- Component route can render them without authoring a local `"use client"`
229
- wrapper. The root entry stays free of `"use client"` and continues to
230
- share a single `NFeedbackDefaultsContext` across `index` and the adapters.
231
-
232
- ## 2.10.0 - 2026-08-10
233
-
234
- - Added a shared, failure-aware media source chain for `NAvatar`, `NImage`, and
235
- the new Next-only `NNextImage` export. Sources reset cleanly when inputs or
236
- cache versions change, duplicate candidates are tried once, consumer event
237
- handlers are preserved, and failed avatars return to initials without a
238
- broken-image glyph.
239
- - `NAvatar` now uses a native lazy-loaded image so loading and error events are
240
- observable, keeps initials visible until the image paints, supports native
241
- `imageProps`, and actually tries `fallbackSrc` after a primary failure.
242
- - Added provider-level status badge defaults and the public
243
- `normalizeStatusToken` helper. Applications can configure translated status
244
- labels, colors, icons, look, shape, and size once while explicit badge props
245
- retain precedence.
246
- - Added `NNextImage` and `NNextImageProps` to `najm-kit/next` only, preserving
247
- Next Image `fill`, `sizes`, optimizer, and `unoptimized` controls without
248
- adding Next.js to the root runtime graph.
249
-
250
- ## 2.9.0 - 2026-08-09
251
-
252
- - Added `najm-kit/server`, a pure UI bootstrap loader. `createUiBootstrapLoader()`
253
- takes the application's own fetcher, endpoint paths, payload parsers, and
254
- factory values, then runs the resources concurrently, unwraps the `{ data }`
255
- envelope (or one the application selects), and falls back per resource so a
256
- branding outage never discards a valid appearance. Failures are reported
257
- through an optional structured `onDiagnostic` callback carrying resource,
258
- reason, path, and status never a response body, header, cookie, or raw
259
- thrown value. A `fallback()` that throws stays a visible error. The entry
260
- imports no React, no Next.js, and no Node built-in, and re-exports
261
- `parseNajmDesignConfig` so an appearance payload can be parsed without
262
- reaching the root barrel.
263
- - Added `najm-kit/server/react`, the React Server Component adapter.
264
- `createReactServerUiBootstrap()` memoizes one bootstrap per request with
265
- React's `cache()` and derives the per-resource accessors from it, so a root
266
- layout, a nested layout, and a page share one resolution and one stable
267
- snapshot for the whole render. Resolution is request-scoped only: separate
268
- requests share no snapshot, failure, or diagnostic, and a transient outage is
269
- retried on the next request. The `browser` export condition resolves to a
270
- module that throws, so importing the adapter from a Client Component fails at
271
- build time instead of shipping the application's fetcher and factory values
272
- into a browser bundle.
273
- - The root `najm-kit`, `najm-kit/next`, and `najm-kit/app` entries are unchanged
274
- and do not reach either server entry.
275
-
276
- ## 2.8.2 - 2026-08-08
277
-
278
- - Added the framework-neutral `najm-kit/person-images` subpath. It ships a
279
- built-in resolver for `child`, `adult`, `parent`, and `family` roles with
280
- the seven WebP illustrations embedded as base64 data URLs, plus
281
- `createPersonImageResolver` so an application can declare its own role
282
- names (`teacher`, `student`, `doctor`, `driver`, …) and TypeScript catches
283
- unknown role strings at the call site. The root `najm-kit` entry stays
284
- unchanged and does not pull in the person images.
285
-
286
- ## 2.8.1 - 2026-08-08
287
-
288
- - Added schema-driven form development tools to `NajmAppProvider`. Passing
289
- `formDevTools` enables F8 filling for every `NForm` and `WizardForm` without
290
- an application helper or additional provider.
291
- - Added built-in Zod 4 form-value generation with per-form overrides for
292
- relation fields and other application-owned values. Existing explicit
293
- `devTools.enabled` and `devTools.fill` usage remains supported.
294
-
295
- ## 2.8.0 - 2026-08-08
296
-
297
- - Added server-safe `najm-kit/format` helpers for currency minor units,
298
- numbers, percentages, dates, times, relative time, tokens, local date inputs,
299
- and slugs. Client applications can use the same contract reactively through
300
- `NajmFormatProvider` and `useNajmFormat`.
301
- - Added the server-safe `najm-kit/pagination` offset protocol, including bounded
302
- page creation, total-aware continuation, probe-row continuation for APIs
303
- without totals, and query cleanup that preserves meaningful `false` and `0`.
304
- - Added the optional-peer `najm-kit/query` entry with offset infinite-query and
305
- responsive paged/card-list hooks, plus shared card-pagination adapters and
306
- localized continuation labels.
307
- - Extended `NajmAppProvider` with formatting locale, currency, and placeholder
308
- configuration so applications can bind language, time zone, and formatting
309
- without another host bridge provider.
310
- - Added shared media-query/card-viewport helpers and avatar-source utilities.
311
-
312
- ## 2.6.2
313
-
314
- - Added `NSidebarProvider` and `useNSidebar`, so sidebar state can be read from a distance. `NSidebar` renders beside the page content rather than around it, which left applications hand-rolling a context to hand `setMobileOpen` down to a page header — a wrapper component plus an aliased import at every call site. Wrap the shell in `NSidebarProvider` and `NPageHeader` now resolves both `onSidebarOpen` and `mobileBreakpoint` from it, so a header nested anywhere below renders a working mobile trigger with no props threaded to it. Also exports the `NSidebarContextValue` type.
315
- - `NSidebar` resolves its open and collapsed state as explicit prop → surrounding provider → internal state. Passing `collapsed`, `mobileOpen`, `onCollapsedChange`, or `onMobileOpenChange` keeps behaving exactly as before, and a sidebar with no provider around it still owns its own state, so this is additive for every existing consumer.
316
- - `logo` accepts a render prop, `({ collapsed, isMobile }) => ReactNode`, alongside the existing node. It receives the state the sidebar actually resolved — including whatever `autoCollapseAt` decided — which consumers previously had to approximate with their own responsive classes, guessing at a breakpoint the sidebar had already computed. The mobile drawer always reports `collapsed: false`, matching how it renders. Exported as `SidebarLogoRender`.
317
-
318
- ## 2.6.1
319
-
320
- - Fixed the non-card `NPageHeader` bleed never taking effect. 2.6.0 cancelled the page padding with the Tailwind utilities `-mt-[var(--najm-section-gap,0px)]` and `-mx-[var(--najm-page-gutter,0px)]`, which only work if the consuming app's Tailwind build happens to emit those arbitrary classes — they exist nowhere but inside this package's bundle, so a consumer could load a stylesheet without them and the header stayed exactly where it was. The offsets are inline styles now and no longer depend on the consumer's CSS pipeline.
321
-
322
- ## 2.6.0
323
-
324
- - Added `NThemePresets`, and wired it into `NThemeCustomizer` through `presets`, `selectedPresetId`, `presetsStatus`, `savedDesign`, `onPresetSelect`, `onPresetSave`, `onPresetDelete`, and `presetLabels`. The picker renders only when a host supplies both `presets` and `onPresetSelect`. It is presentational: the host owns where presets live and what saving one means, and selecting a row hands the design back so it can be previewed before anything is stored. Each row draws a swatch strip from the design's own `sidebar`, `primary`, `secondary`, `accent`, and `background` tokens; the selected row's check sits left in the success colour and per-row delete sits right. Omit `onPresetSave` or `onPresetDelete` to hide those controls. Deleting is pointer-only — Radix owns roving focus inside the listbox.
325
- - **`NPageHeader` in non-card mode is now flush.** It rendered as a full-bleed bar (`border-b`, no radius) but still sat inside the padding `NPageLayout` applies, so it floated below and inside the page gutter and its bottom rule never met the sidebar header's. It now cancels that padding, and its base height goes `min-h-12` → `min-h-14` to match `NSidebarHeader`. **Non-card page headers move up and out to the page edges, and grow 8px below `sm`.** Card mode is unchanged.
326
- - `NPageLayout` publishes its resolved spacing as `--najm-page-gutter` and `--najm-section-gap`. Descendants could not previously cancel the page padding: a design config resolves the gutter to a literal, so the pre-existing `--page-gutter` / `--section-gap` variables only ever held the fallbacks. Full-bleed children should negate these, with `0px` fallbacks so they stay inert outside `NPageLayout`.
327
- - Fixed `SelectInput` discarding `SelectItemType.icon`. The prop was declared but `renderItems` rendered the label alone, so every per-item icon was silently dropped. Items with an icon now render it before a truncating label; items without one are unchanged.
328
-
329
- ## 2.5.0
330
-
331
- - Added `NTableDefaultsProvider`, so an application supplies `paginationLabels` once instead of at every table. Labels merge per key, most specific first: a table's own `paginationLabels` override the provider's for the keys it sets, the provider covers the rest, and anything neither supplies falls back to the packaged English. Also exports `useNTableDefaults` and the `NTableDefaults` type.
332
- - `value` is passed through the provider unmemoized; memoize it in the caller, or every table below re-renders with the shell.
333
-
334
- ## 2.4.0
335
-
336
- - `NTablePagination` renders numbered page buttons instead of `Page X of Y`. The window shows the first and last page, the current page, and one page either side, collapsing the rest into at most two gaps. The slot count is constant for any result longer than the window, so the bar does not change width as the reader pages through it, and a gap never stands in for a single page that slot goes to the page instead.
337
- - Added `paginationVariant`, defaulting to `"numbered"`. Pass `"compact"` to keep the previous position text with first/previous/next/last controls. **This changes the default appearance of every paginated `NTable`.**
338
- - The numbered variant drops the first/last double chevrons, because page 1 and page N are now single-click targets of their own. Previous and next remain. The compact variant is unchanged.
339
- - Numbered pages fall back to compact on their own when the page count is not trustworthy — that is, under `manualPagination` with no `pageCount` supplied, where TanStack infers a count from the rows it happens to hold rather than from a result total. Numbering that would invite clicks on pages that may not exist is not rendered.
340
- - Below the `sm` breakpoint the numbers give way to the position text; seven page buttons plus the rows-per-page select do not fit a phone.
341
- - Added `paginationLabels` so the bar can be localized: `rowsPerPage`, `pagination`, `goToPage`, `currentPage`, `firstPage`, `previousPage`, `nextPage`, `lastPage`, `pageOf`, and `rowsSelected`. All optional, all falling back to the previous English strings.
342
- - Pagination chevrons now mirror under `dir="rtl"`. They previously pointed against the reading direction in right-to-left layouts.
343
- - The page controls are wrapped in a labelled `nav`, and the current page carries `aria-current="page"`.
344
- - Exported `buildPageItems` and `NTablePageItem` for consumers that need the same windowing outside the table.
345
-
346
- ## 2.2.1
347
-
348
- - Fixed a regression in 2.2.0: the dynamic page size reported under `manualPagination` could oscillate. Card row height is measured from rendered cards, so it grows as images decode; feeding that back into the page size refetched, re-rendered, re-measured, and refetched again. A list visibly settled from one page size to another with the loading skeleton flashing twice. The report is now allowed once per container geometry, which does not depend on the rows inside it, so it terminates. A resize still re-arms it, and the debounce still waits for the measurement to settle before reporting.
349
-
350
- ## 2.2.0
351
-
352
- - Added `cardPagination` mode `infinite`: card lists continue on scroll instead of behind a button. No control and no end-of-list element render while the list is healthy. The continuation button appears only after an append failure, as the retry target.
353
- - Infinite continuation is owned by `NTableCards`, so the sentinel lives inside the card list's own `NajmScroll` viewport and uses it as the observer root. An appending page renders shaped card placeholders at the grid tail rather than a spinner in a fixed strip, and the polite `aria-live` announcement of appended row counts is preserved.
354
- - `cardPagination: { mode: "all" }` is now honored in table mode as well as cards: every supplied row renders and no pagination controls are shown.
355
- - `dynamicHeight` now takes effect under `manualPagination`. The measured page size is reported through the ordinary `onPaginationChange` callback, debounced, so server-paginated tables fill their container while the consumer keeps ownership of fetching. `maxHeight` stays caller-owned under manual pagination, and no page size is reported before the first measurement.
356
- - Added `calculateCardPageSize`, floored to whole card rows, published as `calculatedCardPageSize`. A card grid page no longer ends in a ragged partial row. This deliberately differs from `calculateCardSkeletonCount`, which ceils because overfilling placeholders is harmless.
357
- - Removed the deprecated `baseUrl` compiler option from the package tsconfig; `paths` resolves relative to the config file.
358
-
359
- ## 2.1.56
360
-
361
- - Honor `showIcon` on NTable `select` and `combobox` filters. The leading filter icon is still shown by default; pass `showIcon: false` on a filter to hide it.
362
-
363
- ## 2.1.55
364
-
365
- - Added controlled remote-search and loading-state props to `ComboboxInput` and `FormInput type="combobox"` while preserving client filtering by default.
366
-
367
- ## 2.1.53 - 2026-08-05
368
-
369
- - Add typed `NBarChart`, `NLineChart`, `NPieChart`, and `NStatusBreakdown`
370
- components with caller-formatted generic data, accessible summaries, empty
371
- states, responsive RTL-safe layouts, and shape-matched loading skeletons.
372
- - Default chart colors to the live `--chart-1` through `--chart-5` theme
373
- variables, cycle deterministically after five items, and retain explicit
374
- per-series/item color overrides.
375
- - Add preset and numeric chart diameter contracts to `NPieChart` and
376
- `NDonutCard`, including narrow-container shrinking without clipped legends.
377
- - Make `NDonutCard` item colors optional and add accessible loading states to
378
- `NDonutCard` and `NStatCard`.
379
- - Add public API tests, component tests, README guidance, and playground
380
- examples for the chart and loading contracts.
381
-
382
- ## 2.1.49 - 2026-08-04
383
-
384
- ### ImageInput and AvatarInput
385
-
386
- - Add `previewAlt`, `fallbackImage`, `fallbackAlt`, `unavailableContent`,
387
- `imageClassName`, `onPreviewError`, `replaceAriaLabel`, and `clearAriaLabel`
388
- to `ImageInputProps`. `AvatarInput` forwards every new prop unchanged.
389
- - Preview sources resolve in this priority order: `value`, `fallbackImage`,
390
- `defaultImage`. Candidate URLs are deduplicated so a failing primary URL is
391
- never retried through multiple stages. When every candidate fails, the
392
- broken `<img>` is unmounted and `unavailableContent` (or a neutral default)
393
- is rendered in its place.
394
- - `imageVersion` is appended safely to relative, absolute, queried, and
395
- fragmented URLs. `data:`, `blob:`, `javascript:`, and `file:` URLs are
396
- left unchanged.
397
- - Expose `data-image-input-state="empty" | "preview" | "fallback" | "unavailable"`
398
- on the preview container for styling, testing, and consumer diagnostics.
399
- - File selection is race-safe: stale `FileReader` completions cannot replace
400
- a newer value. Object URLs created by the component are tracked so
401
- consumer-owned blob URLs are never revoked.
402
- - Replace and clear controls are real `<button>` elements, are reachable
403
- with the keyboard, and stay visible on touch and coarse-pointer devices.
404
- Only on `(hover: hover) and (pointer: fine)` desktops do the controls fall
405
- back to a hover/focus reveal. `focus-visible` always restores visibility.
406
- - Use logical positioning (`end-*`) so the clear button works correctly in
407
- RTL layouts.
408
- - Re-export `ImageInputPreviewSource` and `ImageInputPreviewError` from
409
- `najm-kit/components/inputs` and the package root.
410
-
411
- ## 2.1.48 - 2026-08-04
412
-
413
- - Keep responsive card row actions visible on phone, tablet, and touch input,
414
- while retaining hover and keyboard-focus reveal on fine-pointer desktops.
415
- - Size table and card loading skeletons from the measured body and active grid,
416
- and keep loading borders, radius, color, and shadow aligned with loaded
417
- surfaces.
418
- - Add the exported `NTableCardPagination` and `NTableLoadMorePagination`
419
- contracts for paged, complete supplied-data, and explicit server-backed Load
420
- more card presentation, including guarded append/retry behavior and accessible
421
- loading, result, error, and terminal feedback.
1
+ # Changelog
2
+
3
+ ## 2.11.18 - 2026-09-06
4
+
5
+ - Added a resolved-state render-prop form of `NSidebar.footer`, matching the
6
+ existing logo contract. Custom footers now receive the actual desktop
7
+ collapsed state, including `autoCollapseAt`, while mobile drawers receive
8
+ their expanded state. `NAppShell.sidebarFooter` forwards the same contract.
9
+ - Centered built-in footer and footer-collapse icons with the same border-aware
10
+ rail inset as navigation items.
11
+
12
+ ## 2.11.17 - 2026-09-05
13
+
14
+ - Added an `i18n` definition prop to `NajmAppProvider`, deriving translations,
15
+ fallback behavior, locale tags, and writing directions from one shared
16
+ declaration while preserving explicit prop overrides. Server Components can
17
+ pass the new serializable `definition.snapshot` from `najm-i18n@2.1.2`.
18
+
19
+ ## 2.11.16 - 2026-09-04
20
+
21
+ - `NBrandingPayload` accepts a `slots` registry, so `NajmAppProvider` takes what
22
+ `najm-theme`'s `loadServerBranding()` returned as `initialBranding` unchanged
23
+ instead of renaming `sidebarLogoExpanded` and `sidebarLogoCollapsed` at the
24
+ call site. Structural the kit still does not depend on `najm-theme`.
25
+ Precedence is explicit marks, then flat payload fields, then slots, and only
26
+ the two sidebar keys are read: an auth logo, a hero, and a consumer's own
27
+ slots stay out of the context the sidebar sees.
28
+
29
+ ## 2.11.15 - 2026-09-04
30
+
31
+ - Fixed `ComboboxInput` keyboard interaction. Enter and Space now open its
32
+ focusable trigger, and Enter commits the active filtered result instead of
33
+ doing nothing when the previously active CmdK item was filtered out. Disabled
34
+ comboboxes remain non-activatable.
35
+
36
+ ## 2.11.14 - 2026-09-04
37
+
38
+ - Added `defineNajmPreferences` to `najm-kit/server`: the language, theme, and
39
+ time-zone cookie contract every Najm application otherwise hand-writes across
40
+ three route handlers and a root layout. `handlers.language`, `handlers.theme`,
41
+ and `handlers.timeZone` export directly as Next.js `POST` handlers, and
42
+ `resolve(cookieStore, { languageFallback })` seeds the root layout. Pure Web
43
+ `Request`/`Response` and a structural cookie reader no React, no Next import.
44
+ - Made it convention-first. A new application passes `{ i18n }` and nothing
45
+ else: `light` default theme, `light | dark` the only accepted modes, `UTC`
46
+ default zone, the canonical `TimeZoneInput` zones, `najm-ui-*` cookie names,
47
+ and `HttpOnly; SameSite=Lax; Path=/; Max-Age=31536000` cookies are all
48
+ defaults. Applications with published cookie names or another product default
49
+ override only those keys. No consumer-side theme guard or normalizer.
50
+ - Fixed the time-zone contract having two sources of truth. `TimeZoneInput`'s
51
+ values moved out of the component into `NAJM_TIME_ZONES`, a pure module now
52
+ read by the input, the resolver, and the POST handler alike, so a zone the
53
+ control offers can no longer be rejected by server validation. Applications
54
+ that pass custom `items` pass the same values as `timeZones`.
55
+ - Exported `NAJM_TIME_ZONES`, `NAJM_DEFAULT_TIME_ZONE`, `NajmTimeZone`, and
56
+ `NajmMode` from `najm-kit/server`, plus `NajmPreferenceLanguage<T>` and
57
+ `NajmPreferenceTimeZone<T>` inferred from a configured definition, so
58
+ consumers declare no language, theme, or time-zone alias of their own.
59
+ - Validated before normalizing in every handler: an unsupported value is a
60
+ `400` with a generic message and no `Set-Cookie`, never the default silently
61
+ written into a cookie. Malformed JSON, a non-object body, and a missing field
62
+ answer the same way, and no request content reaches the response.
63
+
64
+ ## 2.11.13 - 2026-09-04
65
+
66
+ - Fixed the documented `common.feedback.<field>` convention so all shared
67
+ loading, empty, error, forbidden, and not-found states resolve localized
68
+ labels without an application-owned `feedbackDefaults` key map. Missing
69
+ convention keys still fall back to the packaged English labels.
70
+ - Forwarded Najm i18n's missing-key fallback and language-direction resolver
71
+ through `NajmAppProvider`, keeping language changes reactive for partial
72
+ catalogs and non-Arabic RTL languages.
73
+
74
+ ## 2.11.10 - 2026-09-03
75
+
76
+ - Fixed the selected numbered `NTable` pagination button inheriting the regular
77
+ foreground colour over its filled primary style. The active page now keeps
78
+ the primary button foreground token, which is white in the light theme.
79
+ - Fixed focused invalid inputs combining a destructive border with the normal
80
+ theme focus colour. `BaseInput` now keeps the visible keyboard-focus ring but
81
+ colours it from the destructive token for both directly focused composite
82
+ controls and wrappers whose child has `:focus-visible`.
83
+ - Made the desktop and mobile sidebar use logical inline edges, spacing, border
84
+ utilities, chevrons, and drag direction so the complete rail behaves
85
+ correctly in both LTR and RTL layouts.
86
+
87
+ ## 2.11.9 - 2026-08-30
88
+
89
+ - Fixed card grids that paginate client-side rendering their entire dataset on
90
+ one page. `useTable` pinned an uncontrolled paged card list to `data.length`,
91
+ a branch that predates the container measurement added in 2.2.0 and was never
92
+ reconciled with it: `calculatedCardPageSize` was measured and published, but
93
+ only ever applied to a server-paginated grid. A 500-row list rendered 500
94
+ cards on page one, reported a single page, and showed 500 as the current
95
+ Rows/page value. The measured size is now applied to an uncontrolled card grid
96
+ too, through the guards that already keep a server-paginated one stable —
97
+ geometry bucketing, the per-container report budget, and the deferral until
98
+ real cards have been measured. That loop is a property of the measurement
99
+ rather than of fetching: page size decides which cards render, and rendered
100
+ cards are what card height is measured from. Rendering every supplied row
101
+ stays the behavior wherever there is nothing to measure against, so
102
+ `dynamicHeight={false}` and a container that has not yet reported a layout are
103
+ unchanged, and a controlled `pagination` prop or an explicit Rows/page choice
104
+ still wins.
105
+
106
+ ## 2.11.8 - 2026-08-28
107
+
108
+ - Fixed `NContextMenu` leaving keyboard focus on its opener after the menu
109
+ appeared. The first enabled action now receives focus, disabled actions are
110
+ skipped, Arrow Up/Down wrap, Home/End jump to the bounds, Tab dismisses the
111
+ menu, and Escape restores focus to the opener. Focused actions also receive
112
+ the same visual treatment as hovered actions.
113
+
114
+ ## 2.11.7 - 2026-08-17
115
+
116
+ - Fixed date, combobox, and multiselect `FormInput` controls reaching the
117
+ accessibility tree with no accessible name. These types render their trigger
118
+ as a styled `div`, so the `<label for>` that `FormControl` wires up pointed at
119
+ a non-labelable element and named nothing: screen readers announced an unnamed
120
+ control, and `getByRole(..., { name })` could not resolve one. The form label
121
+ now becomes the trigger's `aria-label`, and `DateInput` and `ComboboxInput`
122
+ accept an explicit `ariaLabel` to override it. `select` keeps naming itself
123
+ from `ariaLabel || placeholder`; renaming it would break consumers that select
124
+ it by placeholder today.
125
+ - Fixed `DateInput` rendering its popover trigger as a `div`. Radix only merges
126
+ trigger props onto the child, so the control had no role, no tab stop, and no
127
+ keyboard activation it was unreachable without a mouse. It is now a real
128
+ `<button type="button">`, which also keeps it from submitting the surrounding
129
+ form when the calendar opens.
130
+
131
+ ## 2.11.6 - 2026-08-15
132
+
133
+ - Fixed `FormInput` dropping React Hook Form's native field binding for text,
134
+ number, password, textarea, and time controls. These controls now forward
135
+ their field name, composed blur handler, and focus ref to the actual native
136
+ element, preserving browser form semantics, touched-state tracking, and
137
+ programmatic focus without changing composite-control contracts.
138
+
139
+ ## 2.11.5 - 2026-08-14
140
+
141
+ - Fixed numbered lists staying on an invalid final page after a deletion or
142
+ another mutation reduced the server total. Paged lists now clamp before
143
+ slicing their row buffer, render the previous valid page immediately, and
144
+ reconcile the controlled pagination state without showing a false empty
145
+ state. A genuinely empty result still remains on page one.
146
+ - Fixed panel feedback states stretching their icon, copy, and action across
147
+ separate grid rows in tall table and dialog bodies. Panel content now stays
148
+ together in one centered vertical stack with consistent spacing.
149
+
150
+ ## 2.11.4 - 2026-08-12
151
+
152
+ - Fixed server-paginated card grids briefly rendering at a placeholder-derived
153
+ page size before correcting to the real card height. Card mode now waits for
154
+ real rows and publishes the measured fit before their first paint; fixed-row
155
+ table sizing remains immediate during loading.
156
+
157
+ ## 2.11.3 - 2026-08-12
158
+
159
+ - Fixed `NTable` replacing or clipping an explicit Rows/page choice when
160
+ `dynamicHeight` was enabled. Automatic fit sizing still owns the initial page
161
+ size; after the reader chooses a value, the table preserves it and scrolls
162
+ its bounded body when the requested rows exceed the measured fit.
163
+
164
+ ## 2.11.2 - 2026-08-11
165
+
166
+ - Fixed `PasswordInput` forwarding native input attributes such as generated
167
+ form IDs and ARIA metadata to the actual `<input>`, restoring programmatic
168
+ label association for assistive technology and browser automation.
169
+
170
+ ## 2.11.1 - 2026-08-11
171
+
172
+ - Fixed `NajmThemeProvider` precedence so an explicit runtime `mode` overrides
173
+ a preset stored in the design config. An explicit `preset` prop still wins,
174
+ allowing deliberately fixed-preset subtrees without producing mixed light
175
+ and dark application surfaces.
176
+
177
+ ## 2.11.0 - 2026-08-10
178
+
179
+ - Gave every keyboard-focusable primitive a visible focus ring, from one shared
180
+ source: `focusRingClasses` and `focusRingWithinClasses`, exported from the
181
+ root barrel so consumers stop hand-rolling app-level focus CSS. `TabsContent`
182
+ is keyboard-focusable (Radix sets `tabIndex={0}`) and previously showed no
183
+ indicator at all; `CollapsibleTrigger` now carries its own ring rather than
184
+ relying on whatever it wraps. `BaseInput` paints the ring on the wrapper via
185
+ `has-[:focus-visible]:`, which is what finally gives `NumberInput` and the
186
+ multi-select trigger an indicator both suppress the inner control's ring,
187
+ so focusing the child element used to change only the border colour.
188
+ - Fixed `Dialog` focus restoration. The opener is captured in
189
+ `onOpenAutoFocus` and refocused on close when focus would otherwise land on
190
+ `<body>`, so dismissing a dialog with Escape returns the keyboard to the
191
+ control that opened it (WCAG 2.4.3) instead of stranding it at the top of
192
+ the document. A caller that calls `preventDefault()` on the close event still
193
+ owns focus entirely.
194
+ - Added `NCredentialsCard`, a one-time handover surface for freshly generated
195
+ secrets. Renders a description list of fields with monospaced, break-all
196
+ values, an optional header icon that defaults to a check mark, an optional
197
+ consumer `actions` slot, and a built-in Copy button. The Copy action resolves
198
+ text through `copyText` when supplied or joins `${label}: ${value}` with
199
+ `\n` in field order otherwise. The button is disabled while a clipboard
200
+ write is pending; on success it swaps to a check icon and `copiedLabel`,
201
+ on failure to a warning icon and `copyErrorLabel`, and either state reverts
202
+ to idle after roughly two seconds. Header and field icons are marked
203
+ decorative, status is announced through a polite `aria-live` region, and
204
+ the Copy button renders before consumer actions so a Done-style dismiss
205
+ stays the last tab stop. The component never rethrows: a missing
206
+ `navigator.clipboard`, a rejected or synchronously thrown `writeText`, and
207
+ a synchronously thrown `copyText` all land in the error state with
208
+ `onCopyError` invoked. State updates and revert timers are guarded so
209
+ unmounting during a pending copy, or starting a second copy while the
210
+ first success state is still showing, never fire stale setters. Logical
211
+ spacing properties keep the layout correct under `dir="rtl"` without a
212
+ consumer override, and each value carries `dir="auto"` so it resolves its own
213
+ text direction: credentials are weak-directionality strings, and inheriting an
214
+ ambient `rtl` paints `+1 555 0100` as `0100 555 1+` and `p@ssw0rd!` as
215
+ `!p@ssw0rd` while the DOM and the copied text stay correct. A genuinely Arabic
216
+ value still renders right-to-left. Packaged English fallbacks ship for the three action
217
+ labels only; every field label, title, and description is the consumer's
218
+ text. Exported as `NCredentialsCard`, `NCredentialField`,
219
+ `NCredentialsCardProps`, and `NCredentialsCardClassNames` from the root
220
+ barrel and from `packages/najm-kit/src/components/data-display`.
221
+ - Added a shared `surface` contract across `NLoadingState`, `NErrorState`, and
222
+ `NEmptyState`. `inline` (default) preserves existing behavior; `panel` adds a
223
+ centered minimum-height frame for table, card, dialog, and sheet bodies
224
+ without a page gutter or a landmark; `page` uses the configured page
225
+ spacing through `NPageLayout as="div"` so it never introduces a nested
226
+ `<main>`. `NLoadingState.fullScreen` keeps precedence over `surface`.
227
+ - Added `NForbiddenState` and `NNotFoundState` as first-class generic Kit
228
+ components. Both default to the page surface, ship token-backed default
229
+ icons (`ShieldOff`, `Compass`), and resolve title and description through
230
+ the new provider defaults. Neither knows the dashboard URL, renders a Next
231
+ `Link`, redirects, or writes route metadata.
232
+ - Added `feedbackDefaults` to `NajmUIProvider`. It is inherited by
233
+ `NajmNextUIProvider` and `NajmAppProvider` through TypeScript — no second
234
+ adapter prop or translation source. Labels resolve through the existing
235
+ structural `t` (most specific: explicit prop > literal default > translated
236
+ key > packaged English). Generic error body copy and empty description
237
+ deliberately have no packaged fallback so the no-provider render is
238
+ unchanged.
239
+ - Exported the new feedback components and props (`NLoadingStateProps`,
240
+ `NErrorStateProps`, `NEmptyStateProps`, `NForbiddenStateProps`,
241
+ `NNotFoundStateProps`, `NFeedbackSurface`, `NFeedbackDefaults`,
242
+ `NFeedbackLabels`, `NFeedbackLabelKeys`, `NFeedbackIconSize`) from the root
243
+ barrel and re-exported them directly from `najm-kit/app` so a Next Server
244
+ Component route can render them without authoring a local `"use client"`
245
+ wrapper. The root entry stays free of `"use client"` and continues to
246
+ share a single `NFeedbackDefaultsContext` across `index` and the adapters.
247
+
248
+ ## 2.10.0 - 2026-08-10
249
+
250
+ - Added a shared, failure-aware media source chain for `NAvatar`, `NImage`, and
251
+ the new Next-only `NNextImage` export. Sources reset cleanly when inputs or
252
+ cache versions change, duplicate candidates are tried once, consumer event
253
+ handlers are preserved, and failed avatars return to initials without a
254
+ broken-image glyph.
255
+ - `NAvatar` now uses a native lazy-loaded image so loading and error events are
256
+ observable, keeps initials visible until the image paints, supports native
257
+ `imageProps`, and actually tries `fallbackSrc` after a primary failure.
258
+ - Added provider-level status badge defaults and the public
259
+ `normalizeStatusToken` helper. Applications can configure translated status
260
+ labels, colors, icons, look, shape, and size once while explicit badge props
261
+ retain precedence.
262
+ - Added `NNextImage` and `NNextImageProps` to `najm-kit/next` only, preserving
263
+ Next Image `fill`, `sizes`, optimizer, and `unoptimized` controls without
264
+ adding Next.js to the root runtime graph.
265
+
266
+ ## 2.9.0 - 2026-08-09
267
+
268
+ - Added `najm-kit/server`, a pure UI bootstrap loader. `createUiBootstrapLoader()`
269
+ takes the application's own fetcher, endpoint paths, payload parsers, and
270
+ factory values, then runs the resources concurrently, unwraps the `{ data }`
271
+ envelope (or one the application selects), and falls back per resource so a
272
+ branding outage never discards a valid appearance. Failures are reported
273
+ through an optional structured `onDiagnostic` callback carrying resource,
274
+ reason, path, and status never a response body, header, cookie, or raw
275
+ thrown value. A `fallback()` that throws stays a visible error. The entry
276
+ imports no React, no Next.js, and no Node built-in, and re-exports
277
+ `parseNajmDesignConfig` so an appearance payload can be parsed without
278
+ reaching the root barrel.
279
+ - Added `najm-kit/server/react`, the React Server Component adapter.
280
+ `createReactServerUiBootstrap()` memoizes one bootstrap per request with
281
+ React's `cache()` and derives the per-resource accessors from it, so a root
282
+ layout, a nested layout, and a page share one resolution and one stable
283
+ snapshot for the whole render. Resolution is request-scoped only: separate
284
+ requests share no snapshot, failure, or diagnostic, and a transient outage is
285
+ retried on the next request. The `browser` export condition resolves to a
286
+ module that throws, so importing the adapter from a Client Component fails at
287
+ build time instead of shipping the application's fetcher and factory values
288
+ into a browser bundle.
289
+ - The root `najm-kit`, `najm-kit/next`, and `najm-kit/app` entries are unchanged
290
+ and do not reach either server entry.
291
+
292
+ ## 2.8.2 - 2026-08-08
293
+
294
+ - Added the framework-neutral `najm-kit/person-images` subpath. It ships a
295
+ built-in resolver for `child`, `adult`, `parent`, and `family` roles with
296
+ the seven WebP illustrations embedded as base64 data URLs, plus
297
+ `createPersonImageResolver` so an application can declare its own role
298
+ names (`teacher`, `student`, `doctor`, `driver`, …) and TypeScript catches
299
+ unknown role strings at the call site. The root `najm-kit` entry stays
300
+ unchanged and does not pull in the person images.
301
+
302
+ ## 2.8.1 - 2026-08-08
303
+
304
+ - Added schema-driven form development tools to `NajmAppProvider`. Passing
305
+ `formDevTools` enables F8 filling for every `NForm` and `WizardForm` without
306
+ an application helper or additional provider.
307
+ - Added built-in Zod 4 form-value generation with per-form overrides for
308
+ relation fields and other application-owned values. Existing explicit
309
+ `devTools.enabled` and `devTools.fill` usage remains supported.
310
+
311
+ ## 2.8.0 - 2026-08-08
312
+
313
+ - Added server-safe `najm-kit/format` helpers for currency minor units,
314
+ numbers, percentages, dates, times, relative time, tokens, local date inputs,
315
+ and slugs. Client applications can use the same contract reactively through
316
+ `NajmFormatProvider` and `useNajmFormat`.
317
+ - Added the server-safe `najm-kit/pagination` offset protocol, including bounded
318
+ page creation, total-aware continuation, probe-row continuation for APIs
319
+ without totals, and query cleanup that preserves meaningful `false` and `0`.
320
+ - Added the optional-peer `najm-kit/query` entry with offset infinite-query and
321
+ responsive paged/card-list hooks, plus shared card-pagination adapters and
322
+ localized continuation labels.
323
+ - Extended `NajmAppProvider` with formatting locale, currency, and placeholder
324
+ configuration so applications can bind language, time zone, and formatting
325
+ without another host bridge provider.
326
+ - Added shared media-query/card-viewport helpers and avatar-source utilities.
327
+
328
+ ## 2.6.2
329
+
330
+ - Added `NSidebarProvider` and `useNSidebar`, so sidebar state can be read from a distance. `NSidebar` renders beside the page content rather than around it, which left applications hand-rolling a context to hand `setMobileOpen` down to a page header — a wrapper component plus an aliased import at every call site. Wrap the shell in `NSidebarProvider` and `NPageHeader` now resolves both `onSidebarOpen` and `mobileBreakpoint` from it, so a header nested anywhere below renders a working mobile trigger with no props threaded to it. Also exports the `NSidebarContextValue` type.
331
+ - `NSidebar` resolves its open and collapsed state as explicit prop surrounding provider internal state. Passing `collapsed`, `mobileOpen`, `onCollapsedChange`, or `onMobileOpenChange` keeps behaving exactly as before, and a sidebar with no provider around it still owns its own state, so this is additive for every existing consumer.
332
+ - `logo` accepts a render prop, `({ collapsed, isMobile }) => ReactNode`, alongside the existing node. It receives the state the sidebar actually resolved including whatever `autoCollapseAt` decided — which consumers previously had to approximate with their own responsive classes, guessing at a breakpoint the sidebar had already computed. The mobile drawer always reports `collapsed: false`, matching how it renders. Exported as `SidebarLogoRender`.
333
+
334
+ ## 2.6.1
335
+
336
+ - Fixed the non-card `NPageHeader` bleed never taking effect. 2.6.0 cancelled the page padding with the Tailwind utilities `-mt-[var(--najm-section-gap,0px)]` and `-mx-[var(--najm-page-gutter,0px)]`, which only work if the consuming app's Tailwind build happens to emit those arbitrary classes they exist nowhere but inside this package's bundle, so a consumer could load a stylesheet without them and the header stayed exactly where it was. The offsets are inline styles now and no longer depend on the consumer's CSS pipeline.
337
+
338
+ ## 2.6.0
339
+
340
+ - Added `NThemePresets`, and wired it into `NThemeCustomizer` through `presets`, `selectedPresetId`, `presetsStatus`, `savedDesign`, `onPresetSelect`, `onPresetSave`, `onPresetDelete`, and `presetLabels`. The picker renders only when a host supplies both `presets` and `onPresetSelect`. It is presentational: the host owns where presets live and what saving one means, and selecting a row hands the design back so it can be previewed before anything is stored. Each row draws a swatch strip from the design's own `sidebar`, `primary`, `secondary`, `accent`, and `background` tokens; the selected row's check sits left in the success colour and per-row delete sits right. Omit `onPresetSave` or `onPresetDelete` to hide those controls. Deleting is pointer-only — Radix owns roving focus inside the listbox.
341
+ - **`NPageHeader` in non-card mode is now flush.** It rendered as a full-bleed bar (`border-b`, no radius) but still sat inside the padding `NPageLayout` applies, so it floated below and inside the page gutter and its bottom rule never met the sidebar header's. It now cancels that padding, and its base height goes `min-h-12` `min-h-14` to match `NSidebarHeader`. **Non-card page headers move up and out to the page edges, and grow 8px below `sm`.** Card mode is unchanged.
342
+ - `NPageLayout` publishes its resolved spacing as `--najm-page-gutter` and `--najm-section-gap`. Descendants could not previously cancel the page padding: a design config resolves the gutter to a literal, so the pre-existing `--page-gutter` / `--section-gap` variables only ever held the fallbacks. Full-bleed children should negate these, with `0px` fallbacks so they stay inert outside `NPageLayout`.
343
+ - Fixed `SelectInput` discarding `SelectItemType.icon`. The prop was declared but `renderItems` rendered the label alone, so every per-item icon was silently dropped. Items with an icon now render it before a truncating label; items without one are unchanged.
344
+
345
+ ## 2.5.0
346
+
347
+ - Added `NTableDefaultsProvider`, so an application supplies `paginationLabels` once instead of at every table. Labels merge per key, most specific first: a table's own `paginationLabels` override the provider's for the keys it sets, the provider covers the rest, and anything neither supplies falls back to the packaged English. Also exports `useNTableDefaults` and the `NTableDefaults` type.
348
+ - `value` is passed through the provider unmemoized; memoize it in the caller, or every table below re-renders with the shell.
349
+
350
+ ## 2.4.0
351
+
352
+ - `NTablePagination` renders numbered page buttons instead of `Page X of Y`. The window shows the first and last page, the current page, and one page either side, collapsing the rest into at most two gaps. The slot count is constant for any result longer than the window, so the bar does not change width as the reader pages through it, and a gap never stands in for a single page — that slot goes to the page instead.
353
+ - Added `paginationVariant`, defaulting to `"numbered"`. Pass `"compact"` to keep the previous position text with first/previous/next/last controls. **This changes the default appearance of every paginated `NTable`.**
354
+ - The numbered variant drops the first/last double chevrons, because page 1 and page N are now single-click targets of their own. Previous and next remain. The compact variant is unchanged.
355
+ - Numbered pages fall back to compact on their own when the page count is not trustworthy that is, under `manualPagination` with no `pageCount` supplied, where TanStack infers a count from the rows it happens to hold rather than from a result total. Numbering that would invite clicks on pages that may not exist is not rendered.
356
+ - Below the `sm` breakpoint the numbers give way to the position text; seven page buttons plus the rows-per-page select do not fit a phone.
357
+ - Added `paginationLabels` so the bar can be localized: `rowsPerPage`, `pagination`, `goToPage`, `currentPage`, `firstPage`, `previousPage`, `nextPage`, `lastPage`, `pageOf`, and `rowsSelected`. All optional, all falling back to the previous English strings.
358
+ - Pagination chevrons now mirror under `dir="rtl"`. They previously pointed against the reading direction in right-to-left layouts.
359
+ - The page controls are wrapped in a labelled `nav`, and the current page carries `aria-current="page"`.
360
+ - Exported `buildPageItems` and `NTablePageItem` for consumers that need the same windowing outside the table.
361
+
362
+ ## 2.2.1
363
+
364
+ - Fixed a regression in 2.2.0: the dynamic page size reported under `manualPagination` could oscillate. Card row height is measured from rendered cards, so it grows as images decode; feeding that back into the page size refetched, re-rendered, re-measured, and refetched again. A list visibly settled from one page size to another with the loading skeleton flashing twice. The report is now allowed once per container geometry, which does not depend on the rows inside it, so it terminates. A resize still re-arms it, and the debounce still waits for the measurement to settle before reporting.
365
+
366
+ ## 2.2.0
367
+
368
+ - Added `cardPagination` mode `infinite`: card lists continue on scroll instead of behind a button. No control and no end-of-list element render while the list is healthy. The continuation button appears only after an append failure, as the retry target.
369
+ - Infinite continuation is owned by `NTableCards`, so the sentinel lives inside the card list's own `NajmScroll` viewport and uses it as the observer root. An appending page renders shaped card placeholders at the grid tail rather than a spinner in a fixed strip, and the polite `aria-live` announcement of appended row counts is preserved.
370
+ - `cardPagination: { mode: "all" }` is now honored in table mode as well as cards: every supplied row renders and no pagination controls are shown.
371
+ - `dynamicHeight` now takes effect under `manualPagination`. The measured page size is reported through the ordinary `onPaginationChange` callback, debounced, so server-paginated tables fill their container while the consumer keeps ownership of fetching. `maxHeight` stays caller-owned under manual pagination, and no page size is reported before the first measurement.
372
+ - Added `calculateCardPageSize`, floored to whole card rows, published as `calculatedCardPageSize`. A card grid page no longer ends in a ragged partial row. This deliberately differs from `calculateCardSkeletonCount`, which ceils because overfilling placeholders is harmless.
373
+ - Removed the deprecated `baseUrl` compiler option from the package tsconfig; `paths` resolves relative to the config file.
374
+
375
+ ## 2.1.56
376
+
377
+ - Honor `showIcon` on NTable `select` and `combobox` filters. The leading filter icon is still shown by default; pass `showIcon: false` on a filter to hide it.
378
+
379
+ ## 2.1.55
380
+
381
+ - Added controlled remote-search and loading-state props to `ComboboxInput` and `FormInput type="combobox"` while preserving client filtering by default.
382
+
383
+ ## 2.1.53 - 2026-08-05
384
+
385
+ - Add typed `NBarChart`, `NLineChart`, `NPieChart`, and `NStatusBreakdown`
386
+ components with caller-formatted generic data, accessible summaries, empty
387
+ states, responsive RTL-safe layouts, and shape-matched loading skeletons.
388
+ - Default chart colors to the live `--chart-1` through `--chart-5` theme
389
+ variables, cycle deterministically after five items, and retain explicit
390
+ per-series/item color overrides.
391
+ - Add preset and numeric chart diameter contracts to `NPieChart` and
392
+ `NDonutCard`, including narrow-container shrinking without clipped legends.
393
+ - Make `NDonutCard` item colors optional and add accessible loading states to
394
+ `NDonutCard` and `NStatCard`.
395
+ - Add public API tests, component tests, README guidance, and playground
396
+ examples for the chart and loading contracts.
397
+
398
+ ## 2.1.49 - 2026-08-04
399
+
400
+ ### ImageInput and AvatarInput
401
+
402
+ - Add `previewAlt`, `fallbackImage`, `fallbackAlt`, `unavailableContent`,
403
+ `imageClassName`, `onPreviewError`, `replaceAriaLabel`, and `clearAriaLabel`
404
+ to `ImageInputProps`. `AvatarInput` forwards every new prop unchanged.
405
+ - Preview sources resolve in this priority order: `value`, `fallbackImage`,
406
+ `defaultImage`. Candidate URLs are deduplicated so a failing primary URL is
407
+ never retried through multiple stages. When every candidate fails, the
408
+ broken `<img>` is unmounted and `unavailableContent` (or a neutral default)
409
+ is rendered in its place.
410
+ - `imageVersion` is appended safely to relative, absolute, queried, and
411
+ fragmented URLs. `data:`, `blob:`, `javascript:`, and `file:` URLs are
412
+ left unchanged.
413
+ - Expose `data-image-input-state="empty" | "preview" | "fallback" | "unavailable"`
414
+ on the preview container for styling, testing, and consumer diagnostics.
415
+ - File selection is race-safe: stale `FileReader` completions cannot replace
416
+ a newer value. Object URLs created by the component are tracked so
417
+ consumer-owned blob URLs are never revoked.
418
+ - Replace and clear controls are real `<button>` elements, are reachable
419
+ with the keyboard, and stay visible on touch and coarse-pointer devices.
420
+ Only on `(hover: hover) and (pointer: fine)` desktops do the controls fall
421
+ back to a hover/focus reveal. `focus-visible` always restores visibility.
422
+ - Use logical positioning (`end-*`) so the clear button works correctly in
423
+ RTL layouts.
424
+ - Re-export `ImageInputPreviewSource` and `ImageInputPreviewError` from
425
+ `najm-kit/components/inputs` and the package root.
426
+
427
+ ## 2.1.48 - 2026-08-04
428
+
429
+ - Keep responsive card row actions visible on phone, tablet, and touch input,
430
+ while retaining hover and keyboard-focus reveal on fine-pointer desktops.
431
+ - Size table and card loading skeletons from the measured body and active grid,
432
+ and keep loading borders, radius, color, and shadow aligned with loaded
433
+ surfaces.
434
+ - Add the exported `NTableCardPagination` and `NTableLoadMorePagination`
435
+ contracts for paged, complete supplied-data, and explicit server-backed Load
436
+ more card presentation, including guarded append/retry behavior and accessible
437
+ loading, result, error, and terminal feedback.