Skip to content

Commit c4d40d6

Browse files
authored
Replace project avatars (#403)
* WIP * Update avatar positioning logic and seeding * Update avatar sprite and patterns for avatar URL * Reset djlint lint option * Update avatar image to 100x100 with a limited palette * Lint fixes
1 parent 3585df0 commit c4d40d6

File tree

11 files changed

+123
-22
lines changed

11 files changed

+123
-22
lines changed

.djlintrc

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@
22
"indent": "2",
33
"preserve_blank_lines": true,
44
"ignore": "H006",
5-
"max_line_length": 80
5+
"max_line_length": 80,
6+
"custom_html": "readthedocs-\\w+,readthedocs-\\w+-\\w+",
7+
"format_attribute_template_tags": false
68
}

.pre-commit-config.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ repos:
2929
- repo: https://github.com/djlint/djLint
3030
rev: v1.34.1
3131
hooks:
32-
- id: djlint-django
3332
- id: djlint-reformat-django
33+
- id: djlint-django
3434
- repo: local
3535
hooks:
3636
- id: check-build-assets

package-lock.json

+23
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"mini-css-extract-plugin": "^2.7.6",
5353
"plausible-tracker": "^0.3.8",
5454
"prettier": "^3.0.3",
55+
"pure-rand": "^6.1.0",
5556
"resolve-url-loader": "^5.0.0",
5657
"rollup-plugin-lit-css": "^4.0.1",
5758
"sanitize-html": "^2.11.0",
Loading

readthedocsext/theme/static/readthedocsext/theme/js/site.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

readthedocsext/theme/static/readthedocsext/theme/js/vendor.js

+16-16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

readthedocsext/theme/templates/projects/includes/project_image.html

+10-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,16 @@
55
to the same image always.
66
{% endcomment %}
77

8+
{% load blocktrans from i18n %}
9+
{% load static from static %}
10+
811
{% if project.remote_repository %}
9-
<img class="ui {{ classes|default_if_none:"small rounded" }} image" src="{{ project.remote_repository.avatar_url }}" />
12+
<img class="ui {{ classes|default_if_none:"small rounded" }} image"
13+
src="{{ project.remote_repository.avatar_url }}"
14+
alt="{% blocktrans with project_name=project.name %}{{ project_name }} project{% endblocktrans %}" />
1015
{% else %}
11-
<img class="ui {{ classes|default_if_none:"small rounded" }} image" src="https://api.dicebear.com/5.x/shapes/svg?size=64&backgroundColor=b8abd4&shape1Color=8c74c1&shape2Color=7854c5&shape3Color=f0f0ff&seed={{ project.pk }}" />
16+
<readthedocs-avatar class="ui {{ classes|default_if_none:"small rounded" }} image"
17+
seed="{{ project.pk }}"
18+
url="{% static 'readthedocsext/theme/css/images/avatar-1.png' %}">
19+
</readthedocs-avatar>
1220
{% endif %}

src/css/images/avatar-1.png

18.5 KB
Loading

src/js/modules/avatar.js

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { LitElement, css, html, nothing, unsafeCSS } from "lit";
2+
import pureRand from "pure-rand";
3+
4+
import { LightDOMElement } from "../application/elements";
5+
6+
// This image lives alongside our CSS sources, and bundling outputs this image
7+
// to the application static path. From here, Django ``static`` template tag is
8+
// used to reference the file through storage. So, this import is not directly
9+
// needed here, and this might be a pattern to redo eventually.
10+
import avatarImage from "../../css/images/avatar-1.png";
11+
12+
export class AvatarElement extends LitElement {
13+
static properties = {
14+
seed: { type: String },
15+
url: { type: String },
16+
};
17+
18+
static styles = css`
19+
:host {
20+
--avatar-x: 0;
21+
--avatar-y: 0;
22+
--avatar-scale: -10px;
23+
--avatar-background-image: none;
24+
}
25+
26+
:host > div {
27+
background-image: var(--avatar-background-image);
28+
background-repeat: no-repeat;
29+
background-size: calc(100 * -1 * var(--avatar-scale))
30+
calc(100 * -1 * var(--avatar-scale));
31+
background-position-x: calc(var(--avatar-x) * var(--avatar-scale));
32+
background-position-y: calc(var(--avatar-y) * var(--avatar-scale));
33+
image-rendering: pixelated;
34+
width: calc(var(--avatar-scale) * -4);
35+
height: calc(var(--avatar-scale) * -4);
36+
}
37+
38+
:host(.micro.image) > div {
39+
--avatar-scale: -6px;
40+
}
41+
`;
42+
43+
render() {
44+
return html`<div></div>`;
45+
}
46+
47+
updated(changed) {
48+
// Dynamically update background position through CSS variables. The
49+
// ``styles`` attribute does not work with dynamic rules, but we can do the
50+
// same after an updated event on the web component.
51+
if (changed.has("seed") && this.seed) {
52+
const rng = pureRand.xoroshiro128plus(this.seed);
53+
const posX = pureRand.unsafeUniformIntDistribution(0, 99, rng);
54+
const posY = pureRand.unsafeUniformIntDistribution(0, 99, rng);
55+
this.style.setProperty("--avatar-x", posX);
56+
this.style.setProperty("--avatar-y", posY);
57+
}
58+
// Similarly, load the image through the avatar URL attribute, as we want
59+
// the fully resolved storage URL from Django staticfiles.
60+
if (changed.has("url") && this.url) {
61+
this.style.setProperty("--avatar-background-image", `url("${this.url}")`);
62+
}
63+
}
64+
}
65+
66+
customElements.define("readthedocs-avatar", AvatarElement);

src/js/modules/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import * as avatar from "./avatar";
12
import * as header from "./header";
23
import * as filter from "./filter";
34
import * as menus from "./menus";
45
import * as notifications from "./notifications";
56
import * as popupcards from "./popupcards";
67

7-
export { header, filter, menus, notifications, popupcards };
8+
export { avatar, header, filter, menus, notifications, popupcards };

0 commit comments

Comments
 (0)