@hayasaka7/haya-pet 0.3.13 → 0.3.14
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/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ All notable changes to HAYA Pet are documented here. This project adheres to
|
|
|
7
7
|
> 0.2.0 npm publish; they are listed under 0.2.1, which is the first version that
|
|
8
8
|
> ships them.
|
|
9
9
|
|
|
10
|
+
## [0.3.14]
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- **The session-bubble folder now opens and closes with a smooth animation.**
|
|
14
|
+
Folding or unfolding the bubbles used to pop them in and out instantly; the
|
|
15
|
+
panel now grows out of (and shrinks back into) the folder button's corner with
|
|
16
|
+
a light scale-and-fade — a macOS-popover feel. The transform-origin follows the
|
|
17
|
+
panel's open direction and alignment, so it always springs from whichever corner
|
|
18
|
+
sits against the button. The animation is GPU-composited (transform + opacity
|
|
19
|
+
only, no reflow), so the folder button and the placement math stay put; the list
|
|
20
|
+
also **stays mounted while collapsed**, which preserves both the scroll position
|
|
21
|
+
and the live status spinner across a toggle, and it drops out of hit-testing once
|
|
22
|
+
hidden so the pixel-precise click-through overlay still ignores it. A
|
|
23
|
+
reduced-motion preference (`prefers-reduced-motion`) snaps the panel open/closed
|
|
24
|
+
instead of animating.
|
|
25
|
+
|
|
10
26
|
## [0.3.13]
|
|
11
27
|
|
|
12
28
|
### Added
|
|
@@ -56,26 +56,23 @@ export function createBubbleList(container, { collapsed = false, onRender } = {}
|
|
|
56
56
|
}
|
|
57
57
|
updateFolderButton(folderButtonEl, lastBubbles, isCollapsed);
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
listEl = document.createElement("div");
|
|
74
|
-
listEl.className = "bubble-list interactive";
|
|
75
|
-
container.appendChild(listEl);
|
|
76
|
-
}
|
|
77
|
-
reconcileBubbles(listEl, lastBubbles);
|
|
59
|
+
// The list stays mounted whether open or collapsed so the collapse can
|
|
60
|
+
// animate out (a macOS-popover shrink+fade, see styles.css) instead of
|
|
61
|
+
// vanishing; this also preserves the scroll position and the spinner across
|
|
62
|
+
// a toggle. The `collapsed` class drives the transition and, once faded,
|
|
63
|
+
// drops the list from hit-testing (pointer-events + visibility), so the
|
|
64
|
+
// click-through overlay ignores it — hence it also sheds the `interactive`
|
|
65
|
+
// marker while collapsed.
|
|
66
|
+
if (!listEl) {
|
|
67
|
+
// The list itself must be pointer-active (not just the bubbles): with more
|
|
68
|
+
// than three sessions it scrolls, and the scrollbar + the gaps between
|
|
69
|
+
// bubbles belong to the list element — if it stayed click-through, wheel/
|
|
70
|
+
// drag there would fall through to the desktop.
|
|
71
|
+
listEl = document.createElement("div");
|
|
72
|
+
container.appendChild(listEl);
|
|
78
73
|
}
|
|
74
|
+
reconcileBubbles(listEl, lastBubbles);
|
|
75
|
+
listEl.className = isCollapsed ? "bubble-list collapsed" : "bubble-list interactive";
|
|
79
76
|
|
|
80
77
|
// Let the host reposition the panel now that its size is known. This must
|
|
81
78
|
// happen BEFORE the scroll restore below: the host's placement pass is what
|
|
@@ -84,7 +81,9 @@ export function createBubbleList(container, { collapsed = false, onRender } = {}
|
|
|
84
81
|
// scrollTop assigned to it would clamp back to 0.
|
|
85
82
|
onRender?.();
|
|
86
83
|
|
|
87
|
-
|
|
84
|
+
// Restoring scroll only matters while the list is visible; a collapsed list
|
|
85
|
+
// keeps the scrollTop it had, so it reopens exactly where the user left it.
|
|
86
|
+
if (!isCollapsed) {
|
|
88
87
|
applyListScroll(listEl, scrollTargetSessionId, lastScrollTop);
|
|
89
88
|
lastScrollTop = listEl.scrollTop;
|
|
90
89
|
}
|
|
@@ -150,6 +150,25 @@ body {
|
|
|
150
150
|
max-width: 300px;
|
|
151
151
|
overflow-y: auto;
|
|
152
152
|
pointer-events: auto;
|
|
153
|
+
/* Open/close grows out of the folder-button corner (transform-origin set per
|
|
154
|
+
open direction below), macOS-popover style. transform + opacity are
|
|
155
|
+
GPU-composited and don't reflow, so the button and placement math stay put. */
|
|
156
|
+
transform-origin: top left;
|
|
157
|
+
opacity: 1;
|
|
158
|
+
transform: scale(1);
|
|
159
|
+
transition: opacity 0.16s ease, transform 0.2s cubic-bezier(0.32, 0.72, 0, 1);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Collapsed: shrink toward the anchor corner and fade out, then drop out of
|
|
163
|
+
hit-testing (visibility + pointer-events) once the fade finishes so the
|
|
164
|
+
click-through overlay ignores it. Re-expanding reverses this instantly. */
|
|
165
|
+
.bubble-list.collapsed {
|
|
166
|
+
opacity: 0;
|
|
167
|
+
transform: scale(0.9);
|
|
168
|
+
pointer-events: none;
|
|
169
|
+
visibility: hidden;
|
|
170
|
+
transition: opacity 0.16s ease, transform 0.18s cubic-bezier(0.32, 0.72, 0, 1),
|
|
171
|
+
visibility 0s 0.18s;
|
|
153
172
|
}
|
|
154
173
|
|
|
155
174
|
/* Scrolling is by design (at most three bubbles are visible at once), so give
|
|
@@ -176,6 +195,13 @@ body {
|
|
|
176
195
|
.bubble-list[data-open-align="left"] { left: 0; right: auto; }
|
|
177
196
|
.bubble-list[data-open-align="right"] { right: 0; left: auto; }
|
|
178
197
|
|
|
198
|
+
/* Anchor the grow/shrink to whichever corner sits against the folder button, so
|
|
199
|
+
the panel appears to spring out of it (and collapse back into it). */
|
|
200
|
+
.bubble-list[data-open-direction="down"][data-open-align="left"] { transform-origin: top left; }
|
|
201
|
+
.bubble-list[data-open-direction="down"][data-open-align="right"] { transform-origin: top right; }
|
|
202
|
+
.bubble-list[data-open-direction="up"][data-open-align="left"] { transform-origin: bottom left; }
|
|
203
|
+
.bubble-list[data-open-direction="up"][data-open-align="right"] { transform-origin: bottom right; }
|
|
204
|
+
|
|
179
205
|
.bubble {
|
|
180
206
|
display: flex;
|
|
181
207
|
align-items: flex-start;
|
|
@@ -270,3 +296,13 @@ body {
|
|
|
270
296
|
@keyframes status-spin {
|
|
271
297
|
to { transform: rotate(360deg); }
|
|
272
298
|
}
|
|
299
|
+
|
|
300
|
+
/* Respect a reduced-motion preference: snap the folder open/closed instead of
|
|
301
|
+
animating the caret and the panel. */
|
|
302
|
+
@media (prefers-reduced-motion: reduce) {
|
|
303
|
+
.caret,
|
|
304
|
+
.bubble-list,
|
|
305
|
+
.bubble-list.collapsed {
|
|
306
|
+
transition: none;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
@@ -110,7 +110,7 @@ test("updates the folder button in place (count and urgency dot)", () => {
|
|
|
110
110
|
}
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
-
test("
|
|
113
|
+
test("keeps the list mounted but marked collapsed, preserving scroll across a toggle", () => {
|
|
114
114
|
const restoreDocument = installFakeDocument();
|
|
115
115
|
try {
|
|
116
116
|
const container = new FakeElement("div");
|
|
@@ -120,11 +120,20 @@ test("preserves scroll position across collapse and expand", () => {
|
|
|
120
120
|
listView.render(bubbles);
|
|
121
121
|
findList(container).scrollTop = 100;
|
|
122
122
|
|
|
123
|
+
// Collapsing keeps the node mounted (so it can animate out) but marks it
|
|
124
|
+
// collapsed and drops the `interactive` marker so hit-testing ignores it.
|
|
123
125
|
listView.toggle();
|
|
124
|
-
|
|
125
|
-
|
|
126
|
+
const collapsed = findList(container);
|
|
127
|
+
assert.ok(collapsed);
|
|
128
|
+
assert.ok(collapsed.className.split(" ").includes("collapsed"));
|
|
129
|
+
assert.ok(!collapsed.className.split(" ").includes("interactive"));
|
|
126
130
|
|
|
127
|
-
|
|
131
|
+
listView.toggle();
|
|
132
|
+
const expanded = findList(container);
|
|
133
|
+
assert.ok(!expanded.className.split(" ").includes("collapsed"));
|
|
134
|
+
assert.ok(expanded.className.split(" ").includes("interactive"));
|
|
135
|
+
// The scrollTop rode through untouched since the node was never rebuilt.
|
|
136
|
+
assert.equal(expanded.scrollTop, 100);
|
|
128
137
|
} finally {
|
|
129
138
|
restoreDocument();
|
|
130
139
|
}
|
|
@@ -254,7 +263,7 @@ function makeBubbles(sessionIds) {
|
|
|
254
263
|
}
|
|
255
264
|
|
|
256
265
|
function findList(container) {
|
|
257
|
-
return container.children.find((child) => child.className
|
|
266
|
+
return container.children.find((child) => child.className.split(" ").includes("bubble-list"));
|
|
258
267
|
}
|
|
259
268
|
|
|
260
269
|
function findButton(container) {
|