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

Thanaphomh/sucu 40 component search and drop down #12

Merged
merged 23 commits into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
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
85 changes: 85 additions & 0 deletions src/lib/components/Dropdown/Dropdown.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<script lang="ts">
import Fa from 'svelte-fa';
import { faChevronDown } from '@fortawesome/free-solid-svg-icons';
import { dropdownVariants, type DropdownProps } from '../../../styles/tailwind/dropdown';
import DropdownList from '$lib/components/Dropdown/DropdownList.svelte';
import { onDestroy, onMount } from 'svelte';
import { cn } from '$lib/utils';

ThanaphomH marked this conversation as resolved.
Show resolved Hide resolved
export let items: string[] = [];
export let currentChoice: string | null = null;
export let outerClass: string = '';
let isOpen = false;
let hasSelected = false;
let currentVariant: DropdownProps['variant'] = 'transparent';
let dropdownClass: string = dropdownVariants({ variant: currentVariant });
let placeholder: string = 'Select Item';
let saveChoice: string | null = null;

onMount(() => {
currentChoice = items[0] || null;
saveChoice = currentChoice;
});

$: {
if (currentChoice) {
isOpen = false;
if (currentChoice !== saveChoice) {
hasSelected = true;
}
}
}

$: {
if (isOpen) {
currentVariant = 'focus';
} else if (hasSelected) {
currentVariant = 'default';
} else {
currentVariant = 'transparent';
}

dropdownClass = dropdownVariants({ variant: currentVariant });
}

function handleClickOpen() {
isOpen = !isOpen;
}

let dropdownRef: HTMLElement;
function handleClickOutside(event: MouseEvent) {
if (dropdownRef && !dropdownRef.contains(event.target as Node)) {
isOpen = false;
}
}
onMount(() => {
if (typeof document !== 'undefined') {
document.addEventListener('click', handleClickOutside);
}
});

onDestroy(() => {
if (typeof document !== 'undefined') {
document.removeEventListener('click', handleClickOutside);
}
});
</script>

<div class={cn('relative w-full', outerClass)} bind:this={dropdownRef}>
<button
class={cn(
'w-full px-4 py-3 flex flex-row justify-between items-center rounded-sm gap-1',
dropdownClass
)}
on:click={handleClickOpen}
>
<span class="truncate">{currentChoice || placeholder}</span>
<Fa icon={faChevronDown} class={cn('w-6 h-6 transition-transform', isOpen && 'rotate-180')} />
</button>

{#if isOpen}
<div class="absolute top-[calc(100%+8px)] w-full z-30">
<DropdownList bind:items bind:currentChoice />
</div>
{/if}
</div>
40 changes: 40 additions & 0 deletions src/lib/components/Dropdown/DropdownItem.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<script lang="ts">
import {
dropdownItemVariants,
type DropdownItemProps
} from '../../../styles/tailwind/dropdownItem';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();

export let text: string;
export let disabled: boolean = false;
export let currentSelectedChoice: string | null = null;

let currentVariant: DropdownItemProps['variant'] = 'default';
let isSelected = false;

$: isSelected = currentSelectedChoice === text;

$: {
if (disabled) {
currentVariant = 'disabled';
} else if (isSelected) {
currentVariant = 'pressed';
} else {
currentVariant = 'default';
}
}

function handleClick() {
if (!disabled) {
isSelected = true;
dispatch('select', text);
}
}

$: dropdownClass = dropdownItemVariants({ variant: currentVariant });
</script>

<button class={dropdownClass} on:click={handleClick} {disabled}>
{text}
</button>
18 changes: 18 additions & 0 deletions src/lib/components/Dropdown/DropdownList.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script lang="ts">
import DropdownItem from '$lib/components/Dropdown/DropdownItem.svelte';

export let items: string[] = [];
export let currentChoice: string | null = null;
</script>

<div
class="flex flex-col outline outline-1 outline-sucu-gray rounded-sm gap-1 overflow-hidden bg-white"
>
{#each items as item}
<DropdownItem
text={item}
on:select={(e) => (currentChoice = e.detail)}
currentSelectedChoice={currentChoice}
/>
{/each}
</div>
52 changes: 52 additions & 0 deletions src/lib/components/Playground.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import TabCapsuleItem from './TabCapsule.svelte';
import Modal from '$lib/components/Modal/Modal.svelte';
import { modalShow } from './Modal/store';
import DropdownItem from '$lib/components/Dropdown/DropdownItem.svelte';
import SearchBar from '$lib/components/SearchBar.svelte';
import DropdownList from '$lib/components/Dropdown/DropdownList.svelte';
import Dropdown from '$lib/components/Dropdown/Dropdown.svelte';
import List from './List/List.svelte';
import TabsRoot from './Tabs/TabsRoot.svelte';
import TabsList from './Tabs/TabsList.svelte';
Expand Down Expand Up @@ -61,6 +65,18 @@
? [...selectedTabs, tab.label]
: selectedTabs.filter((label) => label !== tab.label);
}

let searchValue = '';
let listValue: string | null = null;
let dropdownValue: string | null = null;
let choiceList: string[] = [
'ล่าสุด',
'ปี 2567',
'ปี 2566',
'ปี 2565',
'ปี 2564',
'ปีย้าวยาวววววววววววววววววววววววววววววววววววววววววววววววววววววววววววววววววววววววววววววววววว'
];
</script>

<div>
Expand Down Expand Up @@ -167,6 +183,42 @@
{/if}
</section>

<section class="section">
<h2 class="font-bold text-2xl mb-4">Search & Dropdown</h2>

<div class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<h3 class="font-semibold text-xl">SearchBar</h3>
<SearchBar bind:value={searchValue} />
<p>Search Value : {searchValue == '' ? 'no value' : searchValue}</p>
</div>

<div class="flex flex-col gap-2">
<h3 class="font-semibold text-xl mb-2">DropdownItem</h3>

<div>
<DropdownItem text="Hello"></DropdownItem>
<DropdownItem text="Hello" disabled></DropdownItem>
</div>
</div>

<div class="flex flex-col gap-2">
<h3 class="font-semibold text-xl mb-2">DropdownList</h3>

<DropdownList bind:currentChoice={listValue} items={choiceList} />
<p>List Value : {listValue || 'no value'}</p>
</div>

<div class="flex flex-col gap-2">
<h3 class="font-semibold text-xl mb-2">Dropdown</h3>

<Dropdown items={choiceList} bind:currentChoice={dropdownValue} outerClass="w-64" />

<p>Dropdown Value : {dropdownValue || 'no value'}</p>
</div>
</div>
</section>

<section class="section">
<TabsRoot defaultActiveTab="all">
<TabsList>
Expand Down
35 changes: 35 additions & 0 deletions src/lib/components/SearchBar.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script lang="ts">
import { faSearch } from '@fortawesome/free-solid-svg-icons';
import Fa from 'svelte-fa';

export let value: string = '';
let inputRef: HTMLInputElement;

function handleClickIcon() {
inputRef.focus();
}
</script>

<div class="search-bar">
<button on:click={handleClickIcon} type="button">
<Fa icon={faSearch} class="w-5 h-5" />
</button>

<input
type="text"
class="w-full text-black focus:border-none focus:outline-none"
placeholder="ค้นหา"
bind:value
bind:this={inputRef}
/>
</div>

<style>
.search-bar {
@apply w-full flex items-center gap-3 px-4 py-3 rounded-sm border border-sucu-gray-light;
}

.search-bar:focus-within {
@apply border-sucu-pink-03;
}
</style>
22 changes: 22 additions & 0 deletions src/styles/tailwind/dropdown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { tv, type VariantProps } from 'tailwind-variants';
import { typography } from './typography';

export const dropdownVariants = tv({
base: `w-full ${typography({ variant: 'body-normal' })} transition-colors duration-200 px-4 py-2 text-left`,
variants: {
variant: {
default: `text-sucu-gray-dark border border-sucu-gray`,
focus: `text-sucu-gray border border-sucu-pink-03`,
transparent: `text-sucu-gray-light border border-sucu-gray-light`
}
},
defaultVariants: {
variant: 'default'
}
});

export type Variant = VariantProps<typeof dropdownVariants>['variant'];

export type DropdownProps = {
variant?: Variant;
};
22 changes: 22 additions & 0 deletions src/styles/tailwind/dropdownItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { tv, type VariantProps } from 'tailwind-variants';
import { typography } from './typography';

export const dropdownItemVariants = tv({
base: `w-full ${typography({ variant: 'body-normal' })} transition-colors duration-200 px-4 py-2 text-left truncate`,
variants: {
variant: {
default: `bg-white text-black hover:bg-sucu-gray-01 hover:text-sucu-pink-01`,
pressed: `bg-sucu-pink-01 text-white`,
disabled: `bg-white text-sucu-gray-dark`
}
},
defaultVariants: {
variant: 'default'
}
});

export type Variant = VariantProps<typeof dropdownItemVariants>['variant'];

export type DropdownItemProps = {
variant?: Variant;
};
Loading