Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: switch #49

Merged
merged 1 commit into from
Dec 23, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/docs/components/ExampleLayout.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
<script lang="ts">
import Examples from '$docs/components/Examples.svelte';
import { Theme, type ExampleItem } from '$docs/constants.js';
import { Scrollable, Stack } from '@immich/ui';
import { Heading, Scrollable, Stack } from '@immich/ui';
import type { Snippet } from 'svelte';

type Props = {
name: string;
examples: ExampleItem[];
children?: Snippet;
};

const { name, examples }: Props = $props();
const { name, examples, children }: Props = $props();
</script>

<div class="flex h-full flex-col">
@@ -25,8 +27,10 @@
</div>
</nav>

<Scrollable>
<Stack gap={4} class="max-w-screen-lg p-4">
<Scrollable class="p-4">
<Heading size="large">{name}</Heading>
{@render children?.()}
<Stack gap={4} class="mt-4 max-w-screen-md">
<Examples theme={Theme.Dark} {examples} />
</Stack>
</Scrollable>
21 changes: 14 additions & 7 deletions src/docs/components/Lorem.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
<script lang="ts">
type Props = { count?: number };
const { count = 1 }: Props = $props();

const lorem =
'Lorem ipsum dolor sit amet consectetur adipisicing elit. A ipsam tenetur accusantium impedit beatae omnis necessitatibus. Voluptatum blanditiis libero impedit, harum eius inventore nihil, officia voluptate dolorum error consequatur animi.';

const make = (value: number) => {
let content = '';
for (let i = 0; i < value; i++) {
content += lorem;
}
return content;
};

const text = $derived(make(count));
</script>

{#each Array(count) as i}
<p data-index={i}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. A ipsam tenetur accusantium impedit
beatae omnis necessitatibus. Voluptatum blanditiis libero impedit, harum eius inventore nihil,
officia voluptate dolorum error consequatur animi.
</p>
{/each}
{text}
4 changes: 4 additions & 0 deletions src/docs/constants.ts
Original file line number Diff line number Diff line change
@@ -15,7 +15,9 @@ import {
mdiListBoxOutline,
mdiMenu,
mdiNumeric,
mdiPanVertical,
mdiPartyPopper,
mdiToggleSwitch,
mdiViewSequential,
} from '@mdi/js';
import type { Component } from 'svelte';
@@ -47,6 +49,7 @@ export const componentGroups = [
{ name: 'AppShell', icon: mdiApplicationOutline },
{ name: 'Card', icon: mdiCardOutline },
{ name: 'Navbar', icon: mdiMenu },
{ name: 'Scrollable', icon: mdiPanVertical },
{ name: 'Stack', icon: mdiViewSequential },
],
},
@@ -61,6 +64,7 @@ export const componentGroups = [
{ name: 'Input', icon: mdiFormTextbox },
{ name: 'LoadingSpinner', icon: mdiDotsCircle },
{ name: 'PasswordInput', icon: mdiFormTextboxPassword },
{ name: 'Switch', icon: mdiToggleSwitch },
],
},
{
2 changes: 1 addition & 1 deletion src/lib/components/AppShell/AppShellSidebar.svelte
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@
<Child for={ChildKey.AppShell} as={ChildKey.AppShellSidebar}>
<Scrollable
class={cleanClass(
'hidden h-full shrink-0 border-gray-200 dark:border-gray-700 lg:block',
'hidden h-full w-min shrink-0 border-gray-200 dark:border-gray-700 lg:block',
className,
noBorder || 'border-r',
)}
3 changes: 2 additions & 1 deletion src/lib/components/Scrollable/Scrollable.svelte
Original file line number Diff line number Diff line change
@@ -10,14 +10,15 @@
const { class: className, children }: Props = $props();
</script>

<div class={cleanClass('immich-scrollbar overflow-y-auto', className)}>
<div class={cleanClass('immich-scrollbar h-full w-full overflow-auto', className)}>
{@render children?.()}
</div>

<style>
/* width */
.immich-scrollbar::-webkit-scrollbar {
width: 8px;
height: 8px;
}

/* Track */
99 changes: 99 additions & 0 deletions src/lib/components/Switch/Switch.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<script lang="ts">
import { getFieldContext } from '$lib/common/context.svelte.js';
import type { Color } from '$lib/types.js';
import { cleanClass } from '$lib/utils.js';
import type { HTMLInputAttributes } from 'svelte/elements';
import { tv } from 'tailwind-variants';

type Props = {
checked?: boolean;
color?: Color;
disabled?: boolean;
class?: string;
onToggle?: ((checked: boolean) => void) | undefined;
} & HTMLInputAttributes;

let {
checked = $bindable(false),
class: className,
color = 'primary',
onToggle = undefined,
...restProps
}: Props = $props();

const {
label,
readOnly = false,
required = false,
disabled = false,
} = $derived(getFieldContext());

const enabled = $derived(checked && !disabled);

const handleToggle = (event: Event) => onToggle?.((event.target as HTMLInputElement).checked);

const wrapper = tv({
base: 'relative flex flex-col justify-center',
variants: {
disabled: {
true: 'cursor-not-allowed',
false: 'cursor-pointer',
},
},
});

const bar = tv({
base: 'w-12 h-3 my-2 rounded-full border border-transparent',
variants: {
fillColor: {
default: 'bg-gray-400',
primary: 'bg-primary/50',
secondary: 'bg-dark/50',
success: 'bg-success/50',
danger: 'bg-danger/50',
warning: 'bg-warning/50',
info: 'bg-info/50',
},
},
});

const dot = tv({
base: 'absolute transition-colors h-6 w-6 rounded-full transition-transform duration-[400ms]',
variants: {
checked: {
true: 'translate-x-6',
false: '',
},
fillColor: {
default: 'bg-gray-600',
primary: 'bg-primary',
secondary: 'bg-dark',
success: 'bg-success',
danger: 'bg-danger',
warning: 'bg-warning',
info: 'bg-info',
},
},
});
</script>

<label class={cleanClass(className)}>
{label}
<span class={wrapper({ disabled })}>
<input
class="hidden"
type="checkbox"
bind:checked
onclick={handleToggle}
{required}
aria-required={required}
{disabled}
aria-disabled={disabled}
readonly={readOnly}
aria-readonly={readOnly}
{...restProps}
/>
<span class={bar({ fillColor: enabled ? color : 'default' })}> </span>
<span class={dot({ checked: enabled, fillColor: enabled ? color : 'default' })}></span>
</span>
</label>
1 change: 1 addition & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@ export { default as HStack } from '$lib/components/Stack/HStack.svelte';
export { default as Stack } from '$lib/components/Stack/Stack.svelte';
export { default as VStack } from '$lib/components/Stack/VStack.svelte';
export { default as SupporterBadge } from '$lib/components/SupporterBadge/SupporterBadge.svelte';
export { default as Switch } from '$lib/components/Switch/Switch.svelte';
export { default as Text } from '$lib/components/Text/Text.svelte';
export * from '$lib/types.js';
export * from '$lib/utilities/byte-units.js';
5 changes: 4 additions & 1 deletion src/routes/components/button/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts">
import ExampleLayout from '$docs/components/ExampleLayout.svelte';
import { Text } from '@immich/ui';
import BasicExample from './BasicExample.svelte';
import basicExample from './BasicExample.svelte?raw';
import ColorExample from './ColorExample.svelte';
@@ -35,4 +36,6 @@
];
</script>

<ExampleLayout name="Button" {examples} />
<ExampleLayout name="Button" {examples}>
<Text>A button component, which can also be used to render links</Text>
</ExampleLayout>
5 changes: 4 additions & 1 deletion src/routes/components/field/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
<script lang="ts">
import ExampleLayout from '$docs/components/ExampleLayout.svelte';
import { Text } from '@immich/ui';
import BasicExample from './BasicExample.svelte';
import basicExample from './BasicExample.svelte?raw';

const examples = [{ title: 'Basic', code: basicExample, component: BasicExample }];
</script>

<ExampleLayout name="Field" {examples} />
<ExampleLayout name="Field" {examples}>
<Text>A metadata component for tracking common form field information</Text>
</ExampleLayout>
2 changes: 1 addition & 1 deletion src/routes/components/navbar/+page.svelte
Original file line number Diff line number Diff line change
@@ -11,4 +11,4 @@
];
</script>

<ExampleLayout name="Basic" {examples} />
<ExampleLayout name="Navbar" {examples} />
2 changes: 1 addition & 1 deletion src/routes/components/navbar/BasicExample.svelte
Original file line number Diff line number Diff line change
@@ -3,6 +3,6 @@
import { mdiHome } from '@mdi/js';
</script>

<div class="w-[200px]">
<div class="max-w-[200px]">
<NavbarItem icon={mdiHome} title="Home" href="#" active />
</div>
3 changes: 1 addition & 2 deletions src/routes/components/navbar/GroupExample.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script lang="ts">
import NavbarGroup from '$lib/components/Navbar/NavbarGroup.svelte';
import { NavbarItem } from '@immich/ui';
import { NavbarGroup, NavbarItem } from '@immich/ui';
import { mdiButtonPointer, mdiCardOutline, mdiHome } from '@mdi/js';
</script>

9 changes: 9 additions & 0 deletions src/routes/components/scrollable/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script lang="ts">
import ExampleLayout from '$docs/components/ExampleLayout.svelte';
import BasicExample from './BasicExample.svelte';
import basicExample from './BasicExample.svelte?raw';

const examples = [{ title: 'Basic', code: basicExample, component: BasicExample }];
</script>

<ExampleLayout name="Scrollable" {examples} />
22 changes: 22 additions & 0 deletions src/routes/components/scrollable/BasicExample.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script lang="ts">
import Lorem from '$docs/components/Lorem.svelte';
import { Heading, Scrollable, Stack, Text } from '@immich/ui';
</script>

<Stack gap={4}>
<Heading size="tiny">Vertical</Heading>
<div class="h-64 w-32">
<Scrollable>
<Text>
<Lorem count={5} />
</Text>
</Scrollable>
</div>

<Heading size="tiny">Horizontal</Heading>
<Scrollable>
<Text class="h-8 w-[2000px]">
<Lorem />
</Text>
</Scrollable>
</Stack>
20 changes: 20 additions & 0 deletions src/routes/components/switch/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script lang="ts">
import ExampleLayout from '$docs/components/ExampleLayout.svelte';
import { Text } from '@immich/ui';
import BasicExample from './BasicExample.svelte';
import basicExample from './BasicExample.svelte?raw';
import ColorExample from './ColorExample.svelte';
import colorExample from './ColorExample.svelte?raw';
import FormExample from './FormExample.svelte';
import formExample from './FormExample.svelte?raw';

const examples = [
{ title: 'Basic', code: basicExample, component: BasicExample },
{ title: 'Colors', code: colorExample, component: ColorExample },
{ title: 'Form', code: formExample, component: FormExample },
];
</script>

<ExampleLayout name="Switch" {examples}>
<Text>A boolean input element</Text>
</ExampleLayout>
11 changes: 11 additions & 0 deletions src/routes/components/switch/BasicExample.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script>
import { Field, Stack, Switch } from '@immich/ui';
</script>

<Stack>
<Switch />
<Switch checked />
<Field disabled>
<Switch />
</Field>
</Stack>
12 changes: 12 additions & 0 deletions src/routes/components/switch/ColorExample.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script lang="ts">
import { Stack, Switch } from '@immich/ui';
</script>

<Stack>
<Switch color="primary" checked />
<Switch color="secondary" checked />
<Switch color="success" checked />
<Switch color="info" checked />
<Switch color="warning" checked />
<Switch color="danger" checked />
</Stack>
17 changes: 17 additions & 0 deletions src/routes/components/switch/FormExample.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script>
import { Field, Stack, Switch, Text } from '@immich/ui';
</script>

<Stack>
<Field label="Support Immich">
<Switch class="flex justify-between" checked />
</Field>
<Field label="Advanced features">
<Switch class="flex justify-between" />
</Field>
<Field label="Sell my privacy" disabled>
<Switch class="flex justify-between">
<Text>Disabled</Text>
</Switch>
</Field>
</Stack>