@hanzo/ui 4.0.5 → 4.1.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/blocks/components/accordian-block.tsx +1 -1
- package/blocks/components/bullet-cards-block.tsx +1 -1
- package/blocks/components/card-block/index.tsx +1 -1
- package/blocks/components/card-block/link-out-button.tsx +1 -1
- package/blocks/components/card-block/util.ts +1 -1
- package/blocks/components/carte-blanche-block/index.tsx +1 -1
- package/blocks/components/carte-blanche-block/variant-content-left.tsx +1 -1
- package/blocks/components/cta-block.tsx +4 -6
- package/blocks/components/enh-heading-block.tsx +2 -2
- package/blocks/components/heading-block.tsx +1 -1
- package/blocks/components/screenful-block/index.tsx +1 -1
- package/blocks/components/space-block.tsx +1 -1
- package/blocks/components/video-block.tsx +1 -1
- package/blocks/def/cta-block.ts +7 -13
- package/package.json +3 -2
- package/primitives/action-button.tsx +0 -7
- package/primitives/combobox.tsx +143 -0
- package/primitives/command.tsx +4 -2
- package/primitives/{index.ts → index-common.ts} +1 -8
- package/primitives/index-next.ts +2 -0
- package/primitives/{image.tsx → next/image.tsx} +2 -2
- package/primitives/next/index.ts +7 -0
- package/primitives/{inline-icon.tsx → next/inline-icon.tsx} +1 -1
- package/primitives/{link-element.tsx → next/link-element.tsx} +3 -3
- package/primitives/{media-stack.tsx → next/media-stack.tsx} +3 -3
- package/primitives/{nav-items.tsx → next/nav-items.tsx} +2 -2
- package/primitives/{youtube-embed.tsx → next/youtube-embed.tsx} +1 -1
- /package/primitives/{mdx-link.tsx → next/mdx-link.tsx} +0 -0
|
@@ -2,7 +2,7 @@ import React from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { containsToken, cn } from '../../util'
|
|
4
4
|
import type { Block, BulletCardsBlock } from '../def'
|
|
5
|
-
import InlineIcon from '../../primitives/inline-icon'
|
|
5
|
+
import InlineIcon from '../../primitives/next/inline-icon'
|
|
6
6
|
|
|
7
7
|
import type BlockComponentProps from './block-component-props'
|
|
8
8
|
import GridBlockComponent from './grid-block'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
3
|
import type { LinkDef, ButtonDef} from '../../types'
|
|
4
|
-
import { buttonVariants, ActionButton, LinkElement } from '../../primitives'
|
|
4
|
+
import { buttonVariants, ActionButton, LinkElement } from '../../primitives/index-next'
|
|
5
5
|
import type { CTABlock } from '../def'
|
|
6
6
|
import { cn, containsToken, type VariantProps } from '../../util'
|
|
7
7
|
|
|
@@ -44,6 +44,9 @@ const CtaBlockComponent: React.FC<BlockComponentProps & {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
const mobile2Columns = containsToken(specifiers, 'mobile-2-columns')
|
|
47
|
+
// normally 'default' buttons have a min width only at > lg.
|
|
48
|
+
// generally if more than one we don't want this and override it,
|
|
49
|
+
// but this specifier asks to observe the default behavior.
|
|
47
50
|
const fillEvenly = !containsToken(specifiers, 'desktop-dont-fill')
|
|
48
51
|
const mobileCenterFirstIfOdd = containsToken(specifiers, 'mobile-center-first-if-odd')
|
|
49
52
|
const mobileOddFullWidth = containsToken(specifiers, 'mobile-odd-full-width')
|
|
@@ -55,11 +58,6 @@ const CtaBlockComponent: React.FC<BlockComponentProps & {
|
|
|
55
58
|
layoutclx = 'grid grid-cols-2 gap-2 self-stretch '
|
|
56
59
|
resetMinWidth = true
|
|
57
60
|
}
|
|
58
|
-
/*
|
|
59
|
-
else {
|
|
60
|
-
layoutclx = 'flex flex-col items-stretch gap-2 self-stretch md:flex-row sm:justify-center'
|
|
61
|
-
}
|
|
62
|
-
*/
|
|
63
61
|
if (fillEvenly) {
|
|
64
62
|
layoutclx = (layoutclx ?? 'grid grid-cols-2 gap-2 self-stretch')
|
|
65
63
|
resetMinWidth = true
|
|
@@ -2,11 +2,11 @@ import React, {type ElementType} from 'react'
|
|
|
2
2
|
|
|
3
3
|
import type { Icon } from '../../types'
|
|
4
4
|
import type { EnhHeadingBlock } from '../def'
|
|
5
|
-
import { ApplyTypography } from '../../primitives'
|
|
5
|
+
import { ApplyTypography } from '../../primitives/index-common'
|
|
6
6
|
import { cn, containsToken } from '../../util'
|
|
7
7
|
|
|
8
8
|
import type BlockComponentProps from './block-component-props'
|
|
9
|
-
import InlineIcon from '../../primitives/inline-icon'
|
|
9
|
+
import InlineIcon from '../../primitives/next/inline-icon'
|
|
10
10
|
|
|
11
11
|
const DEFAULTS = {
|
|
12
12
|
preheading: {
|
|
@@ -3,7 +3,7 @@ import dynamic from 'next/dynamic'
|
|
|
3
3
|
|
|
4
4
|
import type { Block, ScreenfulBlock, VideoBlock } from '../../def'
|
|
5
5
|
import { containsToken, cn } from '../../../util'
|
|
6
|
-
import { ApplyTypography } from '../../../primitives'
|
|
6
|
+
import { ApplyTypography } from '../../../primitives/index-common'
|
|
7
7
|
|
|
8
8
|
import Poster from './poster-background'
|
|
9
9
|
import Content from './content'
|
|
@@ -5,7 +5,7 @@ import { ldMerge, cn } from '../../util'
|
|
|
5
5
|
import type { Breakpoint } from '../../types'
|
|
6
6
|
import { SPACE_DEFAULTS , type TWSpaceUnit, type HeadingLevel} from '../def/space-block'
|
|
7
7
|
import type SpaceBlock from '../def/space-block'
|
|
8
|
-
import { ApplyTypography } from '../../primitives'
|
|
8
|
+
import { ApplyTypography } from '../../primitives/index-common'
|
|
9
9
|
|
|
10
10
|
import type BlockComponentProps from './block-component-props'
|
|
11
11
|
|
|
@@ -6,7 +6,7 @@ import Image from 'next/image'
|
|
|
6
6
|
import type { Dimensions, TShirtSize, TShirtDimensions } from '../../types'
|
|
7
7
|
import { constrain, asNum, cn } from '../../util'
|
|
8
8
|
import type { VideoBlock } from '../def'
|
|
9
|
-
import { VideoPlayer } from '../../primitives'
|
|
9
|
+
import { VideoPlayer } from '../../primitives/index-common'
|
|
10
10
|
|
|
11
11
|
import type BlockComponentProps from './block-component-props'
|
|
12
12
|
|
package/blocks/def/cta-block.ts
CHANGED
|
@@ -3,19 +3,13 @@ import type Block from './block'
|
|
|
3
3
|
|
|
4
4
|
interface CTABlock extends Block {
|
|
5
5
|
blockType: 'cta'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
default is to center last
|
|
14
|
-
mobile-odd-full-width: fills the full two columns w the centered element
|
|
15
|
-
desktop-dont-fill: normally 'default' buttons have a min width only at > lg.
|
|
16
|
-
generally if more than one we don't want this and override it,
|
|
17
|
-
but this specifier asks to observe the default behavior.
|
|
18
|
-
*/
|
|
6
|
+
// fill: fills the parent width with the elements
|
|
7
|
+
// left / right: (>= md) left or right justify the elements (default is center)
|
|
8
|
+
// mobile-2-columns: mobile defaults to rendering each element full width,
|
|
9
|
+
// on it's own line. This renders them in two columns instead.
|
|
10
|
+
// mobile-center-first-if-odd: if (mobile-2-columns) and length is odd,
|
|
11
|
+
// default is to center last
|
|
12
|
+
// mobile-odd-full-width: fills the full two columns w the centered element
|
|
19
13
|
specifiers?: string
|
|
20
14
|
elements: (LinkDef | ButtonDef)[]
|
|
21
15
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/ui",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Library that contains shared UI primitives, support for a common design system, and other boilerplate support.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
},
|
|
36
36
|
"exports": {
|
|
37
37
|
"./blocks": "./blocks/index.ts",
|
|
38
|
-
"./primitives": "./primitives/index.ts",
|
|
38
|
+
"./primitives": "./primitives/index-next.ts",
|
|
39
|
+
"./primitives-common": "./primitives/index-common.ts",
|
|
39
40
|
"./style/": "./style/*",
|
|
40
41
|
"./tailwind": "./tailwind/index.ts",
|
|
41
42
|
"./types": "./types/index.ts",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
import React from 'react'
|
|
3
|
-
import dynamic from 'next/dynamic'
|
|
4
3
|
|
|
5
4
|
import { cn, type VariantProps } from '../util'
|
|
6
5
|
|
|
@@ -8,12 +7,6 @@ import type { ButtonDef, ButtonModalDef } from '../types'
|
|
|
8
7
|
import type { buttonVariants } from './button'
|
|
9
8
|
import DVC from './dialog-video-controller'
|
|
10
9
|
|
|
11
|
-
// The DVC must be rendered client-side since it accesses the DOM directly.
|
|
12
|
-
// There is no need for a loading UI since the dialog only opens
|
|
13
|
-
// once it's been rendered and the user is already waiting.
|
|
14
|
-
// https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading
|
|
15
|
-
//const DynamicDVC = dynamic(() => (import('./dialog-video-controller')), {ssr: false})
|
|
16
|
-
|
|
17
10
|
const ActionButton: React.FC<
|
|
18
11
|
VariantProps<typeof buttonVariants> &
|
|
19
12
|
{
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React, { useState } from 'react'
|
|
3
|
+
import { Check, ChevronDown } from 'lucide-react'
|
|
4
|
+
|
|
5
|
+
import { cn } from '../util'
|
|
6
|
+
import Button from './button'
|
|
7
|
+
import {
|
|
8
|
+
Command,
|
|
9
|
+
CommandEmpty,
|
|
10
|
+
CommandGroup,
|
|
11
|
+
CommandInput,
|
|
12
|
+
CommandItem,
|
|
13
|
+
CommandList,
|
|
14
|
+
} from './command'
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
Popover,
|
|
18
|
+
PopoverContent,
|
|
19
|
+
PopoverTrigger,
|
|
20
|
+
} from './popover'
|
|
21
|
+
|
|
22
|
+
interface SelectElement {
|
|
23
|
+
value: string,
|
|
24
|
+
label?: string,
|
|
25
|
+
imageUrl?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const IMAGE_SIZE = 32
|
|
29
|
+
|
|
30
|
+
const ElementImage: React.FC<{
|
|
31
|
+
url: string | undefined
|
|
32
|
+
alt?: string
|
|
33
|
+
w: number
|
|
34
|
+
h: number
|
|
35
|
+
}> = ({
|
|
36
|
+
url,
|
|
37
|
+
alt,
|
|
38
|
+
w,
|
|
39
|
+
h
|
|
40
|
+
}) => (url &&
|
|
41
|
+
<img
|
|
42
|
+
src={url}
|
|
43
|
+
alt={alt ?? 'image'}
|
|
44
|
+
height={h}
|
|
45
|
+
width={w}
|
|
46
|
+
loading="eager"
|
|
47
|
+
className="rounded-sm object-contain"
|
|
48
|
+
/>
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
const Combobox: React.FC<{
|
|
53
|
+
elements: SelectElement[]
|
|
54
|
+
buttonClx?: string
|
|
55
|
+
popoverClx?: string
|
|
56
|
+
buttonPlaceholder?: string
|
|
57
|
+
searchPlaceholder?: string
|
|
58
|
+
initial?: SelectElement,
|
|
59
|
+
elementSelected: (e: SelectElement) => void
|
|
60
|
+
disabled?: boolean
|
|
61
|
+
}> = ({
|
|
62
|
+
elements,
|
|
63
|
+
buttonClx='',
|
|
64
|
+
popoverClx='',
|
|
65
|
+
initial,
|
|
66
|
+
searchPlaceholder='Search...',
|
|
67
|
+
buttonPlaceholder='Select...',
|
|
68
|
+
elementSelected,
|
|
69
|
+
disabled=false
|
|
70
|
+
}) => {
|
|
71
|
+
|
|
72
|
+
const [open, setOpen] = useState<boolean>(false)
|
|
73
|
+
const [current, setCurrent] = useState<SelectElement | null>(initial ?? null)
|
|
74
|
+
|
|
75
|
+
const handleSelect = (selectedValue: string) => {
|
|
76
|
+
|
|
77
|
+
// some issue w case... I know, right??
|
|
78
|
+
const found = elements.find((el: SelectElement) => (el.value.toUpperCase() === selectedValue.toUpperCase()) )
|
|
79
|
+
|
|
80
|
+
if (found) {
|
|
81
|
+
setCurrent(found)
|
|
82
|
+
elementSelected(found)
|
|
83
|
+
}
|
|
84
|
+
setOpen(false)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const isCurrent = (el: SelectElement): boolean => (
|
|
88
|
+
!!current && (el.value.toUpperCase() === current.value.toUpperCase())
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
93
|
+
<PopoverTrigger asChild>
|
|
94
|
+
<Button
|
|
95
|
+
variant='outline'
|
|
96
|
+
role='combobox'
|
|
97
|
+
aria-expanded={open}
|
|
98
|
+
className={'flex justify-between ' + buttonClx}
|
|
99
|
+
disabled={disabled}
|
|
100
|
+
>
|
|
101
|
+
<div className='flex justify-start items-center gap-2'>
|
|
102
|
+
{current && (
|
|
103
|
+
<ElementImage url={current.imageUrl} w={IMAGE_SIZE} h={IMAGE_SIZE} alt={current.value + ' image'}/>
|
|
104
|
+
)}
|
|
105
|
+
<span>{ current ? (current.label ?? current.value) : buttonPlaceholder }</span>
|
|
106
|
+
</div>
|
|
107
|
+
<ChevronDown className={open ? '' : 'opacity-50'} />
|
|
108
|
+
</Button>
|
|
109
|
+
</PopoverTrigger>
|
|
110
|
+
<PopoverContent className={'p-0 ' + popoverClx}>
|
|
111
|
+
<Command>
|
|
112
|
+
<CommandInput placeholder={searchPlaceholder} />
|
|
113
|
+
<CommandList>
|
|
114
|
+
<CommandEmpty>No element found.</CommandEmpty>
|
|
115
|
+
<CommandGroup>
|
|
116
|
+
{elements.map((element) => (
|
|
117
|
+
<CommandItem
|
|
118
|
+
key={element.value}
|
|
119
|
+
value={element.value}
|
|
120
|
+
onSelect={handleSelect}
|
|
121
|
+
className='flex justify-between'
|
|
122
|
+
>
|
|
123
|
+
<div className='flex justify-start items-center gap-2'>
|
|
124
|
+
<ElementImage url={element.imageUrl} w={IMAGE_SIZE} h={IMAGE_SIZE} alt={element.value + ' image'}/>
|
|
125
|
+
<span>{ element.label ?? element.value }</span>
|
|
126
|
+
</div>
|
|
127
|
+
<div>
|
|
128
|
+
<Check className={cn('ml-auto', (isCurrent(element)) ? '' : 'invisible' )} />
|
|
129
|
+
</div>
|
|
130
|
+
</CommandItem>
|
|
131
|
+
))}
|
|
132
|
+
</CommandGroup>
|
|
133
|
+
</CommandList>
|
|
134
|
+
</Command>
|
|
135
|
+
</PopoverContent>
|
|
136
|
+
</Popover>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export {
|
|
141
|
+
type SelectElement,
|
|
142
|
+
Combobox as default
|
|
143
|
+
}
|
package/primitives/command.tsx
CHANGED
|
@@ -89,7 +89,7 @@ const CommandGroup = React.forwardRef<
|
|
|
89
89
|
<CommandPrimitive.Group
|
|
90
90
|
ref={ref}
|
|
91
91
|
className={cn(
|
|
92
|
-
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted
|
|
92
|
+
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted",
|
|
93
93
|
className
|
|
94
94
|
)}
|
|
95
95
|
{...props}
|
|
@@ -117,7 +117,9 @@ const CommandItem = React.forwardRef<
|
|
|
117
117
|
<CommandPrimitive.Item
|
|
118
118
|
ref={ref}
|
|
119
119
|
className={cn(
|
|
120
|
-
|
|
120
|
+
'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none',
|
|
121
|
+
'aria-selected:bg-level-3 aria-selected:text-accent',
|
|
122
|
+
'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
121
123
|
className
|
|
122
124
|
)}
|
|
123
125
|
{...props}
|
|
@@ -191,7 +191,6 @@ export {
|
|
|
191
191
|
|
|
192
192
|
export * from './tooltip'
|
|
193
193
|
|
|
194
|
-
|
|
195
194
|
export { default as ActionButton } from './action-button'
|
|
196
195
|
export { default as ApplyTypography, type TypographySize} from './apply-typography'
|
|
197
196
|
export { default as AspectRatio } from './aspect-ratio'
|
|
@@ -199,16 +198,11 @@ export { default as Badge } from './badge'
|
|
|
199
198
|
export { default as BreakpointIndicator } from './breakpoint-indicator'
|
|
200
199
|
export { default as Calendar } from './calendar'
|
|
201
200
|
export { default as Checkbox } from './checkbox'
|
|
201
|
+
export { default as Combobox, type SelectElement } from './combobox'
|
|
202
202
|
export { default as DialogVideoController } from './dialog-video-controller'
|
|
203
|
-
export { default as Image } from './image'
|
|
204
|
-
export { default as InlineIcon } from './inline-icon'
|
|
205
203
|
export { default as Input } from './input'
|
|
206
204
|
export { default as Label } from './label'
|
|
207
|
-
export { default as LinkElement } from './link-element'
|
|
208
205
|
export { default as ListBox } from './list-box'
|
|
209
|
-
export { default as MDXLink } from './mdx-link'
|
|
210
|
-
export { default as MediaStack } from './media-stack'
|
|
211
|
-
export { default as NavItems} from './nav-items'
|
|
212
206
|
export { default as Progress } from './progress'
|
|
213
207
|
export { RadioGroup, RadioGroupItem } from './radio-group'
|
|
214
208
|
export { ScrollArea, ScrollBar } from './scroll-area'
|
|
@@ -222,7 +216,6 @@ export { Toaster, toast } from './sonner'
|
|
|
222
216
|
export { Toggle, toggleVariants } from './toggle'
|
|
223
217
|
export { ToggleGroup, ToggleGroupItem } from './toggle-group'
|
|
224
218
|
export { default as VideoPlayer } from './video-player'
|
|
225
|
-
export { default as YouTubeEmbed } from './youtube-embed'
|
|
226
219
|
|
|
227
220
|
export * as Icons from './icons'
|
|
228
221
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import NextImage from 'next/image'
|
|
3
3
|
|
|
4
|
-
import type { ImageDef, Dimensions, MediaTransform } from '
|
|
5
|
-
import { constrain, cn, spreadToTransform } from '
|
|
4
|
+
import type { ImageDef, Dimensions, MediaTransform } from '../../types'
|
|
5
|
+
import { constrain, cn, spreadToTransform } from '../../util'
|
|
6
6
|
|
|
7
7
|
const Image: React.FC<{
|
|
8
8
|
def: ImageDef
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as Image } from './image'
|
|
2
|
+
export { default as InlineIcon } from './inline-icon'
|
|
3
|
+
export { default as LinkElement } from './link-element'
|
|
4
|
+
export { default as MDXLink } from './mdx-link'
|
|
5
|
+
export { default as MediaStack } from './media-stack'
|
|
6
|
+
export { default as NavItems } from './nav-items'
|
|
7
|
+
export { default as YouTubeEmbed } from './youtube-embed'
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React, { type PropsWithChildren } from 'react'
|
|
2
2
|
import Link from 'next/link'
|
|
3
3
|
|
|
4
|
-
import type { LinkDef, Icon } from '
|
|
5
|
-
import { buttonVariants } from '
|
|
6
|
-
import { cn, type VariantProps } from '
|
|
4
|
+
import type { LinkDef, Icon } from '../../types'
|
|
5
|
+
import { buttonVariants } from '../button'
|
|
6
|
+
import { cn, type VariantProps } from '../../util'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* If this is rendered directly (and not auto generated in a Block)
|
|
@@ -3,11 +3,11 @@ import React from 'react'
|
|
|
3
3
|
|
|
4
4
|
import Spline from '@splinetool/react-spline'
|
|
5
5
|
|
|
6
|
-
import { cn, constrain, spreadToTransform } from '
|
|
7
|
-
import type { MediaStackDef, Dimensions } from '
|
|
6
|
+
import { cn, constrain, spreadToTransform } from '../../util'
|
|
7
|
+
import type { MediaStackDef, Dimensions } from '../../types'
|
|
8
8
|
|
|
9
9
|
import Image from './image'
|
|
10
|
-
import VideoPlayer from '
|
|
10
|
+
import VideoPlayer from '../video-player'
|
|
11
11
|
|
|
12
12
|
const MediaStack: React.FC<{
|
|
13
13
|
media: MediaStackDef
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
|
-
import type { LinkDef } from '
|
|
3
|
+
import type { LinkDef } from '../../types'
|
|
4
4
|
import LinkElement from './link-element'
|
|
5
|
-
import { cn } from '
|
|
5
|
+
import { cn } from '../../util'
|
|
6
6
|
|
|
7
7
|
const NavItems: React.FC<{
|
|
8
8
|
items: LinkDef[]
|
|
File without changes
|