Skip to content

Commit 8eda9f4

Browse files
puikinshclaude
andcommitted
Release 0.2.0
Date the 0.2.0 changelog entry (package rename, per-file RSC-preserving build, optional plugin peers, lifecycle/context fixes, CI) and refresh CLAUDE.md for the new name, build pipeline, and testing setup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 2404f66 commit 8eda9f4

3 files changed

Lines changed: 59 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Changelog
22

3-
All notable changes to **adminlte-react** are documented in this file.
3+
All notable changes to **@adminlte/react** are documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased]
8+
## [0.2.0] - 2026-06-10
99

1010
### Added
1111

@@ -38,9 +38,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3838
the external AdminLTE HTML docs; it now renders only when `docsHref` is set (point it at your own
3939
docs). The demo points it at its in-app `/docs`.
4040

41+
### Changed
42+
43+
- **Package renamed to `@adminlte/react`** (previously `adminlte-react`), published under the
44+
`@adminlte` npm org. Update imports: `from '@adminlte/react'` and
45+
`import '@adminlte/react/css'`.
46+
- **Build: per-file ESM output (RSC boundaries preserved).** The package is no longer a single
47+
bundled file stamped with a blanket `"use client"`. Each source module now compiles to its own
48+
`dist/` file, so the `'use client'` directive survives per component: server-component-authored
49+
widgets (`Button`, `Input`, `SmallBox`, `DashboardLayout`, …) stay Server Components for
50+
consumers, and bundlers can tree-shake through the barrel. CommonJS output was dropped (the
51+
package is ESM-only; it targets Next.js 14+ / modern bundlers). Sourcemaps are no longer
52+
published (≈7 MB lighter tarball; dist shrank from ≈9 MB to ≈0.75 MB).
53+
- **All heavy plugins are now optional peer dependencies** (`apexcharts`, `flatpickr`,
54+
`jsvectormap`, `quill`, `sortablejs`, `tabulator-tables`, `tom-select`). Previously apexcharts,
55+
jsvectormap, and sortablejs were silently bundled in; now all plugins follow the documented
56+
model — install only the ones whose components you use. They are still lazy-loaded via dynamic
57+
`import()`, which Next.js code-splits per page.
58+
- `flattenMenuToCommands` / `CommandItem` moved to a server-safe module so Server Components
59+
(e.g. `DashboardLayout`) can flatten menus during server render. Barrel imports are unchanged.
60+
- `ApexChart` props are now typed (`config: ApexOptions`, typed `series`) instead of `any`, and
61+
prop changes update the chart in place (`updateOptions`) instead of destroy + re-create.
62+
Inline (non-memoized) `series`/`config` objects no longer re-create the chart every render.
63+
- `SidebarProvider` / `ColorModeProvider` context values are memoized — consumers no longer
64+
re-render on every provider render.
65+
- `Input`, `Select`, `Textarea`, and `InputSwitch` forward refs to their underlying form
66+
controls (focus management, react-hook-form, etc.).
67+
- Sidebar OverlayScrollbars detection switched from a 100 ms `setInterval` poll to an immediate
68+
check + `requestAnimationFrame` retry loop, tolerating lazily loaded
69+
(`next/script afterInteractive`) OverlayScrollbars.
70+
71+
### Fixed
72+
73+
- `Datatable` destroys its Tabulator instance on unmount/re-render (previously leaked instances
74+
and stacked tables on prop changes).
75+
- `Editor` no longer creates a second Quill instance when `placeholder`/`quillOptions` change;
76+
the `value` prop now syncs into the editor after mount (controlled usage works), and the
77+
hidden form input tracks the edited HTML instead of the stale `value` prop.
78+
- `Card` `removable` tool button actually removes the card (was a no-op); new `onRemove`
79+
callback fires after removal.
80+
- `Input` `igroupSize` now actually sizes the control (`form-control-sm`/`form-control-lg`);
81+
previously the computed class was never applied.
82+
4183
### Tested
4284

4385
- Vitest unit tests for pure logic and presentational components.
86+
- CI workflow (GitHub Actions): type-check, lint, unit tests, library build, an RSC-boundary
87+
check on `dist/`, and demo type-check + build.
4488

4589
## [0.1.0] - 2026-05-29
4690

CLAUDE.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,23 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
44

55
## What this is
66

7-
`adminlte-react` — an AdminLTE 4 / Bootstrap 5.3 component library built for Next.js 14+ App Router (React Server Components). **The repo root _is_ the library** (the published npm package). It's also a small pnpm workspace: the root package is the library, and `demo/` is a Next.js 14 App Router app that dogfoods it via `"adminlte-react": "workspace:*"` (pnpm symlinks `demo/node_modules/adminlte-react` → repo root).
7+
`@adminlte/react` — an AdminLTE 4 / Bootstrap 5.3 component library built for Next.js 14+ App Router (React Server Components). **The repo root _is_ the library** (the published npm package, under the `@adminlte` npm org). It's also a small pnpm workspace: the root package is the library, and `demo/` is a Next.js 14 App Router app that dogfoods it via `"@adminlte/react": "workspace:*"` (pnpm symlinks `demo/node_modules/@adminlte/react` → repo root).
88

9-
- Root (`src/`, `package.json` named `adminlte-react`, `tsup.config.ts`, `dist/`) — the publishable library
9+
- Root (`src/`, `package.json` named `@adminlte/react`, `tsup.config.ts`, `dist/`) — the publishable library
1010
- `demo/` — demo + dev playground; consumes the library through the workspace link
1111

12-
There is no test framework configured. `pnpm lint` runs ESLint (`eslint.config.mjs`: typescript-eslint + react-hooks) over `src/`; `pnpm type-check` (`tsc --noEmit`) is the strict type gate. Both pass clean — keep them that way.
12+
Testing: `pnpm test` runs Vitest (jsdom) unit tests colocated in `src/` (`*.test.ts(x)`); the demo has Playwright tests (`cd demo && pnpm test` — route smoke + a11y, starts its own dev server). `pnpm lint` runs ESLint (`eslint.config.mjs`: typescript-eslint + react-hooks) over `src/`; `pnpm type-check` (`tsc --noEmit`) is the strict type gate. All pass clean — keep them that way. CI (`.github/workflows/ci.yml`) runs type-check, lint, unit tests, the library build (plus an RSC-boundary check on `dist/`), and the demo type-check + build.
1313

1414
## Commands
1515

1616
From the repo root (these operate on the **library**):
1717

1818
```bash
19-
pnpm build # tsup → copy-css → add-use-client (see "The build pipeline" below)
19+
pnpm build # tsup → copy-css → fix-dist (see "The build pipeline" below)
2020
pnpm dev # tsup --watch (rebuild the library on change)
2121
pnpm type-check # tsc --noEmit — the primary check before considering library work done
2222
pnpm lint # ESLint over src/ (typescript-eslint + react-hooks)
23+
pnpm test # Vitest unit tests (src/**/*.test.*)
2324
pnpm demo # run the demo app (next dev) — alias for the demo workspace
2425
pnpm demo:build # production build of the demo
2526
```
@@ -38,17 +39,17 @@ Every route sets a `<title>` via a `%s · AdminLTE React` template. **The templa
3839

3940
The demo imports the compiled `dist/`, not `src/`. After editing library source, rebuild it (`pnpm build`, or keep `pnpm dev` running in one terminal) before the demo reflects the change — run `pnpm dev` (library watch) and `pnpm demo` side by side during development.
4041

41-
## The build pipeline (and the "use client" gotcha)
42+
## The build pipeline (per-file ESM, RSC boundaries preserved)
4243

43-
`tsup` (esbuild) bundles `src/index.ts` to dual ESM+CJS with `.d.ts`, treeshaken, sourcemapped. The build is **three sequential steps** (`tsup && copy-css && add-use-client`):
44+
`tsup` (esbuild) compiles every `src/` module **unbundled** (`bundle: false`, ESM-only, no sourcemaps) to a mirrored `dist/` tree, plus a single bundled `dist/index.d.ts`. The build is **three sequential steps** (`tsup && copy-css && fix-dist`):
4445

45-
1. `tsup`bundle. **esbuild strips `'use client'` directives during bundling**, so they cannot survive normally.
46+
1. `tsup`per-file transform of `src/**/*.ts(x)` (tests excluded). Each module keeps its own `'use client'` (or lack of one), so the RSC/client split in `src/` **is** the published artifact: server-authored components stay Server Components for consumers, and bundlers tree-shake through the barrel.
4647
2. `copy-css` — copies `node_modules/admin-lte/dist/css/adminlte.css``dist/css/` (exposed as the `./css` export).
47-
3. `add-use-client``add-use-client.js` prepends `"use client";` to the **entire** `dist/index.js` and `dist/index.cjs`.
48+
3. `fix-dist``fix-dist.js` (a) re-applies `"use client"` to any dist file whose source declares it (safety net; esbuild currently preserves them itself, hence "0 directives applied" in output), and (b) rewrites relative specifiers to be fully specified (`'./foo'``'./foo.js'`) — required because webpack enforces fully-specified ESM imports inside `node_modules`. It exits non-zero if a specifier can't be resolved.
4849

49-
**Critical implication:** the published barrel bundle is marked `'use client'` as a whole. The RSC/client split in `src/` (below) is the authoring model; the single-file published artifact is a client boundary. When changing the build or the entry, preserve this post-processing step or RSC consumers will get "use client" errors.
50+
**Critical implications:** the package is ESM-only (no CJS) — `exports['.']` has `types`/`import`/`default` conditions only. Don't re-introduce bundling without solving directive preservation per module; don't add a source file with extensionless imports that point at directories (fix-dist resolves `./x``x.js` or `x/index.js`). CI verifies the src↔dist client-module counts match and that `dist/form/button.js` stays unmarked.
5051

51-
Heavy plugins (`flatpickr`, `tom-select`, `tabulator-tables`, `quill`) plus `react`/`react-dom`/`next` are `external` in `tsup.config.ts` — never bundled.
52+
**All** heavy plugins (`apexcharts`, `flatpickr`, `jsvectormap`, `quill`, `sortablejs`, `tabulator-tables`, `tom-select`) are **optional peer dependencies** — never bundled. They're loaded via dynamic `import()` (unbundled output keeps the specifier as written), so consumers install only what they use and Next code-splits them per page. Shared pure helpers that Server Components call (e.g. `lib/flatten-menu.ts`) must live outside `'use client'` modules.
5253

5354
## Architecture
5455

@@ -74,7 +75,7 @@ Heavy third-party libs are **never** statically imported. Each wrapper component
7475
`next` is declared an **optional** peer dependency, but `layout/sidebar-nav.tsx` imports `usePathname` from `next/navigation` for active-link detection — so the sidebar in practice requires Next. Navigation uses plain `<a href>` (full page loads), not `next/link`.
7576

7677
### What the library ships vs. what the consumer provides
77-
The library ships JS + **only** `dist/css/adminlte.css` (import via `'adminlte-react/css'`). Everything else is the consumer's responsibility, loaded via CDN in `demo/app/layout.tsx`: **Bootstrap JS bundle** (required for dropdowns/modals — the library does not bundle it), Popper, OverlayScrollbars, Bootstrap Icons, Source Sans 3 font, and the plugin CSS (ApexCharts, jsVectorMap, Tabulator). When adding a component that needs runtime JS or CSS the library doesn't bundle, document the CDN/import requirement and mirror it in the demo's root layout.
78+
The library ships JS + **only** `dist/css/adminlte.css` (import via `'@adminlte/react/css'`). Everything else is the consumer's responsibility, loaded via CDN in `demo/app/layout.tsx`: **Bootstrap JS bundle** (required for dropdowns/modals — the library does not bundle it; the bundle build already includes Popper), OverlayScrollbars, Bootstrap Icons, Source Sans 3 font, and the plugin CSS (ApexCharts, jsVectorMap, Tabulator, Quill, Flatpickr, Tom Select). The demo loads the JS via `next/script` `afterInteractive` and preconnects to the CDN. **Gotcha:** markup that Bootstrap JS decorates on window load (e.g. tab/pill `data-bs-toggle` groups) must render those attributes (`aria-selected`, `tabIndex={-1}` on inactive tabs) in JSX, or Bootstrap's mutations race React hydration and cause mismatch errors. When adding a component that needs runtime JS or CSS the library doesn't bundle, document the CDN/import requirement and mirror it in the demo's root layout.
7879

7980
### Demo route groups
8081
`demo/app` uses route groups: `(dashboard)` (main shell via `demo/components/demo-layout.tsx`, which wraps `DashboardLayout` with shared brand/user/topbar), `(auth)` (login/register via `AuthLayout`), `(fullpage)` (layout-flag demos: fixed header/sidebar/footer, RTL, mini, etc.).

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@adminlte/react",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "AdminLTE 4 React/Next.js component library",
55
"license": "MIT",
66
"type": "module",

0 commit comments

Comments
 (0)