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

Open closed state #466

Merged
merged 18 commits into from
May 3, 2021
Merged

Open closed state #466

merged 18 commits into from
May 3, 2021

Conversation

RobinMalfait
Copy link
Collaborator

@RobinMalfait RobinMalfait commented Apr 29, 2021

It might not be immediately obvious what this does, but essentially it allows us to communicate between our components without the need of being explicit. For example, here is a before:

import { Menu, Transition } from "@headlessui/react";

function MyDropdown() {
  return (
    <Menu>
      {({ open }) => (
        <>
          <Menu.Button>More</Menu.Button>

          {/*
            Use the Transition component + open render prop to add transitions.
          */}
          <Transition
            show={open}
            enter="transition duration-100 ease-out"
            enterFrom="transform scale-95 opacity-0"
            enterTo="transform scale-100 opacity-100"
            leave="transition duration-75 ease-out"
            leaveFrom="transform scale-100 opacity-100"
            leaveTo="transform scale-95 opacity-0"
          >
            {/* Don't forget to mark your Menu.Items as static! */}
            <Menu.Items static>
              <Menu.Item>{/* ... */}</Menu.Item>
              {/* ... */}
            </Menu.Items>
          </Transition>
        </>
      )}
    </Menu>
  );
}

This is great, it all still works, but we can also simplify it to this:

import { Menu, Transition } from "@headlessui/react";

function MyDropdown() {
  return (
    <Menu>
      <Menu.Button>More</Menu.Button>

      {/* Use the Transition component. */}
      <Transition
        enter="transition duration-100 ease-out"
        enterFrom="transform scale-95 opacity-0"
        enterTo="transform scale-100 opacity-100"
        leave="transition duration-75 ease-out"
        leaveFrom="transform scale-100 opacity-100"
        leaveTo="transform scale-95 opacity-0"
      >
        <Menu.Items>
          <Menu.Item>{/* ... */}</Menu.Item>
          {/* ... */}
        </Menu.Items>
      </Transition>
    </Menu>
  );
}

A few things to notice:

  1. No need to use a render function to destructure the open prop.
  2. This means that we don't have to introduce a fragment.
  3. We don't need to control the show prop on the Transition component.
  4. We don't need the static prop on the Menu.Items.

If you have components that work together with other components / libraries then you can still control all of this.


Relying on a shared context allows us to not be coupled to other components. This in turns means that once we start working on the build output and tree-shakings that we don't have to worry about all these interconnected components.

@vercel
Copy link

vercel bot commented Apr 29, 2021

This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployments, click below or on the icon next to each commit.

headlessui-react – ./packages/@headlessui-react

🔍 Inspect: https://vercel.com/tailwindlabs/headlessui-react/He6yRLykAjWSNstWPvN1UfJizD2r
✅ Preview: https://headlessui-react-git-open-closed-state-tailwindlabs.vercel.app

headlessui-vue – ./packages/@headlessui-vue

🔍 Inspect: https://vercel.com/tailwindlabs/headlessui-vue/37pAYKKQ48epuwuYfPo5ZfBrL3eo
✅ Preview: https://headlessui-vue-git-open-closed-state-tailwindlabs.vercel.app

@mudssrali
Copy link
Contributor

It might not be immediately obvious what this does, but essentially it allows us to communicate between our components without the need of being explicit. For example, here is a before:

import { Menu, Transition } from "@headlessui/react";

function MyDropdown() {
  return (
    <Menu>
      {({ open }) => (
        <>
          <Menu.Button>More</Menu.Button>

          {/*
            Use the Transition component + open render prop to add transitions.
          */}
          <Transition
            show={open}
            enter="transition duration-100 ease-out"
            enterFrom="transform scale-95 opacity-0"
            enterTo="transform scale-100 opacity-100"
            leave="transition duration-75 ease-out"
            leaveFrom="transform scale-100 opacity-100"
            leaveTo="transform scale-95 opacity-0"
          >
            {/* Don't forget to mark your Menu.Items as static! */}
            <Menu.Items static>
              <Menu.Item>{/* ... */}</Menu.Item>
              {/* ... */}
            </Menu.Items>
          </Transition>
        </>
      )}
    </Menu>
  );
}

This is great, it all still works, but we can also simplify it to this:

import { Menu, Transition } from "@headlessui/react";

function MyDropdown() {
  return (
    <Menu>
      <Menu.Button>More</Menu.Button>

      {/* Use the Transition component. */}
      <Transition
        enter="transition duration-100 ease-out"
        enterFrom="transform scale-95 opacity-0"
        enterTo="transform scale-100 opacity-100"
        leave="transition duration-75 ease-out"
        leaveFrom="transform scale-100 opacity-100"
        leaveTo="transform scale-95 opacity-0"
      >
        <Menu.Items>
          <Menu.Item>{/* ... */}</Menu.Item>
          {/* ... */}
        </Menu.Items>
      </Transition>
    </Menu>
  );
}

A few things to notice:

  1. No need to use a render function to destructure the open prop.
  2. This means that we don't have to introduce a fragment.
  3. We don't need to control the show prop on the Transition component.
  4. We don't need the static prop on the Menu.Items.

If you have components that work together with other components / libraries then you can still control all of this.

Relying on a shared context allows us to not be coupled to other components. This in turns means that once we start working on the build output and tree-shakings that we don't have to worry about all these interconnected components.

The example you shared, it totally makes sense. But how we will handle those cases in which we actually need open e.g. Changing Icon based on open value?

@RobinMalfait
Copy link
Collaborator Author

@mudssrali this code is in addition to the existing code. There are no breaking changes here, so you can still do what you could do before! 😄
Hence this part This is great, it all still works, but we can also simplify it to this:

@mudssrali
Copy link
Contributor

@mudssrali this code is in addition to the existing code. There are no breaking changes here, so you can still do what you could do before!
Hence this part This is great, it all still works, but we can also simplify it to this:

Thanks for clearing up! 😃

@RobinMalfait RobinMalfait merged commit 88aa8e4 into develop May 3, 2021
@RobinMalfait RobinMalfait deleted the open-closed-state branch May 3, 2021 10:52
@RobinMalfait RobinMalfait restored the open-closed-state branch May 3, 2021 11:06
RobinMalfait added a commit that referenced this pull request May 3, 2021
* simplify examples by using the implicit open/closed state

* introduce Open/Closed context (React)

* use Open/Closed context in Dialog component (React)

* use Open/Closed context in Disclosure component (React)

* use Open/Closed context in Listbox component (React)

* use Open/Closed context in Menu component (React)

* use Open/Closed context in Popover component (React)

* use Open/Closed context in Transition component (React)

* introduce Open/Closed context (Vue)

* use Open/Closed context in Dialog component (Vue)

* use Open/Closed context in Disclosure component (Vue)

* use Open/Closed context in Listbox component (Vue)

* use Open/Closed context in Menu component (Vue)

* use Open/Closed context in Popover component (Vue)

* use Open/Closed context in Transition component (Vue)

* use a ref in the Description comopnent

This allows us to update the ref and everything should work after that.
Currently we only saw the "current" state.

* add more Vue examples

* update changelog
@RobinMalfait RobinMalfait deleted the open-closed-state branch May 3, 2021 11:11
@beeirl
Copy link

beeirl commented May 28, 2021

@RobinMalfait getting the error A <Transition /> is used but it is missing a 'show={true | false}' prop. when using a Listbox component without setting show explicitly. Am I missing something?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants