Skip to content

Commit a0bf47a

Browse files
Show image size on image picker; clean up empty metadata; small color fix (#1836)
* Show image size on image picker * Tweak selected metadata colours * Refactor JSX for metadata display --------- Co-authored-by: Benjamin Leonard <hello@benleonard.co.uk>
1 parent 6c9420a commit a0bf47a

File tree

3 files changed

+48
-14
lines changed

3 files changed

+48
-14
lines changed

app/components/form/fields/ImageSelectField.tsx

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { useController, type Control } from 'react-hook-form'
99

1010
import type { Image } from '@oxide/api'
1111
import type { ListboxItem } from '@oxide/ui'
12-
import { GiB } from '@oxide/util'
12+
import { bytesToGiB, GiB } from '@oxide/util'
1313

1414
import type { InstanceCreateInput } from 'app/forms/instance-create'
1515

@@ -41,22 +41,43 @@ export function ImageSelectField({ images, control }: ImageSelectFieldProps) {
4141
)
4242
}
4343

44-
const Slash = () => <span className="mx-0.5 text-quinary">/</span>
44+
const Slash = () => (
45+
<span className="mx-1 text-quinary selected:text-accent-disabled">/</span>
46+
)
4547

4648
export function toListboxItem(i: Image, includeProjectSiloIndicator = false): ListboxItem {
47-
const projectSiloIndicator = includeProjectSiloIndicator ? (
48-
<>
49-
<Slash /> {i.projectId ? 'Project image' : 'Silo image'}
50-
</>
51-
) : null
49+
const { name, os, projectId, size, version } = i
50+
const formattedSize = `${bytesToGiB(size, 1)} GiB`
51+
52+
// filter out any undefined metadata and create a comma-separated list
53+
// for the selected listbox item (shown in labelString)
54+
const condensedImageMetadata = [os, version, formattedSize].filter((i) => !!i).join(', ')
55+
const metadataForLabelString = condensedImageMetadata.length
56+
? ` (${condensedImageMetadata})`
57+
: ''
58+
59+
// for metadata showing in the dropdown's options, include the project / silo indicator if requested
60+
const projectSiloIndicator = includeProjectSiloIndicator
61+
? `${projectId ? 'Project' : 'Silo'} image`
62+
: null
63+
// filter out undefined metadata here, as well, and create a `<Slash />`-separated list
64+
// for the listbox item (shown for each item in the dropdown)
65+
const metadataForLabel = [os, version, formattedSize, projectSiloIndicator]
66+
.filter((i) => !!i)
67+
.map((i, index) => (
68+
<span key={`${i}`}>
69+
{index > 0 ? <Slash /> : ''}
70+
{i}
71+
</span>
72+
))
5273
return {
5374
value: i.id,
54-
labelString: `${i.name} (${i.os}, ${i.version})`,
75+
labelString: `${name}${metadataForLabelString}`,
5576
label: (
5677
<>
57-
<div>{i.name}</div>
58-
<div className="text-secondary">
59-
{i.os} <Slash /> {i.version} {projectSiloIndicator}
78+
<div>{name}</div>
79+
<div className="text-tertiary selected:text-accent-secondary">
80+
{metadataForLabel}
6081
</div>
6182
</>
6283
),

libs/util/math.spec.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,20 @@
77
*/
88
import { expect, it } from 'vitest'
99

10-
import { splitDecimal } from './math'
10+
import { GiB } from '.'
11+
import { round, splitDecimal } from './math'
12+
13+
it('rounds properly', () => {
14+
expect(round(123.456, 0)).toEqual(123)
15+
expect(round(123.456, 1)).toEqual(123.5)
16+
expect(round(123.456, 2)).toEqual(123.46)
17+
expect(round(123.456, 3)).toEqual(123.456)
18+
expect(round(123.456, 4)).toEqual(123.456) // trailing zeros are culled
19+
expect(round(1.9, 0)).toEqual(2)
20+
expect(round(1.9, 1)).toEqual(1.9)
21+
expect(round(5 / 2, 2)).toEqual(2.5) // math expressions are resolved
22+
expect(round(1879048192 / GiB, 2)).toEqual(1.75) // constants can be evaluated
23+
})
1124

1225
it.each([
1326
[1.23, ['1', '.23']],

libs/util/units.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ export const MiB = 1024 * KiB
1212
export const GiB = 1024 * MiB
1313
export const TiB = 1024 * GiB
1414

15-
export const bytesToGiB = (b: number) => round(b / GiB, 2)
16-
export const bytesToTiB = (b: number) => round(b / TiB, 2)
15+
export const bytesToGiB = (b: number, digits = 2) => round(b / GiB, digits)
16+
export const bytesToTiB = (b: number, digits = 2) => round(b / TiB, digits)

0 commit comments

Comments
 (0)