@ecodev/natural 55.2.0 → 55.4.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/esm2022/lib/modules/avatar/component/avatar.component.mjs +33 -9
- package/esm2022/lib/modules/avatar/service/avatar.service.mjs +10 -9
- package/esm2022/lib/modules/avatar/sources/initials.mjs +2 -2
- package/esm2022/lib/modules/common/services/seo.service.mjs +24 -22
- package/fesm2022/ecodev-natural.mjs +62 -36
- package/fesm2022/ecodev-natural.mjs.map +1 -1
- package/lib/modules/avatar/component/avatar.component.d.ts +2 -1
- package/lib/modules/common/services/seo.service.d.ts +11 -31
- package/package.json +1 -1
- package/src/lib/_natural.theme.scss +5 -0
|
@@ -5792,14 +5792,20 @@ function stripTags(str) {
|
|
|
5792
5792
|
* configured for it in the routing.
|
|
5793
5793
|
*/
|
|
5794
5794
|
class NaturalSeoService {
|
|
5795
|
-
constructor(
|
|
5796
|
-
this.config = config;
|
|
5795
|
+
constructor(configToken, router, titleService, metaTagService, document, locale) {
|
|
5797
5796
|
this.router = router;
|
|
5798
5797
|
this.titleService = titleService;
|
|
5799
5798
|
this.metaTagService = metaTagService;
|
|
5800
5799
|
this.document = document;
|
|
5801
5800
|
this.locale = locale;
|
|
5802
|
-
this.
|
|
5801
|
+
this.config = {
|
|
5802
|
+
applicationName: '',
|
|
5803
|
+
};
|
|
5804
|
+
combineLatest({
|
|
5805
|
+
config: configToken instanceof Observable ? configToken.pipe(startWith(this.config)) : of(configToken),
|
|
5806
|
+
navigationEnd: this.router.events.pipe(filter(event => event instanceof NavigationEnd)),
|
|
5807
|
+
}).subscribe(({ config }) => {
|
|
5808
|
+
this.config = config;
|
|
5803
5809
|
const root = this.router.routerState.root.snapshot;
|
|
5804
5810
|
this.routeData = this.getRouteData(root);
|
|
5805
5811
|
const seo = this.routeData.seo ?? { title: '' };
|
|
@@ -5819,11 +5825,11 @@ class NaturalSeoService {
|
|
|
5819
5825
|
/**
|
|
5820
5826
|
* Update the SEO with given info. The extra part and app name will be appended automatically.
|
|
5821
5827
|
*
|
|
5822
|
-
* In most cases this should not be used
|
|
5828
|
+
* In most cases, this should not be used. And instead, the SEO should be configured in the routing,
|
|
5823
5829
|
* possibly with the callback variant for some dynamism.
|
|
5824
5830
|
*
|
|
5825
|
-
* But in rare cases only the Component is able to build a proper page title, after it
|
|
5826
|
-
* needed. For those cases the Component can inject this service and update the SEO directly.
|
|
5831
|
+
* But in rare cases, only the Component is able to build a proper page title, after it gathered everything it
|
|
5832
|
+
* needed. For those cases, the Component can inject this service and update the SEO directly.
|
|
5827
5833
|
*/
|
|
5828
5834
|
update(seo) {
|
|
5829
5835
|
// Title
|
|
@@ -5843,7 +5849,7 @@ class NaturalSeoService {
|
|
|
5843
5849
|
// Canonical
|
|
5844
5850
|
// Add language in url (after domain) if some languages are provided only
|
|
5845
5851
|
const language = this.config.languages?.length && this.locale ? this.locale.split('-')[0] : '';
|
|
5846
|
-
const urlParts = this.getUrlParts(seo.canonicalQueryParamsWhitelist
|
|
5852
|
+
const urlParts = this.getUrlParts(seo.canonicalQueryParamsWhitelist ?? []);
|
|
5847
5853
|
this.updateLinkTag({ rel: 'canonical', href: this.getUrl(urlParts, language) });
|
|
5848
5854
|
this.updateAlternates(urlParts);
|
|
5849
5855
|
}
|
|
@@ -5864,10 +5870,10 @@ class NaturalSeoService {
|
|
|
5864
5870
|
}
|
|
5865
5871
|
// Query Params
|
|
5866
5872
|
let params = '';
|
|
5867
|
-
for (const
|
|
5868
|
-
if (
|
|
5869
|
-
const key = encodeURIComponent(
|
|
5870
|
-
const value = encodeURIComponent(urlTree.queryParams[
|
|
5873
|
+
for (const whiteListedParam of whiteListedParams) {
|
|
5874
|
+
if (whiteListedParam in urlTree.queryParams) {
|
|
5875
|
+
const key = encodeURIComponent(whiteListedParam);
|
|
5876
|
+
const value = encodeURIComponent(urlTree.queryParams[whiteListedParam]);
|
|
5871
5877
|
if (params.length) {
|
|
5872
5878
|
params += '&';
|
|
5873
5879
|
}
|
|
@@ -5881,9 +5887,6 @@ class NaturalSeoService {
|
|
|
5881
5887
|
}
|
|
5882
5888
|
/**
|
|
5883
5889
|
* Add language between domain and uri https://example.com/fr/folder/page
|
|
5884
|
-
* @param urlParts
|
|
5885
|
-
* @param language
|
|
5886
|
-
* @private
|
|
5887
5890
|
*/
|
|
5888
5891
|
getUrl(urlParts, language) {
|
|
5889
5892
|
let url = urlParts.url;
|
|
@@ -5915,7 +5918,7 @@ class NaturalSeoService {
|
|
|
5915
5918
|
}
|
|
5916
5919
|
}
|
|
5917
5920
|
updateLinkTag(definition) {
|
|
5918
|
-
const linkElement = this.document.head.querySelector(this.
|
|
5921
|
+
const linkElement = this.document.head.querySelector(this.parseSelector(definition)) ??
|
|
5919
5922
|
this.document.head.appendChild(this.document.createElement('link'));
|
|
5920
5923
|
if (linkElement) {
|
|
5921
5924
|
Object.keys(definition).forEach((attribute) => {
|
|
@@ -5924,13 +5927,11 @@ class NaturalSeoService {
|
|
|
5924
5927
|
}
|
|
5925
5928
|
}
|
|
5926
5929
|
/**
|
|
5927
|
-
*
|
|
5928
|
-
* @param definition
|
|
5929
|
-
* @return {string} selector to use in querySelector
|
|
5930
|
+
* Returns selector to use in querySelector to get the given link
|
|
5930
5931
|
*/
|
|
5931
|
-
|
|
5932
|
+
parseSelector(definition) {
|
|
5932
5933
|
let attributes = 'link';
|
|
5933
|
-
Object.keys(definition).forEach(
|
|
5934
|
+
Object.keys(definition).forEach(attr => {
|
|
5934
5935
|
if (attr !== 'href') {
|
|
5935
5936
|
attributes += `[${attr}="${definition[attr]}"]`;
|
|
5936
5937
|
}
|
|
@@ -10781,7 +10782,7 @@ function getInitials(name, size) {
|
|
|
10781
10782
|
*/
|
|
10782
10783
|
class Initials extends Source {
|
|
10783
10784
|
getAvatar(size) {
|
|
10784
|
-
return getInitials(this.getValue(), size);
|
|
10785
|
+
return getInitials(this.getValue().replace(/[^a-zA-Z0-9\s]/g, ''), size); // only letters, numbers and space
|
|
10785
10786
|
}
|
|
10786
10787
|
isTextual() {
|
|
10787
10788
|
return true;
|
|
@@ -10815,14 +10816,15 @@ class AvatarService {
|
|
|
10815
10816
|
['initials', Initials],
|
|
10816
10817
|
]);
|
|
10817
10818
|
this.avatarColors = [
|
|
10818
|
-
'#
|
|
10819
|
-
'#
|
|
10820
|
-
'#
|
|
10821
|
-
'#
|
|
10822
|
-
'#
|
|
10823
|
-
'#
|
|
10824
|
-
'#
|
|
10825
|
-
'#
|
|
10819
|
+
'#ff0000',
|
|
10820
|
+
'#ff8800',
|
|
10821
|
+
'#dabb00',
|
|
10822
|
+
'#00c200',
|
|
10823
|
+
'#01cbcb',
|
|
10824
|
+
'#008cff',
|
|
10825
|
+
'#ff00d8',
|
|
10826
|
+
'#c800ff',
|
|
10827
|
+
'#3b3b3b',
|
|
10826
10828
|
];
|
|
10827
10829
|
this.failedSources = new Map();
|
|
10828
10830
|
}
|
|
@@ -10868,10 +10870,11 @@ class NaturalAvatarComponent {
|
|
|
10868
10870
|
constructor(avatarService) {
|
|
10869
10871
|
this.avatarService = avatarService;
|
|
10870
10872
|
this.size = 50;
|
|
10871
|
-
this.
|
|
10873
|
+
this.decorated = true;
|
|
10874
|
+
this.textSizeRatio = 2.25;
|
|
10872
10875
|
this.fgColor = '#FFF';
|
|
10873
10876
|
this.borderRadius = '';
|
|
10874
|
-
this.textMaximumLength =
|
|
10877
|
+
this.textMaximumLength = 2;
|
|
10875
10878
|
this.avatarSrc = null;
|
|
10876
10879
|
this.avatarText = null;
|
|
10877
10880
|
this.avatarStyle = {};
|
|
@@ -10966,6 +10969,8 @@ class NaturalAvatarComponent {
|
|
|
10966
10969
|
backgroundColor: this.bgColor ? this.bgColor : this.avatarService.getRandomColor(avatarValue),
|
|
10967
10970
|
font: Math.floor(+this.size / this.textSizeRatio) + 'px Helvetica, Arial, sans-serif',
|
|
10968
10971
|
lineHeight: this.size + 'px',
|
|
10972
|
+
width: this.size + 'px',
|
|
10973
|
+
height: this.size + 'px',
|
|
10969
10974
|
};
|
|
10970
10975
|
}
|
|
10971
10976
|
/**
|
|
@@ -10980,7 +10985,7 @@ class NaturalAvatarComponent {
|
|
|
10980
10985
|
};
|
|
10981
10986
|
}
|
|
10982
10987
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.1.2", ngImport: i0, type: NaturalAvatarComponent, deps: [{ token: AvatarService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
10983
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.1.2", type: NaturalAvatarComponent, isStandalone: true, selector: "natural-avatar", inputs: { image: "image", initials: "initials", gravatar: "gravatar", size: "size", textSizeRatio: "textSizeRatio", bgColor: "bgColor", fgColor: "fgColor", borderRadius: "borderRadius", textMaximumLength: "textMaximumLength" }, usesOnChanges: true, ngImport: i0, template: `
|
|
10988
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.1.2", type: NaturalAvatarComponent, isStandalone: true, selector: "natural-avatar", inputs: { image: "image", initials: "initials", gravatar: "gravatar", size: "size", decorated: "decorated", textSizeRatio: "textSizeRatio", bgColor: "bgColor", fgColor: "fgColor", borderRadius: "borderRadius", textMaximumLength: "textMaximumLength" }, host: { properties: { "style.height.px": "this.size", "style.width.px": "this.size", "class.decorated": "this.decorated" } }, usesOnChanges: true, ngImport: i0, template: `
|
|
10984
10989
|
<div class="avatar-container" [ngStyle]="hostStyle">
|
|
10985
10990
|
<img
|
|
10986
10991
|
*ngIf="avatarSrc"
|
|
@@ -10992,11 +10997,16 @@ class NaturalAvatarComponent {
|
|
|
10992
10997
|
class="avatar-content"
|
|
10993
10998
|
loading="lazy"
|
|
10994
10999
|
/>
|
|
10995
|
-
<div
|
|
11000
|
+
<div
|
|
11001
|
+
*ngIf="avatarText"
|
|
11002
|
+
class="avatar-content"
|
|
11003
|
+
[class.natural-elevation]="decorated"
|
|
11004
|
+
[ngStyle]="avatarStyle"
|
|
11005
|
+
>
|
|
10996
11006
|
{{ avatarText }}
|
|
10997
11007
|
</div>
|
|
10998
11008
|
</div>
|
|
10999
|
-
`, isInline: true, styles: [":host{border-radius:50%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] }); }
|
|
11009
|
+
`, isInline: true, styles: [":host{display:block}:host.decorated{position:relative}:host.decorated .avatar-container:before{content:\"\";position:absolute;inset:0;border-radius:50%;background:linear-gradient(345deg,rgba(255,255,255,0) 25%,rgba(255,255,255,.33) 100%)}:host.decorated .avatar-content{text-shadow:0 1px 0 rgba(0,0,0,.6)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] }); }
|
|
11000
11010
|
}
|
|
11001
11011
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.1.2", ngImport: i0, type: NaturalAvatarComponent, decorators: [{
|
|
11002
11012
|
type: Component,
|
|
@@ -11012,11 +11022,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.1.2", ngImpor
|
|
|
11012
11022
|
class="avatar-content"
|
|
11013
11023
|
loading="lazy"
|
|
11014
11024
|
/>
|
|
11015
|
-
<div
|
|
11025
|
+
<div
|
|
11026
|
+
*ngIf="avatarText"
|
|
11027
|
+
class="avatar-content"
|
|
11028
|
+
[class.natural-elevation]="decorated"
|
|
11029
|
+
[ngStyle]="avatarStyle"
|
|
11030
|
+
>
|
|
11016
11031
|
{{ avatarText }}
|
|
11017
11032
|
</div>
|
|
11018
11033
|
</div>
|
|
11019
|
-
`, standalone: true, imports: [CommonModule], styles: [":host{border-radius:50%}\n"] }]
|
|
11034
|
+
`, standalone: true, imports: [CommonModule], styles: [":host{display:block}:host.decorated{position:relative}:host.decorated .avatar-container:before{content:\"\";position:absolute;inset:0;border-radius:50%;background:linear-gradient(345deg,rgba(255,255,255,0) 25%,rgba(255,255,255,.33) 100%)}:host.decorated .avatar-content{text-shadow:0 1px 0 rgba(0,0,0,.6)}\n"] }]
|
|
11020
11035
|
}], ctorParameters: function () { return [{ type: AvatarService }]; }, propDecorators: { image: [{
|
|
11021
11036
|
type: Input
|
|
11022
11037
|
}], initials: [{
|
|
@@ -11024,6 +11039,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.1.2", ngImpor
|
|
|
11024
11039
|
}], gravatar: [{
|
|
11025
11040
|
type: Input
|
|
11026
11041
|
}], size: [{
|
|
11042
|
+
type: HostBinding,
|
|
11043
|
+
args: ['style.height.px']
|
|
11044
|
+
}, {
|
|
11045
|
+
type: HostBinding,
|
|
11046
|
+
args: ['style.width.px']
|
|
11047
|
+
}, {
|
|
11048
|
+
type: Input
|
|
11049
|
+
}], decorated: [{
|
|
11050
|
+
type: HostBinding,
|
|
11051
|
+
args: ['class.decorated']
|
|
11052
|
+
}, {
|
|
11027
11053
|
type: Input
|
|
11028
11054
|
}], textSizeRatio: [{
|
|
11029
11055
|
type: Input
|