diff --git a/angular/src/directives/proxies.ts b/angular/src/directives/proxies.ts index 703cd42d4ba..a3d0670ae7a 100644 --- a/angular/src/directives/proxies.ts +++ b/angular/src/directives/proxies.ts @@ -564,13 +564,13 @@ export class Radio { } export declare interface RadioGroup extends StencilComponents<'IonRadioGroup'> {} -@Component({ selector: 'ion-radio-group', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '', inputs: ['allowEmptySelection', 'name', 'disabled', 'value'] }) +@Component({ selector: 'ion-radio-group', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '', inputs: ['allowEmptySelection', 'name', 'value'] }) export class RadioGroup { ionChange: EventEmitter; constructor(r: ElementRef) { const el = r.nativeElement; - proxyInputs(this, el, ['allowEmptySelection', 'name', 'disabled', 'value']); + proxyInputs(this, el, ['allowEmptySelection', 'name', 'value']); proxyOutputs(this, el, ['ionChange']); } } diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 4c04bdccb2f..2c35dff1b5d 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -1292,7 +1292,7 @@ export namespace Components { /** * The value of the datetime as a valid ISO 8601 datetime string. */ - 'value'?: string; + 'value'?: string | null; /** * Values used to create the list of selectable years. By default the year values range between the `min` and `max` datetime inputs. However, to control exactly which years to display, the `yearValues` input can take a number, an array of numbers, or string of comma separated numbers. For example, to show upcoming and recent leap years, then this input's value would be `yearValues="2024,2020,2016,2012,2008"`. */ @@ -1386,7 +1386,7 @@ export namespace Components { /** * The value of the datetime as a valid ISO 8601 datetime string. */ - 'value'?: string; + 'value'?: string | null; /** * Values used to create the list of selectable years. By default the year values range between the `min` and `max` datetime inputs. However, to control exactly which years to display, the `yearValues` input can take a number, an array of numbers, or string of comma separated numbers. For example, to show upcoming and recent leap years, then this input's value would be `yearValues="2024,2020,2016,2012,2008"`. */ @@ -1847,7 +1847,7 @@ export namespace Components { /** * The value of the input. */ - 'value': string; + 'value'?: string | null; } interface IonInputAttributes extends StencilHTMLAttributes { /** @@ -1989,7 +1989,7 @@ export namespace Components { /** * The value of the input. */ - 'value'?: string; + 'value'?: string | null; } interface IonItemDivider { @@ -2207,7 +2207,7 @@ export namespace Components { */ 'mode': Mode; /** - * The position determines where and how the label behaves inside an item. Possible values are: 'inline' | 'fixed' | 'stacked' | 'floating' + * The position determines where and how the label behaves inside an item. */ 'position'?: 'fixed' | 'stacked' | 'floating'; } @@ -2225,7 +2225,7 @@ export namespace Components { */ 'onIonStyle'?: (event: CustomEvent) => void; /** - * The position determines where and how the label behaves inside an item. Possible values are: 'inline' | 'fixed' | 'stacked' | 'floating' + * The position determines where and how the label behaves inside an item. */ 'position'?: 'fixed' | 'stacked' | 'floating'; } @@ -2564,7 +2564,7 @@ export namespace Components { */ 'contentId'?: string; /** - * If `true`, the menu is disabled. Default `false`. + * If `true`, the menu is disabled. Defaults to `false`. */ 'disabled': boolean; /** @@ -2596,7 +2596,7 @@ export namespace Components { */ 'side': Side; /** - * If `true`, swiping the menu is enabled. Default `true`. + * If `true`, swiping the menu is enabled. Defaults to `true`. */ 'swipeGesture': boolean; /** @@ -2614,7 +2614,7 @@ export namespace Components { */ 'contentId'?: string; /** - * If `true`, the menu is disabled. Default `false`. + * If `true`, the menu is disabled. Defaults to `false`. */ 'disabled'?: boolean; /** @@ -2650,7 +2650,7 @@ export namespace Components { */ 'side'?: Side; /** - * If `true`, swiping the menu is enabled. Default `true`. + * If `true`, swiping the menu is enabled. Defaults to `true`. */ 'swipeGesture'?: boolean; /** @@ -3308,14 +3308,10 @@ export namespace Components { interface IonRadioGroup { /** - * If `true`, the radios can be deselected. Default false. + * If `true`, the radios can be deselected. Defaults to `false`. */ 'allowEmptySelection': boolean; /** - * If `true`, the user cannot interact with the radio group. Default false. - */ - 'disabled': boolean; - /** * The name of the control, which is submitted with the form data. */ 'name': string; @@ -3326,14 +3322,10 @@ export namespace Components { } interface IonRadioGroupAttributes extends StencilHTMLAttributes { /** - * If `true`, the radios can be deselected. Default false. + * If `true`, the radios can be deselected. Defaults to `false`. */ 'allowEmptySelection'?: boolean; /** - * If `true`, the user cannot interact with the radio group. Default false. - */ - 'disabled'?: boolean; - /** * The name of the control, which is submitted with the form data. */ 'name'?: string; @@ -3371,7 +3363,7 @@ export namespace Components { /** * the value of the radio. */ - 'value': any | null; + 'value'?: any | null; } interface IonRadioAttributes extends StencilHTMLAttributes { /** @@ -3814,7 +3806,7 @@ export namespace Components { interface IonSearchbar { /** - * If `true`, enable searchbar animation. Default `false`. + * If `true`, enable searchbar animation. Defaults to `false`. */ 'animated': boolean; /** @@ -3862,11 +3854,11 @@ export namespace Components { */ 'setFocus': () => void; /** - * If `true`, show the cancel button. Default `false`. + * If `true`, show the cancel button. Defaults to `false`. */ 'showCancelButton': boolean; /** - * If `true`, enable spellcheck on the input. Default `false`. + * If `true`, enable spellcheck on the input. Defaults to `false`. */ 'spellcheck': boolean; /** @@ -3876,11 +3868,11 @@ export namespace Components { /** * the value of the searchbar. */ - 'value': string; + 'value'?: string | null; } interface IonSearchbarAttributes extends StencilHTMLAttributes { /** - * If `true`, enable searchbar animation. Default `false`. + * If `true`, enable searchbar animation. Defaults to `false`. */ 'animated'?: boolean; /** @@ -3948,11 +3940,11 @@ export namespace Components { */ 'searchIcon'?: string; /** - * If `true`, show the cancel button. Default `false`. + * If `true`, show the cancel button. Defaults to `false`. */ 'showCancelButton'?: boolean; /** - * If `true`, enable spellcheck on the input. Default `false`. + * If `true`, enable spellcheck on the input. Defaults to `false`. */ 'spellcheck'?: boolean; /** @@ -3962,7 +3954,7 @@ export namespace Components { /** * the value of the searchbar. */ - 'value'?: string; + 'value'?: string | null; } interface IonSegmentButton { @@ -3975,7 +3967,7 @@ export namespace Components { */ 'color'?: Color; /** - * If `true`, the user cannot interact with the segment button. Default false. + * If `true`, the user cannot interact with the segment button. Defaults to `false`. */ 'disabled': boolean; /** @@ -3997,7 +3989,7 @@ export namespace Components { */ 'color'?: Color; /** - * If `true`, the user cannot interact with the segment button. Default false. + * If `true`, the user cannot interact with the segment button. Defaults to `false`. */ 'disabled'?: boolean; /** @@ -4067,7 +4059,7 @@ export namespace Components { /** * The text value of the option. */ - 'value'?: any; + 'value'?: any | null; } interface IonSelectOptionAttributes extends StencilHTMLAttributes { /** @@ -4089,7 +4081,7 @@ export namespace Components { /** * The text value of the option. */ - 'value'?: any; + 'value'?: any | null; } interface IonSelectPopover { @@ -4816,7 +4808,7 @@ export namespace Components { /** * The value of the textarea. */ - 'value': string; + 'value'?: string | null; /** * Indicates how the control wraps text. Possible values are: `"hard"`, `"soft"`, `"off"`. */ @@ -4910,7 +4902,7 @@ export namespace Components { /** * The value of the textarea. */ - 'value'?: string; + 'value'?: string | null; /** * Indicates how the control wraps text. Possible values are: `"hard"`, `"soft"`, `"off"`. */ @@ -5102,7 +5094,7 @@ export namespace Components { */ 'color'?: Color; /** - * If `true`, the user cannot interact with the toggle. Default false. + * If `true`, the user cannot interact with the toggle. Defaults to `false`. */ 'disabled': boolean; /** @@ -5116,7 +5108,7 @@ export namespace Components { /** * the value of the toggle. */ - 'value': string; + 'value'?: string | null; } interface IonToggleAttributes extends StencilHTMLAttributes { /** @@ -5128,7 +5120,7 @@ export namespace Components { */ 'color'?: Color; /** - * If `true`, the user cannot interact with the toggle. Default false. + * If `true`, the user cannot interact with the toggle. Defaults to `false`. */ 'disabled'?: boolean; /** @@ -5158,7 +5150,7 @@ export namespace Components { /** * the value of the toggle. */ - 'value'?: string; + 'value'?: string | null; } interface IonToolbar { diff --git a/core/src/components/checkbox/checkbox.tsx b/core/src/components/checkbox/checkbox.tsx index 4d70b939462..bc2bd631d24 100644 --- a/core/src/components/checkbox/checkbox.tsx +++ b/core/src/components/checkbox/checkbox.tsx @@ -1,7 +1,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Prop, State, Watch } from '@stencil/core'; import { CheckedInputChangeEvent, Color, Mode, StyleEvent } from '../../interface'; -import { deferEvent, renderHiddenInput } from '../../utils/helpers'; +import { renderHiddenInput } from '../../utils/helpers'; import { createColorClasses, hostContext } from '../../utils/theme'; @Component({ @@ -78,10 +78,6 @@ export class Checkbox implements ComponentInterface { this.emitStyle(); } - componentDidLoad() { - this.ionStyle = deferEvent(this.ionStyle); - } - @Watch('checked') checkedChanged(isChecked: boolean) { this.ionChange.emit({ diff --git a/core/src/components/datetime/datetime.tsx b/core/src/components/datetime/datetime.tsx index d8e51a32715..5174534ed76 100644 --- a/core/src/components/datetime/datetime.tsx +++ b/core/src/components/datetime/datetime.tsx @@ -1,7 +1,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Method, Prop, State, Watch } from '@stencil/core'; import { InputChangeEvent, Mode, PickerColumn, PickerColumnOption, PickerOptions, StyleEvent } from '../../interface'; -import { clamp, deferEvent } from '../../utils/helpers'; +import { clamp } from '../../utils/helpers'; import { hostContext } from '../../utils/theme'; import { DatetimeData, LocaleData, convertDataToISO, convertFormatToKey, convertToArrayOfNumbers, convertToArrayOfStrings, dateDataSortValue, dateSortValue, dateValueRange, daysInMonth, getValueFromFormat, parseDate, parseTemplate, renderDatetime, renderTextFormat, updateDate } from './datetime-util'; @@ -179,7 +179,7 @@ export class Datetime implements ComponentInterface { /** * The value of the datetime as a valid ISO 8601 datetime string. */ - @Prop({ mutable: true }) value?: string; + @Prop({ mutable: true }) value?: string | null; /** * Update the datetime value when the value changes @@ -212,7 +212,6 @@ export class Datetime implements ComponentInterface { // first see if locale names were provided in the inputs // then check to see if they're in the config // if neither were provided then it will use default English names - this.ionStyle = deferEvent(this.ionStyle); this.locale = { // this.locale[type] = convertToArrayOfStrings((this[type] ? this[type] : this.config.get(type), type); monthNames: convertToArrayOfStrings(this.monthNames, 'monthNames'), @@ -222,9 +221,6 @@ export class Datetime implements ComponentInterface { }; this.updateDatetimeValue(this.value); - } - - componentDidLoad() { this.emitStyle(); } @@ -502,7 +498,7 @@ export class Datetime implements ComponentInterface { this.text = renderDatetime(template, this.datetimeValue, this.locale); } - hasValue(): boolean { + private hasValue(): boolean { const val = this.datetimeValue; return Object.keys(val).length > 0; } @@ -538,7 +534,6 @@ export class Datetime implements ComponentInterface { onClick={this.open.bind(this)} class="datetime-cover" > - {this.mode === 'md' && } ]; } diff --git a/core/src/components/datetime/readme.md b/core/src/components/datetime/readme.md index c7ede98bfa5..01f5527a88d 100644 --- a/core/src/components/datetime/readme.md +++ b/core/src/components/datetime/readme.md @@ -224,7 +224,7 @@ dates in JavaScript. | `pickerFormat` | `picker-format` | The format of the date and time picker columns the user selects. A datetime input can have one or many datetime parts, each getting their own column which allow individual selection of that particular datetime part. For example, year and month columns are two individually selectable columns which help choose an exact date from the datetime picker. Each column follows the string parse format. Defaults to use `displayFormat`. | `string \| undefined` | | `pickerOptions` | -- | Any additional options that the picker interface can accept. See the [Picker API docs](../../picker/Picker) for the picker options. | `PickerOptions \| undefined` | | `placeholder` | `placeholder` | The text to display when there's no date selected yet. Using lowercase to match the input attribute | `null \| string \| undefined` | -| `value` | `value` | The value of the datetime as a valid ISO 8601 datetime string. | `string \| undefined` | +| `value` | `value` | The value of the datetime as a valid ISO 8601 datetime string. | `null \| string \| undefined` | | `yearValues` | -- | Values used to create the list of selectable years. By default the year values range between the `min` and `max` datetime inputs. However, to control exactly which years to display, the `yearValues` input can take a number, an array of numbers, or string of comma separated numbers. For example, to show upcoming and recent leap years, then this input's value would be `yearValues="2024,2020,2016,2012,2008"`. | `number \| number[] \| string \| undefined` | diff --git a/core/src/components/datetime/test/basic/index.html b/core/src/components/datetime/test/basic/index.html index 9ff88d43749..b7eefbb975c 100644 --- a/core/src/components/datetime/test/basic/index.html +++ b/core/src/components/datetime/test/basic/index.html @@ -42,7 +42,7 @@ - MM DD YY + MM DD YY diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx index a69012771c2..ee1ab0c0444 100644 --- a/core/src/components/input/input.tsx +++ b/core/src/components/input/input.tsx @@ -1,7 +1,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Method, Prop, State, Watch } from '@stencil/core'; import { Color, Mode, StyleEvent, TextFieldTypes, TextInputChangeEvent } from '../../interface'; -import { debounceEvent, deferEvent, renderHiddenInput } from '../../utils/helpers'; +import { debounceEvent, renderHiddenInput } from '../../utils/helpers'; import { createColorClasses, hostContext } from '../../utils/theme'; @Component({ @@ -173,7 +173,7 @@ export class Input implements ComponentInterface { /** * The value of the input. */ - @Prop({ mutable: true }) value = ''; + @Prop({ mutable: true }) value?: string | null = ''; /** * Update the native input element when the value changes @@ -229,12 +229,11 @@ export class Input implements ComponentInterface { if (this.clearOnEdit === undefined && this.type === 'password') { this.clearOnEdit = true; } + this.emitStyle(); } componentDidLoad() { - this.ionStyle = deferEvent(this.ionStyle); this.debounceChanged(); - this.emitStyle(); this.ionInputDidLoad.emit(); } @@ -333,7 +332,8 @@ export class Input implements ComponentInterface { } render() { - renderHiddenInput(this.el, this.name, this.getValue(), this.disabled); + const value = this.getValue(); + renderHiddenInput(this.el, this.name, value, this.disabled); return [ ) { ev.stopPropagation(); - const tagName: string = (ev.target as HTMLElement).tagName; - const updatedStyles = ev.detail as any; - const updatedKeys = Object.keys(ev.detail); + const tagName = (ev.target as HTMLElement).tagName; + const updatedStyles = ev.detail; const newStyles = {} as any; const childStyles = this.itemStyles.get(tagName) || {}; + let hasStyleChange = false; - for (const key of updatedKeys) { + Object.keys(updatedStyles).forEach(key => { const itemKey = `item-${key}`; const newValue = updatedStyles[key]; if (newValue !== childStyles[itemKey]) { hasStyleChange = true; } - newStyles[itemKey] = newValue; - } + if (newValue) { + newStyles[itemKey] = true; + } + }); if (hasStyleChange) { this.itemStyles.set(tagName, newStyles); diff --git a/core/src/components/item/test/inputs/e2e.ts b/core/src/components/item/test/inputs/e2e.ts index 0b30b39c1eb..f6497b0710e 100644 --- a/core/src/components/item/test/inputs/e2e.ts +++ b/core/src/components/item/test/inputs/e2e.ts @@ -5,6 +5,33 @@ it('item: inputs', async () => { url: '/src/components/item/test/inputs?ionic:_testing=true' }); - const compare = await page.compareScreenshot(); + // Default case, enabled and no value + let compare = await page.compareScreenshot(); + expect(compare).toMatchScreenshot(); + + // Disable everything + await page.click('#btnDisabled'); + compare = await page.compareScreenshot('should disable all'); + expect(compare).toMatchScreenshot(); + + // Reenable and set some value + await page.click('#btnDisabled'); + await page.click('#btnSomeValue'); + compare = await page.compareScreenshot('should reenable and set value'); + expect(compare).toMatchScreenshot(); + + // Set "null" + await page.click('#btnNullValue'); + compare = await page.compareScreenshot('should set null'); + expect(compare).toMatchScreenshot(); + + // Set "empty" + await page.click('#btnEmptyValue'); + compare = await page.compareScreenshot('should set empty'); + expect(compare).toMatchScreenshot(); + + // Set "empty" + await page.click('#btnEmptyValue'); + compare = await page.compareScreenshot('should set empty'); expect(compare).toMatchScreenshot(); }); diff --git a/core/src/components/item/test/inputs/index.html b/core/src/components/item/test/inputs/index.html index cf74a8c2887..6fdfcdba779 100644 --- a/core/src/components/item/test/inputs/index.html +++ b/core/src/components/item/test/inputs/index.html @@ -16,31 +16,26 @@ Item inputs - - - - - - + Simple item - + Item Button - DateTime - + DateTime + - Select + Select No Game Console NES @@ -54,12 +49,12 @@ Toggle - + - Input (text) - + Input (text) + @@ -69,12 +64,7 @@ Checkbox - - - - - Toggle (left) - + @@ -82,33 +72,115 @@ + + + Controls + Value Controls + + + Toggle Disable + + + Set some value + + + Set empty value + + + Set "null" value + + + Set "undefined" value + + Label Controls + + Default + + + Floating + + + Stacked + + + Fixed + + diff --git a/core/src/components/label/label.ios.scss b/core/src/components/label/label.ios.scss index 3c40090a8be..f2c4a41212f 100644 --- a/core/src/components/label/label.ios.scss +++ b/core/src/components/label/label.ios.scss @@ -21,7 +21,7 @@ :host(.label-stacked) { @include margin(null, null, 4px, null); - font-size: 12px; + font-size: 13.6px; } :host(.label-floating) { diff --git a/core/src/components/label/label.md.scss b/core/src/components/label/label.md.scss index 8afbdc53104..3a3dc093d57 100644 --- a/core/src/components/label/label.md.scss +++ b/core/src/components/label/label.md.scss @@ -19,7 +19,7 @@ // -------------------------------------------------- :host(.label-stacked) { - font-size: 12px; + font-size: 12.8px; } :host(.label-floating) { diff --git a/core/src/components/label/label.tsx b/core/src/components/label/label.tsx index 9df882eef95..839bc0a5a1e 100644 --- a/core/src/components/label/label.tsx +++ b/core/src/components/label/label.tsx @@ -29,7 +29,6 @@ export class Label implements ComponentInterface { /** * The position determines where and how the label behaves inside an item. - * Possible values are: 'inline' | 'fixed' | 'stacked' | 'floating' */ @Prop() position?: 'fixed' | 'stacked' | 'floating'; @@ -42,11 +41,10 @@ export class Label implements ComponentInterface { componentWillLoad() { this.noAnimate = (this.position === 'floating'); + this.emitStyle(); } componentDidLoad() { - this.positionChanged(); - if (this.noAnimate) { setTimeout(() => { this.noAnimate = false; @@ -56,6 +54,10 @@ export class Label implements ComponentInterface { @Watch('position') positionChanged() { + this.emitStyle(); + } + + private emitStyle() { const position = this.position; this.ionStyle.emit({ 'label': true, diff --git a/core/src/components/label/readme.md b/core/src/components/label/readme.md index 647a83f68f1..d99d0c003ce 100644 --- a/core/src/components/label/readme.md +++ b/core/src/components/label/readme.md @@ -12,7 +12,7 @@ Label is a wrapper element that can be used in combination with `ion-item`, `ion | ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | | `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | | `mode` | `mode` | The mode determines which platform styles to use. Possible values are: `"ios"` or `"md"`. | `"ios" \| "md"` | -| `position` | `position` | The position determines where and how the label behaves inside an item. Possible values are: 'inline' \| 'fixed' \| 'stacked' \| 'floating' | `"fixed" \| "floating" \| "stacked" \| undefined` | +| `position` | `position` | The position determines where and how the label behaves inside an item. | `"fixed" \| "floating" \| "stacked" \| undefined` | ## Events diff --git a/core/src/components/menu/menu.tsx b/core/src/components/menu/menu.tsx index ab84baf05d2..e708fa23c6c 100644 --- a/core/src/components/menu/menu.tsx +++ b/core/src/components/menu/menu.tsx @@ -77,7 +77,7 @@ export class Menu implements ComponentInterface, MenuI { } /** - * If `true`, the menu is disabled. Default `false`. + * If `true`, the menu is disabled. Defaults to `false`. */ @Prop({ mutable: true }) disabled = false; @@ -102,7 +102,7 @@ export class Menu implements ComponentInterface, MenuI { } /** - * If `true`, swiping the menu is enabled. Default `true`. + * If `true`, swiping the menu is enabled. Defaults to `true`. */ @Prop() swipeGesture = true; diff --git a/core/src/components/menu/readme.md b/core/src/components/menu/readme.md index dc1a8032767..46a11553bce 100644 --- a/core/src/components/menu/readme.md +++ b/core/src/components/menu/readme.md @@ -16,11 +16,11 @@ These can be controlled from the templates, or programmatically using the MenuCo | Property | Attribute | Description | Type | | -------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------- | | `contentId` | `content-id` | The content's id the menu should use. | `string \| undefined` | -| `disabled` | `disabled` | If `true`, the menu is disabled. Default `false`. | `boolean` | +| `disabled` | `disabled` | If `true`, the menu is disabled. Defaults to `false`. | `boolean` | | `maxEdgeStart` | `max-edge-start` | The edge threshold for dragging the menu open. If a drag/swipe happens over this value, the menu is not triggered. | `number` | | `menuId` | `menu-id` | An id for the menu. | `string \| undefined` | | `side` | `side` | Which side of the view the menu should be placed. Default `"start"`. | `"end" \| "start"` | -| `swipeGesture` | `swipe-gesture` | If `true`, swiping the menu is enabled. Default `true`. | `boolean` | +| `swipeGesture` | `swipe-gesture` | If `true`, swiping the menu is enabled. Defaults to `true`. | `boolean` | | `type` | `type` | The display type of the menu. Available options: `"overlay"`, `"reveal"`, `"push"`. | `string` | diff --git a/core/src/components/radio-group/radio-group.tsx b/core/src/components/radio-group/radio-group.tsx index 1bb3a408e1c..8e082ca45b6 100644 --- a/core/src/components/radio-group/radio-group.tsx +++ b/core/src/components/radio-group/radio-group.tsx @@ -14,7 +14,7 @@ export class RadioGroup implements ComponentInterface { @Element() el!: HTMLElement; /** - * If `true`, the radios can be deselected. Default false. + * If `true`, the radios can be deselected. Defaults to `false`. */ @Prop() allowEmptySelection = false; @@ -23,18 +23,6 @@ export class RadioGroup implements ComponentInterface { */ @Prop() name: string = this.inputId; - /** - * If `true`, the user cannot interact with the radio group. Default false. - */ - @Prop() disabled = false; - - @Watch('disabled') - disabledChanged() { - for (const radio of this.radios) { - radio.disabled = this.disabled; - } - } - /** * the value of the radio group. */ @@ -99,7 +87,6 @@ export class RadioGroup implements ComponentInterface { } } - this.disabledChanged(); this.updateRadios(); } diff --git a/core/src/components/radio-group/readme.md b/core/src/components/radio-group/readme.md index d53016dc0c7..0d86aed0b75 100644 --- a/core/src/components/radio-group/readme.md +++ b/core/src/components/radio-group/readme.md @@ -13,12 +13,11 @@ radio button within the same group. ## Properties -| Property | Attribute | Description | Type | -| --------------------- | ----------------------- | ------------------------------------------------------------------------ | --------- | -| `allowEmptySelection` | `allow-empty-selection` | If `true`, the radios can be deselected. Default false. | `boolean` | -| `disabled` | `disabled` | If `true`, the user cannot interact with the radio group. Default false. | `boolean` | -| `name` | `name` | The name of the control, which is submitted with the form data. | `string` | -| `value` | -- | the value of the radio group. | `any` | +| Property | Attribute | Description | Type | +| --------------------- | ----------------------- | --------------------------------------------------------------- | --------- | +| `allowEmptySelection` | `allow-empty-selection` | If `true`, the radios can be deselected. Defaults to `false`. | `boolean` | +| `name` | `name` | The name of the control, which is submitted with the form data. | `string` | +| `value` | -- | the value of the radio group. | `any` | ## Events diff --git a/core/src/components/radio/radio.tsx b/core/src/components/radio/radio.tsx index b41e6964a43..10620c541d9 100644 --- a/core/src/components/radio/radio.tsx +++ b/core/src/components/radio/radio.tsx @@ -1,7 +1,6 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Prop, State, Watch } from '@stencil/core'; import { CheckedInputChangeEvent, Color, Mode, StyleEvent } from '../../interface'; -import { deferEvent } from '../../utils/helpers'; import { createColorClasses, hostContext } from '../../utils/theme'; @Component({ @@ -52,7 +51,7 @@ export class Radio implements ComponentInterface { /** * the value of the radio. */ - @Prop({ mutable: true }) value!: any | null; + @Prop({ mutable: true }) value?: any | null; /** * Emitted when the radio loads. @@ -85,9 +84,6 @@ export class Radio implements ComponentInterface { @Event() ionBlur!: EventEmitter; componentWillLoad() { - this.ionSelect = deferEvent(this.ionSelect); - this.ionStyle = deferEvent(this.ionStyle); - if (this.value == null) { this.value = this.inputId; } @@ -139,7 +135,7 @@ export class Radio implements ComponentInterface { this.emitStyle(); } - emitStyle() { + private emitStyle() { this.ionStyle.emit({ 'radio-checked': this.checked, 'interactive-disabled': this.disabled, diff --git a/core/src/components/range/range.scss b/core/src/components/range/range.scss index cec6d12a7a6..b00e9a22f54 100644 --- a/core/src/components/range/range.scss +++ b/core/src/components/range/range.scss @@ -19,7 +19,7 @@ display: flex; position: relative; - flex: 1; + flex: 3; align-items: center; font-family: $font-family-base; diff --git a/core/src/components/range/range.tsx b/core/src/components/range/range.tsx index 5e45218b7ad..abca2053d16 100644 --- a/core/src/components/range/range.tsx +++ b/core/src/components/range/range.tsx @@ -1,7 +1,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Listen, Prop, QueueApi, State, Watch } from '@stencil/core'; import { Color, Gesture, GestureDetail, InputChangeEvent, Mode, RangeValue, StyleEvent } from '../../interface'; -import { clamp, debounceEvent, deferEvent } from '../../utils/helpers'; +import { clamp, debounceEvent } from '../../utils/helpers'; import { createColorClasses, hostContext } from '../../utils/theme'; import { Knob, RangeEventDetail } from './range-interface'; @@ -148,8 +148,6 @@ export class Range implements ComponentInterface { @Event() ionBlur!: EventEmitter; componentWillLoad() { - this.ionStyle = deferEvent(this.ionStyle); - this.updateRatio(); this.debounceChanged(); this.emitStyle(); diff --git a/core/src/components/searchbar/readme.md b/core/src/components/searchbar/readme.md index 7c2eacb20ff..38bafcecfac 100644 --- a/core/src/components/searchbar/readme.md +++ b/core/src/components/searchbar/readme.md @@ -11,23 +11,23 @@ A Searchbar should be used instead of an input to search lists. A clear button i ## Properties -| Property | Attribute | Description | Type | -| ------------------ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | -| `animated` | `animated` | If `true`, enable searchbar animation. Default `false`. | `boolean` | -| `autocomplete` | `autocomplete` | Set the input's autocomplete property. Default `"off"`. | `"off" \| "on"` | -| `autocorrect` | `autocorrect` | Set the input's autocorrect property. Default `"off"`. | `"off" \| "on"` | -| `cancelButtonIcon` | `cancel-button-icon` | Set the cancel button icon. Only applies to `md` mode. Defaults to `"md-arrow-back"`. | `string` | -| `cancelButtonText` | `cancel-button-text` | Set the the cancel button text. Only applies to `ios` mode. Default: `"Cancel"`. | `string` | -| `clearIcon` | `clear-icon` | Set the clear icon. Defaults to `"close-circle"` for `ios` and `"close"` for `md`. | `string \| undefined` | -| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | -| `debounce` | `debounce` | Set the amount of time, in milliseconds, to wait to trigger the `ionChange` event after each keystroke. Default `250`. | `number` | -| `mode` | `mode` | The mode determines which platform styles to use. Possible values are: `"ios"` or `"md"`. | `"ios" \| "md"` | -| `placeholder` | `placeholder` | Set the input's placeholder. Default `"Search"`. | `string` | -| `searchIcon` | `search-icon` | The icon to use as the search icon. Defaults to `"search"`. | `string \| undefined` | -| `showCancelButton` | `show-cancel-button` | If `true`, show the cancel button. Default `false`. | `boolean` | -| `spellcheck` | `spellcheck` | If `true`, enable spellcheck on the input. Default `false`. | `boolean` | -| `type` | `type` | Set the type of the input. Values: `"text"`, `"password"`, `"email"`, `"number"`, `"search"`, `"tel"`, `"url"`. Default `"search"`. | `string` | -| `value` | `value` | the value of the searchbar. | `string` | +| Property | Attribute | Description | Type | +| ------------------ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | +| `animated` | `animated` | If `true`, enable searchbar animation. Defaults to `false`. | `boolean` | +| `autocomplete` | `autocomplete` | Set the input's autocomplete property. Default `"off"`. | `"off" \| "on"` | +| `autocorrect` | `autocorrect` | Set the input's autocorrect property. Default `"off"`. | `"off" \| "on"` | +| `cancelButtonIcon` | `cancel-button-icon` | Set the cancel button icon. Only applies to `md` mode. Defaults to `"md-arrow-back"`. | `string` | +| `cancelButtonText` | `cancel-button-text` | Set the the cancel button text. Only applies to `ios` mode. Default: `"Cancel"`. | `string` | +| `clearIcon` | `clear-icon` | Set the clear icon. Defaults to `"close-circle"` for `ios` and `"close"` for `md`. | `string \| undefined` | +| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | +| `debounce` | `debounce` | Set the amount of time, in milliseconds, to wait to trigger the `ionChange` event after each keystroke. Default `250`. | `number` | +| `mode` | `mode` | The mode determines which platform styles to use. Possible values are: `"ios"` or `"md"`. | `"ios" \| "md"` | +| `placeholder` | `placeholder` | Set the input's placeholder. Default `"Search"`. | `string` | +| `searchIcon` | `search-icon` | The icon to use as the search icon. Defaults to `"search"`. | `string \| undefined` | +| `showCancelButton` | `show-cancel-button` | If `true`, show the cancel button. Defaults to `false`. | `boolean` | +| `spellcheck` | `spellcheck` | If `true`, enable spellcheck on the input. Defaults to `false`. | `boolean` | +| `type` | `type` | Set the type of the input. Values: `"text"`, `"password"`, `"email"`, `"number"`, `"search"`, `"tel"`, `"url"`. Default `"search"`. | `string` | +| `value` | `value` | the value of the searchbar. | `null \| string \| undefined` | ## Events diff --git a/core/src/components/searchbar/searchbar.tsx b/core/src/components/searchbar/searchbar.tsx index ef3bdefd778..803bf376fed 100644 --- a/core/src/components/searchbar/searchbar.tsx +++ b/core/src/components/searchbar/searchbar.tsx @@ -39,7 +39,7 @@ export class Searchbar implements ComponentInterface { @Prop() mode!: Mode; /** - * If `true`, enable searchbar animation. Default `false`. + * If `true`, enable searchbar animation. Defaults to `false`. */ @Prop() animated = false; @@ -89,12 +89,12 @@ export class Searchbar implements ComponentInterface { @Prop() searchIcon?: string; /** - * If `true`, show the cancel button. Default `false`. + * If `true`, show the cancel button. Defaults to `false`. */ @Prop() showCancelButton = false; /** - * If `true`, enable spellcheck on the input. Default `false`. + * If `true`, enable spellcheck on the input. Defaults to `false`. */ @Prop() spellcheck = false; @@ -106,7 +106,7 @@ export class Searchbar implements ComponentInterface { /** * the value of the searchbar. */ - @Prop({ mutable: true }) value = ''; + @Prop({ mutable: true }) value?: string | null = ''; /** * Emitted when a keyboard input ocurred. @@ -141,7 +141,7 @@ export class Searchbar implements ComponentInterface { @Watch('value') protected valueChanged() { const inputEl = this.nativeInput; - const value = this.value; + const value = this.getValue(); if (inputEl && inputEl.value !== value) { inputEl.value = value; } @@ -176,7 +176,7 @@ export class Searchbar implements ComponentInterface { // setTimeout() fixes https://github.com/ionic-team/ionic/issues/7527 // wait for 4 frames setTimeout(() => { - const value = this.value; + const value = this.getValue(); if (value !== '') { this.value = ''; this.ionInput.emit(); @@ -235,8 +235,9 @@ export class Searchbar implements ComponentInterface { * based on the input value and if it is focused. (ios only) */ private positionElements() { + const value = this.getValue(); const prevAlignLeft = this.shouldAlignLeft; - const shouldAlignLeft = (!this.animated || (this.value && this.value.toString().trim() !== '') || !!this.focused); + const shouldAlignLeft = (!this.animated || value.trim() !== '' || !!this.focused); this.shouldAlignLeft = shouldAlignLeft; if (this.mode !== 'ios') { @@ -322,12 +323,16 @@ export class Searchbar implements ComponentInterface { } } + private getValue() { + return this.value || ''; + } + hostData() { return { class: { ...createColorClasses(this.color), 'searchbar-animated': this.animated && this.config.getBoolean('animated', true), - 'searchbar-has-value': (this.value !== ''), + 'searchbar-has-value': (this.getValue() !== ''), 'searchbar-show-cancel': this.showCancelButton, 'searchbar-left-aligned': this.shouldAlignLeft, 'searchbar-has-focus': this.focused @@ -364,7 +369,7 @@ export class Searchbar implements ComponentInterface { onFocus={this.onFocus} placeholder={this.placeholder} type={this.type} - value={this.value} + value={this.getValue()} autoComplete={this.autocomplete} autoCorrect={this.autocorrect} spellCheck={this.spellcheck} diff --git a/core/src/components/segment-button/readme.md b/core/src/components/segment-button/readme.md index 9f40d4e0a19..c876e4bca0e 100644 --- a/core/src/components/segment-button/readme.md +++ b/core/src/components/segment-button/readme.md @@ -12,7 +12,7 @@ Segment buttons are groups of related buttons inside of a [Segment](../../segmen | ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | | `checked` | `checked` | If `true`, the segment button is selected. Defaults to `false`. | `boolean` | | `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | -| `disabled` | `disabled` | If `true`, the user cannot interact with the segment button. Default false. | `boolean` | +| `disabled` | `disabled` | If `true`, the user cannot interact with the segment button. Defaults to `false`. | `boolean` | | `mode` | `mode` | The mode determines which platform styles to use. Possible values are: `"ios"` or `"md"`. | `"ios" \| "md"` | | `value` | `value` | The value of the segment button. | `string` | diff --git a/core/src/components/segment-button/segment-button.tsx b/core/src/components/segment-button/segment-button.tsx index a533d6f5ee8..e48cd4ea3c6 100644 --- a/core/src/components/segment-button/segment-button.tsx +++ b/core/src/components/segment-button/segment-button.tsx @@ -33,7 +33,7 @@ export class SegmentButton implements ComponentInterface { @Prop({ mutable: true }) checked = false; /** - * If `true`, the user cannot interact with the segment button. Default false. + * If `true`, the user cannot interact with the segment button. Defaults to `false`. */ @Prop() disabled = false; diff --git a/core/src/components/select-option/readme.md b/core/src/components/select-option/readme.md index 9e2f63986b4..8bcb216bf37 100644 --- a/core/src/components/select-option/readme.md +++ b/core/src/components/select-option/readme.md @@ -15,14 +15,6 @@ SelectOption is a component that is a child element of Select. For more informat | `value` | -- | The text value of the option. | `any` | -## Events - -| Event | Detail | Description | -| -------------------------- | ------ | --------------------------------------- | -| `ionSelectOptionDidLoad` | | Emitted when the select option loads. | -| `ionSelectOptionDidUnload` | | Emitted when the select option unloads. | - - ---------------------------------------------- *Built with [StencilJS](https://stenciljs.com/)* diff --git a/core/src/components/select-option/select-option.tsx b/core/src/components/select-option/select-option.tsx index 39f7f071090..6e48fe83758 100644 --- a/core/src/components/select-option/select-option.tsx +++ b/core/src/components/select-option/select-option.tsx @@ -1,7 +1,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Prop } from '@stencil/core'; @Component({ - tag: 'ion-select-option' + tag: 'ion-select-option', }) export class SelectOption implements ComponentInterface { @@ -22,15 +22,17 @@ export class SelectOption implements ComponentInterface { /** * The text value of the option. */ - @Prop({ mutable: true }) value?: any; + @Prop({ mutable: true }) value?: any | null; /** * Emitted when the select option loads. + * @internal */ @Event() ionSelectOptionDidLoad!: EventEmitter; /** * Emitted when the select option unloads. + * @internal */ @Event() ionSelectOptionDidUnload!: EventEmitter; diff --git a/core/src/components/select/select.tsx b/core/src/components/select/select.tsx index 0514a62e2b1..628958ebfd5 100644 --- a/core/src/components/select/select.tsx +++ b/core/src/components/select/select.tsx @@ -1,7 +1,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Listen, Method, Prop, State, Watch } from '@stencil/core'; import { ActionSheetButton, ActionSheetOptions, AlertOptions, CssClassMap, Mode, OverlaySelect, PopoverOptions, SelectInputChangeEvent, SelectInterface, SelectPopoverOption, StyleEvent } from '../../interface'; -import { deferEvent, renderHiddenInput } from '../../utils/helpers'; +import { renderHiddenInput } from '../../utils/helpers'; import { hostContext } from '../../utils/theme'; @Component({ @@ -228,10 +228,7 @@ export class Select implements ComponentInterface { this.value = this.multiple ? [] : undefined; } } - componentDidLoad() { - this.ionStyle = deferEvent(this.ionStyle); - const label = this.getLabel(); if (label) { this.labelId = label.id = this.name + '-lbl'; diff --git a/core/src/components/textarea/readme.md b/core/src/components/textarea/readme.md index 2b7435830b7..6bc3a52bd0e 100644 --- a/core/src/components/textarea/readme.md +++ b/core/src/components/textarea/readme.md @@ -13,26 +13,26 @@ The textarea component accepts the [native textarea attributes](https://develope ## Properties -| Property | Attribute | Description | Type | -| ---------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | -| `autocapitalize` | `autocapitalize` | Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Defaults to `"none"`. | `string` | -| `autofocus` | `autofocus` | This Boolean attribute lets you specify that a form control should have input focus when the page loads. Defaults to `false`. | `boolean` | -| `clearOnEdit` | `clear-on-edit` | If `true`, the value will be cleared after focus upon edit. Defaults to `true` when `type` is `"password"`, `false` for all other types. | `boolean` | -| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | -| `cols` | `cols` | The visible width of the text control, in average character widths. If it is specified, it must be a positive integer. | `number \| undefined` | -| `debounce` | `debounce` | Set the amount of time, in milliseconds, to wait to trigger the `ionChange` event after each keystroke. Default `0`. | `number` | -| `disabled` | `disabled` | If `true`, the user cannot interact with the textarea. Defaults to `false`. | `boolean` | -| `maxlength` | `maxlength` | If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the maximum number of characters that the user can enter. | `number \| undefined` | -| `minlength` | `minlength` | If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the minimum number of characters that the user can enter. | `number \| undefined` | -| `mode` | `mode` | The mode determines which platform styles to use. Possible values are: `"ios"` or `"md"`. | `"ios" \| "md"` | -| `name` | `name` | The name of the control, which is submitted with the form data. | `string` | -| `placeholder` | `placeholder` | Instructional text that shows before the input has a value. | `string \| undefined` | -| `readonly` | `readonly` | If `true`, the user cannot modify the value. Defaults to `false`. | `boolean` | -| `required` | `required` | If `true`, the user must fill in a value before submitting a form. | `boolean` | -| `rows` | `rows` | The number of visible text lines for the control. | `number \| undefined` | -| `spellcheck` | `spellcheck` | If `true`, the element will have its spelling and grammar checked. Defaults to `false`. | `boolean` | -| `value` | `value` | The value of the textarea. | `string` | -| `wrap` | `wrap` | Indicates how the control wraps text. Possible values are: `"hard"`, `"soft"`, `"off"`. | `string \| undefined` | +| Property | Attribute | Description | Type | +| ---------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | +| `autocapitalize` | `autocapitalize` | Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Defaults to `"none"`. | `string` | +| `autofocus` | `autofocus` | This Boolean attribute lets you specify that a form control should have input focus when the page loads. Defaults to `false`. | `boolean` | +| `clearOnEdit` | `clear-on-edit` | If `true`, the value will be cleared after focus upon edit. Defaults to `true` when `type` is `"password"`, `false` for all other types. | `boolean` | +| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | +| `cols` | `cols` | The visible width of the text control, in average character widths. If it is specified, it must be a positive integer. | `number \| undefined` | +| `debounce` | `debounce` | Set the amount of time, in milliseconds, to wait to trigger the `ionChange` event after each keystroke. Default `0`. | `number` | +| `disabled` | `disabled` | If `true`, the user cannot interact with the textarea. Defaults to `false`. | `boolean` | +| `maxlength` | `maxlength` | If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the maximum number of characters that the user can enter. | `number \| undefined` | +| `minlength` | `minlength` | If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the minimum number of characters that the user can enter. | `number \| undefined` | +| `mode` | `mode` | The mode determines which platform styles to use. Possible values are: `"ios"` or `"md"`. | `"ios" \| "md"` | +| `name` | `name` | The name of the control, which is submitted with the form data. | `string` | +| `placeholder` | `placeholder` | Instructional text that shows before the input has a value. | `string \| undefined` | +| `readonly` | `readonly` | If `true`, the user cannot modify the value. Defaults to `false`. | `boolean` | +| `required` | `required` | If `true`, the user must fill in a value before submitting a form. | `boolean` | +| `rows` | `rows` | The number of visible text lines for the control. | `number \| undefined` | +| `spellcheck` | `spellcheck` | If `true`, the element will have its spelling and grammar checked. Defaults to `false`. | `boolean` | +| `value` | `value` | The value of the textarea. | `null \| string \| undefined` | +| `wrap` | `wrap` | Indicates how the control wraps text. Possible values are: `"hard"`, `"soft"`, `"off"`. | `string \| undefined` | ## Events diff --git a/core/src/components/textarea/textarea.tsx b/core/src/components/textarea/textarea.tsx index 0f5edcfcfef..58108204eb3 100644 --- a/core/src/components/textarea/textarea.tsx +++ b/core/src/components/textarea/textarea.tsx @@ -1,7 +1,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Method, Prop, State, Watch } from '@stencil/core'; import { Color, Mode, StyleEvent, TextInputChangeEvent } from '../../interface'; -import { debounceEvent, deferEvent, renderHiddenInput } from '../../utils/helpers'; +import { debounceEvent, renderHiddenInput } from '../../utils/helpers'; import { createColorClasses } from '../../utils/theme'; @Component({ @@ -123,14 +123,15 @@ export class Textarea implements ComponentInterface { /** * The value of the textarea. */ - @Prop({ mutable: true }) value = ''; + @Prop({ mutable: true }) value?: string | null = ''; /** * Update the native input element when the value changes */ @Watch('value') protected valueChanged() { - const { nativeInput, value } = this; + const nativeInput = this.nativeInput; + const value = this.getValue(); if (nativeInput!.value !== value) { nativeInput!.value = value; } @@ -162,10 +163,12 @@ export class Textarea implements ComponentInterface { */ @Event() ionFocus!: EventEmitter; + componentWillLoad() { + this.emitStyle(); + } + componentDidLoad() { - this.ionStyle = deferEvent(this.ionStyle); this.debounceChanged(); - this.emitStyle(); } /** @@ -241,7 +244,11 @@ export class Textarea implements ComponentInterface { } private hasValue(): boolean { - return this.value !== ''; + return this.getValue() !== ''; + } + + private getValue(): string { + return this.value || ''; } hostData() { @@ -253,7 +260,8 @@ export class Textarea implements ComponentInterface { } render() { - renderHiddenInput(this.el, this.name, this.value, this.disabled); + const value = this.getValue(); + renderHiddenInput(this.el, this.name, value, this.disabled); return ( ); } diff --git a/core/src/components/toggle/readme.md b/core/src/components/toggle/readme.md index 5ae64da2912..82207e49c56 100644 --- a/core/src/components/toggle/readme.md +++ b/core/src/components/toggle/readme.md @@ -9,14 +9,14 @@ Toggles change the state of a single option. Toggles can be switched on or off b ## Properties -| Property | Attribute | Description | Type | -| ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | -| `checked` | `checked` | If `true`, the toggle is selected. Defaults to `false`. | `boolean` | -| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | -| `disabled` | `disabled` | If `true`, the user cannot interact with the toggle. Default false. | `boolean` | -| `mode` | `mode` | The mode determines which platform styles to use. Possible values are: `"ios"` or `"md"`. | `"ios" \| "md"` | -| `name` | `name` | The name of the control, which is submitted with the form data. | `string` | -| `value` | `value` | the value of the toggle. | `string` | +| Property | Attribute | Description | Type | +| ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | +| `checked` | `checked` | If `true`, the toggle is selected. Defaults to `false`. | `boolean` | +| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | +| `disabled` | `disabled` | If `true`, the user cannot interact with the toggle. Defaults to `false`. | `boolean` | +| `mode` | `mode` | The mode determines which platform styles to use. Possible values are: `"ios"` or `"md"`. | `"ios" \| "md"` | +| `name` | `name` | The name of the control, which is submitted with the form data. | `string` | +| `value` | `value` | the value of the toggle. | `null \| string \| undefined` | ## Events diff --git a/core/src/components/toggle/toggle.tsx b/core/src/components/toggle/toggle.tsx index 1460cb19476..824587e128e 100644 --- a/core/src/components/toggle/toggle.tsx +++ b/core/src/components/toggle/toggle.tsx @@ -2,7 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Prop, Queu import { CheckedInputChangeEvent, Color, Gesture, GestureDetail, Mode, StyleEvent } from '../../interface'; import { hapticSelection } from '../../utils/haptic'; -import { deferEvent, renderHiddenInput } from '../../utils/helpers'; +import { renderHiddenInput } from '../../utils/helpers'; import { createColorClasses, hostContext } from '../../utils/theme'; @Component({ @@ -51,14 +51,14 @@ export class Toggle implements ComponentInterface { @Prop({ mutable: true }) checked = false; /** - * If `true`, the user cannot interact with the toggle. Default false. + * If `true`, the user cannot interact with the toggle. Defaults to `false`. */ @Prop() disabled = false; /** * the value of the toggle. */ - @Prop() value = 'on'; + @Prop() value?: string | null = 'on'; /** * Emitted when the value property has changed. @@ -90,16 +90,14 @@ export class Toggle implements ComponentInterface { @Watch('disabled') disabledChanged() { - this.ionStyle.emit({ - 'interactive-disabled': this.disabled, - }); + this.emitStyle(); if (this.gesture) { this.gesture.setDisabled(this.disabled); } } componentWillLoad() { - this.ionStyle = deferEvent(this.ionStyle); + this.emitStyle(); } async componentDidLoad() { @@ -125,6 +123,12 @@ export class Toggle implements ComponentInterface { this.disabledChanged(); } + private emitStyle() { + this.ionStyle.emit({ + 'interactive-disabled': this.disabled, + }); + } + private onStart(detail: GestureDetail) { this.pivotX = detail.currentX; this.activated = true; @@ -171,6 +175,10 @@ export class Toggle implements ComponentInterface { this.ionBlur.emit(); } + private getValue() { + return this.value || ''; + } + hostData() { return { class: { @@ -186,7 +194,8 @@ export class Toggle implements ComponentInterface { } render() { - renderHiddenInput(this.el, this.name, (this.checked ? this.value : ''), this.disabled); + const value = this.getValue(); + renderHiddenInput(this.el, this.name, (this.checked ? value : ''), this.disabled); return [
@@ -201,7 +210,7 @@ export class Toggle implements ComponentInterface { checked={this.checked} id={this.inputId} name={this.name} - value={this.value} + value={value} disabled={this.disabled} ref={r => this.nativeInput = (r as any)} />, diff --git a/core/src/utils/input-interface.ts b/core/src/utils/input-interface.ts index 38567556712..f6a49f6b975 100644 --- a/core/src/utils/input-interface.ts +++ b/core/src/utils/input-interface.ts @@ -1,5 +1,5 @@ export interface TextInputChangeEvent { - value: string | undefined; + value: string | undefined | null; } export interface CheckedInputChangeEvent extends TextInputChangeEvent {