@eagami/ui 3.2.0 → 3.2.1

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/README.md CHANGED
@@ -96,6 +96,10 @@ Every brand-role pairing is checked against WCAG 2.1 AA at bootstrap; a contrast
96
96
 
97
97
  Built-in strings ship in English, French (France), Greek, Polish, and Spanish (Spain), with runtime switching via `EagamiI18nService`. See [internationalization](https://eagami.com/ui/i18n) for setup and per-string overrides.
98
98
 
99
+ ## Server-side rendering
100
+
101
+ The library is SSR-safe and renders on the server (Angular Universal / `@angular/ssr`) without reaching for `window` or `document`. Browser-only work (focus management, the native `<dialog>`, overlay positioning, resize/intersection observers) is deferred to the client via `afterNextRender` and `isPlatformBrowser` guards, so prerendering, streaming SSR, and hydration work with no extra configuration.
102
+
99
103
  ## Framework integration
100
104
 
101
105
  `@eagami/ui` is Angular-only, but its design tokens are framework-agnostic. Copy-and-paste guides for non-Angular targets:
@@ -109,6 +113,7 @@ Built-in strings ship in English, French (France), Greek, Polish, and Spanish (S
109
113
  |---|---|
110
114
  | Angular | `^21.0.0` (peer dep) |
111
115
  | Node | `>= 20` for build/dev tooling |
116
+ | Rendering | Browser, SSR / prerender (Angular Universal), hydration |
112
117
  | Browsers | Last 2 stable versions of Chrome, Edge, Firefox (plus current ESR), Safari |
113
118
 
114
119
  > **Upgrading from v0.x?** See [MIGRATION.md](MIGRATION.md).
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, makeEnvironmentProviders, provideEnvironmentInitializer, signal, inject, effect, computed, Injectable, input, ChangeDetectionStrategy, Component, HostBinding, Directive, model, output, Injector, DestroyRef, afterNextRender, viewChild, ElementRef, HostListener, forwardRef, Renderer2, untracked, ViewEncapsulation, contentChild, viewChildren } from '@angular/core';
3
- import { NgClass, NgComponentOutlet, NgTemplateOutlet } from '@angular/common';
2
+ import { InjectionToken, makeEnvironmentProviders, provideEnvironmentInitializer, signal, inject, effect, computed, Injectable, input, ChangeDetectionStrategy, Component, HostBinding, Directive, model, output, Injector, DestroyRef, afterNextRender, viewChild, ElementRef, HostListener, forwardRef, Renderer2, untracked, ViewEncapsulation, contentChild, viewChildren, PLATFORM_ID } from '@angular/core';
3
+ import { NgClass, NgComponentOutlet, NgTemplateOutlet, isPlatformBrowser } from '@angular/common';
4
4
  import { NgControl, NG_VALUE_ACCESSOR } from '@angular/forms';
5
5
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
6
 
@@ -7320,6 +7320,7 @@ class DialogComponent {
7320
7320
  dialogEl = viewChild('dialogEl', ...(ngDevMode ? [{ debugName: "dialogEl" }] : /* istanbul ignore next */ []));
7321
7321
  previouslyFocused = null;
7322
7322
  i18n = inject(EagamiI18nService);
7323
+ isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
7323
7324
  width = input('md', ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
7324
7325
  closeOnBackdrop = input(true, ...(ngDevMode ? [{ debugName: "closeOnBackdrop" }] : /* istanbul ignore next */ []));
7325
7326
  closeOnEscape = input(true, ...(ngDevMode ? [{ debugName: "closeOnEscape" }] : /* istanbul ignore next */ []));
@@ -7337,10 +7338,13 @@ class DialogComponent {
7337
7338
  constructor() {
7338
7339
  effect(() => {
7339
7340
  const dialogRef = this.dialogEl()?.nativeElement;
7340
- if (!dialogRef) {
7341
+ const open = this.open();
7342
+ // `<dialog>` APIs and focus management are browser-only, but the effect
7343
+ // still runs during SSR, so skip the DOM work on the server.
7344
+ if (!dialogRef || !this.isBrowser) {
7341
7345
  return;
7342
7346
  }
7343
- if (this.open()) {
7347
+ if (open) {
7344
7348
  if (!dialogRef.open) {
7345
7349
  this.previouslyFocused = document.activeElement;
7346
7350
  dialogRef.showModal();
@@ -7394,6 +7398,7 @@ class DrawerComponent {
7394
7398
  drawerEl = viewChild('drawerEl', ...(ngDevMode ? [{ debugName: "drawerEl" }] : /* istanbul ignore next */ []));
7395
7399
  previouslyFocused = null;
7396
7400
  i18n = inject(EagamiI18nService);
7401
+ isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
7397
7402
  position = input('right', ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
7398
7403
  width = input('md', ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
7399
7404
  closeOnBackdrop = input(true, ...(ngDevMode ? [{ debugName: "closeOnBackdrop" }] : /* istanbul ignore next */ []));
@@ -7415,10 +7420,13 @@ class DrawerComponent {
7415
7420
  constructor() {
7416
7421
  effect(() => {
7417
7422
  const drawerRef = this.drawerEl()?.nativeElement;
7418
- if (!drawerRef) {
7423
+ const open = this.open();
7424
+ // `<dialog>` APIs and focus management are browser-only, but the effect
7425
+ // still runs during SSR, so skip the DOM work on the server.
7426
+ if (!drawerRef || !this.isBrowser) {
7419
7427
  return;
7420
7428
  }
7421
- if (this.open()) {
7429
+ if (open) {
7422
7430
  if (!drawerRef.open) {
7423
7431
  this.previouslyFocused = document.activeElement;
7424
7432
  drawerRef.showModal();