feat(voyage): implement two-opacity pattern (active/inactive/resolved)
This commit is contained in:
parent
224517f205
commit
df0e7837af
2 changed files with 88 additions and 4 deletions
|
|
@ -367,15 +367,32 @@
|
|||
cursor: pointer;
|
||||
/* 2-3px accent stripe extending right from the badge into the gutter */
|
||||
box-shadow: 0.25rem 0 0 0 var(--color-scope-voyage);
|
||||
transition: transform 150ms ease;
|
||||
transition: opacity 150ms ease, transform 150ms ease, outline-color 150ms ease;
|
||||
/* v4.3 Step 21 — two-opacity pattern: default = inactive (40%) */
|
||||
opacity: 0.4;
|
||||
}
|
||||
.voyage-anchor-badge:hover {
|
||||
transform: scale(1.1);
|
||||
opacity: 0.85;
|
||||
}
|
||||
.voyage-anchor-badge:focus-visible {
|
||||
outline: 2px solid var(--color-focus-ring, #4d90fe);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
/* v4.3 Step 21 — active badge (selected via J/K nav or click): full opacity
|
||||
+ 2px outline-ring for the "border-width: 2px" intent without layout shift. */
|
||||
.voyage-anchor-badge[data-active="true"] {
|
||||
opacity: 1;
|
||||
outline: 2px solid var(--color-focus-ring, #4d90fe);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
/* v4.3 Step 21 — resolved badge: 30% opacity + strikethrough on the
|
||||
numeric label. Status-vocabulary is annotation-specific (data-resolved
|
||||
on the draft), distinct from dashboard fleet-tile data-status. */
|
||||
.voyage-anchor-badge[data-resolved="true"] {
|
||||
opacity: 0.3;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
/* Yellow-tint highlight on the anchored span when annotation is active.
|
||||
Google Docs pattern; rgba value taken from research/04 Dim 4. */
|
||||
.voyage-anchor-active {
|
||||
|
|
@ -606,12 +623,28 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
/* v4.3 Step 21 — two-opacity pattern: default = inactive (40%).
|
||||
Active sets full opacity + yellow tint (rule below); resolved
|
||||
renders at 30% with strikethrough. */
|
||||
opacity: 0.4;
|
||||
transition: opacity 150ms ease, background 150ms ease;
|
||||
}
|
||||
.voyage-annotation-list__items li:hover {
|
||||
background: var(--color-bg-soft);
|
||||
opacity: 0.85;
|
||||
}
|
||||
.voyage-annotation-list__items li[data-active="true"] {
|
||||
background: rgba(255, 235, 59, 0.18);
|
||||
opacity: 1;
|
||||
}
|
||||
/* v4.3 Step 21 — resolved list-item mirrors badge: 30% opacity +
|
||||
strikethrough on the label. data-resolved is set by renderAnnotationList
|
||||
from draft.resolved. */
|
||||
.voyage-annotation-list__items li[data-resolved="true"] {
|
||||
opacity: 0.3;
|
||||
}
|
||||
.voyage-annotation-list__items li[data-resolved="true"] .voyage-jumplist-label {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
.voyage-annotation-list__items .voyage-jumplist-num {
|
||||
flex: 0 0 auto;
|
||||
|
|
@ -2097,6 +2130,9 @@ playground first-run shows a complete round-trip-able artifact.
|
|||
badge.type = 'button';
|
||||
badge.className = 'voyage-anchor-badge';
|
||||
badge.setAttribute('data-anchor-id', d.id);
|
||||
// v4.3 Step 21 — two-opacity pattern: data-resolved drives 30% +
|
||||
// strikethrough state. data-active is toggled by setActiveAnchor.
|
||||
if (d.resolved) badge.setAttribute('data-resolved', 'true');
|
||||
badge.setAttribute('aria-label', 'Annotering ' + (i + 1) + ': ' + (d.target_anchor || 'page'));
|
||||
badge.textContent = String(i + 1);
|
||||
(function (el, draftId) {
|
||||
|
|
@ -2115,13 +2151,22 @@ playground first-run shows a complete round-trip-able artifact.
|
|||
if (!viewport) return;
|
||||
var prev = viewport.querySelectorAll('.voyage-anchor-active');
|
||||
for (var i = 0; i < prev.length; i++) prev[i].classList.remove('voyage-anchor-active');
|
||||
// v4.3 Step 21 — clear data-active from prior badge AND prior sidebar
|
||||
// list-item, then set on the new ones. Mirrors two-opacity intent.
|
||||
var prevBadges = viewport.querySelectorAll('.voyage-anchor-badge[data-active="true"]');
|
||||
for (var pb = 0; pb < prevBadges.length; pb++) prevBadges[pb].removeAttribute('data-active');
|
||||
var prevListItems = document.querySelectorAll('#voyage-jumplist li[data-active="true"]');
|
||||
for (var pl = 0; pl < prevListItems.length; pl++) prevListItems[pl].removeAttribute('data-active');
|
||||
if (!anchorId) return;
|
||||
var badge = viewport.querySelector('.voyage-anchor-badge[data-anchor-id="' + anchorId + '"]');
|
||||
if (badge && badge.parentElement) {
|
||||
badge.setAttribute('data-active', 'true');
|
||||
badge.parentElement.classList.add('voyage-anchor-active');
|
||||
badge.parentElement.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
||||
badge.focus();
|
||||
}
|
||||
var listItem = document.querySelector('#voyage-jumplist li[data-anchor-id="' + anchorId + '"]');
|
||||
if (listItem) listItem.setAttribute('data-active', 'true');
|
||||
}
|
||||
|
||||
// ---- Gesture 3 — page-level note (button injected near viewport) ---
|
||||
|
|
@ -2350,6 +2395,10 @@ playground first-run shows a complete round-trip-able artifact.
|
|||
var origIdx = sorted.indexOf(d);
|
||||
var li = document.createElement('li');
|
||||
li.setAttribute('data-anchor-id', d.id);
|
||||
// v4.3 Step 21 — two-opacity pattern mirror: list-item inherits the
|
||||
// resolved state from the draft so CSS can render strikethrough +
|
||||
// 30% opacity to match the gutter-badge.
|
||||
if (d.resolved) li.setAttribute('data-resolved', 'true');
|
||||
var num = document.createElement('span');
|
||||
num.className = 'voyage-jumplist-num';
|
||||
num.textContent = String(origIdx + 1);
|
||||
|
|
@ -2361,9 +2410,6 @@ playground first-run shows a complete round-trip-able artifact.
|
|||
(function (anchorId) {
|
||||
li.addEventListener('click', function () {
|
||||
setActiveAnchor(anchorId);
|
||||
var items = list.querySelectorAll('li');
|
||||
for (var k = 0; k < items.length; k++) items[k].removeAttribute('data-active');
|
||||
li.setAttribute('data-active', 'true');
|
||||
});
|
||||
})(d.id);
|
||||
list.appendChild(li);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue