mirror of
https://github.com/svemagie/indiekit-endpoint-activitypub.git
synced 2026-04-02 15:44:58 +02:00
chore: phase 2 convention alignment — onerror/onclick removal, CSS stacking avatar fallback (v2.8.1)
- Replace inline onerror handlers with CSS stacking + event delegation for avatar fallback - Replace inline onclick with event delegation for profile link removal - Replace hardcoded border values with design tokens in reader-links.css - Add data-avatar-fallback pattern: fallback initials always visible, img layered on top Confab-Link: http://localhost:8080/sessions/bb4a6ec4-b711-48cd-b3d7-942ec2a9851d
This commit is contained in:
@@ -91,7 +91,7 @@
|
||||
<label class="label" for="link_value_{{ loop.index }}">{{ __("activitypub.profile.linkValueLabel") }}</label>
|
||||
<input class="input" type="url" id="link_value_{{ loop.index }}" name="link_value[]" value="{{ att.value }}" placeholder="https://example.com">
|
||||
</div>
|
||||
<button type="button" class="button button--small" onclick="this.closest('.profile-link-row').remove()" style="margin-block-end: 4px;">{{ __("activitypub.profile.removeLink") }}</button>
|
||||
<button type="button" class="button button--small profile-link-remove" style="margin-block-end: 4px;">{{ __("activitypub.profile.removeLink") }}</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
@@ -129,6 +129,11 @@
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
document.getElementById('profile-links').addEventListener('click', function(e) {
|
||||
var btn = e.target.closest('.profile-link-remove');
|
||||
if (btn) btn.closest('.profile-link-row').remove();
|
||||
});
|
||||
|
||||
var linkCount = {{ (profile.attachments.length if profile.attachments) or 0 }};
|
||||
document.getElementById('add-link-btn').addEventListener('click', function() {
|
||||
linkCount++;
|
||||
@@ -167,10 +172,9 @@
|
||||
|
||||
var removeBtn = document.createElement('button');
|
||||
removeBtn.type = 'button';
|
||||
removeBtn.className = 'button button--small';
|
||||
removeBtn.className = 'button button--small profile-link-remove';
|
||||
removeBtn.style.cssText = 'margin-block-end: 4px;';
|
||||
removeBtn.textContent = 'Remove';
|
||||
removeBtn.addEventListener('click', function() { row.remove(); });
|
||||
|
||||
row.appendChild(nameDiv);
|
||||
row.appendChild(valueDiv);
|
||||
|
||||
@@ -47,8 +47,7 @@
|
||||
:class="{ 'ap-lookup-autocomplete__item--highlighted': index === highlighted }"
|
||||
@click="selectItem(item)"
|
||||
@mouseenter="highlighted = index">
|
||||
<img :src="item.avatar" :alt="item.name" class="ap-lookup-autocomplete__avatar"
|
||||
onerror="this.style.display='none'">
|
||||
<img :src="item.avatar" :alt="item.name" class="ap-lookup-autocomplete__avatar">
|
||||
<span class="ap-lookup-autocomplete__info">
|
||||
<span class="ap-lookup-autocomplete__name" x-text="item.name"></span>
|
||||
<span class="ap-lookup-autocomplete__handle" x-text="item.handle"></span>
|
||||
|
||||
@@ -38,13 +38,11 @@
|
||||
|
||||
{# Profile info #}
|
||||
<div class="ap-profile__info">
|
||||
<div class="ap-profile__avatar-wrap">
|
||||
<div class="ap-profile__avatar-wrap" data-avatar-fallback>
|
||||
{% if icon %}
|
||||
<img src="{{ icon }}" alt="{{ name }}" class="ap-profile__avatar"
|
||||
onerror="this.replaceWith(Object.assign(document.createElement('div'),{className:'ap-profile__avatar ap-profile__avatar--placeholder',textContent:'{{ name[0] }}'}))">
|
||||
{% else %}
|
||||
<div class="ap-profile__avatar ap-profile__avatar--placeholder">{{ name[0] }}</div>
|
||||
<img src="{{ icon }}" alt="{{ name }}" class="ap-profile__avatar">
|
||||
{% endif %}
|
||||
<div class="ap-profile__avatar ap-profile__avatar--placeholder">{{ name[0] }}</div>
|
||||
</div>
|
||||
|
||||
<div class="ap-profile__details">
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
{# Relative timestamps — converts absolute dates to "5m", "3h", "2d" etc. #}
|
||||
<script defer src="/assets/@rmdes-indiekit-endpoint-activitypub/reader-relative-time.js"></script>
|
||||
|
||||
{# Alpine.js for client-side reactivity (CW toggles, interaction buttons, infinite scroll) #}
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.9/dist/cdn.min.js"></script>
|
||||
{# Avatar fallback — remove broken images to reveal initials fallback underneath #}
|
||||
<script>document.addEventListener("error",function(e){var t=e.target;if(t.tagName==="IMG"&&t.closest("[data-avatar-fallback]"))t.remove()},true)</script>
|
||||
|
||||
{# Alpine.js loaded by default.njk — AP scripts register via alpine:init before it initializes #}
|
||||
|
||||
{# Reader stylesheet — loaded in body is fine for modern browsers #}
|
||||
<link rel="stylesheet" href="/assets/@rmdes-indiekit-endpoint-activitypub/reader.css">
|
||||
|
||||
@@ -39,13 +39,12 @@
|
||||
|
||||
{# Author header #}
|
||||
<header class="ap-card__author">
|
||||
{% if item.author.photo %}
|
||||
<img src="{{ item.author.photo }}" alt="{{ item.author.name }}" class="ap-card__avatar" loading="lazy" crossorigin="anonymous"
|
||||
onerror="this.style.display='none';this.nextElementSibling.style.display=''">
|
||||
<span class="ap-card__avatar ap-card__avatar--default" style="display:none" aria-hidden="true">{{ item.author.name[0] | upper if item.author.name else "?" }}</span>
|
||||
{% else %}
|
||||
<div class="ap-card__avatar-wrap" data-avatar-fallback>
|
||||
{% if item.author.photo %}
|
||||
<img src="{{ item.author.photo }}" alt="{{ item.author.name }}" class="ap-card__avatar" loading="lazy" crossorigin="anonymous">
|
||||
{% endif %}
|
||||
<span class="ap-card__avatar ap-card__avatar--default" aria-hidden="true">{{ item.author.name[0] | upper if item.author.name else "?" }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="ap-card__author-info">
|
||||
<div class="ap-card__author-name">
|
||||
{% if item.author.url %}
|
||||
|
||||
@@ -9,14 +9,11 @@
|
||||
</form>
|
||||
|
||||
{# Actor avatar with type badge #}
|
||||
<div class="ap-notification__avatar-wrap">
|
||||
<div class="ap-notification__avatar-wrap" data-avatar-fallback>
|
||||
{% if item.actorPhoto %}
|
||||
<img src="{{ item.actorPhoto }}" alt="{{ item.actorName }}" class="ap-notification__avatar" loading="lazy" crossorigin="anonymous"
|
||||
onerror="this.style.display='none';this.nextElementSibling.style.display=''">
|
||||
<span class="ap-notification__avatar ap-notification__avatar--default" style="display:none" aria-hidden="true">{{ item.actorName[0] | upper if item.actorName else "?" }}</span>
|
||||
{% else %}
|
||||
<span class="ap-notification__avatar ap-notification__avatar--default" aria-hidden="true">{{ item.actorName[0] | upper if item.actorName else "?" }}</span>
|
||||
<img src="{{ item.actorPhoto }}" alt="{{ item.actorName }}" class="ap-notification__avatar" loading="lazy" crossorigin="anonymous">
|
||||
{% endif %}
|
||||
<span class="ap-notification__avatar ap-notification__avatar--default" aria-hidden="true">{{ item.actorName[0] | upper if item.actorName else "?" }}</span>
|
||||
<span class="ap-notification__type-badge">
|
||||
{% if item.type == "like" %}❤{% elif item.type == "boost" %}🔁{% elif item.type == "follow" %}👤{% elif item.type == "reply" %}💬{% elif item.type == "mention" %}@{% endif %}
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user