Skip to content

Commit

Permalink
feat(select): add multiple option to limel-select, replacing limel-…
Browse files Browse the repository at this point in the history
…multi-select

re #203
  • Loading branch information
adrianschmidt committed Apr 1, 2019
1 parent 119270f commit 25d0f71
Show file tree
Hide file tree
Showing 11 changed files with 413 additions and 150 deletions.
22 changes: 22 additions & 0 deletions src/components/select-multiple/select-multiple.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@import '../../style/variables';

@import '@lime-material-16px/form-field/mdc-form-field';
@import '@lime-material-16px/checkbox/mdc-checkbox';
@import "@lime-material-16px/floating-label/mdc-floating-label";

.multi-select {
position: relative;

.multi-select-label {
padding-left: pxToRem(15);
}

.mdc-form-field {
display: flex;
}

.mdc-checkbox {
@include mdc-checkbox-ink-color(primary);
@include mdc-checkbox-container-colors(secondary, on-primary, secondary, on-primary);
}
}
141 changes: 141 additions & 0 deletions src/components/select-multiple/select-multiple.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { MDCCheckbox } from '@lime-material-16px/checkbox';
import { MDCFormField } from '@lime-material-16px/form-field';
import {
Component,
Element,
Event,
EventEmitter,
Prop,
State,
} from '@stencil/core';
import { Option } from '../../interface';
import { createRandomString } from '../../util/random-string';

@Component({
tag: 'limel-select-multiple',
shadow: true,
styleUrl: 'select-multiple.scss',
})
export class SelectMultiple {
@Prop({ reflectToAttr: true })
public disabled = false;

@Prop({ reflectToAttr: true })
public label: string;

@Prop()
public value: Option[] = [];

@Prop()
public options: Option[] = [];

@Event()
private change: EventEmitter<Option[]>;

@Element()
private limelMultiSelect: HTMLElement;

@State()
private fieldId = createRandomString();

@State()
private mdcCheckboxes = [];

public componentDidLoad() {
const elements = Array.from(
this.limelMultiSelect.shadowRoot.querySelectorAll(
'.multi-select .mdc-form-field'
)
);

elements.forEach(element => {
const formField = new MDCFormField(element);
const checkbox = new MDCCheckbox(element.firstChild);
formField.input = checkbox;
this.mdcCheckboxes.push(checkbox);
});

this.onChange();
}

public render() {
return (
<div class="multi-select">
<label
htmlFor={this.fieldId}
class="multi-select-label mdc-floating-label mdc-floating-label--float-above"
>
{this.label}
</label>
<div id={this.fieldId}>
{this.options.map((option: Option, index: number) => {
return this.renderCheckbox(index, option);
})}
</div>
</div>
);
}

private renderCheckbox(index: number, option: Option) {
return (
<div class="mdc-form-field ">
<div
class={`
mdc-checkbox
${this.disabled ? 'mdc-checkbox--disabled' : ''}
`}
>
<input
type="checkbox"
class="mdc-checkbox__native-control"
id={this.fieldId + '_' + index.toString()}
value={option.value}
checked={!!this.isOptionChecked(option)}
disabled={this.disabled}
onChange={this.onChange}
/>
<div class="mdc-checkbox__background">
<svg
class="mdc-checkbox__checkmark"
viewBox="0 0 24 24"
>
<path
class="mdc-checkbox__checkmark-path"
fill="none"
d="M1.73,12.91 8.1,19.28 22.79,4.59"
/>
</svg>
<div class="mdc-checkbox__mixedmark" />
</div>
</div>
<label htmlFor={this.fieldId + '_' + index.toString()}>
{option.text}
</label>
</div>
);
}

private isOptionChecked(option: Option) {
return this.value.find(checkedOption => {
return checkedOption.value === option.value;
});
}

private onChange = (event?) => {
if (event) {
event.stopPropagation();
}

const checked = this.options.filter(option => {
const optionChecked = this.mdcCheckboxes.some(mdcCheckbox => {
return (
mdcCheckbox.checked && mdcCheckbox.value === option.value
);
});
if (optionChecked) {
return option;
}
});
this.change.emit(checked);
};
}
File renamed without changes.
File renamed without changes.
135 changes: 135 additions & 0 deletions src/components/select-single/select-single.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { MDCSelect } from '@lime-material-16px/select';
import {
Component,
Element,
Event,
EventEmitter,
Prop,
State,
Watch,
} from '@stencil/core';
import { Option } from '../../interface';

@Component({
tag: 'limel-select-single',
shadow: true,
styleUrl: 'select-single.scss',
})
export class SelectSingle {
/**
* Set to `true` to disable the input.
*/
@Prop({ reflectToAttr: true })
public disabled = false;

/**
* The input label.
*/
@Prop({ reflectToAttr: true })
public label: string;

/**
* The currently selected item.
*/
@Prop()
public value: Option;

@Prop()
public options: Option[] = [];

@Event()
private change: EventEmitter<Option>;

@Element()
private limelSelect: HTMLElement;

@State()
private mdcSelect;

public componentDidLoad() {
const element = this.limelSelect.shadowRoot.querySelector(
'.mdc-select'
);
this.mdcSelect = new MDCSelect(element);
this.onChange();
}

public componentDidUnload() {
this.mdcSelect.destroy();
}

public render() {
return (
<div
class={`
mdc-select
${this.disabled ? 'mdc-select--disabled' : ''}
`}
>
<i class="mdc-select__dropdown-icon" />
<select
onChange={this.onChange}
class="mdc-select__native-control"
disabled={this.disabled}
>
{this.options.map(option => {
return (
<option
key={option.value}
value={option.value}
selected={
this.value
? option.value === this.value.value
: option.value === ''
}
disabled={option.disabled}
>
{option.text}
</option>
);
})}
</select>
<label
class={`
mdc-floating-label
${this.value ? 'mdc-floating-label--float-above' : ''}
`}
>
{this.label}
</label>
<div class="mdc-line-ripple" />
</div>
);
}

@Watch('options')
protected optionsWatcher(newOptions) {
if (newOptions && newOptions.length) {
setTimeout(() => {
this.mdcSelect.selectedIndex = 0;
this.onChange();
}, 0);
} else {
this.mdcSelect.value = null;
this.mdcSelect.selectedIndex = -1;
this.onChange();
}
}

private onChange = (event?) => {
if (event) {
event.stopPropagation();
}

const mdcValue = this.mdcSelect.value;
let value: Option;
if (mdcValue === '') {
value = null;
} else {
value = this.options.find(option => {
return mdcValue === option.value;
});
}
this.change.emit(value);
};
}
4 changes: 4 additions & 0 deletions src/components/select/select.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ When importing Option, see [Import Statements](/#import-statements).

<limel-example name="limel-example-select" />

### Select More Than One Option

<limel-example name="limel-example-select-multiple" path="select" />

### Initially Empty

<limel-example name="limel-example-select-initially-empty" path="select" />
Expand Down
Loading

0 comments on commit 25d0f71

Please sign in to comment.