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
|
* Converts formattedValue to modelValue
|
||||||
* For instance, a localized date to a Date Object
|
* 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() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.name = '';
|
this.name = '';
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,50 @@
|
||||||
import { dedupeMixin } from '@lion/core';
|
import { dedupeMixin } from '@lion/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import('../types/ValueMixinTypes').ValueMixin} ValueMixin
|
* @typedef {import('../types/NativeTextFieldMixinTypes').NativeTextFieldMixin} NativeTextFieldMixin
|
||||||
* @type {ValueMixin}
|
* @type {NativeTextFieldMixin}
|
||||||
* @param {import('@open-wc/dedupe-mixin').Constructor<import('../types/ValueMixinTypes').LionFieldWithValue>} superclass} superclass
|
* @param {import('@open-wc/dedupe-mixin').Constructor<import('../types/NativeTextFieldMixinTypes').NativeTextField>} superclass} superclass
|
||||||
*/
|
*/
|
||||||
const ValueMixinImplementation = superclass =>
|
const NativeTextFieldMixinImplementation = superclass =>
|
||||||
class ValueMixin extends 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() {
|
get value() {
|
||||||
return (this._inputNode && this._inputNode.value) || this.__value || '';
|
return (this._inputNode && this._inputNode.value) || this.__value || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't delegate, because we want to preserve caret position via _setValueAndPreserveCaret
|
// We don't delegate, because we want to preserve caret position via _setValueAndPreserveCaret
|
||||||
/** @type {string} */
|
/** @param {string} value */
|
||||||
set value(value) {
|
set value(value) {
|
||||||
// if not yet connected to dom can't change the value
|
// if not yet connected to dom can't change the value
|
||||||
if (this._inputNode) {
|
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() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
// inputNode = this, which always requires a value prop
|
||||||
|
this.value = '';
|
||||||
|
|
||||||
this.disabled = false;
|
this.disabled = false;
|
||||||
this.submitted = false;
|
this.submitted = false;
|
||||||
this.dirty = false;
|
this.dirty = false;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,10 @@ import { DisabledHost } from '@lion/core/types/DisabledMixinTypes';
|
||||||
import { LionValidationFeedback } from '../src/validate/LionValidationFeedback';
|
import { LionValidationFeedback } from '../src/validate/LionValidationFeedback';
|
||||||
import { FormRegisteringHost } from './registration/FormRegisteringMixinTypes';
|
import { FormRegisteringHost } from './registration/FormRegisteringMixinTypes';
|
||||||
|
|
||||||
|
declare interface HTMLElementWithValue extends HTMLElement {
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class FormControlHost {
|
export class FormControlHost {
|
||||||
static get styles(): CSSResult | CSSResult[];
|
static get styles(): CSSResult | CSSResult[];
|
||||||
/**
|
/**
|
||||||
|
|
@ -52,7 +56,7 @@ export class FormControlHost {
|
||||||
get fieldName(): string;
|
get fieldName(): string;
|
||||||
__fieldName: string | undefined;
|
__fieldName: string | undefined;
|
||||||
get slots(): SlotsMap;
|
get slots(): SlotsMap;
|
||||||
get _inputNode(): HTMLElement;
|
get _inputNode(): HTMLElementWithValue;
|
||||||
get _labelNode(): HTMLElement;
|
get _labelNode(): HTMLElement;
|
||||||
get _helpTextNode(): HTMLElement;
|
get _helpTextNode(): HTMLElement;
|
||||||
get _feedbackNode(): LionValidationFeedback | undefined;
|
get _feedbackNode(): LionValidationFeedback | undefined;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ export declare class FormatHost {
|
||||||
serializedValue: string;
|
serializedValue: string;
|
||||||
formatOn: string;
|
formatOn: string;
|
||||||
formatOptions: FormatNumberOptions;
|
formatOptions: FormatNumberOptions;
|
||||||
value: string;
|
|
||||||
__preventRecursiveTrigger: boolean;
|
__preventRecursiveTrigger: boolean;
|
||||||
__isHandlingUserInput: boolean;
|
__isHandlingUserInput: boolean;
|
||||||
|
|
||||||
|
|
@ -18,6 +17,9 @@ export declare class FormatHost {
|
||||||
serializer(v: unknown): string;
|
serializer(v: unknown): string;
|
||||||
deserializer(v: string): unknown;
|
deserializer(v: string): unknown;
|
||||||
|
|
||||||
|
get value(): string;
|
||||||
|
set value(value: string);
|
||||||
|
|
||||||
_calculateValues(opts: { source: 'model' | 'serialized' | 'formatted' | null }): void;
|
_calculateValues(opts: { source: 'model' | 'serialized' | 'formatted' | null }): void;
|
||||||
__callParser(value: string | undefined): object;
|
__callParser(value: string | undefined): object;
|
||||||
__callFormatter(): string;
|
__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() {
|
static get styles() {
|
||||||
return [
|
return css`
|
||||||
super.styles,
|
|
||||||
css`
|
|
||||||
.input-group__container > .input-group__input ::slotted(.form-control) {
|
.input-group__container > .input-group__input ::slotted(.form-control) {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
`,
|
`;
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
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.
|
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you.
|
||||||
export class LionInputStepper extends LionInput {
|
export class LionInputStepper extends LionInput {
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return css`
|
||||||
super.styles,
|
|
||||||
css`
|
|
||||||
.input-group__container > .input-group__input ::slotted(.form-control) {
|
.input-group__container > .input-group__input ::slotted(.form-control) {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
`,
|
`;
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
import { LionField } from '@lion/form-core';
|
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.
|
* LionInput: extension of lion-field with native input element in place and user friendly API.
|
||||||
*
|
*
|
||||||
* @customElement lion-input
|
* @customElement lion-input
|
||||||
* @extends {LionField}
|
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you.
|
// @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() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
|
|
@ -54,38 +53,6 @@ export class LionInput extends ValueMixin(LionField) {
|
||||||
return /** @type {HTMLInputElement} */ (super._inputNode); // casts type
|
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() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.readOnly = false;
|
this.readOnly = false;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { LionOptions } from './LionOptions.js';
|
||||||
// list items that can be found via MutationObserver or registration (.formElements)
|
// 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('./LionOption').LionOption} LionOption
|
||||||
* @typedef {import('../types/ListboxMixinTypes').ListboxMixin} ListboxMixin
|
* @typedef {import('../types/ListboxMixinTypes').ListboxMixin} ListboxMixin
|
||||||
* @typedef {import('../types/ListboxMixinTypes').ListboxHost} ListboxHost
|
* @typedef {import('../types/ListboxMixinTypes').ListboxHost} ListboxHost
|
||||||
|
|
@ -131,15 +132,15 @@ const ListboxMixinImplementation = superclass =>
|
||||||
* @configure FormControlMixin
|
* @configure FormControlMixin
|
||||||
*/
|
*/
|
||||||
get _inputNode() {
|
get _inputNode() {
|
||||||
return /** @type {HTMLElement} */ (this.querySelector('[slot="input"]'));
|
return /** @type {HTMLElementWithValue} */ (this.querySelector('[slot="input"]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @overridable
|
* @overridable
|
||||||
* @type {LionOptions}
|
|
||||||
*/
|
*/
|
||||||
get _listboxNode() {
|
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 */
|
/* eslint-disable max-classes-per-file */
|
||||||
import { LionField } from '@lion/form-core';
|
import { LionField } from '@lion/form-core';
|
||||||
import { ValueMixin } from '@lion/form-core/src/ValueMixin';
|
|
||||||
|
|
||||||
class LionFieldWithSelect extends LionField {
|
class LionFieldWithSelect extends LionField {
|
||||||
/**
|
/**
|
||||||
|
|
@ -38,14 +37,48 @@ class LionFieldWithSelect extends LionField {
|
||||||
* usability for keyboard and screen reader users.
|
* usability for keyboard and screen reader users.
|
||||||
*
|
*
|
||||||
* @customElement lion-select
|
* @customElement lion-select
|
||||||
* @extends {LionField}
|
|
||||||
*/
|
*/
|
||||||
export class LionSelect extends ValueMixin(LionFieldWithSelect) {
|
export class LionSelect extends LionFieldWithSelect {
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this._inputNode.addEventListener('change', this._proxyChangeEvent);
|
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() {
|
disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
this._inputNode.removeEventListener('change', this._proxyChangeEvent);
|
this._inputNode.removeEventListener('change', this._proxyChangeEvent);
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,9 @@ export class LionSwitchButton extends DisabledWithTabIndexMixin(LitElement) {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
// inputNode = this, which always requires a value prop
|
||||||
|
this.value = '';
|
||||||
|
|
||||||
this.role = 'switch';
|
this.role = 'switch';
|
||||||
this.checked = false;
|
this.checked = false;
|
||||||
this.__toggleChecked = this.__toggleChecked.bind(this);
|
this.__toggleChecked = this.__toggleChecked.bind(this);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
import autosize from 'autosize/src/autosize.js';
|
import autosize from 'autosize/src/autosize.js';
|
||||||
import { LionField } from '@lion/form-core';
|
import { LionField } from '@lion/form-core';
|
||||||
import { css } from '@lion/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 {
|
class LionFieldWithTextArea extends LionField {
|
||||||
/**
|
/**
|
||||||
|
|
@ -22,7 +22,7 @@ class LionFieldWithTextArea extends LionField {
|
||||||
* @customElement lion-textarea
|
* @customElement lion-textarea
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error false positive, parent properties get merged by lit-element already
|
// @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() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
maxRows: {
|
maxRows: {
|
||||||
|
|
@ -78,6 +78,15 @@ export class LionTextarea extends ValueMixin(LionFieldWithTextArea) {
|
||||||
/** @param {import('lit-element').PropertyValues } changedProperties */
|
/** @param {import('lit-element').PropertyValues } changedProperties */
|
||||||
updated(changedProperties) {
|
updated(changedProperties) {
|
||||||
super.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')) {
|
if (changedProperties.has('disabled')) {
|
||||||
this._inputNode.disabled = this.disabled;
|
this._inputNode.disabled = this.disabled;
|
||||||
this.validate();
|
this.validate();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue