This commit is contained in:
2026-08-06 20:51:46 +02:00
parent f86db1556c
commit d1b70ea7d2
30 changed files with 1601 additions and 260 deletions

View File

@@ -16,9 +16,9 @@
<button
type="button"
role="radio"
[attr.aria-checked]="theme() === option.value"
[class.is-active]="theme() === option.value"
(click)="theme.set(option.value)"
[attr.aria-checked]="themeService.theme() === option.value"
[class.is-active]="themeService.theme() === option.value"
(click)="themeService.setTheme(option.value)"
>
{{ option.label }}
</button>
@@ -26,7 +26,7 @@
</div>
<label class="theme-toggle">
<span>Modo oscuro</span>
<pi-slide-toggle [(checked)]="darkMode" />
<pi-slide-toggle [(checked)]="themeService.darkMode" />
</label>
</div>
</div>
@@ -43,8 +43,8 @@
</nav>
<p class="theme-hint">
Tema: <strong>{{ theme() }}</strong> ·
<code>{{ shellClassByTheme[theme()] }}</code>
Tema: <strong>{{ themeService.theme() }}</strong> ·
<code>{{ themeService.shellClass() }}</code>
en el <code>&lt;body&gt;</code>. Los cuatro temas comparten exactamente los
mismos selectores: solo cambian los tokens <code>--pi-color-*</code> y
<code>--pi-theme-*</code> que definen <code>themes/_default.scss</code>,

View File

@@ -1,14 +1,12 @@
import { DOCUMENT } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
effect,
inject,
signal,
} from '@angular/core';
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
import { PiLoaderBarComponent, PiSlideToggleComponent, ShellClass } from '@pi/ui';
import {
PiLoaderBarComponent,
PiSlideToggleComponent,
PiThemeService,
ThemeVariant,
} from '@pi/ui';
/**
* Shell del playground: cabecera, navegación y estado de tema. El contenido de
@@ -38,9 +36,12 @@ export class App {
/* --- Tema y modo oscuro -------------------------------------------------- */
private readonly document = inject(DOCUMENT);
protected readonly darkMode = signal(false);
/**
* Aplicar `pi-shell pi-shell__<tema>` en `<body>` y `dark-mode` en `<html>`
* (con persistencia en `localStorage`) vive en `PiThemeService`, no aquí:
* cualquier app que consuma `@pi/ui` necesita este mismo cableado.
*/
protected readonly themeService = inject(PiThemeService);
protected readonly themeOptions: ReadonlyArray<{
value: ThemeVariant;
@@ -51,34 +52,4 @@ export class App {
{ value: 'minimal', label: 'Minimal' },
{ value: 'gradient', label: 'Gradient' },
];
/**
* Tema visual de la librería. `ShellClass` devuelve las dos clases que hay
* que poner juntas (`pi-shell pi-shell__<tema>`); van en el `<body>` porque
* los overlays de Material (diálogos, snackbars) se montan fuera de
* `<app-root>` y también deben heredarlas.
*/
protected readonly theme = signal<ThemeVariant>('default');
protected readonly shellClassByTheme: Record<ThemeVariant, string> = {
default: ShellClass.Default,
alternative: ShellClass.Alternative,
minimal: ShellClass.Minimal,
gradient: ShellClass.Gradient,
};
constructor() {
effect(() => {
this.document.documentElement.classList.toggle(
'dark-mode',
this.darkMode(),
);
});
effect(() => {
this.document.body.className = this.shellClassByTheme[this.theme()];
});
}
}
type ThemeVariant = 'default' | 'alternative' | 'minimal' | 'gradient';