-
-
\ No newline at end of file
diff --git a/docs/app/components/snippets/how-to-use-it-2.js b/docs/app/components/snippets/how-to-use-it-2.js
deleted file mode 100644
index 1af80ea0..00000000
--- a/docs/app/components/snippets/how-to-use-it-2.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import Component from '@glimmer/component';
-
-// eslint-disable-next-line ember/no-empty-glimmer-component-classes
-export default class extends Component {}
diff --git a/docs/app/components/snippets/how-to-use-it-3.gts b/docs/app/components/snippets/how-to-use-it-3.gts
new file mode 100644
index 00000000..a8745c18
--- /dev/null
+++ b/docs/app/components/snippets/how-to-use-it-3.gts
@@ -0,0 +1,11 @@
+import BasicDropdown from 'ember-basic-dropdown/components/basic-dropdown';
+
+
+
+ +
+
+
+
+
+
+
diff --git a/docs/app/components/snippets/how-to-use-it-3.hbs b/docs/app/components/snippets/how-to-use-it-3.hbs
deleted file mode 100644
index a984a39c..00000000
--- a/docs/app/components/snippets/how-to-use-it-3.hbs
+++ /dev/null
@@ -1,7 +0,0 @@
-
- +
-
-
-
-
-
\ No newline at end of file
diff --git a/docs/app/components/snippets/how-to-use-it-3.js b/docs/app/components/snippets/how-to-use-it-3.js
deleted file mode 100644
index 1af80ea0..00000000
--- a/docs/app/components/snippets/how-to-use-it-3.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import Component from '@glimmer/component';
-
-// eslint-disable-next-line ember/no-empty-glimmer-component-classes
-export default class extends Component {}
diff --git a/docs/app/components/snippets/installation-0.gts b/docs/app/components/snippets/installation-0.gts
new file mode 100644
index 00000000..b6ab96ce
--- /dev/null
+++ b/docs/app/components/snippets/installation-0.gts
@@ -0,0 +1,3 @@
+import BasicDropdownWormhole from 'ember-basic-dropdown/components/basic-dropdown-wormhole';
+
+
diff --git a/docs/app/components/snippets/installation-1.css.txt b/docs/app/components/snippets/installation-1.css.txt
new file mode 100644
index 00000000..9e55ae5a
--- /dev/null
+++ b/docs/app/components/snippets/installation-1.css.txt
@@ -0,0 +1 @@
+@import 'ember-basic-dropdown/vendor/ember-basic-dropdown.css';
diff --git a/docs/app/components/snippets/installation-1.js b/docs/app/components/snippets/installation-1.js
new file mode 100644
index 00000000..2457cd3c
--- /dev/null
+++ b/docs/app/components/snippets/installation-1.js
@@ -0,0 +1 @@
+import 'ember-basic-dropdown/styles';
diff --git a/docs/app/templates/snippets/installation-2.js b/docs/app/components/snippets/installation-2-snippet.js
similarity index 100%
rename from docs/app/templates/snippets/installation-2.js
rename to docs/app/components/snippets/installation-2-snippet.js
diff --git a/docs/app/components/snippets/no-trigger-1.gts b/docs/app/components/snippets/no-trigger-1.gts
new file mode 100644
index 00000000..abc0c048
--- /dev/null
+++ b/docs/app/components/snippets/no-trigger-1.gts
@@ -0,0 +1,25 @@
+import BasicDropdown from 'ember-basic-dropdown/components/basic-dropdown';
+import { on } from '@ember/modifier';
+
+
+
+
+
+
+
+
+
+ I don't like the button, I like the input.
+
+
+
diff --git a/docs/app/components/snippets/no-trigger-1.hbs b/docs/app/components/snippets/no-trigger-1.hbs
deleted file mode 100644
index b939efca..00000000
--- a/docs/app/components/snippets/no-trigger-1.hbs
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
- I don't like the button, I like the input.
-
-
\ No newline at end of file
diff --git a/docs/app/components/snippets/no-trigger-1.js b/docs/app/components/snippets/no-trigger-1.js
deleted file mode 100644
index 1af80ea0..00000000
--- a/docs/app/components/snippets/no-trigger-1.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import Component from '@glimmer/component';
-
-// eslint-disable-next-line ember/no-empty-glimmer-component-classes
-export default class extends Component {}
diff --git a/docs/app/components/snippets/no-trigger-2.gts b/docs/app/components/snippets/no-trigger-2.gts
new file mode 100644
index 00000000..9b2c5e2c
--- /dev/null
+++ b/docs/app/components/snippets/no-trigger-2.gts
@@ -0,0 +1,18 @@
+import BasicDropdown from 'ember-basic-dropdown/components/basic-dropdown';
+import basicDropdownTrigger from 'ember-basic-dropdown/modifiers/basic-dropdown-trigger';
+
+
+
+
+
+
+ I was opened with a custom trigger
+
+
+
diff --git a/docs/app/components/snippets/no-trigger-2.hbs b/docs/app/components/snippets/no-trigger-2.hbs
deleted file mode 100644
index dc4a13e0..00000000
--- a/docs/app/components/snippets/no-trigger-2.hbs
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
- I was opened with a custom trigger
-
-
\ No newline at end of file
diff --git a/docs/app/components/snippets/no-trigger-2.js b/docs/app/components/snippets/no-trigger-2.js
deleted file mode 100644
index 1af80ea0..00000000
--- a/docs/app/components/snippets/no-trigger-2.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import Component from '@glimmer/component';
-
-// eslint-disable-next-line ember/no-empty-glimmer-component-classes
-export default class extends Component {}
diff --git a/docs/app/components/snippets/overlays-1.gts b/docs/app/components/snippets/overlays-1.gts
new file mode 100644
index 00000000..27c37b11
--- /dev/null
+++ b/docs/app/components/snippets/overlays-1.gts
@@ -0,0 +1,11 @@
+import BasicDropdown from 'ember-basic-dropdown/components/basic-dropdown';
+
+
+
+ Click me
+
+
+ By default the overlays is black and transparent, but you can change that.
+
+
+
diff --git a/docs/app/components/snippets/overlays-1.hbs b/docs/app/components/snippets/overlays-1.hbs
deleted file mode 100644
index 049a8dc8..00000000
--- a/docs/app/components/snippets/overlays-1.hbs
+++ /dev/null
@@ -1,7 +0,0 @@
-
- Click me
-
-
- By default the overlays is black and transparent, but you can change that.
-
-
\ No newline at end of file
diff --git a/docs/app/components/snippets/overlays-1.js b/docs/app/components/snippets/overlays-1.js
deleted file mode 100644
index 1af80ea0..00000000
--- a/docs/app/components/snippets/overlays-1.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import Component from '@glimmer/component';
-
-// eslint-disable-next-line ember/no-empty-glimmer-component-classes
-export default class extends Component {}
diff --git a/docs/app/components/snippets/position-1.gts b/docs/app/components/snippets/position-1.gts
new file mode 100644
index 00000000..f14969ab
--- /dev/null
+++ b/docs/app/components/snippets/position-1.gts
@@ -0,0 +1,145 @@
+import { fn } from '@ember/helper';
+import { on } from '@ember/modifier';
+import Component from '@glimmer/component';
+import BasicDropdown from 'ember-basic-dropdown/components/basic-dropdown';
+import { eq, not } from 'ember-truth-helpers';
+import type {
+ HorizontalPosition,
+ VerticalPosition,
+} from 'ember-basic-dropdown/types';
+
+export default class extends Component {
+ horizontalPosition: HorizontalPosition = 'auto';
+ verticalPosition: VerticalPosition = 'auto';
+ buttonPosition = 'left';
+ renderInPlace = false;
+
+
+ horizontalPosition:
+ {{this.horizontalPosition}}
+ verticalPosition:
+ {{this.verticalPosition}}
+ buttonPosition:
+ {{this.buttonPosition}}
+
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/cookbook.gts b/docs/app/templates/public-pages/cookbook.gts
new file mode 100644
index 00000000..323168e6
--- /dev/null
+++ b/docs/app/templates/public-pages/cookbook.gts
@@ -0,0 +1,14 @@
+import { LinkTo } from '@ember/routing';
+
+
+
+
+
+ {{outlet}}
+
+
+
diff --git a/docs/app/templates/public-pages/cookbook.hbs b/docs/app/templates/public-pages/cookbook.hbs
deleted file mode 100644
index 6df5b198..00000000
--- a/docs/app/templates/public-pages/cookbook.hbs
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
- {{outlet}}
-
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/cookbook/index.gts b/docs/app/templates/public-pages/cookbook/index.gts
new file mode 100644
index 00000000..11744840
--- /dev/null
+++ b/docs/app/templates/public-pages/cookbook/index.gts
@@ -0,0 +1,78 @@
+import CodeExample from '../../../components/code-example';
+import NoTrigger1Component from '../../../components/snippets/no-trigger-1';
+import NoTrigger2Component from '../../../components/snippets/no-trigger-2';
+
+
+
Usage without trigger
+
+
+ Use the component without a trigger? Is that possible?
+
+
+
+ Yes, it is. You don't need to invoke the
+ <dropdown.trigger>
+ component to make the dropdown work. The yielded
+ dropdown
+ has actions you can invoke from any other item. Yes, it is. You don't need
+ to invoke the
+ <dropdown.trigger>
+ component to make the dropdown work. The yielded
+ dropdown
+ has actions you can invoke from any other item.
+
+
+
+ Sometimes you just cannot achieve what you want with this approach, so you
+ need to step down and wire things yourself.
+
+
+
+ If you invoke the
+ open
+ or
+ toggle
+ action from say, a button, the dropdown is going to open and will take as
+ the element to be anchored to element with
+ data-ebd-id="\{{dropdown.uniqueId}}-trigger".
+
+
+
+ What kind of things can you do with this?
+
+
+
+ By example, you can open or close the component when an item is clicked but
+ make the dropdown attach itself to an entirely different item in the page.
+
+
+
+ Let's create an input with a button. When that button is clicked the drodown
+ will open below the input, not not below the button that you clicked.
+
+
+
+
+
+
+
+ Remember that almost always you will want to use the provided trigger
+ component
+ <dropdown.trigger>
+ because it takes care of all the A11y, bindings and classes for you, but
+ when you can't it's good to know that you can still use your own markup and
+ wire things together yourself.
+
+
+
+ A good middle-ground is to apply the same trigger modifier that the
+ component uses, but to your element or component. It will get all of the
+ same a11y and bindings that the
+ <dropdown.trigger>
+ component gets.
+
- Ember Basic Dropdown can take a fair amount of configuration options on
- invocations to alter its default behavior.
-
-
-
- If you want all your dropdowns to behave on a certain way or have some certain
- classes? Repeating the same options over and over on your templates is boring.
-
-
-
- If you want to apply a configuration option to all your dropdowns there is no
- special way or key in the
- /config/environment.js
- you need to learn. Just use The Ember Way™.
-
-
-
- Inside your app's
- /app/components
- folder create a
- ember-basic-dropdown.js
- file:
-
-
-{{#let (get-code-snippet "system-wide-config-1-js.js") as |snippet|}}
-
-{{/let}}
-
-
- That's all. No new concepts to learn, just your usual ember-cli work flow.
-
-
-
- In this example the component is also named
- \{{basic-dropdown}}
- but using this approach you can create many components with different names
- that extend and customize the default one without modifying it.
-
-
-
- Usage without trigger >
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/cookbook/no-trigger.hbs b/docs/app/templates/public-pages/cookbook/no-trigger.hbs
deleted file mode 100644
index b5206e35..00000000
--- a/docs/app/templates/public-pages/cookbook/no-trigger.hbs
+++ /dev/null
@@ -1,72 +0,0 @@
-
Usage without trigger
-
-
- Use the component without a trigger? Is that possible?
-
-
-
- Yes, it is. You don't need to invoke the
- <dropdown.trigger>
- component to make the dropdown work. The yielded
- dropdown
- has actions you can invoke from any other item. Yes, it is. You don't need to
- invoke the
- <dropdown.trigger>
- component to make the dropdown work. The yielded
- dropdown
- has actions you can invoke from any other item.
-
-
-
- Sometimes you just cannot achieve what you want with this approach, so you
- need to step down and wire things yourself.
-
-
-
- If you invoke the
- open
- or
- toggle
- action from say, a button, the dropdown is going to open and will take as the
- element to be anchored to element with
- data-ebd-id="\{{dropdown.uniqueId}}-trigger".
-
-
-
- What kind of things can you do with this?
-
-
-
- By example, you can open or close the component when an item is clicked but
- make the dropdown attach itself to an entirely different item in the page.
-
-
-
- Let's create an input with a button. When that button is clicked the drodown
- will open below the input, not not below the button that you clicked.
-
- Remember that almost always you will want to use the provided trigger
- component
- <dropdown.trigger>
- because it takes care of all the A11y, bindings and classes for you, but when
- you can't it's good to know that you can still use your own markup and wire
- things together yourself.
-
-
-
- A good middle-ground is to apply the same trigger modifier that the component
- uses, but to your element or component. It will get all of the same a11y and
- bindings that the
- <dropdown.trigger>
- component gets.
-
+ Do you like animations in your dropdowns? Me too. Let's define some
+
+
+
+ It is that simple as create a CSS3 animation using the
+ ember-basic-dropdown--transitioning-in,
+ ember-basic-dropdown--transitioned-in
+ and
+ ember-basic-dropdown--transitioning-out
+ classes that ember-basic-dropdown gives you automatically.
+
+
+
+ You will probably also want to use
+ .ember-basic-dropdown-content--below
+ and
+ .ember-basic-dropdown-content--above
+ to have different animations depending on where the dropdown is positioned.
+
+
+
+
+
+
+
+ There is nothing else to know about animations, other that you have to use
+ CSS
+ animations, not transitions.
+
+
+
+ The dropdown takes care of creating a clone when closing the dropdown so you
+ animation works in both directions.
+
- Do you like animations in your dropdowns? Me too. Let's define some
-
-
-
- It is that simple as create a CSS3 animation using the
- ember-basic-dropdown--transitioning-in,
- ember-basic-dropdown--transitioned-in
- and
- ember-basic-dropdown--transitioning-out
- classes that ember-basic-dropdown gives you automatically.
-
-
-
- You will probably also want to use
- .ember-basic-dropdown-content--below
- and
- .ember-basic-dropdown-content--above
- to have different animations depending on where the dropdown is positioned.
-
- There is nothing else to know about animations, other that you have to use CSS
- animations, not transitions.
-
-
-
- The dropdown takes care of creating a clone when closing the dropdown so you
- animation works in both directions.
-
-
-
- < Custom position
- Migrate from 7.0 to 8.0 >
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/docs/api-reference.gts b/docs/app/templates/public-pages/docs/api-reference.gts
new file mode 100644
index 00000000..5d3f2c98
--- /dev/null
+++ b/docs/app/templates/public-pages/docs/api-reference.gts
@@ -0,0 +1,315 @@
+import { LinkTo } from '@ember/routing';
+
+
+
API reference
+
+
+ It's hard to find a proper place in the guides for explaining every single
+ option in depth, and some of them are so straightforward that they don't
+ require an example, so use this section as a more exhaustive list.
+
+
+
+
Dropdown
+
+
+
+
+
Option
+
Type
+
Description
+
+
+
+
+
calculatePosition
+
Function
+
Function to customize how the content of the dropdown is positioned.
+
+
+
class
+
String
+
The class of the dropdown component. Since this component is tagless
+ by default, you need to combine it with the
+ tagName
+ to be effective
+
+
+
defaultClass
+
String
+
Another way of providing a class to the component without polluting
+ the
+ class
+ attribute. Useful in contextual component to allow users give their
+ own classes while still retaining some defaults
+
+
+
destination
+
String
+
The id of a DOM element where the dropdown will be rendered using
+ #in-element
+
+
+
destinationElement
+
HTMLElement
+
Instead of passing
+ destination, you can pass the DOM element where the
+ dropdown will be rendered using
+ #in-element
+
+
+
contentComponent
+
Component
+
The component to render as content instead of the default content
+ component. You
+ probably
+ don't want to use this option.
+
+
+
horizontalPosition
+
String
+
The horizontal positioning strategy of the content. Can be one of
+ auto
+ (the default),
+ left,
+ center
+ or
+ right
+
+
+
matchTriggerWidth
+
Boolean
+
(Default:
+ false). Flag that indicates whether or not the content's
+ width should be equal to the width of the trigger.
+
+
+
preventScroll
+
Boolean
+
(Default:
+ false). Flag that prevents any elements on the page
+ outside the dropdown from scrolling. This matches platform-provided
+ select
+ element behavior. Note that this has no effect when scroll is
+ performed on a touch device
+
+
+
renderInPlace
+
String
+
When passed
+ true, the content will render next to the trigger instead
+ of being placed in the root of the body.
+
+
+
initiallyOpened
+
Boolean
+
(Default:
+ false). When passed
+ true
+ the component is first rendered open. Used in combination with
+ preventScroll
+ it changes Fastboot user experience, but other than that it does not
+ alter its behavior. The user can close it as usual.
+
+
+
tagName
+
String
+
(Default: "") The tag of the component.
+
+
+
triggerComponent
+
Component
+
The component to render as trigger instead of the default trigger
+ component.
+
+
+
verticalPosition
+
String
+
The vertical positioning strategy of the content. Can be one of
+ auto
+ (the default),
+ above
+ or
+ below
+
+
+
registerAPI
+
Function
+
An action that will be invoked with the new public API of the
+ component every time there is a change in the state of the component.
+
+
+
onOpen
+
Function
+
Action that will be called when the component is about to open.
+ Returning
+ false
+ from this function will prevent the component from being opened.
+
+
+
onClose
+
Function
+
Action that will be called when the component is about to close.
+ Returning
+ false
+ from this function will prevent the component from being closed.
+
+
+
rootEventType
+
String
+
(Default:
+ 'click') The type of mouse event that handles closing the
+ dropdown. Valid values: "mousedown" and "click"
+
+
+
+
+
Trigger
+
+
+
+
+
Option
+
Type
+
Description
+
+
+
+
+
ariaDescribedBy
+
String
+
Maps to aria-described-by
+
+
+
ariaInvalid
+
Boolean
+
Maps to aria-invalid
+
+
+
ariaLabel
+
String
+
Maps to aria-label
+
+
+
ariaLabelledBy
+
String
+
Maps to aria-labeledby
+
+
+
class
+
String
+
Extra classes to be added to the trigger components
+
+
+
tabindex
+
Number
+
Tabindex of the trigger, which defaults to 0 so the trigger is
+ focusable by default
+
+
+
htmlTag
+
String
+
(Default: 'div') The tag of the trigger component
+
+
+
title
+
String
+
Maps to the title attribute
+
+
+
eventType
+
String
+
(Default:
+ 'click') The type of mouse event that triggers the
+ trigger. Valid values: "mousedown" and "click"
+
+
+
stopPropagation
+
Boolean
+
(Default:
+ false) Whether the trigger should prevent the propagation
+ of the event that triggers it (click or mousedown)
+
+
+
+
+
Content
+
+
+
+
+
Option
+
Type
+
Description
+
+
+
+
+
class
+
String
+
The class of the dropdown's content
+
+
+
animationEnabled
+
boolean
+
Flag to determine whether the content will allow CSS animations.
+ Defaults to true
+
+
+
htmlTag
+
String
+
(Default: 'div') The tag of the content component
+
+
+
shouldReposition
+
Function
+
An optional function that can be used to avoid uncecessary
+ repositions. To skip a reposition, simply return
+ false. This function will be invoked when the DOM of the
+ content is changed. It receives two arguments: a
+ MutationRecord
+ and the public API object.
+
+
+
+
+
Public API's methods and actions
+
+
+ All actions and subcomponents of Ember Basic Dropdown receive a single
+ object containing the entirety of the public API of the component.
+
+
+
+ Any non-underscored property or action of this object can be considered
+ public and it's not going to change without causing a major version bump, so
+ if you are building another component on top of Ember Basic Dropdown, you
+ know that you are safe as long as you use this object.
+
+
+
+ {
+ uniqueId: <string>, // Contains the unique of this instance of EmberBasicDropdown. It's of the form 'ember1234'.
+ disabled: <boolean>, // Truthy if the component received 'disabled=true'
+ isOpen: <boolean>, // Truthy if the component is currently opened
+ actions: {
+ close() { ... }, // Closes the dropdown
+ open() { ... }, // Opens the dropdown
+ reposition() { ... }, // Repositions the dropdown
+ toggle() { ... } // Toggles the dropdown
+ }
+ }
+
- It's hard to find a proper place in the guides for explaining every single
- option in depth, and some of them are so straightforward that they don't
- require an example, so use this section as a more exhaustive list.
-
-
-
-
Dropdown
-
-
-
-
-
Option
-
Type
-
Description
-
-
-
-
-
calculatePosition
-
Function
-
Function to customize how the content of the dropdown is positioned.
-
-
-
class
-
String
-
The class of the dropdown component. Since this component is tagless
- by default, you need to combine it with the
- tagName
- to be effective
-
-
-
defaultClass
-
String
-
Another way of providing a class to the component without polluting
- the
- class
- attribute. Useful in contextual component to allow users give their own
- classes while still retaining some defaults
-
-
-
destination
-
String
-
The id of a DOM element where the dropdown will be rendered using
- #in-element
-
-
-
destinationElement
-
HTMLElement
-
Instead of passing
- destination, you can pass the DOM element where the
- dropdown will be rendered using
- #in-element
-
-
-
contentComponent
-
String or Component
-
The component to render as content instead of the default content
- component. You
- probably
- don't want to use this option.
-
-
-
horizontalPosition
-
String
-
The horizontal positioning strategy of the content. Can be one of
- auto
- (the default),
- left,
- center
- or
- right
-
-
-
matchTriggerWidth
-
Boolean
-
(Default:
- false). Flag that indicates whether or not the content's
- width should be equal to the width of the trigger.
-
-
-
preventScroll
-
Boolean
-
(Default:
- false). Flag that prevents any elements on the page outside
- the dropdown from scrolling. This matches platform-provided
- select
- element behavior. Note that this has no effect when scroll is performed
- on a touch device
-
-
-
renderInPlace
-
String
-
When passed
- true, the content will render next to the trigger instead
- of being placed in the root of the body.
-
-
-
initiallyOpened
-
Boolean
-
(Default:
- false). When passed
- true
- the component is first rendered open. Used in combination with
- preventScroll
- it changes Fastboot user experience, but other than that it does not
- alter its behavior. The user can close it as usual.
-
-
-
tagName
-
String
-
(Default: "") The tag of the component.
-
-
-
triggerComponent
-
String
-
The component to render as trigger instead of the default trigger
- component.
-
-
-
verticalPosition
-
String
-
The vertical positioning strategy of the content. Can be one of
- auto
- (the default),
- above
- or
- below
-
-
-
registerAPI
-
Function
-
An action that will be invoked with the new public API of the
- component every time there is a change in the state of the component.
-
-
-
onOpen
-
Function
-
Action that will be called when the component is about to open.
- Returning
- false
- from this function will prevent the component from being opened.
-
-
-
onClose
-
Function
-
Action that will be called when the component is about to close.
- Returning
- false
- from this function will prevent the component from being closed.
-
-
-
rootEventType
-
String
-
(Default:
- 'click') The type of mouse event that handles closing the
- dropdown. Valid values: "mousedown" and "click"
-
-
-
-
-
Trigger
-
-
-
-
-
Option
-
Type
-
Description
-
-
-
-
-
ariaDescribedBy
-
String
-
Maps to aria-described-by
-
-
-
ariaInvalid
-
Boolean
-
Maps to aria-invalid
-
-
-
ariaLabel
-
String
-
Maps to aria-label
-
-
-
ariaLabelledBy
-
String
-
Maps to aria-labeledby
-
-
-
class
-
String
-
Extra classes to be added to the trigger components
-
-
-
tabindex
-
Number
-
Tabindex of the trigger, which defaults to 0 so the trigger is
- focusable by default
-
-
-
htmlTag
-
String
-
(Default: 'div') The tag of the trigger component
-
-
-
title
-
String
-
Maps to the title attribute
-
-
-
eventType
-
String
-
(Default:
- 'click') The type of mouse event that triggers the trigger.
- Valid values: "mousedown" and "click"
-
-
-
stopPropagation
-
Boolean
-
(Default:
- false) Whether the trigger should prevent the propagation
- of the event that triggers it (click or mousedown)
-
-
-
-
-
Content
-
-
-
-
-
Option
-
Type
-
Description
-
-
-
-
-
class
-
String
-
The class of the dropdown's content
-
-
-
to
-
String
-
[DEPRECATED]The selector of a DOM element where the
- dropdown will be rendered using ember-wormhole
-
-
-
animationEnabled
-
boolean
-
Flag to determine whether the content will allow CSS animations.
- Defaults to true
-
-
-
htmlTag
-
String
-
(Default: 'div') The tag of the content component
-
-
-
shouldReposition
-
Function
-
An optional function that can be used to avoid uncecessary
- repositions. To skip a reposition, simply return
- false. This function will be invoked when the DOM of the
- content is changed. It receives two arguments: a
- MutationRecord
- and the public API object.
-
-
-
-
-
Public API's methods and actions
-
-
- All actions and subcomponents of Ember Basic Dropdown receive a single object
- containing the entirety of the public API of the component.
-
-
-
- Any non-underscored property or action of this object can be considered public
- and it's not going to change without causing a major version bump, so if you
- are building another component on top of Ember Basic Dropdown, you know that
- you are safe as long as you use this object.
-
-
-
- { uniqueId: <string>, // Contains the unique of this instance of
- EmberBasicDropdown. It's of the form `ember1234`. disabled: <boolean>,
- // Truthy if the component received `disabled=true` isOpen: <boolean>,
- // Truthy if the component is currently opened actions: { close() { ... }, //
- Closes the dropdown open() { ... }, // Opens the dropdown reposition() { ...
- }, // Repositions the dropdown toggle() { ... } // Toggles the dropdown } }
-
-
-
- < Test helpers
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/docs/content-events.gts b/docs/app/templates/public-pages/docs/content-events.gts
new file mode 100644
index 00000000..129d6ba4
--- /dev/null
+++ b/docs/app/templates/public-pages/docs/content-events.gts
@@ -0,0 +1,75 @@
+import { LinkTo } from '@ember/routing';
+import CodeExample from '../../../components/code-example';
+import ContentEvents1Component from '../../../components/snippets/content-events-1';
+
+
+
Content events
+
+
+ Like the trigger, the dropdown accepts function to bind a few handy events.
+
+
+
onFocusIn/onFocusOut(dropdown, event)
+
+
+ Those events are just identical to the ones in the trigger.
+
+
+
onMouseEnter / onMouseLeave(dropdown, event)
+
+
+ Used in conjunction with the same events in the trigger it quite easy to
+ make a dropdown that opens when you hover the trigger and closes leave it,
+ but stays open if you move from the trigger to the content.
+
+
+
+ You can even delay the closing a bit to allow the users to briefly pass
+ outside the boundaries of the dropdown without closing it. Think the navbar
+ of your favourite social network:
+
+
+
+
+
+
+
+ Let's break this down.
+
+
+
+ First we
+ <dd.Trigger \{{on "mousedown" this.prevent\\}}>
+ neglect mouse input: Open/close both by click and hover will trip our users.
+
+
+
+ Then
+ \{{on "mouseenter" (fn this.open dd)}}
+ and
+ \{{on "mouseleave" (fn this.closeLater dd)}}
+ open a close the dropdown, but we we leave we don't close immediately.
+ Instead we delay the close a few milliseconds as a grace period, allowing
+ the user to transition from the trigger to the content even if the
+ trajectory of the mouse is not perfect.
+
+
+
+ If before that grace period they enter again the boundaries of the
+ component, we cancel the scheduled close. This would be much cleaner using
+ ember-concurrency
+ tasks.
+
+
+
+ In the following chapters we will learn how to customize the dropdown
+ behaviour with all the different options.
+
- Like the trigger, the dropdown accepts function to bind a few handy events.
-
-
-
onFocusIn/onFocusOut(dropdown, event)
-
-
- Those events are just identical to the ones in the trigger.
-
-
-
onMouseEnter / onMouseLeave(dropdown, event)
-
-
- Used in conjunction with the same events in the trigger it quite easy to make
- a dropdown that opens when you hover the trigger and closes leave it, but
- stays open if you move from the trigger to the content.
-
-
-
- You can even delay the closing a bit to allow the users to briefly pass
- outside the boundaries of the dropdown without closing it. Think the navbar of
- your favourite social network:
-
- First we
- <dd.Trigger \{{on "mousedown" this.prevent\\}}>
- neglect mouse input: Open/close both by click and hover will trip our users.
-
-
-
- Then
- \{{on "mouseenter" (fn this.open dd)}}
- and
- \{{on "mouseleave" (fn this.closeLater dd)}}
- open a close the dropdown, but we we leave we don't close immediately. Instead
- we delay the close a few milliseconds as a grace period, allowing the user to
- transition from the trigger to the content even if the trajectory of the mouse
- is not perfect.
-
-
-
- If before that grace period they enter again the boundaries of the component,
- we cancel the scheduled close. This would be much cleaner using
- ember-concurrency
- tasks.
-
-
-
- In the following chapters we will learn how to customize the dropdown
- behaviour with all the different options.
-
-
-
- < Trigger events
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/docs/custom-position.gts b/docs/app/templates/public-pages/docs/custom-position.gts
new file mode 100644
index 00000000..0370cf51
--- /dev/null
+++ b/docs/app/templates/public-pages/docs/custom-position.gts
@@ -0,0 +1,147 @@
+import CustomPosition1Component from '../../../components/snippets/custom-position-1';
+import CustomPosition2Component from '../../../components/snippets/custom-position-2';
+import CodeExample from '../../../components/code-example';
+import { LinkTo } from '@ember/routing';
+
+
+
Custom position
+
+
+ In
+ a previous section
+ we already say that the dropdown bundles a few positioning strategies both
+ horizontally and vertically.
+
+
+
+ But you don't want to be limited to the positioning strategies I designed
+ for you. You might want the dropdown to float to the left of the trigger or
+ perhaps one inch south-west of it.
+
+
+
+ Fear not, my friend, the component has an escape valve for this. You can
+ design your own positioning function and pass it to the component. It just
+ has to fulfill a specific contract.
+
+
+
calculatePosition
+
+
+ This function is the only thing you have to implement to tell the component
+ where it should be positioned.
+
+
+
+ The complete signature of the function is:
+
+ calculatePosition(trigger, content, destination, {
+ previousHorizontalPosition, horizontalPosition, previousVerticalPosition,
+ verticalPosition, matchTriggerWidth })
+
+
+
+
+
trigger: The DOM element of the trigger component
+
content: The DOM element of the content component
+
destination: The DOM element where the content component is
+ going to be inserted
+
previousHorizontalPosition: The string with the horizontal
+ position the component had in the last time it was repositioned. If you
+ don't provide any or you pass
+ horizontalPosition="auto"
+ it will be
+ "left"
+ or
+ "right"
+ depending on the space around the trigger
+
horizontalPosition: The string with the current horizontal
+ position. If you don't provide any or you pass
+ horizontalPosition="auto"
+ it will be
+ "left"
+ or
+ "right"
+ depending on the space around the trigger
+
previousVerticalPosition: The string with the vertical
+ position the component had in the last time it was repositioned. If you
+ don't provide any or you pass
+ verticalPosition="auto"
+ it will be
+ "above"
+ or
+ "below"
+ depending on the space around the trigger
+
verticalPosition: The string with the current vertical
+ position. If you don't provide any or you pass
+ verticalPosition="auto"
+ it will be
+ "above"
+ or
+ "below"
+ depending on the space around the trigger
+
matchTriggerWidth: Boolean that express the intention of
+ the developer to make the dropdown have the same width as the trigger
+
renderInPlace: Boolean that express if the content will be
+ rendered in place. Useful since very usually the reposition logic must be
+ entirely different
+
+
+
+ The return value of this function must also be an object with a specific
+ shape:
+
+ { horizontalPosition, verticalPosition, style }
+
+
+
+
horizontalPosition: The new value of horizontalPosition
+
verticalPosition: The new value of verticalPosition
+
style: An object containing the CSS properties that will
+ position the object. It supports
+ top,
+ left,
+ right
+ and
+ width
+
+
+
+ Sounds like a lot, but with an example you will see that it's not really
+ hard. Let's create a dropdown that opens to the right of the trigger,
+ vertically centered with it.
+
+
+
+
+
+
+
+ You can resize the window and scroll and you can see that the content stays
+ just where it should. It will even reposition automatically if the content
+ inside changes thanks to the magic of
+ MutationObservers.
+
+
+
+
+
+
+
+ The key concept that you need to extract is that all the machinery to
+ position the content and reposition when the content, scroll or screen
+ changes is handled by the dropdown, so the only missing piece you have to
+ provide is the function that calculates the cordinates.
+
- In
- a previous section
- we already say that the dropdown bundles a few positioning strategies both
- horizontally and vertically.
-
-
-
- But you don't want to be limited to the positioning strategies I designed for
- you. You might want the dropdown to float to the left of the trigger or
- perhaps one inch south-west of it.
-
-
-
- Fear not, my friend, the component has an escape valve for this. You can
- design your own positioning function and pass it to the component. It just has
- to fulfill a specific contract.
-
-
-
calculatePosition
-
-
- This function is the only thing you have to implement to tell the component
- where it should be positioned.
-
-
-
- The complete signature of the function is:
-
- calculatePosition(trigger, content, destination, {
- previousHorizontalPosition, horizontalPosition, previousVerticalPosition,
- verticalPosition, matchTriggerWidth })
-
-
-
-
-
trigger: The DOM element of the trigger component
-
content: The DOM element of the content component
-
destination: The DOM element where the content component is
- going to be inserted
-
previousHorizontalPosition: The string with the horizontal
- position the component had in the last time it was repositioned. If you
- don't provide any or you pass
- horizontalPosition="auto"
- it will be
- "left"
- or
- "right"
- depending on the space around the trigger
-
horizontalPosition: The string with the current horizontal
- position. If you don't provide any or you pass
- horizontalPosition="auto"
- it will be
- "left"
- or
- "right"
- depending on the space around the trigger
-
previousVerticalPosition: The string with the vertical
- position the component had in the last time it was repositioned. If you
- don't provide any or you pass
- verticalPosition="auto"
- it will be
- "above"
- or
- "below"
- depending on the space around the trigger
-
verticalPosition: The string with the current vertical
- position. If you don't provide any or you pass
- verticalPosition="auto"
- it will be
- "above"
- or
- "below"
- depending on the space around the trigger
-
matchTriggerWidth: Boolean that express the intention of the
- developer to make the dropdown have the same width as the trigger
-
renderInPlace: Boolean that express if the content will be
- rendered in place. Useful since very usually the reposition logic must be
- entirely different
-
-
-
- The return value of this function must also be an object with a specific
- shape:
-
- { horizontalPosition, verticalPosition, style }
-
-
-
-
horizontalPosition: The new value of horizontalPosition
-
verticalPosition: The new value of verticalPosition
-
style: An object containing the CSS properties that will
- position the object. It supports
- top,
- left,
- right
- and
- width
-
-
-
- Sounds like a lot, but with an example you will see that it's not really hard.
- Let's create a dropdown that opens to the right of the trigger, vertically
- centered with it.
-
- You can resize the window and scroll and you can see that the content stays
- just where it should. It will even reposition automatically if the content
- inside changes thanks to the magic of
- MutationObservers.
-
- The key concept that you need to extract is that all the machinery to position
- the content and reposition when the content, scroll or screen changes is
- handled by the dropdown, so the only missing piece you have to provide is the
- function that calculates the cordinates.
-
-
-
- < Disabled
- Animations >
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/docs/disabled.gts b/docs/app/templates/public-pages/docs/disabled.gts
new file mode 100644
index 00000000..045fb83f
--- /dev/null
+++ b/docs/app/templates/public-pages/docs/disabled.gts
@@ -0,0 +1,41 @@
+import CodeExample from 'docs/components/code-example';
+import Disabled1Component from '../../../components/snippets/disabled-1';
+import { LinkTo } from '@ember/routing';
+
+
+
Disabled
+
+
+ If you disable the dropdown, it will not open or close by any mean, mouse,
+ keyboard or touch screen.
+
+
+
+
+
+
+
+ Note that being disabled does not prevent the dropdown or any of its
+ sub-components from firing events like
+ onMouseEnter.
+
+
+
+ The component takes care by default of usual good practices, removing the
+ tabindex
+ of the trigger and set
+ [aria-disabled="true"]
+ to assist screen readers.
+
- Note that being disabled does not prevent the dropdown or any of its
- sub-components from firing events like
- onMouseEnter.
-
-
-
- The component takes care by default of usual good practices, removing the
- tabindex
- of the trigger and set
- [aria-disabled="true"]
- to assist screen readers.
-
-
-
- < Position
- Overlays >
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/docs/dropdown-events.gts b/docs/app/templates/public-pages/docs/dropdown-events.gts
new file mode 100644
index 00000000..a6d17d53
--- /dev/null
+++ b/docs/app/templates/public-pages/docs/dropdown-events.gts
@@ -0,0 +1,97 @@
+import CodeExample from '../../../components/code-example';
+import { LinkTo } from '@ember/routing';
+import DropDownEvents1Component from '../../../components/snippets/dropdown-events-1';
+import DropDownEvents2Component from '../../../components/snippets/dropdown-events-2';
+import DropDownEvents3Component from '../../../components/snippets/dropdown-events-3';
+
+
+
Dropdown events
+
+
+ Although the top-level component is tagless, it does fire a few events that
+ you can use to react to changes in the state of the dropdown.
+
+
+
+ This has no mystery.
+
+
+
onOpen(dropdown, event?)
+
+
+ Pretty self-explanatory. The
+ dropdown
+ argument in the signature is the public API of the component. The
+ event
+ argument will be passed if this event is fired as a consequence of another
+ event (e.g. a click), but will be undefined if it was fired
+ programmatically.
+
+
+
What kinds of things you can do with this event?
+
+
+ In general when you want to do something outside the component when this
+ loads. By example, you can delay the loading of some data until the dropdown
+ is opened.
+
+
+
+
+
+
+
+ There is something you should know about this hook: If you
+ return false;
+ from it you will prevent the component from opening.
+
+
+
+ Let's use this feature along with the public API and event received as
+ aguments to do a nifty trick. We are going to iterate over the example above
+ so we prevent the component from opening, load the users and once loaded we
+ open it.
+
+
+
+
+
+
+
For the record, I don't think this is good UX
+
+
onClose(dropdown, event?)
+
+
+ Symmetrically, you can perform on action when the dropdown is closed. For
+ example, save the a user selection, and you can also
+ return false
+ to prevent the component from closing.
+
+
+
+ Example: Create a dropdown with some checkboxes inside, and don't allow it
+ to close until one checkbox is selected.
+
+
+
+
+
+
+
Cruel, isn't it?
+
+
+ Those were the events fired by the top-level component, now let's go deep on
+ the events that are fired by the trigger.
+
- Although the top-level component is tagless, it does fire a few events that
- you can use to react to changes in the state of the dropdown.
-
-
-
- This has no mystery.
-
-
-
onOpen(dropdown, event?)
-
-
- Pretty self-explanatory. The
- dropdown
- argument in the signature is the public API of the component. The
- event
- argument will be passed if this event is fired as a consequence of another
- event (e.g. a click), but will be undefined if it was fired programmatically.
-
-
-
What kinds of things you can do with this event?
-
-
- In general when you want to do something outside the component when this
- loads. By example, you can delay the loading of some data until the dropdown
- is opened.
-
- There is something you should know about this hook: If you
- return false;
- from it you will prevent the component from opening.
-
-
-
- Let's use this feature along with the public API and event received as
- aguments to do a nifty trick. We are going to iterate over the example above
- so we prevent the component from opening, load the users and once loaded we
- open it.
-
- Symmetrically, you can perform on action when the dropdown is closed. For
- example, save the a user selection, and you can also
- return false
- to prevent the component from closing.
-
-
-
- Example: Create a dropdown with some checkboxes inside, and don't allow it to
- close until one checkbox is selected.
-
- Those were the events fired by the top-level component, now let's go deep on
- the events that are fired by the trigger.
-
-
-
- < How to use it
- Trigger events >
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/docs/how-to-use-it.gts b/docs/app/templates/public-pages/docs/how-to-use-it.gts
new file mode 100644
index 00000000..e71ee3ef
--- /dev/null
+++ b/docs/app/templates/public-pages/docs/how-to-use-it.gts
@@ -0,0 +1,121 @@
+import CodeExample from '../../../components/code-example';
+import { LinkTo } from '@ember/routing';
+import HowToUseIt1Component from '../../../components/snippets/how-to-use-it-1';
+import HowToUseIt2Component from '../../../components/snippets/how-to-use-it-2';
+import HowToUseIt3Component from '../../../components/snippets/how-to-use-it-3';
+
+
+
How to use it
+
+
+ This component is built with a technique known as
+ Contextual Components. If you haven't heard of it, check
+ the official guides
+ for some background.
+
+
+
+ The basic usage is pretty simple. There are no mandatory fields. Just invoke
+ the
+ basic-dropdown
+ component which yields the public API to its block. That top-level component
+ has no markup, just pure behaviour. Once inside the block, the yielded API
+ has two contextual components on it that you can use:
+ trigger
+ and
+ content.
+
+
+
+ Go, use it and inspect the DOM. I know it looks ugly, we'll style it later.
+
+
+
+
+
+
+
+ As you've inspected, the markup is very simple.
+
+
+ The
+ \{{dd.Trigger}}
+ component generates a simple div with some self-explainatory classes and
+ \{{dd.Content}}
+ doesn't render anything until you open the select.
+
+
+
+ Out of the box the component already takes care of most things you need.
+
+
+
+ First of all, the component opens when you click on the trigger and closes
+ when you click anywhere else in the page. If you are reading this on a
+ smartphone you can also see that it opens when you tap on the trigger and it
+ already distinguishes proper taps from taps used for scrolling. The dropdown
+ is rendered not next to the trigger, but in a placeholder div in the root of
+ the application and positioned automatically with inline styles.
+
+
+
+ Lastly, if you inspect the DOM a second time you can see all the
+ a11y
+ machinery in place to make the component accesible. Even the trigger,
+ despite being a div, is focusable as it should be for a good keyboard
+ experience. Also if you have the trigger focus, you can open and close it
+ with the enter or space keys.
+
+
+
+ Now let's fix that terrible look. I'll give the dropdown a look and feel
+ inspired by dropdown buttons in bootstrap:
+
+
+
+
+
+
+
+ It just took a little bit of CSS. You could also have just assigned the
+ right classes and reused the styles from the framework (I can't demo that
+ because I don't have bootstrap here).
+
+
+
+ I want to stress that when it comes to CSS, the dropdown really doesn't
+ care. Let's make a material-like round button with a round content.
+
+
+
+
+
+
+
+ I got you.
+
+
+
+ And this is more or less everything you need to know about styles. Basically
+ there is none, so you can add create your own in CSS or reuse the classes
+ that CSS frameworks give you.
+
+
+
+ In the next section we will see the basic action hooks that this component
+ gives you to hook to events like opening and closing and many others.
+
- This component is built with a technique known as
- Contextual Components. If you haven't heard of it, check
- the official guides
- for some background.
-
-
-
- The basic usage is pretty simple. There are no mandatory fields. Just invoke
- the
- basic-dropdown
- component which yields the public API to its block. That top-level component
- has no markup, just pure behaviour. Once inside the block, the yielded API has
- two contextual components on it that you can use:
- trigger
- and
- content.
-
-
-
- Go, use it and inspect the DOM. I know it looks ugly, we'll style it later.
-
- As you've inspected, the markup is very simple.
-
-
- The
- \{{dd.Trigger}}
- component generates a simple div with some self-explainatory classes and
- \{{dd.Content}}
- doesn't render anything until you open the select.
-
-
-
- Out of the box the component already takes care of most things you need.
-
-
-
- First of all, the component opens when you click on the trigger and closes
- when you click anywhere else in the page. If you are reading this on a
- smartphone you can also see that it opens when you tap on the trigger and it
- already distinguishes proper taps from taps used for scrolling. The dropdown
- is rendered not next to the trigger, but in a placeholder div in the root of
- the application and positioned automatically with inline styles.
-
-
-
- Lastly, if you inspect the DOM a second time you can see all the
- a11y
- machinery in place to make the component accesible. Even the trigger, despite
- being a div, is focusable as it should be for a good keyboard experience. Also
- if you have the trigger focus, you can open and close it with the enter or
- space keys.
-
-
-
- Now let's fix that terrible look. I'll give the dropdown a look and feel
- inspired by dropdown buttons in bootstrap:
-
- It just took a little bit of CSS. You could also have just assigned the right
- classes and reused the styles from the framework (I can't demo that because I
- don't have bootstrap here).
-
-
-
- I want to stress that when it comes to CSS, the dropdown really doesn't care.
- Let's make a material-like round button with a round content.
-
- And this is more or less everything you need to know about styles. Basically
- there is none, so you can add create your own in CSS or reuse the classes that
- CSS frameworks give you.
-
-
-
- In the next section we will see the basic action hooks that this component
- gives you to hook to events like opening and closing and many others.
-
-
-
- < Installation
- Dropdown events >
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/docs/index.gts b/docs/app/templates/public-pages/docs/index.gts
new file mode 100644
index 00000000..0cec3014
--- /dev/null
+++ b/docs/app/templates/public-pages/docs/index.gts
@@ -0,0 +1,80 @@
+import { LinkTo } from '@ember/routing';
+
+
+
Overview
+
+
I'm going to tell you the story of how this dropdown addon was born.
+
+
+ I was building a select component but I realized that a select is nothing
+ more than a dropdown with some extra stuff on top. And datepickers too. And
+ colorpickers, contextual menus and many other widgets.
+
+
+
+ Essentially, any floating box of content that is opened when you interact
+ with a trigger is basically a dropdown. Dropdowns are the foundation to at
+ least half a dozen common UI widgets we use daily, so I decided to shape
+ this addon in a way that was easy to reuse and customize to create those
+ other widgets.
+
+
+
+ But if a dropdown is just a floating box activated by a trigger,
+ why would you want to use a third part addon for such a simple thing?
+
+
+
+ Well, there are two kinds of people. Those who think that dropdowns
+ are an easy thing and those who have actually built one.
+
+
+
+ When I transitioned from the first kind to the second I learned a lot of
+ stuff.
+
+
+
+
Absolutely position elements in a performant way using the runloop
+
Overcoming z-index limitations
+
How useful MutationObservers and DOM Events can be
+
Proper detection of taps and touch scroll
+
Animating elements being removed from the screen
+
Chasing memory leaks when global events are not properly removed
+
Accessibility and keyboard navigation
+
+
+
+ After I built the initial version, I started to build other widgets on top
+ and I had to step back a few times and identify where it wasn't flexible
+ enough. This process ended up in a component that distilled the essence of
+ what a dropdown is with as few assumptions as possible.
+
+
+
+ I honestly want to save you time and tears. I've been there.
+
+
+
+ This component is a
+ building block
+ for you to build other components on top, so it prioritizes flexibility and
+ explicitness over succinctness in its API, but still allows allows basic
+ usage out of the box with no ceremony.
+
+
+
+ Another thing to note is that this component doesn't have any theme by
+ default, so unless you do something about it, it will look pretty ugly. The
+ few styles it has are only to deal with positioning.
+
I'm going to tell you the story of how this dropdown addon was born.
-
-
- I was building a select component but I realized that a select is nothing more
- than a dropdown with some extra stuff on top. And datepickers too. And
- colorpickers, contextual menus and many other widgets.
-
-
-
- Essentially, any floating box of content that is opened when you interact with
- a trigger is basically a dropdown. Dropdowns are the foundation to at least
- half a dozen common UI widgets we use daily, so I decided to shape this addon
- in a way that was easy to reuse and customize to create those other widgets.
-
-
-
- But if a dropdown is just a floating box activated by a trigger,
- why would you want to use a third part addon for such a simple thing?
-
-
-
- Well, there are two kinds of people. Those who think that dropdowns
- are an easy thing and those who have actually built one.
-
-
-
- When I transitioned from the first kind to the second I learned a lot of
- stuff.
-
-
-
-
Absolutely position elements in a performant way using the runloop
-
Overcoming z-index limitations
-
How useful MutationObservers and DOM Events can be
-
Proper detection of taps and touch scroll
-
Animating elements being removed from the screen
-
Chasing memory leaks when global events are not properly removed
-
Accessibility and keyboard navigation
-
-
-
- After I built the initial version, I started to build other widgets on top and
- I had to step back a few times and identify where it wasn't flexible enough.
- This process ended up in a component that distilled the essence of what a
- dropdown is with as few assumptions as possible.
-
-
-
- I honestly want to save you time and tears. I've been there.
-
-
-
- This component is a
- building block
- for you to build other components on top, so it prioritizes flexibility and
- explicitness over succinctness in its API, but still allows allows basic usage
- out of the box with no ceremony.
-
-
-
- Another thing to note is that this component doesn't have any theme by
- default, so unless you do something about it, it will look pretty ugly. The
- few styles it has are only to deal with positioning.
-
-
-
I hope you like it.
-
-
- Installation >
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/docs/installation.gts b/docs/app/templates/public-pages/docs/installation.gts
new file mode 100644
index 00000000..48e1f44f
--- /dev/null
+++ b/docs/app/templates/public-pages/docs/installation.gts
@@ -0,0 +1,103 @@
+import CodeExample from '../../../components/code-example';
+import { LinkTo } from '@ember/routing';
+
+
+
Installation
+
+
+ Ember-basic-dropdown is distributed as an
+ Ember CLI
+ addon. To install it, run the following command in your ember project
+ directory
+
+
+
+
+
$ ember install ember-basic-dropdown
+
+
+
+
+ When installing this through
+ ember install
+ the addon will add the necessary snippet above automatically in your app.
+
+
+
Manual installation
+
+
+ After the installation you need to add the following lines somewhere in your
+ templates where you want to render the dropdown content into e.g. your
+ application.hbs. In this component will be rendered the
+ dropdown content.
+
+
+
+
+
+ If you use vanilla CSS, you need to add the following line into
+ app.js
+ or in any route/controller/component
+ .js/.ts
+ file:
+
+
+
+
+
+ Instead of adding the styling in an
+ .js
+ file and depending from your build config you can also add the css in any
+ template/component css file by using following line
+
+
+
+
+
+ However, if you are using SASS or LESS you need to add an import statement
+ to your styles.
+
+
+
+
+
+ If you are using LESS there is also necessary to register the
+ paths
+ in
+ lessOptions
+
+
+
+
+
+ The styles of the addon are
+ very minimal
+ and deal mostly with positioning. You can tweak a couple things but we'll
+ get to that later.
+
- Ember-basic-dropdown is distributed as an
- Ember CLI
- addon. To install it, run the following command in your ember project
- directory
-
-
-
-
$ ember install
- ember-basic-dropdown
-
-
-
- When installing this through
- ember install
- the addon will add the necessary snippet above automatically in your app.
-
-
-
Manual installation
-
-
- After the installation you need to add the following lines somewhere in your
- templates where you want to render the dropdown content into e.g. your
- application.hbs. In this component will be rendered the dropdown
- content.
-
-
-" />
-
-
- If you use vanilla CSS, you need to add the following line into
- app.js
- or in any route/controller/component
- .js/.ts
- file:
-
-
-
-
-
- Instead of adding the styling in an
- .js
- file and depending from your build config you can also add the css in any
- template/component css file by using following line
-
- However, if you are using SASS or LESS you need to add an import statement to
- your styles.
-
-
-{{#let (get-code-snippet "installation-1.scss") as |snippet|}}
-
-{{/let}}
-
-
- If you are using LESS there is also necessary to register the
- paths
- in
- lessOptions
-
-
-{{#let (get-code-snippet "installation-2.js") as |snippet|}}
-
-{{/let}}
-
-
- The styles of the addon are
- very minimal
- and deal mostly with positioning. You can tweak a couple things but we'll get
- to that later.
-
-
-
- Now let's learn the API of the component.
-
-
-
- <
- Overview
- How to use it >
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/docs/migrate-7-0-to-8-0.gts b/docs/app/templates/public-pages/docs/migrate-7-0-to-8-0.gts
new file mode 100644
index 00000000..e2cc73fd
--- /dev/null
+++ b/docs/app/templates/public-pages/docs/migrate-7-0-to-8-0.gts
@@ -0,0 +1,50 @@
+import CodeExample from '../../../components/code-example';
+import { LinkTo } from '@ember/routing';
+
+
+
Vanilla JS: If you have used vanilla js you must now import the css in
- app.js
- manually like (for other css adding examples see under installation)
-
-
-
-
LESS: If you are using with less you need to add
- lessOptions
- into your
- ember-cli-build.js
- file
- {{#let (get-code-snippet "installation-2.js") as |snippet|}}
-
- {{/let}}
-
-
-
Typescript: There were added / modified / fixed the typescript
- declarations. This could be braking for consumer app
-
-
-
-
- < Animations
- Test helpers >
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/docs/overlays.gts b/docs/app/templates/public-pages/docs/overlays.gts
new file mode 100644
index 00000000..98914087
--- /dev/null
+++ b/docs/app/templates/public-pages/docs/overlays.gts
@@ -0,0 +1,53 @@
+import CodeExample from '../../../components/code-example';
+import { LinkTo } from '@ember/routing';
+import Overlays1Component from '../../../components/snippets/overlays-1';
+
+
+
Overlays
+
+
+ By default the dropdown renders floating above the rest of your page but it
+ doesn't prevent the user from clicking on any other item. Overlays can fix
+ that and make dropdowns look cooler.
+
+
+
+
+
+
+
+ What do you have to know:
+
+
+
+
+ By default overlays have a semitransparent black background, but you can
+ change that using the
+ $ember-basic-dropdown-overlay-background
+ SASS variable or target the
+ .ember-basic-dropdown-overlay
+
+
+ By default overlays have
+ pointer-events: none, so it is transparent to clicks but you
+ can change that using the
+ $ember-basic-dropdown-overlay-pointer-events
+ SASS variable or target the same class.
+
+
+
+
+ In the next section we'll talk more how to customize the styles.
+
- By default the dropdown renders floating above the rest of your page but it
- doesn't prevent the user from clicking on any other item. Overlays can fix
- that and make dropdowns look cooler.
-
- By default overlays have a semitransparent black background, but you can
- change that using the
- $ember-basic-dropdown-overlay-background
- SASS variable or target the
- .ember-basic-dropdown-overlay
-
-
- By default overlays have
- pointer-events: none, so it is transparent to clicks but you
- can change that using the
- $ember-basic-dropdown-overlay-pointer-events
- SASS variable or target the same class.
-
-
-
-
- In the next section we'll talk more how to customize the styles.
-
-
-
- < Disabled
- Styles >
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/docs/position.gts b/docs/app/templates/public-pages/docs/position.gts
new file mode 100644
index 00000000..4b9a3e27
--- /dev/null
+++ b/docs/app/templates/public-pages/docs/position.gts
@@ -0,0 +1,131 @@
+import CodeExample from '../../../components/code-example';
+import { LinkTo } from '@ember/routing';
+import Position1Component from '../../../components/snippets/position-1';
+import Position2Component from '../../../components/snippets/position-2';
+import Position3Component from '../../../components/snippets/position-3';
+
+
+
Position
+
+
+ The number one thing that you will want to customize in a dropdown is where
+ the floating content will be positioned in relation to the trigger.
+
+
+
horizontalPosition / verticalPosition
+
+
+ Ember Basic Dropdown comes with a nice set of defaults. The rules are as
+ follow:
+
+
+
+
+ The
+ <dd.Content>
+ has not pre-defined size, so it will adapt to the size its childs.
+
+
+ The dropdown's content is positioned below the trigger, unless there is
+ not enough space to fit it, in which case it is positioned above it.
+
+
+ The content's left border will be aligned with the trigger's left border
+ and content will flow towards the right.
+
+
+ If there isn't enough size towards the right to fit the content but there
+ is enough room to the left, the right border of the content will align
+ with the right border of the trigger and it will grow towards the left.
+
+
+ All the rules above are re-checked every time the content of the dropdown
+ changes, the browser is resized or the orientation of the device changes.
+
+
+
+
This sounds like a lot, but with an example it will be crystal clear.
+
+
+
+
+
+
+ In this example above I'm using the options
+ verticalPosition
+ and
+ horizontalPosition
+ to override the default behaviour.
+
+
+
+ The default value for both options is
+ auto, but you can pass
+ verticalPosition=above|below
+ and
+ horizontalPosition=auto-right|right|center|left.
+
+
+
Narrow the window of the browser and play with scroll to see the automatic
+ positioning in action.
+
+
renderInPlace
+
+
+ Although by default the component renders the content in the root of the app
+ and positions it absolutely, there are some situations where you want the
+ content to be
+ physically
+ next to the trigger.
+
+
+
+ To do so, pass
+ renderInPlace=true
+ to the component. Inspect the DOM of the next example to see the difference.
+
+
+
+
+
+
+
+ Please note that when rendering the content in place, the vertical position
+ will not automatically detect the best position based on the space around
+ the trigger. You have to explicitly pass
+ verticalPosition="above"
+ to render it over the trigger.
+
+
+
matchTriggerWidth
+
+
+ There is a few widgets in which the width of the floating box has to match
+ the width of the trigger design reasons. Since this is common enough, there
+ is an option to enable this behaviour.
+
+
+
+
+
+
+
+ In the section about
+ Custom Position
+ strategies we will see how to totally customize how the dropdown is
+ positioned by passing it a function. If you do so the options in this
+ section will not work unless your function, which will receive those
+ options, honors them.
+
- The number one thing that you will want to customize in a dropdown is where
- the floating content will be positioned in relation to the trigger.
-
-
-
horizontalPosition / verticalPosition
-
-
- Ember Basic Dropdown comes with a nice set of defaults. The rules are as
- follow:
-
-
-
-
- The
- <dd.Content>
- has not pre-defined size, so it will adapt to the size its childs.
-
-
- The dropdown's content is positioned below the trigger, unless there is not
- enough space to fit it, in which case it is positioned above it.
-
-
- The content's left border will be aligned with the trigger's left border and
- content will flow towards the right.
-
-
- If there isn't enough size towards the right to fit the content but there is
- enough room to the left, the right border of the content will align with the
- right border of the trigger and it will grow towards the left.
-
-
- All the rules above are re-checked every time the content of the dropdown
- changes, the browser is resized or the orientation of the device changes.
-
-
-
-
This sounds like a lot, but with an example it will be crystal clear.
- In this example above I'm using the options
- verticalPosition
- and
- horizontalPosition
- to override the default behaviour.
-
-
-
- The default value for both options is
- auto, but you can pass
- verticalPosition=above|below
- and
- horizontalPosition=auto-right|right|center|left.
-
-
-
Narrow the window of the browser and play with scroll to see the automatic
- positioning in action.
-
-
renderInPlace
-
-
- Although by default the component renders the content in the root of the app
- and positions it absolutely, there are some situations where you want the
- content to be
- physically
- next to the trigger.
-
-
-
- To do so, pass
- renderInPlace=true
- to the component. Inspect the DOM of the next example to see the difference.
-
- Please note that when rendering the content in place, the vertical position
- will not automatically detect the best position based on the space around the
- trigger. You have to explicitly pass
- verticalPosition="above"
- to render it over the trigger.
-
-
-
matchTriggerWidth
-
-
- There is a few widgets in which the width of the floating box has to match the
- width of the trigger design reasons. Since this is common enough, there is an
- option to enable this behaviour.
-
- In the section about
- Custom Position
- strategies we will see how to totally customize how the dropdown is positioned
- by passing it a function. If you do so the options in this section will not
- work unless your function, which will receive those options, honors them.
-
-
-
- < Trigger events
- Disabled >
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/docs/styles.gts b/docs/app/templates/public-pages/docs/styles.gts
new file mode 100644
index 00000000..8df723bc
--- /dev/null
+++ b/docs/app/templates/public-pages/docs/styles.gts
@@ -0,0 +1,49 @@
+import { LinkTo } from '@ember/routing';
+import CodeExample from '../../../components/code-example';
+
+
+
Styles
+
+
+ Since this component doesn't any visual theme, you can apply styles to it
+ just with plain CSS or even adding the classes your favourite CSS framework
+ gives you.
+
+
+
+ If don't use any css pre-processor this is all. If you do use SASS or LESS,
+ the addon will know it and will have to
+ @import
+ the styles explicitly. This gives you the chance to set a few variables that
+ Ember Basic Dropdown will use.
+
+
+
+ There is only four variables you can tweak (Sass syntax)
+
+
+
+
+
+ If by example you want to change the colour of the overlay to be blue, you
+ could do this in your
+ app.scss/app.less.
+
+
+
+
+
+ In the next sections we'll give in more involved customizations.
+
- Since this component doesn't any visual theme, you can apply styles to it just
- with plain CSS or even adding the classes your favourite CSS framework gives
- you.
-
-
-
- If don't use any css pre-processor this is all. If you do use SASS or LESS,
- the addon will know it and will have to
- @import
- the styles explicitly. This gives you the chance to set a few variables that
- Ember Basic Dropdown will use.
-
-
-
- There is only four variables you can tweak (Sass syntax)
-
-
-{{#let (get-code-snippet "styles-1.scss") as |snippet|}}
-
-{{/let}}
-
-
- If by example you want to change the colour of the overlay to be blue, you
- could do this in your
- app.scss/app.less.
-
-
-{{#let (get-code-snippet "styles-2.scss") as |snippet|}}
-
-{{/let}}
-
-
- In the next sections we'll give in more involved customizations.
-
-
-
- < Overlays
- Custom position >
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/docs/test-helpers.gts b/docs/app/templates/public-pages/docs/test-helpers.gts
new file mode 100644
index 00000000..b85a06d4
--- /dev/null
+++ b/docs/app/templates/public-pages/docs/test-helpers.gts
@@ -0,0 +1,60 @@
+import { LinkTo } from '@ember/routing';
+import CodeExample from '../../../components/code-example';
+
+
+
Test helpers
+
+
+
+
+ Ember Basic Dropdown bundles some handy test helpers (clickDropdown
+ and
+ tapDropdown) that make it easier to simulate user interaction
+ in acceptance tests.
+
+
+
+ You can just have to import them at the top of your tests and call them
+ preceded by
+ await.
+
+
+
+
+
clickTrigger(scope = null, eventOptions)
+
+
+ Simulates a click to open or close the dropdown. As all integration test
+ helpers is already runloop aware, so you don't need to wrap it in
+ Ember.run.
+
+
+
+ In case there is more than one dropdown rendered at the same time you can
+ pass a string with the scope to trigger it over the desired one.
+
+
+
+
+
tapTrigger(scope = null, eventOptions)
+
+
+ Identical to
+ clickTrigger
+ but simulates a tap instead.
+
- Ember Basic Dropdown bundles some handy test helpers (clickDropdown
- and
- tapDropdown) that make it easier to simulate user interaction in
- acceptance tests.
-
-
-
- You can just have to import them at the top of your tests and call them
- preceded by
- await.
-
-
-{{#let (get-code-snippet "test-helpers-1-js.js") as |snippet|}}
-
-{{/let}}
-
-
clickTrigger(scope = null, eventOptions)
-
-
- Simulates a click to open or close the dropdown. As all integration test
- helpers is already runloop aware, so you don't need to wrap it in
- Ember.run.
-
-
-
- In case there is more than one dropdown rendered at the same time you can pass
- a string with the scope to trigger it over the desired one.
-
-
-{{#let (get-code-snippet "test-helpers-2-js.js") as |snippet|}}
-
-{{/let}}
-
-
tapTrigger(scope = null, eventOptions)
-
-
- Identical to
- clickTrigger
- but simulates a tap instead.
-
-
-{{#let (get-code-snippet "test-helpers-3-js.js") as |snippet|}}
-
-{{/let}}
-
-
- < Migrate from 7.0 to 8.0
- API reference >
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/docs/trigger-events.gts b/docs/app/templates/public-pages/docs/trigger-events.gts
new file mode 100644
index 00000000..d637ef88
--- /dev/null
+++ b/docs/app/templates/public-pages/docs/trigger-events.gts
@@ -0,0 +1,160 @@
+import { LinkTo } from '@ember/routing';
+import CodeExample from '../../../components/code-example';
+import TriggerEvents0Component from '../../../components/snippets/trigger-events-0';
+import TriggerEvents1Component from '../../../components/snippets/trigger-events-1';
+import TriggerEvents2Component from '../../../components/snippets/trigger-events-2';
+import TriggerEvents3Component from '../../../components/snippets/trigger-events-3';
+import TriggerEvents4Component from '../../../components/snippets/trigger-events-4';
+
+
+
Trigger events
+
+
+ The trigger is the component in charge of opening and closing the dropdown
+ (although it also closes if you click outside it). By default the event that
+ to which the trigger reacts is the
+ click
+ event.
+
+
+
+ You can change that passing the
+ eventType="mousedown"
+ to the trigger. Check below for both approaches. The difference is subtle.
+
+
+
+
+
+
+
+ This does not affect to the behavior of the dropdown on touch devices. On
+ mobile or tablets the dropdown automatically depends on the
+ "touchend"
+ event.
+
+
+
Event handlers
+
+
+ As with any regular HTML element, you can attach events to the trigger using
+ the
+ \{{on}}
+ element modifier. The events you subscribe to using this approach will aways
+ run before the default events that this component attaches, giving you a
+ chance to prevent the default behavior by calling
+ event.stopImmediatePropagation().
+
+
+
+ What can you do with this? Let's see some examples.
+
+
+
\{{on "keydown"}}
+
+
+ One real world situation where I found this to be necessary, is when you
+ want to open the dropdown with a key that usually does not open it, like by
+ example the arrow keys.
+
+
+
+
+
+
+
+ As with any other event, calling
+ e.stopImmediatePropagation()
+ will stop the default handler from running, so you can use this action to
+ prevent the
+ space,
+ enter
+ and
+ esc
+ keys from doing what they do by default.
+
+
+
\{{on "click"}}
+
+
+ The
+ click
+ event is the one that usually opens the dropdown, but you can pass your own
+ function.
+
+
+
+ Good examples of this are preventing the component from opening with the
+ mouse and/or react in some way to those attempts.
+
+
+
+
+
+
+
\{{on "touchend"}}
+
+
+ Exactly identical to
+ \{{on "click"}}
+ but for touch screens. I'm not even going to create another example.
+
+
+
\{{on "mouseenter"}} / \{{on "mouseleave"}}
+
+
+ I use this event to open the dropdown when you hover it and close it when
+ you leave but you can really use it a lot more.
+
+
+
+ By example, imagine that a dropdown is disabled and you want to highlight
+ some other element in the form that the user must enable first.
+
+
+
+
+
+
+
\{{on "focus"}}/\{{on "blur"}}
+
+
+ I've used this two events in conjuntion to style a parent element while some
+ of the components inside it have the focus, achieving a poor mans' version
+ of the upcoming CSS
+ :focus-within
+ pseudo selector.
+
+
+
+
+
+
+
+ This is the most involved example yet. Both the text input and the trigger
+ are focusable on their own, but by tracking when they get and loose the
+ focus we simulate that the entire input-group is focused, which would be
+ impossible just just CSS today.
+
+
+
+ Right now this behavior cannot be achieved across browsers without some help
+ from javascript.
+
+
+
+ Those are just some examples of the kind of behaviors you can implement
+ adding custom events to the trigger.
+
- The trigger is the component in charge of opening and closing the dropdown
- (although it also closes if you click outside it). By default the event that
- to which the trigger reacts is the
- click
- event.
-
-
-
- You can change that passing the
- eventType="mousedown"
- to the trigger. Check below for both approaches. The difference is subtle.
-
- This does not affect to the behavior of the dropdown on touch devices. On
- mobile or tablets the dropdown automatically depends on the
- "touchend"
- event.
-
-
-
Event handlers
-
-
- As with any regular HTML element, you can attach events to the trigger using
- the
- \{{on}}
- element modifier. The events you subscribe to using this approach will aways
- run before the default events that this component attaches, giving you a
- chance to prevent the default behavior by calling
- event.stopImmediatePropagation().
-
-
-
- What can you do with this? Let's see some examples.
-
-
-
\{{on "keydown"}}
-
-
- One real world situation where I found this to be necessary, is when you want
- to open the dropdown with a key that usually does not open it, like by example
- the arrow keys.
-
- As with any other event, calling
- e.stopImmediatePropagation()
- will stop the default handler from running, so you can use this action to
- prevent the
- space,
- enter
- and
- esc
- keys from doing what they do by default.
-
-
-
\{{on "click"}}
-
-
- The
- click
- event is the one that usually opens the dropdown, but you can pass your own
- function.
-
-
-
- Good examples of this are preventing the component from opening with the mouse
- and/or react in some way to those attempts.
-
- I've used this two events in conjuntion to style a parent element while some
- of the components inside it have the focus, achieving a poor mans' version of
- the upcoming CSS
- :focus-within
- pseudo selector.
-
- This is the most involved example yet. Both the text input and the trigger are
- focusable on their own, but by tracking when they get and loose the focus we
- simulate that the entire input-group is focused, which would be impossible
- just just CSS today.
-
-
-
- Right now this behavior cannot be achieved across browsers without some help
- from javascript.
-
-
-
- Those are just some examples of the kind of behaviors you can implement adding
- custom events to the trigger.
-
-
-
- < Dropdown events
- Content events >
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/index.gts b/docs/app/templates/public-pages/index.gts
new file mode 100644
index 00000000..1fb6277f
--- /dev/null
+++ b/docs/app/templates/public-pages/index.gts
@@ -0,0 +1,67 @@
+import { LinkTo } from '@ember/routing';
+
+
+
+
+ The basic dropdown that your
+
+ needs
+
+
+
+
+
+
+
Ready to go
+ Prepared out of the box with a minimum set of defaults that let you drop
+ it into your app, style it to your taste and not worry about the little
+ details.
+
+
+
+
+
+ \{{#ember as |ember|}}
+
+
+
With Ember for Ember
+ Built on top of other ember addons, it provides an API based on
+ contextual components that feels natural to use and style.
+
+
+
+
+
+
+
Low level primitives
+ Designed as a building block for your own components, it has no styles
+ and makes no assumptions. It gives you the right primitives to taylor
+ your perfect widget.
+
- Prepared out of the box with a minimum set of defaults that let you drop
- it into your app, style it to your taste and not worry about the little
- details.
-
-
-
-
-
- \{{#ember as |ember|}}
-
-
-
With Ember for Ember
- Built on top of other ember addons, it provides an API based on contextual
- components that feels natural to use and style.
-
-
-
-
-
-
-
Low level primitives
- Designed as a building block for your own components, it has no styles and
- makes no assumptions. It gives you the right primitives to taylor your
- perfect widget.
-
-
-
-
- Get
- started
-
\ No newline at end of file
diff --git a/docs/app/templates/public-pages/support-the-project.hbs b/docs/app/templates/public-pages/support-the-project.hbs
deleted file mode 100644
index 7c16d265..00000000
--- a/docs/app/templates/public-pages/support-the-project.hbs
+++ /dev/null
@@ -1,112 +0,0 @@
-
-
-
Support the Ember Power Project
-
-
- The Ember Power Project started with the goal of providing the Ember
- ecosystem with a set of curated components that cover common UX needs,
- built in Ember and designed work well in Ember apps., from following good
- patterns like DDAU to also be test-friend and render property in Fastboot.
-
-
-
- Over the time this evolved a bit into a theory of how an ecosystem should
- shape it's addons: Basic components that compose to create more complex
- components using good practices and allowing programmers to customize or
- directly replace the parts they don't like.
-
-
-
- Those components are divided in three tiers of complexity:
-
-
Building blocks: Components that do only one thing
- but do it right. They are low-level, take very few opinions and are
- focused on flexibility.
- Ember Basic
- Dropdown
- and
- ember-text-measurer
- are two of them.
-
- Power Components: Components that solve an UX problen
- The Ember Way. That is, they provide opinions and work out of
- the box with a nice public API, yet provide hooks for the users to
- customize or replace parts of it.
- Ember Power Select
- and
- Ember Power Calendar
- are of this kind.
-
-
- Power Meta-addons: Components that based on other
- components of the second tier but customize them for a specific
- purpose or pattern that is common enough to deserve to be shared so
- other users can use them without having to do the customization
- themselves.
- ember-power-select-sortable,
- ember-power-select-typeahead,
- ember-power-select-with-fallback
- are some examples, but there is many more.
-
-
-
-
-
- By creating a network if addons that compose between them to create even
- more addons, we share more. The more we share, the smaller our apps become
- because we don't need to reinvent wheels.
-
-
-
- If you enjoy the addons this project provides and they help you in your
- daily work, you can support the development of the addons and it's
- documentation pages with a any contribution you feel appropiate by
- clicking on the button below but above all, by contributing, opening
- issues and Pull Requests, answering questions in Slack or StackOverflow
- and above all, showing appreciation for everyone who works on the Open
- Source Software we all use everyday.
-
-
-
-
-
- Thank you!
-
-
-
\ No newline at end of file
diff --git a/docs/app/templates/scrolling-container.gts b/docs/app/templates/scrolling-container.gts
new file mode 100644
index 00000000..4302859c
--- /dev/null
+++ b/docs/app/templates/scrolling-container.gts
@@ -0,0 +1,22 @@
+import ScrollableContainer1Component from '../components/snippets/scrollable-container-1';
+import CodeExample from 'docs/components/code-example';
+
+
+
+
+
Usage with a scrollable container
+
+
+ What if your app doesn't scroll on the body tag? You can have the dropdown
+ render wherever you would like. If you render it on an element inside the
+ block that is scrolling the dropdown will automatically scroll with your
+ trigger. You need to give the target element
+ position: relative;
+ for the dropdown to be positioned properly.
+
- What if your app doesn't scroll on the body tag? You can have the dropdown
- render wherever you would like. If you render it on an element inside the
- block that is scrolling the dropdown will automatically scroll with your
- trigger. You need to give the target element
- position: relative;
- for the dropdown to be positioned properly.
-
\ No newline at end of file
diff --git a/docs/app/templates/snippets/system-wide-config-1-js.js b/docs/app/templates/snippets/system-wide-config-1-js.js
deleted file mode 100644
index 1c88e92e..00000000
--- a/docs/app/templates/snippets/system-wide-config-1-js.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import EmberBasicDropdown from 'ember-basic-dropdown/components/basic-dropdown';
-
-export default class extends EmberBasicDropdown {
- // Place here your system-wide preferences
- triggerComponent = 'my-custom-trigger';
- calculatePosition() {
- // your custom function to position the dropdown
- }
-}
diff --git a/docs/ember-cli-build.js b/docs/ember-cli-build.js
index 8aaf07ac..4145d349 100644
--- a/docs/ember-cli-build.js
+++ b/docs/ember-cli-build.js
@@ -6,7 +6,7 @@ const crawl = require('prember-crawler');
module.exports = function (defaults) {
const app = new EmberApp(defaults, {
'ember-cli-babel': { enableTypeScriptTransform: true },
- snippetPaths: ['app/components/snippets', 'app/templates/snippets'],
+ snippetPaths: ['app/components/snippets'],
prember: {
urls: crawl,
},
diff --git a/docs/eslint.config.mjs b/docs/eslint.config.mjs
index 5156ec47..19ae44b2 100644
--- a/docs/eslint.config.mjs
+++ b/docs/eslint.config.mjs
@@ -62,7 +62,7 @@ export default ts.config(
'dist/',
'node_modules/',
'coverage/',
- 'app/templates/snippets/',
+ 'app/components/snippets/*-snippet.*',
'!**/.*',
],
},
diff --git a/docs/package.json b/docs/package.json
index 5e9bdfe5..8fdbabc1 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -51,7 +51,6 @@
"@glint/environment-ember-template-imports": "^1.5.2",
"@glint/template": "1.5.2",
"@tsconfig/ember": "^3.0.11",
- "@types/prismjs": "^1.26.5",
"@types/qunit": "^2.19.13",
"@types/rsvp": "^4.0.9",
"broccoli-asset-rev": "^3.0.0",
@@ -80,6 +79,7 @@
"ember-source-channel-url": "^3.0.0",
"ember-template-imports": "^4.3.0",
"ember-template-lint": "^7.9.3",
+ "ember-truth-helpers": "^5.0.0",
"ember-try": "^4.0.0",
"eslint": "^9.36.0",
"eslint-config-prettier": "^10.1.8",
@@ -92,12 +92,11 @@
"prember-crawler": "^1.0.0",
"prettier": "^3.6.2",
"prettier-plugin-ember-template-tag": "^2.1.0",
- "prismjs": "^1.30.0",
- "prismjs-glimmer": "^1.1.1",
"qunit": "^2.24.1",
"qunit-dom": "^3.5.0",
"rsvp": "^4.8.5",
"sass": "^1.93.2",
+ "shiki": "^3.13.0",
"stylelint": "^16.24.0",
"stylelint-config-standard": "^39.0.0",
"tracked-built-ins": "^4.0.0",
diff --git a/docs/tests/integration/components/code-block-test.ts b/docs/tests/integration/components/code-block-test.ts
deleted file mode 100644
index deb637a1..00000000
--- a/docs/tests/integration/components/code-block-test.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { module, test } from 'qunit';
-import { setupRenderingTest } from 'docs/tests/helpers';
-import { render } from '@ember/test-helpers';
-import { hbs } from 'ember-cli-htmlbars';
-
-module('Integration | Component | code-block', function (hooks) {
- setupRenderingTest(hooks);
-
- test('it renders', async function (assert) {
- // Set any properties with this.set('myProperty', 'value');
- // Handle any actions with this.set('myAction', function(val) { ... });
-
- await render(
- hbs``,
- );
-
- assert.dom().hasText("console.log('hello');");
- });
-});
diff --git a/docs/tests/integration/components/code-inline-test.ts b/docs/tests/integration/components/code-inline-test.ts
deleted file mode 100644
index 073abb09..00000000
--- a/docs/tests/integration/components/code-inline-test.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { module, test } from 'qunit';
-import { setupRenderingTest } from 'docs/tests/helpers';
-import { render } from '@ember/test-helpers';
-import { hbs } from 'ember-cli-htmlbars';
-
-module('Integration | Component | code-inline', function (hooks) {
- setupRenderingTest(hooks);
-
- test('it renders', async function (assert) {
- // Set any properties with this.set('myProperty', 'value');
- // Handle any actions with this.set('myAction', function(val) { ... });
-
- await render(
- hbs``,
- );
-
- assert.dom().hasText("console.log('hello');");
- });
-});
diff --git a/docs/tsconfig.json b/docs/tsconfig.json
index d0f1c6f4..a15a5200 100644
--- a/docs/tsconfig.json
+++ b/docs/tsconfig.json
@@ -13,6 +13,6 @@
"docs/*": ["app/*"],
"*": ["types/*"]
},
- "types": ["ember-source/types", "./node_modules/@types/prismjs"]
+ "types": ["ember-source/types"]
}
}
diff --git a/ember-basic-dropdown/README.md b/ember-basic-dropdown/README.md
index e54d58db..2362036d 100644
--- a/ember-basic-dropdown/README.md
+++ b/ember-basic-dropdown/README.md
@@ -1,25 +1,32 @@
-# Ember-basic-dropdown
+[](https://www.npmjs.com/package/ember-basic-dropdown)
+[](https://emberobserver.com/addons/ember-basic-dropdown)
+
+[](https://discord.com/channels/480462759797063690/486202731766349824)
+[](https://github.com/cibernox/ember-basic-dropdown)
-[](https://travis-ci.org/cibernox/ember-basic-dropdown)
+# ember-basic-dropdown
This is a very minimal dropdown. That means that it is agnostic about what it is going to contain.
-It is intended to be a building block for more complex components but is perfectly usable. It is
-by example the addon on which [ember-power-select](https://www.ember-power-select.com)
-or `ember-paper`'s [menu component](http://miguelcobain.github.io/ember-paper/#/components/menu) are built upon.
+It is intended to be a building block for more complex components but is perfectly usable.
-### Compatibility
-
-- Ember.js v3.28 or above
-- Ember CLI v3.28 or above
-
-Versions 1.X require Ember 2.16 or greater
+### Highlights
-Version 2.X requires Ember 3.13 or greater
+- 🖊 **TypeScript support** – ships with type definitions for smooth TypeScript integration.
+- ✨ **Glint support** – template type-checking out of the box for safer templates.
+- 🚀 **FastBoot compatible** – works in server-rendered Ember apps.
+- 🕶 **Shadow DOM support** – can be rendered inside shadow roots without breaking positioning or events.
+- 🛠 **Addon v2 ready** – modern Ember Addon v2 format.
+- 🎯 **Headless & lightweight** – provides dropdown logic and accessibility without forcing styles.
+- 🔧 **Flexible API** – fully customizable trigger and content; you control the markup and styling.
+- 🧩 **Composable** – integrates seamlessly with other Ember addons (e.g. [ember-power-select](https://www.ember-power-select.com)).
+- ♿ **Accessible by default** – full keyboard navigation, ARIA attributes, and focus management built-in.
+- 📱 **Responsive positioning** – automatic repositioning on scroll and viewport boundaries.
-Version 3.X - 6.X requires Ember 3.24 or greater
+### Compatibility
-Version 7.X - 8.X requires Ember 3.28 or greater
+- Embroider or ember-auto-import v2
+- Ember.js v3.28 or above
### Installation
@@ -33,11 +40,15 @@ For more installation details see [documentation](https://ember-basic-dropdown.c
This component leverages contextual components for its API:
-```hbs
-
- Click me
- Content of the trigger
-
+```glimmer-ts
+import BasicDropdown from "ember-basic-dropdown/components/basic-dropdown";
+
+
+
+ Click me
+ Content of the trigger
+
+
```
The yielded `dropdown` object is the public API of the component, and contains
@@ -46,24 +57,29 @@ properties and actions that you can use to control the component.
```js
{
uniqueId: ,
- isOpen: ,
disabled: ,
+ isOpen: ,
actions: {
open: ,
close: ,
toggle: ,
- reposition:
- }
+ reposition: ,
+ registerTriggerElement: ,
+ registerDropdownElement: ,
+ getTriggerElement:
+ },
+ Trigger: ,
+ Content:
}
```
-Check the full documentation with live examples in http://ember-basic-dropdown.com
+Check the full documentation with live examples in https://ember-basic-dropdown.com
### Features
#### Renders on the body or in place
-By default this component will render the dropdown in the body using `#-in-element` and absolutely
+By default this component will render the dropdown in the body using `#in-element` and absolutely
position it to place it in the proper coordinates.
You can opt out to this behavior by passing `renderInPlace=true`. That will add the dropdown just
@@ -75,14 +91,18 @@ You don't need to care about adding or removing events, it does that for you.
You can make the dropdown content standout a little more by adding `overlay=true` to the content options, see example below. This will add a semi transparent overlay covering the whole screen. Also this will stop bubbling the click/touch event which closed the dropdown.
-```hbs
-
- Click me!
-
- {{! here! }}
- content!
-
-
+```glimmer-ts
+import BasicDropdown from "ember-basic-dropdown/components/basic-dropdown";
+
+
+
+ Click me!
+
+ {{! here! }}
+ content!
+
+
+
```
NOTE: If for some reason clicking outside a dropdown doesn't work, you might want to make sure the `` spans the entire viewport. Adding a css rule like `body {min-height: 100vh;}` would do the trick. It ensures that wherever you click on the page, it will close the dropdown.
@@ -91,15 +111,19 @@ NOTE: If for some reason clicking outside a dropdown doesn't work, you might wan
If you'd like the dropdown to close itself after a user clicks on it, you can use `dd.actions.close` from our public API.
-```hbs
-
- Click me!
-
-