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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "radiant-docs",
3
- "version": "0.1.38",
3
+ "version": "0.1.39",
4
4
  "description": "CLI tool for previewing Radiant documentation locally",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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 shadow-xs dark:border-neutral-800 dark:bg-(--rd-code-surface)",
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 class="relative min-w-0">
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 shadow-xs opacity-0 transition-[left,width,opacity] duration-200 ease-out dark:border-neutral-700/70 dark:bg-(--rd-code-surface)"
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 data-rd-code-group-content class="min-w-0">
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 (!contentElement || !tabsElement || !pillElement) return;
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 syncActiveTab = () => {
176
- tabs.forEach(({ itemElement, tabButton, iconContainer }, index) => {
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
- if (activeIndex === index) return;
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", syncPill);
230
- syncActiveTab();
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-white dark:bg-(--rd-code-surface)"
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: #fff !important;
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: #fff !important;
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
- this.$watch('activeTab', (value) => {
38
- this.updateMarker(value);
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
- updateHeight() {
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
- // scrollHeight is often more reliable than offsetHeight for hidden overflow
57
- this.containerHeight = activeSlide.scrollHeight + 'px';
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-fit rounded-lg border border-neutral-200 bg-neutral-100/80 p-[3px] inset-shadow-sm dark:border-none dark:bg-neutral-800/50"
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={`activeTab = ${index}`}
82
- class="relative px-3 h-[32px] font-medium text-sm transition-colors duration-200 cursor-pointer text-nowrap flex items-center gap-2"
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 shrink-0 transition-opacity duration-300 ease-in-out"
110
- :style={`activeTab === ${index} ? 'opacity: 1' : 'opacity: 0 pointer-events-none'`}
111
- style={index === 0 ? 'opacity: 1' : 'opacity: 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;