summaryrefslogtreecommitdiff
path: root/docs/src/components/AvatarList.astro
diff options
context:
space:
mode:
Diffstat (limited to 'docs/src/components/AvatarList.astro')
-rw-r--r--docs/src/components/AvatarList.astro151
1 files changed, 151 insertions, 0 deletions
diff --git a/docs/src/components/AvatarList.astro b/docs/src/components/AvatarList.astro
new file mode 100644
index 000000000..09fc089b9
--- /dev/null
+++ b/docs/src/components/AvatarList.astro
@@ -0,0 +1,151 @@
+---
+// fetch all commits for just this page's path
+const { path } = Astro.props;
+const url = `https://api.github.com/repos/snowpackjs/astro-docs/commits?path=${path}`;
+const commitsURL = `https://github.com/snowpackjs/astro-docs/commits/main/${path}`;
+
+async function getCommits(url) {
+ try {
+ const token = import.meta.env.SNOWPACK_PUBLIC_GITHUB_TOKEN;
+ if (!token) {
+ throw new Error(
+ 'Cannot find "SNOWPACK_PUBLIC_GITHUB_TOKEN" used for escaping rate-limiting.'
+ );
+ }
+
+ const auth = `Basic ${Buffer.from(token, "binary").toString("base64")}`;
+
+ const res = await fetch(url, {
+ method: "GET",
+ headers: {
+ Authorization: auth,
+ "User-Agent": "astro-docs/1.0",
+ },
+ });
+
+ const data = await res.json();
+
+ if (!res.ok) {
+ throw new Error(
+ `Request to fetch commits failed. Reason: ${res.statusText}
+ Message: ${data.message}`
+ );
+ }
+
+ return data;
+ } catch (e) {
+ console.warn(`[error] /src/components/AvatarList.astro
+ ${e?.message ?? e}`);
+ return new Array();
+ }
+}
+
+function removeDups(arr) {
+ if (!arr) {
+ return new Array();
+ }
+ let map = new Map();
+
+ for (let item of arr) {
+ let author = item.author;
+ // Deduplicate based on author.id
+ map.set(author.id, { login: author.login, id: author.id });
+ }
+
+ return Array.from(map.values());
+}
+
+const data = await getCommits(url);
+const unique = removeDups(data);
+const recentContributors = unique.slice(0, 3); // only show avatars for the 3 most recent contributors
+const additionalContributors = unique.length - recentContributors.length; // list the rest of them as # of extra contributors
+
+---
+<!-- Thanks to @5t3ph for https://smolcss.dev/#smol-avatar-list! -->
+<div class="contributors">
+<ul class="avatar-list" style={`--avatar-count: ${recentContributors.length}`}>
+
+{recentContributors.map((item) => (
+ <li><a href={`https://github.com/${item.login}`}><img alt={`Contributor ${item.login}`} title={`Contributor ${item.login}`} width="64" height="64" src={`https://avatars.githubusercontent.com/u/${item.id}`}/></a></li>
+
+))}
+ </ul>
+ {additionalContributors > 0 && <span><a href={commitsURL}>{`and ${additionalContributors} additional contributor${additionalContributors > 1 ? 's' : ''}.`}</a></span>}
+ {unique.length === 0 && <a href={commitsURL}>Contributors</a>}
+</div>
+
+<style>
+.avatar-list {
+ --avatar-size: 2.5rem;
+ --avatar-count: 3;
+
+ display: grid;
+ list-style: none;
+ /* Default to displaying most of the avatar to
+ enable easier access on touch devices, ensuring
+ the WCAG touch target size is met or exceeded */
+ grid-template-columns: repeat(
+ var(--avatar-count),
+ max(44px, calc(var(--avatar-size) / 1.15))
+ );
+ /* `padding` matches added visual dimensions of
+ the `box-shadow` to help create a more accurate
+ computed component size */
+ padding: 0.08em;
+ font-size: var(--avatar-size);
+}
+
+@media (any-hover: hover) and (any-pointer: fine) {
+ .avatar-list {
+ /* We create 1 extra cell to enable the computed
+ width to match the final visual width */
+ grid-template-columns: repeat(
+ calc(var(--avatar-count) + 1),
+ calc(var(--avatar-size) / 1.75)
+ );
+ }
+}
+
+.avatar-list li {
+ width: var(--avatar-size);
+ height: var(--avatar-size);
+}
+
+.avatar-list li:hover ~ li a,
+.avatar-list li:focus-within ~ li a {
+ transform: translateX(33%);
+}
+
+.avatar-list img,
+.avatar-list a {
+ display: block;
+ border-radius: 50%;
+}
+
+.avatar-list a {
+ transition: transform 180ms ease-in-out;
+}
+
+.avatar-list img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ background-color: #fff;
+ box-shadow: 0 0 0 0.05em #fff, 0 0 0 0.08em rgba(0, 0, 0, 0.15);
+}
+
+.avatar-list a:focus {
+ outline: 2px solid transparent;
+ /* Double-layer trick to work for dark and light backgrounds */
+ box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
+}
+
+.contributors {
+ display: flex;
+ align-items: center;
+}
+
+.contributors > * + * {
+ margin-left: .75rem;
+}
+</style>