@hanzo/ui 4.0.5 → 4.1.1

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.
Files changed (29) hide show
  1. package/blocks/components/accordian-block.tsx +1 -1
  2. package/blocks/components/bullet-cards-block.tsx +1 -1
  3. package/blocks/components/card-block/index.tsx +1 -1
  4. package/blocks/components/card-block/link-out-button.tsx +1 -1
  5. package/blocks/components/card-block/util.ts +1 -1
  6. package/blocks/components/carte-blanche-block/index.tsx +1 -1
  7. package/blocks/components/carte-blanche-block/variant-content-left.tsx +1 -1
  8. package/blocks/components/cta-block.tsx +4 -6
  9. package/blocks/components/enh-heading-block.tsx +2 -2
  10. package/blocks/components/heading-block.tsx +1 -1
  11. package/blocks/components/screenful-block/index.tsx +1 -1
  12. package/blocks/components/space-block.tsx +1 -1
  13. package/blocks/components/video-block.tsx +1 -1
  14. package/blocks/def/cta-block.ts +7 -13
  15. package/package.json +4 -3
  16. package/primitives/action-button.tsx +0 -7
  17. package/primitives/combobox.tsx +158 -0
  18. package/primitives/command.tsx +4 -2
  19. package/primitives/{index.ts → index-common.ts} +2 -8
  20. package/primitives/index-next.ts +2 -0
  21. package/primitives/list-adaptor.ts +11 -0
  22. package/primitives/{image.tsx → next/image.tsx} +2 -2
  23. package/primitives/next/index.ts +7 -0
  24. package/primitives/{inline-icon.tsx → next/inline-icon.tsx} +1 -1
  25. package/primitives/{link-element.tsx → next/link-element.tsx} +3 -3
  26. package/primitives/{media-stack.tsx → next/media-stack.tsx} +3 -3
  27. package/primitives/{nav-items.tsx → next/nav-items.tsx} +2 -2
  28. package/primitives/{youtube-embed.tsx → next/youtube-embed.tsx} +1 -1
  29. /package/primitives/{mdx-link.tsx → next/mdx-link.tsx} +0 -0
@@ -6,7 +6,7 @@ import {
6
6
  AccordionItem,
7
7
  AccordionTrigger,
8
8
  ApplyTypography
9
- } from '../../primitives'
9
+ } from '../../primitives/index-common'
10
10
 
11
11
  import { cn } from '../../util'
12
12
 
@@ -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'
@@ -11,7 +11,7 @@ import {
11
11
  CardHeader,
12
12
  CardTitle,
13
13
  type TypographySize
14
- } from '../../../primitives'
14
+ } from '../../../primitives/index-common'
15
15
 
16
16
  import type { CardBlock, VideoBlock } from '../../def'
17
17
 
@@ -1,7 +1,7 @@
1
1
  import React from 'react'
2
2
 
3
3
  import type { LinkDef } from '../../../types'
4
- import { LinkElement, Icons } from '../../../primitives'
4
+ import { LinkElement, Icons } from '../../../primitives/index-next'
5
5
 
6
6
  const ArrowLinkElement: React.FC<{
7
7
  def: LinkDef,
@@ -1,5 +1,5 @@
1
1
  import type { Dimensions } from '../../../types'
2
- import type {TypographySize } from '../../../primitives'
2
+ import type {TypographySize } from '../../../primitives/index-common'
3
3
  import {
4
4
  getSpecifierData,
5
5
  getPrimaryStartingWith,
@@ -5,7 +5,7 @@ import {
5
5
  CardContent,
6
6
  CardFooter,
7
7
  CardHeader,
8
- } from '../../../primitives'
8
+ } from '../../../primitives/index-common'
9
9
 
10
10
  import { cn, containsToken } from '../../../util'
11
11
 
@@ -3,7 +3,7 @@ import {
3
3
  CardContent,
4
4
  CardFooter,
5
5
  CardHeader,
6
- } from '../../../primitives'
6
+ } from '../../../primitives/index-common'
7
7
  import { cn } from '../../../util'
8
8
  import Content from '../content'
9
9
 
@@ -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: {
@@ -1,7 +1,7 @@
1
1
  import React from 'react'
2
2
 
3
3
  import type { HeadingBlock } from '../def'
4
- import { ApplyTypography } from '../../primitives'
4
+ import { ApplyTypography } from '../../primitives/index-common'
5
5
 
6
6
  import type BlockComponentProps from './block-component-props'
7
7
 
@@ -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
 
@@ -3,19 +3,13 @@ import type Block from './block'
3
3
 
4
4
  interface CTABlock extends Block {
5
5
  blockType: 'cta'
6
-
7
- /**
8
- * fill: fills the parent width with the elements
9
- left / right: (>= md) left or right justify the elements (default is center)
10
- mobile-2-columns: mobile defaults to rendering each element full width,
11
- on it's own line. This renders them in two columns instead.
12
- mobile-center-first-if-odd: if (mobile-2-columns) and length is odd,
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.5",
3
+ "version": "4.1.1",
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",
@@ -43,7 +44,7 @@
43
44
  "./util-client": "./util/index-client.ts"
44
45
  },
45
46
  "dependencies": {
46
- "@hanzo/react-drawer": "0.1.7",
47
+ "@hanzo/react-drawer": "^0.1.8",
47
48
  "@next/third-parties": "^15.0.1",
48
49
  "@radix-ui/react-accordion": "^1.1.2",
49
50
  "@radix-ui/react-aspect-ratio": "^1.0.3",
@@ -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,158 @@
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
+ import type ListAdaptor from './list-adaptor'
23
+
24
+ const ElementImage: React.FC<{
25
+ url: string | undefined
26
+ alt?: string
27
+ w: number
28
+ h: number
29
+ className?: string
30
+ }> = ({
31
+ url,
32
+ alt,
33
+ w,
34
+ h,
35
+ className=''
36
+ }) => (url ? (
37
+ <img
38
+ src={url}
39
+ alt={alt ?? 'image'}
40
+ height={h}
41
+ width={w}
42
+ loading="eager"
43
+ className={className}
44
+ />
45
+ ) : null
46
+ )
47
+ // "rounded-sm object-contain"
48
+
49
+ const Combobox = <T,>(
50
+ {
51
+ elements,
52
+ adaptor,
53
+ buttonClx='',
54
+ popoverClx='',
55
+ imageClx='',
56
+ initial,
57
+ searchPlaceholder='Search...',
58
+ buttonPlaceholder='Select...',
59
+ noneFoundMessage='None found.',
60
+ elementSelected,
61
+ disabled=false,
62
+ imageSize=32
63
+ }: {
64
+ elements: T[]
65
+ adaptor: ListAdaptor<T>
66
+ elementSelected: (e: T) => void
67
+ buttonClx?: string
68
+ popoverClx?: string
69
+ imageClx?: string
70
+ buttonPlaceholder?: string
71
+ searchPlaceholder?: string
72
+ noneFoundMessage?: string
73
+ initial?: T,
74
+ disabled?: boolean
75
+ imageSize?: number
76
+ }) => {
77
+
78
+ const [open, setOpen] = useState<boolean>(false)
79
+ const [current, setCurrent] = useState<T | null>(initial ?? null)
80
+
81
+ const handleSelect = (selString: string) => {
82
+
83
+ const found = elements.find((el: T) => (adaptor.valueEquals(el, selString)))
84
+ if (found) {
85
+ setCurrent(found)
86
+ elementSelected(found)
87
+ }
88
+ setOpen(false)
89
+ }
90
+
91
+ const isCurrent = (el: T): boolean => (!!current && (adaptor.equals(el, current)))
92
+
93
+ let currentValue: string | undefined
94
+ let currentLabel: string | undefined
95
+ let currentImageUrl: string | undefined
96
+
97
+ if (current) {
98
+ currentValue = adaptor.getValue(current)
99
+ currentLabel = adaptor.getLabel ? adaptor.getLabel(current) : undefined
100
+ currentImageUrl = adaptor.getImageUrl ? adaptor.getImageUrl(current) : undefined
101
+ }
102
+
103
+ return (
104
+ <Popover open={open} onOpenChange={setOpen}>
105
+ <PopoverTrigger asChild>
106
+ <Button
107
+ variant='outline'
108
+ role='combobox'
109
+ aria-expanded={open}
110
+ className={'flex justify-between ' + buttonClx}
111
+ disabled={disabled}
112
+ >
113
+ <div className='flex justify-start items-center gap-2'>
114
+ {current && (
115
+ <ElementImage url={currentImageUrl} w={imageSize} h={imageSize} className={imageClx} alt={currentValue + ' image'}/>
116
+ )}
117
+ <span>{ current ? (currentLabel ?? currentValue) : buttonPlaceholder }</span>
118
+ </div>
119
+ <ChevronDown className={open ? '' : 'opacity-50'} />
120
+ </Button>
121
+ </PopoverTrigger>
122
+ <PopoverContent className={'p-0 ' + popoverClx}>
123
+ <Command>
124
+ <CommandInput placeholder={searchPlaceholder} />
125
+ <CommandList>
126
+ <CommandEmpty>{noneFoundMessage}</CommandEmpty>
127
+ <CommandGroup>
128
+ {elements.map((el) => (
129
+ <CommandItem
130
+ key={adaptor.getValue(el)}
131
+ value={adaptor.getValue(el)}
132
+ onSelect={handleSelect}
133
+ className='flex justify-between'
134
+ >
135
+ <div className='flex justify-start items-center gap-2'>
136
+ <ElementImage
137
+ url={adaptor.getImageUrl ? adaptor.getImageUrl(el) : undefined}
138
+ w={imageSize}
139
+ h={imageSize}
140
+ className={imageClx}
141
+ alt={adaptor.getValue(el) + ' image'}
142
+ />
143
+ <span>{ adaptor.getLabel ? adaptor.getLabel(el) : adaptor.getValue(el) }</span>
144
+ </div>
145
+ <div>
146
+ <Check className={cn('ml-auto', (isCurrent(el)) ? '' : 'invisible' )} />
147
+ </div>
148
+ </CommandItem>
149
+ ))}
150
+ </CommandGroup>
151
+ </CommandList>
152
+ </Command>
153
+ </PopoverContent>
154
+ </Popover>
155
+ )
156
+ }
157
+
158
+ export default Combobox
@@ -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-foreground",
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
- "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
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,12 @@ 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 } 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'
205
+ export type { default as ListAdaptor } from './list-adaptor'
208
206
  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
207
  export { default as Progress } from './progress'
213
208
  export { RadioGroup, RadioGroupItem } from './radio-group'
214
209
  export { ScrollArea, ScrollBar } from './scroll-area'
@@ -222,7 +217,6 @@ export { Toaster, toast } from './sonner'
222
217
  export { Toggle, toggleVariants } from './toggle'
223
218
  export { ToggleGroup, ToggleGroupItem } from './toggle-group'
224
219
  export { default as VideoPlayer } from './video-player'
225
- export { default as YouTubeEmbed } from './youtube-embed'
226
220
 
227
221
  export * as Icons from './icons'
228
222
 
@@ -0,0 +1,2 @@
1
+ export * from './index-common'
2
+ export * from './next'
@@ -0,0 +1,11 @@
1
+ interface ListAdaptor<T> {
2
+ getValue: (el: T) => string
3
+ equals: (el1: T, el2: T) => boolean
4
+ valueEquals: (el: T, v: string) => boolean
5
+ getLabel?: (el: T) => string
6
+ getImageUrl?: (el: T) => string
7
+ }
8
+
9
+ export {
10
+ type ListAdaptor as default
11
+ }
@@ -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 '../types'
5
- import { constrain, cn, spreadToTransform } from '../util'
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,7 +1,7 @@
1
1
  import React from 'react'
2
2
  import Image from 'next/image'
3
3
 
4
- import type { Icon } from '../types'
4
+ import type { Icon } from '../../types'
5
5
 
6
6
  const InlineIcon: React.FC<{
7
7
  /**
@@ -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 '../types'
5
- import { buttonVariants } from './button'
6
- import { cn, type VariantProps } from '../util'
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 '../util'
7
- import type { MediaStackDef, Dimensions } from '../types'
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 './video-player'
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 '../types'
3
+ import type { LinkDef } from '../../types'
4
4
  import LinkElement from './link-element'
5
- import { cn } from '../util'
5
+ import { cn } from '../../util'
6
6
 
7
7
  const NavItems: React.FC<{
8
8
  items: LinkDef[]
@@ -3,7 +3,7 @@
3
3
  import { useState } from 'react'
4
4
  import Image from 'next/image'
5
5
 
6
- import * as Icons from './icons'
6
+ import * as Icons from '../icons'
7
7
 
8
8
  // Concept based on https://www.youtube.com/watch?v=lLqRchtjN00
9
9
  // (https://github.com/a-trost/fableton)
File without changes