@celestia-island/hikari 0.40.36 → 0.41.0
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
|
@@ -38,6 +38,52 @@ afterEach(() => {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
describe("HkStatusBar", () => {
|
|
41
|
+
it("renders extraDetails rows capped at 400px with an ellipsized, title-backed value", async () => {
|
|
42
|
+
const container = mountBar({
|
|
43
|
+
version: "1.2.3",
|
|
44
|
+
connectionStatus: "connected",
|
|
45
|
+
connectionInfo: INFO,
|
|
46
|
+
extraDetails: [
|
|
47
|
+
{ key: "gateway", label: "Gateway", value: "gateway.celestia.world" },
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
await nextTick();
|
|
51
|
+
// Popover content teleports to <body> and only renders once the tag
|
|
52
|
+
// is hovered open (same grammar as the protocol/network rows).
|
|
53
|
+
const tag = container.querySelector<HTMLElement>(".s-status-bar-tag")!;
|
|
54
|
+
tag.dispatchEvent(new MouseEvent("mouseenter"));
|
|
55
|
+
await nextTick();
|
|
56
|
+
const panel = document.body.querySelector<HTMLElement>(".hk-popover-panel");
|
|
57
|
+
expect(panel, "popover opens on hover").toBeTruthy();
|
|
58
|
+
const row = panel!.querySelector('[data-extra-detail="gateway"]');
|
|
59
|
+
expect(row).not.toBeNull();
|
|
60
|
+
expect((row as HTMLElement).style.maxWidth).toBe("400px");
|
|
61
|
+
const value = panel!.querySelector("span[title]") as HTMLElement;
|
|
62
|
+
expect(value.textContent).toBe("gateway.celestia.world");
|
|
63
|
+
expect(value.title).toBe("gateway.celestia.world");
|
|
64
|
+
expect(value.style.textOverflow).toBe("ellipsis");
|
|
65
|
+
// The label column keeps a minimum width — a long value must not
|
|
66
|
+
// squeeze it (the row space-between-aligns instead).
|
|
67
|
+
const label = row!.querySelector("span[style*='min-width'], span:nth-child(2)") as HTMLElement;
|
|
68
|
+
expect(label.style.minWidth).toBe("72px");
|
|
69
|
+
expect(label.style.flexShrink).toBe("0");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("renders no extraDetails rows when the prop is absent", async () => {
|
|
73
|
+
const container = mountBar({
|
|
74
|
+
version: "1.2.3",
|
|
75
|
+
connectionStatus: "connected",
|
|
76
|
+
connectionInfo: INFO,
|
|
77
|
+
});
|
|
78
|
+
await nextTick();
|
|
79
|
+
container
|
|
80
|
+
.querySelector<HTMLElement>(".s-status-bar-tag")!
|
|
81
|
+
.dispatchEvent(new MouseEvent("mouseenter"));
|
|
82
|
+
await nextTick();
|
|
83
|
+
const panel = document.body.querySelector(".hk-popover-panel");
|
|
84
|
+
expect(panel?.querySelector("[data-extra-detail]")).toBeNull();
|
|
85
|
+
});
|
|
86
|
+
|
|
41
87
|
it("mounts with the traffic light and version rows inline, protocol row on hover-open", async () => {
|
|
42
88
|
const container = mountBar({
|
|
43
89
|
version: "1.2.3",
|
|
@@ -96,6 +96,21 @@ export const HkStatusBar = defineComponent({
|
|
|
96
96
|
transportTier: { type: String as PropType<string>, default: undefined },
|
|
97
97
|
attemptNumber: { type: Number, default: undefined },
|
|
98
98
|
countdown: { type: Number, default: undefined },
|
|
99
|
+
/**
|
|
100
|
+
* Optional extra rows appended to the connection-info popover (after
|
|
101
|
+
* the network row): host-supplied label/value pairs with a prebuilt
|
|
102
|
+
* icon vnode each — the same loose-icon contract as HkAuthMethodList.
|
|
103
|
+
* Rows grow with their content but cap at 400px; values past the cap
|
|
104
|
+
* ellipsize (full text rides the native title). Lets a host surface
|
|
105
|
+
* stage endpoints (gateway, registry…) inside the popover without
|
|
106
|
+
* forking the component.
|
|
107
|
+
*/
|
|
108
|
+
extraDetails: {
|
|
109
|
+
type: Array as PropType<
|
|
110
|
+
Array<{ key: string; icon?: unknown; label: string; value: string }>
|
|
111
|
+
>,
|
|
112
|
+
default: undefined,
|
|
113
|
+
},
|
|
99
114
|
},
|
|
100
115
|
setup(props) {
|
|
101
116
|
const popupOpen = ref(false);
|
|
@@ -358,6 +373,13 @@ export const HkStatusBar = defineComponent({
|
|
|
358
373
|
<span style={{ opacity: 0.5, marginRight: "auto" }}>{t("hikari::statusBar.network", "Network")}</span>
|
|
359
374
|
<span>{regionDisplayName(info.region, locale, t)}{info.asn != null ? ` · AS${info.asn}` : ""}{info.isLocalhost ? " · " + t("hikari::statusBar.local", "Local") : ""}</span>
|
|
360
375
|
</div>
|
|
376
|
+
{(props.extraDetails ?? []).map((row) => (
|
|
377
|
+
<div key={row.key} data-extra-detail={row.key} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: "6px", maxWidth: "400px" }}>
|
|
378
|
+
<span style={{ opacity: 0.5, flexShrink: 0, display: "inline-flex" }}>{row.icon}</span>
|
|
379
|
+
<span style={{ opacity: 0.5, flexShrink: 0, minWidth: "72px", marginRight: "auto" }}>{row.label}</span>
|
|
380
|
+
<span title={row.value} style={{ fontFamily: `var(--font-mono, ${HIKARI_FONT_MONO})`, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{row.value}</span>
|
|
381
|
+
</div>
|
|
382
|
+
))}
|
|
361
383
|
</>
|
|
362
384
|
) : (
|
|
363
385
|
<div style={{ opacity: 0.5 }}>{t("hikari::statusBar.fetching", "Fetching connection info...")}</div>
|
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
align-items: center;
|
|
4
4
|
gap: 0;
|
|
5
5
|
width: 100%;
|
|
6
|
+
|
|
7
|
+
// Shared node geometry — ONE source of truth for the 24px indicator
|
|
8
|
+
// circle and the rail thickness. Every connector math keys off these:
|
|
9
|
+
// window-mode links center on the node along the inline axis, the
|
|
10
|
+
// vertical rail hangs centered under it, so both stay glued to the
|
|
11
|
+
// circles even if the node size ever changes.
|
|
12
|
+
--hk-timeline-node-size: 24px;
|
|
13
|
+
--hk-timeline-link-thickness: 2px;
|
|
14
|
+
--hk-timeline-link-top: calc(
|
|
15
|
+
(var(--hk-timeline-node-size) - var(--hk-timeline-link-thickness)) / 2
|
|
16
|
+
);
|
|
6
17
|
}
|
|
7
18
|
|
|
8
19
|
.hk-timeline[data-orientation="vertical"] {
|
|
@@ -26,12 +37,9 @@
|
|
|
26
37
|
// Geometry contract with the stacked steps above: the node is the 24px
|
|
27
38
|
// indicator circle at the top of every stack, so its center sits half a
|
|
28
39
|
// node below the cell top in ALL columns — that is where link segments go.
|
|
29
|
-
|
|
40
|
+
// (Node size, thickness and link-top are the shared tokens on
|
|
41
|
+
// `.hk-timeline`.)
|
|
30
42
|
--hk-timeline-link-clearance: 20px;
|
|
31
|
-
--hk-timeline-link-thickness: 2px;
|
|
32
|
-
--hk-timeline-link-top: calc(
|
|
33
|
-
(var(--hk-timeline-node-size) - var(--hk-timeline-link-thickness)) / 2
|
|
34
|
-
);
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
.hk-timeline[data-mode="window"] .hk-timeline-window {
|
|
@@ -142,6 +150,95 @@
|
|
|
142
150
|
transform: scaleX(-1);
|
|
143
151
|
}
|
|
144
152
|
|
|
153
|
+
/* ── Vertical window mode ────────────────────────────────────────────────
|
|
154
|
+
The height-starved sibling of the compact horizontal window: when the
|
|
155
|
+
host cannot fit the full column, the timeline collapses to the same
|
|
156
|
+
three-cell window (previous / current / next) stacked as equal thirds,
|
|
157
|
+
the outer two dimmed exactly like their left/right counterparts, and
|
|
158
|
+
vertical rail segments joining the node centers on an overlay — with
|
|
159
|
+
fading continuations past the top/bottom edges where hidden steps go. */
|
|
160
|
+
.hk-timeline[data-orientation="vertical"][data-mode="window"] {
|
|
161
|
+
grid-template-columns: minmax(0, 1fr);
|
|
162
|
+
grid-template-rows: repeat(3, minmax(0, 1fr));
|
|
163
|
+
align-items: stretch;
|
|
164
|
+
|
|
165
|
+
// Each cell centers its step within its third, which puts every node
|
|
166
|
+
// center at the SAME fractions of the host height as the horizontal
|
|
167
|
+
// mode puts them across the width (1/6, 1/2, 5/6) — the link math
|
|
168
|
+
// below is the horizontal one rotated, nothing more.
|
|
169
|
+
.hk-timeline-window {
|
|
170
|
+
display: flex;
|
|
171
|
+
flex-direction: column;
|
|
172
|
+
justify-content: center;
|
|
173
|
+
min-height: 0;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.hk-timeline-window .hk-timeline-step {
|
|
177
|
+
flex: 0 0 auto;
|
|
178
|
+
max-width: 100%;
|
|
179
|
+
|
|
180
|
+
[data-el="label"] {
|
|
181
|
+
overflow: hidden;
|
|
182
|
+
text-overflow: ellipsis;
|
|
183
|
+
white-space: nowrap;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Vertical rails: centered under the node column, spanning between the
|
|
188
|
+
// cells' node centers (at 1/6, 1/2, 5/6 of the height) minus the same
|
|
189
|
+
// clearance the horizontal bonds keep from the circles. Each segment
|
|
190
|
+
// also re-asserts the inline geometry — the horizontal segment rules
|
|
191
|
+
// above carry the same attribute specificity and must not leak in.
|
|
192
|
+
.hk-timeline-link {
|
|
193
|
+
width: var(--hk-timeline-link-thickness);
|
|
194
|
+
height: auto;
|
|
195
|
+
inset-inline-start: var(--hk-timeline-link-top);
|
|
196
|
+
top: auto;
|
|
197
|
+
bottom: auto;
|
|
198
|
+
|
|
199
|
+
&[data-segment="before-current"] {
|
|
200
|
+
inset-inline-start: var(--hk-timeline-link-top);
|
|
201
|
+
width: var(--hk-timeline-link-thickness);
|
|
202
|
+
top: calc(16.6667% + var(--hk-timeline-link-clearance));
|
|
203
|
+
height: calc(33.3334% - (var(--hk-timeline-link-clearance) * 2));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
&[data-segment="current-after"] {
|
|
207
|
+
inset-inline-start: var(--hk-timeline-link-top);
|
|
208
|
+
width: var(--hk-timeline-link-thickness);
|
|
209
|
+
top: calc(50% + var(--hk-timeline-link-clearance));
|
|
210
|
+
height: calc(33.3334% - (var(--hk-timeline-link-clearance) * 2));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Continuation hints: from the container edge toward that cell's node,
|
|
214
|
+
// fading INTO the edge ("more steps exist beyond this side").
|
|
215
|
+
&[data-segment="edge-before"] {
|
|
216
|
+
inset-inline-start: var(--hk-timeline-link-top);
|
|
217
|
+
width: var(--hk-timeline-link-thickness);
|
|
218
|
+
top: 0;
|
|
219
|
+
height: calc(16.6667% - var(--hk-timeline-link-clearance));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
&[data-segment="edge-after"] {
|
|
223
|
+
inset-inline-start: var(--hk-timeline-link-top);
|
|
224
|
+
width: var(--hk-timeline-link-thickness);
|
|
225
|
+
top: auto;
|
|
226
|
+
bottom: 0;
|
|
227
|
+
height: calc(16.6667% - var(--hk-timeline-link-clearance));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
&[data-fade-dir="up"] {
|
|
231
|
+
background-color: transparent;
|
|
232
|
+
background-image: linear-gradient(to top, var(--_link-color), transparent);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
&[data-fade-dir="down"] {
|
|
236
|
+
background-color: transparent;
|
|
237
|
+
background-image: linear-gradient(to bottom, var(--_link-color), transparent);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
145
242
|
|
|
146
243
|
.hk-timeline-step {
|
|
147
244
|
display: flex;
|
|
@@ -187,7 +284,7 @@
|
|
|
187
284
|
}
|
|
188
285
|
|
|
189
286
|
[data-el="connector"] {
|
|
190
|
-
height:
|
|
287
|
+
height: var(--hk-timeline-link-thickness);
|
|
191
288
|
flex: 1 1 0%;
|
|
192
289
|
min-width: 0.5rem;
|
|
193
290
|
margin: 0 var(--hi-space-4, 4px);
|
|
@@ -198,19 +295,41 @@
|
|
|
198
295
|
flex: 0 0 auto;
|
|
199
296
|
}
|
|
200
297
|
|
|
201
|
-
.hk-timeline[data-orientation="vertical"] .hk-timeline-step {
|
|
298
|
+
.hk-timeline[data-orientation="vertical"][data-mode="full"] .hk-timeline-step {
|
|
202
299
|
flex-direction: row;
|
|
203
300
|
flex: 0 0 auto;
|
|
204
|
-
|
|
301
|
+
|
|
302
|
+
// The gap this step's rail must bridge: exactly the space between the
|
|
303
|
+
// bottom edge of this step's node circle and the top edge of the next
|
|
304
|
+
// step's node circle. Padding and rail height share the token, so the
|
|
305
|
+
// rail always lands flush on both circles — no floating dashes, no
|
|
306
|
+
// overlap, whatever the rhythm is tuned to.
|
|
307
|
+
--hk-timeline-vert-gap: var(--hi-space-24, 24px);
|
|
308
|
+
padding-bottom: var(--hk-timeline-vert-gap);
|
|
205
309
|
|
|
206
310
|
[data-el="connector"] {
|
|
207
|
-
width:
|
|
208
|
-
|
|
311
|
+
width: var(--hk-timeline-link-thickness);
|
|
312
|
+
// The horizontal base rule leaves `min-width: 0.5rem` behind; on an
|
|
313
|
+
// absolutely-positioned rail min-width beats width, which once fatted
|
|
314
|
+
// the rail to 8px and shoved it off the node center. Neutralize the
|
|
315
|
+
// leaked sizing constraints explicitly.
|
|
316
|
+
min-width: 0;
|
|
209
317
|
flex: 0 0 auto;
|
|
210
318
|
position: absolute;
|
|
211
|
-
|
|
212
|
-
|
|
319
|
+
height: var(--hk-timeline-vert-gap);
|
|
320
|
+
// Centered under the NODE, not the row: the circle occupies the first
|
|
321
|
+
// node-size square of the row, so the rail's inline offset is
|
|
322
|
+
// (node − thickness) / 2. Logical property mirrors it under RTL
|
|
323
|
+
// together with the flipped flex column.
|
|
324
|
+
inset-inline-start: calc(
|
|
325
|
+
(var(--hk-timeline-node-size) - var(--hk-timeline-link-thickness)) / 2
|
|
326
|
+
);
|
|
327
|
+
inset-inline-end: auto;
|
|
328
|
+
top: var(--hk-timeline-node-size);
|
|
329
|
+
bottom: auto;
|
|
213
330
|
margin: 0;
|
|
331
|
+
// Rounded caps keep the rail's ends from aliasing against the circles.
|
|
332
|
+
border-radius: calc(var(--hk-timeline-link-thickness) / 2);
|
|
214
333
|
}
|
|
215
334
|
}
|
|
216
335
|
|
|
@@ -138,10 +138,48 @@ describe("HkTimeline full mode", () => {
|
|
|
138
138
|
expect(els[3].getAttribute("aria-current")).toBeNull();
|
|
139
139
|
});
|
|
140
140
|
|
|
141
|
-
it("keeps vertical
|
|
142
|
-
|
|
141
|
+
it("keeps a short vertical timeline in full mode", () => {
|
|
142
|
+
// A 3-step vertical timeline is its own window: nothing to collapse.
|
|
143
|
+
const steps = [
|
|
144
|
+
{ key: "a", label: "Alpha" },
|
|
145
|
+
{ key: "b", label: "Beta" },
|
|
146
|
+
{ key: "c", label: "Gamma" },
|
|
147
|
+
];
|
|
148
|
+
const t = mountTimeline({ currentKey: "b", orientation: "vertical", collapse: "always" }, steps);
|
|
143
149
|
expect(t.container.querySelector(".hk-timeline")?.getAttribute("data-mode")).toBe("full");
|
|
144
150
|
});
|
|
151
|
+
|
|
152
|
+
it("collapses a vertical timeline into the stacked window", () => {
|
|
153
|
+
const t = mountTimeline({ currentKey: "c", orientation: "vertical", collapse: "always" });
|
|
154
|
+
const root = t.container.querySelector(".hk-timeline");
|
|
155
|
+
expect(root?.getAttribute("data-orientation")).toBe("vertical");
|
|
156
|
+
expect(root?.getAttribute("data-mode")).toBe("window");
|
|
157
|
+
|
|
158
|
+
const before = t.container.querySelector('.hk-timeline-window[data-side="before"]');
|
|
159
|
+
const current = t.container.querySelector('.hk-timeline-window[data-side="current"]');
|
|
160
|
+
const after = t.container.querySelector('.hk-timeline-window[data-side="after"]');
|
|
161
|
+
|
|
162
|
+
expect(before?.querySelector('[data-el="label"]')?.textContent).toBe("Beta");
|
|
163
|
+
expect(current?.querySelector('[data-el="label"]')?.textContent).toBe("Gamma");
|
|
164
|
+
expect(after?.querySelector('[data-el="label"]')?.textContent).toBe("Delta");
|
|
165
|
+
|
|
166
|
+
// Same dimming grammar as the horizontal window: outer cells dimmed,
|
|
167
|
+
// the current cell at full strength.
|
|
168
|
+
expect(before?.hasAttribute("data-dimmed")).toBe(true);
|
|
169
|
+
expect(current?.hasAttribute("data-dimmed")).toBe(false);
|
|
170
|
+
expect(after?.hasAttribute("data-dimmed")).toBe(true);
|
|
171
|
+
|
|
172
|
+
// Vertical continuation hints fade along the block axis.
|
|
173
|
+
expect(
|
|
174
|
+
t.container.querySelector('.hk-timeline-link[data-segment="edge-before"]')?.getAttribute("data-fade-dir"),
|
|
175
|
+
).toBe("up");
|
|
176
|
+
expect(
|
|
177
|
+
t.container.querySelector('.hk-timeline-link[data-segment="edge-after"]')?.getAttribute("data-fade-dir"),
|
|
178
|
+
).toBe("down");
|
|
179
|
+
|
|
180
|
+
// The current step keeps its aria contract inside the stacked window.
|
|
181
|
+
expect(current?.querySelector(".hk-timeline-step")?.getAttribute("aria-current")).toBe("step");
|
|
182
|
+
});
|
|
145
183
|
});
|
|
146
184
|
|
|
147
185
|
describe("HkTimeline window mode", () => {
|
|
@@ -91,6 +91,23 @@ function naturalRowWidth(el: HTMLElement): number {
|
|
|
91
91
|
return Math.ceil(width);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/** Vertical twin of [`naturalRowWidth`]: the column's natural height when
|
|
95
|
+
* free to grow past the host (steps are `flex: 0 0 auto`, so an offscreen
|
|
96
|
+
* clone with `height: max-content` measures the unclamped stack). */
|
|
97
|
+
function naturalColumnHeight(el: HTMLElement): number {
|
|
98
|
+
const probe = el.cloneNode(true) as HTMLElement;
|
|
99
|
+
probe.style.position = "absolute";
|
|
100
|
+
probe.style.visibility = "hidden";
|
|
101
|
+
probe.style.pointerEvents = "none";
|
|
102
|
+
probe.style.height = "max-content";
|
|
103
|
+
const parent = el.parentElement;
|
|
104
|
+
if (!parent) return el.scrollHeight;
|
|
105
|
+
parent.appendChild(probe);
|
|
106
|
+
const height = probe.getBoundingClientRect().height;
|
|
107
|
+
probe.remove();
|
|
108
|
+
return Math.ceil(height);
|
|
109
|
+
}
|
|
110
|
+
|
|
94
111
|
export default defineComponent({
|
|
95
112
|
name: "HkTimeline",
|
|
96
113
|
props: {
|
|
@@ -112,22 +129,22 @@ export default defineComponent({
|
|
|
112
129
|
setup(props, { emit }) {
|
|
113
130
|
const host = ref<HTMLElement | null>(null);
|
|
114
131
|
const collapsed = ref(false);
|
|
115
|
-
/** Full-row width
|
|
116
|
-
*
|
|
117
|
-
|
|
132
|
+
/** Full-row width (horizontal) or full-column height (vertical)
|
|
133
|
+
* captured right before collapsing; used as the expand-again
|
|
134
|
+
* threshold (plus hysteresis) while windowed. */
|
|
135
|
+
let fullNaturalSize = 0;
|
|
118
136
|
let observer: ResizeObserver | undefined;
|
|
119
137
|
|
|
120
138
|
const currentIndex = computed(() =>
|
|
121
139
|
props.steps.findIndex((s) => s.key === props.currentKey),
|
|
122
140
|
);
|
|
123
141
|
|
|
124
|
-
/** The window only makes sense for
|
|
125
|
-
*
|
|
142
|
+
/** The window only makes sense for more steps than the window can show
|
|
143
|
+
* (a 3-step timeline is its own window). Horizontal collapse keys off
|
|
144
|
+
* width overflow, vertical collapse off height overflow — the stepper
|
|
145
|
+
* behaves the same whether the host squeezes its row or its column. */
|
|
126
146
|
const windowable = computed(
|
|
127
|
-
() =>
|
|
128
|
-
props.orientation === "horizontal" &&
|
|
129
|
-
props.steps.length > 3 &&
|
|
130
|
-
currentIndex.value >= 0,
|
|
147
|
+
() => props.steps.length > 3 && currentIndex.value >= 0,
|
|
131
148
|
);
|
|
132
149
|
|
|
133
150
|
const windowed = computed(
|
|
@@ -152,23 +169,25 @@ export default defineComponent({
|
|
|
152
169
|
|
|
153
170
|
function measure(): void {
|
|
154
171
|
const el = host.value;
|
|
155
|
-
if (!el ||
|
|
172
|
+
if (!el || props.collapse !== "auto") {
|
|
156
173
|
collapsed.value = false;
|
|
157
174
|
return;
|
|
158
175
|
}
|
|
176
|
+
const vertical = props.orientation === "vertical";
|
|
177
|
+
const fitSize = vertical ? el.clientHeight : el.clientWidth;
|
|
159
178
|
if (collapsed.value) {
|
|
160
|
-
// Expand again only once the host comfortably fits the full row
|
|
161
|
-
// could not fit when we collapsed (16px hysteresis
|
|
162
|
-
// around the exact threshold).
|
|
163
|
-
if (
|
|
179
|
+
// Expand again only once the host comfortably fits the full row or
|
|
180
|
+
// column it could not fit when we collapsed (16px hysteresis
|
|
181
|
+
// against flapping around the exact threshold).
|
|
182
|
+
if (fullNaturalSize > 0 && fitSize >= fullNaturalSize + 16) {
|
|
164
183
|
collapsed.value = false;
|
|
165
|
-
|
|
184
|
+
fullNaturalSize = 0;
|
|
166
185
|
}
|
|
167
186
|
return;
|
|
168
187
|
}
|
|
169
|
-
const natural = naturalRowWidth(el);
|
|
170
|
-
if (natural >
|
|
171
|
-
|
|
188
|
+
const natural = vertical ? naturalColumnHeight(el) : naturalRowWidth(el);
|
|
189
|
+
if (natural > fitSize + 1) {
|
|
190
|
+
fullNaturalSize = natural;
|
|
172
191
|
collapsed.value = true;
|
|
173
192
|
}
|
|
174
193
|
}
|
|
@@ -197,7 +216,7 @@ export default defineComponent({
|
|
|
197
216
|
],
|
|
198
217
|
() => {
|
|
199
218
|
collapsed.value = false;
|
|
200
|
-
|
|
219
|
+
fullNaturalSize = 0;
|
|
201
220
|
void nextTick(measure);
|
|
202
221
|
},
|
|
203
222
|
);
|
|
@@ -288,7 +307,7 @@ export default defineComponent({
|
|
|
288
307
|
class="hk-timeline-link"
|
|
289
308
|
data-segment="edge-before"
|
|
290
309
|
data-status={statusOf(w.beforeIndex)}
|
|
291
|
-
data-fade-dir="left"
|
|
310
|
+
data-fade-dir={props.orientation === "vertical" ? "up" : "left"}
|
|
292
311
|
/>
|
|
293
312
|
)}
|
|
294
313
|
{w.afterIndex >= 0 && w.fadeAfter && (
|
|
@@ -296,7 +315,7 @@ export default defineComponent({
|
|
|
296
315
|
class="hk-timeline-link"
|
|
297
316
|
data-segment="edge-after"
|
|
298
317
|
data-status={statusOf(w.afterIndex)}
|
|
299
|
-
data-fade-dir="right"
|
|
318
|
+
data-fade-dir={props.orientation === "vertical" ? "down" : "right"}
|
|
300
319
|
/>
|
|
301
320
|
)}
|
|
302
321
|
</div>
|