breaking(text-input): use native bind:value, dispatch instead of forward change, input events (#1065)

Use the native `bind:value` to fix two-way reactivity. As a result, "type" is read through `$$restProps` because it cannot be dynamic when using `bind:value`.

Extend value type to include `null` for the "number" type. This is similar to how `NumberInput` works; `null` represents "no value."
This commit is contained in:
metonym 2022-02-09 19:52:10 -08:00 committed by GitHub
commit 989e0f4c65
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 92 additions and 72 deletions

View file

@ -4168,11 +4168,10 @@ None.
### Props ### Props
| Prop name | Kind | Reactive | Type | Default value | Description | | Prop name | Kind | Reactive | Type | Default value | Description |
| :---------- | :--------------- | :------- | :---------------------------------------- | ------------------------------------------------ | --------------------------------------------- | | :---------- | :--------------- | :------- | :-------------------------------------------- | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| ref | <code>let</code> | Yes | <code>null &#124; HTMLInputElement</code> | <code>null</code> | Obtain a reference to the input HTML element | | ref | <code>let</code> | Yes | <code>null &#124; HTMLInputElement</code> | <code>null</code> | Obtain a reference to the input HTML element |
| value | <code>let</code> | Yes | <code>number &#124; string</code> | <code>""</code> | Specify the input value | | value | <code>let</code> | Yes | <code>null &#124; number &#124; string</code> | <code>""</code> | Specify the input value.<br /><br />`value` will be set to `null` if type="number"<br />and the value is empty. |
| size | <code>let</code> | No | <code>"sm" &#124; "xl"</code> | <code>undefined</code> | Set the size of the input | | size | <code>let</code> | No | <code>"sm" &#124; "xl"</code> | <code>undefined</code> | Set the size of the input |
| type | <code>let</code> | No | <code>string</code> | <code>""</code> | Specify the input type |
| placeholder | <code>let</code> | No | <code>string</code> | <code>""</code> | Specify the placeholder text | | placeholder | <code>let</code> | No | <code>string</code> | <code>""</code> | Specify the placeholder text |
| light | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to enable the light variant | | light | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to enable the light variant |
| disabled | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to disable the input | | disabled | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to disable the input |
@ -4198,13 +4197,13 @@ None.
### Events ### Events
| Event name | Type | Detail | | Event name | Type | Detail |
| :--------- | :-------- | :----- | | :--------- | :--------- | :-------------------------------------------- |
| change | dispatched | <code>null &#124; number &#124; string</code> |
| input | dispatched | <code>null &#124; number &#124; string</code> |
| click | forwarded | -- | | click | forwarded | -- |
| mouseover | forwarded | -- | | mouseover | forwarded | -- |
| mouseenter | forwarded | -- | | mouseenter | forwarded | -- |
| mouseleave | forwarded | -- | | mouseleave | forwarded | -- |
| change | forwarded | -- |
| input | forwarded | -- |
| keydown | forwarded | -- | | keydown | forwarded | -- |
| keyup | forwarded | -- | | keyup | forwarded | -- |
| focus | forwarded | -- | | focus | forwarded | -- |

View file

@ -11571,25 +11571,14 @@
{ {
"name": "value", "name": "value",
"kind": "let", "kind": "let",
"description": "Specify the input value", "description": "Specify the input value.\n\n`value` will be set to `null` if type=\"number\"\nand the value is empty.",
"type": "number | string", "type": "null | number | string",
"value": "\"\"", "value": "\"\"",
"isFunction": false, "isFunction": false,
"isFunctionDeclaration": false, "isFunctionDeclaration": false,
"constant": false, "constant": false,
"reactive": true "reactive": true
}, },
{
"name": "type",
"kind": "let",
"description": "Specify the input type",
"type": "string",
"value": "\"\"",
"isFunction": false,
"isFunctionDeclaration": false,
"constant": false,
"reactive": false
},
{ {
"name": "placeholder", "name": "placeholder",
"kind": "let", "kind": "let",
@ -11775,12 +11764,20 @@
} }
], ],
"events": [ "events": [
{
"type": "dispatched",
"name": "change",
"detail": "null | number | string"
},
{
"type": "dispatched",
"name": "input",
"detail": "null | number | string"
},
{ "type": "forwarded", "name": "click", "element": "div" }, { "type": "forwarded", "name": "click", "element": "div" },
{ "type": "forwarded", "name": "mouseover", "element": "div" }, { "type": "forwarded", "name": "mouseover", "element": "div" },
{ "type": "forwarded", "name": "mouseenter", "element": "div" }, { "type": "forwarded", "name": "mouseenter", "element": "div" },
{ "type": "forwarded", "name": "mouseleave", "element": "div" }, { "type": "forwarded", "name": "mouseleave", "element": "div" },
{ "type": "forwarded", "name": "change", "element": "input" },
{ "type": "forwarded", "name": "input", "element": "input" },
{ "type": "forwarded", "name": "keydown", "element": "input" }, { "type": "forwarded", "name": "keydown", "element": "input" },
{ "type": "forwarded", "name": "keyup", "element": "input" }, { "type": "forwarded", "name": "keyup", "element": "input" },
{ "type": "forwarded", "name": "focus", "element": "input" }, { "type": "forwarded", "name": "focus", "element": "input" },

View file

@ -1,4 +1,9 @@
<script> <script>
/**
* @event {null | number | string} change
* @event {null | number | string} input
*/
/** /**
* Set the size of the input * Set the size of the input
* @type {"sm" | "xl"} * @type {"sm" | "xl"}
@ -6,14 +11,14 @@
export let size = undefined; export let size = undefined;
/** /**
* Specify the input value * Specify the input value.
* @type {number | string} *
* `value` will be set to `null` if type="number"
* and the value is empty.
* @type {null | number | string}
*/ */
export let value = ""; export let value = "";
/** Specify the input type */
export let type = "";
/** Specify the placeholder text */ /** Specify the placeholder text */
export let placeholder = ""; export let placeholder = "";
@ -65,12 +70,29 @@
/** Set to `true` to use the read-only variant */ /** Set to `true` to use the read-only variant */
export let readonly = false; export let readonly = false;
import { getContext } from "svelte"; import { createEventDispatcher, getContext } from "svelte";
import WarningFilled16 from "../icons/WarningFilled16.svelte"; import WarningFilled16 from "../icons/WarningFilled16.svelte";
import WarningAltFilled16 from "../icons/WarningAltFilled16.svelte"; import WarningAltFilled16 from "../icons/WarningAltFilled16.svelte";
import EditOff16 from "../icons/EditOff16.svelte"; import EditOff16 from "../icons/EditOff16.svelte";
const ctx = getContext("Form"); const ctx = getContext("Form");
const dispatch = createEventDispatcher();
function parse(raw) {
if ($$restProps.type !== "number") return raw;
return raw != "" ? Number(raw) : null;
}
/** @type {(e: Event) => void} */
const onInput = (e) => {
value = parse(e.target.value);
dispatch("input", value);
};
/** @type {(e: Event) => void} */
const onChange = (e) => {
dispatch("change", parse(e.target.value));
};
$: isFluid = !!ctx && ctx.isFluid; $: isFluid = !!ctx && ctx.isFluid;
$: errorId = `error-${id}`; $: errorId = `error-${id}`;
@ -162,8 +184,7 @@
id="{id}" id="{id}"
name="{name}" name="{name}"
placeholder="{placeholder}" placeholder="{placeholder}"
type="{type}" bind:value
value="{value ?? ''}"
required="{required}" required="{required}"
readonly="{readonly}" readonly="{readonly}"
class:bx--text-input="{true}" class:bx--text-input="{true}"
@ -172,11 +193,8 @@
class:bx--text-input--warn="{warn}" class:bx--text-input--warn="{warn}"
{...$$restProps} {...$$restProps}
class="{size && `bx--text-input--${size}`}" class="{size && `bx--text-input--${size}`}"
on:change on:change="{onChange}"
on:input on:input="{onInput}"
on:input="{({ target }) => {
value = type === 'number' ? Number(target.value) : target.value;
}}"
on:keydown on:keydown
on:keyup on:keyup
on:focus on:focus

View file

@ -1,8 +1,17 @@
<script lang="ts"> <script lang="ts">
import { TextInput, TextInputSkeleton } from "../types"; import { TextInput, TextInputSkeleton } from "../types";
let value = null;
</script> </script>
<TextInput labelText="User name" placeholder="Enter user name..." /> <TextInput
type="number"
labelText="User name"
placeholder="Enter user name..."
bind:value
on:input="{(e) => console.log(e.detail)}"
on:change="{(e) => (value = e.detail)}"
/>
<TextInput <TextInput
labelText="User name" labelText="User name"

View file

@ -10,16 +10,13 @@ export interface TextInputProps
size?: "sm" | "xl"; size?: "sm" | "xl";
/** /**
* Specify the input value * Specify the input value.
*
* `value` will be set to `null` if type="number"
* and the value is empty.
* @default "" * @default ""
*/ */
value?: number | string; value?: null | number | string;
/**
* Specify the input type
* @default ""
*/
type?: string;
/** /**
* Specify the placeholder text * Specify the placeholder text
@ -121,12 +118,12 @@ export interface TextInputProps
export default class TextInput extends SvelteComponentTyped< export default class TextInput extends SvelteComponentTyped<
TextInputProps, TextInputProps,
{ {
change: CustomEvent<null | number | string>;
input: CustomEvent<null | number | string>;
click: WindowEventMap["click"]; click: WindowEventMap["click"];
mouseover: WindowEventMap["mouseover"]; mouseover: WindowEventMap["mouseover"];
mouseenter: WindowEventMap["mouseenter"]; mouseenter: WindowEventMap["mouseenter"];
mouseleave: WindowEventMap["mouseleave"]; mouseleave: WindowEventMap["mouseleave"];
change: WindowEventMap["change"];
input: WindowEventMap["input"];
keydown: WindowEventMap["keydown"]; keydown: WindowEventMap["keydown"];
keyup: WindowEventMap["keyup"]; keyup: WindowEventMap["keyup"];
focus: WindowEventMap["focus"]; focus: WindowEventMap["focus"];