chore: add tutorial Input extesnion
This commit is contained in:
parent
9538aa5aa6
commit
cd51f93143
3 changed files with 71 additions and 74 deletions
5
.changeset/tough-lizards-fly.md
Normal file
5
.changeset/tough-lizards-fly.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@lion/input-date': patch
|
||||
---
|
||||
|
||||
configure in constructor, so property effects use preconfigured value
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
# How To >> Creating a custom field ||20
|
||||
|
||||
```js script
|
||||
import { html, render } from '@lion/core';
|
||||
import { Validator } from '@lion/form-core';
|
||||
|
||||
import '@lion/input/define';
|
||||
import '../../docs/systems/form/assets/h-output.js';
|
||||
```
|
||||
|
||||
Custom fields can be created in just a few steps. All you need is an interaction element (like for instance a slider, a listbox or a combobox) and connect it to the [input components](../../components/inputs/input/overview.md).
|
||||
|
||||
## Prerequisite: an interaction element
|
||||
|
||||
An interaction element provides the means for the end user to enter a certain value, just like native elements provide in this (think of `<input>`, `<textarea>` and `<select>`). An example of a non native element is the [slider design pattern](https://www.w3.org/TR/wai-aria-practices-1.1/#slider) described here.
|
||||
|
||||
For this tutorial, we assume we have a component `<my-slider>` that exposes its value via property `mySliderValue` and sends an event `my-slider-changed` on every value change. To make it focusable, it has a tabindex=“0” applied.
|
||||
|
||||
## Connecting the interaction element to the field
|
||||
|
||||
Now we want to integrate the slider in our form framework to enrich the user interface, get
|
||||
validation support and get all the other [benefits of LionField](../../docs/systems/form/overview.md). We start by creating a component `<lion-slider>` that extends from `LionField`. Then we follow the steps below:
|
||||
|
||||
### 1. Add your interaction element as ‘input slot'
|
||||
|
||||
Here you return the element the user interacts with. By configuring it as a slot, it will end up in light DOM, ensuring the best accessibility for the end user.
|
||||
|
||||
### 2. Proxy event `my-slider-changed` to `user-input-changed` event
|
||||
|
||||
The `user-input-changed` event is listened to by the FormatMixin: it should be regarded as the equivalent of the `input` event of the platform, but for custom built interaction elements.
|
||||
|
||||
### 3. Proxy property `<my-slider>.mySliderValue` to `<lion-slider>.value`
|
||||
|
||||
Every time the `user-input-changed` fires, the value of `<my-slider>` is synchronized with the [`modelValue`](../../docs/systems/form/model-value.md) of `<my-slider>`. Now the cycle is complete: the modelValue connects your interaction element to all logic inside the LionField.
|
||||
|
||||
Steps as described can be implemented with the following javascript:
|
||||
|
||||
```js
|
||||
import { LionField } from '@lion/form-core';
|
||||
import './my-slider.js';
|
||||
|
||||
export class LionSlider extends LionField {
|
||||
// 1. Add your interaction element as ‘input slot'
|
||||
get slots() {
|
||||
return {
|
||||
...super.slots,
|
||||
input: () => document.createElement(‘my-slider’),
|
||||
};
|
||||
}
|
||||
|
||||
// 2. Proxy event `my-slider-changed` to `user-input-changed` event
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.addEventListener('my-slider-changed', this._proxyChangeEvent);
|
||||
}
|
||||
|
||||
_proxyChangeEvent() {
|
||||
this._inputNode.dispatchEvent(
|
||||
new CustomEvent('user-input-changed', { bubbles: true, composed: true }),
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Proxy property `<my-slider>.mySliderValue` to `<lion-slider>.value`
|
||||
get value() {
|
||||
return Array.from(this.children).find(child => child.slot === 'input').mySliderValue;
|
||||
}
|
||||
|
||||
set value(newV) {
|
||||
Array.from(this.children).find(child => child.slot === 'input').mySliderValue = newV;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
That was all. Now you can enhance your slider by writing custom validators for it or by writing a parser to get a custom modelValue type.
|
||||
66
docs/guides/how-to/extend-a-native-input.md
Normal file
66
docs/guides/how-to/extend-a-native-input.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# How To >> Extend a native Input ||20
|
||||
|
||||
```js script
|
||||
import { html } from '@lion/core';
|
||||
import { LionInput } from '@lion/input';
|
||||
import { LionInputDate } from '@lion/input-date';
|
||||
import { loadDefaultFeedbackMessages } from '@lion/validate-messages';
|
||||
import '@lion/input/define';
|
||||
import '../../docs/systems/form/assets/h-output.js';
|
||||
|
||||
loadDefaultFeedbackMessages();
|
||||
```
|
||||
|
||||
Input fields can be created by extending [LionInput](../../components/inputs/input/overview.md).
|
||||
|
||||
> In case you want to wrap a custom form element, follow [Create a custom Field](./create-a-custom-field.md).
|
||||
|
||||
For this tutorial, we create an input that wraps native `input[type=datetime-local]`.
|
||||
This is as simple as adding a type:
|
||||
|
||||
```js preview-story
|
||||
export const extendLionInput = () => {
|
||||
class LionInputDatetime extends LionInput {
|
||||
constructor() {
|
||||
super();
|
||||
this.type = 'datetime-local';
|
||||
}
|
||||
}
|
||||
customElements.define('lion-input-datetime', LionInputDatetime);
|
||||
|
||||
return html`<lion-input-datetime label="With Date string"></lion-input-datetime>
|
||||
<h-output .show="${['modelValue', 'touched', 'dirty', 'focused']}"></h-output>`;
|
||||
};
|
||||
```
|
||||
|
||||
However, we might want to have a more advanced modelValue. In the example above, our modelValue is
|
||||
a serialized datetime string.
|
||||
If we want our modelValue to be of type Date, we should do the following:
|
||||
|
||||
```js preview-story
|
||||
export const extendLionInputDate = () => {
|
||||
function toIsoDatetime(d) {
|
||||
return d && new Date(d.getTime() - d.getTimezoneOffset() * 60000).toISOString().split('.')[0];
|
||||
}
|
||||
function fromIsoDatetime(value) {
|
||||
return new Date(value);
|
||||
}
|
||||
|
||||
class LionInputDatetimeWithObject extends LionInputDate {
|
||||
constructor() {
|
||||
super();
|
||||
this.type = 'datetime-local';
|
||||
this.parser = fromIsoDatetime;
|
||||
this.deserializer = fromIsoDatetime;
|
||||
this.serializer = toIsoDatetime;
|
||||
this.formatter = toIsoDatetime;
|
||||
}
|
||||
}
|
||||
customElements.define('lion-input-datetime-with-object', LionInputDatetimeWithObject);
|
||||
|
||||
return html`<lion-input-datetime-with-object
|
||||
label="With Date object"
|
||||
></lion-input-datetime-with-object>
|
||||
<h-output .show="${['modelValue', 'touched', 'dirty', 'focused']}"></h-output>`;
|
||||
};
|
||||
```
|
||||
Loading…
Reference in a new issue