Skip to content

Commit

Permalink
feat(v2)/multi select - DEV-28 (#736)
Browse files Browse the repository at this point in the history
* chore: update bits-ui

* feat: add separator component

* chore: update vite

* feat(v2): add select component

* feat(v2): add select component storybook

* docs: add comment for select

* feat: add multiple select with chip storybook

---------

Co-authored-by: Nutthapat Pongtanyavichai <contact@leomotors.me>
  • Loading branch information
phongit-kha and leomotors authored Jan 23, 2025
1 parent a5a6cea commit b16e429
Show file tree
Hide file tree
Showing 13 changed files with 519 additions and 56 deletions.
5 changes: 3 additions & 2 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@
},
"dependencies": {
"@repo/utils": "workspace:^",
"bits-ui": "^0.22.0",
"bits-ui": "1.0.0-next.74",
"clsx": "^2.1.1",
"lucide-svelte": "^0.473.0",
"tailwind-merge": "^2.6.0",
"tailwind-variants": "^0.3.1",
"tailwindcss": "^3.4.17"
"tailwindcss": "^3.4.17",
"vite": "^6.0.11"
}
}
7 changes: 7 additions & 0 deletions packages/ui/src/components/atom/separator/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Root from './separator.svelte'

export {
Root,
//
Root as Separator,
}
23 changes: 23 additions & 0 deletions packages/ui/src/components/atom/separator/separator.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script lang="ts">
import { Separator as SeparatorPrimitive } from 'bits-ui'
import { cn } from '@repo/utils'
let {
ref = $bindable(null),
class: className,
orientation = 'horizontal',
...restProps
}: SeparatorPrimitive.RootProps = $props()
</script>

<SeparatorPrimitive.Root
bind:ref
class={cn(
'bg-border shrink-0',
orientation === 'horizontal' ? 'h-[1px] w-full' : 'min-h-full w-[1px]',
className,
)}
{orientation}
{...restProps}
/>
34 changes: 34 additions & 0 deletions packages/ui/src/components/molecule/select/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Select as SelectPrimitive } from 'bits-ui'

import Content from './select-content.svelte'
import GroupHeading from './select-group-heading.svelte'
import Item from './select-item.svelte'
import ScrollDownButton from './select-scroll-down-button.svelte'
import ScrollUpButton from './select-scroll-up-button.svelte'
import Separator from './select-separator.svelte'
import Trigger from './select-trigger.svelte'

const Root = SelectPrimitive.Root
const Group = SelectPrimitive.Group

export {
Content,
Group,
GroupHeading,
Item,
Root,
ScrollDownButton,
ScrollUpButton,
//
Root as Select,
Content as SelectContent,
Group as SelectGroup,
GroupHeading as SelectGroupHeading,
Item as SelectItem,
ScrollDownButton as SelectScrollDownButton,
ScrollUpButton as SelectScrollUpButton,
Separator as SelectSeparator,
Trigger as SelectTrigger,
Separator,
Trigger,
}
36 changes: 36 additions & 0 deletions packages/ui/src/components/molecule/select/select-content.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<script lang="ts">
import { Select as SelectPrimitive, type WithoutChild } from 'bits-ui'
import { cn } from '@repo/utils'
let {
ref = $bindable(null),
class: className,
sideOffset = 4,
portalProps,
children,
...restProps
}: WithoutChild<SelectPrimitive.ContentProps> & {
portalProps?: SelectPrimitive.PortalProps
} = $props()
</script>

<SelectPrimitive.Portal {...portalProps}>
<SelectPrimitive.Content
bind:ref
{sideOffset}
class={cn(
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 bg-surface text-neutral-800 relative z-50 max-h-96 overflow-hidden rounded-md border shadow-md data-[side=bottom]:-translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
className,
)}
{...restProps}
>
<SelectPrimitive.Viewport
class={cn(
'h-[var(--bits-select-anchor-height)] w-full min-w-[var(--bits-select-anchor-width)] ',
)}
>
{@render children?.()}
</SelectPrimitive.Viewport>
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui'
import { cn } from '@repo/utils'
let {
ref = $bindable(null),
class: className,
...restProps
}: SelectPrimitive.GroupHeadingProps = $props()
</script>

<SelectPrimitive.GroupHeading
bind:ref
class={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}
{...restProps}
/>
41 changes: 41 additions & 0 deletions packages/ui/src/components/molecule/select/select-item.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script lang="ts">
import { Select as SelectPrimitive, type WithoutChild } from 'bits-ui'
import { Check } from 'lucide-svelte'
import { cn } from '@repo/utils'
let {
ref = $bindable(null),
class: className,
value,
label,
children: childrenProp,
check = false,
...restProps
}: WithoutChild<SelectPrimitive.ItemProps & { check?: boolean }> = $props()
</script>

<SelectPrimitive.Item
bind:ref
{value}
class={cn(
`data-[highlighted]:bg-surface-container-lowest data-[highlighted]:text-accent-foreground relative flex w-full cursor-default select-none items-center rounded-[5px] py-1.5 ${check ? 'pl-2 pr-4' : 'px-2'} text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50`,
className,
)}
{...restProps}
>
{#snippet children({ selected, highlighted })}
{#if childrenProp}
{@render childrenProp({ selected, highlighted })}
{:else}
{label || value}
{/if}
{#if check}
<span class="absolute right-2 flex size-3.5 items-center justify-center">
{#if selected}
<Check class="size-4" />
{/if}
</span>
{/if}
{/snippet}
</SelectPrimitive.Item>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script lang="ts">
import {
Select as SelectPrimitive,
type WithoutChildrenOrChild,
} from 'bits-ui'
import { ChevronDown } from 'lucide-svelte'
import { cn } from '@repo/utils'
let {
ref = $bindable(null),
class: className,
...restProps
}: WithoutChildrenOrChild<SelectPrimitive.ScrollDownButtonProps> = $props()
</script>

<SelectPrimitive.ScrollDownButton
bind:ref
class={cn('flex cursor-default items-center justify-center py-1', className)}
{...restProps}
>
<ChevronDown class="size-4" />
</SelectPrimitive.ScrollDownButton>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script lang="ts">
import {
Select as SelectPrimitive,
type WithoutChildrenOrChild,
} from 'bits-ui'
import { ChevronUp } from 'lucide-svelte'
import { cn } from '@repo/utils'
let {
ref = $bindable(null),
class: className,
...restProps
}: WithoutChildrenOrChild<SelectPrimitive.ScrollUpButtonProps> = $props()
</script>

<SelectPrimitive.ScrollUpButton
bind:ref
class={cn('flex cursor-default items-center justify-center py-1', className)}
{...restProps}
>
<ChevronUp class="size-4" />
</SelectPrimitive.ScrollUpButton>
19 changes: 19 additions & 0 deletions packages/ui/src/components/molecule/select/select-separator.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script lang="ts">
import type { Separator as SeparatorPrimitive } from 'bits-ui'
import { cn } from '@repo/utils'
import { Separator } from '../../atom/separator'
let {
ref = $bindable(null),
class: className,
...restProps
}: SeparatorPrimitive.RootProps = $props()
</script>

<Separator
bind:ref
class={cn('bg-muted -mx-1 my-1 h-px', className)}
{...restProps}
/>
33 changes: 33 additions & 0 deletions packages/ui/src/components/molecule/select/select-trigger.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<script lang="ts">
import { Select as SelectPrimitive, type WithoutChild } from 'bits-ui'
import { ChevronDown } from 'lucide-svelte'
import { cn } from '@repo/utils'
let {
ref = $bindable(null),
class: className,
children,
arrow = false,
...restProps
}: WithoutChild<SelectPrimitive.TriggerProps & { arrow?: boolean }> = $props()
</script>

<SelectPrimitive.Trigger
bind:ref
class={cn(
'bg-surface border-[1px] border-neutral-200 ring-offset-background data-[placeholder]:text-muted-foreground gap-2.5 focus:ring-ring flex h-10 w-full items-center justify-between rounded-[11px] p-2 text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
className,
)}
{...restProps}
>
<div
class="w-full h-full overflow-hidden flex items-center text-left truncate"
>
{@render children?.()}
</div>
{#if arrow}
<ChevronDown class="size-4 text-on-surface-placeholder stroke-[4px]" />
{/if}
<ChevronDown class="size-4 text-on-surface-placeholder stroke-[4px]" />
</SelectPrimitive.Trigger>
Loading

0 comments on commit b16e429

Please sign in to comment.