@skeletonizer/angular 2.1.50 → 2.2.0
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 +27 -19
- package/dist/skeletonizer/fesm2022/skeletonizer-angular.mjs +57 -31
- package/dist/skeletonizer/fesm2022/skeletonizer-angular.mjs.map +1 -1
- package/dist/skeletonizer/types/skeletonizer-angular.d.ts +61 -0
- package/package.json +30 -30
- package/dist/skeletonizer/index.d.ts +0 -17
package/README.md
CHANGED
|
@@ -45,9 +45,16 @@ You can use Skeletonizer either in a standalone component or in a component that
|
|
|
45
45
|
If you wish to use Skeletonizer in a standalone component, you need to add `SkeletonizerSkeletonComponent` in the imports of the component.
|
|
46
46
|
The usage in a component that is a part of a module is the same as the standalone component, but you need to add `SkeletonizerSkeletonComponent` in the imports of the **module** where the component is declared.
|
|
47
47
|
|
|
48
|
-
Every component that uses Skeletonizer should extend `SkeletonAbstractComponent`, which is available in `@skeletonizer/
|
|
48
|
+
Every component that uses Skeletonizer should extend `SkeletonAbstractComponent`, which is available in `@skeletonizer/angular`.
|
|
49
49
|
The `SkeletonAbstractComponent` requires you to pass a type argument that represents the data model of the **part(s) of the component that you intend to skeletonize**.
|
|
50
50
|
It also requires you to implement the `skeletonConfig` (type validated against the type argument you pass to `SkeletonAbstractComponent`) and `showSkeleton` properties which must be passed to the `SkeletonizerSkeletonComponent` as inputs.
|
|
51
|
+
|
|
52
|
+
**Note:** In Angular, both `skeletonConfig` and `showSkeleton` should be `Signal` types to take advantage of Angular's reactive primitives:
|
|
53
|
+
- `skeletonConfig` should be a `Signal<TSchemaConfig<T>>` (can be `WritableSignal` if you need to modify it)
|
|
54
|
+
- `showSkeleton` should be a `WritableSignal<boolean>` or `Signal<boolean>`. Use `.set()` to update its value
|
|
55
|
+
- Call them as functions in templates: `skeletonConfig()` and `showSkeleton()`
|
|
56
|
+
Alternatively, you can import `SkeletonAbstractComponent` from `@skeletonizer/utils` and use non-reactive types, but this is not recommended.
|
|
57
|
+
|
|
51
58
|
By extending the `SkeletonAbstractComponent`, you also get access to the `proxy` method via which you can (type) safely access props and methods **within the skeletonized part of the current component**.
|
|
52
59
|
|
|
53
60
|
In the skeletonized part of the template, you **must** access the data through the `proxy(context)` method.
|
|
@@ -61,9 +68,9 @@ For more details about the `skeletonConfig` property, see the [TSchemaConfig](/p
|
|
|
61
68
|
|
|
62
69
|
|
|
63
70
|
```typescript
|
|
64
|
-
import { Component } from '@angular/core';
|
|
65
|
-
import { SkeletonizerSkeletonComponent } from '@skeletonizer/angular';
|
|
66
|
-
import { SchemaItem,
|
|
71
|
+
import { Component, signal, Signal, WritableSignal } from '@angular/core';
|
|
72
|
+
import { SkeletonizerSkeletonComponent, SkeletonAbstractComponent } from '@skeletonizer/angular';
|
|
73
|
+
import { SchemaItem, TSchemaConfig } from '@skeletonizer/utils';
|
|
67
74
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
68
75
|
|
|
69
76
|
interface IResource {
|
|
@@ -86,7 +93,7 @@ type TSkeletonizedPart = Pick<AppComponent, 'resources' | 'otherPropWeWantToUseI
|
|
|
86
93
|
template: `
|
|
87
94
|
<h2>{{ pageTitle }}</h2>
|
|
88
95
|
|
|
89
|
-
<skeletonizer-skeleton [showSkeleton]="showSkeleton" [config]="skeletonConfig" [scope]="{ resources, otherPropWeWantToUseInSkeletonizedPart }">
|
|
96
|
+
<skeletonizer-skeleton [showSkeleton]="showSkeleton()" [config]="skeletonConfig()" [scope]="{ resources, otherPropWeWantToUseInSkeletonizedPart }">
|
|
90
97
|
<ng-template let-context>
|
|
91
98
|
<span>{{ proxy(context).otherPropWeWantToUseInSkeletonizedPart }}</span>
|
|
92
99
|
|
|
@@ -109,9 +116,9 @@ export class MyComponent extends SkeletonAbstractComponent<TSkeletonizedPart> im
|
|
|
109
116
|
public otherPropWeWantToUseInSkeletonizedPart: string = 'angular';
|
|
110
117
|
|
|
111
118
|
public resources: IResource[] = [];
|
|
112
|
-
public showSkeleton: boolean = true;
|
|
119
|
+
public showSkeleton: WritableSignal<boolean> = signal(true);
|
|
113
120
|
|
|
114
|
-
public readonly skeletonConfig: TSchemaConfig<TSkeletonizedPart
|
|
121
|
+
public readonly skeletonConfig: Signal<TSchemaConfig<TSkeletonizedPart>> = signal({
|
|
115
122
|
repeat: 1,
|
|
116
123
|
schemaGenerator: () => ({
|
|
117
124
|
otherPropWeWantToUseInSkeletonizedPart: new SchemaItem<string>().words(3),
|
|
@@ -122,7 +129,7 @@ export class MyComponent extends SkeletonAbstractComponent<TSkeletonizedPart> im
|
|
|
122
129
|
svg: new SchemaItem().identical(loadingSvg),
|
|
123
130
|
})),
|
|
124
131
|
}),
|
|
125
|
-
};
|
|
132
|
+
});
|
|
126
133
|
|
|
127
134
|
public constructor(
|
|
128
135
|
public readonly sanitizer: DomSanitizer,
|
|
@@ -148,7 +155,7 @@ export class MyComponent extends SkeletonAbstractComponent<TSkeletonizedPart> im
|
|
|
148
155
|
|
|
149
156
|
this.otherPropWeWantToUseInSkeletonizedPart = 'loaded title'
|
|
150
157
|
|
|
151
|
-
this.showSkeleton
|
|
158
|
+
this.showSkeleton.set(false);
|
|
152
159
|
}, Math.max(3_000, Math.random() * 10_000));
|
|
153
160
|
}
|
|
154
161
|
}
|
|
@@ -158,9 +165,9 @@ You can also skeletonize multiple independent parts (ie. parts for which the dat
|
|
|
158
165
|
You can also provide separate config and scope for each `skeletonizer-skeleton` component if needed, although it is recommended that you do not extend `SkeletonAbstractComponent` in this case, and you will need to provide your own (separate) `proxy`-like methods for each of the skeletonized parts of the component to maintain the same level of type safety in the template.
|
|
159
166
|
|
|
160
167
|
```typescript
|
|
161
|
-
import { Component } from '@angular/core';
|
|
162
|
-
import { SkeletonizerSkeletonComponent } from '@skeletonizer/angular';
|
|
163
|
-
import { SchemaItem,
|
|
168
|
+
import { Component, signal, Signal, WritableSignal } from '@angular/core';
|
|
169
|
+
import { SkeletonizerSkeletonComponent, SkeletonAbstractComponent } from '@skeletonizer/angular';
|
|
170
|
+
import { SchemaItem, TSchemaConfig } from '@skeletonizer/utils';
|
|
164
171
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
165
172
|
|
|
166
173
|
interface IResource {
|
|
@@ -181,7 +188,7 @@ type TSkeletonizedPart = Pick<AppComponent, 'resources' | 'otherPropWeWantToUseI
|
|
|
181
188
|
template: `
|
|
182
189
|
<h2>{{ pageTitle }}</h2>
|
|
183
190
|
|
|
184
|
-
<skeletonizer-skeleton [showSkeleton]="showSkeleton" [config]="skeletonConfig" [scope]="{ resources, otherPropWeWantToUseInSkeletonizedPart }">
|
|
191
|
+
<skeletonizer-skeleton [showSkeleton]="showSkeleton()" [config]="skeletonConfig()" [scope]="{ resources, otherPropWeWantToUseInSkeletonizedPart }">
|
|
185
192
|
<ng-template let-context>
|
|
186
193
|
@for (resource of proxy(context).resources; track $index) {
|
|
187
194
|
<a target="_blank" rel="noopener" [href]="resource.link">
|
|
@@ -192,7 +199,7 @@ type TSkeletonizedPart = Pick<AppComponent, 'resources' | 'otherPropWeWantToUseI
|
|
|
192
199
|
</ng-template>
|
|
193
200
|
</skeletonizer-skeleton>
|
|
194
201
|
|
|
195
|
-
<skeletonizer-skeleton [showSkeleton]="showOtherSkeleton" [config]="skeletonConfig" [scope]="{ resources, otherPropWeWantToUseInSkeletonizedPart }">
|
|
202
|
+
<skeletonizer-skeleton [showSkeleton]="showOtherSkeleton()" [config]="skeletonConfig()" [scope]="{ resources, otherPropWeWantToUseInSkeletonizedPart }">
|
|
196
203
|
<ng-template let-context>
|
|
197
204
|
<span>{{ proxy(context).otherPropWeWantToUseInSkeletonizedPart }}</span>
|
|
198
205
|
</ng-template>
|
|
@@ -208,9 +215,10 @@ export class MyComponent extends SkeletonAbstractComponent<TSkeletonizedPart> im
|
|
|
208
215
|
public otherPropWeWantToUseInSkeletonizedPart: string = 'angular';
|
|
209
216
|
|
|
210
217
|
public resources: IResource[] = [];
|
|
211
|
-
public showSkeleton: boolean = true;
|
|
218
|
+
public showSkeleton: WritableSignal<boolean> = signal(true);
|
|
219
|
+
public showOtherSkeleton: WritableSignal<boolean> = signal(true);
|
|
212
220
|
|
|
213
|
-
public readonly skeletonConfig: TSchemaConfig<TSkeletonizedPart
|
|
221
|
+
public readonly skeletonConfig: Signal<TSchemaConfig<TSkeletonizedPart>> = signal({
|
|
214
222
|
repeat: 1,
|
|
215
223
|
schemaGenerator: () => ({
|
|
216
224
|
otherPropWeWantToUseInSkeletonizedPart: new SchemaItem<string>().words(3),
|
|
@@ -221,7 +229,7 @@ export class MyComponent extends SkeletonAbstractComponent<TSkeletonizedPart> im
|
|
|
221
229
|
svg: new SchemaItem().identical(loadingSvg),
|
|
222
230
|
})),
|
|
223
231
|
}),
|
|
224
|
-
};
|
|
232
|
+
});
|
|
225
233
|
|
|
226
234
|
public constructor(
|
|
227
235
|
public readonly sanitizer: DomSanitizer,
|
|
@@ -244,11 +252,11 @@ export class MyComponent extends SkeletonAbstractComponent<TSkeletonizedPart> im
|
|
|
244
252
|
},
|
|
245
253
|
];
|
|
246
254
|
|
|
247
|
-
this.showSkeleton
|
|
255
|
+
this.showSkeleton.set(false);
|
|
248
256
|
}, Math.max(3_000, Math.random() * 10_000));
|
|
249
257
|
|
|
250
258
|
setTimeout(() => {
|
|
251
|
-
this.showOtherSkeleton
|
|
259
|
+
this.showOtherSkeleton.set(false);
|
|
252
260
|
}, Math.max(6_000, Math.random() * 10_000));
|
|
253
261
|
}
|
|
254
262
|
}
|
|
@@ -1,58 +1,84 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, ElementRef,
|
|
3
|
-
import { SkeletonDirective, SkeletonAdapterComponent } from '@skeletonizer/utils';
|
|
2
|
+
import { input, inject, ElementRef, Directive, effect, TemplateRef, ContentChild, ViewEncapsulation, Component } from '@angular/core';
|
|
3
|
+
import { SkeletonDirective, SkeletonAdapterComponent, Schema } from '@skeletonizer/utils';
|
|
4
4
|
import { NgTemplateOutlet } from '@angular/common';
|
|
5
5
|
|
|
6
6
|
class SkeletonizeDirective {
|
|
7
7
|
constructor() {
|
|
8
|
+
this.colorSchema = input(undefined, { ...(ngDevMode ? { debugName: "colorSchema" } : {}), alias: 'skeletonize' });
|
|
8
9
|
this.el = inject(ElementRef);
|
|
9
10
|
}
|
|
10
11
|
ngAfterViewInit() {
|
|
11
|
-
SkeletonDirective.skeletonizeProjectedTemplate(this.el.nativeElement, this.colorSchema);
|
|
12
|
+
SkeletonDirective.skeletonizeProjectedTemplate(this.el.nativeElement, this.colorSchema());
|
|
12
13
|
}
|
|
13
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
14
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "
|
|
14
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: SkeletonizeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
15
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.0", type: SkeletonizeDirective, isStandalone: true, selector: "[skeletonize]", inputs: { colorSchema: { classPropertyName: "colorSchema", publicName: "skeletonize", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
15
16
|
}
|
|
16
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
17
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: SkeletonizeDirective, decorators: [{
|
|
17
18
|
type: Directive,
|
|
18
19
|
args: [{
|
|
19
20
|
selector: '[skeletonize]',
|
|
20
21
|
standalone: true,
|
|
21
22
|
}]
|
|
22
|
-
}], propDecorators: { colorSchema: [{
|
|
23
|
-
type: Input,
|
|
24
|
-
args: [{ alias: 'skeletonize' }]
|
|
25
|
-
}] } });
|
|
23
|
+
}], propDecorators: { colorSchema: [{ type: i0.Input, args: [{ isSignal: true, alias: "skeletonize", required: false }] }] } });
|
|
26
24
|
|
|
27
25
|
class SkeletonizerSkeletonComponent extends SkeletonAdapterComponent {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this.
|
|
26
|
+
constructor() {
|
|
27
|
+
super();
|
|
28
|
+
this.showSkeleton = input.required(...(ngDevMode ? [{ debugName: "showSkeleton" }] : []));
|
|
29
|
+
this.scope = input.required(...(ngDevMode ? [{ debugName: "scope" }] : []));
|
|
30
|
+
this.configInput = input.required({ ...(ngDevMode ? { debugName: "configInput" } : {}), alias: 'config' });
|
|
31
|
+
this.colorSchema = input(undefined, ...(ngDevMode ? [{ debugName: "colorSchema" }] : []));
|
|
32
|
+
effect(() => {
|
|
33
|
+
this.config = this.configInput();
|
|
34
|
+
this.setupModels();
|
|
35
|
+
});
|
|
31
36
|
}
|
|
32
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
33
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
37
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: SkeletonizerSkeletonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
38
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: SkeletonizerSkeletonComponent, isStandalone: true, selector: "skeletonizer-skeleton", inputs: { showSkeleton: { classPropertyName: "showSkeleton", publicName: "showSkeleton", isSignal: true, isRequired: true, transformFunction: null }, scope: { classPropertyName: "scope", publicName: "scope", isSignal: true, isRequired: true, transformFunction: null }, configInput: { classPropertyName: "configInput", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, colorSchema: { classPropertyName: "colorSchema", publicName: "colorSchema", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "templateRef", first: true, predicate: TemplateRef, descendants: true }], usesInheritance: true, ngImport: i0, template: "@if (showSkeleton()) {\n @for (model of viewModels; track model.uuid) {\n <div [skeletonize]=\"colorSchema()\">\n <ng-container *ngTemplateOutlet=\"templateRef; context: { $implicit: model }\"></ng-container>\n </div>\n }\n} @else {\n <ng-container *ngTemplateOutlet=\"templateRef; context: { $implicit: scope() }\"></ng-container>\n}\n", styles: ["[data-skeletonizer=wrapper-element]{--skeletonizer-text-background: rgba(0, 0, 0, .2);display:contents;filter:grayscale(100%);pointer-events:none}[data-skeletonizer=wrapper-element] *{pointer-events:none}[data-skeletonizer=wrapper-element] [data-skeletonizer=text]{animation:text-animation 2s infinite ease-in-out;background:var(--skeletonizer-primary-color);border-radius:50px;color:#0000!important}@keyframes text-animation{0%{background:var(--skeletonizer-primary-color)}50%{background:var(--skeletonizer-secondary-color)}to{background:var(--skeletonizer-primary-color)}}skeletonizer-skeleton{display:contents}\n"], dependencies: [{ kind: "directive", type: SkeletonizeDirective, selector: "[skeletonize]", inputs: ["skeletonize"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
34
39
|
}
|
|
35
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
40
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: SkeletonizerSkeletonComponent, decorators: [{
|
|
36
41
|
type: Component,
|
|
37
42
|
args: [{ selector: 'skeletonizer-skeleton', encapsulation: ViewEncapsulation.None, standalone: true, imports: [
|
|
38
43
|
SkeletonizeDirective,
|
|
39
44
|
NgTemplateOutlet,
|
|
40
|
-
], template: "@if (showSkeleton) {\n @for (model of viewModels; track model.uuid) {\n <div [skeletonize]=\"colorSchema\">\n <ng-container *ngTemplateOutlet=\"templateRef; context: { $implicit: model }\"></ng-container>\n </div>\n }\n} @else {\n <ng-container *ngTemplateOutlet=\"templateRef; context: { $implicit: scope }\"></ng-container>\n}\n", styles: ["[data-skeletonizer=wrapper-element]{--skeletonizer-text-background: rgba(0, 0, 0, .2);display:contents;filter:grayscale(100%);pointer-events:none}[data-skeletonizer=wrapper-element] *{pointer-events:none}[data-skeletonizer=wrapper-element] [data-skeletonizer=text]{animation:text-animation 2s infinite ease-in-out;background:var(--skeletonizer-primary-color);border-radius:50px;color:#0000!important}@keyframes text-animation{0%{background:var(--skeletonizer-primary-color)}50%{background:var(--skeletonizer-secondary-color)}to{background:var(--skeletonizer-primary-color)}}skeletonizer-skeleton{display:contents}\n"] }]
|
|
41
|
-
}], propDecorators: { templateRef: [{
|
|
45
|
+
], template: "@if (showSkeleton()) {\n @for (model of viewModels; track model.uuid) {\n <div [skeletonize]=\"colorSchema()\">\n <ng-container *ngTemplateOutlet=\"templateRef; context: { $implicit: model }\"></ng-container>\n </div>\n }\n} @else {\n <ng-container *ngTemplateOutlet=\"templateRef; context: { $implicit: scope() }\"></ng-container>\n}\n", styles: ["[data-skeletonizer=wrapper-element]{--skeletonizer-text-background: rgba(0, 0, 0, .2);display:contents;filter:grayscale(100%);pointer-events:none}[data-skeletonizer=wrapper-element] *{pointer-events:none}[data-skeletonizer=wrapper-element] [data-skeletonizer=text]{animation:text-animation 2s infinite ease-in-out;background:var(--skeletonizer-primary-color);border-radius:50px;color:#0000!important}@keyframes text-animation{0%{background:var(--skeletonizer-primary-color)}50%{background:var(--skeletonizer-secondary-color)}to{background:var(--skeletonizer-primary-color)}}skeletonizer-skeleton{display:contents}\n"] }]
|
|
46
|
+
}], ctorParameters: () => [], propDecorators: { templateRef: [{
|
|
42
47
|
type: ContentChild,
|
|
43
48
|
args: [TemplateRef]
|
|
44
|
-
}], showSkeleton: [{
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
}], showSkeleton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSkeleton", required: true }] }], scope: [{ type: i0.Input, args: [{ isSignal: true, alias: "scope", required: true }] }], configInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], colorSchema: [{ type: i0.Input, args: [{ isSignal: true, alias: "colorSchema", required: false }] }] } });
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Angular-specific abstract component for skeletonizer.
|
|
53
|
+
* Every Angular component that uses Skeletonizer should extend this class.
|
|
54
|
+
*
|
|
55
|
+
* This version uses Angular signals for reactive state management.
|
|
56
|
+
*
|
|
57
|
+
* @template T - The type of the data model for the skeletonized part of the component
|
|
58
|
+
*/
|
|
59
|
+
class SkeletonAbstractComponent {
|
|
60
|
+
/**
|
|
61
|
+
* Proxy method for type-safe access to properties/methods within the skeletonized part of the view.
|
|
62
|
+
*
|
|
63
|
+
* When accessing component properties/methods from within the skeletonized template,
|
|
64
|
+
* they should be accessed via this proxy method. This ensures:
|
|
65
|
+
* - All accessed properties/methods are part of the skeleton schema
|
|
66
|
+
* - TypeScript understands the correct type of each property/method
|
|
67
|
+
*
|
|
68
|
+
* @param scope - Either the skeleton schema or the actual scope object
|
|
69
|
+
* @returns The unwrapped scope object with proper typing
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```html
|
|
73
|
+
* <ng-template let-context>
|
|
74
|
+
* <div>{{ proxy(context).userName }}</div>
|
|
75
|
+
* </ng-template>
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
proxy(scope) {
|
|
79
|
+
return scope instanceof Schema ? scope.value : scope;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
56
82
|
|
|
57
83
|
/*
|
|
58
84
|
* Public API Surface of skeletonizer
|
|
@@ -62,5 +88,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImpor
|
|
|
62
88
|
* Generated bundle index. Do not edit.
|
|
63
89
|
*/
|
|
64
90
|
|
|
65
|
-
export { SkeletonizerSkeletonComponent };
|
|
91
|
+
export { SkeletonAbstractComponent, SkeletonizerSkeletonComponent };
|
|
66
92
|
//# sourceMappingURL=skeletonizer-angular.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skeletonizer-angular.mjs","sources":["../../../projects/skeletonizer/src/lib/skeletonize.directive.ts","../../../projects/skeletonizer/src/lib/skeletonizer.skeleton.component.ts","../../../projects/skeletonizer/src/lib/skeletonizer.skeleton.component.html","../../../projects/skeletonizer/src/public-api.ts","../../../projects/skeletonizer/src/skeletonizer-angular.ts"],"sourcesContent":["import { AfterViewInit, Directive, ElementRef, inject,
|
|
1
|
+
{"version":3,"file":"skeletonizer-angular.mjs","sources":["../../../projects/skeletonizer/src/lib/skeletonize.directive.ts","../../../projects/skeletonizer/src/lib/skeletonizer.skeleton.component.ts","../../../projects/skeletonizer/src/lib/skeletonizer.skeleton.component.html","../../../projects/skeletonizer/src/lib/skeleton-abstract.component.ts","../../../projects/skeletonizer/src/public-api.ts","../../../projects/skeletonizer/src/skeletonizer-angular.ts"],"sourcesContent":["import { AfterViewInit, Directive, ElementRef, inject, input, InputSignal } from '@angular/core';\nimport { ISkeletonizerColorSchema, SkeletonDirective } from '@skeletonizer/utils';\n\n@Directive({\n selector: '[skeletonize]',\n standalone: true,\n})\nexport class SkeletonizeDirective implements AfterViewInit {\n public readonly colorSchema: InputSignal<ISkeletonizerColorSchema | undefined> = input<ISkeletonizerColorSchema | undefined>(\n undefined,\n { alias: 'skeletonize' },\n );\n\n private readonly el: ElementRef = inject(ElementRef);\n\n public ngAfterViewInit(): void {\n SkeletonDirective.skeletonizeProjectedTemplate(this.el.nativeElement, this.colorSchema());\n }\n}\n","import { Component, ContentChild, effect, input, InputSignal, TemplateRef, ViewEncapsulation } from '@angular/core';\nimport { ISkeletonizerColorSchema, Schema, SkeletonAdapterComponent, TSchemaConfig } from '@skeletonizer/utils';\nimport { SkeletonizeDirective } from './skeletonize.directive';\nimport { NgTemplateOutlet } from '@angular/common';\n\n@Component({\n selector: 'skeletonizer-skeleton',\n templateUrl: './skeletonizer.skeleton.component.html',\n styleUrls: ['./skeletonizer.skeleton.component.scss'],\n encapsulation: ViewEncapsulation.None,\n standalone: true,\n imports: [\n SkeletonizeDirective,\n NgTemplateOutlet,\n ],\n})\nexport class SkeletonizerSkeletonComponent<T extends object, Scope extends T> extends SkeletonAdapterComponent<T> {\n @ContentChild(TemplateRef) public readonly templateRef!: TemplateRef<{ $implicit: Schema<T> | Scope }>;\n\n public readonly showSkeleton: InputSignal<boolean> = input.required<boolean>();\n public readonly scope: InputSignal<Scope> = input.required<Scope>();\n public readonly configInput: InputSignal<TSchemaConfig<T>> = input.required<TSchemaConfig<T>>({ alias: 'config' });\n public readonly colorSchema: InputSignal<ISkeletonizerColorSchema | undefined> = input<ISkeletonizerColorSchema | undefined>(undefined);\n\n public constructor() {\n super();\n\n effect(() => {\n this.config = this.configInput();\n this.setupModels();\n });\n }\n}\n","@if (showSkeleton()) {\n @for (model of viewModels; track model.uuid) {\n <div [skeletonize]=\"colorSchema()\">\n <ng-container *ngTemplateOutlet=\"templateRef; context: { $implicit: model }\"></ng-container>\n </div>\n }\n} @else {\n <ng-container *ngTemplateOutlet=\"templateRef; context: { $implicit: scope() }\"></ng-container>\n}\n","import { Signal } from '@angular/core';\nimport { Schema, TSchemaConfig, SkeletonAbstractComponent as BaseAbstract } from '@skeletonizer/utils';\n\ntype TAbstractComponent<T extends object> = {\n [K in keyof BaseAbstract<T>]: K extends 'proxy' ? BaseAbstract<T>[K] : Signal<BaseAbstract<T>[K]>;\n};\n\n/**\n * Angular-specific abstract component for skeletonizer.\n * Every Angular component that uses Skeletonizer should extend this class.\n *\n * This version uses Angular signals for reactive state management.\n *\n * @template T - The type of the data model for the skeletonized part of the component\n */\nexport abstract class SkeletonAbstractComponent<T extends object> implements TAbstractComponent<T> {\n /**\n * Configuration for the skeleton schema.\n * Defines how the skeleton data should be generated.\n */\n public abstract skeletonConfig: Signal<TSchemaConfig<T>>;\n\n /**\n * Signal that controls whether the skeleton is shown or the actual content.\n * Use `.set()` to update the value if using WritableSignal.\n */\n public abstract showSkeleton: Signal<boolean>;\n\n /**\n * Proxy method for type-safe access to properties/methods within the skeletonized part of the view.\n *\n * When accessing component properties/methods from within the skeletonized template,\n * they should be accessed via this proxy method. This ensures:\n * - All accessed properties/methods are part of the skeleton schema\n * - TypeScript understands the correct type of each property/method\n *\n * @param scope - Either the skeleton schema or the actual scope object\n * @returns The unwrapped scope object with proper typing\n *\n * @example\n * ```html\n * <ng-template let-context>\n * <div>{{ proxy(context).userName }}</div>\n * </ng-template>\n * ```\n */\n public proxy(scope: T | Schema<T>): T {\n return scope instanceof Schema ? scope.value : scope;\n }\n}\n","/*\n * Public API Surface of skeletonizer\n */\n\nexport * from './lib/skeletonizer.skeleton.component';\nexport * from './lib/skeleton-abstract.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { TemplateRef, InputSignal, Signal } from '@angular/core';
|
|
3
|
+
import { SkeletonAdapterComponent, Schema, TSchemaConfig, ISkeletonizerColorSchema, SkeletonAbstractComponent as SkeletonAbstractComponent$1 } from '@skeletonizer/utils';
|
|
4
|
+
|
|
5
|
+
declare class SkeletonizerSkeletonComponent<T extends object, Scope extends T> extends SkeletonAdapterComponent<T> {
|
|
6
|
+
readonly templateRef: TemplateRef<{
|
|
7
|
+
$implicit: Schema<T> | Scope;
|
|
8
|
+
}>;
|
|
9
|
+
readonly showSkeleton: InputSignal<boolean>;
|
|
10
|
+
readonly scope: InputSignal<Scope>;
|
|
11
|
+
readonly configInput: InputSignal<TSchemaConfig<T>>;
|
|
12
|
+
readonly colorSchema: InputSignal<ISkeletonizerColorSchema | undefined>;
|
|
13
|
+
constructor();
|
|
14
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SkeletonizerSkeletonComponent<any, any>, never>;
|
|
15
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SkeletonizerSkeletonComponent<any, any>, "skeletonizer-skeleton", never, { "showSkeleton": { "alias": "showSkeleton"; "required": true; "isSignal": true; }; "scope": { "alias": "scope"; "required": true; "isSignal": true; }; "configInput": { "alias": "config"; "required": true; "isSignal": true; }; "colorSchema": { "alias": "colorSchema"; "required": false; "isSignal": true; }; }, {}, ["templateRef"], never, true, never>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type TAbstractComponent<T extends object> = {
|
|
19
|
+
[K in keyof SkeletonAbstractComponent$1<T>]: K extends 'proxy' ? SkeletonAbstractComponent$1<T>[K] : Signal<SkeletonAbstractComponent$1<T>[K]>;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Angular-specific abstract component for skeletonizer.
|
|
23
|
+
* Every Angular component that uses Skeletonizer should extend this class.
|
|
24
|
+
*
|
|
25
|
+
* This version uses Angular signals for reactive state management.
|
|
26
|
+
*
|
|
27
|
+
* @template T - The type of the data model for the skeletonized part of the component
|
|
28
|
+
*/
|
|
29
|
+
declare abstract class SkeletonAbstractComponent<T extends object> implements TAbstractComponent<T> {
|
|
30
|
+
/**
|
|
31
|
+
* Configuration for the skeleton schema.
|
|
32
|
+
* Defines how the skeleton data should be generated.
|
|
33
|
+
*/
|
|
34
|
+
abstract skeletonConfig: Signal<TSchemaConfig<T>>;
|
|
35
|
+
/**
|
|
36
|
+
* Signal that controls whether the skeleton is shown or the actual content.
|
|
37
|
+
* Use `.set()` to update the value if using WritableSignal.
|
|
38
|
+
*/
|
|
39
|
+
abstract showSkeleton: Signal<boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* Proxy method for type-safe access to properties/methods within the skeletonized part of the view.
|
|
42
|
+
*
|
|
43
|
+
* When accessing component properties/methods from within the skeletonized template,
|
|
44
|
+
* they should be accessed via this proxy method. This ensures:
|
|
45
|
+
* - All accessed properties/methods are part of the skeleton schema
|
|
46
|
+
* - TypeScript understands the correct type of each property/method
|
|
47
|
+
*
|
|
48
|
+
* @param scope - Either the skeleton schema or the actual scope object
|
|
49
|
+
* @returns The unwrapped scope object with proper typing
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```html
|
|
53
|
+
* <ng-template let-context>
|
|
54
|
+
* <div>{{ proxy(context).userName }}</div>
|
|
55
|
+
* </ng-template>
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
proxy(scope: T | Schema<T>): T;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { SkeletonAbstractComponent, SkeletonizerSkeletonComponent };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skeletonizer/angular",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "The way to skeletonize your Angular components",
|
|
5
5
|
"author": "Luka Varga",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,42 +54,42 @@
|
|
|
54
54
|
"**/*.{ts,js}": "eslint --fix"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"@angular/animations": "^
|
|
58
|
-
"@angular/common": "^
|
|
59
|
-
"@angular/compiler": "^
|
|
60
|
-
"@angular/core": "^
|
|
61
|
-
"@angular/forms": "^
|
|
62
|
-
"@angular/platform-browser": "^
|
|
63
|
-
"@angular/platform-browser-dynamic": "^
|
|
64
|
-
"@angular/router": "^
|
|
65
|
-
"@skeletonizer/utils": "^2.
|
|
66
|
-
"rxjs": "~7.8.
|
|
57
|
+
"@angular/animations": "^21.1.0",
|
|
58
|
+
"@angular/common": "^21.1.0",
|
|
59
|
+
"@angular/compiler": "^21.1.0",
|
|
60
|
+
"@angular/core": "^21.1.0",
|
|
61
|
+
"@angular/forms": "^21.1.0",
|
|
62
|
+
"@angular/platform-browser": "^21.1.0",
|
|
63
|
+
"@angular/platform-browser-dynamic": "^21.1.0",
|
|
64
|
+
"@angular/router": "^21.1.0",
|
|
65
|
+
"@skeletonizer/utils": "^2.2.0",
|
|
66
|
+
"rxjs": "~7.8.2",
|
|
67
67
|
"tslib": "^2.8.1",
|
|
68
|
-
"zone.js": "~0.
|
|
68
|
+
"zone.js": "~0.16.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
-
"@angular-devkit/build-angular": "^
|
|
72
|
-
"@angular-eslint/builder": "
|
|
73
|
-
"@angular-eslint/eslint-plugin": "
|
|
74
|
-
"@angular-eslint/eslint-plugin-template": "
|
|
75
|
-
"@angular-eslint/schematics": "
|
|
76
|
-
"@angular-eslint/template-parser": "
|
|
77
|
-
"@angular/cli": "~
|
|
78
|
-
"@angular/compiler-cli": "^
|
|
79
|
-
"@skeletonizer/utils": "^2.
|
|
80
|
-
"@types/jasmine": "~5.1.
|
|
81
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
82
|
-
"@typescript-eslint/parser": "^8.
|
|
83
|
-
"angular-eslint": "^
|
|
84
|
-
"eslint": "^9.
|
|
85
|
-
"jasmine-core": "~5.
|
|
71
|
+
"@angular-devkit/build-angular": "^21.1.0",
|
|
72
|
+
"@angular-eslint/builder": "21.1.0",
|
|
73
|
+
"@angular-eslint/eslint-plugin": "21.1.0",
|
|
74
|
+
"@angular-eslint/eslint-plugin-template": "21.1.0",
|
|
75
|
+
"@angular-eslint/schematics": "21.1.0",
|
|
76
|
+
"@angular-eslint/template-parser": "21.1.0",
|
|
77
|
+
"@angular/cli": "~21.1.0",
|
|
78
|
+
"@angular/compiler-cli": "^21.1.0",
|
|
79
|
+
"@skeletonizer/utils": "^2.2.0",
|
|
80
|
+
"@types/jasmine": "~5.1.15",
|
|
81
|
+
"@typescript-eslint/eslint-plugin": "^8.53.0",
|
|
82
|
+
"@typescript-eslint/parser": "^8.53.0",
|
|
83
|
+
"angular-eslint": "^21.1.0",
|
|
84
|
+
"eslint": "^9.39.2",
|
|
85
|
+
"jasmine-core": "~5.13.0",
|
|
86
86
|
"karma": "~6.4.4",
|
|
87
87
|
"karma-chrome-launcher": "~3.2.0",
|
|
88
88
|
"karma-coverage": "~2.2.1",
|
|
89
89
|
"karma-jasmine": "~5.1.0",
|
|
90
90
|
"karma-jasmine-html-reporter": "~2.1.0",
|
|
91
|
-
"ng-packagr": "^
|
|
92
|
-
"typescript": "~5.
|
|
91
|
+
"ng-packagr": "^21.1.0",
|
|
92
|
+
"typescript": "~5.9.3"
|
|
93
93
|
},
|
|
94
|
-
"gitHead": "
|
|
94
|
+
"gitHead": "3cbb3ce5029b087eb1f2317d98fe6a50157faf13"
|
|
95
95
|
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import * as i0 from '@angular/core';
|
|
2
|
-
import { TemplateRef } from '@angular/core';
|
|
3
|
-
import { SkeletonAdapterComponent, Schema, ISkeletonizerColorSchema, TSchemaConfig } from '@skeletonizer/utils';
|
|
4
|
-
|
|
5
|
-
declare class SkeletonizerSkeletonComponent<T extends object, Scope extends T> extends SkeletonAdapterComponent<T> {
|
|
6
|
-
readonly templateRef: TemplateRef<{
|
|
7
|
-
$implicit: Schema<T> | Scope;
|
|
8
|
-
}>;
|
|
9
|
-
showSkeleton: boolean;
|
|
10
|
-
scope: Scope;
|
|
11
|
-
colorSchema?: ISkeletonizerColorSchema;
|
|
12
|
-
set configInput(config: TSchemaConfig<T>);
|
|
13
|
-
static ɵfac: i0.ɵɵFactoryDeclaration<SkeletonizerSkeletonComponent<any, any>, never>;
|
|
14
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<SkeletonizerSkeletonComponent<any, any>, "skeletonizer-skeleton", never, { "showSkeleton": { "alias": "showSkeleton"; "required": true; }; "scope": { "alias": "scope"; "required": true; }; "colorSchema": { "alias": "colorSchema"; "required": false; }; "configInput": { "alias": "config"; "required": true; }; }, {}, ["templateRef"], never, true, never>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export { SkeletonizerSkeletonComponent };
|