@celestia-island/hikari 0.55.275 → 0.55.276
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
|
@@ -53,6 +53,32 @@
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/* The horizontal axis: the SAME list laid out as ONE never-wrapping row
|
|
57
|
+
* (wowsp's overlay-columns editor is the first host). Only geometry
|
|
58
|
+
* changes — the drop line moves to the leading/trailing edge (the
|
|
59
|
+
* horizontal twin of the top/bottom inset above; the descendant rules
|
|
60
|
+
* below out-specify the vertical box-shadow, so they REPLACE it rather
|
|
61
|
+
* than stack onto it), and min-width: 0 lets an over-full row shrink
|
|
62
|
+
* its items so the single line always holds (the content lane keeps its
|
|
63
|
+
* own min-width, so ellipsis chains keep working inside them). The
|
|
64
|
+
* reserved handle lane stays the row's leading column exactly as it is
|
|
65
|
+
* vertically, and the actions lane stays the row's trailing edge. */
|
|
66
|
+
.hk-draggable-list[data-axis="horizontal"] {
|
|
67
|
+
flex-direction: row;
|
|
68
|
+
flex-wrap: nowrap;
|
|
69
|
+
|
|
70
|
+
.hk-draggable-list-item {
|
|
71
|
+
min-width: 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.hk-draggable-list-item[data-placeholder="before"] {
|
|
75
|
+
box-shadow: inset 2px 0 0 0 var(--hi-color-primary, #7aa2f7);
|
|
76
|
+
}
|
|
77
|
+
.hk-draggable-list-item[data-placeholder="after"] {
|
|
78
|
+
box-shadow: inset -2px 0 0 0 var(--hi-color-primary, #7aa2f7);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
56
82
|
.hk-draggable-list-handle {
|
|
57
83
|
display: flex;
|
|
58
84
|
align-items: center;
|
|
@@ -108,3 +108,66 @@ describe("HkDraggableList actions-lane sheet", () => {
|
|
|
108
108
|
expect(content).toContain("flex: 1");
|
|
109
109
|
});
|
|
110
110
|
});
|
|
111
|
+
|
|
112
|
+
describe("HkDraggableList horizontal-axis sheet", () => {
|
|
113
|
+
/** axis="horizontal" (DOM twin: the horizontal-axis cases in
|
|
114
|
+
* HkDraggableList.test.tsx) lays the SAME list as one never-wrapping
|
|
115
|
+
* row. Only geometry changes: the drop line moves to the item's
|
|
116
|
+
* leading/trailing edge — the horizontal twin of the top/bottom inset
|
|
117
|
+
* — and the items become shrinkable so the single line always holds. */
|
|
118
|
+
const css = listCss();
|
|
119
|
+
|
|
120
|
+
// ruleBody selectors are written in the COMPILED form: sass expanded
|
|
121
|
+
// output drops attribute-value quotes ([data-axis=horizontal]).
|
|
122
|
+
|
|
123
|
+
it("lays the horizontal list out as one never-wrapping row", () => {
|
|
124
|
+
const body = ruleBody(css, ".hk-draggable-list[data-axis=horizontal]");
|
|
125
|
+
expect(body, "the horizontal-axis layout rule must exist").not.toBeNull();
|
|
126
|
+
expect(body).toContain("flex-direction: row");
|
|
127
|
+
expect(body).toContain("flex-wrap: nowrap");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("lets an over-full row shrink its items so the one line holds", () => {
|
|
131
|
+
const body = ruleBody(
|
|
132
|
+
css,
|
|
133
|
+
".hk-draggable-list[data-axis=horizontal] .hk-draggable-list-item",
|
|
134
|
+
);
|
|
135
|
+
expect(body, "the horizontal item rule must exist").not.toBeNull();
|
|
136
|
+
expect(body).toContain("min-width: 0");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("marks the drop edge on the leading/trailing side, not top/bottom", () => {
|
|
140
|
+
const before = ruleBody(
|
|
141
|
+
css,
|
|
142
|
+
".hk-draggable-list[data-axis=horizontal] .hk-draggable-list-item[data-placeholder=before]",
|
|
143
|
+
);
|
|
144
|
+
expect(before, "the horizontal before rule must exist").not.toBeNull();
|
|
145
|
+
expect(before).toContain("inset 2px 0 0 0");
|
|
146
|
+
expect(before).not.toContain("inset 0 2px");
|
|
147
|
+
expect(before).not.toContain("inset 0 -2px");
|
|
148
|
+
|
|
149
|
+
const after = ruleBody(
|
|
150
|
+
css,
|
|
151
|
+
".hk-draggable-list[data-axis=horizontal] .hk-draggable-list-item[data-placeholder=after]",
|
|
152
|
+
);
|
|
153
|
+
expect(after, "the horizontal after rule must exist").not.toBeNull();
|
|
154
|
+
expect(after).toContain("inset -2px 0 0 0");
|
|
155
|
+
expect(after).not.toContain("inset 0 2px");
|
|
156
|
+
expect(after).not.toContain("inset 0 -2px");
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("keeps the vertical drop edges on top/bottom for the default axis", () => {
|
|
160
|
+
// First source match = the plain vertical rule (the horizontal twin
|
|
161
|
+
// comes later and under a descendant selector); its body must carry
|
|
162
|
+
// the top/bottom inset and never the horizontal one.
|
|
163
|
+
const before = ruleBody(css, ".hk-draggable-list-item[data-placeholder=before]");
|
|
164
|
+
expect(before, "the vertical before rule must exist").not.toBeNull();
|
|
165
|
+
expect(before).toContain("inset 0 2px 0 0");
|
|
166
|
+
expect(before).not.toContain("inset 2px 0 0 0");
|
|
167
|
+
|
|
168
|
+
const after = ruleBody(css, ".hk-draggable-list-item[data-placeholder=after]");
|
|
169
|
+
expect(after, "the vertical after rule must exist").not.toBeNull();
|
|
170
|
+
expect(after).toContain("inset 0 -2px 0 0");
|
|
171
|
+
expect(after).not.toContain("inset -2px 0 0 0");
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -525,3 +525,193 @@ describe("HkDraggableList actions lane", () => {
|
|
|
525
525
|
fireWin("pointerup");
|
|
526
526
|
});
|
|
527
527
|
});
|
|
528
|
+
|
|
529
|
+
describe("HkDraggableList horizontal axis", () => {
|
|
530
|
+
/** axis="horizontal" lays the SAME list as one never-wrapping row
|
|
531
|
+
* (wowsp's overlay-columns editor). Only the pointer geometry
|
|
532
|
+
* changes: the arm reads |dx| alone, the ghost follows BOTH axes,
|
|
533
|
+
* and the placeholder targets slots by pointer.x against their
|
|
534
|
+
* horizontal midpoints (the plain single-line rule — the list never
|
|
535
|
+
* wraps). Roles, slots, the reserved handle lane and the
|
|
536
|
+
* commit-once-at-pointerup contract stay exactly as vertical. */
|
|
537
|
+
|
|
538
|
+
const PointerEventCtor: typeof MouseEvent =
|
|
539
|
+
(globalThis as { PointerEvent?: typeof MouseEvent }).PointerEvent ?? MouseEvent;
|
|
540
|
+
|
|
541
|
+
const pointerInit: MouseEventInit = { bubbles: true, cancelable: true };
|
|
542
|
+
|
|
543
|
+
function fire(el: Element, type: string, x = 0, y = 0) {
|
|
544
|
+
el.dispatchEvent(new PointerEventCtor(type, {
|
|
545
|
+
...pointerInit,
|
|
546
|
+
button: 0,
|
|
547
|
+
clientX: x,
|
|
548
|
+
clientY: y,
|
|
549
|
+
}));
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function fireWin(type: string, x = 0, y = 0) {
|
|
553
|
+
window.dispatchEvent(new PointerEventCtor(type, {
|
|
554
|
+
...pointerInit,
|
|
555
|
+
clientX: x,
|
|
556
|
+
clientY: y,
|
|
557
|
+
}));
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const emitLog: Array<{ from: number; to: number }> = [];
|
|
561
|
+
|
|
562
|
+
function mountHorizontal(items: string[], opts?: { lockedKeys?: string[] }): HTMLElement {
|
|
563
|
+
emitLog.length = 0;
|
|
564
|
+
const container = document.createElement("div");
|
|
565
|
+
document.body.appendChild(container);
|
|
566
|
+
const app = createApp({
|
|
567
|
+
render: () =>
|
|
568
|
+
h(
|
|
569
|
+
HDraggableList,
|
|
570
|
+
{
|
|
571
|
+
items: items.map((key) => ({ key })),
|
|
572
|
+
axis: "horizontal",
|
|
573
|
+
lockedKeys: opts?.lockedKeys ?? [],
|
|
574
|
+
onReorder: (from: number, to: number) => emitLog.push({ from, to }),
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
item: ({ item }: { item: { key: string } }) =>
|
|
578
|
+
h("span", { class: "row-body" }, item.key),
|
|
579
|
+
},
|
|
580
|
+
),
|
|
581
|
+
});
|
|
582
|
+
app.mount(container);
|
|
583
|
+
mounts.push({ app, container });
|
|
584
|
+
return container.querySelector(".hk-draggable-list") as HTMLElement;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function rowsOf(root: HTMLElement): HTMLElement[] {
|
|
588
|
+
return Array.from(root.querySelectorAll(".hk-draggable-list-item")) as HTMLElement[];
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/** happy-dom reports zero rects; pin the slots to a 100px-each single
|
|
592
|
+
* line [0..100][100..200][200..300] (40px tall) so the midpoint rule
|
|
593
|
+
* walks real geometry. */
|
|
594
|
+
function layLine(root: HTMLElement, width = 100, height = 40): HTMLElement[] {
|
|
595
|
+
const rows = rowsOf(root);
|
|
596
|
+
rows.forEach((row, i) => {
|
|
597
|
+
const left = i * width;
|
|
598
|
+
row.getBoundingClientRect = () =>
|
|
599
|
+
({
|
|
600
|
+
left,
|
|
601
|
+
top: 0,
|
|
602
|
+
right: left + width,
|
|
603
|
+
bottom: height,
|
|
604
|
+
width,
|
|
605
|
+
height,
|
|
606
|
+
x: left,
|
|
607
|
+
y: 0,
|
|
608
|
+
toJSON: () => ({}),
|
|
609
|
+
}) as DOMRect;
|
|
610
|
+
});
|
|
611
|
+
return rows;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
it("keeps list semantics and marks no axis on the vertical default", () => {
|
|
615
|
+
const verticalRoot = mountList(["a"], []);
|
|
616
|
+
expect(verticalRoot.hasAttribute("data-axis")).toBe(false);
|
|
617
|
+
|
|
618
|
+
const root = mountHorizontal(["a", "b"]);
|
|
619
|
+
expect(root.getAttribute("data-axis")).toBe("horizontal");
|
|
620
|
+
expect(root.getAttribute("role")).toBe("list");
|
|
621
|
+
const rows = rowsOf(root);
|
|
622
|
+
expect(rows).toHaveLength(2);
|
|
623
|
+
expect(rows.every((row) => row.getAttribute("role") === "listitem")).toBe(true);
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
it("arms on |dx| alone — vertical travel never arms a horizontal drag", async () => {
|
|
627
|
+
const root = mountHorizontal(["a", "b"]);
|
|
628
|
+
layLine(root);
|
|
629
|
+
const row = rowsOf(root)[0]!;
|
|
630
|
+
|
|
631
|
+
fire(row, "pointerdown", 50, 20);
|
|
632
|
+
// 30px of pure-Y travel: under the |dx| arm, still a plain press.
|
|
633
|
+
fireWin("pointermove", 50, 50);
|
|
634
|
+
await nextTick();
|
|
635
|
+
expect(row.hasAttribute("data-dragging")).toBe(false);
|
|
636
|
+
|
|
637
|
+
// 6px of pure-X travel crosses ARM_PX: the drag publishes.
|
|
638
|
+
fireWin("pointermove", 56, 50);
|
|
639
|
+
await nextTick();
|
|
640
|
+
expect(row.getAttribute("data-dragging")).not.toBeNull();
|
|
641
|
+
|
|
642
|
+
fireWin("pointerup");
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
it("moves the ghost on BOTH axes", async () => {
|
|
646
|
+
const root = mountHorizontal(["a"]);
|
|
647
|
+
layLine(root); // slot [0..100]
|
|
648
|
+
// A stale ghost from an earlier failed run would shadow this assert.
|
|
649
|
+
document.querySelectorAll(".hk-draggable-list-ghost").forEach((n) => n.remove());
|
|
650
|
+
const row = rowsOf(root)[0]!;
|
|
651
|
+
|
|
652
|
+
fire(row, "pointerdown", 50, 20); // grabOffset (50, 20)
|
|
653
|
+
fireWin("pointermove", 106, 20); // arm past ARM_PX on |dx|
|
|
654
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
655
|
+
let ghost = document.querySelector(".hk-draggable-list-ghost") as HTMLElement | null;
|
|
656
|
+
expect(ghost, "the ghost must rise next frame").not.toBeNull();
|
|
657
|
+
|
|
658
|
+
fireWin("pointermove", 260, 130); // both axes travel: (260-50, 130-20)
|
|
659
|
+
ghost = document.querySelector(".hk-draggable-list-ghost");
|
|
660
|
+
expect(ghost!.style.transform).toBe("translate3d(210px, 110px, 0)");
|
|
661
|
+
|
|
662
|
+
fireWin("pointerup");
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
it("targets the placeholder by pointer.x against slot midpoints", async () => {
|
|
666
|
+
const root = mountHorizontal(["a", "b", "c"]);
|
|
667
|
+
const rows = layLine(root);
|
|
668
|
+
const row = rows[0]!;
|
|
669
|
+
|
|
670
|
+
fire(row, "pointerdown", 50, 20);
|
|
671
|
+
fireWin("pointermove", 60, 20); // arm; still left of b's midpoint (150) → before b
|
|
672
|
+
await nextTick();
|
|
673
|
+
expect(rows[1]!.getAttribute("data-placeholder")).toBe("after");
|
|
674
|
+
|
|
675
|
+
fireWin("pointermove", 180, 20); // past b's midpoint, under c's (250) → before c
|
|
676
|
+
await nextTick();
|
|
677
|
+
expect(rows[1]!.hasAttribute("data-placeholder")).toBe(false);
|
|
678
|
+
expect(rows[2]!.getAttribute("data-placeholder")).toBe("after");
|
|
679
|
+
|
|
680
|
+
fireWin("pointerup");
|
|
681
|
+
// Commit-once-at-pointerup: ONE reorder, at the placeholder slot.
|
|
682
|
+
expect(emitLog).toEqual([{ from: 0, to: 2 }]);
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
it("marks the leading edge when the pointer retreats before earlier slots", async () => {
|
|
686
|
+
const root = mountHorizontal(["a", "b", "c"]);
|
|
687
|
+
const rows = layLine(root);
|
|
688
|
+
const row = rows[2]!;
|
|
689
|
+
|
|
690
|
+
fire(row, "pointerdown", 250, 20);
|
|
691
|
+
fireWin("pointermove", 240, 20); // arm
|
|
692
|
+
await nextTick();
|
|
693
|
+
|
|
694
|
+
fireWin("pointermove", 20, 20); // before a's midpoint (50) → lands before a
|
|
695
|
+
await nextTick();
|
|
696
|
+
expect(rows[0]!.getAttribute("data-placeholder")).toBe("before");
|
|
697
|
+
expect(rows[1]!.hasAttribute("data-placeholder")).toBe(false);
|
|
698
|
+
|
|
699
|
+
fireWin("pointerup");
|
|
700
|
+
expect(emitLog).toEqual([{ from: 2, to: 0 }]);
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it("skips locked slots when walking the row", async () => {
|
|
704
|
+
const root = mountHorizontal(["a", "b", "c"], { lockedKeys: ["b"] });
|
|
705
|
+
const rows = layLine(root);
|
|
706
|
+
const row = rows[0]!;
|
|
707
|
+
|
|
708
|
+
fire(row, "pointerdown", 50, 20);
|
|
709
|
+
fireWin("pointermove", 160, 20); // past b's midpoint — b is locked, c takes the drop
|
|
710
|
+
await nextTick();
|
|
711
|
+
expect(rows[1]!.hasAttribute("data-placeholder")).toBe(false);
|
|
712
|
+
expect(rows[2]!.getAttribute("data-placeholder")).toBe("after");
|
|
713
|
+
|
|
714
|
+
fireWin("pointerup");
|
|
715
|
+
expect(emitLog).toEqual([{ from: 0, to: 2 }]);
|
|
716
|
+
});
|
|
717
|
+
});
|
|
@@ -46,6 +46,16 @@ export default defineComponent({
|
|
|
46
46
|
* mirroring the menu/vocabulary a11y rulings (#736/#737): chrome
|
|
47
47
|
* announces structure, the host supplies the name. */
|
|
48
48
|
label: { type: String, default: undefined },
|
|
49
|
+
/** The direction the list flows: "vertical" (the default) stacks
|
|
50
|
+
* rows in one column; "horizontal" lays them in ONE never-wrapping
|
|
51
|
+
* row (wowsp's overlay-columns editor is the first host). Only the
|
|
52
|
+
* pointer geometry differs — the arm threshold reads |dx| instead
|
|
53
|
+
* of the hypot, the ghost follows BOTH axes, and the placeholder
|
|
54
|
+
* targets slots by pointer.x against their horizontal midpoints
|
|
55
|
+
* (the same adjacent-midpoint rule, walked along the row). Roles,
|
|
56
|
+
* slots, the reserved handle lane and the commit-once-at-pointerup
|
|
57
|
+
* contract are identical on both axes. */
|
|
58
|
+
axis: { type: String as PropType<"vertical" | "horizontal">, default: "vertical" },
|
|
49
59
|
},
|
|
50
60
|
emits: {
|
|
51
61
|
reorder: (_fromIndex: number, _toIndex: number) => true,
|
|
@@ -173,21 +183,27 @@ export default defineComponent({
|
|
|
173
183
|
}
|
|
174
184
|
}
|
|
175
185
|
|
|
176
|
-
function moveGhost(clientY: number) {
|
|
186
|
+
function moveGhost(clientX: number, clientY: number) {
|
|
177
187
|
const s = drag.value;
|
|
178
188
|
if (!s || !ghostNode) return;
|
|
179
189
|
// gx/gy are the ghost's visual top-left (client coords minus the
|
|
180
190
|
// visual grab offset); the teleported ghost paints local px scaled
|
|
181
|
-
// by the cumulative zoom — divide once at the write layer.
|
|
191
|
+
// by the cumulative zoom — divide once at the write layer. A
|
|
192
|
+
// vertical drag pins the ghost to the source row's x (rows only
|
|
193
|
+
// ever move along the column); a horizontal drag follows BOTH
|
|
194
|
+
// axes — a card drifts vertically as naturally as a row drifts.
|
|
182
195
|
const z = ancestorZoom(document.body);
|
|
183
|
-
const gx = (
|
|
196
|
+
const gx = (props.axis === "horizontal"
|
|
197
|
+
? clientX - s.grabOffsetX
|
|
198
|
+
: s.originX - s.grabOffsetX) / z;
|
|
184
199
|
const gy = (clientY - s.grabOffsetY) / z;
|
|
185
200
|
ghostNode.style.transform = `translate3d(${gx}px, ${gy}px, 0)`;
|
|
186
201
|
}
|
|
187
202
|
|
|
188
|
-
function recomputePlaceholder(clientY: number) {
|
|
203
|
+
function recomputePlaceholder(clientX: number, clientY: number) {
|
|
189
204
|
const s = drag.value;
|
|
190
205
|
if (!s) return;
|
|
206
|
+
const horizontal = props.axis === "horizontal";
|
|
191
207
|
const rows = rowEls.value;
|
|
192
208
|
let target = s.fromIndex;
|
|
193
209
|
let fallback = s.fromIndex;
|
|
@@ -200,10 +216,15 @@ export default defineComponent({
|
|
|
200
216
|
if (!el) continue;
|
|
201
217
|
|
|
202
218
|
const rect = el.getBoundingClientRect();
|
|
203
|
-
|
|
219
|
+
// The adjacent-midpoint rule along the list's ONE axis — the
|
|
220
|
+
// column walks by y, the row walks by x. The horizontal form is
|
|
221
|
+
// the plain rule over every slot: this list never wraps, so the
|
|
222
|
+
// pointer's line is always the whole row (no banding).
|
|
223
|
+
const mid = horizontal ? rect.left + rect.width / 2 : rect.top + rect.height / 2;
|
|
224
|
+
const pointer = horizontal ? clientX : clientY;
|
|
204
225
|
fallback = i;
|
|
205
226
|
|
|
206
|
-
if (
|
|
227
|
+
if (pointer < mid) {
|
|
207
228
|
target = i;
|
|
208
229
|
break;
|
|
209
230
|
}
|
|
@@ -227,8 +248,14 @@ export default defineComponent({
|
|
|
227
248
|
|
|
228
249
|
if (!p.armed) {
|
|
229
250
|
// Arm past the threshold: only now does the drag publish, the
|
|
230
|
-
// ghost rise, and the gesture claim its defaults.
|
|
231
|
-
|
|
251
|
+
// ghost rise, and the gesture claim its defaults. Vertical arms
|
|
252
|
+
// on the hypot (travel along either axis counts); horizontal
|
|
253
|
+
// arms on |dx| alone — a y-wiggle under a pressed card is
|
|
254
|
+
// jitter, never a drag intent.
|
|
255
|
+
const travelled = props.axis === "horizontal"
|
|
256
|
+
? Math.abs(e.clientX - p.state.originX)
|
|
257
|
+
: Math.hypot(e.clientX - p.state.originX, e.clientY - p.state.originY);
|
|
258
|
+
if (travelled <= ARM_PX) {
|
|
232
259
|
return;
|
|
233
260
|
}
|
|
234
261
|
p.armed = true;
|
|
@@ -243,8 +270,8 @@ export default defineComponent({
|
|
|
243
270
|
}
|
|
244
271
|
|
|
245
272
|
e.preventDefault();
|
|
246
|
-
moveGhost(e.clientY);
|
|
247
|
-
recomputePlaceholder(e.clientY);
|
|
273
|
+
moveGhost(e.clientX, e.clientY);
|
|
274
|
+
recomputePlaceholder(e.clientX, e.clientY);
|
|
248
275
|
}
|
|
249
276
|
|
|
250
277
|
function onPointerUp() {
|
|
@@ -291,6 +318,7 @@ export default defineComponent({
|
|
|
291
318
|
class="hk-draggable-list"
|
|
292
319
|
role="list"
|
|
293
320
|
aria-label={props.label || undefined}
|
|
321
|
+
data-axis={props.axis === "horizontal" ? "horizontal" : undefined}
|
|
294
322
|
data-dragging={s ? "" : undefined}
|
|
295
323
|
data-all-locked={allLocked || undefined}
|
|
296
324
|
>
|
package/src/runtime/index.ts
CHANGED
|
@@ -51,6 +51,13 @@ export {
|
|
|
51
51
|
DESKTOP_GUTTER_PX,
|
|
52
52
|
GUTTER_MOBILE_MAX_WIDTH,
|
|
53
53
|
} from "./viewportGutter";
|
|
54
|
+
// The cumulative CSS-zoom factor between a body-teleported element and
|
|
55
|
+
// the viewport root (runtime/cssZoom.ts). Hosts that measure anchors
|
|
56
|
+
// with gBCR and write fixed px themselves — a horizontal HkDraggableList
|
|
57
|
+
// port especially — need the same ghost-math division the components run
|
|
58
|
+
// internally; until this re-export existed the factor had to be copied
|
|
59
|
+
// verbatim into consumers.
|
|
60
|
+
export { ancestorZoom } from "./cssZoom";
|
|
54
61
|
export {
|
|
55
62
|
displayWidthUnits,
|
|
56
63
|
glyphWidthUnits,
|