mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-17 18:54:11 +08:00
fix(inputs): inputs work inside <form>
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
import { Component, Element, Event, EventEmitter, Prop, State, Watch } from '@stencil/core';
|
import { Component, Element, Event, EventEmitter, Prop, State, Watch } from '@stencil/core';
|
||||||
import { CheckboxInput, CheckedInputChangeEvent, Color, Mode, StyleEvent } from '../../interface';
|
import { CheckboxInput, CheckedInputChangeEvent, Color, Mode, StyleEvent } from '../../interface';
|
||||||
import { deferEvent } from '../../utils/helpers';
|
import { deferEvent, renderHiddenInput } from '../../utils/helpers';
|
||||||
import { createColorClasses, hostContext } from '../../utils/theme';
|
import { createColorClasses, hostContext } from '../../utils/theme';
|
||||||
|
|
||||||
|
|
||||||
@ -129,6 +129,8 @@ export class Checkbox implements CheckboxInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
renderHiddenInput(this.el, this.name, this.value, this.disabled);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
<div class="checkbox-icon">
|
<div class="checkbox-icon">
|
||||||
<div class="checkbox-inner"></div>
|
<div class="checkbox-inner"></div>
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
/* stylelint-disable */
|
/* stylelint-disable */
|
||||||
|
/* TODO: find a better solution in padding.css, that does not require !important, */
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
|
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
|
@ -18,6 +18,11 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
/* stylelint-disable */
|
||||||
|
/* TODO: find a better solution in padding.css, that does not require !important, */
|
||||||
|
padding: 0 !important;
|
||||||
|
/* stylelint-enable */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -55,15 +60,16 @@
|
|||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-weight: var(--placeholder-weight);
|
font-weight: var(--placeholder-weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:-webkit-autofill {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.native-input[disabled] {
|
.native-input[disabled] {
|
||||||
opacity: .4;
|
opacity: .4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.native-input:-webkit-autofill {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Input Cover: Unfocused
|
// Input Cover: Unfocused
|
||||||
@ -99,16 +105,13 @@
|
|||||||
|
|
||||||
border: 0;
|
border: 0;
|
||||||
|
|
||||||
|
outline: none;
|
||||||
|
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
|
|
||||||
&:active,
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:host(.has-focus.has-value) .input-clear-icon {
|
:host(.has-focus.has-value) .input-clear-icon {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Component, Element, Event, EventEmitter, Prop, State, Watch } from '@stencil/core';
|
import { Component, Element, Event, EventEmitter, Prop, State, Watch } from '@stencil/core';
|
||||||
import { Color, InputChangeEvent, Mode, StyleEvent } from '../../interface';
|
import { Color, InputChangeEvent, Mode, StyleEvent } from '../../interface';
|
||||||
import { debounceEvent, deferEvent } from '../../utils/helpers';
|
import { debounceEvent, deferEvent, renderHiddenInput } from '../../utils/helpers';
|
||||||
import { hostContext } from '../../utils/theme';
|
import { hostContext } from '../../utils/theme';
|
||||||
import { InputComponent } from './input-base';
|
import { InputComponent } from './input-base';
|
||||||
|
|
||||||
@ -16,6 +16,7 @@ import { InputComponent } from './input-base';
|
|||||||
export class Input implements InputComponent {
|
export class Input implements InputComponent {
|
||||||
|
|
||||||
private nativeInput?: HTMLInputElement;
|
private nativeInput?: HTMLInputElement;
|
||||||
|
private inputId = `ion-input-${inputIds++}`;
|
||||||
didBlurAfterEdit = false;
|
didBlurAfterEdit = false;
|
||||||
|
|
||||||
mode!: Mode;
|
mode!: Mode;
|
||||||
@ -148,7 +149,7 @@ export class Input implements InputComponent {
|
|||||||
/**
|
/**
|
||||||
* The name of the control, which is submitted with the form data.
|
* The name of the control, which is submitted with the form data.
|
||||||
*/
|
*/
|
||||||
@Prop() name?: string;
|
@Prop() name: string = this.inputId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A regular expression that the value is checked against. The pattern must match the entire value, not just some subset. Use the title attribute to describe the pattern to help the user. This attribute applies when the value of the type attribute is `"text"`, `"search"`, `"tel"`, `"url"`, `"email"`, or `"password"`, otherwise it is ignored.
|
* A regular expression that the value is checked against. The pattern must match the entire value, not just some subset. Use the title attribute to describe the pattern to help the user. This attribute applies when the value of the type attribute is `"text"`, `"search"`, `"tel"`, `"url"`, `"email"`, or `"password"`, otherwise it is ignored.
|
||||||
@ -276,14 +277,10 @@ export class Input implements InputComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inputKeydown() {
|
|
||||||
this.checkClearOnEdit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if we need to clear the text input if clearOnEdit is enabled
|
* Check if we need to clear the text input if clearOnEdit is enabled
|
||||||
*/
|
*/
|
||||||
private checkClearOnEdit() {
|
private onKeydown() {
|
||||||
if (!this.clearOnEdit) {
|
if (!this.clearOnEdit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -317,7 +314,7 @@ export class Input implements InputComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// TODO aria-labelledby={this.item.labelId}
|
renderHiddenInput(this.el, this.name, this.value, this.disabled);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
<input
|
<input
|
||||||
@ -350,8 +347,9 @@ export class Input implements InputComponent {
|
|||||||
onInput={this.onInput.bind(this)}
|
onInput={this.onInput.bind(this)}
|
||||||
onBlur={this.onBlur.bind(this)}
|
onBlur={this.onBlur.bind(this)}
|
||||||
onFocus={this.onFocus.bind(this)}
|
onFocus={this.onFocus.bind(this)}
|
||||||
onKeyDown={this.inputKeydown.bind(this)}
|
onKeyDown={this.onKeydown.bind(this)}
|
||||||
/>,
|
/>,
|
||||||
|
<slot></slot>,
|
||||||
this.clearInput && <button
|
this.clearInput && <button
|
||||||
type="button"
|
type="button"
|
||||||
class="input-clear-icon"
|
class="input-clear-icon"
|
||||||
@ -360,3 +358,5 @@ export class Input implements InputComponent {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let inputIds = 0;
|
||||||
|
@ -7,11 +7,17 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
<script src="/dist/ionic.js"></script>
|
<script src="/dist/ionic.js"></script>
|
||||||
<link rel="stylesheet" type="text/css" href="/css/ionic.min.css">
|
<link rel="stylesheet" type="text/css" href="/css/ionic.min.css">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
ion-label {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<ion-label>Default</ion-label>
|
<ion-label>Default</ion-label>
|
||||||
<ion-label>Wrap label this label just goes on and on and on</ion-label>
|
<ion-label color="primary">Wrap label this label just goes on and on and on</ion-label>
|
||||||
<ion-label position="fixed">Fixed</ion-label>
|
<ion-label position="fixed">Fixed</ion-label>
|
||||||
<ion-label position="floating">Floating</ion-label>
|
<ion-label position="floating">Floating</ion-label>
|
||||||
<ion-label position="stacked">Stacked</ion-label>
|
<ion-label position="stacked">Stacked</ion-label>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Component, Element, Event, EventEmitter, Listen, Prop, State, Watch } from '@stencil/core';
|
import { Component, Element, Event, EventEmitter, Listen, Prop, State, Watch } from '@stencil/core';
|
||||||
import { ActionSheetButton, ActionSheetOptions, AlertInput, AlertOptions, CssClassMap, Mode, PopoverOptions, SelectInputChangeEvent, SelectInterface, SelectPopoverOption, StyleEvent} from '../../interface';
|
import { ActionSheetButton, ActionSheetOptions, AlertInput, AlertOptions, CssClassMap, Mode, PopoverOptions, SelectInputChangeEvent, SelectInterface, SelectPopoverOption, StyleEvent} from '../../interface';
|
||||||
import { deferEvent } from '../../utils/helpers';
|
import { deferEvent, renderHiddenInput } from '../../utils/helpers';
|
||||||
import { createThemedClasses, hostContext } from '../../utils/theme';
|
import { createThemedClasses, hostContext } from '../../utils/theme';
|
||||||
|
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ import { createThemedClasses, hostContext } from '../../utils/theme';
|
|||||||
export class Select {
|
export class Select {
|
||||||
|
|
||||||
private childOpts: HTMLIonSelectOptionElement[] = [];
|
private childOpts: HTMLIonSelectOptionElement[] = [];
|
||||||
private selectId = `ion-sel-${selectIds++}`;
|
private inputId = `ion-sel-${selectIds++}`;
|
||||||
private labelId?: string;
|
private labelId?: string;
|
||||||
private overlay?: HTMLIonActionSheetElement | HTMLIonAlertElement | HTMLIonPopoverElement;
|
private overlay?: HTMLIonActionSheetElement | HTMLIonAlertElement | HTMLIonPopoverElement;
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ export class Select {
|
|||||||
/**
|
/**
|
||||||
* The name of the control, which is submitted with the form data.
|
* The name of the control, which is submitted with the form data.
|
||||||
*/
|
*/
|
||||||
@Prop({ mutable: true }) name?: string;
|
@Prop() name: string = this.inputId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The text to display instead of the selected option's value.
|
* The text to display instead of the selected option's value.
|
||||||
@ -225,7 +225,6 @@ export class Select {
|
|||||||
if (!this.value) {
|
if (!this.value) {
|
||||||
this.value = this.multiple ? [] : undefined;
|
this.value = this.multiple ? [] : undefined;
|
||||||
}
|
}
|
||||||
this.name = this.name || this.selectId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidLoad() {
|
componentDidLoad() {
|
||||||
@ -460,6 +459,8 @@ export class Select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
renderHiddenInput(this.el, this.name, parseValue(this.value), this.disabled);
|
||||||
|
|
||||||
let addPlaceholderClass = false;
|
let addPlaceholderClass = false;
|
||||||
|
|
||||||
let selectText = this.selectedText || this.text;
|
let selectText = this.selectedText || this.text;
|
||||||
@ -496,8 +497,7 @@ export class Select {
|
|||||||
class="select-cover">
|
class="select-cover">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
{ this.mode === 'md' && <ion-ripple-effect tapClick={true}/> }
|
{ this.mode === 'md' && <ion-ripple-effect tapClick={true}/> }
|
||||||
</button>,
|
</button>
|
||||||
<input type="hidden" name={this.name} value={parseValue(this.value)}/>
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Component, Element, Event, EventEmitter, Prop, State, Watch } from '@stencil/core';
|
import { Component, Element, Event, EventEmitter, Prop, State, Watch } from '@stencil/core';
|
||||||
import { Color, InputChangeEvent, Mode, StyleEvent } from '../../interface';
|
import { Color, InputChangeEvent, Mode, StyleEvent } from '../../interface';
|
||||||
import { debounceEvent, deferEvent } from '../../utils/helpers';
|
import { debounceEvent, deferEvent, renderHiddenInput } from '../../utils/helpers';
|
||||||
import { TextareaComponent } from '../input/input-base';
|
import { TextareaComponent } from '../input/input-base';
|
||||||
|
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ import { TextareaComponent } from '../input/input-base';
|
|||||||
export class Textarea implements TextareaComponent {
|
export class Textarea implements TextareaComponent {
|
||||||
|
|
||||||
private inputEl?: HTMLTextAreaElement;
|
private inputEl?: HTMLTextAreaElement;
|
||||||
|
private inputId = `ion-input-${textareaIds++}`;
|
||||||
|
|
||||||
mode!: Mode;
|
mode!: Mode;
|
||||||
color?: Color;
|
color?: Color;
|
||||||
@ -103,7 +104,7 @@ export class Textarea implements TextareaComponent {
|
|||||||
/**
|
/**
|
||||||
* The name of the control, which is submitted with the form data.
|
* The name of the control, which is submitted with the form data.
|
||||||
*/
|
*/
|
||||||
@Prop() name?: string;
|
@Prop() name: string = this.inputId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instructional text that shows before the input has a value.
|
* Instructional text that shows before the input has a value.
|
||||||
@ -229,6 +230,8 @@ export class Textarea implements TextareaComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
renderHiddenInput(this.el, this.name, this.value, this.disabled);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<textarea
|
<textarea
|
||||||
class="native-textarea"
|
class="native-textarea"
|
||||||
@ -257,3 +260,5 @@ export class Textarea implements TextareaComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let textareaIds = 0;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Component, Element, Event, EventEmitter, Prop, State, Watch } from '@stencil/core';
|
import { Component, Element, Event, EventEmitter, Prop, State, Watch } from '@stencil/core';
|
||||||
import { CheckboxInput, CheckedInputChangeEvent, Color, GestureDetail, Mode, StyleEvent } from '../../interface';
|
import { CheckboxInput, CheckedInputChangeEvent, Color, GestureDetail, Mode, StyleEvent } from '../../interface';
|
||||||
import { hapticSelection } from '../../utils/haptic';
|
import { hapticSelection } from '../../utils/haptic';
|
||||||
import { deferEvent } from '../../utils/helpers';
|
import { deferEvent, renderHiddenInput } from '../../utils/helpers';
|
||||||
import { createColorClasses, hostContext } from '../../utils/theme';
|
import { createColorClasses, hostContext } from '../../utils/theme';
|
||||||
|
|
||||||
|
|
||||||
@ -169,6 +169,8 @@ export class Toggle implements CheckboxInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
renderHiddenInput(this.el, this.name, this.value, this.disabled);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
<ion-gesture
|
<ion-gesture
|
||||||
onStart={this.onDragStart.bind(this)}
|
onStart={this.onDragStart.bind(this)}
|
||||||
@ -197,7 +199,8 @@ export class Toggle implements CheckboxInput {
|
|||||||
name={this.name}
|
name={this.name}
|
||||||
value={this.value}
|
value={this.value}
|
||||||
disabled={this.disabled}
|
disabled={this.disabled}
|
||||||
ref={r => this.nativeInput = (r as any)}/>
|
ref={r => this.nativeInput = (r as any)}/>,
|
||||||
|
<slot></slot>
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,21 @@ export function reorderArray(array: any[], indexes: {from: number, to: number}):
|
|||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function renderHiddenInput(container: HTMLElement, name: string, value: string, disabled: boolean) {
|
||||||
|
if (container.shadowRoot) {
|
||||||
|
let input = container.querySelector('input.aux-input') as HTMLInputElement;
|
||||||
|
if (!input) {
|
||||||
|
input = container.ownerDocument.createElement('input');
|
||||||
|
input.type = 'hidden';
|
||||||
|
input.classList.add('aux-input');
|
||||||
|
container.appendChild(input);
|
||||||
|
}
|
||||||
|
input.disabled = disabled;
|
||||||
|
input.name = name;
|
||||||
|
input.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function clamp(min: number, n: number, max: number) {
|
export function clamp(min: number, n: number, max: number) {
|
||||||
return Math.max(min, Math.min(n, max));
|
return Math.max(min, Math.min(n, max));
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ export function startInputShims(
|
|||||||
const scrollAssistMap = new WeakMap<HTMLElement, Function>();
|
const scrollAssistMap = new WeakMap<HTMLElement, Function>();
|
||||||
|
|
||||||
function registerInput(componentEl: HTMLElement) {
|
function registerInput(componentEl: HTMLElement) {
|
||||||
const inputEl = componentEl.querySelector('input');
|
const inputEl = (componentEl.shadowRoot || componentEl).querySelector('input');
|
||||||
const contentEl = componentEl.closest('ion-content');
|
const contentEl = componentEl.closest('ion-content');
|
||||||
const scrollEl = contentEl && contentEl.getScrollElement();
|
const scrollEl = contentEl && contentEl.getScrollElement();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user