chore: refactor some more

This commit is contained in:
Joren Broekema 2020-10-01 14:24:05 +02:00
parent 0aa4480e0c
commit 2f48f59244
15 changed files with 152 additions and 90 deletions

View file

@ -122,6 +122,23 @@ const FormatMixinImplementation = superclass =>
}
}
get value() {
return (this._inputNode && this._inputNode.value) || this.__value || '';
}
// We don't delegate, because we want to preserve caret position via _setValueAndPreserveCaret
/** @type {string} */
set value(value) {
// if not yet connected to dom can't change the value
if (this._inputNode) {
this._inputNode.value = value;
/** @type {string | undefined} */
this.__value = undefined;
} else {
this.__value = value;
}
}
/**
* Converts formattedValue to modelValue
* For instance, a localized date to a Date Object

View file

@ -38,10 +38,6 @@ export class LionField extends FormControlMixin(
};
}
get _inputNode() {
return /** @type {HTMLElement} */ (super._inputNode); // casts type
}
constructor() {
super();
this.name = '';

View file

@ -1,18 +1,50 @@
import { dedupeMixin } from '@lion/core';
/**
* @typedef {import('../types/ValueMixinTypes').ValueMixin} ValueMixin
* @type {ValueMixin}
* @param {import('@open-wc/dedupe-mixin').Constructor<import('../types/ValueMixinTypes').LionFieldWithValue>} superclass} superclass
* @typedef {import('../types/NativeTextFieldMixinTypes').NativeTextFieldMixin} NativeTextFieldMixin
* @type {NativeTextFieldMixin}
* @param {import('@open-wc/dedupe-mixin').Constructor<import('../types/NativeTextFieldMixinTypes').NativeTextField>} superclass} superclass
*/
const ValueMixinImplementation = superclass =>
class ValueMixin extends superclass {
const NativeTextFieldMixinImplementation = superclass =>
class NativeTextFieldMixin extends superclass {
/** @type {number} */
get selectionStart() {
const native = this._inputNode;
if (native && native.selectionStart) {
return native.selectionStart;
}
return 0;
}
set selectionStart(value) {
const native = this._inputNode;
if (native && native.selectionStart) {
native.selectionStart = value;
}
}
/** @type {number} */
get selectionEnd() {
const native = this._inputNode;
if (native && native.selectionEnd) {
return native.selectionEnd;
}
return 0;
}
set selectionEnd(value) {
const native = this._inputNode;
if (native && native.selectionEnd) {
native.selectionEnd = value;
}
}
get value() {
return (this._inputNode && this._inputNode.value) || this.__value || '';
}
// We don't delegate, because we want to preserve caret position via _setValueAndPreserveCaret
/** @type {string} */
/** @param {string} value */
set value(value) {
// if not yet connected to dom can't change the value
if (this._inputNode) {
@ -54,4 +86,4 @@ const ValueMixinImplementation = superclass =>
}
};
export const ValueMixin = dedupeMixin(ValueMixinImplementation);
export const NativeTextFieldMixin = dedupeMixin(NativeTextFieldMixinImplementation);

View file

@ -125,6 +125,9 @@ const FormGroupMixinImplementation = superclass =>
constructor() {
super();
// inputNode = this, which always requires a value prop
this.value = '';
this.disabled = false;
this.submitted = false;
this.dirty = false;

View file

@ -6,6 +6,10 @@ import { DisabledHost } from '@lion/core/types/DisabledMixinTypes';
import { LionValidationFeedback } from '../src/validate/LionValidationFeedback';
import { FormRegisteringHost } from './registration/FormRegisteringMixinTypes';
declare interface HTMLElementWithValue extends HTMLElement {
value: string;
}
export class FormControlHost {
static get styles(): CSSResult | CSSResult[];
/**
@ -52,7 +56,7 @@ export class FormControlHost {
get fieldName(): string;
__fieldName: string | undefined;
get slots(): SlotsMap;
get _inputNode(): HTMLElement;
get _inputNode(): HTMLElementWithValue;
get _labelNode(): HTMLElement;
get _helpTextNode(): HTMLElement;
get _feedbackNode(): LionValidationFeedback | undefined;

View file

@ -9,7 +9,6 @@ export declare class FormatHost {
serializedValue: string;
formatOn: string;
formatOptions: FormatNumberOptions;
value: string;
__preventRecursiveTrigger: boolean;
__isHandlingUserInput: boolean;
@ -18,6 +17,9 @@ export declare class FormatHost {
serializer(v: unknown): string;
deserializer(v: string): unknown;
get value(): string;
set value(value: string);
_calculateValues(opts: { source: 'model' | 'serialized' | 'formatted' | null }): void;
__callParser(value: string | undefined): object;
__callFormatter(): string;

View file

@ -0,0 +1,19 @@
import { Constructor } from '@open-wc/dedupe-mixin';
import { LionField } from '@lion/form-core/src/LionField';
export declare class NativeTextField extends LionField {
get _inputNode(): HTMLTextAreaElement | HTMLInputElement;
}
export declare class NativeTextFieldHost {
get selectionStart(): number;
set selectionStart(value: number);
get selectionEnd(): number;
set selectionEnd(value: number);
}
export declare function NativeTextFieldImplementation<T extends Constructor<NativeTextField>>(
superclass: T,
): T & Constructor<NativeTextFieldHost> & NativeTextFieldHost;
export type NativeTextFieldMixin = typeof NativeTextFieldImplementation;

View file

@ -1,18 +0,0 @@
import { Constructor } from '@open-wc/dedupe-mixin';
import { LionField } from '@lion/form-core/src/LionField';
export declare class LionFieldWithValue extends LionField {
get _inputNode(): HTMLTextAreaElement | HTMLInputElement | HTMLSelectElement;
}
export declare class ValueHost {
get value(): string;
set value(value: string);
_setValueAndPreserveCaret(newValue: string): void;
}
export declare function ValueImplementation<T extends Constructor<LionFieldWithValue>>(
superclass: T,
): T & Constructor<ValueHost> & ValueHost;
export type ValueMixin = typeof ValueImplementation;

View file

@ -53,14 +53,11 @@ export class LionInputAmount extends LocalizeMixin(LionInput) {
}
static get styles() {
return [
super.styles,
css`
.input-group__container > .input-group__input ::slotted(.form-control) {
text-align: right;
}
`,
];
return css`
.input-group__container > .input-group__input ::slotted(.form-control) {
text-align: right;
}
`;
}
constructor() {

View file

@ -10,14 +10,11 @@ import { IsNumber, MinNumber, MaxNumber } from '@lion/form-core';
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you.
export class LionInputStepper extends LionInput {
static get styles() {
return [
super.styles,
css`
.input-group__container > .input-group__input ::slotted(.form-control) {
text-align: center;
}
`,
];
return css`
.input-group__container > .input-group__input ::slotted(.form-control) {
text-align: center;
}
`;
}
static get properties() {

View file

@ -1,14 +1,13 @@
import { LionField } from '@lion/form-core';
import { ValueMixin } from '@lion/form-core/src/ValueMixin';
import { NativeTextFieldMixin } from '@lion/form-core/src/NativeTextFieldMixin';
/**
* LionInput: extension of lion-field with native input element in place and user friendly API.
*
* @customElement lion-input
* @extends {LionField}
*/
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you.
export class LionInput extends ValueMixin(LionField) {
export class LionInput extends NativeTextFieldMixin(LionField) {
static get properties() {
return {
/**
@ -54,38 +53,6 @@ export class LionInput extends ValueMixin(LionField) {
return /** @type {HTMLInputElement} */ (super._inputNode); // casts type
}
/** @type {number} */
get selectionStart() {
const native = this._inputNode;
if (native && native.selectionStart) {
return native.selectionStart;
}
return 0;
}
set selectionStart(value) {
const native = this._inputNode;
if (native && native.selectionStart) {
native.selectionStart = value;
}
}
/** @type {number} */
get selectionEnd() {
const native = this._inputNode;
if (native && native.selectionEnd) {
return native.selectionEnd;
}
return 0;
}
set selectionEnd(value) {
const native = this._inputNode;
if (native && native.selectionEnd) {
native.selectionEnd = value;
}
}
constructor() {
super();
this.readOnly = false;

View file

@ -9,6 +9,7 @@ import { LionOptions } from './LionOptions.js';
// list items that can be found via MutationObserver or registration (.formElements)
/**
* @typedef {import('@lion/form-core/types/FormControlMixinTypes').HTMLElementWithValue} HTMLElementWithValue
* @typedef {import('./LionOption').LionOption} LionOption
* @typedef {import('../types/ListboxMixinTypes').ListboxMixin} ListboxMixin
* @typedef {import('../types/ListboxMixinTypes').ListboxHost} ListboxHost
@ -131,15 +132,15 @@ const ListboxMixinImplementation = superclass =>
* @configure FormControlMixin
*/
get _inputNode() {
return /** @type {HTMLElement} */ (this.querySelector('[slot="input"]'));
return /** @type {HTMLElementWithValue} */ (this.querySelector('[slot="input"]'));
}
/**
* @overridable
* @type {LionOptions}
*/
get _listboxNode() {
return /** @type {LionOptions} */ (this._inputNode);
// Cast to unknown first, since HTMLElementWithValue is not compatible with LionOptions
return /** @type {LionOptions} */ (/** @type {unknown} */ (this._inputNode));
}
/**

View file

@ -1,6 +1,5 @@
/* eslint-disable max-classes-per-file */
import { LionField } from '@lion/form-core';
import { ValueMixin } from '@lion/form-core/src/ValueMixin';
class LionFieldWithSelect extends LionField {
/**
@ -38,14 +37,48 @@ class LionFieldWithSelect extends LionField {
* usability for keyboard and screen reader users.
*
* @customElement lion-select
* @extends {LionField}
*/
export class LionSelect extends ValueMixin(LionFieldWithSelect) {
export class LionSelect extends LionFieldWithSelect {
connectedCallback() {
super.connectedCallback();
this._inputNode.addEventListener('change', this._proxyChangeEvent);
}
// FIXME: For some reason we have to override this FormatMixin getter/setter pair for the tests to pass
get value() {
return (this._inputNode && this._inputNode.value) || this.__value || '';
}
// We don't delegate, because we want to preserve caret position via _setValueAndPreserveCaret
/** @type {string} */
set value(value) {
// if not yet connected to dom can't change the value
if (this._inputNode) {
this._inputNode.value = value;
/** @type {string | undefined} */
this.__value = undefined;
} else {
this.__value = value;
}
}
/** @param {import('lit-element').PropertyValues } changedProperties */
updated(changedProperties) {
super.updated(changedProperties);
if (changedProperties.has('disabled')) {
this._inputNode.disabled = this.disabled;
this.validate();
}
if (changedProperties.has('name')) {
this._inputNode.name = this.name;
}
if (changedProperties.has('autocomplete')) {
this._inputNode.autocomplete = /** @type {string} */ (this.autocomplete);
}
}
disconnectedCallback() {
super.disconnectedCallback();
this._inputNode.removeEventListener('change', this._proxyChangeEvent);

View file

@ -72,6 +72,9 @@ export class LionSwitchButton extends DisabledWithTabIndexMixin(LitElement) {
constructor() {
super();
// inputNode = this, which always requires a value prop
this.value = '';
this.role = 'switch';
this.checked = false;
this.__toggleChecked = this.__toggleChecked.bind(this);

View file

@ -3,7 +3,7 @@
import autosize from 'autosize/src/autosize.js';
import { LionField } from '@lion/form-core';
import { css } from '@lion/core';
import { ValueMixin } from '@lion/form-core/src/ValueMixin';
import { NativeTextFieldMixin } from '@lion/form-core/src/NativeTextFieldMixin';
class LionFieldWithTextArea extends LionField {
/**
@ -22,7 +22,7 @@ class LionFieldWithTextArea extends LionField {
* @customElement lion-textarea
*/
// @ts-expect-error false positive, parent properties get merged by lit-element already
export class LionTextarea extends ValueMixin(LionFieldWithTextArea) {
export class LionTextarea extends NativeTextFieldMixin(LionFieldWithTextArea) {
static get properties() {
return {
maxRows: {
@ -78,6 +78,15 @@ export class LionTextarea extends ValueMixin(LionFieldWithTextArea) {
/** @param {import('lit-element').PropertyValues } changedProperties */
updated(changedProperties) {
super.updated(changedProperties);
if (changedProperties.has('name')) {
this._inputNode.name = this.name;
}
if (changedProperties.has('autocomplete')) {
this._inputNode.autocomplete = /** @type {string} */ (this.autocomplete);
}
if (changedProperties.has('disabled')) {
this._inputNode.disabled = this.disabled;
this.validate();