@xiriframework/xiri-ng 0.2.18 → 0.2.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @xiriframework/xiri-ng
|
|
2
2
|
|
|
3
|
-
Angular UI component library for the Xiri Framework.
|
|
3
|
+
Angular UI component library for the [Xiri Framework](https://github.com/xiriframework) — a JSON-driven approach where your backend defines the UI and `xiri-ng` renders it. Pairs with [xiri-go](https://github.com/xiriframework/xiri-go) (Go backend) or any backend that emits the expected JSON shape.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,61 +8,156 @@ Angular UI component library for the Xiri Framework.
|
|
|
8
8
|
npm install @xiriframework/xiri-ng
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
Register the Xiri services in your application config. `api` is the base URL prepended to HTTP requests made by `XiriDataService`:
|
|
12
14
|
|
|
13
15
|
```typescript
|
|
16
|
+
import { ApplicationConfig } from '@angular/core';
|
|
17
|
+
import { provideHttpClient } from '@angular/common/http';
|
|
18
|
+
import { provideRouter } from '@angular/router';
|
|
14
19
|
import { provideXiriServices } from '@xiriframework/xiri-ng';
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
export const appConfig: ApplicationConfig = {
|
|
17
22
|
providers: [
|
|
23
|
+
provideHttpClient(),
|
|
24
|
+
provideRouter(routes),
|
|
18
25
|
provideXiriServices({ api: '/api/' }),
|
|
19
|
-
]
|
|
20
|
-
}
|
|
26
|
+
],
|
|
27
|
+
};
|
|
21
28
|
```
|
|
22
29
|
|
|
23
|
-
The
|
|
30
|
+
## The main entry point: `xiri-dyncomponent`
|
|
24
31
|
|
|
25
|
-
|
|
32
|
+
`xiri-ng` is designed around a central component that dynamically renders an array of JSON component definitions:
|
|
33
|
+
|
|
34
|
+
```html
|
|
35
|
+
<xiri-dyncomponent [data]="components"></xiri-dyncomponent>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { XiriDynComponentComponent, XiriDynData } from '@xiriframework/xiri-ng';
|
|
40
|
+
|
|
41
|
+
components: XiriDynData[] = [
|
|
42
|
+
{ type: 'page-header', data: { title: 'Dashboard' } },
|
|
43
|
+
{ type: 'stat-grid', data: { stats: [...], columns: 4 } },
|
|
44
|
+
{ type: 'table', data: { url: '/api/devices', ... } },
|
|
45
|
+
];
|
|
46
|
+
```
|
|
26
47
|
|
|
27
|
-
|
|
48
|
+
Individual components can also be used directly (all are standalone):
|
|
28
49
|
|
|
29
50
|
```typescript
|
|
30
|
-
import { XiriTableComponent
|
|
51
|
+
import { XiriTableComponent } from '@xiriframework/xiri-ng';
|
|
52
|
+
|
|
53
|
+
@Component({
|
|
54
|
+
selector: 'app-devices',
|
|
55
|
+
imports: [XiriTableComponent],
|
|
56
|
+
template: `<xiri-table [settings]="tableSettings" />`,
|
|
57
|
+
})
|
|
58
|
+
export class DevicesComponent { /* ... */ }
|
|
31
59
|
```
|
|
32
60
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
## Components
|
|
62
|
+
|
|
63
|
+
40+ standalone Material-Design components. Import individually for tree-shaking.
|
|
64
|
+
|
|
65
|
+
### Forms & data
|
|
66
|
+
|
|
67
|
+
| Component | Use case |
|
|
68
|
+
| ---------------------------- | -------------------------------------------------- |
|
|
69
|
+
| `XiriFormComponent` | Dynamic form with backend-driven fields + submit |
|
|
70
|
+
| `XiriFormFieldsComponent` | Just the fields — full control over submit flow |
|
|
71
|
+
| `XiriQueryComponent` | Filter form + live results via `xiri-dyncomponent` |
|
|
72
|
+
| `XiriTableComponent` | Full data table — sort, paginate, filter, inline-edit, export |
|
|
73
|
+
| `XiriRawTableComponent` | Minimal table for static data |
|
|
74
|
+
| `XiriDialogComponent` | Modal dialogs (question / form / table / waiting) |
|
|
75
|
+
| `XiriStepperComponent` | Multi-step wizard |
|
|
76
|
+
|
|
77
|
+
### Layout & navigation
|
|
78
|
+
|
|
79
|
+
| Component | Use case |
|
|
80
|
+
| ---------------------------- | -------------------------------------------------- |
|
|
81
|
+
| `XiriPageHeaderComponent` | Page title + subtitle + actions |
|
|
82
|
+
| `XiriToolbarComponent` | Toolbar with title, search, buttons |
|
|
83
|
+
| `XiriSectionComponent` | Collapsible content sections |
|
|
84
|
+
| `XiriDividerComponent` | Section divider with optional text/icon |
|
|
85
|
+
| `XiriTabsComponent` | Material tabs with lazy-loading |
|
|
86
|
+
| `XiriExpansionComponent` | Accordion panels with lazy-loading |
|
|
87
|
+
| `XiriSidenavComponent` | Side navigation with nested items |
|
|
88
|
+
| `XiriBreadcrumbComponent` | Breadcrumb trail |
|
|
89
|
+
|
|
90
|
+
### Cards, lists, info
|
|
91
|
+
|
|
92
|
+
| Component | Use case |
|
|
93
|
+
| --------------------------------- | --------------------------------------------- |
|
|
94
|
+
| `XiriCardComponent` | Card container with header + content |
|
|
95
|
+
| `XiriCardlinkComponent` | Clickable card as a navigation link |
|
|
96
|
+
| `XiriLinksComponent` | List of navigation links/buttons |
|
|
97
|
+
| `XiriListComponent` | Sectioned list with favorites |
|
|
98
|
+
| `XiriInfopointComponent` | Info card (icon + text + optional link) |
|
|
99
|
+
| `XiriImagetextComponent` | Image + text card |
|
|
100
|
+
| `XiriDescriptionListComponent` | Key/value pairs in 1-3 columns |
|
|
101
|
+
|
|
102
|
+
### Stats, progress, feedback
|
|
103
|
+
|
|
104
|
+
| Component | Use case |
|
|
105
|
+
| ----------------------------- | ------------------------------------------------- |
|
|
106
|
+
| `XiriStatComponent` | Single KPI tile with value + label + trend |
|
|
107
|
+
| `XiriStatGridComponent` | Grid of KPI tiles |
|
|
108
|
+
| `XiriMultiprogressComponent` | Multi-row progress bars |
|
|
109
|
+
| `XiriTimelineComponent` | Vertical or horizontal timeline |
|
|
110
|
+
| `XiriEmptyStateComponent` | Empty-state placeholder with optional action |
|
|
111
|
+
| `XiriSkeletonComponent` | Loading skeleton (text/circle/rect/table-row) |
|
|
112
|
+
| `XiriAlertComponent` | Alert / confirmation dialog content |
|
|
113
|
+
| `XiriDoneComponent` | Success checkmark |
|
|
114
|
+
| `XiriErrorComponent` | Error message display |
|
|
115
|
+
| `XiriHeaderComponent` | In-page section header |
|
|
116
|
+
|
|
117
|
+
### Buttons & search
|
|
118
|
+
|
|
119
|
+
| Component | Use case |
|
|
120
|
+
| ----------------------------- | ------------------------------------------------- |
|
|
121
|
+
| `XiriButtonComponent` | Single button with action handling |
|
|
122
|
+
| `XiriButtonlineComponent` | Horizontal button line |
|
|
123
|
+
| `XiriButtonstyleComponent` | Internal — button visual renderer |
|
|
124
|
+
| `XiriSearchComponent` | Debounced search input |
|
|
125
|
+
|
|
126
|
+
## Services
|
|
127
|
+
|
|
128
|
+
All services are `providedIn: 'root'` and are activated by `provideXiriServices()`:
|
|
129
|
+
|
|
130
|
+
| Service | Purpose |
|
|
131
|
+
| ------------------------------- | ----------------------------------------------------------------- |
|
|
132
|
+
| `XiriDataService` | HTTP client with automatic snackbar response handling |
|
|
133
|
+
| `XiriSnackbarService` | Toast notifications (success, error, info, warning) |
|
|
134
|
+
| `XiriResponseHandlerService` | Parses backend responses (refresh, goto, table-update) centrally |
|
|
135
|
+
| `XiriFormService` | Form data fetch/submit + state persistence |
|
|
136
|
+
| `XiriDownloadService` | File download handling (blob → browser) |
|
|
137
|
+
| `XiriDateService` | Unix timestamp ↔ locale-formatted strings (via `date-fns`) |
|
|
138
|
+
| `XiriNumberService` | Locale-aware number formatting |
|
|
139
|
+
| `XiriLocalStorageService` | Typed localStorage wrapper with timestamp-based expiry |
|
|
140
|
+
| `XiriSessionStorageService` | Typed sessionStorage wrapper |
|
|
141
|
+
| `ThemeService` | Theme management (light / dark / auto) via signals |
|
|
142
|
+
|
|
143
|
+
## Pipes
|
|
144
|
+
|
|
145
|
+
- `SafehtmlPipe` (`| safeHtml`) — bypass DomSanitizer for trusted HTML. Never feed user input into this pipe.
|
|
146
|
+
|
|
147
|
+
## Claude Code support
|
|
148
|
+
|
|
149
|
+
The main repository ships a [Claude Code](https://claude.com/claude-code) skill (`xiri-ng-expert`) that teaches Claude the full API, patterns, and conventions. See the [main README](https://github.com/xiriframework/xiri-ng#claude-code-integration) for installation.
|
|
150
|
+
|
|
151
|
+
## Peer dependencies
|
|
61
152
|
|
|
62
153
|
- Angular 21+
|
|
63
154
|
- Angular Material 21+
|
|
64
|
-
-
|
|
65
|
-
- RxJS 7+
|
|
155
|
+
- Angular CDK 21+
|
|
156
|
+
- RxJS 7.8+
|
|
157
|
+
- `date-fns` 4+
|
|
158
|
+
- `@date-fns/tz` 1+
|
|
159
|
+
- `ngx-mat-select-search` 8+
|
|
160
|
+
- `material-symbols` ≥ 0.40
|
|
66
161
|
|
|
67
162
|
## License
|
|
68
163
|
|
|
@@ -3362,6 +3362,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
|
|
|
3362
3362
|
}], propDecorators: { settings: [{ type: i0.Input, args: [{ isSignal: true, alias: "settings", required: true }] }], buttonResult: [{ type: i0.Output, args: ["buttonResult"] }] } });
|
|
3363
3363
|
|
|
3364
3364
|
class XiriTableComponent {
|
|
3365
|
+
tableStateKey() {
|
|
3366
|
+
return this.options.saveStateId ? this.options.saveStateId + ':table' : null;
|
|
3367
|
+
}
|
|
3365
3368
|
// Inline edit signals delegated to inlineEdit service
|
|
3366
3369
|
get editingCell() { return this.inlineEdit.editingCell; }
|
|
3367
3370
|
get editingChipsValues() { return this.inlineEdit.editingChipsValues; }
|
|
@@ -3500,8 +3503,8 @@ class XiriTableComponent {
|
|
|
3500
3503
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
3501
3504
|
.subscribe(data => {
|
|
3502
3505
|
this._displayeddata = data || [];
|
|
3503
|
-
if (this.options.saveState && this._firstData === false) {
|
|
3504
|
-
this.sessionStorageService.set(this.
|
|
3506
|
+
if (this.options.saveState && this._firstData === false && this.tableStateKey()) {
|
|
3507
|
+
this.sessionStorageService.set(this.tableStateKey(), {
|
|
3505
3508
|
pageIndex: this.paginator()?.pageIndex,
|
|
3506
3509
|
pageSize: this.paginator()?.pageSize,
|
|
3507
3510
|
filter: this.searchText,
|
|
@@ -3630,8 +3633,8 @@ class XiriTableComponent {
|
|
|
3630
3633
|
this._alldata.set(data);
|
|
3631
3634
|
if (this._firstData) {
|
|
3632
3635
|
this._firstData = false;
|
|
3633
|
-
if (this.options.saveState) {
|
|
3634
|
-
let data = this.sessionStorageService.getTimeout(this.
|
|
3636
|
+
if (this.options.saveState && this.tableStateKey()) {
|
|
3637
|
+
let data = this.sessionStorageService.getTimeout(this.tableStateKey(), 3600);
|
|
3635
3638
|
if (!data)
|
|
3636
3639
|
return;
|
|
3637
3640
|
if (data.filter !== undefined) {
|
|
@@ -4211,20 +4214,23 @@ class XiriQueryComponent {
|
|
|
4211
4214
|
this.filterData = signal(null, ...(ngDevMode ? [{ debugName: "filterData" }] : /* istanbul ignore next */ []));
|
|
4212
4215
|
this.loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
4213
4216
|
}
|
|
4217
|
+
get effectiveSaveKey() {
|
|
4218
|
+
return this.saveStateId ? this.saveStateId + ':filter' : null;
|
|
4219
|
+
}
|
|
4214
4220
|
ngOnInit() {
|
|
4215
4221
|
let settings = this.settings();
|
|
4216
4222
|
if (settings.dyn !== undefined && settings.dyn !== null && settings.dyn.length != 0)
|
|
4217
4223
|
this.dynData.data = settings.dyn;
|
|
4218
4224
|
this.saveState = settings.saveState === undefined ? false : settings.saveState;
|
|
4219
4225
|
this.saveStateId = settings.saveStateId === undefined ? null : settings.saveStateId;
|
|
4220
|
-
this.formFields.set(this.formService.loadState(
|
|
4226
|
+
this.formFields.set(this.formService.loadState(this.effectiveSaveKey, settings.fields));
|
|
4221
4227
|
this.extra = settings.extra || null;
|
|
4222
4228
|
this.waiter.pipe(debounceTime(300), takeUntilDestroyed(this.destroyRef)).subscribe(event => {
|
|
4223
4229
|
if (event.valid) {
|
|
4224
4230
|
this.dynData.filterData = this.filterData();
|
|
4225
4231
|
this.change.emit(this.filterData());
|
|
4226
4232
|
if (this.saveState)
|
|
4227
|
-
this.formService.saveState(this.
|
|
4233
|
+
this.formService.saveState(this.effectiveSaveKey, event.value);
|
|
4228
4234
|
if (this.settings().url)
|
|
4229
4235
|
this.load();
|
|
4230
4236
|
}
|
|
@@ -4252,7 +4258,7 @@ class XiriQueryComponent {
|
|
|
4252
4258
|
this.dynData.filterData = this.filterData();
|
|
4253
4259
|
this.change.emit(this.filterData());
|
|
4254
4260
|
if (this.saveState)
|
|
4255
|
-
this.formService.saveState(this.
|
|
4261
|
+
this.formService.saveState(this.effectiveSaveKey, event.value);
|
|
4256
4262
|
if (this.settings().url)
|
|
4257
4263
|
this.load();
|
|
4258
4264
|
}
|
|
@@ -5149,11 +5155,11 @@ class XiriTimelineComponent {
|
|
|
5149
5155
|
this.settings = input.required(...(ngDevMode ? [{ debugName: "settings" }] : /* istanbul ignore next */ []));
|
|
5150
5156
|
}
|
|
5151
5157
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: XiriTimelineComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
5152
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: XiriTimelineComponent, isStandalone: true, selector: "xiri-timeline", inputs: { settings: { classPropertyName: "settings", publicName: "settings", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"timeline\">\n\t@for (item of settings().items; track $index; let last = $last) {\n\t\t<div class=\"timeline-item\" [class.last]=\"last\">\n\t\t\t<div class=\"timeline-marker\">\n\t\t\t\t<div class=\"timeline-dot\" [class]=\"item.iconColor || 'primary'\">\n\t\t\t\t\t@if (item.icon) {\n\t\t\t\t\t\t<mat-icon>{{ item.icon }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t\t
|
|
5158
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: XiriTimelineComponent, isStandalone: true, selector: "xiri-timeline", inputs: { settings: { classPropertyName: "settings", publicName: "settings", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"timeline\" [class.horizontal]=\"settings().orientation === 'horizontal'\">\n\t@for (item of settings().items; track $index; let last = $last; let first = $first) {\n\t\t<div class=\"timeline-item\" [class.last]=\"last\" [class.first]=\"first\">\n\t\t\t<div class=\"timeline-marker\">\n\t\t\t\t<div class=\"timeline-line line-before\"></div>\n\t\t\t\t<div class=\"timeline-dot\" [class]=\"item.iconColor || 'primary'\">\n\t\t\t\t\t@if (item.icon) {\n\t\t\t\t\t\t<mat-icon>{{ item.icon }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t\t<div class=\"timeline-line line-after\"></div>\n\t\t\t</div>\n\t\t\t<div class=\"timeline-content\">\n\t\t\t\t<div class=\"timeline-header\">\n\t\t\t\t\t<span class=\"timeline-title\">{{ item.title }}</span>\n\t\t\t\t\t@if (item.datetime) {\n\t\t\t\t\t\t<span class=\"timeline-datetime\">{{ item.datetime }}</span>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t\t@if (item.description && settings().orientation !== 'horizontal') {\n\t\t\t\t\t<div class=\"timeline-description\">{{ item.description }}</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t</div>\n\t}\n</div>\n", styles: [":host{display:block;container-type:inline-size}.timeline{display:flex;flex-direction:column}.timeline-item{display:flex;gap:var(--xiri-spacing-md);animation:fadeInUp var(--xiri-duration-normal, .3s) var(--xiri-easing-decelerate, cubic-bezier(0, 0, .2, 1)) both}.timeline-item:nth-child(1){animation-delay:0ms}.timeline-item:nth-child(2){animation-delay:50ms}.timeline-item:nth-child(3){animation-delay:.1s}.timeline-item:nth-child(4){animation-delay:.15s}.timeline-item:nth-child(5){animation-delay:.2s}.timeline-item:nth-child(6){animation-delay:.25s}.timeline-item:nth-child(7){animation-delay:.3s}.timeline-item:nth-child(8){animation-delay:.35s}.timeline-item:nth-child(9){animation-delay:.4s}.timeline-item:nth-child(10){animation-delay:.45s}.timeline-item:nth-child(11){animation-delay:.5s}.timeline-item:nth-child(12){animation-delay:.55s}.timeline-item:nth-child(13){animation-delay:.6s}.timeline-item:nth-child(14){animation-delay:.65s}.timeline-item:nth-child(15){animation-delay:.7s}.timeline-item:nth-child(16){animation-delay:.75s}.timeline-item:nth-child(17){animation-delay:.8s}.timeline-item:nth-child(18){animation-delay:.85s}.timeline-item:nth-child(19){animation-delay:.9s}.timeline-item:nth-child(20){animation-delay:.95s}.timeline-item .timeline-line.line-before,.timeline-item.last .timeline-line.line-after{display:none}.timeline-marker{display:flex;flex-direction:column;align-items:center;flex-shrink:0;width:2.5rem}.timeline-dot{width:2.5rem;height:2.5rem;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;background:var(--primary, #2892D9);color:var(--on-primary, white)}.timeline-dot mat-icon{font-size:1.25rem;width:1.25rem;height:1.25rem}.timeline-dot.primary{background:var(--primary)}.timeline-dot.secondary{background:var(--secondary)}.timeline-dot.tertiary{background:var(--tertiary)}.timeline-dot.accent{background:var(--secondary)}.timeline-dot.warn,.timeline-dot.error{background:var(--error)}.timeline-dot.success{background:var(--color-success)}.timeline-dot.emerald{background:#2892d9}.timeline-dot.red{background:#e74c3c}.timeline-dot.yellow{background:#f1c40f}.timeline-dot.green{background:#2ecc71}.timeline-dot.blue{background:#3498db}.timeline-dot.purple{background:#9b59b6}.timeline-dot.gray{background:#95a5a6}.timeline-dot.lightgray{background:#bdc3c7}.timeline-dot.darkgray{background:#7f8c8d}.timeline-dot.orange{background:#e67e22}.timeline-dot.white{background:#fff}.timeline-dot.black{background:#000}.timeline-dot.inherit{background:inherit}.timeline-line{width:2px;flex:1;min-height:var(--xiri-spacing-lg);background:var(--mat-sys-outline-variant, rgba(0, 0, 0, .12))}.timeline-content{padding-bottom:var(--xiri-spacing-lg);flex:1;min-width:0}.timeline-header{display:flex;align-items:baseline;gap:.75rem;flex-wrap:wrap;min-height:2.5rem;align-content:center}.timeline-title{font-weight:500;font-size:var(--xiri-font-size-body, .875rem);color:var(--mat-sys-on-surface, rgba(0, 0, 0, .87))}.timeline-datetime{font-size:var(--xiri-font-size-helper, .75rem);color:var(--mat-sys-on-surface-variant, rgba(0, 0, 0, .6))}.timeline-description{margin-top:.25rem;font-size:var(--xiri-font-size-body, .875rem);color:var(--mat-sys-on-surface-variant, rgba(0, 0, 0, .6));line-height:1.5}@container (min-width: 481px){.timeline.horizontal{flex-direction:row}.timeline.horizontal .timeline-item{flex:1;min-width:0;flex-direction:column;align-items:center;gap:var(--xiri-spacing-xs);padding-bottom:0}.timeline.horizontal .timeline-item .timeline-line.line-before{display:block}.timeline.horizontal .timeline-item.first .timeline-line.line-before{visibility:hidden}.timeline.horizontal .timeline-item.last .timeline-line.line-after{display:block;visibility:hidden}.timeline.horizontal .timeline-content,.timeline.horizontal .timeline-header{display:contents}.timeline.horizontal .timeline-title{order:-2;text-align:center;min-height:2.5em;display:flex;align-items:center;justify-content:center;padding:0 var(--xiri-spacing-xs)}.timeline.horizontal .timeline-marker{order:-1;width:100%;flex-direction:row;align-items:center;gap:0}.timeline.horizontal .timeline-datetime{order:1;text-align:center;min-height:1.5em;display:flex;align-items:center;justify-content:center}.timeline.horizontal .timeline-line{flex:1;width:auto;height:2px;min-height:0}}\n"], dependencies: [{ kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
5153
5159
|
}
|
|
5154
5160
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: XiriTimelineComponent, decorators: [{
|
|
5155
5161
|
type: Component,
|
|
5156
|
-
args: [{ selector: 'xiri-timeline', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatIcon], template: "<div class=\"timeline\">\n\t@for (item of settings().items; track $index; let last = $last) {\n\t\t<div class=\"timeline-item\" [class.last]=\"last\">\n\t\t\t<div class=\"timeline-marker\">\n\t\t\t\t<div class=\"timeline-dot\" [class]=\"item.iconColor || 'primary'\">\n\t\t\t\t\t@if (item.icon) {\n\t\t\t\t\t\t<mat-icon>{{ item.icon }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t\t
|
|
5162
|
+
args: [{ selector: 'xiri-timeline', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatIcon], template: "<div class=\"timeline\" [class.horizontal]=\"settings().orientation === 'horizontal'\">\n\t@for (item of settings().items; track $index; let last = $last; let first = $first) {\n\t\t<div class=\"timeline-item\" [class.last]=\"last\" [class.first]=\"first\">\n\t\t\t<div class=\"timeline-marker\">\n\t\t\t\t<div class=\"timeline-line line-before\"></div>\n\t\t\t\t<div class=\"timeline-dot\" [class]=\"item.iconColor || 'primary'\">\n\t\t\t\t\t@if (item.icon) {\n\t\t\t\t\t\t<mat-icon>{{ item.icon }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t\t<div class=\"timeline-line line-after\"></div>\n\t\t\t</div>\n\t\t\t<div class=\"timeline-content\">\n\t\t\t\t<div class=\"timeline-header\">\n\t\t\t\t\t<span class=\"timeline-title\">{{ item.title }}</span>\n\t\t\t\t\t@if (item.datetime) {\n\t\t\t\t\t\t<span class=\"timeline-datetime\">{{ item.datetime }}</span>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t\t@if (item.description && settings().orientation !== 'horizontal') {\n\t\t\t\t\t<div class=\"timeline-description\">{{ item.description }}</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t</div>\n\t}\n</div>\n", styles: [":host{display:block;container-type:inline-size}.timeline{display:flex;flex-direction:column}.timeline-item{display:flex;gap:var(--xiri-spacing-md);animation:fadeInUp var(--xiri-duration-normal, .3s) var(--xiri-easing-decelerate, cubic-bezier(0, 0, .2, 1)) both}.timeline-item:nth-child(1){animation-delay:0ms}.timeline-item:nth-child(2){animation-delay:50ms}.timeline-item:nth-child(3){animation-delay:.1s}.timeline-item:nth-child(4){animation-delay:.15s}.timeline-item:nth-child(5){animation-delay:.2s}.timeline-item:nth-child(6){animation-delay:.25s}.timeline-item:nth-child(7){animation-delay:.3s}.timeline-item:nth-child(8){animation-delay:.35s}.timeline-item:nth-child(9){animation-delay:.4s}.timeline-item:nth-child(10){animation-delay:.45s}.timeline-item:nth-child(11){animation-delay:.5s}.timeline-item:nth-child(12){animation-delay:.55s}.timeline-item:nth-child(13){animation-delay:.6s}.timeline-item:nth-child(14){animation-delay:.65s}.timeline-item:nth-child(15){animation-delay:.7s}.timeline-item:nth-child(16){animation-delay:.75s}.timeline-item:nth-child(17){animation-delay:.8s}.timeline-item:nth-child(18){animation-delay:.85s}.timeline-item:nth-child(19){animation-delay:.9s}.timeline-item:nth-child(20){animation-delay:.95s}.timeline-item .timeline-line.line-before,.timeline-item.last .timeline-line.line-after{display:none}.timeline-marker{display:flex;flex-direction:column;align-items:center;flex-shrink:0;width:2.5rem}.timeline-dot{width:2.5rem;height:2.5rem;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;background:var(--primary, #2892D9);color:var(--on-primary, white)}.timeline-dot mat-icon{font-size:1.25rem;width:1.25rem;height:1.25rem}.timeline-dot.primary{background:var(--primary)}.timeline-dot.secondary{background:var(--secondary)}.timeline-dot.tertiary{background:var(--tertiary)}.timeline-dot.accent{background:var(--secondary)}.timeline-dot.warn,.timeline-dot.error{background:var(--error)}.timeline-dot.success{background:var(--color-success)}.timeline-dot.emerald{background:#2892d9}.timeline-dot.red{background:#e74c3c}.timeline-dot.yellow{background:#f1c40f}.timeline-dot.green{background:#2ecc71}.timeline-dot.blue{background:#3498db}.timeline-dot.purple{background:#9b59b6}.timeline-dot.gray{background:#95a5a6}.timeline-dot.lightgray{background:#bdc3c7}.timeline-dot.darkgray{background:#7f8c8d}.timeline-dot.orange{background:#e67e22}.timeline-dot.white{background:#fff}.timeline-dot.black{background:#000}.timeline-dot.inherit{background:inherit}.timeline-line{width:2px;flex:1;min-height:var(--xiri-spacing-lg);background:var(--mat-sys-outline-variant, rgba(0, 0, 0, .12))}.timeline-content{padding-bottom:var(--xiri-spacing-lg);flex:1;min-width:0}.timeline-header{display:flex;align-items:baseline;gap:.75rem;flex-wrap:wrap;min-height:2.5rem;align-content:center}.timeline-title{font-weight:500;font-size:var(--xiri-font-size-body, .875rem);color:var(--mat-sys-on-surface, rgba(0, 0, 0, .87))}.timeline-datetime{font-size:var(--xiri-font-size-helper, .75rem);color:var(--mat-sys-on-surface-variant, rgba(0, 0, 0, .6))}.timeline-description{margin-top:.25rem;font-size:var(--xiri-font-size-body, .875rem);color:var(--mat-sys-on-surface-variant, rgba(0, 0, 0, .6));line-height:1.5}@container (min-width: 481px){.timeline.horizontal{flex-direction:row}.timeline.horizontal .timeline-item{flex:1;min-width:0;flex-direction:column;align-items:center;gap:var(--xiri-spacing-xs);padding-bottom:0}.timeline.horizontal .timeline-item .timeline-line.line-before{display:block}.timeline.horizontal .timeline-item.first .timeline-line.line-before{visibility:hidden}.timeline.horizontal .timeline-item.last .timeline-line.line-after{display:block;visibility:hidden}.timeline.horizontal .timeline-content,.timeline.horizontal .timeline-header{display:contents}.timeline.horizontal .timeline-title{order:-2;text-align:center;min-height:2.5em;display:flex;align-items:center;justify-content:center;padding:0 var(--xiri-spacing-xs)}.timeline.horizontal .timeline-marker{order:-1;width:100%;flex-direction:row;align-items:center;gap:0}.timeline.horizontal .timeline-datetime{order:1;text-align:center;min-height:1.5em;display:flex;align-items:center;justify-content:center}.timeline.horizontal .timeline-line{flex:1;width:auto;height:2px;min-height:0}}\n"] }]
|
|
5157
5163
|
}], propDecorators: { settings: [{ type: i0.Input, args: [{ isSignal: true, alias: "settings", required: true }] }] } });
|
|
5158
5164
|
|
|
5159
5165
|
var timeline_component = /*#__PURE__*/Object.freeze({
|