From 6a11ef0cdf7b70210a5369f822942ac80b9ac58f Mon Sep 17 00:00:00 2001 From: Olexii Kyrychenko Date: Mon, 14 Sep 2026 21:07:01 +0300 Subject: [PATCH] chore: prepare v0.2.0 release --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ README.md | 16 +++++++++++++--- package.json | 3 ++- scripts/check-packed-package.mjs | 1 + tsup.config.ts | 2 +- 5 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9eafd8d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,32 @@ +# Changelog + +All notable changes to this project are documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.2.0] - 2026-09-14 + +### Added + +- Allow modal definitions whose input is `void` or `undefined` to be opened without an input argument through the modal manager, registered definitions, and typed registries. +- Add verified React 18 and React 19 compatibility, including Strict Mode and independent-root coverage. +- Add tested SSR, hydration, and Next.js App Router/React Server Components guidance. +- Add compile-checked adoption examples, packed-package contract checks, lifecycle benchmarks, and reproducible competitive package checks. +- Add automated pnpm-based CI and provenance-enabled npm release workflows. + +### Changed + +- Move modal lifecycle ownership entirely into each `ModalProvider`, with React observing the lifecycle state directly. +- Simplify imperative modal access: pass a typed registry directly to `ModalProvider`; external registry calls route to the most recently mounted matching provider and fall back when it unmounts. +- Remove Zustand from the public installation contract. React is now the only peer dependency; `@okyrychenko-dev/type-utils` is the sole runtime dependency. +- Migrate repository development, validation, package inspection, and release workflows from npm to pnpm. +- Expand the README with provider-scope, lifecycle, registry, SSR/RSC, custom-renderer, accessibility, adoption, and troubleshooting guidance. + +### Fixed + +- Preserve provider lifecycle state during React Strict Mode effect replay. +- Preserve registry routing across nested providers, adjacent providers, and independent React roots. +- Make lifecycle Storybook examples repeatable after modal settlement. + +[0.2.0]: https://github.com/okyrychenko-dev/react-modal-manager/compare/v0.1.0...v0.2.0 diff --git a/README.md b/README.md index 9e5d2c6..c87d058 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ const app = ( ## Why This Library -- **Typed results, not `any`.** `open(def, input)` returns a `Promise`. Both sides of the call are checked. +- **Typed results, not `any`.** `open(def, input)` returns a `Promise`. Both sides of the call are checked, and inputless modals can omit the argument. - **Per-provider isolation.** Each `ModalProvider` owns an independent lifecycle whose authoritative state React observes directly — no global lifecycle singleton, so subtrees and tests never leak modal state into each other. - **Open from non-React code.** A typed registry lets event buses, command palettes, and action maps open modals while keeping full inference. - **UI-agnostic core.** A single `renderer` seam lets you plug in portals, overlays, animations, or any design system. The core never prescribes DOM or styling. @@ -44,7 +44,7 @@ const app = ( ### Compared to [`@ebay/nice-modal-react`](https://github.com/eBay/nice-modal-react) -Revalidated **2026-09-13** against this package at **0.1.0** (`0004c89`) and the current stable [`@ebay/nice-modal-react` 1.2.13](https://www.npmjs.com/package/@ebay/nice-modal-react/v/1.2.13). “Verified behavior” below means an executable public-surface check; “architecture” describes source structure and is not itself a consumer guarantee. +Revalidated **2026-09-13** for this package's **0.2.0** release line (through `3a98aa8`) and the current stable [`@ebay/nice-modal-react` 1.2.13](https://www.npmjs.com/package/@ebay/nice-modal-react/v/1.2.13). “Verified behavior” below means an executable public-surface check; “architecture” describes source structure and is not itself a consumer guarantee. | Area | `react-modal-manager` | `nice-modal-react` | Evidence kind | | --- | --- | --- | --- | @@ -59,7 +59,7 @@ Revalidated **2026-09-13** against this package at **0.1.0** (`0004c89`) and the | First-use ergonomics | `confirm()` is the shortest path; custom flows define a modal and open it directly or through a registry | `show(component, props)` is the shortest path; string access adds `register(id, component)` | Documented public APIs: [this README](#quick-start), [Nice Modal usage](https://github.com/eBay/nice-modal-react/tree/1.2.13#usage) | | Package cost | Recorded minimal `createModal` consumer: **2,058 B / 1,044 B gzip**; React is the only peer and `type-utils` the only runtime dependency | Reproduced minimal named-`show` consumer: **758 B / 473 B gzip**; zero runtime dependencies, with React and React DOM as peers | Reproduce with [`package:check`](scripts/check-packed-package.mjs) and [`competitive:check`](scripts/check-competitive-package.mjs). The entry points differ, so these are package-cost observations, not a universal size ranking. | | Performance | Optimized-build raw samples and summaries cover mount, unmount, open/render, settlement, delayed removal, stacking, and registry routing | No like-for-like run was made against the competitor | Reproducible local evidence: [`benchmark:lifecycle`](scripts/benchmark-lifecycle.mjs). No performance winner is claimed. | -| Maintenance status | 0.1.0 is the version evaluated on this repository’s current main branch | 1.2.13 was published 2023-10-03; it remains the npm `latest` release on the evaluation date | Release evidence: [local manifest](package.json), [npm version](https://www.npmjs.com/package/@ebay/nice-modal-react/v/1.2.13), [GitHub release](https://github.com/eBay/nice-modal-react/releases/tag/1.2.13) | +| Maintenance status | 0.2.0 is the release line evaluated in this repository | 1.2.13 was published 2023-10-03; it remains the npm `latest` release on the evaluation date | Release evidence: [local manifest](package.json), [npm version](https://www.npmjs.com/package/@ebay/nice-modal-react/v/1.2.13), [GitHub release](https://github.com/eBay/nice-modal-react/releases/tag/1.2.13) | The main trade-off is deliberate: this package does not provide unchecked `show("any-string")` routing. Imperative callers import a typed definition or use a typed registry, and a registry must be bound to a mounted provider. Nice Modal’s global component/id calls require less setup and can be more convenient when that trade-off is acceptable. Conversely, this package’s provider ownership, result inference, SSR behavior, and built-in confirmation are explicit tested contracts rather than conclusions drawn only from implementation structure. @@ -251,6 +251,16 @@ const result = await handle; The handle's `dismiss()` stays bound to the provider that opened the modal. +For a modal that needs no input, declare its input as `void` and omit the second argument: + +```tsx +const infoModal = createModal({ component: InfoModal }); + +await modal.open(infoModal); +``` + +Modals with any other input type still require an input argument. + ## Typed Modal Registry Use `createModalRegistry()` when code needs to open modals by a stable key while keeping typed input and result contracts. This suits command palettes, event buses, action maps, and configuration-driven flows. Pass the registry straight to `ModalProvider` — there is no controller to wire up. diff --git a/package.json b/package.json index 8b5be1c..f8514d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@okyrychenko-dev/react-modal-manager", - "version": "0.1.0", + "version": "0.2.0", "packageManager": "pnpm@11.21.0", "type": "module", "description": "Typed modal/dialog lifecycle manager for React", @@ -23,6 +23,7 @@ "files": [ "dist", "README.md", + "CHANGELOG.md", "LICENSE" ], "scripts": { diff --git a/scripts/check-packed-package.mjs b/scripts/check-packed-package.mjs index ac34424..5867fef 100644 --- a/scripts/check-packed-package.mjs +++ b/scripts/check-packed-package.mjs @@ -70,6 +70,7 @@ try { const packageRoot = join(extractRoot, "package"); const expectedFiles = [ + "CHANGELOG.md", "LICENSE", "README.md", "dist/index.cjs", diff --git a/tsup.config.ts b/tsup.config.ts index 3e6d6ff..a53fbb0 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -7,7 +7,7 @@ export default defineConfig({ splitting: false, sourcemap: true, clean: true, - external: ["react", "zustand"], + external: ["react"], treeshake: true, minify: false, });