@openproject/primer-view-components 0.79.0 → 0.79.1-rc.bfd002c20
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/app/assets/javascripts/components/primer/open_project/avatar_fallback.d.ts +8 -0
- package/app/assets/javascripts/primer_view_components.js +1 -1
- package/app/assets/javascripts/primer_view_components.js.map +1 -1
- package/app/components/primer/open_project/avatar_fallback.d.ts +8 -0
- package/app/components/primer/open_project/avatar_fallback.js +44 -4
- package/package.json +1 -1
- package/static/arguments.json +1 -1
- package/static/info_arch.json +27 -1
- package/static/previews.json +26 -0
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
export declare class AvatarFallbackElement extends HTMLElement {
|
|
2
2
|
uniqueId: string;
|
|
3
3
|
altText: string;
|
|
4
|
+
fallbackSrc: string;
|
|
5
|
+
private img;
|
|
6
|
+
private boundErrorHandler?;
|
|
4
7
|
connectedCallback(): void;
|
|
8
|
+
disconnectedCallback(): void;
|
|
9
|
+
private isImageBroken;
|
|
10
|
+
private handleImageError;
|
|
11
|
+
private applyColor;
|
|
5
12
|
private valueHash;
|
|
6
13
|
private updateSvgColor;
|
|
14
|
+
private isFallbackImage;
|
|
7
15
|
}
|
|
@@ -10,16 +10,50 @@ let AvatarFallbackElement = class AvatarFallbackElement extends HTMLElement {
|
|
|
10
10
|
super(...arguments);
|
|
11
11
|
this.uniqueId = '';
|
|
12
12
|
this.altText = '';
|
|
13
|
+
this.fallbackSrc = '';
|
|
14
|
+
this.img = null;
|
|
13
15
|
}
|
|
14
16
|
connectedCallback() {
|
|
17
|
+
this.img = this.querySelector('img') ?? null;
|
|
18
|
+
if (!this.img)
|
|
19
|
+
return;
|
|
20
|
+
this.boundErrorHandler = () => this.handleImageError(this.img);
|
|
21
|
+
// Handle image load errors (404, network failure, etc.)
|
|
22
|
+
this.img.addEventListener('error', this.boundErrorHandler);
|
|
23
|
+
// Check if image already failed (error event fired before listener attached)
|
|
24
|
+
if (this.isImageBroken(this.img)) {
|
|
25
|
+
this.handleImageError(this.img);
|
|
26
|
+
}
|
|
27
|
+
else if (this.isFallbackImage(this.img)) {
|
|
28
|
+
this.applyColor(this.img);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
disconnectedCallback() {
|
|
32
|
+
if (this.boundErrorHandler && this.img) {
|
|
33
|
+
this.img.removeEventListener('error', this.boundErrorHandler);
|
|
34
|
+
}
|
|
35
|
+
this.boundErrorHandler = undefined;
|
|
36
|
+
this.img = null;
|
|
37
|
+
}
|
|
38
|
+
isImageBroken(img) {
|
|
39
|
+
// Image is broken if loading completed but no actual image data loaded
|
|
40
|
+
// Skip check for data URIs (fallback SVGs) as they're always valid
|
|
41
|
+
return img.complete && img.naturalWidth === 0 && !img.src.startsWith('data:');
|
|
42
|
+
}
|
|
43
|
+
handleImageError(img) {
|
|
44
|
+
// Prevent infinite loop if fallback also fails
|
|
45
|
+
if (this.isFallbackImage(img))
|
|
46
|
+
return;
|
|
47
|
+
if (this.fallbackSrc) {
|
|
48
|
+
img.src = this.fallbackSrc;
|
|
49
|
+
this.applyColor(img);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
applyColor(img) {
|
|
15
53
|
// If either uniqueId or altText is missing, skip color customization so the SVG
|
|
16
54
|
// keeps its default gray fill defined in the source and no color override is applied.
|
|
17
55
|
if (!this.uniqueId || !this.altText)
|
|
18
56
|
return;
|
|
19
|
-
const img = this.querySelector('img[src^="data:image/svg+xml"]');
|
|
20
|
-
if (!img)
|
|
21
|
-
return;
|
|
22
|
-
// Generate consistent color based on uniqueId and altText (hash must match OP Core)
|
|
23
57
|
const text = `${this.uniqueId}${this.altText}`;
|
|
24
58
|
const hue = this.valueHash(text);
|
|
25
59
|
const color = `hsl(${hue}, 50%, 30%)`;
|
|
@@ -49,6 +83,9 @@ let AvatarFallbackElement = class AvatarFallbackElement extends HTMLElement {
|
|
|
49
83
|
// to avoid breaking the component.
|
|
50
84
|
}
|
|
51
85
|
}
|
|
86
|
+
isFallbackImage(img) {
|
|
87
|
+
return img.src === this.fallbackSrc;
|
|
88
|
+
}
|
|
52
89
|
};
|
|
53
90
|
__decorate([
|
|
54
91
|
attr
|
|
@@ -56,6 +93,9 @@ __decorate([
|
|
|
56
93
|
__decorate([
|
|
57
94
|
attr
|
|
58
95
|
], AvatarFallbackElement.prototype, "altText", void 0);
|
|
96
|
+
__decorate([
|
|
97
|
+
attr
|
|
98
|
+
], AvatarFallbackElement.prototype, "fallbackSrc", void 0);
|
|
59
99
|
AvatarFallbackElement = __decorate([
|
|
60
100
|
controller
|
|
61
101
|
], AvatarFallbackElement);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openproject/primer-view-components",
|
|
3
|
-
"version": "0.79.
|
|
3
|
+
"version": "0.79.1-rc.bfd002c20",
|
|
4
4
|
"description": "ViewComponents of the Primer Design System for OpenProject",
|
|
5
5
|
"main": "app/assets/javascripts/primer_view_components.js",
|
|
6
6
|
"module": "app/components/primer/primer.js",
|
package/static/arguments.json
CHANGED
|
@@ -5775,7 +5775,7 @@
|
|
|
5775
5775
|
"name": "src",
|
|
5776
5776
|
"type": "String",
|
|
5777
5777
|
"default": "`nil`",
|
|
5778
|
-
"description": "The source url of the avatar image. When nil, renders a fallback with initials."
|
|
5778
|
+
"description": "The source url of the avatar image. When nil or a broken URL, it renders a fallback with initials."
|
|
5779
5779
|
},
|
|
5780
5780
|
{
|
|
5781
5781
|
"name": "alt",
|
package/static/info_arch.json
CHANGED
|
@@ -18882,7 +18882,7 @@
|
|
|
18882
18882
|
"name": "src",
|
|
18883
18883
|
"type": "String",
|
|
18884
18884
|
"default": "`nil`",
|
|
18885
|
-
"description": "The source url of the avatar image. When nil, renders a fallback with initials."
|
|
18885
|
+
"description": "The source url of the avatar image. When nil or a broken URL, it renders a fallback with initials."
|
|
18886
18886
|
},
|
|
18887
18887
|
{
|
|
18888
18888
|
"name": "alt",
|
|
@@ -19040,6 +19040,32 @@
|
|
|
19040
19040
|
"color-contrast"
|
|
19041
19041
|
]
|
|
19042
19042
|
}
|
|
19043
|
+
},
|
|
19044
|
+
{
|
|
19045
|
+
"preview_path": "primer/open_project/avatar_with_fallback/broken_image_404",
|
|
19046
|
+
"name": "broken_image_404",
|
|
19047
|
+
"snapshot": "true",
|
|
19048
|
+
"skip_rules": {
|
|
19049
|
+
"wont_fix": [
|
|
19050
|
+
"region"
|
|
19051
|
+
],
|
|
19052
|
+
"will_fix": [
|
|
19053
|
+
"color-contrast"
|
|
19054
|
+
]
|
|
19055
|
+
}
|
|
19056
|
+
},
|
|
19057
|
+
{
|
|
19058
|
+
"preview_path": "primer/open_project/avatar_with_fallback/multiple_broken_images",
|
|
19059
|
+
"name": "multiple_broken_images",
|
|
19060
|
+
"snapshot": "false",
|
|
19061
|
+
"skip_rules": {
|
|
19062
|
+
"wont_fix": [
|
|
19063
|
+
"region"
|
|
19064
|
+
],
|
|
19065
|
+
"will_fix": [
|
|
19066
|
+
"color-contrast"
|
|
19067
|
+
]
|
|
19068
|
+
}
|
|
19043
19069
|
}
|
|
19044
19070
|
],
|
|
19045
19071
|
"subcomponents": []
|
package/static/previews.json
CHANGED
|
@@ -1633,6 +1633,32 @@
|
|
|
1633
1633
|
"color-contrast"
|
|
1634
1634
|
]
|
|
1635
1635
|
}
|
|
1636
|
+
},
|
|
1637
|
+
{
|
|
1638
|
+
"preview_path": "primer/open_project/avatar_with_fallback/broken_image_404",
|
|
1639
|
+
"name": "broken_image_404",
|
|
1640
|
+
"snapshot": "true",
|
|
1641
|
+
"skip_rules": {
|
|
1642
|
+
"wont_fix": [
|
|
1643
|
+
"region"
|
|
1644
|
+
],
|
|
1645
|
+
"will_fix": [
|
|
1646
|
+
"color-contrast"
|
|
1647
|
+
]
|
|
1648
|
+
}
|
|
1649
|
+
},
|
|
1650
|
+
{
|
|
1651
|
+
"preview_path": "primer/open_project/avatar_with_fallback/multiple_broken_images",
|
|
1652
|
+
"name": "multiple_broken_images",
|
|
1653
|
+
"snapshot": "false",
|
|
1654
|
+
"skip_rules": {
|
|
1655
|
+
"wont_fix": [
|
|
1656
|
+
"region"
|
|
1657
|
+
],
|
|
1658
|
+
"will_fix": [
|
|
1659
|
+
"color-contrast"
|
|
1660
|
+
]
|
|
1661
|
+
}
|
|
1636
1662
|
}
|
|
1637
1663
|
]
|
|
1638
1664
|
},
|