@celestia-island/hikari 0.43.15 → 0.43.16
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/package.json
CHANGED
|
@@ -97,20 +97,25 @@ describe("HkAuthMethodList", () => {
|
|
|
97
97
|
await nextTick();
|
|
98
98
|
const wrapper = c.querySelector<HTMLElement>(".s-auth-methods-list")!;
|
|
99
99
|
const spans = [...wrapper.querySelectorAll<HTMLElement>(".s-auth-methods-label")];
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
100
|
+
// The scale has to come from a LABEL: the list is `display: contents` and
|
|
101
|
+
// generates no box at all (Blink reports offsetWidth 0 and a 0x0 rect), so
|
|
102
|
+
// stubbing a box on the list would only ever be green by accident.
|
|
103
|
+
for (const [i, drawn] of [58, 61.4].entries()) {
|
|
104
|
+
Object.defineProperty(spans[i]!, "getBoundingClientRect", {
|
|
105
|
+
configurable: true,
|
|
106
|
+
value: () => ({ left: 0, top: 0, right: drawn * 2, bottom: 40, width: drawn * 2, height: 40, x: 0, y: 0 }) as DOMRect,
|
|
107
|
+
});
|
|
108
|
+
// Laid out at half: the label is drawn twice the size it is laid out at.
|
|
109
|
+
Object.defineProperty(spans[i]!, "offsetWidth", { configurable: true, get: () => drawn });
|
|
110
|
+
Object.defineProperty(spans[i]!, "offsetHeight", { configurable: true, get: () => 20 });
|
|
111
|
+
}
|
|
109
112
|
const instance = (wrapper as unknown as { __vueParentComponent?: { exposed?: Record<string, unknown> } })
|
|
110
113
|
.__vueParentComponent?.exposed;
|
|
111
114
|
(instance!.measure as () => void)();
|
|
112
|
-
//
|
|
113
|
-
|
|
115
|
+
// 122.8 drawn are 61.4 of the column's own: ceil 62, plus the 1px headroom.
|
|
116
|
+
// Reading the scale off the LIST instead — the shape this shipped with —
|
|
117
|
+
// yields 1 for a `display: contents` element and publishes the drawn 124px.
|
|
118
|
+
expect(wrapper.style.getPropertyValue("--auth-methods-label-width")).toBe("63px");
|
|
114
119
|
});
|
|
115
120
|
|
|
116
121
|
it("leaves the column fallback intact when no text metrics are available", async () => {
|
|
@@ -86,12 +86,21 @@ export default defineComponent({
|
|
|
86
86
|
// published as a CSS length the column is laid out with: in a scaled or
|
|
87
87
|
// zoomed root the two differ by the scale of the space, so the column
|
|
88
88
|
// would come out that many times too wide (see `layoutGeometry`).
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
//
|
|
90
|
+
// The scale is read from the LABEL — the element the box was measured
|
|
91
|
+
// on. Reading it from the list itself cannot work: the list is
|
|
92
|
+
// `display: contents`, so it generates no box at all (measured in Blink:
|
|
93
|
+
// `offsetWidth` 0, a 0x0 rect, computed width `auto`), and every reader
|
|
94
|
+
// of a box reports nothing to divide by.
|
|
91
95
|
for (const label of root.querySelectorAll<HTMLElement>(".s-auth-methods-label")) {
|
|
92
96
|
const previous = label.style.width;
|
|
93
97
|
label.style.width = "max-content";
|
|
94
|
-
|
|
98
|
+
const drawn = label.getBoundingClientRect().width;
|
|
99
|
+
const scale = drawnScale(label);
|
|
100
|
+
const factor = Number.isFinite(scale.x) && scale.x > 0 ? scale.x : 1;
|
|
101
|
+
// `offsetWidth` is the laid-out fallback — already in the units the
|
|
102
|
+
// published length is used in, so it needs no division.
|
|
103
|
+
widest = Math.max(widest, factor > 0 ? drawn / factor : label.offsetWidth);
|
|
95
104
|
label.style.width = previous;
|
|
96
105
|
}
|
|
97
106
|
// Guard: only publish when real text metrics were observed, so a
|
|
@@ -110,6 +110,13 @@ export function layoutOffset(el: HTMLElement, frame?: HTMLElement | null): Layou
|
|
|
110
110
|
* while the drawn-box equivalent (`rect.top - frameRect.top + frame.scrollTop`)
|
|
111
111
|
* is `k` times too large at zoom `k`.
|
|
112
112
|
*
|
|
113
|
+
* The frame's own border is excluded exactly when the walk passed THROUGH it
|
|
114
|
+
* (`through`): a positioned frame measures its children from its padding edge,
|
|
115
|
+
* so the difference does too, while a frame that is not in the chain (a
|
|
116
|
+
* `position: static` scroller) is measured from its border box. The result is
|
|
117
|
+
* the value `scrollTop`/`scrollLeft` want in both cases — those are offsets
|
|
118
|
+
* into the scrollable area, whose origin is the padding edge.
|
|
119
|
+
*
|
|
113
120
|
* `y` (and `x`) are `NaN` when the chain cannot be added up or when `frame` is
|
|
114
121
|
* not an ancestor of `el` — the difference is meaningless then, and callers
|
|
115
122
|
* check `Number.isFinite` before scrolling with it. */
|
|
@@ -208,6 +208,34 @@ describe("attachOverlayScrollbars", () => {
|
|
|
208
208
|
expect(viewport.scrollTop).toBe(150);
|
|
209
209
|
});
|
|
210
210
|
|
|
211
|
+
it("maps a horizontal track click onto the RTL scroll range", () => {
|
|
212
|
+
const { viewport, wrapper, handle } = mountViewport("horizontal");
|
|
213
|
+
stubGeometry(viewport, {
|
|
214
|
+
scrollHeight: 100, clientHeight: 100, scrollTop: 0,
|
|
215
|
+
scrollWidth: 400, clientWidth: 100, scrollLeft: 0,
|
|
216
|
+
});
|
|
217
|
+
// An RTL scroller counts `scrollLeft` down from 0 to −max (see the thumb
|
|
218
|
+
// mirroring): the track's LEFT end is the content's end, so a click at the
|
|
219
|
+
// halfway point must land at −150, not +150.
|
|
220
|
+
viewport.style.direction = "rtl";
|
|
221
|
+
handle.update();
|
|
222
|
+
const track = wrapper.querySelector<HTMLElement>(".hk-scrollbar-track")!;
|
|
223
|
+
vi.spyOn(track, "getBoundingClientRect").mockReturnValue({
|
|
224
|
+
top: 0, left: 0, width: 100, height: 6,
|
|
225
|
+
bottom: 6, right: 100, x: 0, y: 0, toJSON: () => ({}),
|
|
226
|
+
} as DOMRect);
|
|
227
|
+
track.dispatchEvent(new MouseEvent("click", { bubbles: true, clientX: 25 }));
|
|
228
|
+
// A quarter along the track, measured from its left = three quarters into
|
|
229
|
+
// the content from its (right-hand) start: −0.75 × 300.
|
|
230
|
+
expect(viewport.scrollLeft).toBe(-225);
|
|
231
|
+
// The magnitude is what the thumb's own mirroring reads back as progress
|
|
232
|
+
// (|scrollLeft| / max), so click and thumb now agree on the position.
|
|
233
|
+
// The LTR bar keeps mapping straight through.
|
|
234
|
+
viewport.style.direction = "ltr";
|
|
235
|
+
track.dispatchEvent(new MouseEvent("click", { bubbles: true, clientX: 25 }));
|
|
236
|
+
expect(viewport.scrollLeft).toBe(75);
|
|
237
|
+
});
|
|
238
|
+
|
|
211
239
|
it("marks and unmarks a static parent with inline position:relative (guard)", () => {
|
|
212
240
|
const { wrapper, handle } = mountViewport("vertical");
|
|
213
241
|
// happy-dom computes no CSS → position computes "static" → the
|
|
@@ -266,8 +266,16 @@ export function attachOverlayScrollbars(
|
|
|
266
266
|
: (e.clientY - rect.top) / rect.height;
|
|
267
267
|
const { scrollSize, clientSize } = axisMetrics(viewport, horizontal);
|
|
268
268
|
const max = scrollSize - clientSize;
|
|
269
|
-
if (horizontal)
|
|
270
|
-
|
|
269
|
+
if (horizontal) {
|
|
270
|
+
// RTL: the track's left end is the content's END, and `scrollLeft`
|
|
271
|
+
// runs 0 → −max (see the thumb mirroring in `update`), so a click at
|
|
272
|
+
// `ratio` from the left asks for `(ratio - 1) * max`. Without this the
|
|
273
|
+
// thumb and the click disagreed in RTL: the thumb was mirrored but the
|
|
274
|
+
// click still mapped LTR, so clicking right of the thumb scrolled the
|
|
275
|
+
// other way.
|
|
276
|
+
const rtl = isRtlAxis(viewport, true);
|
|
277
|
+
viewport.scrollLeft = rtl ? (ratio - 1) * max : ratio * max;
|
|
278
|
+
} else viewport.scrollTop = ratio * max;
|
|
271
279
|
};
|
|
272
280
|
const onEnter = () => s.track.classList.add("is-hovering");
|
|
273
281
|
const onLeave = () => s.track.classList.remove("is-hovering");
|