@3mo/field
Version:
A set of field web components.
156 lines (154 loc) • 4.26 kB
JavaScript
import { __decorate } from "tslib";
import { html, property, event, Component, state, css } from '@a11d/lit';
import { SlotController } from '@3mo/slot-controller';
import { FocusController } from '@3mo/focus-controller';
/**
* @attr value - The field's value
* @attr label - The field's label
* @attr readonly - Whether the field is readonly
* @attr disabled - Whether the field is disabled
* @attr required - Whether the field is required
*
* @slot - The field's content
* @slot start - Content to be placed at the start of the field
* @slot end - Content to be placed at the end of the field
*
* @csspart container - Field's container
*
* @fires change
* @fires input
* @fires validityChange
*/
export class FieldComponent extends Component {
constructor() {
super(...arguments);
this.label = '';
this.readonly = false;
this.disabled = false;
this.required = false;
this.invalid = false;
this.focused = false;
this.slotController = new SlotController(this);
this.focusController = new FocusController(this, {
handleChange: (focused, bubbled, method) => {
this.focused = focused;
focused ? this.handleFocus(bubbled, method) : this.handleBlur(bubbled, method);
}
});
}
update(changedProperties) {
if (changedProperties.has('value')) {
this.valueUpdated();
}
super.update(changedProperties);
}
valueUpdated() {
this.inputValue = this.value;
this.validate();
}
handleFocus(bubbled, method) {
bubbled;
method;
}
handleBlur(bubbled, method) {
bubbled;
method;
}
handleInput(value, e) {
e?.stopPropagation();
this.inputValue = value;
this.input.dispatch(this.inputValue);
this.validate();
}
handleChange(value, e) {
e?.stopPropagation();
this.value = value;
this.change.dispatch(this.value);
this.validate();
}
get isPopulated() {
return this.inputValue !== undefined;
}
get isActive() {
return this.focused;
}
get isDense() {
return false;
}
static get styles() {
return css `
:host {
display: block;
}
mo-field {
width: 100%;
}
`;
}
get template() {
return html `
<mo-field id='field' exportparts='container'
label=${this.label}
?populated=${this.isPopulated}
?disabled=${this.disabled}
?readonly=${this.readonly}
?required=${this.required}
?dense=${this.isDense}
?invalid=${this.invalid}
?active=${this.isActive}
>
${this.startSlotTemplate}
${this.inputTemplate}
${this.endSlotTemplate}
</mo-field>
`;
}
get startSlotTemplate() {
return !this.slotController.hasAssignedElements('start') ? html.nothing : html `
<slot slot='start' name='start'></slot>
`;
}
get endSlotTemplate() {
return !this.slotController.hasAssignedElements('end') ? html.nothing : html `
<slot slot='end' name='end'></slot>
`;
}
async validate() {
const validity = await this.checkValidity();
this.invalid = !validity;
this.validityChange.dispatch(validity);
}
}
(() => {
property({ type: Object, bindingDefault: true })(FieldComponent.prototype, 'value');
})();
__decorate([
event()
], FieldComponent.prototype, "change", void 0);
__decorate([
event()
], FieldComponent.prototype, "input", void 0);
__decorate([
event()
], FieldComponent.prototype, "validityChange", void 0);
__decorate([
property()
], FieldComponent.prototype, "label", void 0);
__decorate([
property({ type: Boolean })
], FieldComponent.prototype, "readonly", void 0);
__decorate([
property({ type: Boolean })
], FieldComponent.prototype, "disabled", void 0);
__decorate([
property({ type: Boolean })
], FieldComponent.prototype, "required", void 0);
__decorate([
property({ type: Boolean })
], FieldComponent.prototype, "invalid", void 0);
__decorate([
state()
], FieldComponent.prototype, "focused", void 0);
__decorate([
state()
], FieldComponent.prototype, "inputValue", void 0);