Skip to content

Commit

Permalink
feat(ld-select): implement select component
Browse files Browse the repository at this point in the history
  • Loading branch information
borisdiakur committed Jun 17, 2021
1 parent 804685a commit f8415c0
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 38 deletions.
13 changes: 13 additions & 0 deletions src/liquid/components/ld-checkbox/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,19 @@ The `ld-checkbox` web component provides a low level API for integrating the com
| `tone` | `tone` | Checkbox tone. Use `'dark'` on white backgrounds. Default is a light tone. | `"dark"` | `undefined` |


## Dependencies

### Used by

- [ld-option](../ld-option)

### Graph
```mermaid
graph TD;
ld-option --> ld-checkbox
style ld-checkbox fill:#f9f,stroke:#333,stroke-width:4px
```

----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*
6 changes: 5 additions & 1 deletion src/liquid/components/ld-label/ld-label.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ ld-label {
.ld-label {
font: var(--ld-typo-label-s);
display: inline-grid;
gap: var(--ld-sp-6);
row-gap: var(--ld-sp-6);
column-gap: 0;
max-width: 100%;
align-self: flex-start;

Expand All @@ -18,11 +19,14 @@ ld-label {
.ld-label--right {
justify-content: flex-start;
align-items: center;
column-gap: var(--ld-sp-6);
row-gap: 0;

> ld-input-message,
> .ld-input-message {
order: 0;
grid-area: message;
margin-top: var(--ld-sp-4);
}

> :not(ld-input-message):not(.ld-input-message) {
Expand Down
13 changes: 13 additions & 0 deletions src/liquid/components/ld-label/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,19 @@ The default size is small. You can use a slightly bigger label (size medium) by
| `size` | `size` | Size of the label. Default is small. | `"m"` | `undefined` |


## Dependencies

### Used by

- [ld-option](../ld-option)

### Graph
```mermaid
graph TD;
ld-option --> ld-label
style ld-label fill:#f9f,stroke:#333,stroke-width:4px
```

----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*
20 changes: 17 additions & 3 deletions src/liquid/components/ld-option/ld-option.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,35 @@ export class LdOption {
* should this option be selected. If this attribute is omitted, the value is taken
* from the text content of the option element.
*/
@Prop({ mutable: true }) value: string
@Prop({ mutable: true, reflect: true }) value: string

/**
* If present, this boolean attribute indicates that the option is selected.
*/
@Prop({ mutable: true, reflect: true }) selected: false

private handleInput(ev) {
this.selected = ev.target.checked
}

componentWillLoad() {
if (typeof this.value === 'undefined') this.value = this.el.innerText
if (typeof this.value === 'undefined') {
setTimeout(() => {
this.value = this.el.innerText
})
}
}

render() {
return (
<Host class="ld-option" role="option" tabindex="-1">
<slot></slot>
<ld-label position="right" size="m">
<ld-checkbox
checked={this.selected}
onInput={this.handleInput.bind(this)}
></ld-checkbox>
<slot></slot>
</ld-label>
</Host>
)
}
Expand Down
15 changes: 15 additions & 0 deletions src/liquid/components/ld-option/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,21 @@
| `value` | `value` | The content of this attribute represents the value to be submitted with the form, should this option be selected. If this attribute is omitted, the value is taken from the text content of the option element. | `string` | `undefined` |


## Dependencies

### Depends on

- [ld-label](../ld-label)
- [ld-checkbox](../ld-checkbox)

### Graph
```mermaid
graph TD;
ld-option --> ld-label
ld-option --> ld-checkbox
style ld-option fill:#f9f,stroke:#333,stroke-width:4px
```

----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*
91 changes: 65 additions & 26 deletions src/liquid/components/ld-select/ld-select.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import '../../components' // type definitions for type checks and intelliSense
import { Component, Element, h, Host, Prop, State, Watch } from '@stencil/core'
import {
Component,
Element,
h,
Host,
Listen,
Prop,
State,
Watch,
} from '@stencil/core'
import type {
Instance as PopperInstance,
StrictModifiers,
} from '@popperjs/core'
import { createPopper } from '@popperjs/core/lib/popper-lite.js'
import preventOverflow from '@popperjs/core/lib/modifiers/preventOverflow.js'
import flip from '@popperjs/core/lib/modifiers/flip.js'
import offset from '@popperjs/core/lib/modifiers/offset.js'
import { makeInert, unmakeInert } from '../../utils/makeInert'
import { LdOption } from '../ld-option/ld-option'

Expand All @@ -30,15 +40,18 @@ export class LdSelect {
/** Multiselect mode. */
@Prop() multiple = false

/** Used as trigger button label in multiselect mode. */
@Prop() label: string
/**
* Used as trigger button label in multiselect mode
* and in single select mode if nothing is selected.
*/
@Prop() placeholder: string

/** Used to specify the name of the control. */
@Prop() name: string

@State() expanded = false

@State() selected: string[] = []
@State() selected: LdOption[] = []

@Watch('expanded')
updateInert() {
Expand All @@ -49,12 +62,17 @@ export class LdSelect {
}
}

private updatePopper() {
offset.options = { offset: [0, -21] }
this.popper.update()
}

private initPopper() {
this.popper = createPopper<StrictModifiers>(
this.selectRef,
this.popperRef,
{
modifiers: [preventOverflow, flip],
modifiers: [preventOverflow, flip, offset],
placement: 'bottom-start',
}
)
Expand All @@ -80,27 +98,17 @@ export class LdSelect {
})

setTimeout(() => {
this.selected = ((childrenArr as unknown[]) as LdOption[])
.filter((child) => child.selected)
.map((child) => child.value)
if (!this.multiple && !this.selected.length) {
this.selected = [childrenArr[0].textContent]
}
this.selected = ((childrenArr as unknown[]) as LdOption[]).filter(
(child) => child.selected
)
})
}

private togglePopper() {
this.expanded = !this.expanded
}

private handleClick(ev) {
ev.preventDefault()

if (!this.popper) this.initPopper()

const target = ev.target
if (target.closest('.ld-select__btn-trigger')) {
this.togglePopper()
if (this.expanded) {
this.updatePopper()
}
}

Expand All @@ -109,6 +117,18 @@ export class LdSelect {
this.updateInert()
}

private handleInput(ev) {
if (!this.multiple) {
Array.from(this.el.querySelectorAll('ld-option')).forEach((option) => {
if (option !== ev.target.closest('ld-option')) {
option.selected = false
}
})
this.togglePopper()
}
this.initOptions()
}

private handleKeyDown(ev: KeyboardEvent) {
switch (ev.key) {
case 'ArrowDown':
Expand Down Expand Up @@ -172,6 +192,25 @@ export class LdSelect {
// with the string of characters typed.
}

@Listen('click', {
target: 'window',
})
handleClick(ev) {
const target = ev.target

if (target.closest('ld-select') !== this.el) {
this.expanded = false
return
}

if (!this.popper) this.initPopper()

if (target.closest('.ld-select__btn-trigger')) {
ev.preventDefault()
this.togglePopper()
}
}

componentDidLoad() {
this.initOptions()
this.updateInert()
Expand All @@ -193,11 +232,7 @@ export class LdSelect {
if (this.expanded) popperCl += ' ld-select__popper--expanded'

return (
<Host
class="ld-select"
onKeyDown={this.handleKeyDown.bind(this)}
onClick={this.handleClick.bind(this)}
>
<Host class="ld-select" onKeyDown={this.handleKeyDown.bind(this)}>
<div
class="ld-select__select"
ref={(el) => (this.selectRef = el as HTMLElement)}
Expand All @@ -207,12 +242,16 @@ export class LdSelect {
aria-haspopup="listbox"
aria-expanded={this.expanded ? 'true' : 'false'}
>
{this.multiple ? this.label : this.selected[0]}
{this.multiple
? this.placeholder
: ((this.selected[0] as unknown) as HTMLElement)?.textContent ||
this.placeholder}
</button>
</div>
<ul
role="listbox"
class={popperCl}
onInput={this.handleInput.bind(this)}
ref={(el) => (this.popperRef = el as HTMLElement)}
>
<slot></slot>
Expand Down
25 changes: 17 additions & 8 deletions src/liquid/components/ld-select/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ permalink: components/ld-select/
## Examples

### Single select mode

{% example %}
<ld-select>
<ld-option value="">Pick a fruit</ld-option>
<ld-select placeholder="Pick a fruit">
<ld-option value="apple">Apple</ld-option>
<ld-option value="banana">Banana</ld-option>
<ld-option value="strawberry">Strawberry</ld-option>
Expand All @@ -25,24 +26,32 @@ permalink: components/ld-select/
function change() {
const newItem = document.createElement("ld-option")
newItem.innerText = 'Orange'
document.getElementById('popper').appendChild(newItem)
document.querySelector('ld-select ul').appendChild(newItem)
}
</script>
<button onclick="change()">change</button>
{% endexample %}

### Multiple select mode

{% example %}
<ld-select placeholder="Pick a fruit" multiple>
<ld-option value="apple">Apple</ld-option>
<ld-option value="banana">Banana</ld-option>
<ld-option value="strawberry">Strawberry</ld-option>
</ld-select>
{% endexample %}

<!-- Auto Generated Below -->


## Properties

| Property | Attribute | Description | Type | Default |
| ---------- | ---------- | ------------------------------------------------- | --------- | ----------- |
| `label` | `label` | Used as trigger button label in multiselect mode. | `string` | `undefined` |
| `multiple` | `multiple` | Multiselect mode. | `boolean` | `false` |
| `name` | `name` | Used to specify the name of the control. | `string` | `undefined` |
| Property | Attribute | Description | Type | Default |
| ------------- | ------------- | -------------------------------------------------------------------------------------------------- | --------- | ----------- |
| `multiple` | `multiple` | Multiselect mode. | `boolean` | `false` |
| `name` | `name` | Used to specify the name of the control. | `string` | `undefined` |
| `placeholder` | `placeholder` | Used as trigger button label in multiselect mode and in single select mode if nothing is selected. | `string` | `undefined` |


## Slots
Expand Down

0 comments on commit f8415c0

Please sign in to comment.