radiant-docs 0.1.38 → 0.1.39
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 +1 -1
- package/template/src/components/user/CodeBlock.astro +7 -4
- package/template/src/components/user/CodeGroup.astro +246 -13
- package/template/src/components/user/ComponentPreviewBlock.astro +3 -3
- package/template/src/components/user/Tabs.astro +128 -23
- package/template/src/styles/global.css +23 -0
package/package.json
CHANGED
|
@@ -256,8 +256,8 @@ const renderedCodeLinesHtml = normalizedTokenLines
|
|
|
256
256
|
|
|
257
257
|
<div
|
|
258
258
|
class:list={[
|
|
259
|
-
"group/prose-code not-prose relative w-full max-w-full min-w-0",
|
|
260
|
-
parsedInCodeGroup ? "my-0" : "my-6",
|
|
259
|
+
"group/prose-code not-prose relative w-full max-w-full min-w-0 rounded-xl",
|
|
260
|
+
parsedInCodeGroup ? "my-0" : "my-6 shadow-xs",
|
|
261
261
|
]}
|
|
262
262
|
data-rd-code-block-root="true"
|
|
263
263
|
data-rd-collapsed-lines={collapsedLinesValue}
|
|
@@ -271,7 +271,7 @@ const renderedCodeLinesHtml = normalizedTokenLines
|
|
|
271
271
|
>
|
|
272
272
|
<div
|
|
273
273
|
class:list={[
|
|
274
|
-
"w-full max-w-full min-w-0 overflow-hidden border border-neutral-200 bg-white
|
|
274
|
+
"w-full max-w-full min-w-0 overflow-hidden border border-neutral-200 bg-white dark:border-neutral-800 dark:bg-(--rd-code-surface)",
|
|
275
275
|
parsedInCodeGroup ? "rounded-t-none rounded-b-xl" : "rounded-xl",
|
|
276
276
|
]}
|
|
277
277
|
>
|
|
@@ -322,7 +322,10 @@ const renderedCodeLinesHtml = normalizedTokenLines
|
|
|
322
322
|
) : null
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
-
<div
|
|
325
|
+
<div
|
|
326
|
+
class="relative min-w-0"
|
|
327
|
+
data-rd-code-group-panel={parsedInCodeGroup ? "true" : undefined}
|
|
328
|
+
>
|
|
326
329
|
{
|
|
327
330
|
!parsedInCodeGroup && !parsedShowFilename ? (
|
|
328
331
|
<div class="pointer-events-none absolute right-2 top-2 z-20">
|
|
@@ -4,7 +4,7 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
|
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
<div
|
|
7
|
-
class="group/prose-code-group not-prose relative my-6 w-full max-w-full min-w-0"
|
|
7
|
+
class="group/prose-code-group not-prose relative my-6 w-full max-w-full min-w-0 shadow-xs rounded-xl"
|
|
8
8
|
data-rd-code-group-root="true"
|
|
9
9
|
>
|
|
10
10
|
<div
|
|
@@ -19,7 +19,7 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
|
|
|
19
19
|
<div
|
|
20
20
|
data-rd-code-group-pill
|
|
21
21
|
aria-hidden="true"
|
|
22
|
-
class="pointer-events-none absolute top-1/2 z-0 h-[28px] -translate-y-1/2 rounded-[9px] border-[0.5px] border-neutral-200 bg-white
|
|
22
|
+
class="pointer-events-none absolute top-1/2 z-0 h-[28px] -translate-y-1/2 rounded-[9px] border-[0.5px] border-neutral-200 bg-white opacity-0 transition-[left,width,opacity] duration-200 ease-out dark:border-neutral-700/70 dark:bg-(--rd-code-surface)"
|
|
23
23
|
>
|
|
24
24
|
</div>
|
|
25
25
|
</div>
|
|
@@ -58,7 +58,10 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
|
|
|
58
58
|
</div>
|
|
59
59
|
</div>
|
|
60
60
|
|
|
61
|
-
<div
|
|
61
|
+
<div
|
|
62
|
+
data-rd-code-group-content
|
|
63
|
+
class="relative min-w-0 overflow-hidden transition-[height] duration-300 ease-in-out"
|
|
64
|
+
>
|
|
62
65
|
<slot />
|
|
63
66
|
</div>
|
|
64
67
|
<script is:inline>
|
|
@@ -74,14 +77,27 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
|
|
|
74
77
|
const pillElement = root.querySelector("[data-rd-code-group-pill]");
|
|
75
78
|
const copyButton = root.querySelector("[data-rd-copy-trigger='true']");
|
|
76
79
|
|
|
77
|
-
if (
|
|
80
|
+
if (
|
|
81
|
+
!(contentElement instanceof HTMLElement) ||
|
|
82
|
+
!(tabsElement instanceof HTMLElement) ||
|
|
83
|
+
!(pillElement instanceof HTMLElement)
|
|
84
|
+
) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
78
87
|
|
|
79
88
|
const codeItems = Array.from(
|
|
80
89
|
contentElement.querySelectorAll("[data-rd-code-group-item='true']"),
|
|
81
|
-
);
|
|
90
|
+
).filter((item) => item instanceof HTMLElement);
|
|
82
91
|
if (codeItems.length === 0) return;
|
|
83
92
|
|
|
93
|
+
const prefersReducedMotion =
|
|
94
|
+
typeof window.matchMedia === "function" &&
|
|
95
|
+
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
96
|
+
const TRANSITION_DURATION_MS = prefersReducedMotion ? 0 : 300;
|
|
97
|
+
|
|
84
98
|
let activeIndex = 0;
|
|
99
|
+
let transitionTimeoutId = null;
|
|
100
|
+
let activeItemResizeObserver = null;
|
|
85
101
|
|
|
86
102
|
const setCopiedState = (button, copied) => {
|
|
87
103
|
const copyIcon = button.querySelector("[data-rd-copy-icon]");
|
|
@@ -146,14 +162,28 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
|
|
|
146
162
|
tabWrapper.appendChild(tabButton);
|
|
147
163
|
tabsElement.appendChild(tabWrapper);
|
|
148
164
|
|
|
165
|
+
const panelCandidate = itemElement.querySelector(
|
|
166
|
+
"[data-rd-code-group-panel='true']",
|
|
167
|
+
);
|
|
168
|
+
const panelElement =
|
|
169
|
+
panelCandidate instanceof HTMLElement ? panelCandidate : itemElement;
|
|
170
|
+
const frameElement =
|
|
171
|
+
itemElement.firstElementChild instanceof HTMLElement
|
|
172
|
+
? itemElement.firstElementChild
|
|
173
|
+
: null;
|
|
174
|
+
|
|
149
175
|
return {
|
|
150
176
|
itemElement,
|
|
177
|
+
panelElement,
|
|
178
|
+
frameElement,
|
|
151
179
|
tabWrapper,
|
|
152
180
|
tabButton,
|
|
153
181
|
iconContainer,
|
|
154
182
|
};
|
|
155
183
|
});
|
|
156
184
|
|
|
185
|
+
contentElement.style.transitionDuration = `${TRANSITION_DURATION_MS}ms`;
|
|
186
|
+
|
|
157
187
|
const syncPill = () => {
|
|
158
188
|
const activeTab = tabs[activeIndex]?.tabWrapper;
|
|
159
189
|
if (!activeTab) {
|
|
@@ -172,10 +202,9 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
|
|
|
172
202
|
pillElement.classList.add("opacity-100");
|
|
173
203
|
};
|
|
174
204
|
|
|
175
|
-
const
|
|
176
|
-
tabs.forEach(({
|
|
205
|
+
const updateTabButtonStates = () => {
|
|
206
|
+
tabs.forEach(({ tabButton, iconContainer }, index) => {
|
|
177
207
|
const isActive = index === activeIndex;
|
|
178
|
-
itemElement.style.display = isActive ? "" : "none";
|
|
179
208
|
tabButton.classList.toggle("text-foreground", isActive);
|
|
180
209
|
tabButton.classList.toggle("text-muted-foreground", !isActive);
|
|
181
210
|
|
|
@@ -186,11 +215,163 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
|
|
|
186
215
|
syncPill();
|
|
187
216
|
};
|
|
188
217
|
|
|
218
|
+
const getItemOuterHeight = (itemElement) => {
|
|
219
|
+
const frameElement =
|
|
220
|
+
itemElement.firstElementChild instanceof HTMLElement
|
|
221
|
+
? itemElement.firstElementChild
|
|
222
|
+
: null;
|
|
223
|
+
if (frameElement) {
|
|
224
|
+
const frameRectHeight = frameElement.getBoundingClientRect().height;
|
|
225
|
+
if (Number.isFinite(frameRectHeight) && frameRectHeight > 0) {
|
|
226
|
+
return Math.ceil(frameRectHeight);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const frameOffsetHeight = frameElement.offsetHeight;
|
|
230
|
+
if (Number.isFinite(frameOffsetHeight) && frameOffsetHeight > 0) {
|
|
231
|
+
return frameOffsetHeight;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const rectHeight = itemElement.getBoundingClientRect().height;
|
|
236
|
+
if (Number.isFinite(rectHeight) && rectHeight > 0) {
|
|
237
|
+
return Math.ceil(rectHeight);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const offsetHeight = itemElement.offsetHeight;
|
|
241
|
+
if (Number.isFinite(offsetHeight) && offsetHeight > 0) {
|
|
242
|
+
return offsetHeight;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return itemElement.scrollHeight;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const syncContentHeight = () => {
|
|
249
|
+
const activeItem = tabs[activeIndex]?.itemElement;
|
|
250
|
+
if (!activeItem) return;
|
|
251
|
+
contentElement.style.height = `${getItemOuterHeight(activeItem)}px`;
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const observeActiveItemHeight = () => {
|
|
255
|
+
if (activeItemResizeObserver) {
|
|
256
|
+
activeItemResizeObserver.disconnect();
|
|
257
|
+
activeItemResizeObserver = null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
261
|
+
|
|
262
|
+
const activeItem = tabs[activeIndex]?.itemElement;
|
|
263
|
+
if (!activeItem) return;
|
|
264
|
+
|
|
265
|
+
activeItemResizeObserver = new ResizeObserver(() => {
|
|
266
|
+
if (transitionTimeoutId !== null) return;
|
|
267
|
+
syncContentHeight();
|
|
268
|
+
});
|
|
269
|
+
activeItemResizeObserver.observe(activeItem);
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const setPanelsToRestState = () => {
|
|
273
|
+
tabs.forEach(({ itemElement, panelElement, frameElement }, index) => {
|
|
274
|
+
const isActive = index === activeIndex;
|
|
275
|
+
itemElement.style.position = isActive ? "relative" : "absolute";
|
|
276
|
+
itemElement.style.inset = isActive ? "" : "0";
|
|
277
|
+
itemElement.style.opacity = isActive ? "1" : "0";
|
|
278
|
+
itemElement.style.visibility = isActive ? "visible" : "hidden";
|
|
279
|
+
itemElement.style.pointerEvents = isActive ? "auto" : "none";
|
|
280
|
+
itemElement.style.zIndex = isActive ? "1" : "0";
|
|
281
|
+
panelElement.style.transform = "translateX(0)";
|
|
282
|
+
panelElement.style.animation = "none";
|
|
283
|
+
panelElement.style.willChange = "auto";
|
|
284
|
+
if (frameElement) {
|
|
285
|
+
frameElement.style.height = "";
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const normalizeToCurrentState = () => {
|
|
291
|
+
setPanelsToRestState();
|
|
292
|
+
syncContentHeight();
|
|
293
|
+
observeActiveItemHeight();
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const transitionToIndex = (nextIndex) => {
|
|
297
|
+
if (nextIndex === activeIndex) return;
|
|
298
|
+
|
|
299
|
+
if (transitionTimeoutId !== null) {
|
|
300
|
+
window.clearTimeout(transitionTimeoutId);
|
|
301
|
+
transitionTimeoutId = null;
|
|
302
|
+
normalizeToCurrentState();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const previousIndex = activeIndex;
|
|
306
|
+
const direction = nextIndex > previousIndex ? 1 : -1;
|
|
307
|
+
activeIndex = nextIndex;
|
|
308
|
+
updateTabButtonStates();
|
|
309
|
+
|
|
310
|
+
const previousTab = tabs[previousIndex];
|
|
311
|
+
const nextTab = tabs[nextIndex];
|
|
312
|
+
const previousItem = previousTab?.itemElement;
|
|
313
|
+
const nextItem = nextTab?.itemElement;
|
|
314
|
+
const previousPanel = previousTab?.panelElement;
|
|
315
|
+
const nextPanel = nextTab?.panelElement;
|
|
316
|
+
const previousFrame = previousTab?.frameElement;
|
|
317
|
+
const nextFrame = nextTab?.frameElement;
|
|
318
|
+
if (!previousItem || !nextItem || !previousPanel || !nextPanel) {
|
|
319
|
+
normalizeToCurrentState();
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const previousHeight = getItemOuterHeight(previousItem);
|
|
324
|
+
const nextHeight = getItemOuterHeight(nextItem);
|
|
325
|
+
contentElement.style.height = `${previousHeight}px`;
|
|
326
|
+
if (TRANSITION_DURATION_MS === 0) {
|
|
327
|
+
contentElement.style.height = `${nextHeight}px`;
|
|
328
|
+
} else {
|
|
329
|
+
requestAnimationFrame(() => {
|
|
330
|
+
contentElement.style.height = `${nextHeight}px`;
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
previousItem.style.position = "absolute";
|
|
335
|
+
previousItem.style.inset = "0";
|
|
336
|
+
previousItem.style.opacity = "1";
|
|
337
|
+
previousItem.style.visibility = "visible";
|
|
338
|
+
previousItem.style.pointerEvents = "none";
|
|
339
|
+
previousItem.style.zIndex = "1";
|
|
340
|
+
previousPanel.style.transform = "translateX(0)";
|
|
341
|
+
previousPanel.style.willChange = "transform";
|
|
342
|
+
if (previousFrame) {
|
|
343
|
+
previousFrame.style.height = "100%";
|
|
344
|
+
}
|
|
345
|
+
previousPanel.style.animation =
|
|
346
|
+
direction === 1
|
|
347
|
+
? `rd-code-group-slide-out-to-left ${TRANSITION_DURATION_MS}ms ease-in-out both`
|
|
348
|
+
: `rd-code-group-slide-out-to-right ${TRANSITION_DURATION_MS}ms ease-in-out both`;
|
|
349
|
+
|
|
350
|
+
nextItem.style.position = "absolute";
|
|
351
|
+
nextItem.style.inset = "0";
|
|
352
|
+
nextItem.style.opacity = "1";
|
|
353
|
+
nextItem.style.visibility = "visible";
|
|
354
|
+
nextItem.style.pointerEvents = "auto";
|
|
355
|
+
nextItem.style.zIndex = "2";
|
|
356
|
+
nextPanel.style.transform = "translateX(0)";
|
|
357
|
+
nextPanel.style.willChange = "transform";
|
|
358
|
+
if (nextFrame) {
|
|
359
|
+
nextFrame.style.height = "100%";
|
|
360
|
+
}
|
|
361
|
+
nextPanel.style.animation =
|
|
362
|
+
direction === 1
|
|
363
|
+
? `rd-code-group-slide-in-from-right ${TRANSITION_DURATION_MS}ms ease-in-out both`
|
|
364
|
+
: `rd-code-group-slide-in-from-left ${TRANSITION_DURATION_MS}ms ease-in-out both`;
|
|
365
|
+
|
|
366
|
+
transitionTimeoutId = window.setTimeout(() => {
|
|
367
|
+
transitionTimeoutId = null;
|
|
368
|
+
normalizeToCurrentState();
|
|
369
|
+
}, TRANSITION_DURATION_MS);
|
|
370
|
+
};
|
|
371
|
+
|
|
189
372
|
tabs.forEach(({ tabButton }, index) => {
|
|
190
373
|
tabButton.addEventListener("click", () => {
|
|
191
|
-
|
|
192
|
-
activeIndex = index;
|
|
193
|
-
syncActiveTab();
|
|
374
|
+
transitionToIndex(index);
|
|
194
375
|
});
|
|
195
376
|
});
|
|
196
377
|
|
|
@@ -226,9 +407,61 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
|
|
|
226
407
|
}
|
|
227
408
|
|
|
228
409
|
tabsElement.addEventListener("scroll", syncPill, { passive: true });
|
|
229
|
-
window.addEventListener("resize",
|
|
230
|
-
|
|
410
|
+
window.addEventListener("resize", () => {
|
|
411
|
+
syncPill();
|
|
412
|
+
if (transitionTimeoutId === null) {
|
|
413
|
+
syncContentHeight();
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
updateTabButtonStates();
|
|
417
|
+
normalizeToCurrentState();
|
|
231
418
|
requestAnimationFrame(syncPill);
|
|
232
419
|
})();
|
|
233
420
|
</script>
|
|
234
421
|
</div>
|
|
422
|
+
|
|
423
|
+
<style>
|
|
424
|
+
@keyframes rd-code-group-slide-in-from-right {
|
|
425
|
+
from {
|
|
426
|
+
transform: translateX(100%);
|
|
427
|
+
opacity: 0;
|
|
428
|
+
}
|
|
429
|
+
to {
|
|
430
|
+
transform: translateX(0);
|
|
431
|
+
opacity: 1;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
@keyframes rd-code-group-slide-in-from-left {
|
|
436
|
+
from {
|
|
437
|
+
transform: translateX(-100%);
|
|
438
|
+
opacity: 0;
|
|
439
|
+
}
|
|
440
|
+
to {
|
|
441
|
+
transform: translateX(0);
|
|
442
|
+
opacity: 1;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
@keyframes rd-code-group-slide-out-to-left {
|
|
447
|
+
from {
|
|
448
|
+
transform: translateX(0);
|
|
449
|
+
opacity: 1;
|
|
450
|
+
}
|
|
451
|
+
to {
|
|
452
|
+
transform: translateX(-100%);
|
|
453
|
+
opacity: 0;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
@keyframes rd-code-group-slide-out-to-right {
|
|
458
|
+
from {
|
|
459
|
+
transform: translateX(0);
|
|
460
|
+
opacity: 1;
|
|
461
|
+
}
|
|
462
|
+
to {
|
|
463
|
+
transform: translateX(100%);
|
|
464
|
+
opacity: 0;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
</style>
|
|
@@ -69,7 +69,7 @@ const isInitiallyExpanded = shouldShowAllCode || totalLineCount <= visibleLines;
|
|
|
69
69
|
<slot />
|
|
70
70
|
</div>
|
|
71
71
|
<div
|
|
72
|
-
class="rd-component-preview__code not-prose relative overflow-hidden rounded-b-xl bg-
|
|
72
|
+
class="rd-component-preview__code not-prose relative overflow-hidden rounded-b-xl bg-neutral-50 dark:bg-(--rd-code-surface)"
|
|
73
73
|
data-rd-preview-expanded={isInitiallyExpanded ? "true" : "false"}
|
|
74
74
|
style={{ "--rd-preview-visible-lines": String(visibleLines) }}
|
|
75
75
|
>
|
|
@@ -142,7 +142,7 @@ const isInitiallyExpanded = shouldShowAllCode || totalLineCount <= visibleLines;
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
.rd-component-preview__code :global(.group\/prose-code > div) {
|
|
145
|
-
background-color:
|
|
145
|
+
background-color: var(--color-neutral-50) !important;
|
|
146
146
|
border-top-left-radius: 0 !important;
|
|
147
147
|
border-top-right-radius: 0 !important;
|
|
148
148
|
}
|
|
@@ -153,7 +153,7 @@ const isInitiallyExpanded = shouldShowAllCode || totalLineCount <= visibleLines;
|
|
|
153
153
|
|
|
154
154
|
.rd-component-preview__code :global(.group\/prose-code pre),
|
|
155
155
|
.rd-component-preview__code :global(.group\/prose-code code) {
|
|
156
|
-
background-color:
|
|
156
|
+
background-color: var(--color-neutral-50) !important;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
:global(.dark) .rd-component-preview__code :global(.group\/prose-code pre),
|
|
@@ -27,17 +27,55 @@ if (labels.length === 0) {
|
|
|
27
27
|
|
|
28
28
|
<div x-data="{
|
|
29
29
|
activeTab: 0,
|
|
30
|
+
previousTab: null,
|
|
31
|
+
transitionDirection: 1,
|
|
32
|
+
isTransitioning: false,
|
|
33
|
+
transitionDurationMs: 300,
|
|
34
|
+
transitionTimeout: null,
|
|
30
35
|
containerHeight: 'auto',
|
|
31
36
|
markerStyle: { left: null, width: null },
|
|
37
|
+
resizeHandler: null,
|
|
32
38
|
init() {
|
|
39
|
+
this.resizeHandler = () => {
|
|
40
|
+
this.updateMarker(this.activeTab);
|
|
41
|
+
this.updateHeight(this.isTransitioning);
|
|
42
|
+
};
|
|
43
|
+
window.addEventListener('resize', this.resizeHandler);
|
|
44
|
+
|
|
33
45
|
this.$nextTick(() => {
|
|
34
46
|
this.updateMarker(this.activeTab);
|
|
35
47
|
this.updateHeight();
|
|
36
48
|
});
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
},
|
|
50
|
+
destroy() {
|
|
51
|
+
if (this.resizeHandler) {
|
|
52
|
+
window.removeEventListener('resize', this.resizeHandler);
|
|
53
|
+
}
|
|
54
|
+
if (this.transitionTimeout) {
|
|
55
|
+
window.clearTimeout(this.transitionTimeout);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
selectTab(index) {
|
|
59
|
+
if (index === this.activeTab) return;
|
|
60
|
+
|
|
61
|
+
if (this.transitionTimeout) {
|
|
62
|
+
window.clearTimeout(this.transitionTimeout);
|
|
63
|
+
this.transitionTimeout = null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.previousTab = this.activeTab;
|
|
67
|
+
this.transitionDirection = index > this.activeTab ? 1 : -1;
|
|
68
|
+
this.isTransitioning = true;
|
|
69
|
+
this.activeTab = index;
|
|
70
|
+
this.updateMarker(this.activeTab);
|
|
71
|
+
this.updateHeight(true);
|
|
72
|
+
|
|
73
|
+
this.transitionTimeout = window.setTimeout(() => {
|
|
74
|
+
this.isTransitioning = false;
|
|
75
|
+
this.previousTab = null;
|
|
76
|
+
this.transitionTimeout = null;
|
|
39
77
|
this.updateHeight();
|
|
40
|
-
});
|
|
78
|
+
}, this.transitionDurationMs);
|
|
41
79
|
},
|
|
42
80
|
updateMarker(index) {
|
|
43
81
|
const el = this.$refs['tab-' + index];
|
|
@@ -48,20 +86,54 @@ if (labels.length === 0) {
|
|
|
48
86
|
};
|
|
49
87
|
}
|
|
50
88
|
},
|
|
51
|
-
|
|
89
|
+
getPanelStyle(index) {
|
|
90
|
+
const base = 'position: absolute; inset: 0;';
|
|
91
|
+
|
|
92
|
+
const isActive = index === this.activeTab;
|
|
93
|
+
const isPrevious = this.isTransitioning && index === this.previousTab;
|
|
94
|
+
|
|
95
|
+
if (!isActive && !isPrevious) {
|
|
96
|
+
return `${base} opacity: 0; pointer-events: none; visibility: hidden; z-index: 0;`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!this.isTransitioning) {
|
|
100
|
+
return 'position: relative; transform: translateX(0); opacity: 1; pointer-events: auto; visibility: visible; z-index: 1;';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (isActive) {
|
|
104
|
+
const animationName =
|
|
105
|
+
this.transitionDirection === 1
|
|
106
|
+
? 'rd-tabs-slide-in-from-right'
|
|
107
|
+
: 'rd-tabs-slide-in-from-left';
|
|
108
|
+
return `${base} opacity: 1; pointer-events: auto; visibility: visible; z-index: 2; animation: ${animationName} ${this.transitionDurationMs}ms ease-in-out both;`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const animationName =
|
|
112
|
+
this.transitionDirection === 1
|
|
113
|
+
? 'rd-tabs-slide-out-to-left'
|
|
114
|
+
: 'rd-tabs-slide-out-to-right';
|
|
115
|
+
return `${base} opacity: 1; pointer-events: none; visibility: visible; z-index: 1; animation: ${animationName} ${this.transitionDurationMs}ms ease-in-out both;`;
|
|
116
|
+
},
|
|
117
|
+
updateHeight(includePrevious = false) {
|
|
52
118
|
this.$nextTick(() => {
|
|
53
|
-
// We look for the internal wrapper or the content div specifically
|
|
54
119
|
const activeSlide = this.$refs['content-' + this.activeTab];
|
|
55
|
-
if (activeSlide)
|
|
56
|
-
|
|
57
|
-
|
|
120
|
+
if (!activeSlide) return;
|
|
121
|
+
|
|
122
|
+
let nextHeight = activeSlide.scrollHeight;
|
|
123
|
+
if (includePrevious && this.previousTab !== null) {
|
|
124
|
+
const previousSlide = this.$refs['content-' + this.previousTab];
|
|
125
|
+
if (previousSlide) {
|
|
126
|
+
nextHeight = Math.max(nextHeight, previousSlide.scrollHeight);
|
|
127
|
+
}
|
|
58
128
|
}
|
|
129
|
+
|
|
130
|
+
this.containerHeight = nextHeight + 'px';
|
|
59
131
|
});
|
|
60
132
|
}
|
|
61
133
|
}"
|
|
62
134
|
class="my-5">
|
|
63
135
|
<ul
|
|
64
|
-
class="relative isolate not-prose flex w-
|
|
136
|
+
class="relative isolate not-prose flex w-full max-w-full min-w-0 rounded-lg border border-neutral-100 bg-neutral-100/80 p-[3px] dark:border-none dark:bg-neutral-800/50"
|
|
65
137
|
>
|
|
66
138
|
<div
|
|
67
139
|
class="absolute top-[3px] bottom-[3px] -z-10 flex items-center justify-center rounded-md bg-white shadow-sm transition-all duration-300 ease-out dark:bg-neutral-700/30 dark:border dark:border-neutral-700/40 dark:shadow-black/30."
|
|
@@ -74,12 +146,12 @@ class="my-5">
|
|
|
74
146
|
</div>
|
|
75
147
|
|
|
76
148
|
{ labels.map((label, index) => (
|
|
77
|
-
<li>
|
|
149
|
+
<li class="min-w-0 flex-1">
|
|
78
150
|
<button
|
|
79
151
|
type="button"
|
|
80
152
|
x-ref={`tab-${index}`}
|
|
81
|
-
@click={`
|
|
82
|
-
class="relative
|
|
153
|
+
@click={`selectTab(${index})`}
|
|
154
|
+
class="relative flex h-[32px] w-full min-w-0 max-w-full cursor-pointer items-center gap-2 px-3 text-sm font-medium transition-colors duration-200"
|
|
83
155
|
style={index === 0 ? "" : ""}
|
|
84
156
|
class:list={[index === 0 ? "text-foreground" : "text-muted-foreground"]}
|
|
85
157
|
:class={`{
|
|
@@ -88,30 +160,63 @@ class="my-5">
|
|
|
88
160
|
}`}
|
|
89
161
|
>
|
|
90
162
|
{icons[index] && <Icon name={icons[index]} class="size-4 shrink-0" />}
|
|
91
|
-
{label}
|
|
163
|
+
<span class="min-w-0 flex-1 truncate" title={label}>{label}</span>
|
|
92
164
|
</button>
|
|
93
165
|
</li>
|
|
94
166
|
)) }
|
|
95
167
|
</ul>
|
|
96
168
|
|
|
97
169
|
<div
|
|
98
|
-
class="mt-4 overflow-hidden transition-[height] duration-300 ease-in-out"
|
|
170
|
+
class="relative mt-4 overflow-hidden transition-[height] duration-300 ease-in-out"
|
|
99
171
|
:style="'height: ' + containerHeight"
|
|
100
172
|
>
|
|
101
|
-
<div
|
|
102
|
-
class="flex items-start transition-transform duration-300 ease-in-out"
|
|
103
|
-
:style="'transform: translateX(-' + (activeTab * 100) + '%)'"
|
|
104
|
-
>
|
|
105
173
|
{ tabContents.map((content, index) => (
|
|
106
|
-
// We add a ref here so we can measure the height
|
|
107
174
|
<div
|
|
175
|
+
{...(index !== 0 ? { "x-cloak": true } : {})}
|
|
108
176
|
x-ref={`content-${index}`}
|
|
109
|
-
class="w-full
|
|
110
|
-
:style={`
|
|
111
|
-
style={index === 0 ? '
|
|
177
|
+
class="w-full"
|
|
178
|
+
:style={`getPanelStyle(${index})`}
|
|
179
|
+
style={index === 0 ? 'position: relative;' : ''}
|
|
112
180
|
set:html={content}
|
|
113
181
|
/>
|
|
114
182
|
)) }
|
|
115
|
-
</div>
|
|
116
183
|
</div>
|
|
117
184
|
</div>
|
|
185
|
+
|
|
186
|
+
<style>
|
|
187
|
+
@keyframes rd-tabs-slide-in-from-right {
|
|
188
|
+
from {
|
|
189
|
+
transform: translateX(100%);
|
|
190
|
+
}
|
|
191
|
+
to {
|
|
192
|
+
transform: translateX(0);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@keyframes rd-tabs-slide-in-from-left {
|
|
197
|
+
from {
|
|
198
|
+
transform: translateX(-100%);
|
|
199
|
+
}
|
|
200
|
+
to {
|
|
201
|
+
transform: translateX(0);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
@keyframes rd-tabs-slide-out-to-left {
|
|
206
|
+
from {
|
|
207
|
+
transform: translateX(0);
|
|
208
|
+
}
|
|
209
|
+
to {
|
|
210
|
+
transform: translateX(-100%);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
@keyframes rd-tabs-slide-out-to-right {
|
|
215
|
+
from {
|
|
216
|
+
transform: translateX(0);
|
|
217
|
+
}
|
|
218
|
+
to {
|
|
219
|
+
transform: translateX(100%);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
</style>
|
|
@@ -151,6 +151,29 @@
|
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
/* Markdown table styling */
|
|
155
|
+
.prose-rules :where(table) {
|
|
156
|
+
@apply w-full overflow-hidden rounded-xl border border-neutral-200 shadow-xs dark:border-neutral-800;
|
|
157
|
+
border-collapse: separate;
|
|
158
|
+
border-spacing: 0;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.prose-rules :where(thead th) {
|
|
162
|
+
@apply bg-neutral-50 dark:bg-neutral-900/60;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.prose-rules :where(th, td) {
|
|
166
|
+
@apply border-b border-neutral-200 px-4 py-2 align-top dark:border-neutral-800;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.prose-rules :where(th + th, td + td) {
|
|
170
|
+
@apply border-l border-neutral-200 dark:border-neutral-800;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.prose-rules :where(tbody tr:last-child td) {
|
|
174
|
+
@apply border-b-0;
|
|
175
|
+
}
|
|
176
|
+
|
|
154
177
|
/* Code single-line styling */
|
|
155
178
|
:is(.prose, .prose-rules) :not(pre) > code {
|
|
156
179
|
@apply px-1 py-px bg-neutral-100/90 text-neutral-600 rounded-md font-mono font-medium border border-neutral-200/80 after:hidden before:hidden dark:bg-neutral-800/80 dark:text-neutral-200 dark:border-neutral-700/80;
|