chore: refactor some more
This commit is contained in:
parent
0aa4480e0c
commit
2f48f59244
15 changed files with 152 additions and 90 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -38,10 +38,6 @@ export class LionField extends FormControlMixin(
|
|||
};
|
||||
}
|
||||
|
||||
get _inputNode() {
|
||||
return /** @type {HTMLElement} */ (super._inputNode); // casts type
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.name = '';
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
19
packages/form-core/types/NativeTextFieldMixinTypes.d.ts
vendored
Normal file
19
packages/form-core/types/NativeTextFieldMixinTypes.d.ts
vendored
Normal 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;
|
||||
18
packages/form-core/types/ValueMixinTypes.d.ts
vendored
18
packages/form-core/types/ValueMixinTypes.d.ts
vendored
|
|
@ -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;
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue