najm-kit 2.8.1 → 2.9.0
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 +145 -109
- package/README.md +205 -5
- package/dist/{NajmUIProvider-BSbXaqak.d.ts → NajmUIProvider-CI_73GEp.d.ts} +5 -162
- package/dist/adapters/app.d.ts +2 -1
- package/dist/adapters/next.d.ts +2 -1
- package/dist/chunk-7H6NFBQT.mjs +75 -0
- package/dist/chunk-BEGOH366.mjs +12 -0
- package/dist/chunk-JUYT2ISO.mjs +404 -0
- package/dist/{chunk-6OOBAEH2.mjs → chunk-TFHWLE7N.mjs} +1 -403
- package/dist/design-config-D_x1GCu3.d.ts +17 -0
- package/dist/design-types-Rkpt8Pg1.d.ts +159 -0
- package/dist/index.d.ts +5 -17
- package/dist/index.mjs +7 -14
- package/dist/json.mjs +3 -2
- package/dist/person-images.d.ts +98 -0
- package/dist/person-images.mjs +86 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.mjs +2 -0
- package/dist/server/react.d.ts +16 -0
- package/dist/server/react.mjs +23 -0
- package/dist/server/reactClientGuard.d.ts +2 -0
- package/dist/server/reactClientGuard.mjs +4 -0
- package/dist/uiBootstrap-Ci6K5j5t.d.ts +93 -0
- package/package.json +21 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.9.0 - 2026-08-09
|
|
4
|
+
|
|
5
|
+
- Added `najm-kit/server`, a pure UI bootstrap loader. `createUiBootstrapLoader()`
|
|
6
|
+
takes the application's own fetcher, endpoint paths, payload parsers, and
|
|
7
|
+
factory values, then runs the resources concurrently, unwraps the `{ data }`
|
|
8
|
+
envelope (or one the application selects), and falls back per resource so a
|
|
9
|
+
branding outage never discards a valid appearance. Failures are reported
|
|
10
|
+
through an optional structured `onDiagnostic` callback carrying resource,
|
|
11
|
+
reason, path, and status — never a response body, header, cookie, or raw
|
|
12
|
+
thrown value. A `fallback()` that throws stays a visible error. The entry
|
|
13
|
+
imports no React, no Next.js, and no Node built-in, and re-exports
|
|
14
|
+
`parseNajmDesignConfig` so an appearance payload can be parsed without
|
|
15
|
+
reaching the root barrel.
|
|
16
|
+
- Added `najm-kit/server/react`, the React Server Component adapter.
|
|
17
|
+
`createReactServerUiBootstrap()` memoizes one bootstrap per request with
|
|
18
|
+
React's `cache()` and derives the per-resource accessors from it, so a root
|
|
19
|
+
layout, a nested layout, and a page share one resolution and one stable
|
|
20
|
+
snapshot for the whole render. Resolution is request-scoped only: separate
|
|
21
|
+
requests share no snapshot, failure, or diagnostic, and a transient outage is
|
|
22
|
+
retried on the next request. The `browser` export condition resolves to a
|
|
23
|
+
module that throws, so importing the adapter from a Client Component fails at
|
|
24
|
+
build time instead of shipping the application's fetcher and factory values
|
|
25
|
+
into a browser bundle.
|
|
26
|
+
- The root `najm-kit`, `najm-kit/next`, and `najm-kit/app` entries are unchanged
|
|
27
|
+
and do not reach either server entry.
|
|
28
|
+
|
|
29
|
+
## 2.8.2 - 2026-08-08
|
|
30
|
+
|
|
31
|
+
- Added the framework-neutral `najm-kit/person-images` subpath. It ships a
|
|
32
|
+
built-in resolver for `child`, `adult`, `parent`, and `family` roles with
|
|
33
|
+
the seven WebP illustrations embedded as base64 data URLs, plus
|
|
34
|
+
`createPersonImageResolver` so an application can declare its own role
|
|
35
|
+
names (`teacher`, `student`, `doctor`, `driver`, …) and TypeScript catches
|
|
36
|
+
unknown role strings at the call site. The root `najm-kit` entry stays
|
|
37
|
+
unchanged and does not pull in the person images.
|
|
38
|
+
|
|
3
39
|
## 2.8.1 - 2026-08-08
|
|
4
40
|
|
|
5
41
|
- Added schema-driven form development tools to `NajmAppProvider`. Passing
|
|
@@ -27,112 +63,112 @@
|
|
|
27
63
|
- Added shared media-query/card-viewport helpers and avatar-source utilities.
|
|
28
64
|
|
|
29
65
|
## 2.6.2
|
|
30
|
-
|
|
31
|
-
- 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.
|
|
32
|
-
- `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.
|
|
33
|
-
- `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`.
|
|
34
|
-
|
|
35
|
-
## 2.6.1
|
|
36
|
-
|
|
37
|
-
- 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.
|
|
38
|
-
|
|
39
|
-
## 2.6.0
|
|
40
|
-
|
|
41
|
-
- 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.
|
|
42
|
-
- **`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.
|
|
43
|
-
- `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`.
|
|
44
|
-
- 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.
|
|
45
|
-
|
|
46
|
-
## 2.5.0
|
|
47
|
-
|
|
48
|
-
- 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.
|
|
49
|
-
- `value` is passed through the provider unmemoized; memoize it in the caller, or every table below re-renders with the shell.
|
|
50
|
-
|
|
51
|
-
## 2.4.0
|
|
52
|
-
|
|
53
|
-
- `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.
|
|
54
|
-
- 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`.**
|
|
55
|
-
- 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.
|
|
56
|
-
- 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.
|
|
57
|
-
- 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.
|
|
58
|
-
- 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.
|
|
59
|
-
- Pagination chevrons now mirror under `dir="rtl"`. They previously pointed against the reading direction in right-to-left layouts.
|
|
60
|
-
- The page controls are wrapped in a labelled `nav`, and the current page carries `aria-current="page"`.
|
|
61
|
-
- Exported `buildPageItems` and `NTablePageItem` for consumers that need the same windowing outside the table.
|
|
62
|
-
|
|
63
|
-
## 2.2.1
|
|
64
|
-
|
|
65
|
-
- 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.
|
|
66
|
-
|
|
67
|
-
## 2.2.0
|
|
68
|
-
|
|
69
|
-
- 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.
|
|
70
|
-
- 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.
|
|
71
|
-
- `cardPagination: { mode: "all" }` is now honored in table mode as well as cards: every supplied row renders and no pagination controls are shown.
|
|
72
|
-
- `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.
|
|
73
|
-
- 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.
|
|
74
|
-
- Removed the deprecated `baseUrl` compiler option from the package tsconfig; `paths` resolves relative to the config file.
|
|
75
|
-
|
|
76
|
-
## 2.1.56
|
|
77
|
-
|
|
78
|
-
- 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.
|
|
79
|
-
|
|
80
|
-
## 2.1.55
|
|
81
|
-
|
|
82
|
-
- Added controlled remote-search and loading-state props to `ComboboxInput` and `FormInput type="combobox"` while preserving client filtering by default.
|
|
83
|
-
|
|
84
|
-
## 2.1.53 - 2026-08-05
|
|
85
|
-
|
|
86
|
-
- Add typed `NBarChart`, `NLineChart`, `NPieChart`, and `NStatusBreakdown`
|
|
87
|
-
components with caller-formatted generic data, accessible summaries, empty
|
|
88
|
-
states, responsive RTL-safe layouts, and shape-matched loading skeletons.
|
|
89
|
-
- Default chart colors to the live `--chart-1` through `--chart-5` theme
|
|
90
|
-
variables, cycle deterministically after five items, and retain explicit
|
|
91
|
-
per-series/item color overrides.
|
|
92
|
-
- Add preset and numeric chart diameter contracts to `NPieChart` and
|
|
93
|
-
`NDonutCard`, including narrow-container shrinking without clipped legends.
|
|
94
|
-
- Make `NDonutCard` item colors optional and add accessible loading states to
|
|
95
|
-
`NDonutCard` and `NStatCard`.
|
|
96
|
-
- Add public API tests, component tests, README guidance, and playground
|
|
97
|
-
examples for the chart and loading contracts.
|
|
98
|
-
|
|
99
|
-
## 2.1.49 - 2026-08-04
|
|
100
|
-
|
|
101
|
-
### ImageInput and AvatarInput
|
|
102
|
-
|
|
103
|
-
- Add `previewAlt`, `fallbackImage`, `fallbackAlt`, `unavailableContent`,
|
|
104
|
-
`imageClassName`, `onPreviewError`, `replaceAriaLabel`, and `clearAriaLabel`
|
|
105
|
-
to `ImageInputProps`. `AvatarInput` forwards every new prop unchanged.
|
|
106
|
-
- Preview sources resolve in this priority order: `value`, `fallbackImage`,
|
|
107
|
-
`defaultImage`. Candidate URLs are deduplicated so a failing primary URL is
|
|
108
|
-
never retried through multiple stages. When every candidate fails, the
|
|
109
|
-
broken `<img>` is unmounted and `unavailableContent` (or a neutral default)
|
|
110
|
-
is rendered in its place.
|
|
111
|
-
- `imageVersion` is appended safely to relative, absolute, queried, and
|
|
112
|
-
fragmented URLs. `data:`, `blob:`, `javascript:`, and `file:` URLs are
|
|
113
|
-
left unchanged.
|
|
114
|
-
- Expose `data-image-input-state="empty" | "preview" | "fallback" | "unavailable"`
|
|
115
|
-
on the preview container for styling, testing, and consumer diagnostics.
|
|
116
|
-
- File selection is race-safe: stale `FileReader` completions cannot replace
|
|
117
|
-
a newer value. Object URLs created by the component are tracked so
|
|
118
|
-
consumer-owned blob URLs are never revoked.
|
|
119
|
-
- Replace and clear controls are real `<button>` elements, are reachable
|
|
120
|
-
with the keyboard, and stay visible on touch and coarse-pointer devices.
|
|
121
|
-
Only on `(hover: hover) and (pointer: fine)` desktops do the controls fall
|
|
122
|
-
back to a hover/focus reveal. `focus-visible` always restores visibility.
|
|
123
|
-
- Use logical positioning (`end-*`) so the clear button works correctly in
|
|
124
|
-
RTL layouts.
|
|
125
|
-
- Re-export `ImageInputPreviewSource` and `ImageInputPreviewError` from
|
|
126
|
-
`najm-kit/components/inputs` and the package root.
|
|
127
|
-
|
|
128
|
-
## 2.1.48 - 2026-08-04
|
|
129
|
-
|
|
130
|
-
- Keep responsive card row actions visible on phone, tablet, and touch input,
|
|
131
|
-
while retaining hover and keyboard-focus reveal on fine-pointer desktops.
|
|
132
|
-
- Size table and card loading skeletons from the measured body and active grid,
|
|
133
|
-
and keep loading borders, radius, color, and shadow aligned with loaded
|
|
134
|
-
surfaces.
|
|
135
|
-
- Add the exported `NTableCardPagination` and `NTableLoadMorePagination`
|
|
136
|
-
contracts for paged, complete supplied-data, and explicit server-backed Load
|
|
137
|
-
more card presentation, including guarded append/retry behavior and accessible
|
|
138
|
-
loading, result, error, and terminal feedback.
|
|
66
|
+
|
|
67
|
+
- 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.
|
|
68
|
+
- `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.
|
|
69
|
+
- `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`.
|
|
70
|
+
|
|
71
|
+
## 2.6.1
|
|
72
|
+
|
|
73
|
+
- 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.
|
|
74
|
+
|
|
75
|
+
## 2.6.0
|
|
76
|
+
|
|
77
|
+
- 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.
|
|
78
|
+
- **`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.
|
|
79
|
+
- `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`.
|
|
80
|
+
- 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.
|
|
81
|
+
|
|
82
|
+
## 2.5.0
|
|
83
|
+
|
|
84
|
+
- 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.
|
|
85
|
+
- `value` is passed through the provider unmemoized; memoize it in the caller, or every table below re-renders with the shell.
|
|
86
|
+
|
|
87
|
+
## 2.4.0
|
|
88
|
+
|
|
89
|
+
- `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.
|
|
90
|
+
- 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`.**
|
|
91
|
+
- 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.
|
|
92
|
+
- 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.
|
|
93
|
+
- 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.
|
|
94
|
+
- 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.
|
|
95
|
+
- Pagination chevrons now mirror under `dir="rtl"`. They previously pointed against the reading direction in right-to-left layouts.
|
|
96
|
+
- The page controls are wrapped in a labelled `nav`, and the current page carries `aria-current="page"`.
|
|
97
|
+
- Exported `buildPageItems` and `NTablePageItem` for consumers that need the same windowing outside the table.
|
|
98
|
+
|
|
99
|
+
## 2.2.1
|
|
100
|
+
|
|
101
|
+
- 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.
|
|
102
|
+
|
|
103
|
+
## 2.2.0
|
|
104
|
+
|
|
105
|
+
- 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.
|
|
106
|
+
- 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.
|
|
107
|
+
- `cardPagination: { mode: "all" }` is now honored in table mode as well as cards: every supplied row renders and no pagination controls are shown.
|
|
108
|
+
- `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.
|
|
109
|
+
- 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.
|
|
110
|
+
- Removed the deprecated `baseUrl` compiler option from the package tsconfig; `paths` resolves relative to the config file.
|
|
111
|
+
|
|
112
|
+
## 2.1.56
|
|
113
|
+
|
|
114
|
+
- 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.
|
|
115
|
+
|
|
116
|
+
## 2.1.55
|
|
117
|
+
|
|
118
|
+
- Added controlled remote-search and loading-state props to `ComboboxInput` and `FormInput type="combobox"` while preserving client filtering by default.
|
|
119
|
+
|
|
120
|
+
## 2.1.53 - 2026-08-05
|
|
121
|
+
|
|
122
|
+
- Add typed `NBarChart`, `NLineChart`, `NPieChart`, and `NStatusBreakdown`
|
|
123
|
+
components with caller-formatted generic data, accessible summaries, empty
|
|
124
|
+
states, responsive RTL-safe layouts, and shape-matched loading skeletons.
|
|
125
|
+
- Default chart colors to the live `--chart-1` through `--chart-5` theme
|
|
126
|
+
variables, cycle deterministically after five items, and retain explicit
|
|
127
|
+
per-series/item color overrides.
|
|
128
|
+
- Add preset and numeric chart diameter contracts to `NPieChart` and
|
|
129
|
+
`NDonutCard`, including narrow-container shrinking without clipped legends.
|
|
130
|
+
- Make `NDonutCard` item colors optional and add accessible loading states to
|
|
131
|
+
`NDonutCard` and `NStatCard`.
|
|
132
|
+
- Add public API tests, component tests, README guidance, and playground
|
|
133
|
+
examples for the chart and loading contracts.
|
|
134
|
+
|
|
135
|
+
## 2.1.49 - 2026-08-04
|
|
136
|
+
|
|
137
|
+
### ImageInput and AvatarInput
|
|
138
|
+
|
|
139
|
+
- Add `previewAlt`, `fallbackImage`, `fallbackAlt`, `unavailableContent`,
|
|
140
|
+
`imageClassName`, `onPreviewError`, `replaceAriaLabel`, and `clearAriaLabel`
|
|
141
|
+
to `ImageInputProps`. `AvatarInput` forwards every new prop unchanged.
|
|
142
|
+
- Preview sources resolve in this priority order: `value`, `fallbackImage`,
|
|
143
|
+
`defaultImage`. Candidate URLs are deduplicated so a failing primary URL is
|
|
144
|
+
never retried through multiple stages. When every candidate fails, the
|
|
145
|
+
broken `<img>` is unmounted and `unavailableContent` (or a neutral default)
|
|
146
|
+
is rendered in its place.
|
|
147
|
+
- `imageVersion` is appended safely to relative, absolute, queried, and
|
|
148
|
+
fragmented URLs. `data:`, `blob:`, `javascript:`, and `file:` URLs are
|
|
149
|
+
left unchanged.
|
|
150
|
+
- Expose `data-image-input-state="empty" | "preview" | "fallback" | "unavailable"`
|
|
151
|
+
on the preview container for styling, testing, and consumer diagnostics.
|
|
152
|
+
- File selection is race-safe: stale `FileReader` completions cannot replace
|
|
153
|
+
a newer value. Object URLs created by the component are tracked so
|
|
154
|
+
consumer-owned blob URLs are never revoked.
|
|
155
|
+
- Replace and clear controls are real `<button>` elements, are reachable
|
|
156
|
+
with the keyboard, and stay visible on touch and coarse-pointer devices.
|
|
157
|
+
Only on `(hover: hover) and (pointer: fine)` desktops do the controls fall
|
|
158
|
+
back to a hover/focus reveal. `focus-visible` always restores visibility.
|
|
159
|
+
- Use logical positioning (`end-*`) so the clear button works correctly in
|
|
160
|
+
RTL layouts.
|
|
161
|
+
- Re-export `ImageInputPreviewSource` and `ImageInputPreviewError` from
|
|
162
|
+
`najm-kit/components/inputs` and the package root.
|
|
163
|
+
|
|
164
|
+
## 2.1.48 - 2026-08-04
|
|
165
|
+
|
|
166
|
+
- Keep responsive card row actions visible on phone, tablet, and touch input,
|
|
167
|
+
while retaining hover and keyboard-focus reveal on fine-pointer desktops.
|
|
168
|
+
- Size table and card loading skeletons from the measured body and active grid,
|
|
169
|
+
and keep loading borders, radius, color, and shadow aligned with loaded
|
|
170
|
+
surfaces.
|
|
171
|
+
- Add the exported `NTableCardPagination` and `NTableLoadMorePagination`
|
|
172
|
+
contracts for paged, complete supplied-data, and explicit server-backed Load
|
|
173
|
+
more card presentation, including guarded append/retry behavior and accessible
|
|
174
|
+
loading, result, error, and terminal feedback.
|
package/README.md
CHANGED
|
@@ -507,9 +507,209 @@ const data = [
|
|
|
507
507
|
/>
|
|
508
508
|
```
|
|
509
509
|
|
|
510
|
-
### Server-backed combobox search
|
|
510
|
+
### Server-backed combobox search
|
|
511
|
+
|
|
512
|
+
`ComboboxInput` and `FormInput type="combobox"` can delegate filtering to a
|
|
513
|
+
server by setting `shouldFilter={false}` and handling `onSearchChange`. Use
|
|
514
|
+
`loading` and `loadingMessage` while replacement options are being fetched.
|
|
515
|
+
Client-side filtering remains the default.
|
|
516
|
+
|
|
517
|
+
## Person image fallbacks (`najm-kit/person-images`)
|
|
518
|
+
|
|
519
|
+
A framework-neutral, React-free subpath that resolves person-image fallbacks
|
|
520
|
+
for any application. The seven WebP illustrations are embedded as base64 data
|
|
521
|
+
URLs in the published bundle, so consumers do not need to copy package files
|
|
522
|
+
into `public/` or wire an asset server.
|
|
523
|
+
|
|
524
|
+
```ts
|
|
525
|
+
import { getPersonImage } from "najm-kit/person-images";
|
|
526
|
+
|
|
527
|
+
const childSrc = getPersonImage({
|
|
528
|
+
image: child.image,
|
|
529
|
+
role: "child",
|
|
530
|
+
gender: child.gender,
|
|
531
|
+
});
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
Built-in roles:
|
|
535
|
+
|
|
536
|
+
| Role | Default | Female | Male |
|
|
537
|
+
| -------- | ---------------- | --------------- | --------------- |
|
|
538
|
+
| `child` | male child art | female child | male child |
|
|
539
|
+
| `adult` | male adult art | female adult | male adult |
|
|
540
|
+
| `parent` | male parent art | female parent | male parent |
|
|
541
|
+
| `family` | neutral family | neutral family | neutral family |
|
|
542
|
+
|
|
543
|
+
Resolution precedence, for every call:
|
|
544
|
+
|
|
545
|
+
1. A real `image` (anything that survives `resolveAvatarSrc`).
|
|
546
|
+
2. A per-call `fallback` that is not blank and is not the `noavatar.png`
|
|
547
|
+
sentinel.
|
|
548
|
+
3. The configured role's gender variant, or the role's required `default`
|
|
549
|
+
when the variant or the gender is missing.
|
|
550
|
+
|
|
551
|
+
The per-call `fallback` is treated like a real source: an empty string, a
|
|
552
|
+
blank trimmed value, or any `noavatar.png` path falls through to the role
|
|
553
|
+
default. The Kafil data is a worked example: children use `role: "child"`,
|
|
554
|
+
households use `role: "family"`, sponsors, staff, applicants, and delivery
|
|
555
|
+
staff use `role: "adult"`, and a household parent uses `role: "parent"`
|
|
556
|
+
after the family dashboard maps its relationship value (`mother`, `mère`,
|
|
557
|
+
`madre`, `أم`, …) to `F`, `M`, or `null` at the feature boundary.
|
|
558
|
+
|
|
559
|
+
### Custom roles
|
|
560
|
+
|
|
561
|
+
`createPersonImageResolver` returns a typed resolver that accepts the
|
|
562
|
+
application's own role names. Unknown role strings fail type checking:
|
|
563
|
+
|
|
564
|
+
```ts
|
|
565
|
+
import { createPersonImageResolver } from "najm-kit/person-images";
|
|
566
|
+
|
|
567
|
+
const getSmsPersonImage = createPersonImageResolver({
|
|
568
|
+
teacher: {
|
|
569
|
+
default: "/images/teachers/default.webp",
|
|
570
|
+
female: "/images/teachers/female.webp",
|
|
571
|
+
male: "/images/teachers/male.webp",
|
|
572
|
+
},
|
|
573
|
+
student: {
|
|
574
|
+
default: "/images/students/default.webp",
|
|
575
|
+
female: "/images/students/female.webp",
|
|
576
|
+
male: "/images/students/male.webp",
|
|
577
|
+
},
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
const teacherSrc = getSmsPersonImage({
|
|
581
|
+
image: teacher.image,
|
|
582
|
+
role: "teacher",
|
|
583
|
+
gender: teacher.gender,
|
|
584
|
+
});
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
The factory merges custom definitions over the built-in map. A custom `child`
|
|
588
|
+
override replaces the built-in child art for that application alone — the
|
|
589
|
+
package itself is untouched, and other consumers keep their built-in
|
|
590
|
+
fallbacks.
|
|
591
|
+
|
|
592
|
+
Custom paths may be application-relative URLs, managed API URLs, CDN URLs,
|
|
593
|
+
or data URLs. najm-kit does not fetch, upload, authorize, or persist them.
|
|
594
|
+
|
|
595
|
+
### Per-call fallback override
|
|
596
|
+
|
|
597
|
+
Every call accepts a `fallback`. It overrides the role default for that call
|
|
598
|
+
only, after a real `image` and before the role's gender variant:
|
|
599
|
+
|
|
600
|
+
```ts
|
|
601
|
+
getPersonImage({ image: child.image, role: "child", gender: child.gender, fallback: child.placeholder });
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
## Server UI bootstrap (`najm-kit/server`, `najm-kit/server/react`)
|
|
606
|
+
|
|
607
|
+
An application that renders its own theme and its own logos on the server ends
|
|
608
|
+
up writing the same module every time: fetch the public endpoints, unwrap the
|
|
609
|
+
`data` envelope, validate the payload, fall back to the built-in assets when
|
|
610
|
+
any of that fails, and run the resources in parallel. These two entries own
|
|
611
|
+
that mechanism. What stays with the application is what is genuinely
|
|
612
|
+
application-specific — how a request reaches its own backend, which paths it
|
|
613
|
+
serves, what a valid payload looks like, what the factory values are, and where
|
|
614
|
+
a diagnostic goes.
|
|
615
|
+
|
|
616
|
+
Neither entry is re-exported from `najm-kit`, `najm-kit/next`, or
|
|
617
|
+
`najm-kit/app`. `najm-kit/server` imports no React at all, so a route handler
|
|
618
|
+
or a plain script can use it.
|
|
619
|
+
|
|
620
|
+
### The application's one server module
|
|
621
|
+
|
|
622
|
+
```ts
|
|
623
|
+
// src/lib/serverLoader.ts
|
|
624
|
+
import "server-only";
|
|
625
|
+
|
|
626
|
+
import { parseNajmDesignConfig } from "najm-kit/server";
|
|
627
|
+
import { createReactServerUiBootstrap } from "najm-kit/server/react";
|
|
628
|
+
|
|
629
|
+
export const serverUi = createReactServerUiBootstrap({
|
|
630
|
+
fetcher: async (path) => {
|
|
631
|
+
const { server } = await import("@app/server");
|
|
632
|
+
return server.fetch(new Request(`http://internal${path}`));
|
|
633
|
+
},
|
|
634
|
+
resources: {
|
|
635
|
+
appearance: {
|
|
636
|
+
path: "/api/appearance",
|
|
637
|
+
parse: parseAppearance, // returns undefined or throws to reject
|
|
638
|
+
fallback: getFactoryAppearance, // called per load
|
|
639
|
+
},
|
|
640
|
+
branding: {
|
|
641
|
+
path: "/api/branding",
|
|
642
|
+
parse: parseBranding,
|
|
643
|
+
fallback: getFactoryBranding,
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
onDiagnostic: (diagnostic) => {
|
|
647
|
+
console.warn(`[ui-bootstrap] ${diagnostic.resource} ${diagnostic.reason}`, diagnostic);
|
|
648
|
+
},
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
export const loadServerUiBootstrap = serverUi.load;
|
|
652
|
+
export const { appearance: loadServerAppearance, branding: loadServerBranding } =
|
|
653
|
+
serverUi.loaders;
|
|
654
|
+
```
|
|
511
655
|
|
|
512
|
-
`
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
656
|
+
`load()` resolves every resource; `loaders.<name>()` and `loadResource(name)`
|
|
657
|
+
read one off the same resolution. Resource names, payload types, and the number
|
|
658
|
+
of resources are the application's — the snapshot type is inferred from the
|
|
659
|
+
`resources` object, so `snapshot.branding` is your branding type and not a
|
|
660
|
+
package interface.
|
|
661
|
+
|
|
662
|
+
### Call the factory once, at module scope
|
|
663
|
+
|
|
664
|
+
`createReactServerUiBootstrap()` builds one `React.cache()` entry. Calling it
|
|
665
|
+
inside a layout, page, or component builds a fresh one per call and shares
|
|
666
|
+
nothing. Every server boundary in a render must import the same module.
|
|
667
|
+
|
|
668
|
+
The cache is React's, so it is request-scoped and nothing else: separate
|
|
669
|
+
requests never see each other's snapshot or each other's failure, and a
|
|
670
|
+
transient outage is retried on the next request rather than pinned into a
|
|
671
|
+
process-global. That also rules out a module `Map`, a module promise,
|
|
672
|
+
`unstable_cache`, `"use cache"`, or a durable cache here — every one of them
|
|
673
|
+
would leak one visitor's render into another's.
|
|
674
|
+
|
|
675
|
+
The snapshot is deliberately stable for the length of one render. A settings
|
|
676
|
+
surface that saves appearance or branding updates the client provider and then
|
|
677
|
+
refreshes or navigates into a new render to observe the persisted result.
|
|
678
|
+
|
|
679
|
+
Outside a render — route handlers, server actions, scripts — use
|
|
680
|
+
`createUiBootstrapLoader()` from `najm-kit/server` directly. There is no request
|
|
681
|
+
cache for `cache()` to write to there, so the adapter would silently re-fetch
|
|
682
|
+
per call.
|
|
683
|
+
|
|
684
|
+
### Failure behaviour
|
|
685
|
+
|
|
686
|
+
Resources fall back independently: a branding outage never discards a valid
|
|
687
|
+
appearance. Each failure calls `onDiagnostic` once with a `reason` of
|
|
688
|
+
`fetch-failed`, `response-not-ok`, `invalid-json`, `invalid-envelope`, or
|
|
689
|
+
`invalid-payload`, plus the path and — for a non-success response — the status.
|
|
690
|
+
Diagnostics never carry response bodies, headers, cookies, or raw thrown
|
|
691
|
+
values; `error` is a normalized `"<name>: <message>"` for an `Error` and the
|
|
692
|
+
value's type for anything else.
|
|
693
|
+
|
|
694
|
+
A `fallback()` that throws is **not** caught. A missing factory theme is the
|
|
695
|
+
application's configuration error, and a second fallback would only hide it.
|
|
696
|
+
|
|
697
|
+
Falling back is right for *public* appearance and branding, where the worst case
|
|
698
|
+
is a visitor seeing the built-in logo. It is not a general rule: do not route
|
|
699
|
+
authenticated, financial, or privacy-sensitive reads through this, because a
|
|
700
|
+
silent fallback there hides an outage behind plausible-looking data.
|
|
701
|
+
|
|
702
|
+
### Envelopes
|
|
703
|
+
|
|
704
|
+
`select` defaults to Najm's `{ data }` envelope. Applications behind a different
|
|
705
|
+
envelope pass their own at the loader level or per resource; returning the
|
|
706
|
+
payload unchanged is a valid selector, and throwing rejects the response as
|
|
707
|
+
`invalid-envelope`.
|
|
708
|
+
|
|
709
|
+
### Client Components
|
|
710
|
+
|
|
711
|
+
`najm-kit/server/react` maps the `browser` export condition to a module that
|
|
712
|
+
throws, so importing it from a Client Component fails the build with an
|
|
713
|
+
explanation rather than shipping the application's fetcher and factory values
|
|
714
|
+
into a browser bundle. Seed the client from the server snapshot through
|
|
715
|
+
`NajmAppProvider` instead.
|