diff --git a/.gitignore b/.gitignore
index f7f5069..56a79ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,9 @@ out-tsc
# dependencies
node_modules
+# Credenciales de registro npm (Nexus u otro). Ver .npmrc.example.
+.npmrc
+
# IDEs and editors
/.idea
.project
diff --git a/.npmrc.example b/.npmrc.example
new file mode 100644
index 0000000..d3df147
--- /dev/null
+++ b/.npmrc.example
@@ -0,0 +1,21 @@
+# Copia este fichero a `.npmrc` (en la raíz del repo) y rellena los valores
+# reales de tu Nexus. `.npmrc` está en .gitignore: nunca se commitea un token.
+#
+# Solo hace falta esto para PUBLICAR @pi/ui. Instalar paquetes públicos
+# (@angular/*, rxjs, etc.) sigue yendo contra el registro público de npm salvo
+# que tu Nexus también actúe de proxy/group para el scope por defecto.
+
+# Redirige solo el scope @pi al repositorio npm (hosted) de Nexus.
+# Cambia la URL por la de tu instancia y el nombre de tu repo npm-hosted.
+@pi:registry=https://nexus.tuempresa.com/repository/npm-private/
+
+# Autenticación por token (recomendado, funciona igual en local y en CI).
+# El token se genera en Nexus: usuario > Security > NuGet/npm token, o vía
+# `npm login --registry=https://nexus.tuempresa.com/repository/npm-private/`
+# (esto último escribe la línea de abajo automáticamente en tu ~/.npmrc).
+//nexus.tuempresa.com/repository/npm-private/:_authToken=${NEXUS_NPM_TOKEN}
+
+# Nexus no es el registro público de npm: sin este flag, `npm publish`
+# rechaza paquetes con scope si no lleva --access, y con un registro privado
+# "restricted" es lo correcto (no lo confundas con "hazlo público en npmjs").
+always-auth=true
diff --git a/apps/playground/src/app/app.config.ts b/apps/playground/src/app/app.config.ts
index 62ef223..ce1cd7e 100644
--- a/apps/playground/src/app/app.config.ts
+++ b/apps/playground/src/app/app.config.ts
@@ -6,7 +6,6 @@ import {
} from '@angular/core';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { provideRouter, withInMemoryScrolling } from '@angular/router';
-import { provideStore } from '@ngrx/store';
import { routes } from './app.routes';
import {
@@ -16,7 +15,7 @@ import {
} from '@ngx-translate/core';
import { Observable, of } from 'rxjs';
-import { IconRegistryService, deviceConfigsReducer, featureKey } from '@pi/ui';
+import { DeviceConfigsService, IconRegistryService } from '@pi/ui';
/**
* Diccionario en línea: la librería resuelve los textos con `| translate`, y
@@ -62,12 +61,15 @@ export const appConfig: ApplicationConfig = {
// Los iconos son SVG registrados en el MatIconRegistry (assets/icons).
provideAppInitializer(() => inject(IconRegistryService).init()),
+ // data-table, carousel y background-image leen el tipo de dispositivo;
+ // instanciarlo arranca el listener de breakpoints.
+ provideAppInitializer(() => {
+ inject(DeviceConfigsService);
+ }),
+
provideTranslateService({
fallbackLang: 'es',
loader: { provide: TranslateLoader, useClass: PlaygroundTranslateLoader },
}),
-
- // data-table y background-image leen el tipo de dispositivo del store.
- provideStore({ [featureKey]: deviceConfigsReducer }),
],
};
diff --git a/apps/playground/src/app/app.html b/apps/playground/src/app/app.html
index 8740f0d..a812adf 100644
--- a/apps/playground/src/app/app.html
+++ b/apps/playground/src/app/app.html
@@ -9,10 +9,19 @@
-
- Tema alternativo
-
-
+
+ @for (option of themeOptions; track option.value) {
+
+ {{ option.label }}
+
+ }
+
Modo oscuro
@@ -32,16 +41,14 @@
- Tema:
- {{ alternativeTheme() ? 'alternative' : 'default' }} ·
- {{ alternativeTheme() ? 'pi-shell__alternative' : 'pi-shell__default'
- }}
- en el <body>. Los dos temas comparten exactamente los
+ Tema: {{ theme() }} ·
+ {{ shellClassByTheme[theme()] }}
+ en el <body>. Los cuatro temas comparten exactamente los
mismos selectores: solo cambian los tokens --pi-color-* y
- --pi-theme-* que definen themes/_default.scss y
- themes/_alternative.scss.
+ --pi-theme-* que definen themes/_default.scss,
+ themes/_alternative.scss, themes/_minimal.scss y
+ themes/_gradient.scss. Prueba "Gradient" y enfoca un campo del
+ formulario.
diff --git a/apps/playground/src/app/app.scss b/apps/playground/src/app/app.scss
index b9cfd15..61feea2 100644
--- a/apps/playground/src/app/app.scss
+++ b/apps/playground/src/app/app.scss
@@ -47,6 +47,30 @@
white-space: nowrap;
}
+.theme-switcher {
+ display: flex;
+ gap: 0.25rem;
+ padding: 0.2rem;
+ border: 1px solid var(--pg-card-border);
+ border-radius: 8px;
+
+ button {
+ padding: 0.3rem 0.75rem;
+ border: none;
+ border-radius: 6px;
+ background: transparent;
+ color: var(--pg-text-muted);
+ font-size: 0.8rem;
+ cursor: pointer;
+
+ &.is-active {
+ background: var(--pg-badge-bg);
+ color: var(--pg-badge-text);
+ font-weight: 600;
+ }
+ }
+}
+
.theme-hint {
margin: 0.75rem 0 0;
font-size: 0.8rem;
diff --git a/apps/playground/src/app/app.ts b/apps/playground/src/app/app.ts
index 7212053..c842729 100644
--- a/apps/playground/src/app/app.ts
+++ b/apps/playground/src/app/app.ts
@@ -33,13 +33,30 @@ export class App {
protected readonly darkMode = signal(false);
+ protected readonly themeOptions: ReadonlyArray<{
+ value: ThemeVariant;
+ label: string;
+ }> = [
+ { value: 'default', label: 'Default' },
+ { value: 'alternative', label: 'Alternative' },
+ { 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__`); van en el `` porque
* los overlays de Material (diálogos, snackbars) se montan fuera de
* `` y también deben heredarlas.
*/
- protected readonly alternativeTheme = signal(false);
+ protected readonly theme = signal('default');
+
+ protected readonly shellClassByTheme: Record = {
+ default: ShellClass.Default,
+ alternative: ShellClass.Alternative,
+ minimal: ShellClass.Minimal,
+ gradient: ShellClass.Gradient,
+ };
constructor() {
effect(() => {
@@ -50,10 +67,9 @@ export class App {
});
effect(() => {
- const shell = this.alternativeTheme()
- ? ShellClass.Alternative
- : ShellClass.Default;
- this.document.body.className = shell;
+ this.document.body.className = this.shellClassByTheme[this.theme()];
});
}
}
+
+type ThemeVariant = 'default' | 'alternative' | 'minimal' | 'gradient';
diff --git a/apps/playground/src/app/pages/catalog/catalog.page.html b/apps/playground/src/app/pages/catalog/catalog.page.html
index 2afa7ee..4b74211 100644
--- a/apps/playground/src/app/pages/catalog/catalog.page.html
+++ b/apps/playground/src/app/pages/catalog/catalog.page.html
@@ -195,6 +195,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Abrir drawer (derecha)
+
+
+ Abrir drawer (izquierda)
+
+
+ Contenido de ejemplo dentro del drawer.
+ Puede llevar cualquier plantilla: formularios, listas, filtros…
+
+
+
+
+
+ Click (popover)
+
+
+ Hover (popover)
+
+
+ Contenido del popover, con cierre programático.
+ Cerrar
+
+
+
>('drawerTemplate');
+
+ protected openDemoDrawer(side: 'left' | 'right'): void {
+ this.dialog.open(
+ PiDrawerComponent,
+ getDrawerConfigs(
+ side === 'left' ? DrawerConfigKeys.Left : DrawerConfigKeys.Right,
+ { title: 'Detalle', template: this.drawerTemplate() },
+ ),
+ );
+ this.log(`drawer abierto (${side})`);
+ }
+
/* --- Datos: grid, tabla, gráfico, carrusel ------------------------------ */
protected readonly employees = [
diff --git a/libs/ui/README.md b/libs/ui/README.md
index c939b7f..a3fcd47 100644
--- a/libs/ui/README.md
+++ b/libs/ui/README.md
@@ -179,11 +179,10 @@ corrigió la aridad a `track trackByFn(i)`.
El origen tiene símbolos duplicados. En `index.ts` gana el primero y el otro se
omite (queda anotado en el fichero). Siguen siendo accesibles por ruta directa:
-| Símbolo | Se exporta desde | Se omite de |
-| ----------------------------------------------- | --------------------- | ------------------- |
-| `PiSlideToggleComponent` | `ui/form-field` | `ui/slide-toggle` |
-| `PiDisabledDirective` | `util/status` | `ui/text` |
-| `featureKey`, `getFeatureState`, `initialState` | `util/device-configs` | `util/list-configs` |
+| Símbolo | Se exporta desde | Se omite de |
+| ------------------------- | ---------------- | ------------------ |
+| `PiSlideToggleComponent` | `ui/form-field` | `ui/slide-toggle` |
+| `PiDisabledDirective` | `util/status` | `ui/text` |
## APIs de terceros actualizadas
@@ -197,7 +196,6 @@ omite (queda anotado en el fichero). Siguen siendo accesibles por ruta directa:
- **Tests**: no se copiaron los `*.spec.ts` del origen.
- **`Sector`** vivía en `deale-saas/util/models` (lib de aplicación, fuera de alcance); se trajo la interfaz a `util/models`.
-- **ngrx 21 con Angular 22**: instalado con `--legacy-peer-deps` (aún no declara soporte para 22).
- **Lint**: `nx lint ui` falla con avisos heredados del código original —
`prefer-inject` (51), `directive-selector` (46), `no-explicit-any` (35),
`no-unused-expressions` (33), a11y (`click-events-have-key-events`,
diff --git a/libs/ui/docs/theming.pdf b/libs/ui/docs/theming.pdf
new file mode 100644
index 0000000..fab3c5f
Binary files /dev/null and b/libs/ui/docs/theming.pdf differ
diff --git a/libs/ui/project.json b/libs/ui/project.json
index 4852c7b..8da1cee 100644
--- a/libs/ui/project.json
+++ b/libs/ui/project.json
@@ -23,6 +23,23 @@
"development": {}
}
},
+ "package": {
+ "executor": "@nx/angular:package",
+ "outputs": [
+ "{workspaceRoot}/dist/{projectRoot}"
+ ],
+ "defaultConfiguration": "production",
+ "options": {
+ "project": "libs/ui/ng-package.json",
+ "tsConfig": "libs/ui/tsconfig.lib.json"
+ },
+ "configurations": {
+ "production": {
+ "tsConfig": "libs/ui/tsconfig.lib.publish.json"
+ },
+ "development": {}
+ }
+ },
"test": {
"executor": "@nx/jest:jest",
"outputs": [
diff --git a/libs/ui/src/index.ts b/libs/ui/src/index.ts
index 2aca869..661d1ab 100644
--- a/libs/ui/src/index.ts
+++ b/libs/ui/src/index.ts
@@ -11,16 +11,7 @@ export * from './lib/util/device-configs';
export * from './lib/util/environments';
export * from './lib/util/icon-registry';
export * from './lib/util/infinite-scroll';
-// ./lib/util/list-configs: omitido(s) por colisión con otro módulo -> featureKey, getFeatureState, initialState
-export {
- getListMode,
- ListConfigsFacade,
- listConfigsReducer,
- ListModeActionsType,
- resetListMode,
- setListMode,
-} from './lib/util/list-configs';
-export type { ListConfigsState, ListMode } from './lib/util/list-configs';
+export * from './lib/util/list-configs';
export * from './lib/util/loaders';
export * from './lib/util/local-storage';
export * from './lib/util/menu';
@@ -36,6 +27,8 @@ export * from './lib/util/value';
export * from './lib/util/view-title';
export * from './lib/ui/accordion';
+export * from './lib/ui/avatar';
+export * from './lib/ui/badge';
export * from './lib/ui/banner';
export * from './lib/ui/button';
export * from './lib/ui/card';
@@ -45,6 +38,7 @@ export * from './lib/ui/chip';
export * from './lib/ui/data-grid';
export * from './lib/ui/data-table';
export * from './lib/ui/divider';
+export * from './lib/ui/drawer';
export * from './lib/ui/error-status-code';
export * from './lib/ui/file-upload';
export * from './lib/ui/form-field';
@@ -57,6 +51,7 @@ export * from './lib/ui/note';
export * from './lib/ui/notification';
export * from './lib/ui/paginator';
export * from './lib/ui/panel';
+export * from './lib/ui/popover';
export * from './lib/ui/progress';
export * from './lib/ui/select';
export * from './lib/ui/slider';
diff --git a/libs/ui/src/lib/styles/common/components/_pi-popover.scss b/libs/ui/src/lib/styles/common/components/_pi-popover.scss
new file mode 100644
index 0000000..bb5b051
--- /dev/null
+++ b/libs/ui/src/lib/styles/common/components/_pi-popover.scss
@@ -0,0 +1,17 @@
+// El panel de `pi-popover` es un overlay de `@angular/cdk/overlay`: cuelga de
+// `.cdk-overlay-container`, fuera del árbol del componente que lo dispara.
+.cdk-overlay-container {
+ .pi-popover__panel {
+ // `.cdk-overlay-pane` ya es `display: flex` (así lo fija el propio CDK);
+ // el `` del consumidor no aporta un contenedor propio, así
+ // que sus hijos quedan como items flex directos de este panel. Sin
+ // `column` quedarían en fila en vez de apilados.
+ flex-direction: column;
+ gap: var(--pi-spacing-x-small);
+ background-color: var(--pi-color-white);
+ border-radius: var(--pi-border-radius-medium);
+ box-shadow: var(--pi-box-shadow-panel);
+ padding: var(--pi-spacing-medium);
+ max-width: 20rem;
+ }
+}
diff --git a/libs/ui/src/lib/styles/common/components/index.scss b/libs/ui/src/lib/styles/common/components/index.scss
index 4aaf36d..fdf45b8 100644
--- a/libs/ui/src/lib/styles/common/components/index.scss
+++ b/libs/ui/src/lib/styles/common/components/index.scss
@@ -5,6 +5,7 @@
@forward 'pi-list-item';
@forward 'pi-note';
@forward 'pi-panel';
+@forward 'pi-popover';
@forward 'pi-spinner';
@forward 'pi-tab';
@forward 'pi-text-viewer';
diff --git a/libs/ui/src/lib/styles/common/vendors/_mat-drawer.scss b/libs/ui/src/lib/styles/common/vendors/_mat-drawer.scss
new file mode 100644
index 0000000..d7a0605
--- /dev/null
+++ b/libs/ui/src/lib/styles/common/vendors/_mat-drawer.scss
@@ -0,0 +1,40 @@
+// El panel de `pi-drawer` es un `MatDialogConfig.panelClass`: el overlay lo
+// cuelga fuera de `pi-drawer.component`, así que estos estilos no pueden ir
+// en el scss del componente (view encapsulation no llegaría hasta ahí).
+.cdk-overlay-container,
+.pi-shell {
+ .pi-drawer__panel {
+ margin: 0;
+
+ .mat-mdc-dialog-container,
+ .mdc-dialog__surface {
+ border-radius: 0;
+ height: 100%;
+ }
+
+ &.pi-drawer__panel--right {
+ animation: pi-drawer-slide-in-right 0.25s ease-out;
+ }
+ &.pi-drawer__panel--left {
+ animation: pi-drawer-slide-in-left 0.25s ease-out;
+ }
+ }
+}
+
+@keyframes pi-drawer-slide-in-right {
+ from {
+ transform: translateX(100%);
+ }
+ to {
+ transform: translateX(0);
+ }
+}
+
+@keyframes pi-drawer-slide-in-left {
+ from {
+ transform: translateX(-100%);
+ }
+ to {
+ transform: translateX(0);
+ }
+}
diff --git a/libs/ui/src/lib/styles/common/vendors/_mat-form-field.scss b/libs/ui/src/lib/styles/common/vendors/_mat-form-field.scss
index f54103e..555c697 100644
--- a/libs/ui/src/lib/styles/common/vendors/_mat-form-field.scss
+++ b/libs/ui/src/lib/styles/common/vendors/_mat-form-field.scss
@@ -17,6 +17,35 @@
}
}
+ // Anillo de foco en degradado: opt-in por tema vía
+ // `--pi-theme-field-focus-gradient` (ver `themes/_gradient.scss`). Los
+ // temas que no la declaren pintan `background: none` y no se ve nada.
+ //
+ // El notched-outline dibuja su borde partido en 3 piezas (leading/notch/
+ // trailing) para dejar hueco a la etiqueta flotante; un degradado
+ // continuo ahí se vería cortado. Por eso el anillo va aparte, como
+ // `::after` de `.mat-mdc-text-field-wrapper` (la caja completa, sin
+ // partir), con `mask-composite` para que solo pinte el borde y no tape
+ // ni el texto ni los iconos.
+ .mat-mdc-text-field-wrapper {
+ position: relative;
+ }
+ &.mat-focused .mat-mdc-text-field-wrapper::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ z-index: 1;
+ border-radius: var(--pi-theme-field-radius);
+ padding: var(--pi-theme-field-border-width, 1px);
+ background: var(--pi-theme-field-focus-gradient, none);
+ -webkit-mask:
+ linear-gradient(#fff 0 0) content-box,
+ linear-gradient(#fff 0 0);
+ -webkit-mask-composite: xor;
+ mask-composite: exclude;
+ pointer-events: none;
+ }
+
// NOT Invalid
&:not(.mat-form-field-invalid) {
&:not(.mat-focused) {
diff --git a/libs/ui/src/lib/styles/common/vendors/index.scss b/libs/ui/src/lib/styles/common/vendors/index.scss
index 949c524..619f553 100644
--- a/libs/ui/src/lib/styles/common/vendors/index.scss
+++ b/libs/ui/src/lib/styles/common/vendors/index.scss
@@ -3,6 +3,7 @@
@forward 'mat-chip';
@forward 'mat-datepicker';
@forward 'mat-dialog';
+@forward 'mat-drawer';
@forward 'mat-expansion-panel';
@forward 'mat-form-field';
@forward 'mat-menu';
diff --git a/libs/ui/src/lib/styles/configs.ts b/libs/ui/src/lib/styles/configs.ts
index 361822a..2051096 100644
--- a/libs/ui/src/lib/styles/configs.ts
+++ b/libs/ui/src/lib/styles/configs.ts
@@ -14,6 +14,10 @@ export const ShellClass = {
Default: shellTheming('default'),
/** Aspecto de marketing: degradados, brillo al hover y controles mayores. */
Alternative: shellTheming('alternative'),
+ /** Esquinas casi rectas, borde fino en vez de sombra, sin degradados. */
+ Minimal: shellTheming('minimal'),
+ /** Como Default, pero con anillo en degradado al enfocar un campo. */
+ Gradient: shellTheming('gradient'),
};
// View component level
diff --git a/libs/ui/src/lib/styles/themes/_gradient.scss b/libs/ui/src/lib/styles/themes/_gradient.scss
new file mode 100644
index 0000000..3073fa8
--- /dev/null
+++ b/libs/ui/src/lib/styles/themes/_gradient.scss
@@ -0,0 +1,49 @@
+// TEMA GRADIENT
+//
+// Como `_alternative.scss`, reusa el contrato de tokens sin tocar selectores
+// de componente. La firma de este tema es el degradado: en el anillo de foco
+// de los campos (`--pi-theme-field-focus-gradient`, consumido en
+// `_mat-form-field.scss`) y en el relleno de los botones
+// (`--pi-theme-button-fill-*`, consumido en `_pi-button-theme.scss`, el mismo
+// mecanismo que ya usa `_alternative.scss`). El resto (forma, tamaño,
+// tipografía) se deja igual que el tema por defecto para que el foco visual
+// sea justo el degradado.
+//
+// `--pi-theme-field-border-color-focus: transparent` apaga el borde sólido
+// del notched-outline al enfocar: si no, se vería el borde plano por debajo
+// del anillo en degradado.
+//
+// `warn`/`success` no se tocan (igual que en `_alternative.scss`): son
+// colores semánticos y un degradado ahí perjudicaría la lectura del estado.
+
+@mixin gradientThemeTokens {
+ // --- Campos: anillo de foco en degradado ------------------------------
+ --pi-theme-field-focus-gradient: linear-gradient(
+ 120deg,
+ var(--pi-color-primary),
+ var(--pi-color-accent),
+ var(--pi-color-highlighted)
+ );
+ --pi-theme-field-border-color-focus: transparent;
+
+ // --- Botones: relleno en degradado -------------------------------------
+ --pi-theme-button-fill-primary: linear-gradient(
+ 120deg,
+ var(--pi-color-primary-400),
+ var(--pi-color-primary-900)
+ );
+ --pi-theme-button-fill-accent: linear-gradient(
+ 120deg,
+ var(--pi-color-accent-400),
+ var(--pi-color-accent-900)
+ );
+ --pi-theme-button-fill-highlighted: linear-gradient(
+ 120deg,
+ var(--pi-color-highlighted-300),
+ var(--pi-color-highlighted)
+ );
+}
+
+.pi-shell__gradient {
+ @include gradientThemeTokens;
+}
diff --git a/libs/ui/src/lib/styles/themes/_minimal.scss b/libs/ui/src/lib/styles/themes/_minimal.scss
new file mode 100644
index 0000000..b5f3759
--- /dev/null
+++ b/libs/ui/src/lib/styles/themes/_minimal.scss
@@ -0,0 +1,50 @@
+// TEMA MINIMALISTA
+//
+// Sale del lenguaje visual "Material" (relleno con degradado, esquinas muy
+// redondeadas, sombra de elevación) hacia algo más plano: esquinas casi
+// rectas, borde fino en vez de sombra, sin versalitas ni interletrado. Reusa
+// el mismo contrato de tokens que `_alternative.scss`, así que tampoco
+// duplica ni un selector de componente.
+//
+// La paleta de marca (--pi-color-*) se deja intacta a propósito: "minimalista"
+// aquí es una cuestión de forma (radio, sombra, peso), no de color.
+
+@mixin minimalThemeTokens {
+ // --- Botones: esquinas casi rectas, sin sombra ni degradado -----------
+ --pi-theme-button-height: 2.5rem;
+ --pi-theme-button-radius: var(--pi-border-radius-xx-small);
+ --pi-theme-button-font-weight: 500;
+ --pi-theme-button-letter-spacing: normal;
+ --pi-theme-button-text-transform: none;
+ --pi-theme-button-shadow: none;
+ --pi-theme-button-transition:
+ background-color 0.12s ease-out,
+ border-color 0.12s ease-out;
+ --pi-theme-button-fill-primary: none;
+ --pi-theme-button-fill-accent: none;
+ --pi-theme-button-fill-warn: none;
+ --pi-theme-button-fill-success: none;
+ --pi-theme-button-fill-highlighted: none;
+
+ // --- Campos: mismas esquinas rectas, borde fino y neutro --------------
+ --pi-theme-field-radius: var(--pi-border-radius-xx-small);
+ --pi-theme-field-border-width: 1px;
+ --pi-theme-field-border-color: var(--pi-color-grey-200);
+ --pi-theme-field-border-color-hover: var(--pi-color-grey-400);
+ --pi-theme-field-border-color-focus: var(--pi-color-black);
+ --pi-theme-field-surface: var(--pi-color-white);
+ --pi-theme-field-label-color: var(--pi-color-grey-400);
+ --pi-theme-field-label-transform: none;
+ --pi-theme-field-label-letter-spacing: normal;
+ --pi-theme-field-label-weight: 500;
+
+ // --- Superficies: separan por borde, no por elevación -----------------
+ --pi-theme-card-radius: var(--pi-border-radius-xx-small);
+ --pi-theme-card-shadow: none;
+ --pi-theme-panel-radius: var(--pi-border-radius-xx-small);
+ --pi-theme-chip-radius: 0;
+}
+
+.pi-shell__minimal {
+ @include minimalThemeTokens;
+}
diff --git a/libs/ui/src/lib/styles/themes/index.scss b/libs/ui/src/lib/styles/themes/index.scss
index 08da36c..c5838f7 100644
--- a/libs/ui/src/lib/styles/themes/index.scss
+++ b/libs/ui/src/lib/styles/themes/index.scss
@@ -1,7 +1,11 @@
-// El orden importa: `default` declara el contrato completo de tokens y
-// `alternative` lo sobreescribe.
+// El orden importa: `default` declara el contrato completo de tokens y los
+// demás temas lo sobreescriben.
@forward 'default';
@forward 'alternative';
+@forward 'minimal';
+@forward 'gradient';
@use 'default' as *;
@use 'alternative' as *;
+@use 'minimal' as *;
+@use 'gradient' as *;
diff --git a/libs/ui/src/lib/ui/avatar/avatar/avatar.component.html b/libs/ui/src/lib/ui/avatar/avatar/avatar.component.html
new file mode 100644
index 0000000..070d7e2
--- /dev/null
+++ b/libs/ui/src/lib/ui/avatar/avatar/avatar.component.html
@@ -0,0 +1,7 @@
+@if (showImage()) {
+
+} @else if (initials()) {
+ {{ initials() }}
+} @else {
+
+}
diff --git a/libs/ui/src/lib/ui/avatar/avatar/avatar.component.scss b/libs/ui/src/lib/ui/avatar/avatar/avatar.component.scss
new file mode 100644
index 0000000..57fc856
--- /dev/null
+++ b/libs/ui/src/lib/ui/avatar/avatar/avatar.component.scss
@@ -0,0 +1,73 @@
+@mixin avatarColor($color) {
+ background-color: var(--pi-color-#{$color}-75);
+ color: var(--pi-color-#{$color});
+}
+
+@mixin avatarSize($dimension, $font-size) {
+ height: $dimension;
+ width: $dimension;
+ min-width: $dimension;
+
+ .pi-avatar__initials {
+ font-size: $font-size;
+ }
+}
+
+:host {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+ flex-shrink: 0;
+
+ img {
+ height: 100%;
+ width: 100%;
+ object-fit: cover;
+ }
+
+ .pi-avatar__initials {
+ font-weight: 700;
+ line-height: 1;
+ user-select: none;
+ }
+
+ // Shape
+ &.pi-avatar__shape--circle {
+ border-radius: 50%;
+ }
+ &.pi-avatar__shape--square {
+ border-radius: var(--pi-border-radius-small);
+ }
+
+ // Size
+ &.pi-avatar__size--x-small {
+ @include avatarSize(1.5rem, var(--pi-font-size-xxx-small));
+ }
+ &.pi-avatar__size--small {
+ @include avatarSize(2rem, var(--pi-font-size-xx-small));
+ }
+ &.pi-avatar__size--medium {
+ @include avatarSize(2.5rem, var(--pi-font-size-x-small));
+ }
+ &.pi-avatar__size--large {
+ @include avatarSize(3.25rem, var(--pi-font-size-small));
+ }
+ &.pi-avatar__size--x-large {
+ @include avatarSize(4rem, var(--pi-font-size-medium));
+ }
+
+ // Color (fallback: iniciales o icono)
+ &.pi-color__primary {
+ @include avatarColor(primary);
+ }
+ &.pi-color__accent {
+ @include avatarColor(accent);
+ }
+ &.pi-color__highlighted {
+ @include avatarColor(highlighted);
+ }
+ &.pi-color__grey {
+ @include avatarColor(grey);
+ }
+}
diff --git a/libs/ui/src/lib/ui/avatar/avatar/avatar.component.ts b/libs/ui/src/lib/ui/avatar/avatar/avatar.component.ts
new file mode 100644
index 0000000..13db1e0
--- /dev/null
+++ b/libs/ui/src/lib/ui/avatar/avatar/avatar.component.ts
@@ -0,0 +1,60 @@
+import {
+ ChangeDetectionStrategy,
+ Component,
+ HostBinding,
+ computed,
+ input,
+ signal,
+} from '@angular/core';
+
+// Shared
+import { iconSize, theming } from '../../../styles';
+import { PiIconComponent } from '../../icon/icon/icon.component';
+
+@Component({
+ selector: 'pi-avatar',
+ templateUrl: './avatar.component.html',
+ styleUrls: ['./avatar.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ imports: [PiIconComponent],
+})
+export class PiAvatarComponent {
+ @HostBinding('class') get classes(): string {
+ return [
+ 'pi-avatar',
+ `pi-avatar__size--${this.size()}`,
+ `pi-avatar__shape--${this.shape()}`,
+ `pi-color__${this.color()}`,
+ ].join(' ');
+ }
+
+ // Content
+ readonly image = input();
+ readonly alt = input();
+ readonly name = input();
+ /** Icono de respaldo cuando no hay `image` ni `name`. */
+ readonly icon = input('user');
+
+ // Skin
+ readonly size = input('medium');
+ readonly shape = input<'circle' | 'square'>('circle');
+ readonly color = input('primary');
+
+ private readonly imageFailed = signal(false);
+
+ protected readonly showImage = computed(
+ () => !!this.image() && !this.imageFailed(),
+ );
+
+ protected readonly initials = computed(() => {
+ const value = this.name()?.trim();
+ if (!value) return '';
+ const [first, ...rest] = value.split(/\s+/).filter(Boolean);
+ const last = rest[rest.length - 1];
+ return ((first?.[0] ?? '') + (last?.[0] ?? '')).toUpperCase();
+ });
+
+ protected onImageError(): void {
+ this.imageFailed.set(true);
+ }
+}
diff --git a/libs/ui/src/lib/ui/avatar/index.ts b/libs/ui/src/lib/ui/avatar/index.ts
new file mode 100644
index 0000000..51e9cdb
--- /dev/null
+++ b/libs/ui/src/lib/ui/avatar/index.ts
@@ -0,0 +1 @@
+export * from './avatar/avatar.component';
diff --git a/libs/ui/src/lib/ui/badge/badge/badge.component.html b/libs/ui/src/lib/ui/badge/badge/badge.component.html
new file mode 100644
index 0000000..0bb2414
--- /dev/null
+++ b/libs/ui/src/lib/ui/badge/badge/badge.component.html
@@ -0,0 +1,9 @@
+
+
+@if (!isHidden()) {
+
+ @if (hasValue()) {
+ {{ displayValue() }}
+ }
+
+}
diff --git a/libs/ui/src/lib/ui/badge/badge/badge.component.scss b/libs/ui/src/lib/ui/badge/badge/badge.component.scss
new file mode 100644
index 0000000..0f738f3
--- /dev/null
+++ b/libs/ui/src/lib/ui/badge/badge/badge.component.scss
@@ -0,0 +1,97 @@
+@mixin badgeColor($background, $color) {
+ background-color: $background;
+ color: $color;
+}
+
+@mixin badgePosition($vertical, $horizontal, $translate-x, $translate-y) {
+ #{$vertical}: 0;
+ #{$horizontal}: 0;
+ transform: translate($translate-x, $translate-y);
+}
+
+:host {
+ position: relative;
+ display: inline-flex;
+}
+
+.pi-badge__indicator {
+ position: absolute;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 999px;
+ line-height: 1;
+ font-weight: 700;
+ white-space: nowrap;
+ pointer-events: none;
+
+ // Position
+ &.pi-badge__position--top-right {
+ @include badgePosition(top, right, 50%, -50%);
+ }
+ &.pi-badge__position--top-left {
+ @include badgePosition(top, left, -50%, -50%);
+ }
+ &.pi-badge__position--bottom-right {
+ @include badgePosition(bottom, right, 50%, 50%);
+ }
+ &.pi-badge__position--bottom-left {
+ @include badgePosition(bottom, left, -50%, 50%);
+ }
+
+ // Size (with value)
+ &.pi-badge__size--medium {
+ min-width: 1.125rem;
+ height: 1.125rem;
+ padding: 0 var(--pi-spacing-xxx-small);
+ font-size: var(--pi-font-size-xxx-small);
+ }
+ &.pi-badge__size--small {
+ min-width: 0.875rem;
+ height: 0.875rem;
+ padding: 0 var(--pi-spacing-xxxx-small);
+ font-size: 0.4375rem;
+ }
+
+ // Dot (no value)
+ &.pi-badge__indicator--dot {
+ padding: 0;
+ &.pi-badge__size--medium {
+ min-width: 0.5rem;
+ width: 0.5rem;
+ height: 0.5rem;
+ }
+ &.pi-badge__size--small {
+ min-width: 0.375rem;
+ width: 0.375rem;
+ height: 0.375rem;
+ }
+ }
+
+ // Color
+ &.pi-color__primary {
+ @include badgeColor(var(--pi-color-primary), var(--pi-color-white));
+ }
+ &.pi-color__accent {
+ @include badgeColor(var(--pi-color-accent), var(--pi-color-black));
+ }
+ &.pi-color__warn {
+ @include badgeColor(var(--pi-color-warn), var(--pi-color-white));
+ }
+ &.pi-color__success {
+ @include badgeColor(var(--pi-color-success), var(--pi-color-white));
+ }
+ &.pi-color__highlighted {
+ @include badgeColor(var(--pi-color-highlighted), var(--pi-color-white));
+ }
+ &.pi-color__grey {
+ @include badgeColor(var(--pi-color-grey), var(--pi-color-white));
+ }
+ &.pi-color__black {
+ @include badgeColor(var(--pi-color-black), var(--pi-color-white));
+ }
+ &.pi-color__white {
+ @include badgeColor(var(--pi-color-white), var(--pi-color-black));
+ border: 0.0625rem solid var(--pi-color-grey-200);
+ }
+}
diff --git a/libs/ui/src/lib/ui/badge/badge/badge.component.ts b/libs/ui/src/lib/ui/badge/badge/badge.component.ts
new file mode 100644
index 0000000..de736c9
--- /dev/null
+++ b/libs/ui/src/lib/ui/badge/badge/badge.component.ts
@@ -0,0 +1,60 @@
+import {
+ ChangeDetectionStrategy,
+ Component,
+ HostBinding,
+ computed,
+ input,
+} from '@angular/core';
+
+// Shared
+import { styleType } from '../../../styles';
+
+@Component({
+ selector: 'pi-badge',
+ templateUrl: './badge.component.html',
+ styleUrls: ['./badge.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class PiBadgeComponent {
+ @HostBinding('class') className = 'pi-badge';
+
+ // Content
+ /** Si no se define, se muestra un punto sin valor. */
+ readonly value = input();
+ /** Solo aplica cuando `value` es numérico: por encima se muestra `${max}+`. */
+ readonly max = input(99);
+
+ // Skin
+ readonly color = input('warn');
+ readonly position = input<
+ 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'
+ >('top-right');
+ readonly size = input<'small' | 'medium'>('medium');
+
+ /** Oculta el indicador sin desmontar el contenido proyectado. */
+ readonly isHidden = input(false);
+
+ protected readonly hasValue = computed(() => {
+ const value = this.value();
+ return value !== undefined && value !== null && value !== '';
+ });
+
+ protected readonly displayValue = computed(() => {
+ const value = this.value();
+ if (typeof value === 'number') {
+ const max = this.max();
+ return value > max ? `${max}+` : `${value}`;
+ }
+ return value ?? '';
+ });
+
+ protected get indicatorClasses(): string {
+ return [
+ 'pi-badge__indicator',
+ `pi-badge__position--${this.position()}`,
+ `pi-badge__size--${this.size()}`,
+ `pi-color__${this.color()}`,
+ this.hasValue() ? '' : 'pi-badge__indicator--dot',
+ ].join(' ');
+ }
+}
diff --git a/libs/ui/src/lib/ui/badge/index.ts b/libs/ui/src/lib/ui/badge/index.ts
new file mode 100644
index 0000000..c2defca
--- /dev/null
+++ b/libs/ui/src/lib/ui/badge/index.ts
@@ -0,0 +1 @@
+export * from './badge/badge.component';
diff --git a/libs/ui/src/lib/ui/button/icon-button/icon-button.component.html b/libs/ui/src/lib/ui/button/icon-button/icon-button.component.html
index d1b1695..8942f9a 100644
--- a/libs/ui/src/lib/ui/button/icon-button/icon-button.component.html
+++ b/libs/ui/src/lib/ui/button/icon-button/icon-button.component.html
@@ -23,7 +23,7 @@
@if (text()) {
{{ text() }}
}
-
+
} @else {
@@ -54,7 +54,7 @@
@if (text()) {
{{ text() }}
}
-
+
@if (!previous()) {
}
diff --git a/libs/ui/src/lib/ui/button/icon-button/icon-button.component.scss b/libs/ui/src/lib/ui/button/icon-button/icon-button.component.scss
index 4af4dbb..efad676 100644
--- a/libs/ui/src/lib/ui/button/icon-button/icon-button.component.scss
+++ b/libs/ui/src/lib/ui/button/icon-button/icon-button.component.scss
@@ -17,6 +17,14 @@
align-items: center;
justify-content: center;
gap: var(--pi-spacing-xx-small);
+
+ // `#projectedText` se pinta siempre (se usa para detectar si hay
+ // contenido proyectado), pero sin `display: contents` cuenta como hijo
+ // flex vacío y el `gap` de arriba desplaza el icono del centro real
+ // cuando no se proyecta nada (p. ej. ` `).
+ .pi-icon-button__projected {
+ display: contents;
+ }
&:not(:disabled) {
&:hover {
cursor: pointer;
diff --git a/libs/ui/src/lib/ui/data-table/base-data-table/base-data-table.component.ts b/libs/ui/src/lib/ui/data-table/base-data-table/base-data-table.component.ts
index 3aafee7..74bf501 100644
--- a/libs/ui/src/lib/ui/data-table/base-data-table/base-data-table.component.ts
+++ b/libs/ui/src/lib/ui/data-table/base-data-table/base-data-table.component.ts
@@ -6,15 +6,11 @@ import {
effect,
inject,
} from '@angular/core';
-import { Store } from '@ngrx/store';
import { DataTableItem } from '../entities/data-table-item.model';
import { ComponentFamilyClass } from '../../../styles';
-import {
- DeviceConfigsState,
- DeviceTypeSelectors,
-} from '../../../util/device-configs';
+import { DeviceConfigsFacade } from '../../../util/device-configs';
import { take } from 'rxjs/operators';
@Component({
@@ -37,7 +33,7 @@ export class PiBaseDataTableComponent {
readonly type = input<'header' | 'data'>('data');
readonly hideValues = input(false);
- private readonly store = inject>(Store);
+ private readonly deviceConfigsFacade = inject(DeviceConfigsFacade);
constructor() {
// `hideHeader` lee inputs; las otras dos líneas del antiguo `ngOnInit` solo
@@ -54,10 +50,9 @@ export class PiBaseDataTableComponent {
}
private hideHeader(): void {
- this.store
- .select(DeviceTypeSelectors.getDevice)
+ this.deviceConfigsFacade.device$
.pipe(take(1))
- .subscribe((deviceType: string) => {
+ .subscribe((deviceType) => {
if (deviceType !== 'DESKTOP' && this.hideValues()) {
delete this.items()?.[0]?.values;
}
diff --git a/libs/ui/src/lib/ui/data-table/data-table-item/data-table-item.component.ts b/libs/ui/src/lib/ui/data-table/data-table-item/data-table-item.component.ts
index 2223828..1852a4b 100644
--- a/libs/ui/src/lib/ui/data-table/data-table-item/data-table-item.component.ts
+++ b/libs/ui/src/lib/ui/data-table/data-table-item/data-table-item.component.ts
@@ -8,16 +8,8 @@ import {
input,
contentChild,
viewChild,
- inject,
} from '@angular/core';
import { textStyle, justify, borderType } from '../../../styles';
-import { Store } from '@ngrx/store';
-
-// Store
-import { DeviceConfigsState } from '../../../util/device-configs';
-
-// RxJS
-import { take } from 'rxjs/operators';
import { NgTemplateOutlet } from '@angular/common';
@Component({
selector: 'pi-data-table-item',
@@ -42,8 +34,6 @@ export class PiDataTableItemComponent implements AfterViewInit {
readonly item = contentChild>('item');
readonly componentItem = viewChild('componentItem');
- private readonly store = inject>(Store);
-
ngAfterViewInit(): void {
this.setCellClassByIndex();
}
diff --git a/libs/ui/src/lib/ui/drawer/drawer/drawer.component.html b/libs/ui/src/lib/ui/drawer/drawer/drawer.component.html
new file mode 100644
index 0000000..3f1acdc
--- /dev/null
+++ b/libs/ui/src/lib/ui/drawer/drawer/drawer.component.html
@@ -0,0 +1,10 @@
+
+ @if (data.title) {
+ {{ data.title }}
+ }
+
+
+
+
+
+
diff --git a/libs/ui/src/lib/ui/drawer/drawer/drawer.component.scss b/libs/ui/src/lib/ui/drawer/drawer/drawer.component.scss
new file mode 100644
index 0000000..84d4b2a
--- /dev/null
+++ b/libs/ui/src/lib/ui/drawer/drawer/drawer.component.scss
@@ -0,0 +1,21 @@
+:host {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ width: 100%;
+
+ header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--pi-spacing-medium);
+ padding: var(--pi-spacing-large) var(--pi-spacing-x-large);
+ border-bottom: 0.0625rem solid var(--pi-color-grey-100);
+ }
+
+ main {
+ flex-grow: 1;
+ overflow-y: auto;
+ padding: var(--pi-spacing-x-large);
+ }
+}
diff --git a/libs/ui/src/lib/ui/drawer/drawer/drawer.component.ts b/libs/ui/src/lib/ui/drawer/drawer/drawer.component.ts
new file mode 100644
index 0000000..cc7bc22
--- /dev/null
+++ b/libs/ui/src/lib/ui/drawer/drawer/drawer.component.ts
@@ -0,0 +1,31 @@
+import { ChangeDetectionStrategy, Component, HostBinding, inject } from '@angular/core';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { NgTemplateOutlet } from '@angular/common';
+
+import { DrawerData } from '../entities/drawer.model';
+import { PiCloseButtonComponent } from '../../button/close-button/close-button.component';
+import { PiTextWeightBoldDirective } from '../../text/directives/text-weights.directive';
+import { PiTextLargeDirective } from '../../text/directives/text-sizes.directive';
+
+@Component({
+ selector: 'pi-drawer',
+ templateUrl: './drawer.component.html',
+ styleUrls: ['./drawer.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ imports: [
+ NgTemplateOutlet,
+ PiCloseButtonComponent,
+ PiTextWeightBoldDirective,
+ PiTextLargeDirective,
+ ],
+})
+export class PiDrawerComponent {
+ @HostBinding('class') className = 'pi-drawer';
+
+ readonly dialogRef = inject>(MatDialogRef);
+ readonly data = inject(MAT_DIALOG_DATA);
+
+ close(): void {
+ this.dialogRef.close();
+ }
+}
diff --git a/libs/ui/src/lib/ui/drawer/entities/drawer-configs.ts b/libs/ui/src/lib/ui/drawer/entities/drawer-configs.ts
new file mode 100644
index 0000000..df80a9f
--- /dev/null
+++ b/libs/ui/src/lib/ui/drawer/entities/drawer-configs.ts
@@ -0,0 +1,41 @@
+import { MatDialogConfig } from '@angular/material/dialog';
+
+export enum DrawerConfigKeys {
+ Left = 'left',
+ Right = 'right',
+}
+
+export type DrawerConfigs = {
+ [key in DrawerConfigKeys]: MatDialogConfig;
+};
+
+/**
+ * `enterAnimationDuration`/`exitAnimationDuration` a `0ms`: la animación de
+ * `MatDialog` (fade + scale desde el centro) no encaja con un panel anclado
+ * al borde. El deslizamiento real lo hace `_mat-drawer.scss` sobre
+ * `panelClass`, fuera del ciclo de animación de Material.
+ */
+export const drawerConfigs: DrawerConfigs = {
+ [DrawerConfigKeys.Right]: {
+ height: '100vh',
+ maxHeight: '100vh',
+ width: '24rem',
+ maxWidth: '90vw',
+ position: { top: '0', right: '0' },
+ backdropClass: 'pi-backdrop',
+ panelClass: ['pi-drawer__panel', 'pi-drawer__panel--right'],
+ enterAnimationDuration: '0ms',
+ exitAnimationDuration: '0ms',
+ },
+ [DrawerConfigKeys.Left]: {
+ height: '100vh',
+ maxHeight: '100vh',
+ width: '24rem',
+ maxWidth: '90vw',
+ position: { top: '0', left: '0' },
+ backdropClass: 'pi-backdrop',
+ panelClass: ['pi-drawer__panel', 'pi-drawer__panel--left'],
+ enterAnimationDuration: '0ms',
+ exitAnimationDuration: '0ms',
+ },
+};
diff --git a/libs/ui/src/lib/ui/drawer/entities/drawer.model.ts b/libs/ui/src/lib/ui/drawer/entities/drawer.model.ts
new file mode 100644
index 0000000..e7ceabf
--- /dev/null
+++ b/libs/ui/src/lib/ui/drawer/entities/drawer.model.ts
@@ -0,0 +1,6 @@
+import { TemplateRef } from '@angular/core';
+
+export interface DrawerData {
+ title?: string;
+ template: TemplateRef;
+}
diff --git a/libs/ui/src/lib/ui/drawer/index.ts b/libs/ui/src/lib/ui/drawer/index.ts
new file mode 100644
index 0000000..98422fa
--- /dev/null
+++ b/libs/ui/src/lib/ui/drawer/index.ts
@@ -0,0 +1,9 @@
+// Component
+export * from './drawer/drawer.component';
+
+// Entities
+export * from './entities/drawer.model';
+export * from './entities/drawer-configs';
+
+// Utils
+export * from './utils';
diff --git a/libs/ui/src/lib/ui/drawer/utils.ts b/libs/ui/src/lib/ui/drawer/utils.ts
new file mode 100644
index 0000000..a54f419
--- /dev/null
+++ b/libs/ui/src/lib/ui/drawer/utils.ts
@@ -0,0 +1,16 @@
+import { MatDialogConfig } from '@angular/material/dialog';
+import { DrawerConfigKeys, drawerConfigs } from './entities/drawer-configs';
+import { DrawerData } from './entities/drawer.model';
+
+// Drawer config
+export function getDrawerConfigs(
+ key: DrawerConfigKeys,
+ data: DrawerData,
+ disableClose?: boolean,
+): MatDialogConfig {
+ return {
+ ...(disableClose && { disableClose }),
+ ...drawerConfigs[key],
+ data,
+ };
+}
diff --git a/libs/ui/src/lib/ui/notification/index.ts b/libs/ui/src/lib/ui/notification/index.ts
index 44fc646..a442192 100644
--- a/libs/ui/src/lib/ui/notification/index.ts
+++ b/libs/ui/src/lib/ui/notification/index.ts
@@ -5,14 +5,8 @@ export * from './entities/notification.data';
// Services
export * from './infrastructure/notification.service';
-// State
-export * from './state/notification.actions';
-export * from './state/notification.effects';
-
// Utils
export * from './utils';
-// Module
-
// Components
export * from './snack-bar/snack-bar.component';
diff --git a/libs/ui/src/lib/ui/notification/state/notification.actions.ts b/libs/ui/src/lib/ui/notification/state/notification.actions.ts
deleted file mode 100644
index e2618e3..0000000
--- a/libs/ui/src/lib/ui/notification/state/notification.actions.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { createAction, props } from '@ngrx/store';
-
-export const displaySuccess = createAction(
- '[CORE NOTIFICATION] Display Success',
- props<{
- payload: string;
- description?: string;
- }>(),
-);
-
-export const displayError = createAction(
- '[CORE NOTIFICATION] Display Error',
- props<{
- payload: string;
- description?: string;
- }>(),
-);
-
-export const displayInfo = createAction(
- '[CORE NOTIFICATION] Display Info',
- props<{
- payload: string;
- description?: string;
- }>(),
-);
-
-export const triggerNotificationAction = createAction(
- '[CORE NOTIFICATION] Trigger Notification Action',
-);
diff --git a/libs/ui/src/lib/ui/notification/state/notification.effects.ts b/libs/ui/src/lib/ui/notification/state/notification.effects.ts
deleted file mode 100644
index 2f6a898..0000000
--- a/libs/ui/src/lib/ui/notification/state/notification.effects.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import { Actions, createEffect, ofType } from '@ngrx/effects';
-
-import * as NotificationActions from './notification.actions';
-
-import { map, switchMap, tap } from 'rxjs/operators';
-
-import { Injectable } from '@angular/core';
-
-import { NotificationService } from '../infrastructure/notification.service';
-
-@Injectable()
-export class NotificationEffects {
- constructor(
- private action$: Actions,
- private _notification: NotificationService,
- ) {}
-
- displaySuccess$ = createEffect(
- () => {
- return this.action$.pipe(
- ofType(NotificationActions.displaySuccess),
- map((action) => {
- return action;
- }),
- tap((action) => {
- this._notification.success(action.payload, action.description);
- }),
- );
- },
- { dispatch: false },
- );
-
- displayError$ = createEffect(
- () => {
- return this.action$.pipe(
- ofType(NotificationActions.displayError),
- map((action) => {
- return action;
- }),
- tap((action) => {
- this._notification.error(action.payload, action.description);
- }),
- );
- },
- { dispatch: false },
- );
-
- displayInfo$ = createEffect(
- () => {
- return this.action$.pipe(
- ofType(NotificationActions.displayInfo),
- map((action) => {
- return action;
- }),
- tap((action) => {
- this._notification.info(action.payload, action.description);
- }),
- );
- },
- { dispatch: false },
- );
-}
diff --git a/libs/ui/src/lib/ui/popover/index.ts b/libs/ui/src/lib/ui/popover/index.ts
new file mode 100644
index 0000000..7a2ac91
--- /dev/null
+++ b/libs/ui/src/lib/ui/popover/index.ts
@@ -0,0 +1 @@
+export * from './popover.directive';
diff --git a/libs/ui/src/lib/ui/popover/popover.directive.ts b/libs/ui/src/lib/ui/popover/popover.directive.ts
new file mode 100644
index 0000000..a5a1b73
--- /dev/null
+++ b/libs/ui/src/lib/ui/popover/popover.directive.ts
@@ -0,0 +1,226 @@
+import {
+ Directive,
+ ElementRef,
+ OnDestroy,
+ TemplateRef,
+ ViewContainerRef,
+ inject,
+ input,
+ output,
+} from '@angular/core';
+import {
+ ConnectedPosition,
+ Overlay,
+ OverlayRef,
+} from '@angular/cdk/overlay';
+import { TemplatePortal } from '@angular/cdk/portal';
+
+export type PopoverPosition = 'above' | 'below' | 'before' | 'after';
+export type PopoverTrigger = 'click' | 'hover';
+
+export interface PopoverContext {
+ close: () => void;
+}
+
+const GAP = 8;
+
+/** Cada posición trae su opuesta como fallback si no cabe en el viewport. */
+const POSITIONS: Record = {
+ below: [
+ {
+ originX: 'center',
+ originY: 'bottom',
+ overlayX: 'center',
+ overlayY: 'top',
+ offsetY: GAP,
+ },
+ {
+ originX: 'center',
+ originY: 'top',
+ overlayX: 'center',
+ overlayY: 'bottom',
+ offsetY: -GAP,
+ },
+ ],
+ above: [
+ {
+ originX: 'center',
+ originY: 'top',
+ overlayX: 'center',
+ overlayY: 'bottom',
+ offsetY: -GAP,
+ },
+ {
+ originX: 'center',
+ originY: 'bottom',
+ overlayX: 'center',
+ overlayY: 'top',
+ offsetY: GAP,
+ },
+ ],
+ after: [
+ {
+ originX: 'end',
+ originY: 'center',
+ overlayX: 'start',
+ overlayY: 'center',
+ offsetX: GAP,
+ },
+ {
+ originX: 'start',
+ originY: 'center',
+ overlayX: 'end',
+ overlayY: 'center',
+ offsetX: -GAP,
+ },
+ ],
+ before: [
+ {
+ originX: 'start',
+ originY: 'center',
+ overlayX: 'end',
+ overlayY: 'center',
+ offsetX: -GAP,
+ },
+ {
+ originX: 'end',
+ originY: 'center',
+ overlayX: 'start',
+ overlayY: 'center',
+ offsetX: GAP,
+ },
+ ],
+};
+
+/**
+ * A diferencia de `pi-tooltip` (envuelve `MatTooltip`, solo texto), aquí no
+ * hay componente Material equivalente para contenido enriquecido: se monta
+ * directamente sobre `@angular/cdk/overlay`.
+ */
+@Directive({
+ selector: '[pi-popover]',
+ host: {
+ '(click)': 'onHostClick()',
+ '(mouseenter)': 'onHostMouseEnter()',
+ '(mouseleave)': 'onHostMouseLeave()',
+ },
+})
+export class PiPopoverDirective implements OnDestroy {
+ readonly popoverContent = input>();
+ readonly popoverPosition = input('below');
+ readonly popoverTrigger = input('click');
+ readonly popoverPanelClass = input('pi-popover__panel');
+
+ readonly opened = output();
+ readonly closed = output();
+
+ private readonly overlay = inject(Overlay);
+ private readonly elementRef = inject(ElementRef);
+ private readonly viewContainerRef = inject(ViewContainerRef);
+
+ private overlayRef?: OverlayRef;
+ private closeTimeoutId?: ReturnType;
+
+ protected onHostClick(): void {
+ if (this.popoverTrigger() === 'click') this.toggle();
+ }
+
+ protected onHostMouseEnter(): void {
+ if (this.popoverTrigger() === 'hover') {
+ this.cancelScheduledClose();
+ this.show();
+ }
+ }
+
+ protected onHostMouseLeave(): void {
+ if (this.popoverTrigger() === 'hover') this.scheduleClose();
+ }
+
+ toggle(): void {
+ if (this.isOpen()) {
+ this.close();
+ } else {
+ this.show();
+ }
+ }
+
+ isOpen(): boolean {
+ return !!this.overlayRef?.hasAttached();
+ }
+
+ show(): void {
+ const template = this.popoverContent();
+ if (!template || this.isOpen()) return;
+
+ const overlayRef = this.getOverlayRef();
+ const context: PopoverContext = { close: () => this.close() };
+ overlayRef.attach(
+ new TemplatePortal(template, this.viewContainerRef, context),
+ );
+ this.opened.emit();
+ }
+
+ close(): void {
+ this.cancelScheduledClose();
+ if (!this.isOpen()) return;
+ this.overlayRef?.detach();
+ this.closed.emit();
+ }
+
+ ngOnDestroy(): void {
+ this.overlayRef?.dispose();
+ this.cancelScheduledClose();
+ }
+
+ private getOverlayRef(): OverlayRef {
+ if (this.overlayRef) return this.overlayRef;
+
+ const isHoverTrigger = this.popoverTrigger() === 'hover';
+
+ const positionStrategy = this.overlay
+ .position()
+ .flexibleConnectedTo(this.elementRef)
+ .withPositions(POSITIONS[this.popoverPosition()])
+ .withFlexibleDimensions(false)
+ .withPush(true);
+
+ const overlayRef = this.overlay.create({
+ positionStrategy,
+ scrollStrategy: this.overlay.scrollStrategies.reposition(),
+ hasBackdrop: !isHoverTrigger,
+ backdropClass: 'cdk-overlay-transparent-backdrop',
+ panelClass: this.popoverPanelClass(),
+ });
+
+ if (!isHoverTrigger) {
+ overlayRef.backdropClick().subscribe(() => this.close());
+ } else {
+ // Sin backdrop en modo hover: el ratón entra/sale directamente del panel.
+ overlayRef.overlayElement.addEventListener('mouseenter', () =>
+ this.cancelScheduledClose(),
+ );
+ overlayRef.overlayElement.addEventListener('mouseleave', () =>
+ this.scheduleClose(),
+ );
+ }
+ overlayRef.keydownEvents().subscribe((event) => {
+ if (event.key === 'Escape') this.close();
+ });
+
+ this.overlayRef = overlayRef;
+ return overlayRef;
+ }
+
+ private scheduleClose(): void {
+ this.cancelScheduledClose();
+ // Margen para que el ratón viaje del disparador al panel sin que cierre.
+ this.closeTimeoutId = setTimeout(() => this.close(), 100);
+ }
+
+ private cancelScheduledClose(): void {
+ if (this.closeTimeoutId) {
+ clearTimeout(this.closeTimeoutId);
+ this.closeTimeoutId = undefined;
+ }
+ }
+}
diff --git a/libs/ui/src/lib/util/device-configs/application/device-configs.facade.ts b/libs/ui/src/lib/util/device-configs/application/device-configs.facade.ts
index 9087907..5b51116 100644
--- a/libs/ui/src/lib/util/device-configs/application/device-configs.facade.ts
+++ b/libs/ui/src/lib/util/device-configs/application/device-configs.facade.ts
@@ -1,46 +1,41 @@
// Angular
-import { Injectable, inject } from '@angular/core';
-
-// Ngrx
-import { Store } from '@ngrx/store';
+import { Injectable } from '@angular/core';
// Rxjs
-import { Observable } from 'rxjs';
+import { BehaviorSubject, Observable, combineLatest } from 'rxjs';
+import { map } from 'rxjs/operators';
// State
import { Browser, Device, Size } from '../entities/device-configs.model';
-import * as fromActions from '../state/device-configs.actions';
-import * as fromState from '../state/device-configs.reducer';
-import * as fromSelectors from '../state/device-configs.selectors';
+import { getUserBrowser } from '../services/device-configs.service';
@Injectable({ providedIn: 'root' })
export class DeviceConfigsFacade {
- // `inject()` en vez de parámetro de constructor: los inicializadores de
- // propiedad de abajo se evalúan antes que los parámetros del constructor.
- private readonly store = inject>(Store);
+ private readonly deviceSubject = new BehaviorSubject('DESKTOP');
+ private readonly sizeSubject = new BehaviorSubject(null);
+ private readonly browserSubject = new BehaviorSubject('UNKNOWN');
// Device
- readonly device$: Observable = this.store.select(
- fromSelectors.getDevice,
- );
+ readonly device$: Observable = this.deviceSubject.asObservable();
// `null` hasta que se resuelve el tamaño (así lo declara el estado inicial)
- readonly size$: Observable = this.store.select(
- fromSelectors.getSize,
- );
- readonly isSmallDevice$: Observable = this.store.select(
- fromSelectors.getIsSmallDevice,
- );
+ readonly size$: Observable = this.sizeSubject.asObservable();
+ readonly isSmallDevice$: Observable = combineLatest([
+ this.device$,
+ this.size$,
+ ]).pipe(map(([device, size]) => device !== 'DESKTOP' && size !== 'LARGE'));
// Browser
- readonly browser$: Observable = this.store.select(
- fromSelectors.getBrowser,
- );
+ readonly browser$: Observable = this.browserSubject.asObservable();
getBrowser(): void {
- this.store.dispatch(fromActions.BrowserActions.getBrowser());
+ this.browserSubject.next(getUserBrowser());
}
setDevice(device: Device): void {
- this.store.dispatch(fromActions.DeviceActions.saveDeviceType({ device }));
+ this.deviceSubject.next(device);
+ }
+
+ setSize(size: Size): void {
+ this.sizeSubject.next(size);
}
}
diff --git a/libs/ui/src/lib/util/device-configs/index.ts b/libs/ui/src/lib/util/device-configs/index.ts
index 25525b3..d718d1b 100644
--- a/libs/ui/src/lib/util/device-configs/index.ts
+++ b/libs/ui/src/lib/util/device-configs/index.ts
@@ -7,12 +7,3 @@ export * from './entities/device-configs.model';
// Services
export * from './services/device-configs.service';
-
-// State
-export * from './state/device-configs.actions';
-export * from './state/device-configs.effects';
-export * from './state/device-configs.selectors';
-export * from './state/device-configs.reducer';
-import * as DeviceTypeActions from './state/device-configs.actions';
-import * as DeviceTypeSelectors from './state/device-configs.selectors';
-export { DeviceTypeActions, DeviceTypeSelectors };
diff --git a/libs/ui/src/lib/util/device-configs/services/device-configs.service.ts b/libs/ui/src/lib/util/device-configs/services/device-configs.service.ts
index 79ea92d..caa3db0 100644
--- a/libs/ui/src/lib/util/device-configs/services/device-configs.service.ts
+++ b/libs/ui/src/lib/util/device-configs/services/device-configs.service.ts
@@ -1,8 +1,6 @@
import { BreakpointObserver } from '@angular/cdk/layout';
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
-import { Inject, Injectable, PLATFORM_ID, inject } from '@angular/core';
-
-import { Store } from '@ngrx/store';
+import { Injectable, PLATFORM_ID, inject } from '@angular/core';
// Entities
import {
@@ -23,8 +21,24 @@ import {
import { Browser } from '../entities/device-configs.model';
// State
-import * as fromActions from '../state/device-configs.actions';
-import { DeviceConfigsState } from '../state/device-configs.reducer';
+import { DeviceConfigsFacade } from '../application/device-configs.facade';
+
+export function getUserBrowser(): Browser {
+ const userAgent = window.navigator.userAgent;
+ if (/MSIE|Trident/.test(userAgent)) {
+ return 'EXPLORER';
+ } else if (/Firefox/.test(userAgent)) {
+ return 'FIREFOX';
+ } else if (/Edg|Edge/.test(userAgent)) {
+ return 'EDGE';
+ } else if (/Chrome/.test(userAgent) && !/Edg|Edge/.test(userAgent)) {
+ return 'CHROME';
+ } else if (/Safari/.test(userAgent) && !/Edg|Edge/.test(userAgent)) {
+ return 'SAFARI';
+ } else {
+ return 'UNKNOWN';
+ }
+}
@Injectable({
providedIn: 'root',
@@ -32,7 +46,7 @@ import { DeviceConfigsState } from '../state/device-configs.reducer';
export class DeviceConfigsService {
private htmlTag!: HTMLElement;
- private readonly store = inject>(Store);
+ private readonly deviceConfigsFacade = inject(DeviceConfigsFacade);
private readonly breakpointObserver = inject(BreakpointObserver);
private readonly document = inject(DOCUMENT);
readonly platformId = inject(PLATFORM_ID);
@@ -42,20 +56,7 @@ export class DeviceConfigsService {
}
getUserBrowser(): Browser {
- const userAgent = window.navigator.userAgent;
- if (/MSIE|Trident/.test(userAgent)) {
- return 'EXPLORER';
- } else if (/Firefox/.test(userAgent)) {
- return 'FIREFOX';
- } else if (/Edg|Edge/.test(userAgent)) {
- return 'EDGE';
- } else if (/Chrome/.test(userAgent) && !/Edg|Edge/.test(userAgent)) {
- return 'CHROME';
- } else if (/Safari/.test(userAgent) && !/Edg|Edge/.test(userAgent)) {
- return 'SAFARI';
- } else {
- return 'UNKNOWN';
- }
+ return getUserBrowser();
}
private deviceConfigsHandler(): void {
@@ -82,12 +83,8 @@ export class DeviceConfigsService {
if (result.matches) {
// Mobile
if (result.breakpoints[MaxMobileBreakpoint]) {
- this.store.dispatch(
- fromActions.DeviceActions.saveDeviceType({ device: 'MOBILE' }),
- );
- this.store.dispatch(
- fromActions.DeviceActions.saveDeviceSize({ size: 'SMALL' }),
- );
+ this.deviceConfigsFacade.setDevice('MOBILE');
+ this.deviceConfigsFacade.setSize('SMALL');
this.setClassIsMobile();
}
// Tablet
@@ -95,18 +92,14 @@ export class DeviceConfigsService {
result.breakpoints[MinTabletBreakpoint] &&
result.breakpoints[MaxTabletBreakpoint]
) {
- this.store.dispatch(
- fromActions.DeviceActions.saveDeviceType({ device: 'TABLET' }),
- );
+ this.deviceConfigsFacade.setDevice('TABLET');
this.setClassIsTablet();
// Tablet S
if (
result.breakpoints[MinTabletSmallBreakpoint] &&
result.breakpoints[MaxTabletSmallBreakpoint]
) {
- this.store.dispatch(
- fromActions.DeviceActions.saveDeviceSize({ size: 'SMALL' }),
- );
+ this.deviceConfigsFacade.setSize('SMALL');
this.setClassIsTabletSmall();
}
// Tablet L
@@ -114,27 +107,21 @@ export class DeviceConfigsService {
result.breakpoints[MinTabletLargeBreakpoint] &&
result.breakpoints[MaxTabletLargeBreakpoint]
) {
- this.store.dispatch(
- fromActions.DeviceActions.saveDeviceSize({ size: 'LARGE' }),
- );
+ this.deviceConfigsFacade.setSize('LARGE');
this.setClassIsTabletLarge();
}
}
// Desktop
if (result.breakpoints[MinDesktopBreakpoint]) {
- this.store.dispatch(
- fromActions.DeviceActions.saveDeviceType({ device: 'DESKTOP' }),
- );
+ this.deviceConfigsFacade.setDevice('DESKTOP');
this.setClassIsDesktop();
// Desktop S
if (
result.breakpoints[MinDesktopSmallBreakpoint] &&
result.breakpoints[MaxDesktopSmallBreakpoint]
) {
- this.store.dispatch(
- fromActions.DeviceActions.saveDeviceSize({ size: 'SMALL' }),
- );
+ this.deviceConfigsFacade.setSize('SMALL');
this.setClassIsDesktopSmall();
}
// Desktop M
@@ -142,16 +129,12 @@ export class DeviceConfigsService {
result.breakpoints[MinDesktopMediumBreakpoint] &&
result.breakpoints[MaxDesktopMediumBreakpoint]
) {
- this.store.dispatch(
- fromActions.DeviceActions.saveDeviceSize({ size: 'MEDIUM' }),
- );
+ this.deviceConfigsFacade.setSize('MEDIUM');
this.setClassIsDesktopMedium();
}
// Desktop L
if (result.breakpoints[MinDesktopLargeBreakpoint]) {
- this.store.dispatch(
- fromActions.DeviceActions.saveDeviceSize({ size: 'LARGE' }),
- );
+ this.deviceConfigsFacade.setSize('LARGE');
this.setClassIsDesktopLarge();
}
}
diff --git a/libs/ui/src/lib/util/device-configs/state/device-configs.actions.ts b/libs/ui/src/lib/util/device-configs/state/device-configs.actions.ts
deleted file mode 100644
index 18a20bf..0000000
--- a/libs/ui/src/lib/util/device-configs/state/device-configs.actions.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { createActionGroup, emptyProps, props } from '@ngrx/store';
-
-// Entities
-import { Device, Size, Browser } from '../entities/device-configs.model';
-
-export const DeviceActions = createActionGroup({
- source: '[Main] Get Device Breakpoints',
- events: {
- 'Save Device Type': props<{ device: Device }>(),
- 'Save Device Size': props<{ size: Size }>(),
- },
-});
-
-export const BrowserActions = createActionGroup({
- source: '[Main] Get Browser',
- events: {
- 'Get Browser': emptyProps(),
- 'Get Browser Success': props<{ response: Browser }>(),
- 'Get Browser Error': emptyProps(),
- },
-});
diff --git a/libs/ui/src/lib/util/device-configs/state/device-configs.effects.ts b/libs/ui/src/lib/util/device-configs/state/device-configs.effects.ts
deleted file mode 100644
index 3473fac..0000000
--- a/libs/ui/src/lib/util/device-configs/state/device-configs.effects.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { Injectable, inject } from '@angular/core';
-
-// Rxjs
-import { of } from 'rxjs';
-import { catchError, exhaustMap, map } from 'rxjs/operators';
-
-// Ngrx
-import { Actions, createEffect, ofType } from '@ngrx/effects';
-
-// Actions
-import * as fromActions from './device-configs.actions';
-
-// Service
-import { DeviceConfigsService } from '../services/device-configs.service';
-
-@Injectable()
-export class DeviceConfigsEffects {
- private readonly deviceConfigsService = inject(DeviceConfigsService);
- private readonly actions = inject(Actions);
-
- getBrowser$ = createEffect(() => {
- return this.actions.pipe(
- ofType(fromActions.BrowserActions.getBrowser),
- exhaustMap(() =>
- of(this.deviceConfigsService.getUserBrowser()).pipe(
- map((response) => {
- return fromActions.BrowserActions.getBrowserSuccess({ response });
- }),
- catchError(() => {
- return of(fromActions.BrowserActions.getBrowserError());
- }),
- ),
- ),
- );
- });
-}
diff --git a/libs/ui/src/lib/util/device-configs/state/device-configs.reducer.ts b/libs/ui/src/lib/util/device-configs/state/device-configs.reducer.ts
deleted file mode 100644
index 9155853..0000000
--- a/libs/ui/src/lib/util/device-configs/state/device-configs.reducer.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { createReducer, on } from '@ngrx/store';
-import * as fromActions from './device-configs.actions';
-
-import { Device, Size, Browser } from '../entities/device-configs.model';
-
-export const featureKey = 'CORE_deviceConfigs';
-
-export interface DeviceConfigsState {
- device: Device;
- size: Size | null;
- browser: Browser;
-}
-
-export const initialState: DeviceConfigsState = {
- device: 'DESKTOP',
- size: null,
- browser: 'UNKNOWN',
-};
-
-export const deviceConfigsReducer = createReducer(
- initialState,
- on(fromActions.DeviceActions.saveDeviceType, (state, action) => {
- return { ...state, device: action.device };
- }),
-
- on(fromActions.DeviceActions.saveDeviceSize, (state, action) => {
- return { ...state, size: action.size };
- }),
- on(fromActions.BrowserActions.getBrowserSuccess, (state, action) => {
- return { ...state, browser: action.response };
- }),
-);
diff --git a/libs/ui/src/lib/util/device-configs/state/device-configs.selectors.ts b/libs/ui/src/lib/util/device-configs/state/device-configs.selectors.ts
deleted file mode 100644
index 3a446db..0000000
--- a/libs/ui/src/lib/util/device-configs/state/device-configs.selectors.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { createSelector, createFeatureSelector } from '@ngrx/store';
-
-import * as fromReducer from './device-configs.reducer';
-
-export const getFeatureState =
- createFeatureSelector(fromReducer.featureKey);
-
-// Device Breakpoints
-export const getDevice = createSelector(
- getFeatureState,
- (state) => state.device,
-);
-export const getSize = createSelector(getFeatureState, (state) => state.size);
-
-// Mobile & Tablet S
-export const getIsSmallDevice = createSelector(getFeatureState, (state) => {
- return state.device !== 'DESKTOP' && state.size !== 'LARGE';
-});
-
-// Browser
-export const getBrowser = createSelector(
- getFeatureState,
- (state) => state.browser,
-);
diff --git a/libs/ui/src/lib/util/list-configs/application/list-configs.facade.ts b/libs/ui/src/lib/util/list-configs/application/list-configs.facade.ts
index c99e9fb..b1a819d 100644
--- a/libs/ui/src/lib/util/list-configs/application/list-configs.facade.ts
+++ b/libs/ui/src/lib/util/list-configs/application/list-configs.facade.ts
@@ -1,32 +1,24 @@
// Angular
-import { Injectable, inject } from '@angular/core';
-
-// Ngrx
-import { Store } from '@ngrx/store';
+import { Injectable } from '@angular/core';
// Rxjs
-import { Observable } from 'rxjs';
+import { BehaviorSubject, Observable } from 'rxjs';
-// State
-import * as fromSelectors from '../state/list-configs.selectors';
-import * as fromActions from '../state/list-configs.actions';
-import * as fromState from '../state/list-configs.reducer';
+export type ListMode = 'grid' | 'list';
+
+const initialMode: ListMode = 'list';
@Injectable({ providedIn: 'root' })
export class ListConfigsFacade {
- // `inject()` en vez de parámetro de constructor: los inicializadores de
- // propiedad de abajo se evalúan antes que los parámetros del constructor.
- private readonly store = inject>(Store);
+ private readonly modeSubject = new BehaviorSubject(initialMode);
- readonly mode$: Observable = this.store.select(
- fromSelectors.getListMode,
- );
+ readonly mode$: Observable = this.modeSubject.asObservable();
- setListMode(payload: fromState.ListMode): void {
- this.store.dispatch(fromActions.setListMode({ payload }));
+ setListMode(payload: ListMode): void {
+ this.modeSubject.next(payload);
}
resetListMode(): void {
- this.store.dispatch(fromActions.resetListMode());
+ this.modeSubject.next(initialMode);
}
}
diff --git a/libs/ui/src/lib/util/list-configs/index.ts b/libs/ui/src/lib/util/list-configs/index.ts
index 37d7ada..2a04fc9 100644
--- a/libs/ui/src/lib/util/list-configs/index.ts
+++ b/libs/ui/src/lib/util/list-configs/index.ts
@@ -1,7 +1,2 @@
// Facade
export * from './application/list-configs.facade';
-
-// State
-export * from './state/list-configs.actions';
-export * from './state/list-configs.selectors';
-export * from './state/list-configs.reducer';
diff --git a/libs/ui/src/lib/util/list-configs/state/list-configs.actions.ts b/libs/ui/src/lib/util/list-configs/state/list-configs.actions.ts
deleted file mode 100644
index 4956683..0000000
--- a/libs/ui/src/lib/util/list-configs/state/list-configs.actions.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { createAction, props } from '@ngrx/store';
-
-import { ListMode } from './list-configs.reducer';
-
-export enum ListModeActionsType {
- SET_LIST_MODE = '[Main] Set List Mode',
- RESET_LIST_MODE = '[Main] Reset List Mode',
-}
-
-export const setListMode = createAction(
- ListModeActionsType.SET_LIST_MODE,
- props<{ payload: ListMode }>(),
-);
-
-export const resetListMode = createAction(ListModeActionsType.RESET_LIST_MODE);
diff --git a/libs/ui/src/lib/util/list-configs/state/list-configs.reducer.ts b/libs/ui/src/lib/util/list-configs/state/list-configs.reducer.ts
deleted file mode 100644
index 2ad5843..0000000
--- a/libs/ui/src/lib/util/list-configs/state/list-configs.reducer.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { createReducer, on } from '@ngrx/store';
-
-import { resetListMode, setListMode } from './list-configs.actions';
-
-export const featureKey = 'CORE_listConfigs';
-
-export interface ListConfigsState {
- mode: ListMode;
-}
-
-export type ListMode = 'grid' | 'list';
-
-export const initialState: ListConfigsState = {
- mode: 'list',
-};
-
-export const listConfigsReducer = createReducer(
- initialState,
- on(setListMode, (state, action) => {
- return { ...state, mode: action.payload };
- }),
- on(resetListMode, (state) => {
- return { ...initialState };
- }),
-);
diff --git a/libs/ui/src/lib/util/list-configs/state/list-configs.selectors.ts b/libs/ui/src/lib/util/list-configs/state/list-configs.selectors.ts
deleted file mode 100644
index 2aeba62..0000000
--- a/libs/ui/src/lib/util/list-configs/state/list-configs.selectors.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { createFeatureSelector, createSelector } from '@ngrx/store';
-
-import * as fromReducer from './list-configs.reducer';
-
-export const getFeatureState =
- createFeatureSelector(fromReducer.featureKey);
-
-export const getListMode = createSelector(
- getFeatureState,
- (state) => state.mode,
-);
diff --git a/libs/ui/src/lib/util/loaders/aplication/loader.facade.ts b/libs/ui/src/lib/util/loaders/aplication/loader.facade.ts
index d0ce9cb..3512968 100644
--- a/libs/ui/src/lib/util/loaders/aplication/loader.facade.ts
+++ b/libs/ui/src/lib/util/loaders/aplication/loader.facade.ts
@@ -1,32 +1,20 @@
// Angular
-import { Injectable, inject } from '@angular/core';
+import { Injectable } from '@angular/core';
// RxJS
-import { Observable, Subscribable } from 'rxjs';
+import { BehaviorSubject, Observable } from 'rxjs';
-// NgRx
-import { Store, select } from '@ngrx/store';
-
-// Store
-import * as LoaderSelectors from '../state/loader.selectors';
-import * as LoaderActions from '../state/loader.actions';
-
-@Injectable()
+@Injectable({ providedIn: 'root' })
export class LoaderFacade {
- // `inject()` en vez de parámetro de constructor: los inicializadores de
- // propiedad de abajo se evalúan antes que los parámetros del constructor.
- private readonly store = inject(Store);
+ private readonly loaderSubject = new BehaviorSubject(false);
- loader$: Observable = this.store.pipe(
- select(LoaderSelectors.getLoader),
- );
- showLoader$?: Observable | Subscribable | Promise;
+ loader$: Observable = this.loaderSubject.asObservable();
- showLoader() {
- this.store.dispatch(LoaderActions.showLoader());
+ showLoader(): void {
+ this.loaderSubject.next(true);
}
- hideLoader() {
- this.store.dispatch(LoaderActions.hideLoader());
+ hideLoader(): void {
+ this.loaderSubject.next(false);
}
}
diff --git a/libs/ui/src/lib/util/loaders/index.ts b/libs/ui/src/lib/util/loaders/index.ts
index 7010bc7..bd5cdb8 100644
--- a/libs/ui/src/lib/util/loaders/index.ts
+++ b/libs/ui/src/lib/util/loaders/index.ts
@@ -1,10 +1,2 @@
-// Module
-export * from './util-loader.module';
-
// Facade
export * from './aplication/loader.facade';
-
-// State
-export * from './state/loader.actions';
-export * from './state/loader.reducer';
-export * from './state/loader.selectors';
diff --git a/libs/ui/src/lib/util/loaders/state/loader.actions.ts b/libs/ui/src/lib/util/loaders/state/loader.actions.ts
deleted file mode 100644
index 8d1166b..0000000
--- a/libs/ui/src/lib/util/loaders/state/loader.actions.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-// NgRx
-import { createAction } from '@ngrx/store';
-
-export const showLoader = createAction('[Loader] Show Loader');
-
-export const hideLoader = createAction('[Loader] Hide Loader');
diff --git a/libs/ui/src/lib/util/loaders/state/loader.reducer.ts b/libs/ui/src/lib/util/loaders/state/loader.reducer.ts
deleted file mode 100644
index b3a66d7..0000000
--- a/libs/ui/src/lib/util/loaders/state/loader.reducer.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-// NgRx
-import { createReducer, on, Action } from '@ngrx/store';
-
-// Store
-import * as LoaderActions from './loader.actions';
-
-export const loadersKey = 'CORE_loaderState';
-
-export interface LoaderState {
- loader: boolean;
-}
-
-export const initialLoaderState: LoaderState = {
- loader: false,
-};
-
-export const loaderReducer = createReducer(
- initialLoaderState,
-
- on(LoaderActions.showLoader, (state) => {
- return {
- ...state,
- loader: true,
- };
- }),
-
- on(LoaderActions.hideLoader, (state) => {
- return {
- ...state,
- loader: false,
- };
- }),
-);
-
-export function loaderReducerFunction(
- state: LoaderState | undefined,
- action: Action,
-) {
- return loaderReducer(state, action);
-}
diff --git a/libs/ui/src/lib/util/loaders/state/loader.selectors.ts b/libs/ui/src/lib/util/loaders/state/loader.selectors.ts
deleted file mode 100644
index 77cf756..0000000
--- a/libs/ui/src/lib/util/loaders/state/loader.selectors.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-// NgRx
-import { createFeatureSelector, createSelector } from '@ngrx/store';
-
-// Store
-import { loadersKey, LoaderState } from './loader.reducer';
-
-export const selectLoaderState = createFeatureSelector(loadersKey);
-
-export const getLoader = createSelector(selectLoaderState, (state) => {
- return state.loader;
-});
diff --git a/libs/ui/src/lib/util/loaders/util-loader.module.ts b/libs/ui/src/lib/util/loaders/util-loader.module.ts
deleted file mode 100644
index 28108d9..0000000
--- a/libs/ui/src/lib/util/loaders/util-loader.module.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { StoreModule } from '@ngrx/store';
-
-import * as fromReducer from './state/loader.reducer';
-
-@NgModule({
- imports: [
- CommonModule,
- StoreModule.forFeature(
- fromReducer.loadersKey,
- fromReducer.loaderReducerFunction,
- ),
- ],
-})
-export class PiSharedUtilLoadersModule {}
diff --git a/libs/ui/src/lib/util/router-store/custom-route-serializer.ts b/libs/ui/src/lib/util/router-store/custom-route-serializer.ts
deleted file mode 100644
index 88d9a82..0000000
--- a/libs/ui/src/lib/util/router-store/custom-route-serializer.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-// Angular
-import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
-
-// Router
-import { RouterStateUrl } from './state/router.reducer';
-
-// Ngrx
-import * as fromRouter from '@ngrx/router-store';
-
-export class CustomSerializer implements fromRouter.RouterStateSerializer {
- serialize(routerState: RouterStateSnapshot): RouterStateUrl {
- const { url } = routerState;
- const { queryParams } = routerState.root;
-
- let route: ActivatedRouteSnapshot = routerState.root;
-
- while (route.firstChild) {
- route = route.firstChild;
- }
- const { params, routeConfig } = route;
-
- // Only return an object including the URL, params and query params
- // instead of the entire snapshot
- return {
- url,
- params,
- queryParams,
- title: typeof routeConfig?.title === 'string' ? routeConfig.title : '',
- };
- }
-}
diff --git a/libs/ui/src/lib/util/router-store/index.ts b/libs/ui/src/lib/util/router-store/index.ts
index b07e710..16dc88a 100644
--- a/libs/ui/src/lib/util/router-store/index.ts
+++ b/libs/ui/src/lib/util/router-store/index.ts
@@ -1,10 +1,5 @@
-export * from './util-router-store.module';
-
-// State
-export * from './state/router.actions';
-export * from './state/router.effects';
-export * from './state/router.reducer';
-export * from './state/router.selectors';
+// Facade
+export * from './router.facade';
// Error handler
export * from './error-handler';
diff --git a/libs/ui/src/lib/util/router-store/router.facade.ts b/libs/ui/src/lib/util/router-store/router.facade.ts
new file mode 100644
index 0000000..dfa3c23
--- /dev/null
+++ b/libs/ui/src/lib/util/router-store/router.facade.ts
@@ -0,0 +1,55 @@
+// Angular
+import { Injectable, inject } from '@angular/core';
+import { Location } from '@angular/common';
+import { Title } from '@angular/platform-browser';
+import { NavigationEnd, NavigationExtras, Router } from '@angular/router';
+
+// RxJS
+import { Observable } from 'rxjs';
+import { filter, map, startWith } from 'rxjs/operators';
+
+export interface RouterNavigationPayload {
+ path: any[];
+ query?: object;
+ extras?: NavigationExtras;
+}
+
+@Injectable({ providedIn: 'root' })
+export class RouterFacade {
+ private readonly router = inject(Router);
+ private readonly location = inject(Location);
+ private readonly title = inject(Title);
+
+ private readonly navigationEnd$: Observable =
+ this.router.events.pipe(
+ filter((event): event is NavigationEnd => event instanceof NavigationEnd),
+ );
+
+ readonly route$: Observable = this.navigationEnd$.pipe(
+ map(() => this.router.url),
+ startWith(this.router.url),
+ );
+
+ readonly title$: Observable = this.navigationEnd$.pipe(
+ map(() => this.title.getTitle()),
+ startWith(this.title.getTitle()),
+ );
+
+ navigateTo({ path, query, extras }: RouterNavigationPayload): void {
+ this.router.navigate(path, { queryParams: query, ...extras });
+ }
+
+ navigateRefreshTo({ path, query, extras }: RouterNavigationPayload): void {
+ this.router.routeReuseStrategy.shouldReuseRoute = () => false;
+ this.router.onSameUrlNavigation = 'reload';
+ this.router.navigate(path, { queryParams: query, ...extras });
+ }
+
+ navigateBack(): void {
+ this.location.back();
+ }
+
+ navigateForward(): void {
+ this.location.forward();
+ }
+}
diff --git a/libs/ui/src/lib/util/router-store/state/router.actions.ts b/libs/ui/src/lib/util/router-store/state/router.actions.ts
deleted file mode 100644
index cecfe2e..0000000
--- a/libs/ui/src/lib/util/router-store/state/router.actions.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { NavigationExtras } from '@angular/router';
-
-import { createAction, props } from '@ngrx/store';
-
-export const navigateTo = createAction(
- '[Core Router] Navigate To',
- props<{
- payload: {
- path: any[];
- query?: object;
- extras?: NavigationExtras;
- };
- }>(),
-);
-
-export const navigateRefreshTo = createAction(
- '[Core Router] Navigate Refresh To',
- props<{
- payload: {
- path: any[];
- query?: object;
- extras?: NavigationExtras;
- };
- }>(),
-);
-
-export const navigateBack = createAction('[Core Router] Navigate Back');
-
-export const navigateForward = createAction('[Core Router] Navigate Forward');
diff --git a/libs/ui/src/lib/util/router-store/state/router.effects.ts b/libs/ui/src/lib/util/router-store/state/router.effects.ts
deleted file mode 100644
index bda0c32..0000000
--- a/libs/ui/src/lib/util/router-store/state/router.effects.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import { Actions, createEffect, ofType } from '@ngrx/effects';
-
-import * as RouterActions from './router.actions';
-
-import { map, tap } from 'rxjs/operators';
-
-import { Injectable } from '@angular/core';
-import { Location } from '@angular/common';
-import { Router } from '@angular/router';
-
-@Injectable()
-export class RouterEffects {
- navigateTo$ = createEffect(
- () => {
- return this.action$.pipe(
- ofType(RouterActions.navigateTo),
- map((action) => {
- return action.payload;
- }),
- tap(({ path, query: queryParams, extras }) => {
- this._router.navigate(path, { queryParams, ...extras });
- }),
- );
- },
- { dispatch: false },
- );
-
- navigateRefreshTo$ = createEffect(
- () => {
- return this.action$.pipe(
- ofType(RouterActions.navigateRefreshTo),
- map((action) => {
- return action.payload;
- }),
- tap(({ path, query: queryParams, extras }) => {
- this._router.routeReuseStrategy.shouldReuseRoute = () => false;
- this._router.onSameUrlNavigation = 'reload';
- this._router.navigate(path, { queryParams, ...extras });
- }),
- );
- },
- { dispatch: false },
- );
-
- navigateBack$ = createEffect(
- () => {
- return this.action$.pipe(
- ofType(RouterActions.navigateBack),
- tap(() => {
- this._location.back();
- }),
- );
- },
- { dispatch: false },
- );
-
- navigateForward$ = createEffect(
- () => {
- return this.action$.pipe(
- ofType(RouterActions.navigateForward),
- tap(() => {
- this._location.forward();
- }),
- );
- },
- { dispatch: false },
- );
-
- constructor(
- private _location: Location,
- private action$: Actions,
- private _router: Router,
- ) {}
-}
diff --git a/libs/ui/src/lib/util/router-store/state/router.reducer.ts b/libs/ui/src/lib/util/router-store/state/router.reducer.ts
deleted file mode 100644
index 98b8d03..0000000
--- a/libs/ui/src/lib/util/router-store/state/router.reducer.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import * as fromRouter from '@ngrx/router-store';
-
-import { Params } from '@angular/router';
-
-export const coreRouterFeatureKey = 'CORE_router';
-
-export interface RouterStateUrl {
- url: string;
- queryParams: Params;
- params: Params;
- title: string;
-}
-
-export interface RouterState {
- routerReducer: fromRouter.RouterReducerState;
-}
diff --git a/libs/ui/src/lib/util/router-store/state/router.selectors.ts b/libs/ui/src/lib/util/router-store/state/router.selectors.ts
deleted file mode 100644
index 4aec2a3..0000000
--- a/libs/ui/src/lib/util/router-store/state/router.selectors.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { createFeatureSelector, createSelector } from '@ngrx/store';
-
-import * as fromRouter from '@ngrx/router-store';
-
-import * as fromReducer from './router.reducer';
-
-export const getRouterState = createFeatureSelector<
- fromRouter.RouterReducerState
->(fromReducer.coreRouterFeatureKey);
-
-export const getTitle = createSelector(getRouterState, (state) => {
- return state?.state?.title;
-});
-
-export const getRoute = createSelector(
- getRouterState,
- (state) => state?.state?.url,
-);
diff --git a/libs/ui/src/lib/util/router-store/util-router-store.module.ts b/libs/ui/src/lib/util/router-store/util-router-store.module.ts
deleted file mode 100644
index 262269d..0000000
--- a/libs/ui/src/lib/util/router-store/util-router-store.module.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { ErrorHandler, NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
-
-import { RouterState, StoreRouterConnectingModule } from '@ngrx/router-store';
-import { CustomSerializer } from './custom-route-serializer';
-
-@NgModule({
- imports: [
- CommonModule,
- StoreRouterConnectingModule.forRoot({
- routerState: RouterState.Minimal,
- serializer: CustomSerializer,
- }),
- ],
-})
-export class PiSharedUtilRouterStoreModule {}
diff --git a/libs/ui/tsconfig.lib.publish.json b/libs/ui/tsconfig.lib.publish.json
new file mode 100644
index 0000000..07fd5c0
--- /dev/null
+++ b/libs/ui/tsconfig.lib.publish.json
@@ -0,0 +1,6 @@
+{
+ "extends": "./tsconfig.lib.prod.json",
+ "angularCompilerOptions": {
+ "compilationMode": "partial"
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index 5aab161..7914312 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,9 +23,6 @@
"@angular/router": "22.0.6",
"@angular/ssr": "22.0.6",
"@apollo/client": "^4.2.8",
- "@ngrx/effects": "^21.1.1",
- "@ngrx/router-store": "^21.1.1",
- "@ngrx/store": "^21.1.1",
"@ngx-translate/core": "^18.0.0",
"@ngx-translate/http-loader": "^18.0.0",
"apollo-angular": "^14.1.0",
@@ -2698,6 +2695,7 @@
"version": "22.0.6",
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-22.0.6.tgz",
"integrity": "sha512-HPiceCIEyIcTmOuIFiyqu04kbLoJhSUavcOy/kzJkJ2OPXtPvGKaimC4lgs0C86uFy4P6rj6PDITBe3AVHkMTA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/core": "7.29.7",
@@ -2730,6 +2728,7 @@
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.29.7",
@@ -2760,12 +2759,14 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -2775,6 +2776,7 @@
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
"integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.29.7",
@@ -4969,7 +4971,7 @@
"version": "2.13.0",
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.13.0.tgz",
"integrity": "sha512-acq7c49vxfm1ggJ95P70TX7ABDM0vxr1SYD3BB0o0jnBLB4OAqeHyKuN+cD3w80gXEDQ2zxHpR6CUeA+O/aU9g==",
- "devOptional": true,
+ "dev": true,
"license": "(Apache-2.0 AND BSD-3-Clause)"
},
"node_modules/@colordx/core": {
@@ -5165,6 +5167,7 @@
"cpu": [
"ppc64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5181,6 +5184,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5197,6 +5201,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5213,6 +5218,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5229,6 +5235,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5245,6 +5252,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5261,6 +5269,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5277,6 +5286,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5293,6 +5303,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5309,6 +5320,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5325,6 +5337,7 @@
"cpu": [
"ia32"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5341,6 +5354,7 @@
"cpu": [
"loong64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5357,6 +5371,7 @@
"cpu": [
"mips64el"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5373,6 +5388,7 @@
"cpu": [
"ppc64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5389,6 +5405,7 @@
"cpu": [
"riscv64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5405,6 +5422,7 @@
"cpu": [
"s390x"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5421,6 +5439,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5437,6 +5456,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5453,6 +5473,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5469,6 +5490,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5485,6 +5507,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5501,6 +5524,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5517,6 +5541,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5533,6 +5558,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5549,6 +5575,7 @@
"cpu": [
"ia32"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5565,6 +5592,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -9134,49 +9162,6 @@
"@tybys/wasm-util": "^0.9.0"
}
},
- "node_modules/@ngrx/effects": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-21.1.1.tgz",
- "integrity": "sha512-JQUv2tYyjkbdsE40WYtJDsiI+3qtn4V5m0Ybr2CYw9zKpVQshDV5MLLAni+SuijlHAWJkw8x4wwnYmNyG/Rmng==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.0.0"
- },
- "peerDependencies": {
- "@angular/core": "^21.0.0",
- "@ngrx/store": "21.1.1",
- "rxjs": "^6.5.3 || ^7.5.0"
- }
- },
- "node_modules/@ngrx/router-store": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/@ngrx/router-store/-/router-store-21.1.1.tgz",
- "integrity": "sha512-niO2J0aYgMcplHRgg3Nf1DPjHpg4ehZjqnA282DtLOXlYtHWoP2ePZcvnPxhK1K77Am5hSGOLa7U2DjjgwlSdQ==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.0.0"
- },
- "peerDependencies": {
- "@angular/common": "^21.0.0",
- "@angular/core": "^21.0.0",
- "@angular/router": "^21.0.0",
- "@ngrx/store": "21.1.1",
- "rxjs": "^6.5.3 || ^7.5.0"
- }
- },
- "node_modules/@ngrx/store": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-21.1.1.tgz",
- "integrity": "sha512-B0/IyBTqBExmwPAmrpcJrhlrn+FeeitvOW8ltf7O3kJb2vRKduV5Oi34GLiwYQ8eAukTbKyWQv/VV02gxLeEMg==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.0.0"
- },
- "peerDependencies": {
- "@angular/core": "^21.0.0",
- "rxjs": "^6.5.3 || ^7.5.0"
- }
- },
"node_modules/@ngtools/webpack": {
"version": "22.0.6",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-22.0.6.tgz",
@@ -10318,18 +10303,6 @@
}
}
},
- "node_modules/@nx/module-federation/node_modules/@swc/helpers": {
- "version": "0.5.23",
- "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz",
- "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==",
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "peer": true,
- "dependencies": {
- "tslib": "^2.8.0"
- }
- },
"node_modules/@nx/module-federation/node_modules/eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@@ -11381,43 +11354,6 @@
"node": ">=14.0.0"
}
},
- "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/core": {
- "version": "1.11.3",
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz",
- "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@emnapi/wasi-threads": "1.2.3",
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/runtime": {
- "version": "1.11.3",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz",
- "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz",
- "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
"node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
@@ -12371,6 +12307,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -12387,6 +12324,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -12403,6 +12341,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -12419,6 +12358,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -12435,6 +12375,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -12451,6 +12392,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -12467,6 +12409,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -12483,6 +12426,7 @@
"cpu": [
"ppc64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -12499,6 +12443,7 @@
"cpu": [
"s390x"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -12515,6 +12460,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -12531,6 +12477,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -12547,6 +12494,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -12563,6 +12511,7 @@
"cpu": [
"wasm32"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -12578,6 +12527,7 @@
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz",
"integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==",
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -12589,6 +12539,7 @@
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz",
"integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==",
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -12599,6 +12550,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -12609,6 +12561,7 @@
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
"integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==",
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -12627,6 +12580,7 @@
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
"integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==",
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -12640,6 +12594,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -12656,6 +12611,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -12669,13 +12625,14 @@
"version": "1.0.0-rc.16",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.16.tgz",
"integrity": "sha512-45+YtqxLYKDWQouLKCrpIZhke+nXxhsw+qAHVzHDVwttyBlHNBVs2K25rDXrZzhpTp9w1FlAlvweV1H++fdZoA==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@rollup/plugin-json": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
"integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.1.0"
@@ -12696,7 +12653,7 @@
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz",
"integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0",
@@ -12719,7 +12676,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
@@ -13051,7 +13008,7 @@
"version": "4.62.3",
"resolved": "https://registry.npmjs.org/@rollup/wasm-node/-/wasm-node-4.62.3.tgz",
"integrity": "sha512-sEOQWamme1YgXeVcE1JkB1k44RwtPWcpYpGUW1Lso7bOj+Ay9lN2Pak/CcfNeC4xqIPNhYSabQNaWYkJJR7m8Q==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.9"
@@ -13925,7 +13882,7 @@
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
"integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/deep-eql": "*",
@@ -13962,7 +13919,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
"integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/@types/eslint": {
@@ -14976,7 +14933,7 @@
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz",
"integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.1.0",
@@ -14994,7 +14951,7 @@
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz",
"integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "4.1.10",
@@ -15021,7 +14978,7 @@
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz",
"integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"tinyrainbow": "^3.1.0"
@@ -15034,7 +14991,7 @@
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz",
"integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "4.1.10",
@@ -15048,7 +15005,7 @@
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz",
"integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.1.10",
@@ -15064,7 +15021,7 @@
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz",
"integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"funding": {
"url": "https://opencollective.com/vitest"
@@ -15096,7 +15053,7 @@
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz",
"integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.1.10",
@@ -15111,7 +15068,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/ast": {
@@ -15711,7 +15668,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -16543,7 +16500,7 @@
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
"integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
@@ -16599,6 +16556,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
"integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^5.0.0"
@@ -16887,7 +16845,7 @@
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz",
"integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/columnify": {
@@ -16931,7 +16889,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz",
"integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==",
- "devOptional": true,
+ "dev": true,
"license": "ISC"
},
"node_modules/component-emitter": {
@@ -17625,7 +17583,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz",
"integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -17645,6 +17603,7 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "devOptional": true,
"license": "Apache-2.0",
"engines": {
"node": ">=8"
@@ -18031,7 +17990,7 @@
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
"integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
- "devOptional": true,
+ "dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
@@ -18498,7 +18457,7 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0"
@@ -18623,7 +18582,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
"integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==",
- "devOptional": true,
+ "dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=12.0.0"
@@ -18993,7 +18952,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/find-cache-directory/-/find-cache-directory-6.0.0.tgz",
"integrity": "sha512-CvFd5ivA6HcSHbD+59P7CyzINHXzwhuQK8RY7CxJZtgDSAtRlHiCaQpZQ2lMR/WRyUIEmzUvL6G2AGurMfegZA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"common-path-prefix": "^3.0.0",
@@ -19010,7 +18969,7 @@
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-8.0.0.tgz",
"integrity": "sha512-4peoBq4Wks0riS0z8741NVv+/8IiTvqnZAr8QGgtdifrtpdXbNw/FxRS1l6NFqm4EMzuS0EDqNNx4XGaz8cuyQ==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"find-up-simple": "^1.0.0"
@@ -19040,7 +18999,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz",
"integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
@@ -19459,6 +19418,7 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
@@ -20283,7 +20243,7 @@
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/injection-js/-/injection-js-2.6.1.tgz",
"integrity": "sha512-dbR5bdhi7TWDoCye9cByZqeg/gAfamm8Vu3G1KZOTYkOif8WkuM8CD0oeDPtZYMzT5YH76JAFB7bkmyY9OJi2A==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
@@ -23559,7 +23519,7 @@
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
"integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"bin": {
"jiti": "lib/jiti-cli.mjs"
@@ -24028,6 +23988,7 @@
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
"integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
+ "dev": true,
"license": "MPL-2.0",
"dependencies": {
"detect-libc": "^2.0.3"
@@ -24060,6 +24021,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -24080,6 +24042,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -24100,6 +24063,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -24120,6 +24084,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -24140,6 +24105,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -24160,6 +24126,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -24180,6 +24147,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -24200,6 +24168,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -24220,6 +24189,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -24240,6 +24210,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -24260,6 +24231,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -25265,7 +25237,7 @@
"version": "22.0.2",
"resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-22.0.2.tgz",
"integrity": "sha512-8qQ+RWm0W3gK8Sch1kmo3EzIUiCTXIOP2gS7b+vtqCMBf4KBjBP0wFBikW5qRau9lGOzRTML0GPFGoARPR4+fw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.3.0",
@@ -25756,7 +25728,7 @@
"version": "14.0.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
"integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=20"
@@ -25766,7 +25738,7 @@
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
"integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
- "devOptional": true,
+ "dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
@@ -26517,7 +26489,7 @@
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz",
"integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==",
- "devOptional": true,
+ "dev": true,
"funding": [
"https://github.com/sponsors/sxzz",
"https://opencollective.com/debug"
@@ -27201,7 +27173,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/picocolors": {
@@ -28325,6 +28297,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
"integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 20.19.0"
@@ -28578,6 +28551,7 @@
"version": "1.0.0-rc.16",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.16.tgz",
"integrity": "sha512-rzi5WqKzEZw3SooTt7cgm4eqIoujPIyGcJNGFL7iPEuajQw7vxMHUkXylu4/vhCkJGXsgRmxqMKXUpT6FEgl0g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@oxc-project/types": "=0.126.0",
@@ -28611,6 +28585,7 @@
"version": "0.126.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.126.0.tgz",
"integrity": "sha512-oGfVtjAgwQVVpfBrbtk4e1XDyWHRFta6BS3GWVzrF8xYBT2VGQAk39yJS/wFSMrZqoiCU4oghT3Ch0HaHGIHcQ==",
+ "dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/Boshen"
@@ -28664,7 +28639,7 @@
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-6.4.1.tgz",
"integrity": "sha512-l//F3Zf7ID5GoOfLfD8kroBjQKEKpy1qfhtAdnpibFZMffPaylrg1CoDC2vGkPeTeyxUe4bVFCln2EFuL7IGGg==",
- "devOptional": true,
+ "dev": true,
"license": "LGPL-3.0-only",
"dependencies": {
"@jridgewell/remapping": "^2.3.5",
@@ -28690,7 +28665,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/rollup/node_modules/@types/estree": {
@@ -28829,7 +28804,7 @@
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.100.0.tgz",
"integrity": "sha512-Ut8wlQSk19tm7jMK6mz6cF1+e+E7tUnW2tM02zQDPnOTcVbV8qCQG8UWxZkkNlY50+hV3hqP24OOkUlMz8xBpw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"@bufbuild/protobuf": "^2.5.0",
@@ -28877,6 +28852,7 @@
"!riscv64",
"!x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -28887,6 +28863,7 @@
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.100.0.tgz",
"integrity": "sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==",
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -28911,6 +28888,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -28927,6 +28905,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -28943,6 +28922,7 @@
"cpu": [
"riscv64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -28959,6 +28939,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -28975,6 +28956,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -28991,6 +28973,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -29007,6 +28990,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -29023,6 +29007,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -29039,6 +29024,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -29055,6 +29041,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -29071,6 +29058,7 @@
"cpu": [
"riscv64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -29087,6 +29075,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -29103,6 +29092,7 @@
"cpu": [
"riscv64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -29119,6 +29109,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -29132,6 +29123,7 @@
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-unknown-all/-/sass-embedded-unknown-all-1.100.0.tgz",
"integrity": "sha512-c+naBgWId4MIpToXcI0DgqetjdAkwTTAxFAuOaBz7HUXLdyG1oZRrEvSsbe41nEdQOKH0vgofVFCeSQgoXOG9A==",
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -29148,6 +29140,7 @@
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.100.0.tgz",
"integrity": "sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==",
+ "dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -29172,6 +29165,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -29188,6 +29182,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -29201,7 +29196,7 @@
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
@@ -29608,7 +29603,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
"integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
- "devOptional": true,
+ "dev": true,
"license": "ISC"
},
"node_modules/signal-exit": {
@@ -29955,7 +29950,7 @@
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
"integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/statuses": {
@@ -29971,7 +29966,7 @@
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz",
"integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/stdin-discarder": {
@@ -30324,7 +30319,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz",
"integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"sync-message-port": "^1.0.0"
@@ -30337,7 +30332,7 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.2.0.tgz",
"integrity": "sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=16.0.0"
@@ -30659,14 +30654,14 @@
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
"integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/tinyexec": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz",
"integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
@@ -30692,7 +30687,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
"integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=14.0.0"
@@ -31119,6 +31114,7 @@
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
+ "dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@@ -31406,7 +31402,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
"integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/vary": {
@@ -31422,6 +31418,7 @@
"version": "8.0.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.9.tgz",
"integrity": "sha512-t7g7GVRpMXjNpa67HaVWI/8BWtdVIQPCL2WoozXXA7LBGEFK4AkkKkHx2hAQf5x1GZSlcmEDPkVLSGahxnEEZw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"lightningcss": "^1.32.0",
@@ -31537,27 +31534,11 @@
}
}
},
- "node_modules/vite-tsconfig-paths/node_modules/typescript": {
- "version": "5.9.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
- "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "peer": true,
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
"node_modules/vitest": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz",
"integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"@vitest/expect": "4.1.10",
@@ -32295,7 +32276,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
"integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"siginfo": "^2.0.0",
@@ -32528,6 +32509,7 @@
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
"license": "ISC",
"engines": {
"node": ">=10"
@@ -32543,7 +32525,7 @@
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
"integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
- "devOptional": true,
+ "dev": true,
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
@@ -32559,6 +32541,7 @@
"version": "18.0.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
"integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"cliui": "^9.0.1",
@@ -32586,6 +32569,7 @@
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -32598,6 +32582,7 @@
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -32610,6 +32595,7 @@
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
"integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^7.2.0",
@@ -32624,12 +32610,14 @@
"version": "10.6.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
"integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
"license": "MIT"
},
"node_modules/yargs/node_modules/string-width": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^10.3.0",
@@ -32647,6 +32635,7 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
"integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^6.2.2"
@@ -32662,6 +32651,7 @@
"version": "9.0.2",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
"integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^6.2.1",
@@ -32679,6 +32669,7 @@
"version": "22.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
"integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+ "dev": true,
"license": "ISC",
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=23"
diff --git a/package.json b/package.json
index 76c93a8..bce978b 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,9 @@
"build": "nx build ui",
"build:playground": "nx build playground",
"build:all": "nx run-many -t build",
+ "package:ui": "nx run ui:package",
+ "pack:ui": "nx run ui:package && npm pack ./dist/libs/ui --pack-destination ./dist",
+ "publish:ui": "nx run ui:package && npm publish ./dist/libs/ui",
"lint": "nx lint ui",
"format": "prettier --write \"{apps,libs}/**/*.{ts,html,scss,md}\""
},
@@ -27,9 +30,6 @@
"@angular/router": "22.0.6",
"@angular/ssr": "22.0.6",
"@apollo/client": "^4.2.8",
- "@ngrx/effects": "^21.1.1",
- "@ngrx/router-store": "^21.1.1",
- "@ngrx/store": "^21.1.1",
"@ngx-translate/core": "^18.0.0",
"@ngx-translate/http-loader": "^18.0.0",
"apollo-angular": "^14.1.0",