@@ -10,12 +10,12 @@ import { useController, type Control } from 'react-hook-form'
1010import type { Image } from '@oxide/api'
1111
1212import type { InstanceCreateInput } from '~/forms/instance-create'
13- import type { ListboxItem } from '~/ui/lib/Listbox '
13+ import type { ComboboxItem } from '~/ui/lib/Combobox '
1414import { Slash } from '~/ui/lib/Slash'
1515import { nearest10 } from '~/util/math'
1616import { bytesToGiB , GiB } from '~/util/units'
1717
18- import { ListboxField } from './ListboxField '
18+ import { ComboboxField } from './ComboboxField '
1919
2020type ImageSelectFieldProps = {
2121 images : Image [ ]
@@ -32,18 +32,22 @@ export function BootDiskImageSelectField({
3232} : ImageSelectFieldProps ) {
3333 const diskSizeField = useController ( { control, name : 'bootDiskSize' } ) . field
3434 return (
35- // This should be migrated to a `ComboboxField` (and with a `toComboboxItem`), once
36- // we have a combobox that supports more elaborate labels (beyond just strings).
37- < ListboxField
35+ < ComboboxField
3836 disabled = { disabled }
3937 control = { control }
4038 name = { name }
4139 label = "Image"
42- placeholder = "Select an image"
43- items = { images . map ( ( i ) => toListboxItem ( i ) ) }
40+ placeholder = {
41+ name === 'siloImageSource' ? 'Select a silo image' : 'Select a project image'
42+ }
43+ items = { images . map ( ( i ) => toImageComboboxItem ( i ) ) }
4444 required
4545 onChange = { ( id ) => {
46- const image = images . find ( ( i ) => i . id === id ) ! // if it's selected, it must be present
46+ const image = images . find ( ( i ) => i . id === id )
47+ // the most likely scenario where image would be undefined is if the user has
48+ // manually cleared the ComboboxField; they will need to pick a boot disk image
49+ // in order to submit the form, so we don't need to do anything here
50+ if ( ! image ) return
4751 const imageSizeGiB = image . size / GiB
4852 if ( diskSizeField . value < imageSizeGiB ) {
4953 diskSizeField . onChange ( nearest10 ( imageSizeGiB ) )
@@ -53,24 +57,18 @@ export function BootDiskImageSelectField({
5357 )
5458}
5559
56- export function toListboxItem ( i : Image , includeProjectSiloIndicator = false ) : ListboxItem {
57- const { name, os, projectId, size, version } = i
58- const formattedSize = `${ bytesToGiB ( size , 1 ) } GiB`
59-
60- // filter out any undefined metadata and create a comma-separated list
61- // for the selected listbox item (shown in selectedLabel)
62- const condensedImageMetadata = [ os , version , formattedSize ] . filter ( ( i ) => ! ! i ) . join ( ', ' )
63- const metadataForSelectedLabel = condensedImageMetadata . length
64- ? ` (${ condensedImageMetadata } )`
65- : ''
60+ export function toImageComboboxItem (
61+ image : Image ,
62+ includeProjectSiloIndicator = false
63+ ) : ComboboxItem {
64+ const { id, name, os, projectId, size, version } = image
6665
6766 // for metadata showing in the dropdown's options, include the project / silo indicator if requested
6867 const projectSiloIndicator = includeProjectSiloIndicator
6968 ? `${ projectId ? 'Project' : 'Silo' } image`
7069 : null
71- // filter out undefined metadata here, as well, and create a `<Slash />`-separated list
72- // for the listbox item (shown for each item in the dropdown)
73- const metadataForLabel = [ os , version , formattedSize , projectSiloIndicator ]
70+ // filter out undefined metadata and create a `<Slash />`-separated list for each comboboxitem
71+ const itemMetadata = [ os , version , `${ bytesToGiB ( size , 1 ) } GiB` , projectSiloIndicator ]
7472 . filter ( ( i ) => ! ! i )
7573 . map ( ( i , index ) => (
7674 < span key = { `${ i } ` } >
@@ -79,14 +77,12 @@ export function toListboxItem(i: Image, includeProjectSiloIndicator = false): Li
7977 </ span >
8078 ) )
8179 return {
82- value : i . id ,
83- selectedLabel : ` ${ name } ${ metadataForSelectedLabel } ` ,
80+ value : id ,
81+ selectedLabel : name ,
8482 label : (
8583 < >
8684 < div > { name } </ div >
87- < div className = "text-tertiary selected:text-accent-secondary" >
88- { metadataForLabel }
89- </ div >
85+ < div className = "text-tertiary selected:text-accent-secondary" > { itemMetadata } </ div >
9086 </ >
9187 ) ,
9288 }
0 commit comments