ps-helix 6.1.0 → 6.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/fesm2022/ps-helix.mjs +81 -31
- package/fesm2022/ps-helix.mjs.map +1 -1
- package/package.json +1 -1
- package/types/ps-helix.d.ts +40 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A comprehensive Angular component library built with Angular 22+ featuring modern design patterns, accessibility-first development, and optimal developer experience.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/ps-helix)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
[](https://angular.dev/)
|
|
8
8
|
[](https://www.typescriptlang.org/)
|
|
@@ -106,7 +106,7 @@ After installation, verify that ps-helix is in your `package.json`:
|
|
|
106
106
|
```json
|
|
107
107
|
{
|
|
108
108
|
"dependencies": {
|
|
109
|
-
"ps-helix": "^6.1
|
|
109
|
+
"ps-helix": "^6.2.1"
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
```
|
|
@@ -1182,7 +1182,7 @@ Copyright (c) 2025 PACK Solutions
|
|
|
1182
1182
|
|
|
1183
1183
|
---
|
|
1184
1184
|
|
|
1185
|
-
**Version**: 6.1
|
|
1185
|
+
**Version**: 6.2.1
|
|
1186
1186
|
**Built with**: Angular 22.0.0, TypeScript 6.0.0, Phosphor Icons 2.0.3
|
|
1187
1187
|
**Author**: Fabrice PEREZ | Design Engineer at PACK Solutions
|
|
1188
1188
|
**Last Updated**: January 2026
|
package/fesm2022/ps-helix.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, output, computed, ChangeDetectionStrategy, Component, model, inject, ElementRef, signal, PLATFORM_ID, ViewEncapsulation, InjectionToken, viewChild, effect, Injectable, DestroyRef, Directive, ViewContainerRef, ChangeDetectorRef, ViewChild, Injector, afterNextRender, HostListener, Renderer2, contentChild, linkedSignal, EventEmitter, Output, Input, contentChildren } from '@angular/core';
|
|
2
|
+
import { input, output, computed, ChangeDetectionStrategy, Component, model, inject, ElementRef, signal, PLATFORM_ID, ViewEncapsulation, InjectionToken, viewChild, effect, Injectable, DestroyRef, Directive, ViewContainerRef, ChangeDetectorRef, ViewChild, Injector, afterNextRender, HostListener, Renderer2, contentChild, linkedSignal, isDevMode, EventEmitter, Output, Input, contentChildren } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
4
|
import { CommonModule, DOCUMENT, isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
|
|
5
5
|
import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
|
|
@@ -1134,6 +1134,31 @@ class PshInfoCardComponent {
|
|
|
1134
1134
|
return false;
|
|
1135
1135
|
return val.toString().trim().length > 0;
|
|
1136
1136
|
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Calcule les classes CSS de la valeur d'une ligne à partir de son emphasis.
|
|
1139
|
+
* Fonction pure (compatible OnPush) : aucun effet de bord.
|
|
1140
|
+
* Un emphasis explicite prime sur l'auto-muted des valeurs nullish.
|
|
1141
|
+
*/
|
|
1142
|
+
getValueClasses(item) {
|
|
1143
|
+
const classes = ['info-card-value'];
|
|
1144
|
+
const emphasis = item.emphasis;
|
|
1145
|
+
const isEmpty = item.value == null;
|
|
1146
|
+
const autoMuted = isEmpty && this.options().mutedEmptyValues !== false && !emphasis;
|
|
1147
|
+
if (autoMuted) {
|
|
1148
|
+
classes.push('info-card-value--italic', 'info-card-value--tone-muted');
|
|
1149
|
+
}
|
|
1150
|
+
else if (emphasis) {
|
|
1151
|
+
if (emphasis.italic)
|
|
1152
|
+
classes.push('info-card-value--italic');
|
|
1153
|
+
if (emphasis.bold)
|
|
1154
|
+
classes.push('info-card-value--bold');
|
|
1155
|
+
if (emphasis.strikethrough)
|
|
1156
|
+
classes.push('info-card-value--strikethrough');
|
|
1157
|
+
if (emphasis.tone)
|
|
1158
|
+
classes.push(`info-card-value--tone-${emphasis.tone}`);
|
|
1159
|
+
}
|
|
1160
|
+
return classes.join(' ');
|
|
1161
|
+
}
|
|
1137
1162
|
copyRowValue(item, index) {
|
|
1138
1163
|
if (!isPlatformBrowser(this.platformId)) {
|
|
1139
1164
|
this.copyFailed.emit(item);
|
|
@@ -1159,13 +1184,13 @@ class PshInfoCardComponent {
|
|
|
1159
1184
|
});
|
|
1160
1185
|
}
|
|
1161
1186
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PshInfoCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1162
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: PshInfoCardComponent, isStandalone: true, selector: "psh-info-card", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, cssClass: { classPropertyName: "cssClass", publicName: "cssClass", isSignal: true, isRequired: false, transformFunction: null }, customStyle: { classPropertyName: "customStyle", publicName: "customStyle", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, hoverable: { classPropertyName: "hoverable", publicName: "hoverable", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, copyable: { classPropertyName: "copyable", publicName: "copyable", isSignal: true, isRequired: false, transformFunction: null }, copyButtonLabel: { classPropertyName: "copyButtonLabel", publicName: "copyButtonLabel", isSignal: true, isRequired: false, transformFunction: null }, copyFeedbackText: { classPropertyName: "copyFeedbackText", publicName: "copyFeedbackText", isSignal: true, isRequired: false, transformFunction: null }, autoFullWidthOnMobile: { classPropertyName: "autoFullWidthOnMobile", publicName: "autoFullWidthOnMobile", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked", copied: "copied", copyFailed: "copyFailed" }, host: { styleAttribute: "display: block; height: 100%;" }, ngImport: i0, template: "<div\r\n [class]=\"computedClasses()\"\r\n [ngStyle]=\"computedStyles()\"\r\n (click)=\"handleClick($event)\"\r\n (keydown)=\"handleKeydown($event)\"\r\n role=\"region\"\r\n [attr.tabindex]=\"interactive() && !disabled() ? 0 : null\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\r\n [attr.aria-busy]=\"loading() ? 'true' : null\"\r\n>\r\n @if (title() || hasHeaderActions()) {\r\n <div class=\"info-card-header\">\r\n @if (title()) {\r\n <h3 class=\"info-card-title\">\r\n @if (icon()) {\r\n <i\r\n class=\"ph {{ titleIcon() }}\"\r\n aria-hidden=\"true\"\r\n [attr.data-icon]=\"icon()\"\r\n ></i>\r\n }\r\n {{ title() }}\r\n </h3>\r\n }\r\n <div class=\"info-card-header-actions\">\r\n <ng-content select=\"[card-header-actions]\"></ng-content>\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"info-card-body\">\r\n @if (loading()) {\r\n <div class=\"info-card-loading\">\r\n <div class=\"skeleton-line\"></div>\r\n <div class=\"skeleton-line\"></div>\r\n <div class=\"skeleton-line short\"></div>\r\n </div>\r\n } @else {\r\n <div\r\n class=\"info-card-content\"\r\n role=\"list\"\r\n >\r\n @if (shouldShowEmptyState()) {\r\n <div\r\n class=\"info-card-empty-state\"\r\n role=\"status\"\r\n aria-live=\"polite\"\r\n [attr.data-testid]=\"'info-card-empty-state'\"\r\n >\r\n {{ getEmptyStateMessage() }}\r\n </div>\r\n } @else {\r\n @for (item of data(); track $index) {\r\n <div\r\n class=\"info-card-row\"\r\n role=\"listitem\"\r\n [attr.data-index]=\"$index\"\r\n [class]=\"item.customClass || ''\"\r\n >\r\n <div\r\n class=\"info-card-label\"\r\n [style.width]=\"item.labelWidth || options().labelWidth\"\r\n [attr.data-label]=\"item.label\"\r\n >\r\n {{ item.label }}\r\n </div>\r\n <div\r\n class=\"info-card-value-group\"\r\n [style.width]=\"item.valueWidth || options().valueWidth\"\r\n >\r\n <span\r\n class=\"info-card-value\"\r\n [attr.data-value]=\"item.value\"\r\n [title]=\"item.value?.toString() || ''\"\r\n >{{ item.value ?? 'Non renseign\u00E9' }}</span>\r\n @if (isRowCopyable(item) && hasNonEmptyValue(item)) {\r\n <button\r\n type=\"button\"\r\n class=\"info-card-copy-btn\"\r\n [class.copied]=\"copiedRowIndex() === $index\"\r\n [attr.aria-label]=\"copyButtonLabel() + ' ' + item.label\"\r\n (click)=\"copyRowValue(item, $index); $event.stopPropagation()\"\r\n >\r\n @if (copiedRowIndex() === $index) {\r\n <i class=\"ph ph-check\" aria-hidden=\"true\"></i>\r\n <span class=\"info-card-copy-feedback\" aria-hidden=\"true\">{{ copyFeedbackText() }}</span>\r\n } @else {\r\n <i class=\"ph ph-copy\" aria-hidden=\"true\"></i>\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n <span\r\n class=\"info-card-sr-only\"\r\n role=\"status\"\r\n aria-live=\"polite\"\r\n >\r\n @if (copiedRowIndex() !== null) {\r\n {{ copyFeedbackText() }}\r\n }\r\n </span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"info-card-actions\" [class]=\"getActionsClasses()\">\r\n <ng-content select=\"[card-actions]\"></ng-content>\r\n </div>\r\n</div>\r\n", styles: [".info-card{background:var(--surface-card);border-radius:var(--border-radius-lg, .75rem);overflow:hidden;transition:all .2s ease;width:100%;height:100%;display:flex;flex-direction:column;position:relative}.info-card.variant-elevated{box-shadow:var(--shadow-sm)}.info-card.variant-elevated.hoverable:hover{box-shadow:var(--shadow-md)}.info-card.variant-outlined{border:1px solid var(--surface-border)}.info-card.variant-default{background:var(--surface-section)}.info-card.hoverable:hover{transform:translateY(-2px)}.info-card.interactive{cursor:pointer;-webkit-user-select:none;user-select:none}.info-card.interactive:focus{outline:none;box-shadow:0 0 0 3px rgba(var(--primary-500-rgb, 59, 130, 246),.2)}.info-card.interactive:focus:not(:focus-visible){box-shadow:none}.info-card.interactive:active{transform:translateY(0)}.info-card.disabled{opacity:.6;pointer-events:none;cursor:not-allowed}.info-card.loading{pointer-events:none}.info-card-header{background:var(--surface-ground);border-bottom:var(--border-width-1) solid var(--surface-border);display:flex;align-items:center;justify-content:space-between;gap:var(--size-2)}.info-card-header-actions{display:flex;align-items:center;gap:var(--spacing-xs);padding-right:var(--size-2);flex-shrink:0}.info-card-header-actions:empty{display:none}.info-card-title{color:var(--primary-color);margin:0;font-size:var(--font-size-base);font-weight:var(--font-weight-semibold);padding:var(--size-2) var(--size-4);display:flex;align-items:center;gap:var(--size-2);transition:all var(--animation-duration-fast) var(--animation-easing-default)}.info-card-title i{font-size:var(--icon-size-md);flex-shrink:0}.info-card-body{flex:1;display:flex;flex-direction:column}.info-card-content{padding:var(--size-2);min-height:var(--size-48);display:flex;flex-direction:column;transition:opacity var(--animation-duration-normal) var(--animation-easing-default)}.info-card-row{display:flex;align-items:center;padding:var(--size-1) 0;min-height:var(--size-8);animation:fadeIn var(--animation-duration-fast) var(--animation-easing-default);transition:background-color var(--animation-duration-fast) var(--animation-easing-default)}.info-card-row:hover{background-color:var(--surface-hover);border-radius:var(--radius-base)}.info-card-row:last-child{border-bottom:none}.info-card-label{font-weight:var(--font-weight-medium);color:var(--text-color-secondary);font-size:var(--font-size-sm);padding-right:var(--size-2);position:relative;min-width:0;flex:1;line-height:var(--line-height-normal)}.info-card-label:after{content:\"\";position:absolute;right:0;top:50%;width:var(--size-1);height:var(--border-width-1);background-color:var(--surface-border)}.info-card-value-group{display:inline-flex;align-items:center;gap:var(--spacing-xs);padding-left:var(--size-2);min-width:0;flex:2}.info-card-value{font-weight:var(--font-weight-medium);color:var(--text-color);font-size:var(--font-size-sm);text-align:left;min-width:0;flex-shrink:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:var(--line-height-normal)}.info-card-empty-state{flex:1;display:flex;align-items:center;justify-content:center;text-align:center;color:var(--text-color-secondary);font-style:italic;font-size:var(--font-size-sm);padding:var(--size-6);background:var(--surface-ground);border-radius:var(--radius-lg);margin:var(--size-2);border:var(--border-width-1) dashed var(--surface-border);animation:fadeIn var(--animation-duration-normal) var(--animation-easing-default)}.info-card-actions>*{padding:var(--size-3) var(--size-4);background:var(--surface-ground);border-top:var(--border-width-1) solid var(--surface-border);display:flex;gap:var(--spacing-sm);align-items:center;justify-content:flex-end}.info-card-actions:not(:has(*)){display:none}.info-card-actions.mobile-full-width-buttons>*{display:flex;flex-direction:column;width:100%;gap:var(--spacing-sm)}.info-card-loading{display:flex;flex-direction:column;gap:var(--spacing-sm);padding:var(--size-2);animation:pulse 1.5s ease-in-out infinite}.skeleton-line{height:var(--size-4);background:linear-gradient(90deg,var(--surface-border) 25%,var(--surface-hover) 50%,var(--surface-border) 75%);background-size:200% 100%;animation:shimmer 1.5s ease-in-out infinite;border-radius:var(--spacing-xs)}.skeleton-line.short{width:60%}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.7}}@keyframes fadeIn{0%{opacity:0;transform:translateY(var(--size-1))}to{opacity:1;transform:translateY(0)}}.info-card-copy-btn{display:inline-flex;align-items:center;gap:var(--spacing-xs);padding:var(--spacing-xs);border:none;background:transparent;color:var(--text-color-secondary);border-radius:var(--border-radius);cursor:pointer;min-width:24px;min-height:24px;flex-shrink:0;transition:opacity var(--animation-duration-fast) var(--animation-easing-default),color var(--animation-duration-fast) var(--animation-easing-default)}@media(hover:hover){.info-card-copy-btn{opacity:0}.info-card-row:hover .info-card-copy-btn,.info-card-row:focus-within .info-card-copy-btn,.info-card-value-group:hover .info-card-copy-btn,.info-card-value-group:focus-within .info-card-copy-btn{opacity:1}}.info-card-copy-btn:hover{color:var(--primary-color)}.info-card-copy-btn:focus-visible{outline:none;box-shadow:0 0 0 2px var(--primary-color);opacity:1}.info-card-copy-btn.copied{color:var(--success-color);opacity:1}.info-card-copy-btn i{font-size:var(--icon-size-sm)}.info-card-copy-feedback{font-size:var(--font-size-xs);font-weight:var(--font-weight-medium);color:var(--success-color);white-space:nowrap}.info-card-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(max-width:768px){.info-card-title{font-size:var(--font-size-sm);padding:var(--size-1-5) var(--size-3);gap:var(--size-1-5)}.info-card-title i{font-size:var(--icon-size-sm)}.info-card-header-actions{padding-right:var(--size-1-5)}.info-card-content{padding:var(--size-1-5);min-height:var(--size-36)}.info-card-row{min-height:var(--size-7);padding:var(--size-0-5) 0}.info-card-label,.info-card-value{font-size:var(--font-size-xs)}.info-card-empty-state{font-size:var(--font-size-xs);padding:var(--size-4);margin:var(--size-1-5)}.info-card-actions{padding:var(--size-2) var(--size-3)}}@media(max-width:480px){.info-card-header{flex-wrap:wrap;gap:var(--size-1)}.info-card-title{font-size:var(--font-size-xs);padding:var(--size-1) var(--size-2);gap:var(--size-1)}.info-card-title i{font-size:var(--icon-size-xs)}.info-card-header-actions{padding:0 var(--size-1) var(--size-1) var(--size-1)}.info-card-content{padding:var(--size-1);min-height:auto}.info-card-row{flex-direction:column;align-items:flex-start;gap:var(--size-0-5);padding:var(--size-1-5) 0;min-height:auto}.info-card-row:hover{background-color:transparent}.info-card-label{width:100%;padding-right:0;text-align:left;flex:none;font-weight:var(--font-weight-semibold);color:var(--text-color)}.info-card-label:after{display:none}.info-card-value-group{width:100%;padding-left:0;flex:none}.info-card-value{padding-left:0;text-align:left;flex-shrink:1;font-weight:var(--font-weight-normal);white-space:normal}.info-card-empty-state{padding:var(--size-3);margin:var(--size-1)}.info-card-actions{padding:var(--size-1-5) var(--size-2);flex-direction:column;gap:var(--spacing-md)}.info-card-actions>*{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
1187
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: PshInfoCardComponent, isStandalone: true, selector: "psh-info-card", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, cssClass: { classPropertyName: "cssClass", publicName: "cssClass", isSignal: true, isRequired: false, transformFunction: null }, customStyle: { classPropertyName: "customStyle", publicName: "customStyle", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, hoverable: { classPropertyName: "hoverable", publicName: "hoverable", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, copyable: { classPropertyName: "copyable", publicName: "copyable", isSignal: true, isRequired: false, transformFunction: null }, copyButtonLabel: { classPropertyName: "copyButtonLabel", publicName: "copyButtonLabel", isSignal: true, isRequired: false, transformFunction: null }, copyFeedbackText: { classPropertyName: "copyFeedbackText", publicName: "copyFeedbackText", isSignal: true, isRequired: false, transformFunction: null }, autoFullWidthOnMobile: { classPropertyName: "autoFullWidthOnMobile", publicName: "autoFullWidthOnMobile", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked", copied: "copied", copyFailed: "copyFailed" }, host: { styleAttribute: "display: block; height: 100%;" }, ngImport: i0, template: "<div\r\n [class]=\"computedClasses()\"\r\n [ngStyle]=\"computedStyles()\"\r\n (click)=\"handleClick($event)\"\r\n (keydown)=\"handleKeydown($event)\"\r\n role=\"region\"\r\n [attr.tabindex]=\"interactive() && !disabled() ? 0 : null\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\r\n [attr.aria-busy]=\"loading() ? 'true' : null\"\r\n>\r\n @if (title() || hasHeaderActions()) {\r\n <div class=\"info-card-header\">\r\n @if (title()) {\r\n <h3 class=\"info-card-title\">\r\n @if (icon()) {\r\n <i\r\n class=\"ph {{ titleIcon() }}\"\r\n aria-hidden=\"true\"\r\n [attr.data-icon]=\"icon()\"\r\n ></i>\r\n }\r\n {{ title() }}\r\n </h3>\r\n }\r\n <div class=\"info-card-header-actions\">\r\n <ng-content select=\"[card-header-actions]\"></ng-content>\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"info-card-body\">\r\n @if (loading()) {\r\n <div class=\"info-card-loading\">\r\n <div class=\"skeleton-line\"></div>\r\n <div class=\"skeleton-line\"></div>\r\n <div class=\"skeleton-line short\"></div>\r\n </div>\r\n } @else {\r\n <div\r\n class=\"info-card-content\"\r\n role=\"list\"\r\n >\r\n @if (shouldShowEmptyState()) {\r\n <div\r\n class=\"info-card-empty-state\"\r\n role=\"status\"\r\n aria-live=\"polite\"\r\n [attr.data-testid]=\"'info-card-empty-state'\"\r\n >\r\n {{ getEmptyStateMessage() }}\r\n </div>\r\n } @else {\r\n @for (item of data(); track $index) {\r\n <div\r\n class=\"info-card-row\"\r\n role=\"listitem\"\r\n [attr.data-index]=\"$index\"\r\n [class]=\"item.customClass || ''\"\r\n >\r\n <div\r\n class=\"info-card-label\"\r\n [style.width]=\"item.labelWidth || options().labelWidth\"\r\n [attr.data-label]=\"item.label\"\r\n >\r\n {{ item.label }}\r\n </div>\r\n <div\r\n class=\"info-card-value-group\"\r\n [style.width]=\"item.valueWidth || options().valueWidth\"\r\n >\r\n <span\r\n [class]=\"getValueClasses(item)\"\r\n [attr.data-value]=\"item.value\"\r\n [title]=\"item.value?.toString() || ''\"\r\n >{{ item.value ?? 'Non renseign\u00E9' }}</span>\r\n @if (isRowCopyable(item) && hasNonEmptyValue(item)) {\r\n <button\r\n type=\"button\"\r\n class=\"info-card-copy-btn\"\r\n [class.copied]=\"copiedRowIndex() === $index\"\r\n [attr.aria-label]=\"copyButtonLabel() + ' ' + item.label\"\r\n (click)=\"copyRowValue(item, $index); $event.stopPropagation()\"\r\n >\r\n @if (copiedRowIndex() === $index) {\r\n <i class=\"ph ph-check\" aria-hidden=\"true\"></i>\r\n <span class=\"info-card-copy-feedback\" aria-hidden=\"true\">{{ copyFeedbackText() }}</span>\r\n } @else {\r\n <i class=\"ph ph-copy\" aria-hidden=\"true\"></i>\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n <span\r\n class=\"info-card-sr-only\"\r\n role=\"status\"\r\n aria-live=\"polite\"\r\n >\r\n @if (copiedRowIndex() !== null) {\r\n {{ copyFeedbackText() }}\r\n }\r\n </span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"info-card-actions\" [class]=\"getActionsClasses()\">\r\n <ng-content select=\"[card-actions]\"></ng-content>\r\n </div>\r\n</div>\r\n", styles: [".info-card{background:var(--surface-card);border-radius:var(--border-radius-lg, .75rem);overflow:hidden;transition:all .2s ease;width:100%;height:100%;display:flex;flex-direction:column;position:relative}.info-card.variant-elevated{box-shadow:var(--shadow-sm)}.info-card.variant-elevated.hoverable:hover{box-shadow:var(--shadow-md)}.info-card.variant-outlined{border:1px solid var(--surface-border)}.info-card.variant-default{background:var(--surface-section)}.info-card.hoverable:hover{transform:translateY(-2px)}.info-card.interactive{cursor:pointer;-webkit-user-select:none;user-select:none}.info-card.interactive:focus{outline:none;box-shadow:0 0 0 3px rgba(var(--primary-500-rgb, 59, 130, 246),.2)}.info-card.interactive:focus:not(:focus-visible){box-shadow:none}.info-card.interactive:active{transform:translateY(0)}.info-card.disabled{opacity:.6;pointer-events:none;cursor:not-allowed}.info-card.loading{pointer-events:none}.info-card-header{background:var(--surface-ground);border-bottom:var(--border-width-1) solid var(--surface-border);display:flex;align-items:center;justify-content:space-between;gap:var(--size-2)}.info-card-header-actions{display:flex;align-items:center;gap:var(--spacing-xs);padding-right:var(--size-2);flex-shrink:0}.info-card-header-actions:empty{display:none}.info-card-title{color:var(--primary-color);margin:0;font-size:var(--font-size-base);font-weight:var(--font-weight-semibold);padding:var(--size-2) var(--size-4);display:flex;align-items:center;gap:var(--size-2);transition:all var(--animation-duration-fast) var(--animation-easing-default)}.info-card-title i{font-size:var(--icon-size-md);flex-shrink:0}.info-card-body{flex:1;display:flex;flex-direction:column}.info-card-content{padding:var(--size-2);min-height:var(--size-48);display:flex;flex-direction:column;transition:opacity var(--animation-duration-normal) var(--animation-easing-default)}.info-card-row{display:flex;align-items:center;padding:var(--size-1) 0;min-height:var(--size-8);animation:fadeIn var(--animation-duration-fast) var(--animation-easing-default);transition:background-color var(--animation-duration-fast) var(--animation-easing-default)}.info-card-row:hover{background-color:var(--surface-hover);border-radius:var(--radius-base)}.info-card-row:last-child{border-bottom:none}.info-card-label{font-weight:var(--font-weight-medium);color:var(--text-color-secondary);font-size:var(--font-size-sm);padding-right:var(--size-2);position:relative;min-width:0;flex:1;line-height:var(--line-height-normal)}.info-card-label:after{content:\"\";position:absolute;right:0;top:50%;width:var(--size-1);height:var(--border-width-1);background-color:var(--surface-border)}.info-card-value-group{display:inline-flex;align-items:center;gap:var(--spacing-xs);padding-left:var(--size-2);min-width:0;flex:2}.info-card-value{font-weight:var(--font-weight-medium);color:var(--text-color);font-size:var(--font-size-sm);text-align:left;min-width:0;flex-shrink:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:var(--line-height-normal)}.info-card-value--italic{font-style:italic}.info-card-value--strikethrough{text-decoration:line-through}.info-card-value.info-card-value--bold{font-weight:var(--font-weight-semibold)}.info-card-value--tone-primary{color:var(--primary-color)}.info-card-value--tone-success{color:var(--success-color)}.info-card-value--tone-warning{color:var(--warning-color)}.info-card-value--tone-danger{color:var(--danger-color)}.info-card-value--tone-info{color:var(--alert-info-text-color)}.info-card-value.info-card-value--tone-muted{color:var(--text-color-secondary);font-weight:var(--font-weight-normal)}.info-card-empty-state{flex:1;display:flex;align-items:center;justify-content:center;text-align:center;color:var(--text-color-secondary);font-style:italic;font-size:var(--font-size-sm);padding:var(--size-6);background:var(--surface-ground);border-radius:var(--radius-lg);margin:var(--size-2);border:var(--border-width-1) dashed var(--surface-border);animation:fadeIn var(--animation-duration-normal) var(--animation-easing-default)}.info-card-actions>*{padding:var(--size-3) var(--size-4);background:var(--surface-ground);border-top:var(--border-width-1) solid var(--surface-border);display:flex;gap:var(--spacing-sm);align-items:center;justify-content:flex-end}.info-card-actions:not(:has(*)){display:none}.info-card-actions.mobile-full-width-buttons>*{display:flex;flex-direction:column;width:100%;gap:var(--spacing-sm)}.info-card-loading{display:flex;flex-direction:column;gap:var(--spacing-sm);padding:var(--size-2);animation:pulse 1.5s ease-in-out infinite}.skeleton-line{height:var(--size-4);background:linear-gradient(90deg,var(--surface-border) 25%,var(--surface-hover) 50%,var(--surface-border) 75%);background-size:200% 100%;animation:shimmer 1.5s ease-in-out infinite;border-radius:var(--spacing-xs)}.skeleton-line.short{width:60%}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.7}}@keyframes fadeIn{0%{opacity:0;transform:translateY(var(--size-1))}to{opacity:1;transform:translateY(0)}}.info-card-copy-btn{display:inline-flex;align-items:center;gap:var(--spacing-xs);padding:var(--spacing-xs);border:none;background:transparent;color:var(--text-color-secondary);border-radius:var(--border-radius);cursor:pointer;min-width:24px;min-height:24px;flex-shrink:0;transition:opacity var(--animation-duration-fast) var(--animation-easing-default),color var(--animation-duration-fast) var(--animation-easing-default)}@media(hover:hover){.info-card-copy-btn{opacity:0}.info-card-row:hover .info-card-copy-btn,.info-card-row:focus-within .info-card-copy-btn,.info-card-value-group:hover .info-card-copy-btn,.info-card-value-group:focus-within .info-card-copy-btn{opacity:1}}.info-card-copy-btn:hover{color:var(--primary-color)}.info-card-copy-btn:focus-visible{outline:none;box-shadow:0 0 0 2px var(--primary-color);opacity:1}.info-card-copy-btn.copied{color:var(--success-color);opacity:1}.info-card-copy-btn i{font-size:var(--icon-size-sm)}.info-card-copy-feedback{font-size:var(--font-size-xs);font-weight:var(--font-weight-medium);color:var(--success-color);white-space:nowrap}.info-card-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(max-width:768px){.info-card-title{font-size:var(--font-size-sm);padding:var(--size-1-5) var(--size-3);gap:var(--size-1-5)}.info-card-title i{font-size:var(--icon-size-sm)}.info-card-header-actions{padding-right:var(--size-1-5)}.info-card-content{padding:var(--size-1-5);min-height:var(--size-36)}.info-card-row{min-height:var(--size-7);padding:var(--size-0-5) 0}.info-card-label,.info-card-value{font-size:var(--font-size-xs)}.info-card-empty-state{font-size:var(--font-size-xs);padding:var(--size-4);margin:var(--size-1-5)}.info-card-actions{padding:var(--size-2) var(--size-3)}}@media(max-width:480px){.info-card-header{flex-wrap:wrap;gap:var(--size-1)}.info-card-title{font-size:var(--font-size-xs);padding:var(--size-1) var(--size-2);gap:var(--size-1)}.info-card-title i{font-size:var(--icon-size-xs)}.info-card-header-actions{padding:0 var(--size-1) var(--size-1) var(--size-1)}.info-card-content{padding:var(--size-1);min-height:auto}.info-card-row{flex-direction:column;align-items:flex-start;gap:var(--size-0-5);padding:var(--size-1-5) 0;min-height:auto}.info-card-row:hover{background-color:transparent}.info-card-label{width:100%;padding-right:0;text-align:left;flex:none;font-weight:var(--font-weight-semibold);color:var(--text-color)}.info-card-label:after{display:none}.info-card-value-group{width:100%;padding-left:0;flex:none}.info-card-value{padding-left:0;text-align:left;flex-shrink:1;font-weight:var(--font-weight-normal);white-space:normal}.info-card-empty-state{padding:var(--size-3);margin:var(--size-1)}.info-card-actions{padding:var(--size-1-5) var(--size-2);flex-direction:column;gap:var(--spacing-md)}.info-card-actions>*{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
1163
1188
|
}
|
|
1164
1189
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PshInfoCardComponent, decorators: [{
|
|
1165
1190
|
type: Component,
|
|
1166
1191
|
args: [{ selector: 'psh-info-card', imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
|
|
1167
1192
|
style: 'display: block; height: 100%;'
|
|
1168
|
-
}, template: "<div\r\n [class]=\"computedClasses()\"\r\n [ngStyle]=\"computedStyles()\"\r\n (click)=\"handleClick($event)\"\r\n (keydown)=\"handleKeydown($event)\"\r\n role=\"region\"\r\n [attr.tabindex]=\"interactive() && !disabled() ? 0 : null\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\r\n [attr.aria-busy]=\"loading() ? 'true' : null\"\r\n>\r\n @if (title() || hasHeaderActions()) {\r\n <div class=\"info-card-header\">\r\n @if (title()) {\r\n <h3 class=\"info-card-title\">\r\n @if (icon()) {\r\n <i\r\n class=\"ph {{ titleIcon() }}\"\r\n aria-hidden=\"true\"\r\n [attr.data-icon]=\"icon()\"\r\n ></i>\r\n }\r\n {{ title() }}\r\n </h3>\r\n }\r\n <div class=\"info-card-header-actions\">\r\n <ng-content select=\"[card-header-actions]\"></ng-content>\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"info-card-body\">\r\n @if (loading()) {\r\n <div class=\"info-card-loading\">\r\n <div class=\"skeleton-line\"></div>\r\n <div class=\"skeleton-line\"></div>\r\n <div class=\"skeleton-line short\"></div>\r\n </div>\r\n } @else {\r\n <div\r\n class=\"info-card-content\"\r\n role=\"list\"\r\n >\r\n @if (shouldShowEmptyState()) {\r\n <div\r\n class=\"info-card-empty-state\"\r\n role=\"status\"\r\n aria-live=\"polite\"\r\n [attr.data-testid]=\"'info-card-empty-state'\"\r\n >\r\n {{ getEmptyStateMessage() }}\r\n </div>\r\n } @else {\r\n @for (item of data(); track $index) {\r\n <div\r\n class=\"info-card-row\"\r\n role=\"listitem\"\r\n [attr.data-index]=\"$index\"\r\n [class]=\"item.customClass || ''\"\r\n >\r\n <div\r\n class=\"info-card-label\"\r\n [style.width]=\"item.labelWidth || options().labelWidth\"\r\n [attr.data-label]=\"item.label\"\r\n >\r\n {{ item.label }}\r\n </div>\r\n <div\r\n class=\"info-card-value-group\"\r\n [style.width]=\"item.valueWidth || options().valueWidth\"\r\n >\r\n <span\r\n class=\"info-card-value\"\r\n [attr.data-value]=\"item.value\"\r\n [title]=\"item.value?.toString() || ''\"\r\n >{{ item.value ?? 'Non renseign\u00E9' }}</span>\r\n @if (isRowCopyable(item) && hasNonEmptyValue(item)) {\r\n <button\r\n type=\"button\"\r\n class=\"info-card-copy-btn\"\r\n [class.copied]=\"copiedRowIndex() === $index\"\r\n [attr.aria-label]=\"copyButtonLabel() + ' ' + item.label\"\r\n (click)=\"copyRowValue(item, $index); $event.stopPropagation()\"\r\n >\r\n @if (copiedRowIndex() === $index) {\r\n <i class=\"ph ph-check\" aria-hidden=\"true\"></i>\r\n <span class=\"info-card-copy-feedback\" aria-hidden=\"true\">{{ copyFeedbackText() }}</span>\r\n } @else {\r\n <i class=\"ph ph-copy\" aria-hidden=\"true\"></i>\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n <span\r\n class=\"info-card-sr-only\"\r\n role=\"status\"\r\n aria-live=\"polite\"\r\n >\r\n @if (copiedRowIndex() !== null) {\r\n {{ copyFeedbackText() }}\r\n }\r\n </span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"info-card-actions\" [class]=\"getActionsClasses()\">\r\n <ng-content select=\"[card-actions]\"></ng-content>\r\n </div>\r\n</div>\r\n", styles: [".info-card{background:var(--surface-card);border-radius:var(--border-radius-lg, .75rem);overflow:hidden;transition:all .2s ease;width:100%;height:100%;display:flex;flex-direction:column;position:relative}.info-card.variant-elevated{box-shadow:var(--shadow-sm)}.info-card.variant-elevated.hoverable:hover{box-shadow:var(--shadow-md)}.info-card.variant-outlined{border:1px solid var(--surface-border)}.info-card.variant-default{background:var(--surface-section)}.info-card.hoverable:hover{transform:translateY(-2px)}.info-card.interactive{cursor:pointer;-webkit-user-select:none;user-select:none}.info-card.interactive:focus{outline:none;box-shadow:0 0 0 3px rgba(var(--primary-500-rgb, 59, 130, 246),.2)}.info-card.interactive:focus:not(:focus-visible){box-shadow:none}.info-card.interactive:active{transform:translateY(0)}.info-card.disabled{opacity:.6;pointer-events:none;cursor:not-allowed}.info-card.loading{pointer-events:none}.info-card-header{background:var(--surface-ground);border-bottom:var(--border-width-1) solid var(--surface-border);display:flex;align-items:center;justify-content:space-between;gap:var(--size-2)}.info-card-header-actions{display:flex;align-items:center;gap:var(--spacing-xs);padding-right:var(--size-2);flex-shrink:0}.info-card-header-actions:empty{display:none}.info-card-title{color:var(--primary-color);margin:0;font-size:var(--font-size-base);font-weight:var(--font-weight-semibold);padding:var(--size-2) var(--size-4);display:flex;align-items:center;gap:var(--size-2);transition:all var(--animation-duration-fast) var(--animation-easing-default)}.info-card-title i{font-size:var(--icon-size-md);flex-shrink:0}.info-card-body{flex:1;display:flex;flex-direction:column}.info-card-content{padding:var(--size-2);min-height:var(--size-48);display:flex;flex-direction:column;transition:opacity var(--animation-duration-normal) var(--animation-easing-default)}.info-card-row{display:flex;align-items:center;padding:var(--size-1) 0;min-height:var(--size-8);animation:fadeIn var(--animation-duration-fast) var(--animation-easing-default);transition:background-color var(--animation-duration-fast) var(--animation-easing-default)}.info-card-row:hover{background-color:var(--surface-hover);border-radius:var(--radius-base)}.info-card-row:last-child{border-bottom:none}.info-card-label{font-weight:var(--font-weight-medium);color:var(--text-color-secondary);font-size:var(--font-size-sm);padding-right:var(--size-2);position:relative;min-width:0;flex:1;line-height:var(--line-height-normal)}.info-card-label:after{content:\"\";position:absolute;right:0;top:50%;width:var(--size-1);height:var(--border-width-1);background-color:var(--surface-border)}.info-card-value-group{display:inline-flex;align-items:center;gap:var(--spacing-xs);padding-left:var(--size-2);min-width:0;flex:2}.info-card-value{font-weight:var(--font-weight-medium);color:var(--text-color);font-size:var(--font-size-sm);text-align:left;min-width:0;flex-shrink:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:var(--line-height-normal)}.info-card-empty-state{flex:1;display:flex;align-items:center;justify-content:center;text-align:center;color:var(--text-color-secondary);font-style:italic;font-size:var(--font-size-sm);padding:var(--size-6);background:var(--surface-ground);border-radius:var(--radius-lg);margin:var(--size-2);border:var(--border-width-1) dashed var(--surface-border);animation:fadeIn var(--animation-duration-normal) var(--animation-easing-default)}.info-card-actions>*{padding:var(--size-3) var(--size-4);background:var(--surface-ground);border-top:var(--border-width-1) solid var(--surface-border);display:flex;gap:var(--spacing-sm);align-items:center;justify-content:flex-end}.info-card-actions:not(:has(*)){display:none}.info-card-actions.mobile-full-width-buttons>*{display:flex;flex-direction:column;width:100%;gap:var(--spacing-sm)}.info-card-loading{display:flex;flex-direction:column;gap:var(--spacing-sm);padding:var(--size-2);animation:pulse 1.5s ease-in-out infinite}.skeleton-line{height:var(--size-4);background:linear-gradient(90deg,var(--surface-border) 25%,var(--surface-hover) 50%,var(--surface-border) 75%);background-size:200% 100%;animation:shimmer 1.5s ease-in-out infinite;border-radius:var(--spacing-xs)}.skeleton-line.short{width:60%}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.7}}@keyframes fadeIn{0%{opacity:0;transform:translateY(var(--size-1))}to{opacity:1;transform:translateY(0)}}.info-card-copy-btn{display:inline-flex;align-items:center;gap:var(--spacing-xs);padding:var(--spacing-xs);border:none;background:transparent;color:var(--text-color-secondary);border-radius:var(--border-radius);cursor:pointer;min-width:24px;min-height:24px;flex-shrink:0;transition:opacity var(--animation-duration-fast) var(--animation-easing-default),color var(--animation-duration-fast) var(--animation-easing-default)}@media(hover:hover){.info-card-copy-btn{opacity:0}.info-card-row:hover .info-card-copy-btn,.info-card-row:focus-within .info-card-copy-btn,.info-card-value-group:hover .info-card-copy-btn,.info-card-value-group:focus-within .info-card-copy-btn{opacity:1}}.info-card-copy-btn:hover{color:var(--primary-color)}.info-card-copy-btn:focus-visible{outline:none;box-shadow:0 0 0 2px var(--primary-color);opacity:1}.info-card-copy-btn.copied{color:var(--success-color);opacity:1}.info-card-copy-btn i{font-size:var(--icon-size-sm)}.info-card-copy-feedback{font-size:var(--font-size-xs);font-weight:var(--font-weight-medium);color:var(--success-color);white-space:nowrap}.info-card-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(max-width:768px){.info-card-title{font-size:var(--font-size-sm);padding:var(--size-1-5) var(--size-3);gap:var(--size-1-5)}.info-card-title i{font-size:var(--icon-size-sm)}.info-card-header-actions{padding-right:var(--size-1-5)}.info-card-content{padding:var(--size-1-5);min-height:var(--size-36)}.info-card-row{min-height:var(--size-7);padding:var(--size-0-5) 0}.info-card-label,.info-card-value{font-size:var(--font-size-xs)}.info-card-empty-state{font-size:var(--font-size-xs);padding:var(--size-4);margin:var(--size-1-5)}.info-card-actions{padding:var(--size-2) var(--size-3)}}@media(max-width:480px){.info-card-header{flex-wrap:wrap;gap:var(--size-1)}.info-card-title{font-size:var(--font-size-xs);padding:var(--size-1) var(--size-2);gap:var(--size-1)}.info-card-title i{font-size:var(--icon-size-xs)}.info-card-header-actions{padding:0 var(--size-1) var(--size-1) var(--size-1)}.info-card-content{padding:var(--size-1);min-height:auto}.info-card-row{flex-direction:column;align-items:flex-start;gap:var(--size-0-5);padding:var(--size-1-5) 0;min-height:auto}.info-card-row:hover{background-color:transparent}.info-card-label{width:100%;padding-right:0;text-align:left;flex:none;font-weight:var(--font-weight-semibold);color:var(--text-color)}.info-card-label:after{display:none}.info-card-value-group{width:100%;padding-left:0;flex:none}.info-card-value{padding-left:0;text-align:left;flex-shrink:1;font-weight:var(--font-weight-normal);white-space:normal}.info-card-empty-state{padding:var(--size-3);margin:var(--size-1)}.info-card-actions{padding:var(--size-1-5) var(--size-2);flex-direction:column;gap:var(--spacing-md)}.info-card-actions>*{width:100%}}\n"] }]
|
|
1193
|
+
}, template: "<div\r\n [class]=\"computedClasses()\"\r\n [ngStyle]=\"computedStyles()\"\r\n (click)=\"handleClick($event)\"\r\n (keydown)=\"handleKeydown($event)\"\r\n role=\"region\"\r\n [attr.tabindex]=\"interactive() && !disabled() ? 0 : null\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\r\n [attr.aria-busy]=\"loading() ? 'true' : null\"\r\n>\r\n @if (title() || hasHeaderActions()) {\r\n <div class=\"info-card-header\">\r\n @if (title()) {\r\n <h3 class=\"info-card-title\">\r\n @if (icon()) {\r\n <i\r\n class=\"ph {{ titleIcon() }}\"\r\n aria-hidden=\"true\"\r\n [attr.data-icon]=\"icon()\"\r\n ></i>\r\n }\r\n {{ title() }}\r\n </h3>\r\n }\r\n <div class=\"info-card-header-actions\">\r\n <ng-content select=\"[card-header-actions]\"></ng-content>\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"info-card-body\">\r\n @if (loading()) {\r\n <div class=\"info-card-loading\">\r\n <div class=\"skeleton-line\"></div>\r\n <div class=\"skeleton-line\"></div>\r\n <div class=\"skeleton-line short\"></div>\r\n </div>\r\n } @else {\r\n <div\r\n class=\"info-card-content\"\r\n role=\"list\"\r\n >\r\n @if (shouldShowEmptyState()) {\r\n <div\r\n class=\"info-card-empty-state\"\r\n role=\"status\"\r\n aria-live=\"polite\"\r\n [attr.data-testid]=\"'info-card-empty-state'\"\r\n >\r\n {{ getEmptyStateMessage() }}\r\n </div>\r\n } @else {\r\n @for (item of data(); track $index) {\r\n <div\r\n class=\"info-card-row\"\r\n role=\"listitem\"\r\n [attr.data-index]=\"$index\"\r\n [class]=\"item.customClass || ''\"\r\n >\r\n <div\r\n class=\"info-card-label\"\r\n [style.width]=\"item.labelWidth || options().labelWidth\"\r\n [attr.data-label]=\"item.label\"\r\n >\r\n {{ item.label }}\r\n </div>\r\n <div\r\n class=\"info-card-value-group\"\r\n [style.width]=\"item.valueWidth || options().valueWidth\"\r\n >\r\n <span\r\n [class]=\"getValueClasses(item)\"\r\n [attr.data-value]=\"item.value\"\r\n [title]=\"item.value?.toString() || ''\"\r\n >{{ item.value ?? 'Non renseign\u00E9' }}</span>\r\n @if (isRowCopyable(item) && hasNonEmptyValue(item)) {\r\n <button\r\n type=\"button\"\r\n class=\"info-card-copy-btn\"\r\n [class.copied]=\"copiedRowIndex() === $index\"\r\n [attr.aria-label]=\"copyButtonLabel() + ' ' + item.label\"\r\n (click)=\"copyRowValue(item, $index); $event.stopPropagation()\"\r\n >\r\n @if (copiedRowIndex() === $index) {\r\n <i class=\"ph ph-check\" aria-hidden=\"true\"></i>\r\n <span class=\"info-card-copy-feedback\" aria-hidden=\"true\">{{ copyFeedbackText() }}</span>\r\n } @else {\r\n <i class=\"ph ph-copy\" aria-hidden=\"true\"></i>\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n <span\r\n class=\"info-card-sr-only\"\r\n role=\"status\"\r\n aria-live=\"polite\"\r\n >\r\n @if (copiedRowIndex() !== null) {\r\n {{ copyFeedbackText() }}\r\n }\r\n </span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"info-card-actions\" [class]=\"getActionsClasses()\">\r\n <ng-content select=\"[card-actions]\"></ng-content>\r\n </div>\r\n</div>\r\n", styles: [".info-card{background:var(--surface-card);border-radius:var(--border-radius-lg, .75rem);overflow:hidden;transition:all .2s ease;width:100%;height:100%;display:flex;flex-direction:column;position:relative}.info-card.variant-elevated{box-shadow:var(--shadow-sm)}.info-card.variant-elevated.hoverable:hover{box-shadow:var(--shadow-md)}.info-card.variant-outlined{border:1px solid var(--surface-border)}.info-card.variant-default{background:var(--surface-section)}.info-card.hoverable:hover{transform:translateY(-2px)}.info-card.interactive{cursor:pointer;-webkit-user-select:none;user-select:none}.info-card.interactive:focus{outline:none;box-shadow:0 0 0 3px rgba(var(--primary-500-rgb, 59, 130, 246),.2)}.info-card.interactive:focus:not(:focus-visible){box-shadow:none}.info-card.interactive:active{transform:translateY(0)}.info-card.disabled{opacity:.6;pointer-events:none;cursor:not-allowed}.info-card.loading{pointer-events:none}.info-card-header{background:var(--surface-ground);border-bottom:var(--border-width-1) solid var(--surface-border);display:flex;align-items:center;justify-content:space-between;gap:var(--size-2)}.info-card-header-actions{display:flex;align-items:center;gap:var(--spacing-xs);padding-right:var(--size-2);flex-shrink:0}.info-card-header-actions:empty{display:none}.info-card-title{color:var(--primary-color);margin:0;font-size:var(--font-size-base);font-weight:var(--font-weight-semibold);padding:var(--size-2) var(--size-4);display:flex;align-items:center;gap:var(--size-2);transition:all var(--animation-duration-fast) var(--animation-easing-default)}.info-card-title i{font-size:var(--icon-size-md);flex-shrink:0}.info-card-body{flex:1;display:flex;flex-direction:column}.info-card-content{padding:var(--size-2);min-height:var(--size-48);display:flex;flex-direction:column;transition:opacity var(--animation-duration-normal) var(--animation-easing-default)}.info-card-row{display:flex;align-items:center;padding:var(--size-1) 0;min-height:var(--size-8);animation:fadeIn var(--animation-duration-fast) var(--animation-easing-default);transition:background-color var(--animation-duration-fast) var(--animation-easing-default)}.info-card-row:hover{background-color:var(--surface-hover);border-radius:var(--radius-base)}.info-card-row:last-child{border-bottom:none}.info-card-label{font-weight:var(--font-weight-medium);color:var(--text-color-secondary);font-size:var(--font-size-sm);padding-right:var(--size-2);position:relative;min-width:0;flex:1;line-height:var(--line-height-normal)}.info-card-label:after{content:\"\";position:absolute;right:0;top:50%;width:var(--size-1);height:var(--border-width-1);background-color:var(--surface-border)}.info-card-value-group{display:inline-flex;align-items:center;gap:var(--spacing-xs);padding-left:var(--size-2);min-width:0;flex:2}.info-card-value{font-weight:var(--font-weight-medium);color:var(--text-color);font-size:var(--font-size-sm);text-align:left;min-width:0;flex-shrink:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:var(--line-height-normal)}.info-card-value--italic{font-style:italic}.info-card-value--strikethrough{text-decoration:line-through}.info-card-value.info-card-value--bold{font-weight:var(--font-weight-semibold)}.info-card-value--tone-primary{color:var(--primary-color)}.info-card-value--tone-success{color:var(--success-color)}.info-card-value--tone-warning{color:var(--warning-color)}.info-card-value--tone-danger{color:var(--danger-color)}.info-card-value--tone-info{color:var(--alert-info-text-color)}.info-card-value.info-card-value--tone-muted{color:var(--text-color-secondary);font-weight:var(--font-weight-normal)}.info-card-empty-state{flex:1;display:flex;align-items:center;justify-content:center;text-align:center;color:var(--text-color-secondary);font-style:italic;font-size:var(--font-size-sm);padding:var(--size-6);background:var(--surface-ground);border-radius:var(--radius-lg);margin:var(--size-2);border:var(--border-width-1) dashed var(--surface-border);animation:fadeIn var(--animation-duration-normal) var(--animation-easing-default)}.info-card-actions>*{padding:var(--size-3) var(--size-4);background:var(--surface-ground);border-top:var(--border-width-1) solid var(--surface-border);display:flex;gap:var(--spacing-sm);align-items:center;justify-content:flex-end}.info-card-actions:not(:has(*)){display:none}.info-card-actions.mobile-full-width-buttons>*{display:flex;flex-direction:column;width:100%;gap:var(--spacing-sm)}.info-card-loading{display:flex;flex-direction:column;gap:var(--spacing-sm);padding:var(--size-2);animation:pulse 1.5s ease-in-out infinite}.skeleton-line{height:var(--size-4);background:linear-gradient(90deg,var(--surface-border) 25%,var(--surface-hover) 50%,var(--surface-border) 75%);background-size:200% 100%;animation:shimmer 1.5s ease-in-out infinite;border-radius:var(--spacing-xs)}.skeleton-line.short{width:60%}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.7}}@keyframes fadeIn{0%{opacity:0;transform:translateY(var(--size-1))}to{opacity:1;transform:translateY(0)}}.info-card-copy-btn{display:inline-flex;align-items:center;gap:var(--spacing-xs);padding:var(--spacing-xs);border:none;background:transparent;color:var(--text-color-secondary);border-radius:var(--border-radius);cursor:pointer;min-width:24px;min-height:24px;flex-shrink:0;transition:opacity var(--animation-duration-fast) var(--animation-easing-default),color var(--animation-duration-fast) var(--animation-easing-default)}@media(hover:hover){.info-card-copy-btn{opacity:0}.info-card-row:hover .info-card-copy-btn,.info-card-row:focus-within .info-card-copy-btn,.info-card-value-group:hover .info-card-copy-btn,.info-card-value-group:focus-within .info-card-copy-btn{opacity:1}}.info-card-copy-btn:hover{color:var(--primary-color)}.info-card-copy-btn:focus-visible{outline:none;box-shadow:0 0 0 2px var(--primary-color);opacity:1}.info-card-copy-btn.copied{color:var(--success-color);opacity:1}.info-card-copy-btn i{font-size:var(--icon-size-sm)}.info-card-copy-feedback{font-size:var(--font-size-xs);font-weight:var(--font-weight-medium);color:var(--success-color);white-space:nowrap}.info-card-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(max-width:768px){.info-card-title{font-size:var(--font-size-sm);padding:var(--size-1-5) var(--size-3);gap:var(--size-1-5)}.info-card-title i{font-size:var(--icon-size-sm)}.info-card-header-actions{padding-right:var(--size-1-5)}.info-card-content{padding:var(--size-1-5);min-height:var(--size-36)}.info-card-row{min-height:var(--size-7);padding:var(--size-0-5) 0}.info-card-label,.info-card-value{font-size:var(--font-size-xs)}.info-card-empty-state{font-size:var(--font-size-xs);padding:var(--size-4);margin:var(--size-1-5)}.info-card-actions{padding:var(--size-2) var(--size-3)}}@media(max-width:480px){.info-card-header{flex-wrap:wrap;gap:var(--size-1)}.info-card-title{font-size:var(--font-size-xs);padding:var(--size-1) var(--size-2);gap:var(--size-1)}.info-card-title i{font-size:var(--icon-size-xs)}.info-card-header-actions{padding:0 var(--size-1) var(--size-1) var(--size-1)}.info-card-content{padding:var(--size-1);min-height:auto}.info-card-row{flex-direction:column;align-items:flex-start;gap:var(--size-0-5);padding:var(--size-1-5) 0;min-height:auto}.info-card-row:hover{background-color:transparent}.info-card-label{width:100%;padding-right:0;text-align:left;flex:none;font-weight:var(--font-weight-semibold);color:var(--text-color)}.info-card-label:after{display:none}.info-card-value-group{width:100%;padding-left:0;flex:none}.info-card-value{padding-left:0;text-align:left;flex-shrink:1;font-weight:var(--font-weight-normal);white-space:normal}.info-card-empty-state{padding:var(--size-3);margin:var(--size-1)}.info-card-actions{padding:var(--size-1-5) var(--size-2);flex-direction:column;gap:var(--spacing-md)}.info-card-actions>*{width:100%}}\n"] }]
|
|
1169
1194
|
}], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], cssClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "cssClass", required: false }] }], customStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "customStyle", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], hoverable: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverable", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }], copyable: [{ type: i0.Input, args: [{ isSignal: true, alias: "copyable", required: false }] }], copyButtonLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "copyButtonLabel", required: false }] }], copyFeedbackText: [{ type: i0.Input, args: [{ isSignal: true, alias: "copyFeedbackText", required: false }] }], copied: [{ type: i0.Output, args: ["copied"] }], copyFailed: [{ type: i0.Output, args: ["copyFailed"] }], autoFullWidthOnMobile: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoFullWidthOnMobile", required: false }] }] } });
|
|
1170
1195
|
|
|
1171
1196
|
const CHECKBOX_CONFIG = new InjectionToken('CHECKBOX_CONFIG', {
|
|
@@ -3480,16 +3505,31 @@ class PshPaginationComponent {
|
|
|
3480
3505
|
this.itemsPerPageInput = input(10, { ...(ngDevMode ? { debugName: "itemsPerPageInput" } : /* istanbul ignore next */ {}), alias: 'itemsPerPage' });
|
|
3481
3506
|
this.itemsPerPage = linkedSignal(this.itemsPerPageInput, /* @ts-ignore */
|
|
3482
3507
|
...(ngDevMode ? [{ debugName: "itemsPerPage" }] : /* istanbul ignore next */ []));
|
|
3508
|
+
/**
|
|
3509
|
+
* Number of pages used for rendering and navigation: never below 1, and never
|
|
3510
|
+
* written back to the `totalPages` input. `totalPages` is derived data owned by
|
|
3511
|
+
* the caller (`Math.ceil(total / pageSize)`, or a server-side page count), so the
|
|
3512
|
+
* component reads it and clamps its own view of it instead of correcting it.
|
|
3513
|
+
*/
|
|
3514
|
+
this.effectiveTotalPages = computed(() => {
|
|
3515
|
+
const total = this.totalPages();
|
|
3516
|
+
return Number.isInteger(total) && total >= 1 ? total : 1;
|
|
3517
|
+
}, /* @ts-ignore */
|
|
3518
|
+
...(ngDevMode ? [{ debugName: "effectiveTotalPages" }] : /* istanbul ignore next */ []));
|
|
3483
3519
|
this.size = input(this.config.size ?? 'medium', { ...(ngDevMode ? { debugName: "size" } : /* istanbul ignore next */ {}), transform: (value) => {
|
|
3484
3520
|
if (!['small', 'medium', 'large'].includes(value)) {
|
|
3485
|
-
|
|
3521
|
+
if (isDevMode()) {
|
|
3522
|
+
console.warn(`[psh-pagination] Invalid size "${value}", falling back to "medium"`);
|
|
3523
|
+
}
|
|
3486
3524
|
return 'medium';
|
|
3487
3525
|
}
|
|
3488
3526
|
return value;
|
|
3489
3527
|
} });
|
|
3490
3528
|
this.variant = input(this.config.variant ?? 'default', { ...(ngDevMode ? { debugName: "variant" } : /* istanbul ignore next */ {}), transform: (value) => {
|
|
3491
3529
|
if (!['default', 'outline'].includes(value)) {
|
|
3492
|
-
|
|
3530
|
+
if (isDevMode()) {
|
|
3531
|
+
console.warn(`[psh-pagination] Invalid variant "${value}", falling back to "default"`);
|
|
3532
|
+
}
|
|
3493
3533
|
return 'default';
|
|
3494
3534
|
}
|
|
3495
3535
|
return value;
|
|
@@ -3534,7 +3574,7 @@ class PshPaginationComponent {
|
|
|
3534
3574
|
const pages = [];
|
|
3535
3575
|
const halfVisible = Math.floor(this.maxVisiblePages() / 2);
|
|
3536
3576
|
let start = Math.max(1, this.currentPage() - halfVisible);
|
|
3537
|
-
const end = Math.min(this.
|
|
3577
|
+
const end = Math.min(this.effectiveTotalPages(), start + this.maxVisiblePages() - 1);
|
|
3538
3578
|
if (end - start + 1 < this.maxVisiblePages()) {
|
|
3539
3579
|
start = Math.max(1, end - this.maxVisiblePages() + 1);
|
|
3540
3580
|
}
|
|
@@ -3546,60 +3586,70 @@ class PshPaginationComponent {
|
|
|
3546
3586
|
...(ngDevMode ? [{ debugName: "pages" }] : /* istanbul ignore next */ []));
|
|
3547
3587
|
this.state = computed(() => this.getState(), /* @ts-ignore */
|
|
3548
3588
|
...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
3549
|
-
this.canGoNext = computed(() => this.currentPage() < this.
|
|
3589
|
+
this.canGoNext = computed(() => this.currentPage() < this.effectiveTotalPages(), /* @ts-ignore */
|
|
3550
3590
|
...(ngDevMode ? [{ debugName: "canGoNext" }] : /* istanbul ignore next */ []));
|
|
3551
3591
|
this.canGoPrevious = computed(() => this.currentPage() > 1, /* @ts-ignore */
|
|
3552
3592
|
...(ngDevMode ? [{ debugName: "canGoPrevious" }] : /* istanbul ignore next */ []));
|
|
3553
3593
|
this.isFirstPage = computed(() => this.currentPage() === 1, /* @ts-ignore */
|
|
3554
3594
|
...(ngDevMode ? [{ debugName: "isFirstPage" }] : /* istanbul ignore next */ []));
|
|
3555
|
-
this.isLastPage = computed(() => this.currentPage() === this.
|
|
3595
|
+
this.isLastPage = computed(() => this.currentPage() === this.effectiveTotalPages(), /* @ts-ignore */
|
|
3556
3596
|
...(ngDevMode ? [{ debugName: "isLastPage" }] : /* istanbul ignore next */ []));
|
|
3557
3597
|
this.currentPageAnnouncement = computed(() => {
|
|
3558
|
-
return `Page ${this.currentPage()} sur ${this.
|
|
3598
|
+
return `Page ${this.currentPage()} sur ${this.effectiveTotalPages()}`;
|
|
3559
3599
|
}, /* @ts-ignore */
|
|
3560
3600
|
...(ngDevMode ? [{ debugName: "currentPageAnnouncement" }] : /* istanbul ignore next */ []));
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
this.totalPages
|
|
3566
|
-
|
|
3567
|
-
|
|
3601
|
+
if (isDevMode()) {
|
|
3602
|
+
effect(() => {
|
|
3603
|
+
// `0` is a legitimate state (empty list, or a server-side page count that is
|
|
3604
|
+
// not loaded yet): stay silent. Only report what betrays a caller bug.
|
|
3605
|
+
const total = this.totalPages();
|
|
3606
|
+
if (total !== 0 && (!Number.isInteger(total) || total < 1)) {
|
|
3607
|
+
console.warn(`[psh-pagination] Invalid totalPages "${total}", using 1 page`);
|
|
3608
|
+
}
|
|
3609
|
+
});
|
|
3610
|
+
effect(() => {
|
|
3611
|
+
const maxVisible = this.maxVisiblePages();
|
|
3612
|
+
if (maxVisible < 1 || !Number.isFinite(maxVisible)) {
|
|
3613
|
+
console.warn(`[psh-pagination] Invalid maxVisiblePages "${maxVisible}", must be >= 1`);
|
|
3614
|
+
}
|
|
3615
|
+
});
|
|
3616
|
+
}
|
|
3617
|
+
// `currentPage` IS owned by the component (it navigates), so correcting it with
|
|
3618
|
+
// a write-back is the intended behaviour — clamped on the derived page count.
|
|
3568
3619
|
effect(() => {
|
|
3569
3620
|
const current = this.currentPage();
|
|
3570
|
-
const total = this.
|
|
3621
|
+
const total = this.effectiveTotalPages();
|
|
3571
3622
|
if (current < 1 || !Number.isFinite(current)) {
|
|
3572
|
-
|
|
3623
|
+
if (isDevMode()) {
|
|
3624
|
+
console.warn(`[psh-pagination] Invalid currentPage "${current}", setting to 1`);
|
|
3625
|
+
}
|
|
3573
3626
|
this.currentPage.set(1);
|
|
3574
3627
|
}
|
|
3575
|
-
else if (current > total
|
|
3576
|
-
|
|
3628
|
+
else if (current > total) {
|
|
3629
|
+
if (isDevMode()) {
|
|
3630
|
+
console.warn(`[psh-pagination] currentPage "${current}" exceeds totalPages "${total}", setting to ${total}`);
|
|
3631
|
+
}
|
|
3577
3632
|
this.currentPage.set(total);
|
|
3578
3633
|
}
|
|
3579
3634
|
});
|
|
3580
|
-
effect(() => {
|
|
3581
|
-
const maxVisible = this.maxVisiblePages();
|
|
3582
|
-
if (maxVisible < 1 || !Number.isFinite(maxVisible)) {
|
|
3583
|
-
console.warn(`[psh-pagination] Invalid maxVisiblePages "${maxVisible}", must be >= 1`);
|
|
3584
|
-
}
|
|
3585
|
-
});
|
|
3586
3635
|
}
|
|
3587
3636
|
getState() {
|
|
3588
3637
|
if (this.currentPage() === 1)
|
|
3589
3638
|
return 'first';
|
|
3590
|
-
if (this.currentPage() === this.
|
|
3639
|
+
if (this.currentPage() === this.effectiveTotalPages())
|
|
3591
3640
|
return 'last';
|
|
3592
3641
|
return 'default';
|
|
3593
3642
|
}
|
|
3594
3643
|
goToPage(page) {
|
|
3595
|
-
|
|
3644
|
+
const total = this.effectiveTotalPages();
|
|
3645
|
+
if (page !== this.currentPage() && page >= 1 && page <= total) {
|
|
3596
3646
|
this.currentPage.set(page);
|
|
3597
3647
|
this.pageChange.emit(page);
|
|
3598
3648
|
}
|
|
3599
|
-
else if (page < 1 || page >
|
|
3649
|
+
else if (page < 1 || page > total) {
|
|
3600
3650
|
this.navigationError.emit({
|
|
3601
3651
|
action: 'goToPage',
|
|
3602
|
-
reason: `Page ${page} is out of bounds (1-${
|
|
3652
|
+
reason: `Page ${page} is out of bounds (1-${total})`,
|
|
3603
3653
|
});
|
|
3604
3654
|
}
|
|
3605
3655
|
}
|
|
@@ -3607,7 +3657,7 @@ class PshPaginationComponent {
|
|
|
3607
3657
|
this.goToPage(1);
|
|
3608
3658
|
}
|
|
3609
3659
|
goToLastPage() {
|
|
3610
|
-
this.goToPage(this.
|
|
3660
|
+
this.goToPage(this.effectiveTotalPages());
|
|
3611
3661
|
}
|
|
3612
3662
|
goToNextPage() {
|
|
3613
3663
|
if (this.canGoNext()) {
|