GitHub

Original file line numberDiff line numberDiff line change

@@ -204,9 +204,10 @@ import { useFuse } from '@vueuse/integrations/useFuse'

204204

import { useAppConfig } from '#imports'

205205

import { useLocale } from '../composables/useLocale'

206206

import { omit, get } from '../utils'

207-

import { tv } from '../utils/tv'

208207

import { highlight } from '../utils/fuse'

209208

import { pickLinkProps } from '../utils/link'

209+

import { getEstimateSize } from '../utils/virtualizer'

210+

import { tv } from '../utils/tv'

210211

import UIcon from './Icon.vue'

211212

import UAvatar from './Avatar.vue'

212213

import UButton from './Button.vue'

@@ -238,7 +239,13 @@ const appConfig = useAppConfig() as CommandPalette['AppConfig']

238239
239240

const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'disabled', 'multiple', 'modelValue', 'defaultValue', 'highlightOnHover'), emits)

240241

const inputProps = useForwardProps(reactivePick(props, 'loading'))

241-

const virtualizerProps = toRef(() => !!props.virtualize && defu(typeof props.virtualize === 'boolean' ? {} : props.virtualize, { estimateSize: 32 }))

242+

const virtualizerProps = toRef(() => {

243+

if (!props.virtualize) return false

244+
245+

return defu(typeof props.virtualize === 'boolean' ? {} : props.virtualize, {

246+

estimateSize: getEstimateSize(filteredItems.value, 'md', props.descriptionKey as string)

247+

})

248+

})

242249
243250

const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: CommandPaletteItem, group?: CommandPaletteGroup, index: number }>({

244251

props: {

Original file line numberDiff line numberDiff line change

@@ -202,6 +202,7 @@ import { useFormField } from '../composables/useFormField'

202202

import { useLocale } from '../composables/useLocale'

203203

import { usePortal } from '../composables/usePortal'

204204

import { compare, get, getDisplayValue, isArrayOfArray } from '../utils'

205+

import { getEstimateSize } from '../utils/virtualizer'

205206

import { tv } from '../utils/tv'

206207

import UIcon from './Icon.vue'

207208

import UAvatar from './Avatar.vue'

@@ -232,15 +233,13 @@ const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', '

232233

const portalProps = usePortal(toRef(() => props.portal))

233234

const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }) as ComboboxContentProps)

234235

const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)

235-

const virtualizerProps = toRef(() => !!props.virtualize && defu(typeof props.virtualize === 'boolean' ? {} : props.virtualize, {

236-

estimateSize: ({

237-

xs: 24,

238-

sm: 28,

239-

md: 32,

240-

lg: 36,

241-

xl: 40

242-

})[props.size || 'md']

243-

}))

236+

const virtualizerProps = toRef(() => {

237+

if (!props.virtualize) return false

238+
239+

return defu(typeof props.virtualize === 'boolean' ? {} : props.virtualize, {

240+

estimateSize: getEstimateSize(items.value, inputSize.value || 'md', props.descriptionKey as string)

241+

})

242+

})

244243
245244

const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField<InputProps>(props)

246245

const { orientation, size: fieldGroupSize } = useFieldGroup<InputProps>(props)

Original file line numberDiff line numberDiff line change

@@ -194,6 +194,7 @@ import { useFormField } from '../composables/useFormField'

194194

import { useLocale } from '../composables/useLocale'

195195

import { usePortal } from '../composables/usePortal'

196196

import { compare, get, getDisplayValue, isArrayOfArray } from '../utils'

197+

import { getEstimateSize } from '../utils/virtualizer'

197198

import { tv } from '../utils/tv'

198199

import UIcon from './Icon.vue'

199200

import UAvatar from './Avatar.vue'

@@ -225,15 +226,13 @@ const rootProps = useForwardPropsEmits(reactivePick(props, 'modelValue', 'defaul

225226

const portalProps = usePortal(toRef(() => props.portal))

226227

const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }) as ComboboxContentProps)

227228

const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)

228-

const virtualizerProps = toRef(() => !!props.virtualize && defu(typeof props.virtualize === 'boolean' ? {} : props.virtualize, {

229-

estimateSize: ({

230-

xs: 24,

231-

sm: 28,

232-

md: 32,

233-

lg: 36,

234-

xl: 40

235-

})[props.size || 'md']

236-

}))

229+

const virtualizerProps = toRef(() => {

230+

if (!props.virtualize) return false

231+
232+

return defu(typeof props.virtualize === 'boolean' ? {} : props.virtualize, {

233+

estimateSize: getEstimateSize(items.value, props.size || 'md', props.descriptionKey as string)

234+

})

235+

})

237236

const searchInputProps = toRef(() => defu(props.searchInput, { placeholder: t('selectMenu.search'), variant: 'none' }) as InputProps)

238237
239238

const { emitFormBlur, emitFormFocus, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField<InputProps>(props)

Original file line numberDiff line numberDiff line change

@@ -148,6 +148,7 @@ import { reactivePick, createReusableTemplate } from '@vueuse/core'

148148

import { defu } from 'defu'

149149

import { useAppConfig } from '#imports'

150150

import { get } from '../utils'

151+

import { getEstimateSize } from '../utils/virtualizer'

151152

import { tv } from '../utils/tv'

152153

import UIcon from './Icon.vue'

153154

@@ -187,15 +188,13 @@ const flattenedPaddingFormula = computed(() => {

187188

return (level: number) => `calc(var(--spacing) * ${(level - 1) * config.perLevel + config.base})`

188189

})

189190
190-

const virtualizerProps = toRef(() => !!props.virtualize && defu(typeof props.virtualize === 'boolean' ? {} : props.virtualize, {

191-

estimateSize: ({

192-

xs: 24,

193-

sm: 28,

194-

md: 32,

195-

lg: 36,

196-

xl: 40

197-

})[props.size || 'md']

198-

}))

191+

const virtualizerProps = toRef(() => {

192+

if (!props.virtualize) return false

193+
194+

return defu(typeof props.virtualize === 'boolean' ? {} : props.virtualize, {

195+

estimateSize: getEstimateSize(props.items || [], props.size || 'md')

196+

})

197+

})

199198
200199

const [DefineTreeTemplate, ReuseTreeTemplate] = createReusableTemplate<{ items?: TreeItem[], level: number }, TreeSlots<T>>()

201200

const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: TreeItem, index: number, level: number }, TreeSlots<T>>({

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,47 @@

1+

import { get } from './index'

2+
3+

function hasDescription(item: any, descriptionKey: string): boolean {

4+

if (typeof item !== 'object' || item === null) {

5+

return false

6+

}

7+

const value = get(item, descriptionKey)

8+

return value !== undefined && value !== null && value !== ''

9+

}

10+
11+

function getSize(size: 'xs' | 'sm' | 'md' | 'lg' | 'xl', hasDescription: boolean): number {

12+

if (hasDescription) {

13+

return ({

14+

xs: 44,

15+

sm: 48,

16+

md: 52,

17+

lg: 56,

18+

xl: 60

19+

})[size]

20+

}

21+
22+

return ({

23+

xs: 24,

24+

sm: 28,

25+

md: 32,

26+

lg: 36,

27+

xl: 40

28+

})[size]

29+

}

30+
31+

/**

32+

* Get estimate size for virtualizers that checks each item individually

33+

* NOTE: This requires Reka UI to support functions for estimateSize (https://github.com/unovue/reka-ui/pull/2288)

34+

* Until then, we check if ANY item has a description and use that for all items

35+

*/

36+

export function getEstimateSize(items: any[], size: 'xs' | 'sm' | 'md' | 'lg' | 'xl', descriptionKey?: string): number {

37+

// TODO: Once Reka UI supports functions, uncomment and change return type to: number | ((index: number) => number)

38+

// return (index: number) => {

39+

// const item = items[index]

40+

// const hasDescription = descriptionKey ? hasDescription(item, descriptionKey) : false

41+

// return getSize(size, hasDescription)

42+

// }

43+
44+

// For now, check if ANY item has a description and use a static size

45+

const anyHasDescription = descriptionKey ? items.some(item => hasDescription(item, descriptionKey)) : false

46+

return getSize(size, anyHasDescription)

47+

}

Read the original on github.com ↗