Skip to content

Commit

Permalink
feat: format bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
jrasm91 committed Dec 20, 2024
1 parent 4db3f89 commit 2286ed7
Show file tree
Hide file tree
Showing 87 changed files with 235 additions and 116 deletions.
15 changes: 6 additions & 9 deletions src/docs/components/ComponentCard.svelte
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
<script lang="ts">
import { asComponentHref } from '$docs/utilities.js';
import { Button, Heading, Icon, VStack } from '@immich/ui';
type Props = {
title: string;
icon: string;
component: { name: string; icon: string };
};
const { title, icon }: Props = $props();
const asSlug = (value: string) =>
value.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`).replace(/^-/, '');
const { component }: Props = $props();
</script>

<Button href="/examples/{asSlug(title)}" variant="outline" color="primary" class="h-24 w-40">
<Button href={asComponentHref(component.name)} variant="outline" color="primary" class="h-24 w-40">
<VStack gap={2} class="p-6">
<Icon {icon} size="2em" />
<Heading size="tiny">{title}</Heading>
<Icon icon={component.icon} size="2em" />
<Heading size="tiny">{component.name}</Heading>
</VStack>
</Button>
2 changes: 2 additions & 0 deletions src/docs/components/ExampleLayout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
<div class="flex items-center gap-2">
<a href="/" class="underline">Home</a>
<span>/</span>
<a href="/" class="underline">Components</a>
<span>/</span>
<span class="capitalize">{name}</span>
</div>
</nav>
Expand Down
60 changes: 60 additions & 0 deletions src/docs/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
import {
mdiAlertCircleOutline,
mdiApplicationOutline,
mdiButtonCursor,
mdiCardOutline,
mdiCheckboxMarked,
mdiCloseCircle,
mdiCreditCardOutline,
mdiFormatHeaderPound,
mdiFormTextbox,
mdiFormTextboxPassword,
mdiHomeCircle,
mdiImage,
mdiLink,
mdiListBoxOutline,
mdiNumeric,
mdiPartyPopper,
mdiViewSequential,
} from '@mdi/js';
import type { Component } from 'svelte';

export enum DisplayOption {
Expand All @@ -18,3 +37,44 @@ export type ExampleItem = {
};

export type ExampleCardProps = ExampleItem & { theme: Theme };

export const componentGroups = [
{
name: 'Layout',
components: [
{ name: 'Alert', icon: mdiAlertCircleOutline },
{ name: 'AppShell', icon: mdiApplicationOutline },
{ name: 'Card', icon: mdiCardOutline },
{ name: 'Stack', icon: mdiViewSequential },
],
},
{
name: 'Forms',
components: [
{ name: 'Button', icon: mdiButtonCursor },
{ name: 'IconButton', icon: mdiHomeCircle },
{ name: 'Checkbox', icon: mdiCheckboxMarked },
{ name: 'CloseButton', icon: mdiCloseCircle },
{ name: 'Field', icon: mdiListBoxOutline },
{ name: 'Input', icon: mdiFormTextbox },
{ name: 'LoadingSpinner', icon: mdiCreditCardOutline },
{ name: 'PasswordInput', icon: mdiFormTextboxPassword },
],
},
{
name: 'Text',
components: [
{ name: 'Text', icon: mdiFormatHeaderPound },
{ name: 'Heading', icon: mdiFormTextbox },
{ name: 'Link', icon: mdiLink },
{ name: 'FormatBytes', icon: mdiNumeric },
],
},
{
name: 'Immich',
components: [
{ name: 'Logo', icon: mdiImage },
{ name: 'SupporterBadge', icon: mdiPartyPopper },
],
},
];
4 changes: 4 additions & 0 deletions src/docs/utilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const asSlug = (value: string) =>
value.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`).replace(/^-/, '');

export const asComponentHref = (componentName: string) => `/components/${asSlug(componentName)}`;
16 changes: 16 additions & 0 deletions src/lib/components/FormatBytes/FormatBytes.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script lang="ts">
import { getBytesWithUnit } from '$lib/utilities/byte-units.js';
type Props = {
bytes: number;
precision?: number;
variant?: 'short' | 'narrow';
};
const { bytes, precision, variant = 'short' }: Props = $props();
const [value, unit] = $derived(getBytesWithUnit(bytes, precision));
const separator = $derived(variant === 'narrow' ? '' : ' ');
</script>

<span>{value}{separator}{unit}</span>
2 changes: 2 additions & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export { default as HelperText } from '$lib/components/Form/HelperText.svelte';
export { default as Input } from '$lib/components/Form/Input.svelte';
export { default as Label } from '$lib/components/Form/Label.svelte';
export { default as PasswordInput } from '$lib/components/Form/PasswordInput.svelte';
export { default as FormatBytes } from '$lib/components/FormatBytes/FormatBytes.svelte';
export { default as Heading } from '$lib/components/Heading/Heading.svelte';
export { default as Icon } from '$lib/components/Icon/Icon.svelte';
export { default as IconButton } from '$lib/components/IconButton/IconButton.svelte';
Expand All @@ -29,3 +30,4 @@ export { default as VStack } from '$lib/components/Stack/VStack.svelte';
export { default as SupporterBadge } from '$lib/components/SupporterBadge/SupporterBadge.svelte';
export { default as Text } from '$lib/components/Text/Text.svelte';
export * from '$lib/types.js';
export * from '$lib/utilities/byte-units.js';
80 changes: 80 additions & 0 deletions src/lib/utilities/byte-units.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
export const enum ByteUnit {
'B' = 'B',
'KiB' = 'KiB',
'MiB' = 'MiB',
'GiB' = 'GiB',
'TiB' = 'TiB',
'PiB' = 'PiB',
'EiB' = 'EiB',
}

const byteUnits = [
ByteUnit.B,
ByteUnit.KiB,
ByteUnit.MiB,
ByteUnit.GiB,
ByteUnit.TiB,
ByteUnit.PiB,
ByteUnit.EiB,
];

/**
* Convert bytes to best human readable unit and number of that unit.
*
* * For `1024` bytes, returns `1` and `KiB`.
* * For `1536` bytes, returns `1.5` and `KiB`.
*
* @param bytes number of bytes
* @param maxPrecision maximum number of decimal places, default is `1`
* @returns size (number) and unit (string)
*/
export function getBytesWithUnit(bytes: number, maxPrecision = 1): [number, ByteUnit] {
const magnitude = Math.floor(Math.log(bytes === 0 ? 1 : bytes) / Math.log(1024));

return [
Number.parseFloat((bytes / 1024 ** magnitude).toFixed(maxPrecision)),
byteUnits[magnitude],
];
}

/**
* Localized number of bytes with a unit.
*
* For `1536` bytes:
* * en: `1.5 KiB`
* * de: `1,5 KiB`
*
* @param bytes number of bytes
* @param maxPrecision maximum number of decimal places, default is `1`
* @returns localized bytes with unit as string
*/
export function getByteUnitString(bytes: number, locale?: string, maxPrecision = 1): string {
const [size, unit] = getBytesWithUnit(bytes, maxPrecision);
return `${size.toLocaleString(locale)} ${unit}`;
}

/**
* Convert to bytes from on a specified unit.
*
* * `1, 'GiB'`, returns `1073741824` bytes
*
* @param size value to be converted
* @param unit unit to convert from
* @returns bytes (number)
*/
export function convertToBytes(size: number, unit: ByteUnit): number {
return size * 1024 ** byteUnits.indexOf(unit);
}

/**
* Convert from bytes to a specified unit.
*
* * `11073741824, 'GiB'`, returns `1` GiB
*
* @param bytes value to be converted
* @param unit unit to convert to
* @returns bytes (number)
*/
export function convertFromBytes(bytes: number, unit: ByteUnit): number {
return bytes / 1024 ** byteUnits.indexOf(unit);
}
52 changes: 19 additions & 33 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
<script lang="ts">
import Navbar from '$docs/components/Navbar.svelte';
import { Theme } from '$docs/constants.js';
import AppShellHeader from '$lib/components/AppShell/AppShellHeader.svelte';
import { componentGroups, Theme } from '$docs/constants.js';
import { asComponentHref } from '$docs/utilities.js';
import { theme } from '$lib/services/theme.svelte.js';
import { AppShell, AppShellSidebar, Heading, IconButton, Link, Stack } from '@immich/ui';
import {
AppShell,
AppShellHeader,
AppShellSidebar,
Heading,
IconButton,
Link,
Stack,
} from '@immich/ui';
import { mdiWeatherNight, mdiWeatherSunny } from '@mdi/js';
import '../app.css';
let { children } = $props();
Expand All @@ -30,36 +38,14 @@

<AppShellSidebar class="p-4">
<Stack class="min-w-[200px]">
<Heading size="tiny">Layout</Heading>
<Stack class="pl-4">
<Link href="/examples/app-shell">AppShell</Link>
<Link href="/examples/alert">Alert</Link>
<Link href="/examples/card">Card</Link>
<Link href="/examples/stack">Stack</Link>
</Stack>
<Heading size="tiny">Forms</Heading>
<Stack class="pl-4">
<Link href="/examples/button">Button</Link>
<Link href="/examples/icon-button">IconButton</Link>
<Link href="/examples/checkbox">Checkbox</Link>
<Link href="/examples/close-button">CloseButton</Link>
<Link href="/examples/field">Field</Link>
<Link href="/examples/input">Input</Link>
<Link href="/examples/loading-spinner">LoadingSpinner</Link>
<Link href="/examples/password-input">PasswordInput</Link>
</Stack>
<Heading size="tiny">Text</Heading>
<Stack class="pl-4">
<Link href="/examples/heading">Heading</Link>
<Link href="/examples/text">Text</Link>
<Link href="/examples/link">Link</Link>
</Stack>

<Heading size="tiny">Immich</Heading>
<Stack class="pl-4">
<Link href="/examples/logo">Logo</Link>
<Link href="/examples/supporter-badge">SupporterBadge</Link>
</Stack>
{#each componentGroups as group}
<Heading size="tiny">{group.name}</Heading>
<Stack class="pl-4">
{#each group.components as component}
<Link href={asComponentHref(component.name)}>{component.name}</Link>
{/each}
</Stack>
{/each}
</Stack>
</AppShellSidebar>

Expand Down
72 changes: 11 additions & 61 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,26 +1,8 @@
<script lang="ts">
import ComponentCard from '$docs/components/ComponentCard.svelte';
import Grid from '$docs/components/Grid.svelte';
import { componentGroups } from '$docs/constants.js';
import { Heading, Link, Stack, Text } from '@immich/ui';
import {
mdiAlertCircleOutline,
mdiApplicationOutline,
mdiButtonCursor,
mdiCardOutline,
mdiCheckboxMarked,
mdiCircleOutline,
mdiCloseCircle,
mdiFormatHeaderPound,
mdiFormatText,
mdiFormTextbox,
mdiFormTextboxPassword,
mdiHomeCircle,
mdiImage,
mdiLink,
mdiListBoxOutline,
mdiPartyPopper,
mdiViewSequential,
} from '@mdi/js';
</script>

<div class="max-w-screen-lg p-2">
Expand All @@ -41,48 +23,16 @@

<Heading size="large">Components</Heading>
<Stack gap={8}>
<Stack>
<Heading size="medium">Layout</Heading>
<Grid>
<ComponentCard icon={mdiAlertCircleOutline} title="Alert" />
<ComponentCard icon={mdiApplicationOutline} title="AppShell" />
<ComponentCard icon={mdiCardOutline} title="Card" />
<ComponentCard icon={mdiViewSequential} title="Stack" />
</Grid>
</Stack>

<Stack>
<Heading size="medium">Forms</Heading>

<Grid>
<ComponentCard icon={mdiButtonCursor} title="Button" />
<ComponentCard icon={mdiHomeCircle} title="IconButton" />
<ComponentCard icon={mdiCheckboxMarked} title="Checkbox" />
<ComponentCard icon={mdiCloseCircle} title="CloseButton" />
<ComponentCard icon={mdiListBoxOutline} title="Field" />
<ComponentCard icon={mdiFormTextbox} title="Input" />
<ComponentCard icon={mdiCircleOutline} title="LoadingSpinner" />
<ComponentCard icon={mdiFormTextboxPassword} title="PasswordInput" />
</Grid>
</Stack>

<Stack>
<Heading size="medium">Text</Heading>

<Grid>
<ComponentCard icon={mdiFormatHeaderPound} title="Heading" />
<ComponentCard icon={mdiFormatText} title="Text" />
<ComponentCard icon={mdiLink} title="Link" />
</Grid>
</Stack>

<Stack>
<Heading size="medium">Immich</Heading>
<Grid>
<ComponentCard icon={mdiImage} title="Logo" />
<ComponentCard icon={mdiPartyPopper} title="SupporterBadge" />
</Grid>
</Stack>
{#each componentGroups as group}
<Stack>
<Heading size="medium">{group.name}</Heading>
<Grid>
{#each group.components as component}
<ComponentCard {component} />
{/each}
</Grid>
</Stack>
{/each}
</Stack>
</Stack>
</div>
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script lang="ts">
import DecorativeBlock from '$docs/components/DecorativeBlock.svelte';
import Stack from '$lib/components/Stack/Stack.svelte';
import { AppShell, AppShellHeader, AppShellSidebar, Heading } from '@immich/ui';
import { AppShell, AppShellHeader, AppShellSidebar, Heading, Stack } from '@immich/ui';
</script>

<Stack>
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
<script lang="ts">
import Lorem from '$docs/components/Lorem.svelte';
import CardBody from '$lib/components/Card/CardBody.svelte';
import { Card, CardDescription, CardFooter, CardHeader, CardTitle, Stack } from '@immich/ui';
import {
Card,
CardBody,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
Stack,
} from '@immich/ui';
</script>

<Stack>
Expand Down
File renamed without changes.
Loading

0 comments on commit 2286ed7

Please sign in to comment.