Code Monkey home page Code Monkey logo

svelte-headlessui's Introduction

minified minified + zipped

Svelte-HeadlessUI

Svelte Headless-UI

svelte-headlessui is an unofficial implementation of Tailwind HeadlessUI for Svelte. Just like the official implementation, they are:

  • ✅ Completely unstyled
  • ✅ Fully WIA-ARIA accessible UI components
  • ✅ Fully typed with Typescript
  • ✅ Designed to integrate beautifully with Tailwind CSS

But also:

  • ✅ Designed to integrate beautifully with Svelte and SvelteKit
  • ✅ Less than 14kB minified / 4kB minified gzipped

Installation

Install using your package manager of choice, e.g.

pnpm install svelte-headlessui

Import the appropriate create... factory method in your component and use the custom store it returns to manage your component state and attach use:action behaviors to your elements.

See the individual component pages for usage examples.

Alternative

You may be interested in evaluating @rgossiaux/svelte-headlessui. This package aligns closer to the official components in it's approach although I believe that approach (driven by React / Vue) contributes to the larger size of 173kB minified / 29kB minified + gzipped.

Logo

Special thanks to Shoob for the logo!

svelte-headlessui's People

Contributors

bennymi avatar captaincodeman avatar danielimmke avatar eddie1o avatar hshoob avatar jamesbirtles avatar lachlancollins avatar mattcroat avatar sebasmaltese avatar thenbe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

svelte-headlessui's Issues

Test functionalities across browsers

Hi there, really love the project

Some components don't properly work on the latest version of Firefox, like Combobox or Listbox (the list appears but nothing is mouse clickable)
Although everything works if I use keyboard navigation :)

Keep up the good work !

Stricter 'value' type for list items

It's a little "loosey-goosey" right now, using "any" for the value but really expecting / treating things as a string in some places.

It either needs to be a string consistently or could allow an object if the component is also provided with a "get me the string value" function.

This would also allow stricter lint checks.

Bug: Combobox trapping focus

Hi, thank you for porting this Headless UI library for Svelte.

Seems like Combobox is currently trapping focus, you can reproduce the issue by trying to tab it out from Combobox. Here is a video demo on the issue:

Screen.Recording.2023-06-14.at.5.13.29.PM.mov

As you can see, the original React version works as expected.

Your Svelte version:

https://captaincodeman.github.io/svelte-headlessui/combobox/

Original React version:

https://headlessui.com/react/combobox

Thank you.

How to make a dialog non-closable?

Hello, how do I make it so that the opened dialog can only be closed with me manually evoking dialog.close()? The library's dialog component uses only the Transition component (not Dialog like in the official headlessui implementation) so I have no idea how to do this.

ListBox example not working.

pnpm create svelte@latest listbox # y to ts, n to the rest
cd listbox
pnpm i
pnpm i svelte-headlessui
pnpx svelte-add @latest tailwindcss
pnpm i 

goto Docs - Listbox

Copy Paste Example Code
gist

Paste into /src/routes/+page.svelte

pnpm run dev

Error

[plugin:vite:import-analysis] Failed to resolve import "svelte-transition" from "src/routes/+page.svelte". Does the file exist?
39 |  const { console: console_1 } = globals;
40 |  import { createListbox } from "svelte-headlessui";
41 |  import Transition from "svelte-transition";
   |                          ^
42 |  import Selector from "./Selector.svelte";
43 |  import Check from "./Check.svelte";

Ok So I fix that.

-       import Transition from 'svelte-transition'
+       import Transition from 'svelte/transition'

More Errors

	import Selector from './Selector.svelte'
	import Check from './Check.svelte'

These both do not exist: so I make the two files each with one word in there so svelte has something to load.

diff --git a/src/routes/Check.svelte b/src/routes/Check.svelte
index 4c04caa..cc10e34 100644
--- a/src/routes/Check.svelte
+++ b/src/routes/Check.svelte
@@ -1 +1 @@
-check
\ No newline at end of file
+Check
\ No newline at end of file
diff --git a/src/routes/Selector.svelte b/src/routes/Selector.svelte
index 728f0f8..3d44d7b 100644
--- a/src/routes/Selector.svelte
+++ b/src/routes/Selector.svelte
@@ -1 +1 @@
-Selector
\ No newline at end of file
+Selector

Error:

Error: <Transition> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules. Otherwise you may need to fix a <Transition>.

And then i gave up.

<a> link in menu

First of all, wonderful work!

When I add an <a use:menu.item href="/">Home</a> link as a menu item, it goes to the href page, but instead of a hot reload, it completely refreshes the page. Am I missing something ?

Thank you.

Disclosure: How to iterate over a list of options?

Thanks a ton for this beautiful library! I had one small doubt.

Say I have the list of options

const content = [
        {
            title: '1. Is your Instagram page a business or a creator account?',
            description:
                "Here's how you can confirm whether your Instagram page is a business or a cretor account ->",
        },
        {
            title: '2. Is your Instagram page connected to a Facebook page?',
            description:
                "Here's how you can confirm whether your Instagram page is connected to a Facebook page ->",
        },
        {
            title: '3.Turn on "Allow Messages" setting',
            description: 'Here’s how you can toggle the “Allow Messages” setting ->',
        },
    ];

How do I iterate over these and create a disclosure?

The following code doesn't work since one cant just iterate over an array of stores I guess (or maybe i'm not smart enough to figure out how)

<script lang="ts">
// .. define content like above
 const contentStores = 
        content.map((item) => {
            return createDisclosure({ label: item.title, expanded: false });
        }),
</script>

{#each $contentStores as store, idx}
            <DownArrow class="h-6 w-6 {$store.expanded ? 'rotate-180' : ''}" />
            // Error: Stores must be declared at the top level of the component (this may change in a future version of Svelte)
{/each}

Expand controls outside dialog without triggering auto close

Hello, nice work on putting this together!

I'm trying to put a listbox inside a dialog, but I want it to render beyond the dialog when expanded. I can do this by moving the expanded part of the listbox outside the dialog (using a portal) to overlay it on top. However, this causes the dialog to close when a listbox item is clicked because it is no longer a descendant of the dialog. Is there a way to prevent the dialog from closing in this scenario? (Listbox is just an example here, but the same behaviour is desired with combobox, menu, etc.)

Can't enter space in Combobox

When entering a space in the Combobox it selects the currently highlighted element and if there is no highlighted Element it does not work at all.

Bugs in some of the documentation examples

The popover, menu, and autocomplete combobox have some strange bugs on the documentation site. Gifs and explanation below, ignore the weird artifacts in the Gifs.

OS: Windows 11
Browsers: Firefox 115.0.2 and Chrome 114.0.5735.248

Popover

Spam clicking the popover works fine until I spam the chevron SVG. My guess is that the click event is triggered twice, once from the button and once from the SVG somehow, and the rapid events cause the popover to permanently disappear. I only think this because holding down enter to trigger click events with the button focused causes the same issue.

Single clicking the SVG also causes the popover menu to flicker closed briefly and immediately reopen, see the Menu below for this behavior.

popover

Menu

Again, I think this bug is related to clicking specifically on the SVG. Seems to cause 2 click events, one that closes the menu and one that immediately reopens it. This doesn't happen if the click happens from a closed menu however.

menu

Autocomplete Combobox

This bug happens on Firefox but not on Chrome. Unable to try Safari. Again seems to be related to clicking the SVG that makes the menu disappear. I've had the menu disappear with a single click and spam clicks. Also noticed I was able to click the invisible menu to select different options.

autocomplete-combobox

Tab Components

Hi @CaptainCodeman,

props to this super nice and easy to style library. I just wanted to ask the question when the see apparently already implemented Tabs component will be released. Are there any todos one you need help with? Asking because the original library has a rather unpleasant approach to styling.

Thanks!

Add note to docs about svelte-transition

Make it clear that svelte-transition is a separate package that needs to be installed if you want to use it in your own component (as shown in the examples).

It's not a dependency of svelte-headlessui itself, although it may be worth considering adding it to this package (as it will likely be used more often than not).

Can't get Dialog to work with Sveltekit

Hey there, thanks for your effort on making this port! Having some issues getting it working properly on Sveltekit.

Issue

When importing the Dialog component, it seems to break the page, here's the error:

Cannot read properties of undefined (reading 'default')
TypeError: Cannot read properties of undefined (reading 'default')
    at eval (/src/routes/(app)/trips/[tripId]/places/+page.svelte:55:101)
    at Object.$$render (/node_modules/svelte/internal/index.mjs:1876:22)
    at Object.default (root.svelte:44:40)
    at Object.left (/src/routes/(app)/trips/[tripId]/[email protected]:57:43)
    at SplitLayout.svelte:17:52
    at Object.$$render (/node_modules/svelte/internal/index.mjs:1876:22)
    at eval (/src/routes/(app)/trips/[tripId]/[email protected]:51:99)
    at Object.$$render (/node_modules/svelte/internal/index.mjs:1876:22)
    at Object.default (root.svelte:43:39)
    at eval (/src/routes/+layout.svelte:28:41)

How to reproduce

  1. Copy and pasted the example Dialog code into a +page.svelte
  2. Component will render on initial save, but as soon as you refresh the page it will break and show error above in server logs

Version info

"svelte": "^3.54.0",
"@sveltejs/kit": "^1.0.0",
"svelte-headlessui": "^0.0.11",

Side note: What do you recommend when trying to wrap this into a custom component and passing in the toggle function and content?

Add project setup instructions in the readme

I'm not that familiar with monorepos but I managed to get the project working using intuition and some luck but it would be great to include instructions for people who want to contribute and ones that just want to explore and learn how it works — I can open a pull request if someone enlightens me on the correct setup.

Error when attempting to clear the input of the combobox after selection

I'm attempting to clear the combobox input after selecting an item like so.

  function handleSelect(e: Event) {
    ...select actions
    $combobox.selected = null;
  }

However I get the error

Uncaught TypeError: can't access property "value", t2.items[t2.active] is undefined
    q list.ts:37
    <anonymous> combobox.ts:227
    sync index.mjs:77
    unsubscribers index.mjs:89
    set index.mjs:34

My use case is that the user searches a dynamic data set via the combobox, I then perform an action with their selection, then I would like to clear to input.

Let me know if this is kind of a weird use case, otherwise thanks for the work on the library :)

Question: How to provide dynamic data to combobox?

Hello and thanks for taking on HeadlessUI for Svelte! I'm just starting to play around with it and am having some trouble understanding the right way to provide dynamic data to combobox results.

I'd like to use the combobox as an autocomplete for a dynamic form. As the user types, I'll send out an API call to find all of the matching entries and fill in the results in the dropdown as they stream in.

I've tried updating the combobox state directly, e.g. combobox.set({items: itemsFromServer}) whenever the API call finishes. However, I'm not able to use the combobox's filter without ending up in an infinite loop.

What's the right way to go about this?

Thanks!

e.q.

<script lang="ts">
// Autocomplete.svelte

export let filter = ''
export let items: {id: string; name: string; value: any}[] = []
const combobox = createCombobox({filter})

let prevFilter = ''
combobox.subscribe((state) => {
  if (state.filter !== prevFilter) {
    filter = state.filter
  }
})

$: updateCombobox(items)

function updateCombobox(items) {
  combobox.set({ items })
}
</script>

<input use:combobox.input value={$combobox.selected?.name ?? ''} />
<button use:combobox.button type="button" />

<ul use:combobox.items>
  {#each items as value}
    <li use:combobox.item={{ value }} />
  {/each}
</ul>
<script lang="ts">
// +page.svelte

import Autocomplete from './Autocomplete.svelte'

let searchTerm = ''
let items = writable([])

$: updateSearchResults(searchTerm)

function updateSearchResults(searchTerm) {
  makeApiCall(searchTerm).then(results => {
    items.set(results)
  })
}

</script>

<Autocomplete {items} bind:filter={searchTerm} />

Feature request: dispatch event according actions for easier integration with other libraries

Right now this lib does not dispatch any event, which makes integrating with other libraries difficult. For example, if I am using Dialog action, it would be great if an close event will be dispatched when dialog.close is called, so that I can do something custom after that dialog is closed(For example toggle a boolean in local storage so that user will not see that dialog again)

Is that something you would consider in this library? I can help with PR but want to know your thought first

some suggestions

  • on the home page, the line that says how to install the package (i.e. pnpm install svelte-headlessui) could be highlighted.
  • for issue #43, i would suggest adding a note under the "Installation" section, stating that the examples provided in the site require the svelte-transition package.

thank you so much for this package! i really enjoy using it with daisyui components

Consider exporting the setter function of stores provided

Currently I can see that stores in this project are derived stores, and only exporting the essential methods:

const { subscribe } = derived(store, $state => {
const { expanded, selected } = $state
return { expanded, selected, active: active($state) }
})
return {
subscribe,
button,
items,
item,
open,
close,
}

This is fine for setting values with user interaction, but impossible for setting values programmatically. A practical use case would be injecting previously selected values from local storage. This is impossible right now. Is exporting the setter function something you would consider to unblock that usecase?

Consider dispatching event for ListBox

Right now I am using createListbox to create a custom <select> element with styling. One issue that I bumped into is, the custom select element does not fire any change event, and no event is dispatched once the value has been selected. If a event is dispatched once an element is selected, I can at least do something programatically there.

This is how my component looks like:

<script lang="ts" context="module">
    import { createListbox } from 'svelte-headlessui'
</script>

<script lang="ts">
    export let name: string;
    export let options: Array<{
        label: string,
        value: string
    }>;

    const listbox = createListbox({
        selected: options[0]
    })
</script>

<div class="listbox">
    <!-- Option1. This would work best with form library like Felte, as it would trigger a Change event -->
    <select class="hidden" {name}>
        {#each options as unit}
            <option value={unit.value} selected={unit.value === $listbox.selected.value}>{unit.label}</option>
        {/each}
    </select>
    <!-- Option2. The value is also avaliable on:submit, but it will not trigger and Change event -->
    <!-- <input type="hidden" {name} value={$listbox.selected.value}/> -->
    <button type="button" use:listbox.button>
        <slot name="button" label={$listbox.selected.label}/>
    </button>
    {#if $listbox.expanded}
    <ul class="listbox-panel" use:listbox.items>
    {#each options as unit, i}
        {@const active = $listbox.active === unit}
        {@const selected = $listbox.selected === unit}
        <li
            use:listbox.item={{
                value: unit
            }}
        >
            <slot name="option" item={unit}>
                <span>
                    {unit.label}
                </span>
            </slot>
        </li>
    {/each}
    </ul>
    {/if}
</div>

<style lang="scss">
    .hidden {
        display: none;
    }
</style>

Since I can see from doc no event is dispatched this library, is there a way originally designed to handle issue like this?

Uncaught TypeError: stop is not a function

I got this error:

index.mjs:54 Uncaught TypeError: stop is not a function
    at index.mjs:54:17
    at behavior.ts:6:52

when a menu (dropdown) is unmounted while redirecting.
Seems like it has something to do with unsubscribing:

export function applyBehaviors(node: HTMLElement, behaviors: Behavior[]) {
const unsubscribes = behaviors.map(behavior => behavior(node))
return () => unsubscribes.forEach(unsubscribe => unsubscribe())
}

see sveltejs/svelte#4765 and sveltejs/svelte#8186

Maybe the error should be catched and silenced while waiting for the PR above to land, or finding a way to prevent these falsy unsubscribe?

Differences from the canonical version

I'm not sure how closely this version is meant to align with the original, but here are a few things I've noticed:

  • Mousedown on listbox options then releasing outside the dialog should not cause it to close
  • Combobox does not allow tabbing away from its input field
  • Typing with the listbox open does not focus on matched item. (Though the canonical implementation is also lacking compared to a native select where you can type to select without needing to expand the options first).

If I come across any other differences I'll try to update this list.

Functionality of Popover.Group

Thanks for all the work you've put into this library! Excited to use it and see it grow. 😃


I want to use multiple Popovers to build a more complex site navigation header. Some pseudocode:

<script>
   const popover1 = createPopover({});
   const popover2 = createPopover({});
</script>

<button use:popover1.button ... />
<div use:popover1.panel ... />

<button use:popover2.button ... />
<div use:popover2.panel ... />

However, if I first click popover1.button, then click popover2.button (while popover1.panel is still "expanded") it will only close popover1.panel. I must click popover2.button a second time to open popover2.panel.

In headlessui, this action is supported with grouping related popovers (<Popover.Group />).

Is there a way to achieve that functionality?

onSelect not getting fired for Combobox

In the following docs for combobox

https://captaincodeman.github.io/svelte-headlessui/combobox/

If you select any element from the list, it does not call the onSelect method. This can be verified by just opening the console & no log is printed when an item is selected. If you open the docs for listbox on the other hand, the console log works as expected. Also verified by using the library myself & the onSelect is not gettting fired.

Additionally, I I search for something that is not in the list, the following error shows up

image

Let me know if I can help in fixing this issues, once again, thanks for the effort!

Feature request: multiple select for listbox and combobox

Apologies if I've missed something, but after a look through the code I couldn't determine an easy way of hacking together a solution for multiple select. Primarily I think the issue is needing alternate behavior for use:listbox.items for it to stay open after a selection.

It was implemented in the canonical version a while back:
tailwindlabs/headlessui#1243

Also, there's a slight difference in behavior with selects and menus -- when triggering mouse down on an item and then releasing outside the menu it should remain open.

Safari: Modal breaks when clicking outside of modal to close

After you click outside the modal to close it, the modal no longer appears (the full screen background does, just not the modal) and you cannot close it anymore

Broken in Safari 16.3
This works fine in Firefox 111, Chrome 112, and Edge 111.

Code: The example code for the modal on the docs.

Do not require function onSelect for Menu

If we are just using anchor links or forms and we don't need to track the events in JS, we should not need to have function onSelect(e: Event). As of now, if you omit this function, it will cause an error.

ReferenceError: onSelect is not defined

Switch component

Hi,
thanks for awesome work! I'm planning to migrate from Rgossiaux's port to this one.
Any idea when Switch component might be available?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.