@lexibase/rdx-ui 1.0.0-beta.1 → 1.0.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/README.md +88 -1
- package/package.json +32 -26
- package/postcss-plugin.cjs +2 -0
- package/postcss.config.mjs +1 -5
- package/src/components/accordion.tsx +91 -0
- package/src/components/blockquote.tsx +33 -0
- package/src/components/button-link.tsx +27 -0
- package/src/components/button.tsx +9 -12
- package/src/components/callout.tsx +96 -0
- package/src/components/code-block.tsx +55 -0
- package/src/components/command.tsx +90 -0
- package/src/components/copy-code-button.tsx +78 -0
- package/src/components/dialog.tsx +76 -0
- package/src/components/docs-search-dialog.tsx +184 -0
- package/src/components/docs-search.tsx +80 -0
- package/src/components/docs-sidebar.tsx +13 -20
- package/src/components/docs-toc.tsx +231 -0
- package/src/components/dropdown-menu.tsx +88 -39
- package/src/components/home.tsx +5 -6
- package/src/components/mdx-components.tsx +118 -27
- package/src/components/navbar.tsx +22 -28
- package/src/components/popover.tsx +68 -0
- package/src/components/separator.tsx +11 -9
- package/src/components/tabs.tsx +183 -0
- package/src/components/version-dropdown.tsx +38 -32
- package/src/components/version-notice.tsx +39 -0
- package/src/lib/docs-search.ts +68 -0
- package/src/lib/utils.ts +10 -0
- package/src/styles/globals.css +34 -0
- package/src/styles/globals.d.css.ts +2 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useState } from 'react'
|
|
4
|
+
import { usePathname } from 'next/navigation'
|
|
5
|
+
|
|
6
|
+
import { cn } from '@lexibase/rdx-ui/lib/utils'
|
|
7
|
+
import {
|
|
8
|
+
Popover,
|
|
9
|
+
PopoverContent,
|
|
10
|
+
PopoverTitle,
|
|
11
|
+
PopoverTrigger,
|
|
12
|
+
type PopoverActions,
|
|
13
|
+
} from './popover'
|
|
14
|
+
|
|
15
|
+
type Heading = { id: string; title: string; depth: number }
|
|
16
|
+
type HeadingNode = Heading & { children: HeadingNode[] }
|
|
17
|
+
|
|
18
|
+
function headingTree(headings: Heading[]) {
|
|
19
|
+
const roots: HeadingNode[] = []
|
|
20
|
+
const parents: HeadingNode[] = []
|
|
21
|
+
for (const heading of headings) {
|
|
22
|
+
while (
|
|
23
|
+
parents.length &&
|
|
24
|
+
parents[parents.length - 1]!.depth >= heading.depth
|
|
25
|
+
)
|
|
26
|
+
parents.pop()
|
|
27
|
+
const node: HeadingNode = { ...heading, children: [] }
|
|
28
|
+
const siblings = parents[parents.length - 1]?.children ?? roots
|
|
29
|
+
siblings.push(node)
|
|
30
|
+
parents.push(node)
|
|
31
|
+
}
|
|
32
|
+
return roots
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function HeadingLinks({
|
|
36
|
+
nodes,
|
|
37
|
+
baseDepth,
|
|
38
|
+
activeId,
|
|
39
|
+
}: {
|
|
40
|
+
nodes: HeadingNode[]
|
|
41
|
+
baseDepth: number
|
|
42
|
+
activeId: string
|
|
43
|
+
}) {
|
|
44
|
+
return (
|
|
45
|
+
<ol className="space-y-0.5">
|
|
46
|
+
{nodes.map((heading) => (
|
|
47
|
+
<li key={heading.id}>
|
|
48
|
+
<a
|
|
49
|
+
href={`#${encodeURIComponent(heading.id)}`}
|
|
50
|
+
aria-current={heading.id === activeId ? 'location' : undefined}
|
|
51
|
+
style={{ paddingLeft: 8 + (heading.depth - baseDepth) * 12 }}
|
|
52
|
+
className={cn(
|
|
53
|
+
'block rounded-md py-1.5 pr-2 text-xs leading-5 text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
|
54
|
+
heading.id === activeId &&
|
|
55
|
+
'bg-accent/60 font-medium text-foreground'
|
|
56
|
+
)}
|
|
57
|
+
>
|
|
58
|
+
{heading.title}
|
|
59
|
+
</a>
|
|
60
|
+
{heading.children.length > 0 && (
|
|
61
|
+
<HeadingLinks
|
|
62
|
+
nodes={heading.children}
|
|
63
|
+
baseDepth={baseDepth}
|
|
64
|
+
activeId={activeId}
|
|
65
|
+
/>
|
|
66
|
+
)}
|
|
67
|
+
</li>
|
|
68
|
+
))}
|
|
69
|
+
</ol>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Read the rendered MDX, sharing exactly the IDs used by document links. */
|
|
74
|
+
export function DocsToc({ contentId = 'rdx-main' }: { contentId?: string }) {
|
|
75
|
+
const pathname = usePathname()
|
|
76
|
+
return <PageToc key={pathname} contentId={contentId} />
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function PageToc({ contentId }: { contentId: string }) {
|
|
80
|
+
const [headings, setHeadings] = useState<Heading[]>([])
|
|
81
|
+
const [activeId, setActiveId] = useState('')
|
|
82
|
+
const actions = useRef<PopoverActions | null>(null)
|
|
83
|
+
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
const content = document.getElementById(contentId)
|
|
86
|
+
if (!content) return
|
|
87
|
+
let elements: HTMLHeadingElement[] = []
|
|
88
|
+
let frame = 0
|
|
89
|
+
let scrollRoot: HTMLElement | null = content.parentElement
|
|
90
|
+
while (
|
|
91
|
+
scrollRoot &&
|
|
92
|
+
!/(auto|scroll)/.test(getComputedStyle(scrollRoot).overflowY)
|
|
93
|
+
) {
|
|
94
|
+
scrollRoot = scrollRoot.parentElement
|
|
95
|
+
}
|
|
96
|
+
const scrollTarget = scrollRoot ?? window
|
|
97
|
+
// Keep native fragment links/history; smooth the actual document scroller.
|
|
98
|
+
// Tailwind's motion-safe variant leaves reduced-motion navigation instant.
|
|
99
|
+
const scrollElement = scrollRoot ?? document.documentElement
|
|
100
|
+
const alreadySmooth = scrollElement.classList.contains(
|
|
101
|
+
'motion-safe:scroll-smooth'
|
|
102
|
+
)
|
|
103
|
+
scrollElement.classList.add('motion-safe:scroll-smooth')
|
|
104
|
+
function updateActive() {
|
|
105
|
+
frame = 0
|
|
106
|
+
const top = (scrollRoot?.getBoundingClientRect().top ?? 0) + 96
|
|
107
|
+
let current = elements[0]?.id ?? ''
|
|
108
|
+
for (const heading of elements) {
|
|
109
|
+
if (heading.getBoundingClientRect().top > top) break
|
|
110
|
+
current = heading.id
|
|
111
|
+
}
|
|
112
|
+
// A short final section cannot reach the top: there is no content left
|
|
113
|
+
// to scroll. Reaching the document's bottom must still activate it.
|
|
114
|
+
if (
|
|
115
|
+
elements.length &&
|
|
116
|
+
scrollElement.scrollHeight > scrollElement.clientHeight + 2 &&
|
|
117
|
+
scrollElement.scrollTop + scrollElement.clientHeight >=
|
|
118
|
+
scrollElement.scrollHeight - 2
|
|
119
|
+
)
|
|
120
|
+
current = elements[elements.length - 1]!.id
|
|
121
|
+
setActiveId(current)
|
|
122
|
+
}
|
|
123
|
+
function schedule() {
|
|
124
|
+
if (!frame) frame = requestAnimationFrame(updateActive)
|
|
125
|
+
}
|
|
126
|
+
function collect() {
|
|
127
|
+
if (!window.matchMedia('(min-width: 1280px)').matches)
|
|
128
|
+
actions.current?.close()
|
|
129
|
+
elements = Array.from(
|
|
130
|
+
content!.querySelectorAll<HTMLHeadingElement>(
|
|
131
|
+
'h2[id], h3[id], h4[id], h5[id], h6[id]'
|
|
132
|
+
)
|
|
133
|
+
).filter(
|
|
134
|
+
(element) =>
|
|
135
|
+
element.getClientRects().length > 0 &&
|
|
136
|
+
!!element.id &&
|
|
137
|
+
!!element.textContent?.trim() &&
|
|
138
|
+
getComputedStyle(element).visibility !== 'hidden'
|
|
139
|
+
)
|
|
140
|
+
setHeadings(
|
|
141
|
+
elements
|
|
142
|
+
.map((element) => ({
|
|
143
|
+
id: element.id,
|
|
144
|
+
title: element.textContent?.trim() ?? '',
|
|
145
|
+
depth: Number(element.tagName.slice(1)),
|
|
146
|
+
}))
|
|
147
|
+
.filter((heading) => heading.title)
|
|
148
|
+
)
|
|
149
|
+
schedule()
|
|
150
|
+
}
|
|
151
|
+
const initialFrame = requestAnimationFrame(collect)
|
|
152
|
+
const observer = new MutationObserver(collect)
|
|
153
|
+
observer.observe(content, {
|
|
154
|
+
childList: true,
|
|
155
|
+
subtree: true,
|
|
156
|
+
characterData: true,
|
|
157
|
+
attributes: true,
|
|
158
|
+
attributeFilter: ['hidden', 'style', 'class', 'id'],
|
|
159
|
+
})
|
|
160
|
+
scrollTarget.addEventListener('scroll', schedule, { passive: true })
|
|
161
|
+
window.addEventListener('resize', collect)
|
|
162
|
+
const resize = new ResizeObserver(schedule)
|
|
163
|
+
resize.observe(content)
|
|
164
|
+
return () => {
|
|
165
|
+
observer.disconnect()
|
|
166
|
+
resize.disconnect()
|
|
167
|
+
scrollTarget.removeEventListener('scroll', schedule)
|
|
168
|
+
window.removeEventListener('resize', collect)
|
|
169
|
+
cancelAnimationFrame(frame)
|
|
170
|
+
cancelAnimationFrame(initialFrame)
|
|
171
|
+
if (!alreadySmooth)
|
|
172
|
+
scrollElement.classList.remove('motion-safe:scroll-smooth')
|
|
173
|
+
}
|
|
174
|
+
}, [contentId])
|
|
175
|
+
|
|
176
|
+
if (headings.length < 2) return null
|
|
177
|
+
const baseDepth = Math.min(...headings.map((heading) => heading.depth))
|
|
178
|
+
return (
|
|
179
|
+
<aside
|
|
180
|
+
data-rdx-toc
|
|
181
|
+
className="fixed right-4 top-1/2 z-30 hidden -translate-y-1/2 xl:block"
|
|
182
|
+
>
|
|
183
|
+
<Popover actionsRef={actions}>
|
|
184
|
+
<PopoverTrigger
|
|
185
|
+
openOnHover
|
|
186
|
+
delay={120}
|
|
187
|
+
aria-label="On this page"
|
|
188
|
+
className="flex max-h-[40vh] w-12 flex-col items-end gap-2 overflow-hidden rounded-md p-3 text-muted-foreground/40 outline-none transition-colors hover:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring"
|
|
189
|
+
>
|
|
190
|
+
{headings.map((heading) => (
|
|
191
|
+
<span
|
|
192
|
+
key={heading.id}
|
|
193
|
+
aria-hidden="true"
|
|
194
|
+
className={cn(
|
|
195
|
+
'h-0.5 shrink-0 rounded-full bg-current transition-colors',
|
|
196
|
+
heading.depth === baseDepth ? 'w-5' : 'w-3',
|
|
197
|
+
heading.id === activeId && 'text-foreground'
|
|
198
|
+
)}
|
|
199
|
+
/>
|
|
200
|
+
))}
|
|
201
|
+
</PopoverTrigger>
|
|
202
|
+
<PopoverContent
|
|
203
|
+
side="left"
|
|
204
|
+
sideOffset={0}
|
|
205
|
+
onMouseLeave={(event) => {
|
|
206
|
+
// Base UI keeps click-opened popovers pinned; the TOC instead
|
|
207
|
+
// stays open only while the pointer is over its popup or marks.
|
|
208
|
+
if (
|
|
209
|
+
event.relatedTarget instanceof Element &&
|
|
210
|
+
event.relatedTarget.closest('[data-rdx-toc]')
|
|
211
|
+
)
|
|
212
|
+
return
|
|
213
|
+
actions.current?.close()
|
|
214
|
+
}}
|
|
215
|
+
className="hidden max-h-[min(60vh,var(--available-height))] w-64 overflow-y-auto xl:flex"
|
|
216
|
+
>
|
|
217
|
+
<PopoverTitle className="px-2 pt-1 text-xs text-muted-foreground">
|
|
218
|
+
On this page
|
|
219
|
+
</PopoverTitle>
|
|
220
|
+
<nav aria-label="On this page">
|
|
221
|
+
<HeadingLinks
|
|
222
|
+
nodes={headingTree(headings)}
|
|
223
|
+
baseDepth={baseDepth}
|
|
224
|
+
activeId={activeId}
|
|
225
|
+
/>
|
|
226
|
+
</nav>
|
|
227
|
+
</PopoverContent>
|
|
228
|
+
</Popover>
|
|
229
|
+
</aside>
|
|
230
|
+
)
|
|
231
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
|
-
import
|
|
4
|
+
import { Menu as DropdownMenuPrimitive } from '@base-ui/react/menu'
|
|
5
5
|
import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react'
|
|
6
6
|
|
|
7
|
-
import { cn } from '@lexibase/rdx-ui/lib/utils'
|
|
7
|
+
import { cn, mergeStateClassName } from '@lexibase/rdx-ui/lib/utils'
|
|
8
8
|
|
|
9
9
|
function DropdownMenu({
|
|
10
10
|
...props
|
|
11
11
|
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
|
12
|
-
return <DropdownMenuPrimitive.Root
|
|
12
|
+
return <DropdownMenuPrimitive.Root {...props} />
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function DropdownMenuPortal({
|
|
@@ -31,22 +31,42 @@ function DropdownMenuTrigger({
|
|
|
31
31
|
)
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
type DropdownMenuContentProps = React.ComponentProps<
|
|
35
|
+
typeof DropdownMenuPrimitive.Popup
|
|
36
|
+
> &
|
|
37
|
+
Pick<
|
|
38
|
+
React.ComponentProps<typeof DropdownMenuPrimitive.Positioner>,
|
|
39
|
+
'align' | 'alignOffset' | 'side' | 'sideOffset' | 'collisionPadding'
|
|
40
|
+
>
|
|
41
|
+
|
|
34
42
|
function DropdownMenuContent({
|
|
35
43
|
className,
|
|
44
|
+
align = 'start',
|
|
45
|
+
alignOffset = 0,
|
|
46
|
+
side = 'bottom',
|
|
36
47
|
sideOffset = 4,
|
|
48
|
+
collisionPadding = 8,
|
|
37
49
|
...props
|
|
38
|
-
}:
|
|
50
|
+
}: DropdownMenuContentProps) {
|
|
39
51
|
return (
|
|
40
52
|
<DropdownMenuPrimitive.Portal>
|
|
41
|
-
<DropdownMenuPrimitive.
|
|
42
|
-
|
|
53
|
+
<DropdownMenuPrimitive.Positioner
|
|
54
|
+
className="isolate z-50 outline-none"
|
|
55
|
+
align={align}
|
|
56
|
+
alignOffset={alignOffset}
|
|
57
|
+
side={side}
|
|
43
58
|
sideOffset={sideOffset}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
59
|
+
collisionPadding={collisionPadding}
|
|
60
|
+
>
|
|
61
|
+
<DropdownMenuPrimitive.Popup
|
|
62
|
+
data-slot="dropdown-menu-content"
|
|
63
|
+
className={mergeStateClassName(
|
|
64
|
+
'bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--available-height) min-w-[8rem] origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md outline-none',
|
|
65
|
+
className
|
|
66
|
+
)}
|
|
67
|
+
{...props}
|
|
68
|
+
/>
|
|
69
|
+
</DropdownMenuPrimitive.Positioner>
|
|
50
70
|
</DropdownMenuPrimitive.Portal>
|
|
51
71
|
)
|
|
52
72
|
}
|
|
@@ -73,8 +93,31 @@ function DropdownMenuItem({
|
|
|
73
93
|
data-slot="dropdown-menu-item"
|
|
74
94
|
data-inset={inset}
|
|
75
95
|
data-variant={variant}
|
|
76
|
-
className={
|
|
77
|
-
"
|
|
96
|
+
className={mergeStateClassName(
|
|
97
|
+
"data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:data-highlighted:bg-destructive/10 dark:data-[variant=destructive]:data-highlighted:bg-destructive/20 data-[variant=destructive]:data-highlighted:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
98
|
+
className
|
|
99
|
+
)}
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function DropdownMenuLinkItem({
|
|
106
|
+
className,
|
|
107
|
+
inset,
|
|
108
|
+
variant = 'default',
|
|
109
|
+
...props
|
|
110
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.LinkItem> & {
|
|
111
|
+
inset?: boolean
|
|
112
|
+
variant?: 'default' | 'destructive'
|
|
113
|
+
}) {
|
|
114
|
+
return (
|
|
115
|
+
<DropdownMenuPrimitive.LinkItem
|
|
116
|
+
data-slot="dropdown-menu-item"
|
|
117
|
+
data-inset={inset}
|
|
118
|
+
data-variant={variant}
|
|
119
|
+
className={mergeStateClassName(
|
|
120
|
+
"data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:data-highlighted:bg-destructive/10 dark:data-[variant=destructive]:data-highlighted:bg-destructive/20 data-[variant=destructive]:data-highlighted:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
78
121
|
className
|
|
79
122
|
)}
|
|
80
123
|
{...props}
|
|
@@ -91,17 +134,17 @@ function DropdownMenuCheckboxItem({
|
|
|
91
134
|
return (
|
|
92
135
|
<DropdownMenuPrimitive.CheckboxItem
|
|
93
136
|
data-slot="dropdown-menu-checkbox-item"
|
|
94
|
-
className={
|
|
95
|
-
"
|
|
137
|
+
className={mergeStateClassName(
|
|
138
|
+
"data-highlighted:bg-accent data-highlighted:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
96
139
|
className
|
|
97
140
|
)}
|
|
98
141
|
checked={checked}
|
|
99
142
|
{...props}
|
|
100
143
|
>
|
|
101
144
|
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
102
|
-
<DropdownMenuPrimitive.
|
|
145
|
+
<DropdownMenuPrimitive.CheckboxItemIndicator>
|
|
103
146
|
<CheckIcon className="size-4" />
|
|
104
|
-
</DropdownMenuPrimitive.
|
|
147
|
+
</DropdownMenuPrimitive.CheckboxItemIndicator>
|
|
105
148
|
</span>
|
|
106
149
|
{children}
|
|
107
150
|
</DropdownMenuPrimitive.CheckboxItem>
|
|
@@ -127,16 +170,16 @@ function DropdownMenuRadioItem({
|
|
|
127
170
|
return (
|
|
128
171
|
<DropdownMenuPrimitive.RadioItem
|
|
129
172
|
data-slot="dropdown-menu-radio-item"
|
|
130
|
-
className={
|
|
131
|
-
"
|
|
173
|
+
className={mergeStateClassName(
|
|
174
|
+
"data-highlighted:bg-accent data-highlighted:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
132
175
|
className
|
|
133
176
|
)}
|
|
134
177
|
{...props}
|
|
135
178
|
>
|
|
136
179
|
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
137
|
-
<DropdownMenuPrimitive.
|
|
180
|
+
<DropdownMenuPrimitive.RadioItemIndicator>
|
|
138
181
|
<CircleIcon className="size-2 fill-current" />
|
|
139
|
-
</DropdownMenuPrimitive.
|
|
182
|
+
</DropdownMenuPrimitive.RadioItemIndicator>
|
|
140
183
|
</span>
|
|
141
184
|
{children}
|
|
142
185
|
</DropdownMenuPrimitive.RadioItem>
|
|
@@ -147,14 +190,14 @@ function DropdownMenuLabel({
|
|
|
147
190
|
className,
|
|
148
191
|
inset,
|
|
149
192
|
...props
|
|
150
|
-
}: React.ComponentProps<typeof DropdownMenuPrimitive.
|
|
193
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.GroupLabel> & {
|
|
151
194
|
inset?: boolean
|
|
152
195
|
}) {
|
|
153
196
|
return (
|
|
154
|
-
<DropdownMenuPrimitive.
|
|
197
|
+
<DropdownMenuPrimitive.GroupLabel
|
|
155
198
|
data-slot="dropdown-menu-label"
|
|
156
199
|
data-inset={inset}
|
|
157
|
-
className={
|
|
200
|
+
className={mergeStateClassName(
|
|
158
201
|
'px-2 py-1.5 text-sm font-medium data-[inset]:pl-8',
|
|
159
202
|
className
|
|
160
203
|
)}
|
|
@@ -170,7 +213,7 @@ function DropdownMenuSeparator({
|
|
|
170
213
|
return (
|
|
171
214
|
<DropdownMenuPrimitive.Separator
|
|
172
215
|
data-slot="dropdown-menu-separator"
|
|
173
|
-
className={
|
|
216
|
+
className={mergeStateClassName('bg-border -mx-1 my-1 h-px', className)}
|
|
174
217
|
{...props}
|
|
175
218
|
/>
|
|
176
219
|
)
|
|
@@ -194,8 +237,8 @@ function DropdownMenuShortcut({
|
|
|
194
237
|
|
|
195
238
|
function DropdownMenuSub({
|
|
196
239
|
...props
|
|
197
|
-
}: React.ComponentProps<typeof DropdownMenuPrimitive.
|
|
198
|
-
return <DropdownMenuPrimitive.
|
|
240
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubmenuRoot>) {
|
|
241
|
+
return <DropdownMenuPrimitive.SubmenuRoot {...props} />
|
|
199
242
|
}
|
|
200
243
|
|
|
201
244
|
function DropdownMenuSubTrigger({
|
|
@@ -203,36 +246,41 @@ function DropdownMenuSubTrigger({
|
|
|
203
246
|
inset,
|
|
204
247
|
children,
|
|
205
248
|
...props
|
|
206
|
-
}: React.ComponentProps<typeof DropdownMenuPrimitive.
|
|
249
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubmenuTrigger> & {
|
|
207
250
|
inset?: boolean
|
|
208
251
|
}) {
|
|
209
252
|
return (
|
|
210
|
-
<DropdownMenuPrimitive.
|
|
253
|
+
<DropdownMenuPrimitive.SubmenuTrigger
|
|
211
254
|
data-slot="dropdown-menu-sub-trigger"
|
|
212
255
|
data-inset={inset}
|
|
213
|
-
className={
|
|
214
|
-
'
|
|
256
|
+
className={mergeStateClassName(
|
|
257
|
+
'data-highlighted:bg-accent data-highlighted:text-accent-foreground data-popup-open:bg-accent data-popup-open:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8',
|
|
215
258
|
className
|
|
216
259
|
)}
|
|
217
260
|
{...props}
|
|
218
261
|
>
|
|
219
262
|
{children}
|
|
220
263
|
<ChevronRightIcon className="ml-auto size-4" />
|
|
221
|
-
</DropdownMenuPrimitive.
|
|
264
|
+
</DropdownMenuPrimitive.SubmenuTrigger>
|
|
222
265
|
)
|
|
223
266
|
}
|
|
224
267
|
|
|
225
268
|
function DropdownMenuSubContent({
|
|
226
269
|
className,
|
|
270
|
+
align = 'start',
|
|
271
|
+
alignOffset = -3,
|
|
272
|
+
side = 'right',
|
|
273
|
+
sideOffset = 0,
|
|
227
274
|
...props
|
|
228
|
-
}:
|
|
275
|
+
}: DropdownMenuContentProps) {
|
|
229
276
|
return (
|
|
230
|
-
<
|
|
277
|
+
<DropdownMenuContent
|
|
231
278
|
data-slot="dropdown-menu-sub-content"
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
279
|
+
align={align}
|
|
280
|
+
alignOffset={alignOffset}
|
|
281
|
+
side={side}
|
|
282
|
+
sideOffset={sideOffset}
|
|
283
|
+
className={mergeStateClassName('shadow-lg', className)}
|
|
236
284
|
{...props}
|
|
237
285
|
/>
|
|
238
286
|
)
|
|
@@ -246,6 +294,7 @@ export {
|
|
|
246
294
|
DropdownMenuGroup,
|
|
247
295
|
DropdownMenuLabel,
|
|
248
296
|
DropdownMenuItem,
|
|
297
|
+
DropdownMenuLinkItem,
|
|
249
298
|
DropdownMenuCheckboxItem,
|
|
250
299
|
DropdownMenuRadioGroup,
|
|
251
300
|
DropdownMenuRadioItem,
|
package/src/components/home.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react'
|
|
2
|
-
import Link from 'next/link'
|
|
3
2
|
|
|
4
|
-
import {
|
|
3
|
+
import { ButtonLink } from './button-link'
|
|
5
4
|
import { Logo } from './logo'
|
|
6
5
|
|
|
7
6
|
/** The default landing page from apps/web, not a separate marketing theme. */
|
|
@@ -33,13 +32,13 @@ export function Home({
|
|
|
33
32
|
{buttons.length > 0 && (
|
|
34
33
|
<div className="flex flex-wrap justify-center gap-3 mt-6 px-6">
|
|
35
34
|
{buttons.map((button, index) => (
|
|
36
|
-
<
|
|
35
|
+
<ButtonLink
|
|
37
36
|
key={`${index}:${button.href}`}
|
|
38
37
|
variant={index === 0 ? 'default' : 'outline'}
|
|
39
|
-
|
|
38
|
+
href={button.href}
|
|
40
39
|
>
|
|
41
|
-
|
|
42
|
-
</
|
|
40
|
+
{button.label}
|
|
41
|
+
</ButtonLink>
|
|
43
42
|
))}
|
|
44
43
|
</div>
|
|
45
44
|
)}
|
|
@@ -1,51 +1,142 @@
|
|
|
1
1
|
import type { ComponentProps } from 'react'
|
|
2
2
|
|
|
3
|
+
import { cn } from '../lib/utils'
|
|
4
|
+
import {
|
|
5
|
+
Accordion,
|
|
6
|
+
AccordionContent,
|
|
7
|
+
AccordionItem,
|
|
8
|
+
AccordionTrigger,
|
|
9
|
+
} from './accordion'
|
|
10
|
+
import { Blockquote } from './blockquote'
|
|
11
|
+
import { Callout } from './callout'
|
|
12
|
+
import { CodeBlock } from './code-block'
|
|
13
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from './tabs'
|
|
3
14
|
import { Separator } from '@lexibase/rdx-ui/components/separator'
|
|
4
15
|
|
|
5
16
|
export const mdxComponents = {
|
|
6
|
-
h1: (props: ComponentProps<'h1'>) => (
|
|
7
|
-
<h1 className=
|
|
17
|
+
h1: ({ className, ...props }: ComponentProps<'h1'>) => (
|
|
18
|
+
<h1 className={cn('text-4xl font-bold my-6', className)} {...props} />
|
|
8
19
|
),
|
|
9
|
-
h2: (props: ComponentProps<'h2'>) => (
|
|
10
|
-
<h2 className=
|
|
20
|
+
h2: ({ className, ...props }: ComponentProps<'h2'>) => (
|
|
21
|
+
<h2 className={cn('text-2xl font-bold my-6', className)} {...props} />
|
|
11
22
|
),
|
|
12
|
-
h3: (props: ComponentProps<'h3'>) => (
|
|
13
|
-
<h3 className=
|
|
23
|
+
h3: ({ className, ...props }: ComponentProps<'h3'>) => (
|
|
24
|
+
<h3 className={cn('text-xl font-bold my-6', className)} {...props} />
|
|
14
25
|
),
|
|
15
|
-
p: (props: ComponentProps<'p'>) =>
|
|
16
|
-
|
|
17
|
-
<ul className="list-disc my-4 ml-6" {...props} />
|
|
26
|
+
p: ({ className, ...props }: ComponentProps<'p'>) => (
|
|
27
|
+
<p className={cn('my-4', className)} {...props} />
|
|
18
28
|
),
|
|
19
|
-
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
ul: ({ className, ...props }: ComponentProps<'ul'>) => (
|
|
30
|
+
<ul
|
|
31
|
+
className={cn(
|
|
32
|
+
'list-disc my-4 ml-6 [&.contains-task-list]:list-none [&.contains-task-list]:ml-0',
|
|
33
|
+
className
|
|
34
|
+
)}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
24
37
|
),
|
|
25
|
-
|
|
26
|
-
<
|
|
38
|
+
ol: ({ className, ...props }: ComponentProps<'ol'>) => (
|
|
39
|
+
<ol className={cn('list-decimal my-4 ml-6', className)} {...props} />
|
|
27
40
|
),
|
|
28
|
-
|
|
29
|
-
<
|
|
30
|
-
className=
|
|
41
|
+
li: ({ className, ...props }: ComponentProps<'li'>) => (
|
|
42
|
+
<li
|
|
43
|
+
className={cn(
|
|
44
|
+
'my-2 before:mr-2 [&>input[type=checkbox]]:mr-2',
|
|
45
|
+
className
|
|
46
|
+
)}
|
|
31
47
|
{...props}
|
|
32
48
|
/>
|
|
33
49
|
),
|
|
34
|
-
|
|
35
|
-
<
|
|
36
|
-
className=
|
|
50
|
+
a: ({ className, ...props }: ComponentProps<'a'>) => (
|
|
51
|
+
<a
|
|
52
|
+
className={cn('underline text-chart-1 text-blue-500', className)}
|
|
37
53
|
{...props}
|
|
38
54
|
/>
|
|
39
55
|
),
|
|
40
|
-
|
|
56
|
+
blockquote: Blockquote,
|
|
57
|
+
pre: CodeBlock,
|
|
58
|
+
code: ({ className, ...props }: ComponentProps<'code'>) => (
|
|
41
59
|
<code
|
|
42
|
-
className=
|
|
60
|
+
className={cn(
|
|
61
|
+
'bg-secondary text-secondary-foreground rounded-sm px-1 py-0.5',
|
|
62
|
+
className
|
|
63
|
+
)}
|
|
43
64
|
{...props}
|
|
44
65
|
/>
|
|
45
66
|
),
|
|
46
67
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
68
|
+
h4: ({ className, ...props }: ComponentProps<'h4'>) => (
|
|
69
|
+
<h4 className={cn('text-lg font-semibold my-4', className)} {...props} />
|
|
70
|
+
),
|
|
71
|
+
h5: ({ className, ...props }: ComponentProps<'h5'>) => (
|
|
72
|
+
<h5 className={cn('text-base font-semibold my-4', className)} {...props} />
|
|
73
|
+
),
|
|
74
|
+
h6: ({ className, ...props }: ComponentProps<'h6'>) => (
|
|
75
|
+
<h6 className={cn('text-sm font-semibold my-4', className)} {...props} />
|
|
76
|
+
),
|
|
77
|
+
hr: ({ className, ...props }: ComponentProps<'hr'>) => (
|
|
78
|
+
<hr className={cn('my-8 border-border', className)} {...props} />
|
|
79
|
+
),
|
|
80
|
+
img: ({ className, alt = '', ...props }: ComponentProps<'img'>) => (
|
|
81
|
+
<img
|
|
82
|
+
alt={alt}
|
|
83
|
+
className={cn('max-w-full h-auto rounded-md my-4', className)}
|
|
84
|
+
{...props}
|
|
85
|
+
/>
|
|
86
|
+
),
|
|
87
|
+
table: ({ className, ...props }: ComponentProps<'table'>) => (
|
|
88
|
+
<div
|
|
89
|
+
data-slot="mdx-table-scroll"
|
|
90
|
+
role="region"
|
|
91
|
+
aria-label="Table"
|
|
92
|
+
tabIndex={0}
|
|
93
|
+
className="my-6 min-w-0 max-w-full overflow-x-auto rounded-lg border border-border bg-background focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
|
|
94
|
+
>
|
|
95
|
+
<table
|
|
96
|
+
className={cn(
|
|
97
|
+
'w-full border-separate border-spacing-0 text-sm [&_tbody>tr]:transition-colors motion-reduce:[&_tbody>tr]:transition-none [&_tbody>tr:nth-child(even)]:bg-muted/20 [&_tbody>tr:hover]:bg-muted/40 [&_tbody>tr:last-child>td]:border-b-0',
|
|
98
|
+
className
|
|
99
|
+
)}
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
),
|
|
104
|
+
th: ({ className, scope = 'col', ...props }: ComponentProps<'th'>) => (
|
|
105
|
+
<th
|
|
106
|
+
scope={scope}
|
|
107
|
+
className={cn(
|
|
108
|
+
'border-b border-r border-border bg-muted/40 px-4 py-3 text-left text-xs font-semibold tracking-wide text-foreground/90 last:border-r-0',
|
|
109
|
+
className
|
|
110
|
+
)}
|
|
111
|
+
{...props}
|
|
112
|
+
/>
|
|
113
|
+
),
|
|
114
|
+
td: ({ className, ...props }: ComponentProps<'td'>) => (
|
|
115
|
+
<td
|
|
116
|
+
className={cn(
|
|
117
|
+
'min-w-40 border-b border-r border-border/70 px-4 py-3 align-top leading-6 last:border-r-0',
|
|
118
|
+
className
|
|
119
|
+
)}
|
|
120
|
+
{...props}
|
|
121
|
+
/>
|
|
122
|
+
),
|
|
123
|
+
Callout,
|
|
124
|
+
Accordion,
|
|
125
|
+
AccordionItem,
|
|
126
|
+
AccordionTrigger,
|
|
127
|
+
AccordionContent,
|
|
128
|
+
Tabs,
|
|
129
|
+
TabsList,
|
|
130
|
+
TabsTrigger,
|
|
131
|
+
TabsContent,
|
|
132
|
+
Separator: ({ className, ...props }: ComponentProps<typeof Separator>) => (
|
|
133
|
+
<Separator
|
|
134
|
+
className={
|
|
135
|
+
typeof className === 'function'
|
|
136
|
+
? (state) => cn('my-8', className(state))
|
|
137
|
+
: cn('my-8', className)
|
|
138
|
+
}
|
|
139
|
+
{...props}
|
|
140
|
+
/>
|
|
50
141
|
),
|
|
51
142
|
}
|