Button 및 Input 컴포넌트를 atoms 디렉터리로 이동 및 리팩토링

This commit is contained in:
static
2025-01-24 16:39:09 +09:00
parent a01137bbf9
commit 1c09d93b41
33 changed files with 145 additions and 172 deletions

View File

@@ -0,0 +1,35 @@
<script lang="ts">
import type { Snippet } from "svelte";
import type { ClassValue } from "svelte/elements";
interface Props {
children?: Snippet;
class?: ClassValue;
color?: "primary" | "gray";
onclick?: () => void;
}
let { children, color = "primary", onclick, ...props }: Props = $props();
let bgColor = $derived(
{
primary: "bg-primary-600 active:bg-primary-500",
gray: "bg-gray-300 active:bg-gray-400",
}[color],
);
let textColor = $derived(
{
primary: "text-white",
gray: "text-gray-800",
}[color],
);
</script>
<button
onclick={onclick && (() => setTimeout(onclick, 100))}
class={["h-12 min-w-fit rounded-xl font-medium", bgColor, textColor, props.class]}
>
<div class="h-full p-3 transition active:scale-95">
{@render children?.()}
</div>
</button>

View File

@@ -0,0 +1,28 @@
<script lang="ts">
import type { Snippet } from "svelte";
import type { ClassValue } from "svelte/elements";
import IconChevronRight from "~icons/material-symbols/chevron-right";
interface Props {
children?: Snippet;
class?: ClassValue;
onclick?: () => void;
}
let { children, onclick, ...props }: Props = $props();
</script>
<button
onclick={onclick && (() => setTimeout(onclick, 100))}
class={["rounded-xl active:bg-gray-100", props.class]}
>
<div class="flex h-full p-2 transition active:scale-95">
<div class="flex-grow">
{@render children?.()}
</div>
<div class="flex flex-shrink-0 items-center">
<IconChevronRight class="text-xl text-gray-800" />
</div>
</div>
</button>

View File

@@ -0,0 +1,30 @@
<script lang="ts">
import type { Component } from "svelte";
import type { ClassValue, SvelteHTMLElements } from "svelte/elements";
import { AdaptiveDiv } from "$lib/components/divs";
interface Props {
icon: Component<SvelteHTMLElements["svg"]>;
offset?: ClassValue;
onclick?: () => void;
}
let { icon: Icon, offset = "bottom-20", onclick }: Props = $props();
</script>
<div class="pointer-events-none fixed inset-0">
<div class={["absolute w-full", offset]}>
<AdaptiveDiv>
<div class="relative">
<div class="absolute bottom-4 right-4">
<button
onclick={onclick && (() => setTimeout(onclick, 100))}
class="pointer-events-auto flex h-14 w-14 items-center justify-center rounded-full bg-gray-300 shadow-lg transition active:scale-95 active:bg-gray-400"
>
<Icon class="text-xl" />
</button>
</div>
</div>
</AdaptiveDiv>
</div>
</div>

View File

@@ -0,0 +1,19 @@
<script lang="ts">
import type { Snippet } from "svelte";
interface Props {
children?: Snippet;
onclick?: () => void;
}
let { children, onclick }: Props = $props();
</script>
<button
onclick={onclick && (() => setTimeout(onclick, 100))}
class="text-sm font-medium text-gray-800 underline underline-offset-2 active:rounded-xl active:bg-gray-100"
>
<div class="h-full p-1 transition active:scale-95">
{@render children?.()}
</div>
</button>

View File

@@ -0,0 +1,4 @@
export { default as Button } from "./Button.svelte";
export { default as EntryButton } from "./EntryButton.svelte";
export { default as FloatingButton } from "./FloatingButton.svelte";
export { default as TextButton } from "./TextButton.svelte";

View File

@@ -0,0 +1,2 @@
export * from "./buttons";
export * from "./inputs";

View File

@@ -0,0 +1,25 @@
<script lang="ts">
import type { Snippet } from "svelte";
import IconCheckCircle from "~icons/material-symbols/check-circle";
import IconCheckCircleOutline from "~icons/material-symbols/check-circle-outline";
interface Props {
checked?: boolean;
children?: Snippet;
}
let { checked = $bindable(false), children }: Props = $props();
</script>
<label class="flex items-center gap-x-1">
<input bind:checked type="checkbox" class="hidden" />
<div>
{@render children?.()}
</div>
{#if checked}
<IconCheckCircle class="text-primary-600" />
{:else}
<IconCheckCircleOutline class="text-gray-300" />
{/if}
</label>

View File

@@ -0,0 +1,35 @@
<script lang="ts">
interface Props {
placeholder: string;
type?: "text" | "password";
value?: string;
}
let { placeholder, type = "text", value = $bindable("") }: Props = $props();
</script>
<div class="relative mt-5">
<input
bind:value
{type}
placeholder=""
class="w-full border-b-2 border-gray-300 py-1 text-xl outline-none transition duration-300 ease-in-out"
/>
<!-- svelte-ignore a11y_label_has_associated_control -->
<label
class="pointer-events-none absolute left-0 top-1/2 -translate-y-1/2 transform text-xl text-gray-400 transition-all duration-300 ease-in-out"
>
{placeholder}
</label>
</div>
<style>
input:focus,
input:not(:placeholder-shown) {
@apply border-primary-300;
}
input:focus + label,
input:not(:placeholder-shown) + label {
@apply top-0 -translate-y-full text-sm text-primary-400;
}
</style>

View File

@@ -0,0 +1,2 @@
export { default as CheckBox } from "./CheckBox.svelte";
export { default as TextInput } from "./TextInput.svelte";