Giter VIP home page Giter VIP logo

terracotta's Introduction

terracotta

Headless UI library for SolidJS

Install

npm i terracotta
yarn add terracotta
pnpm add terracotta

Note

The project is still in development, as I aim to implement the WAI-ARIA Design Patterns and Widgets.

Here's the current components:

  • Accordion Open in StackBlitz
  • Alert
  • Alert Dialog
  • Button
  • Button (Toggle)
  • Checkbox Open in StackBlitz
  • Dialog (Modal) Open in StackBlitz
  • Dialog (Popover) Open in StackBlitz
  • Disclosure (Show/Hide) Open in StackBlitz
  • Feed Open in StackBlitz
  • Listbox (Select) Open in StackBlitz
  • Listbox (Dropdown) Open in StackBlitz
  • Menu Open in StackBlitz
  • Radio Group Open in StackBlitz
  • Toolbar Open in StackBlitz
  • Transition Open in StackBlitz
  • Toaster/Toast Open in StackBlitz
  • ContextMenu (Dialog) Open in StackBlitz
  • CommandBar (Dialog) Open in StackBlitz
  • Tabs Open in StackBlitz

FAQ

Is this the official HeadlessUI for SolidJS?

Uhh, no. I intended to port the original however this is not recognized as the official port for SolidJS. There's also some differences in development path since I intended to implement the WAI-ARIA widgets. The reason is that there seems to be no resolution from the original Headless UI if they're going to implement WAI-ARIA widgets in the first place.

If this is a port, does it mean it is 100% compatible?

Kinda. Due to differences in principle between SolidJS and React, the implementation would be different and such, features that are originally found in the original HeadlessUI may be incompatible with the SolidJS version.

License

MIT © lxsmnsyc

terracotta's People

Contributors

lxsmnsyc avatar razboy20 avatar vincentfretin 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

terracotta's Issues

What do I use instead of Fragment?

I am moving from React, and I've been using the headless-ui.
I have a Transition:

<Transition.Root show={sidebarOpen} as={Fragment}>

What can I use instead of Fragment here?

createUniqueId in Toaster causes issues with solid-start

When using the Toaster in solid-start, the following error is given:

createUniqueId cannot be used under non-hydrating context

After debugging this, I found that the error is thrown from the following line:
https://github.com/lxsmnsyc/solid-headless/blob/3844f9a58dcc2698090eb242635a2715a73741df/packages/solid-headless/src/components/toast/Toaster.ts#L27

The original error is given here:
https://github.com/solidjs/solid/blob/19e63798990be0f40b27a5ddd58fe3af837d818a/packages/solid/src/server/rendering.ts#L68

Popover but without the close on blur

I'm using the Popover components to open a chat panel that includes latest messages and an input to enter a new message. When the popover is open, the input is focused, that's great. But I don't want the panel to close when I click outside the chat panel or inside the panel if I want to select and copy a message for example. I want the panel to stay open unless Esc or click again on the PopoverButton.
The close on blur behavior seems to be triggered by
https://github.com/lxsmnsyc/solid-headless/blob/983581b93fc06169325703c8b0aa734ff349924a/packages/solid-headless/src/components/popover/PopoverPanel.ts#L68-L70
Could we have a prop to not register this close on blur behavior? Something like <PopoverPanel closeOnFocusOut={false}> ? What do you think?

listbox with multiple prop unable to unselect option

hello,
I'm using listbox as a multiselect via the multiple prop. However, clicking a selected item does not unselect the item. I tried the react headless UI and it unselects automatically.

Thanks for the library!

Combobox

Feature request:
Combobox component from HeadlessUI

I can't find it in the source code. If it's there under a different name, let me know and feel free to close the ticket.

Usage with Islands?

Hi! I'm fairly new to both headless ui (react and solid), so this may be somewhat obvious, but I'm trying to get solid-headless to work with solid. I am able to get this library to work using regular SSR, as well as SPA. I am also able to get reactive components to work using islands when using simple Signals. For example:

// vite.config.ts
solid({
  islands: true, 
  islandsRouter: true,
}),
// Counter.tsx
import { createSignal } from "solid-js";

export default function Counter() {
  const [count, setCount] = createSignal(0);
  return (
    <button class="increment" onClick={() => setCount(count() + 1)}>
      Clicks: {count()}
    </button>
  );
}
// index.tsx
import { unstable_island } from 'solid-start'
const Counter = unstable_island(()=>import("./Counter"))

export default function App() {
  return (
    <div>
      <Counter />
    </div>
  )
}

Unfortunately, I am unable to do this with solid-headless:

const { Popover } = unstable_island(()=>import("solid-headless"))

Looking further, it seems that unstable_island needs a default export, so I tried wrapping

// WrappedPopover.tsx
export { Popover as default } from "solid-headless"
// index.tsx
import { unstable_island } from 'solid-start'
const WrappedPopover = unstable_island(()=>import("./WrappedPopover"))

export default function App() {
  return (
    <div>
      <WrappedPopover>
        {/* other elements that were also exported */}
      </WrappedPopover>
    </div>
  )
}

Two outcomes occur. If I directly wrap the headless elements, then my entire page shows blank.

But if I import a component inside of index.tsx which uses unstable islands to import, the page shows, but the component becomes static and has no reactivity.

Menu components that are difficult to understand

Hello, I noticed there are three components in the menu component that are difficult to understand: ContextMenu, ContextMenuBoundary, and ContextMenuPanel. What do they mean?

Also, how do I know which menu I have selected? Is there a simple example?

Issue when using solid-headless with Astro

This message is copied from issue #6 as it was deemed to be a different issue

The codebase I am using is closed source for now but I could invite you to it if you can't reproduce the issue. I got the following error when doing a production build (astro build):

Error
generating static routes 
 error   Named export 'Popover' not found. The requested module 'solid-headless' is a CommonJS module, which may not support all module.exports as named exports.
  CommonJS modules can always be imported via the default export, for example using:
  
  import pkg from 'solid-headless';
  const { Popover, PopoverButton, Transition, TransitionChild, PopoverPanel, TabGroup, TabList, Tab } = pkg;
  
file:///Users/oscar/github/oscartbeaumont/Mattrax/landing/dist/entry.mjs?time=1663903191396:4
import { Popover, PopoverButton, Transition, TransitionChild, PopoverPanel, TabGroup, TabList, Tab } from 'solid-headless';
         ^^^^^^^
SyntaxError: Named export 'Popover' not found. The requested module 'solid-headless' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'solid-headless';
const { Popover, PopoverButton, Transition, TransitionChild, PopoverPanel, TabGroup, TabList, Tab } = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:123:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:189:5)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:541:24)
    at async generatePages (file:///Users/oscar/github/oscartbeaumont/Mattrax/node_modules/.pnpm/[email protected]/node_modules/astro/dist/core/build/generate.js:71:20)
    at async staticBuild (file:///Users/oscar/github/oscartbeaumont/Mattrax/node_modules/.pnpm/[email protected]/node_modules/astro/dist/core/build/static-build.js:68:5)
    at async AstroBuilder.build (file:///Users/oscar/github/oscartbeaumont/Mattrax/node_modules/.pnpm/[email protected]/node_modules/astro/dist/core/build/index.js:86:5)
    at async AstroBuilder.run (file:///Users/oscar/github/oscartbeaumont/Mattrax/node_modules/.pnpm/[email protected]/node_modules/astro/dist/core/build/index.js:127:7)
    at async build (file:///Users/oscar/github/oscartbeaumont/Mattrax/node_modules/.pnpm/[email protected]/node_modules/astro/dist/core/build/index.js:22:3)
    at async runCommand (file:///Users/oscar/github/oscartbeaumont/Mattrax/node_modules/.pnpm/[email protected]/node_modules/astro/dist/cli/index.js:190:14)
    at async cli (file:///Users/oscar/github/oscartbeaumont/Mattrax/node_modules/.pnpm/[email protected]/node_modules/astro/dist/cli/index.js:207:5)
 ELIFECYCLE  Command failed with exit code 1.

This codebase is a completly new Astro project I created yesterday and is using the Astro integrations for SolidJS and Tailwind CSS. I included the package.json and the component using the Popover below. This package is in a pnpm workspace

package.json
{
  "name": "@mattrax/landing",
  "type": "module",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "dev": "astro dev",
    "start": "astro dev",
    "build": "astro build",
    "preview": "astro preview",
    "astro": "astro"
  },
  "dependencies": {
    "@astrojs/solid-js": "^1.1.0",
    "@astrojs/tailwind": "^2.0.1",
    "@solid-primitives/event-listener": "^2.2.2",
    "astro": "^1.3.0",
    "clsx": "^1.2.1",
    "solid-headless": "^0.12.6",
    "solid-js": "^1.4.3",
    "tailwindcss": "^3.0.24"
  },
  "devDependencies": {
    "@babel/core": "^7.19.1"
  }
}
component using `Popover` - A direct conversion of a TailwindUI template to SolidJS
import {
  Popover,
  PopoverButton,
  PopoverPanel,
  Transition,
  TransitionChild,
} from "solid-headless";
import clsx from "clsx";

import { Button } from "./Button";
import { Container } from "./Container";
import { Logo } from "./Logo";
import { NavLink } from "./NavLink";

function MobileNavLink({ href, children }) {
  return (
    <PopoverButton as={Link} href={href} className="block w-full p-2">
      {children}
    </PopoverButton>
  );
}

function MobileNavIcon({ open }) {
  return (
    <svg
      aria-hidden="true"
      className="h-3.5 w-3.5 overflow-visible stroke-slate-700"
      fill="none"
      strokeWidth={2}
      strokeLinecap="round"
    >
      <path
        d="M0 1H14M0 7H14M0 13H14"
        className={clsx(
          "origin-center transition",
          open && "scale-90 opacity-0"
        )}
      />
      <path
        d="M2 2L12 12M12 2L2 12"
        className={clsx(
          "origin-center transition",
          !open && "scale-90 opacity-0"
        )}
      />
    </svg>
  );
}

function MobileNavigation() {
  return (
    <Popover>
      {({ isOpen }) => (
        <>
          <PopoverButton
            className="relative z-10 flex h-8 w-8 items-center justify-center [&:not(:focus-visible)]:focus:outline-none"
            aria-label="Toggle Navigation"
          >
            {({ open }) => <MobileNavIcon open={open} />}
          </PopoverButton>
          <Transition>
            <TransitionChild
              as={Fragment}
              enter="duration-150 ease-out"
              enterFrom="opacity-0"
              enterTo="opacity-100"
              leave="duration-150 ease-in"
              leaveFrom="opacity-100"
              leaveTo="opacity-0"
            >
              <Popover.Overlay className="fixed inset-0 bg-slate-300/50" />
            </TransitionChild>
            <TransitionChild
              as={Fragment}
              enter="duration-150 ease-out"
              enterFrom="opacity-0 scale-95"
              enterTo="opacity-100 scale-100"
              leave="duration-100 ease-in"
              leaveFrom="opacity-100 scale-100"
              leaveTo="opacity-0 scale-95"
            >
              <PopoverPanel
                as="div"
                className="absolute inset-x-0 top-full mt-4 flex origin-top flex-col rounded-2xl bg-white p-4 text-lg tracking-tight text-slate-900 shadow-xl ring-1 ring-slate-900/5"
              >
                <MobileNavLink href="#features">Features</MobileNavLink>
                <MobileNavLink href="#testimonials">Testimonials</MobileNavLink>
                <MobileNavLink href="#pricing">Pricing</MobileNavLink>
                <hr className="m-2 border-slate-300/40" />
                <MobileNavLink href="/login">Sign in</MobileNavLink>
              </PopoverPanel>
            </TransitionChild>
          </Transition>
        </>
      )}
    </Popover>
  );
}

export function Header() {
  return (
    <header className="py-10">
      <Container>
        <nav className="relative z-50 flex justify-between">
          <div className="flex items-center md:gap-x-12">
            <a href="#" aria-label="Home">
              <Logo className="h-10 w-auto" />
            </a>
            <div className="hidden md:flex md:gap-x-6">
              <NavLink href="#features">Features</NavLink>
              <NavLink href="#testimonials">Testimonials</NavLink>
              <NavLink href="#pricing">Pricing</NavLink>
            </div>
          </div>
          <div className="flex items-center gap-x-5 md:gap-x-8">
            <div className="hidden md:block">
              <NavLink href="/login">Sign in</NavLink>
            </div>
            <Button href="/register" color="blue">
              <span>
                Get started <span className="hidden lg:inline">today</span>
              </span>
            </Button>
            <div className="-mr-1 md:hidden">
              <MobileNavigation />
            </div>
          </div>
        </nav>
      </Container>
    </header>
  );
}

I ended up having to add the following to the astro.config.mjs to make the project build successfully.

export default defineConfig({
  integrations: [solidJs(), tailwind()],
  vite: {
    ssr: {
      noExternal: ["solid-headless", "solid-use"],
    },
  },
});

Missing `Tab.Panels` component

I have been loving this library but I could not find an alternative to Table.Panels. Does one exist that I am just missing or is this something that has not yet been added to the libray?

Rebranding

Thinking of rebranding this library into a new one, probably a catchy name that reminds us of Headless UI and Solid. solid-headless isn't really helpful at all and I want this library to be distinct from the existing Headless UI (by Adam Wathan, great author) because of the library's scope and features.

A few dropdown issues

First off, thanks for making a great start with these components. I appreciate it is early days.

I've just started doing some testing based on the Dropdown menu example and have made a few notes:

  1. With the menu open, pressing up/down on a long page causes the page to scroll (in addition to selecting the next menu item).
  2. Also related to up/down, it seems disabled menu items aren't skipped.
  3. Also, when pressing escape, should the PopoverButton get focused? The doesn't seem to be what happens at the moment.
  4. It also seems common behaviour for the Dropdown to automatically close itself when a non-disabled item is chosen (clicked/Enter/Space)

Hide PopoverPanel on href/link click

Hi,

I have this component

function ProfileButton() {
    const { logged, user, login, logout } = useAuth()

    return <Show
            when={logged() && user()}
            fallback={<LoginIcon class="h-10 w-10 rounded-md cursor-pointer hover:bg-white hover:bg-opacity-25 p-1" onClick={login}/>}
        >
            <Popover class="relative text-dark" defaultOpen={false}>
                {({ isOpen }) => (
                    <>
                        <div>
                            <PopoverButton class="menu-btn">
                                <ChevronUp class={`${!isOpen() && 'rotate-180'} w-6 h-6 text-dark self-center ml-1 sm:block hidden transition ease-out duration-100`}/>
                                <p class='mx-2 text-lg font-medium sm:block hidden'>{user().username}</p>
                                <img class="sm:h-10 sm:w-10 h-12 w-12 rounded-full" src={`https://cdn.discordapp.com/avatars/${user().id}/${user().avatar}.png?size=512`} alt="profile"/>
                            </PopoverButton>
                        </div>
                        <Transition
                            show={isOpen()}
                            enter="transition ease-out duration-100"
                            enterFrom="transform opacity-0 scale-95"
                            enterTo="transform opacity-100 scale-100"
                            leave="transition ease-in duration-75"
                            leaveFrom="transform opacity-100 scale-100"
                            leaveTo="transform opacity-0 scale-95"
                        >
                            <PopoverPanel class="nav-menu">
                                <Menu>
                                    <MenuItem>
                                        <A href="/profile" class="nav-menu-item">Your Profile</A>
                                        {/* <A href="/profile/connections" class="nav-menu-item">Connections</A> */}
                                        <Show when={user().admin}>
                                            <A href="/admin/premium" class="nav-menu-item">Admin Panel</A>
                                        </Show>
                                    </MenuItem>
                                    <MenuItem>
                                        <div onClick={logout} id="logout-btn">Sign Out</div>
                                    </MenuItem>
                                </Menu>
                            </PopoverPanel>
                        </Transition>
                    </>
                )}
            </Popover>
        </Show>
}

I want the to hide/close the popover panel when an component is clicked (from solid-router redirect/navigate) how can I achieve that?

also on the vercel page are only 4 components shown? is that correct? or is it wrong link?
and lastly, is there a discord server for this package?

thanks

1.0 release

This issue thread will serve as the list of goals for the major release.

  • Rebrand solid-headless (#5). The following are the candidates:
    • polyui - poly-ui is already used
    • amorphous - already used
    • morphous - ready to use
    • malleable - already used
    • pliable - already used
    • morph-ui - already used
  • Finish the docs (#2)
  • Provide solutions for known issues in other frameworks (e.g. Astro, Solid Start) (#6)
  • Split components by entry points
  • Pre-compile or write the code with no JSX to allow working with Vite-based SSR (#6)

`HTMLElement is not defined` when using SSR, render function & `createResource`

I don't know if this is actually related to solid-headless, or some other weird internals of vite / solid-start, but in this minimal example, variant A causes the server-side rendering to crash, while variant B renders successfully. If I remove the PopoverButton, or use a component with no render function (e.g. Alert), there's also no issue.

export function Test(): JSX.Element {
    const [ foo ] = createResource(async () => { return "foo"; }); // variant A
    const [ foo ] = createSignal("foo"); // variant B
    return (
        <>
            <div>{foo()}</div>
            <Popover defaultOpen={false}>
                {() => (
                    <>
                        <PopoverButton>
                            <span>Open</span>
                        </PopoverButton>
                    </>
                )}
            </Popover>
    </>
    );
}

Stacktrace:

An unhandled error occured: ReferenceError: HTMLElement is not defined
    at FocusStartPoint.load (/Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected][email protected]/node_modules/solid-headless/dist/cjs/development/index.cjs:1910:39)
    at Array.<anonymous> (/Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected][email protected]/node_modules/solid-headless/dist/cjs/development/index.cjs:1906:12)
    at cleanNode (/Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:118:68)
    at Array.<anonymous> (/Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:387:19)
    at cleanNode (/Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:118:68)
    at Object.get children [as children] (/Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:586:20)
    at /Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:187:35
    at /Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:148:49
    at createMemo (/Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:77:9)
    at children (/Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:148:16)
    at /Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:187:14
    at createMemo (/Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:77:9)
    at provider (/Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:183:12)
    at createComponent (/Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:283:15)
    at /Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:583:14
    at runWithOwner (/Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:159:12)
    at runSuspense (/Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:582:12)
    at Object.completed (/Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:572:19)
    at notifySuspense (/Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:541:32)
    at /Users/.../sideprojects/solidstarttest/node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/server.cjs:484:9
    at processTicksAndRejections (node:internal/process/task_queues:96:5)```

Add a demo page

Hi !

Awesome work, though the one thing that is missing is a page where we can actually try the components. I saw the examples directory but a page hosted on vercel/netlify/cloudflare/... would be much better.

Thanks a lot for your work

Popover transition enter doesn't work.

I noticed this trying to port something form TailwindUI, but it shows up in the Popover example as well. It's easier to notice if you make the duration like duration-1000.

Modal allows background text selection & scroll

When the modal window is open, the user can still scroll the background content (via the mouse wheel) or select all text (via Ctrl+A).

To minimally reproduce this:

  1. open https://solid-headless.vercel.app/preview/dialog
  2. open dev tools and add a lot of text after the button html code
  3. open the modal window
  4. try scrolling or pressing Ctrl+A to select all the text

The issue might be related to tailwindlabs/headlessui#1056
Also maybe it has something to do with the fact that I am using astro js, but I do not think so.

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.