Coder Social home page Coder Social logo

gavinjoyce / ember-headlessui Goto Github PK

View Code? Open in Web Editor NEW
92.0 16.0 34.0 6.29 MB

Home Page: https://gavinjoyce.github.io/ember-headlessui/

License: Other

JavaScript 87.28% HTML 0.55% Handlebars 11.30% CSS 0.02% TypeScript 0.85%
hacktoberfest emberjs ember-addon ember hacktoberfest2021

ember-headlessui's Introduction

ember-headlessui

ember-headlessui Actions Status

This is a work-in-progress implementation of:

A set of completely unstyled, fully accessible UI components for Ember.js, designed to integrate beautifully with Tailwind CSS.

ember-headlessui

Installation

ember install ember-headlessui

Usage

Todo..

Contributing

See the Contributing guide for details.

License

This project is licensed under the MIT License.

ember-headlessui's People

Contributors

achambers avatar alexlafroscia avatar aoifehannigan avatar balinterdi avatar bertdeblock avatar dmcnamara-eng avatar ember-tomster avatar far-fetched avatar gavinjoyce avatar herzzanu avatar mansona avatar mateusalexandre avatar mofiebiger avatar nullvoxpopuli avatar roomman avatar rreckonerr avatar samharnack 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

Watchers

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

ember-headlessui's Issues

✨ `<Dialog>`Add support for nested dialogs

Summary

As react and vue packages support nested dialogs, we should introduce the same concept.

example in react

The concept assumes that a parent dialog cannot be closed when at least one child dialog is opened. By clicking outside (ESC, on overlay) we only close the dialog which was opened lastly (leaf).

Detailed design

To achieve this goal, we need sort of counter / state in each dialog, which will keep track of information that at least one children dialog is opened. For communication between parent - child, vue uses concept of inject/provider, link.
In practice they don't have to pass explicitly properties to child component in template.
As in ember we don't have similar mechanism, and we would like to keep headless API as much unified as possible, I propose to use yield emberism. We can yield another value from <Dialog> component, e.g. Nested and then we can pass this.element to its child. Example:

{{yield (hash
   isOpen=@isOpen
   onClose=@onClose
   Overlay=(
    component "dialog/-overlay"
    ...
   )
   ...       
   Nested=(component "dialog" parentDialog=this)
)}}

then nested dialog use:

<Dialog
      @isOpen={{state.isOpen}}
      @onClose={{set state.isOpen false}}
      as |dialog|
 >
  Parent dialog
  <dialog.Nested>
     child dialog
  </dialog.Nested>
</Dialog>

Drawbacks

API is not 100% match, compare to react and vue where they used just <Dialog> inside another <Dialog>

Alternatives

I was thinking about service injected to all dialogs, then when a component is mounted, the dialog is registered in service. Each time when new dialog is registered, the service iterates on opened dialogs and checks relationship parent - child. Open question is: as long as we use portals (dialogs are render as siblings) we cannot rely on DOM nodes, native events for checking parent - child. @achambers you said about some solution for this problem - I cannot remember details - can you remind me ?

@GavinJoyce @alexlafroscia
Would be wonderful to see your voices here.

Rename to `ember-headlessui`

The React and Vue versions will be known as @headlessui/react and @headlessui/vue.

If we were to move this addon into the @headlessui monorepo in future, @headlessui/ember will be the correct name. Before then, we should use ember naming patterns. gavinjoyce/ember-headlessui probably makes the most sense

Support Ember v5

It looks like this addon only supports Ember v3 and v4. It would be awesome to update this to support v5.

Build is broken due to external dependency hell

ERR_PNPM_PEER_DEP_ISSUES  Unmet peer dependencies

ember-headlessui
└─┬ release-it
  └─┬ @octokit/rest
    └─┬ @octokit/plugin-paginate-rest
      └── ✕ unmet peer @octokit/core@>=4: found 3.6.0 in @octokit/rest

Not compatible with ember 4

This is due to the use of @ember/render-modifiers.

we'll need to rip that out.

only 3.22 capabilities are allowed:
image

`<Transition>` with `<Dialog>` breaks clicking the overlay to close

I noticed when perusing the examples today that the animation modal dialog example would not allow me to click outside of it to close the dialog. The non-animated one seems to work fine, and for the animated one if I remove the second <t.Child> it will also work.

I wonder if we need to pass different parents to the transition or something? This is a complex z-index type of issue, since the overlay is nested in one parent and the modal content is nested in another and there is some fixed positioning.

I've been trying to debug this for awhile, but cannot seem to find a workaround. Perhaps we could use something like https://github.com/zeppelin/ember-click-outside and put that modifier on the modal dialog content, instead of handling the click on the overlay?

I'm happy to explore this as a solution if folks think it would be good!

Update `Transition` component to allow to be renderless

I've noticed that the Transition component always renders an element (div by default). There are a lot of Tailwind UI examples that pass a React Fragment component to Transition so it doesn't render anything. Is that possible with Ember? I mainly ask because it's more difficult to implement Tailwind UI code examples with the Ember version since you end up with an extra element in the hierarchy that messes with styling.

Here is an example from Tailwind UI for building a select menu:

<Transition
  show={open}
  as={Fragment}
  leave="transition ease-in duration-100"
  leaveFrom="opacity-100"
  leaveTo="opacity-0"
>

Click on `dialog.Overlay` does not close the modal

... in case dialog.Overlay a wrapped in t.Child.

Reproduction:

  1. Navigate to https://gavinjoyce.github.io/ember-headlessui/dialog/dialog-modal-with-transition
  2. Open the dialog
  3. Click on the overlay
  4. See that the Dialog's onClose handler is not called

<Dialog
class='fixed inset-0 z-10 overflow-y-auto'
@as='div'
@isOpen={{true}}
@onClose={{set this 'isOpen' false}}
as |dialog|
>
<div
class='flex items-end justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0'
>
<t.Child
@appear={{true}}
@enter='ease-out duration-700'
@enterFrom='opacity-0'
@enterTo='opacity-100'
@leave='ease-in duration-200'
@leaveFrom='opacity-100'
@leaveTo='opacity-0'
>
<dialog.Overlay
class='fixed inset-0 transition-opacity bg-gray-500 bg-opacity-75'
/>
</t.Child>

Transitions are throwing errors in Fastboot

I'm seeing the following error from a Fastboot server:

There was an error running your app in fastboot. More info about the error: 
 ReferenceError: getComputedStyle is not defined
    at waitForTransition (webpack://solomon-website/./node_modules/ember-headlessui/utils/transition.js?:42:7)
    at AppliedClassNamesManager.setTransitionClassesTask (webpack://solomon-website/./node_modules/ember-headlessui/helpers/transition/applied-class-names.js?:122:79)
    at setTransitionClassesTask.next (<anonymous>)
    at GeneratorState.step (webpack://solomon-website/./node_modules/ember-concurrency/-private/external/generator-state.js?:27:35)
    at TaskInstanceExecutor.generatorStep (webpack://solomon-website/./node_modules/ember-concurrency/-private/external/task-instance/executor.js?:285:42)
    at TaskInstanceExecutor.handleResolvedContinueValue (webpack://solomon-website/./node_modules/ember-concurrency/-private/external/task-instance/executor.js?:149:27)
    at TaskInstanceExecutor.proceedSync (webpack://solomon-website/./node_modules/ember-concurrency/-private/external/task-instance/executor.js?:112:12)
    at eval (webpack://solomon-website/./node_modules/ember-concurrency/-private/external/task-instance/executor.js?:99:31)
    at invokeWithOnError (/var/folders/cp/mp10cjhd1xdf8wg5gk7s9g500000gn/T/broccoli-65417B5xDdFci2l2P/out-586-packager_runner_embroider_webpack/assets/backburner.js:360:1)
    at Queue.flush (/var/folders/cp/mp10cjhd1xdf8wg5gk7s9g500000gn/T/broccoli-65417B5xDdFci2l2P/out-586-packager_runner_embroider_webpack/assets/backburner.js:241:1)
    at DeferredActionQueues.flush (/var/folders/cp/mp10cjhd1xdf8wg5gk7s9g500000gn/T/broccoli-65417B5xDdFci2l2P/out-586-packager_runner_embroider_webpack/assets/backburner.js:447:1)
    at Backburner._end (/var/folders/cp/mp10cjhd1xdf8wg5gk7s9g500000gn/T/broccoli-65417B5xDdFci2l2P/out-586-packager_runner_embroider_webpack/assets/backburner.js:999:1)
    at Backburner.end (/var/folders/cp/mp10cjhd1xdf8wg5gk7s9g500000gn/T/broccoli-65417B5xDdFci2l2P/out-586-packager_runner_embroider_webpack/assets/backburner.js:729:1)
    at Backburner._runExpiredTimers (/var/folders/cp/mp10cjhd1xdf8wg5gk7s9g500000gn/T/broccoli-65417B5xDdFci2l2P/out-586-packager_runner_embroider_webpack/assets/backburner.js:1136:1)
    at listOnTimeout (node:internal/timers:557:17)
    at processTimers (node:internal/timers:500:7)

@alexlafroscia, as the author of Transition, how would you feel about adding (or reviewing a PR to add) some guarding for Fastboot? Is that appropriate or should it be for addon consumers to work that out?

BUG: click outside <Dialog /> should be allowed

Usecase

I'm working on adding notifications to the app, and it should be possible to click/close the notification without closing the current <Dialog />

Problem

At the moment any click outside <Dialog /> will trigger @onclose. The only exception is child dialogs spawned by the modal (handled by dialog-stack-provider service).

This behavior used to be the same in @headlessui/[email protected], but since @headlessui/[email protected] it's possible to click outside the dialog without triggering the @onclose action IF the "outsider" is wrapped with headlessui <Portal /> component.

Workaround

As a workaround, I extended the dialog.js component as follows

import { action } from '@ember/object';
import EmberHeadlessUiDialog from 'ember-headlessui/components/dialog.js';

export default class extends EmberHeadlessUiDialog {
  @action
  allowOutsideClick(e) {
    const target = e.target;

    if (target && target.tagName !== 'BODY') {
      this.outsideClickedElement = target;
    }

    // 👇
    // this.onClose();

    return true;
  }
}

Resources

Template lint errors in test files

I'm adding some <Menu> tests and I am seeing ember-template-lint(prettier) errors in VSCode, wherever a template is rendered into a test file:

Screenshot 2021-10-04 at 10 18 52

Assuming others are not seeing these errors(?), and it's an issue with my editor's setup rather than the lint config, what extension are others using for formatting code in VSCode? Hopefully if I mirror, the problem will go away 🤞🏻

Not Compatible with `ember-click-outside-modifier`

I have ember-click-outside-modifier installed in my app. It seems as though ember-headlessui breaks it, due to both packages introducing a click-outside modifier that are implemented slightly differently.

ember-click-outside-modifier passes the event object along as an argument to the action that it calls, while the modifier provided by this addon does not. This addon's modifier is taking precedence (maybe due to it coming alphabetically after the other addon?), so actions in my app that expect to receive an event argument no longer do.

I see a few decent options available, any of which I'm happy to open a PR for; I just wanted to get some feedback on what would be most desirable first!

  • Ditch the local modifier and add ember-click-outside-modifier as a dependency. This is a nice win for people already using both, and allows each package to do their respective jobs without overlap.
  • Avoid registering the click-outside modifier that ships with this addon to the global registry at all, instead opting to make it "local" to the components where it's used. This introduces duplication for folks that have ember-click-outside-modifier installed, but ensures that they don't compete
  • Forward the event argument along to the action so that the API for each modifier is the same. Then this helper could be used and ember-click-outside-modifier uninstalled
  • Configure ember-headlessui to be run "before" ember-click-outside-modifier so the slightly-more-featureful version of the modifier is actually used in the app.

In the meantime, in case anyone else runs into this issue, I'm going to resolve it with a yarn patch to apply the "before" config to my local installation of the addon.

Bump `@embroider/macros`

The version currently used by ember-headlessui causes a build error for me post install.

ember-cli: 4.1.1
node: 16.13.2
os: darwin arm64

Auto-Deploy Docs Site to GitHub Pages

The docs site for ember-headlessui is really nice! It would be great to make it easier for people checking out this addon to see it.

Would you be interested in having it automatically deploy to GitHub Pages? I do that for a number of my addons, like ember-steps, and the configuration for it is pretty easy! I would be happy to set that up if you're interested.

`Dialog` breaks Ember-dispatched `click` events

While migrating our app's modal to use Dialog instead of ember-modal-dialog, I ran into something of an odd bug! Many click handlers in our app suddenly broke.

As it turns out, any click event dispatched through Ember's internal event-delegation mechanism are no longer fired. This includes Ember.Component classes that define the click method (which is attached to the root element) and any usage of the {{action}} modifier with the default configuration.

This ends up making sense, sort of -- Dialog stops propagation of the click event outside of the Dialog element. The Ember-event-dispatching system listens for events on the app root (.ember-application) and then dynamically dispatches the event back to the listener by looking at event.target. Because the event is not allowed to propagate up to .ember-application, the click events end up "blocked", even if they come from inside Dialog.

Note: this does not impact the {{on}} modifier, so modern code is fine, but it's difficult to integrate this component into a legacy codebase without doing a ton of refactoring to remove every usage of Ember's event-dispatching system related to click events.

Additionally, because the Dialog is projected directly beneath the .ember-application element, stopping propagation does not have the probably-intended affect of preventing click events "behind" the Dialog, since the app "behind" the Dialog is actually adjacent to it. So really the only events that have propagation cancelled on them are the ones that are headed for .ember-application and Ember's event delegation system.

Whether and how to fix this might be tricky... I think that just removing the propagation-stopping might be fine, but there might be something more fancy we can do to achieve the same result.

Closing issue when using powerselect inside a dialog

When using ember-power-select inside the dialog, the dialog will trigger the @onclose event whenever clicking on the dropdown of the power select. The dropdown of the power-select gets rendered inside the 'ember-basic-dropdown-wormhole' div that is right under the body tag.

I tried to dig into it a bit and found out that it is caused by the headlessui-focus-trap modifier. Specifically allowOutsideClick that always calls the this.onClose(). I am rather unsure why this is the case for a dialog component as I am guessing that notification toasts for example will probably also close the dialog unless if it is rendered inside the <Dialog />'component.

{{headlessui-focus-trap
focusTrapOptions=(hash
initialFocus=@initialFocus
allowOutsideClick=this.allowOutsideClick
setReturnFocus=this.setReturnFocus
)
}}

@action
allowOutsideClick(e: MouseEvent) {
let target = e.target as HTMLElement;
if (target && target.tagName !== 'BODY') {
this.outsideClickedElement = target;
}
this.onClose();
return true;
}

Docs for using Tailwind

Since there is a high liklihood people are using headlessui with tailwind, maybe we should have docs on how to set up tailwind?

ember-cli-postcss isn't a great solution right now (esp for embroider), and overall, the ember-cli style pipeline is full of dragons / unintuitiveness.

So, We'll need to verify all the ember-integrated options, such as:
https://github.com/NullVoxPopuli/limber/blob/main/frontend/ember-cli-build.js#L32-L37

But, by far the easiest thing to do, which I kinda beat myself up about for not trying sooner, is to just build tailwind totally separate from ember (JIT, tailwind 3, etc):
https://github.com/NullVoxPopuli/limber/pull/375/files#diff-1b9c4fc3d51e8aff3ccf57346453ff963f824fb8e61a2c6fb32dc969377b6019

(It kinda looks like a lot, because I mentioned all the steps, but these steps are far less than what's needed to get ember-cli-postcss working with embroider (It doesn't really) -- the thing to do with embroider right now is to use a postcss loader with webpack... but, webpack configs have their own set of problems)

`<Menu>` doesn't close items dropdown on `Escape`

We should make the <Menu> dropdown close when Escape is hit. I suspect it should go here.

onKeydown(event) {
switch (event.key) {
// Ref: https://www.w3.org/TR/wai-aria-practices-1.2/#keyboard-interaction-12
case 'Enter':
if (this.args.activeItem) {
this.args.activeItem.element.click();
}
this.args.closeMenu();
break;
case 'ArrowDown':
return this.args.goToNextItem();
case 'ArrowUp':
return this.args.goToPreviousItem();
}
}

Implement `Transition` component

Just creating this as a way of letting people know I am working on this one in my free time, to avoid two people starting on it without knowing the other has something in progress!

🐛 `<Menu>` focus button after close items

Clicking outside the menu items should close menu, and focus button.

Test

Code

Bug:

Steps to reproduce the problem

  • menu is rendered on the dialog,
  • clicking outside dialog (close dialog and menu)

What went wrong?

  • Button doesn't exist and cannot be focused.

Captura de pantalla 2021-06-15 a las 9 52 05

Grabacion.de.pantalla.2021-06-15.a.las.10.01.22.mov

What is the expected behavior?

Does not focus anything ?

Preventing default key event behaviours for menu

Hi all, thanks for this add-on. It's been great to see this develop and to learn some of the newer ways of developing with Ember.

Just a quick question on the keyboard events for <Menu>. Is there a rationale for not preventing the default behaviour for the up and down arrow events? They are causing some jerky scrolling, when the containing element has a height greater than the viewport.

The following additions resolve it for me, but I'm unclear if this creates new issues in terms of accessibility? Can't see anything in the w3 docs to suggest it would.

  @action
  onKeydown(event) {
    switch (event.key) {
      // Ref: https://www.w3.org/TR/wai-aria-practices-1.2/#keyboard-interaction-12
      case 'Enter':
        if (this.args.activeItem) {
          this.args.activeItem.element.click();
        }
        this.args.closeMenu();
        break;
      case 'ArrowDown':
+     event.preventDefault();
        return this.args.goToNextItem();
      case 'ArrowUp':
+     event.preventDefault();
        return this.args.goToPreviousItem();
      default:
        if (event.key.length === 1) {
          return this.args.search(event.key);
        }
    }
  }

Would you accept a PR to handle these events?

Which components are public API?

Traditionally, all components used must have app-re-exports, but with Ember 3.25+, we can get around that -- and there may be some internal organization benefits to having total control over implementation details if we can ensure that certain components, like, list-box/button is private (in this case, the only intended use is through the yield of the listbox component, which pre-wires up a bunch of stuff -- which maybe we want to restrict what folks can do?)

Focus of menu button on close seems incorrect.

When I click out side of the menu, say on another button, when the menu closes it sets focus back on the menu button whereas I'd expect the focus to remain on the element I just clicked on. I'm not clear if it makes sense to refocus the button at all. Maybe it does under some circumstances but unsure what they are. In short though, the example below doesn't make sense so there is some improvement that could be made:

In this screencast I'm doing the following:

  1. Click on the 3rd dropdown
  2. Click on the 2nd dropdown
  3. Press up/down keys which opens the 3rd dropdown again.
Screen.Recording.2021-06-18.at.10.36.56.mov

I think the offending code is here:

next(() => {
document.getElementById(this.buttonGuid).focus();
});

The active `Listbox` option doesn't scroll into view when using keyboard arrows

It seems that when you render a Listbox with enough items where the menu scrolls, using the keyboard arrows to navigate through the options list won't scroll the active option into view.

To reproduce, I copied the contents of the listbox-with-transition.hbs example and added enough options so that the menu is scrollable. When I navigate the menu using the arrow keys, the active option isn't scrolled to. I have to manually use my mouse to scroll it into view.

Use `@as` to customize root element

Some of our component use a @tagName argument to customize the root element, while others use @as

We should use @as across all components for two main reasons:

  1. Consistency with the React/Vue implementations
  2. Accepting a component rather than just a string would be nice, like Menu::ItemElement does (to support LinkTo)
    {{#let
    (if this.tagNameIsComponent @tagName (element (or @tagName 'a')))
    as |Tag|
    }}

Note: this will be a breaking change for the components currently configured to use @tagName

TypeError: can't convert value to string

UPDATE

It appeared to be a simple naming collision! The as |t| and the t helper part.
Took me a while to figure that out 🙈

<Transition @show={{true}} as |t|>
  <TestMe>{{t 'some.translation'}}</TestMe>
</Transition>

Original issue descrition

When component with {{yield}} is wrapped in Transition and yields helper, e.g. {{t 'some.translation'}}

This will throw

// test-me.hbs
{{yield}}

// application.hbs
<Transition @show={{true}}>
  <TestMe>{{t 'some.translation'}}</TestMe>
</Transition>

These will not throw

// test-me.hbs
{{yield}}

// application.hbs
<Transition @show={{true}}>
  <TestMe>{{this.someTranslation}}</TestMe>
</Transition>

// application.js
get someTranslation() {
  return this.intl.t('some.translation');
}
// test-me.hbs
{{yield}}

// application.hbs
<Transition @show={{true}}>
  <TestMe>Some content</TestMe>
</Transition>

Implement `<Listbox>` component

I have already implemented it in one of my pet project. There is a lack of accessibility features there, but at least it would be a good starting point. I just let you know to not repeat work. I can push it forward in spare time.

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.