mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-08 23:58:13 +08:00
feat(radio): add radio and radio group component
This commit is contained in:
@ -12,6 +12,9 @@ import { createThemedClasses } from '../../utils/theme';
|
||||
}
|
||||
})
|
||||
export class Item {
|
||||
private ids: number = -1;
|
||||
private id: string;
|
||||
|
||||
private childStyles: CssClassMap = Object.create(null);
|
||||
private label: any;
|
||||
|
||||
@ -63,6 +66,15 @@ export class Item {
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@Method()
|
||||
registerInput(type: string) {
|
||||
// this.inputs.push(type);
|
||||
return this.id + '-' + (++this.ids);
|
||||
}
|
||||
|
||||
render() {
|
||||
let themedClasses = {
|
||||
...this.childStyles,
|
||||
|
||||
159
packages/core/src/components/radio/radio-group.tsx
Normal file
159
packages/core/src/components/radio/radio-group.tsx
Normal file
@ -0,0 +1,159 @@
|
||||
import { Component, Element, Event, EventEmitter, HostElement, Prop , State} from '@stencil/core';
|
||||
|
||||
import { Radio } from './radio';
|
||||
|
||||
import { isCheckedProperty } from '../../utils/helpers';
|
||||
|
||||
/**
|
||||
* @name RadioGroup
|
||||
* @description
|
||||
* A radio group is a group of [radio buttons](../RadioButton). It allows
|
||||
* a user to select at most one radio button from a set. Checking one radio
|
||||
* button that belongs to a radio group unchecks any previous checked
|
||||
* radio button within the same group.
|
||||
*
|
||||
* See the [Angular Forms Docs](https://angular.io/docs/ts/latest/guide/forms.html)
|
||||
* for more information on forms and inputs.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <ion-list radio-group [(ngModel)]="autoManufacturers">
|
||||
*
|
||||
* <ion-list-header>
|
||||
* Auto Manufacturers
|
||||
* </ion-list-header>
|
||||
*
|
||||
* <ion-item>
|
||||
* <ion-label>Cord</ion-label>
|
||||
* <ion-radio value="cord"></ion-radio>
|
||||
* </ion-item>
|
||||
*
|
||||
* <ion-item>
|
||||
* <ion-label>Duesenberg</ion-label>
|
||||
* <ion-radio value="duesenberg"></ion-radio>
|
||||
* </ion-item>
|
||||
*
|
||||
* <ion-item>
|
||||
* <ion-label>Hudson</ion-label>
|
||||
* <ion-radio value="hudson"></ion-radio>
|
||||
* </ion-item>
|
||||
*
|
||||
* <ion-item>
|
||||
* <ion-label>Packard</ion-label>
|
||||
* <ion-radio value="packard"></ion-radio>
|
||||
* </ion-item>
|
||||
*
|
||||
* <ion-item>
|
||||
* <ion-label>Studebaker</ion-label>
|
||||
* <ion-radio value="studebaker"></ion-radio>
|
||||
* </ion-item>
|
||||
*
|
||||
* </ion-list>
|
||||
* ```
|
||||
*
|
||||
* @demo /docs/demos/src/radio/
|
||||
* @see {@link /docs/components#radio Radio Component Docs}
|
||||
* @see {@link ../RadioButton RadioButton API Docs}
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-radio-group'
|
||||
})
|
||||
export class RadioGroup {
|
||||
radios: Radio[];
|
||||
|
||||
@Element() el: HTMLElement;
|
||||
|
||||
@State() activeId: string;
|
||||
@State() headerId: string;
|
||||
|
||||
|
||||
/**
|
||||
* @output {any} Emitted when the selected button has changed.
|
||||
*/
|
||||
@Event() ionChange: EventEmitter;
|
||||
|
||||
/*
|
||||
* @input {boolean} If true, the radios can be deselected. Default false.
|
||||
*/
|
||||
@Prop() allowEmptySelection: boolean = false;
|
||||
|
||||
/*
|
||||
* @input {boolean} If true, the user cannot interact with this element. Default false.
|
||||
*/
|
||||
@Prop({ state: true }) disabled: boolean = false;
|
||||
|
||||
/**
|
||||
* @input {string} the value of the radio.
|
||||
*/
|
||||
@Prop({ state: true }) value: string;
|
||||
|
||||
|
||||
ionViewWillLoad() {
|
||||
let radioGroupId = ++radioGroupIds;
|
||||
let radioId = 0;
|
||||
|
||||
const radios = this.el.querySelectorAll('ion-radio') as NodeListOf<HostElement>;
|
||||
|
||||
for (var i = 0; i < radios.length; i++) {
|
||||
const radio = radios[i].$instance;
|
||||
|
||||
if (radio) {
|
||||
radio.id = 'rb-' + radioGroupId + '-' + (++radioId);
|
||||
|
||||
if (radio.checked) {
|
||||
this.activeId = radio.id;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
// Get the list header if it exists and set the id
|
||||
const header = this.el.querySelector('ion-list-header') as HostElement;
|
||||
|
||||
if (header) {
|
||||
if (!header.id) {
|
||||
header.id = 'rg-hdr-' + radioGroupId;
|
||||
}
|
||||
this.headerId = header.id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
update() {
|
||||
// loop through each of the radios
|
||||
let hasChecked = false;
|
||||
this.radios.forEach(radio => {
|
||||
|
||||
// check this radiobutton if its value is
|
||||
// the same as the radiogroups value
|
||||
radio.checked = isCheckedProperty(this.value, radio.value) && !hasChecked;
|
||||
|
||||
if (radio.checked) {
|
||||
// if this button is checked, then set it as
|
||||
// the radiogroup's active descendant
|
||||
this.activeId = radio.id;
|
||||
hasChecked = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
attrs: {
|
||||
'role': 'radiogroup',
|
||||
'aria-activedescendant': this.activeId,
|
||||
'aria-describedby': this.headerId
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return <slot></slot>;
|
||||
}
|
||||
}
|
||||
|
||||
let radioGroupIds = -1;
|
||||
156
packages/core/src/components/radio/radio.ios.scss
Normal file
156
packages/core/src/components/radio/radio.ios.scss
Normal file
@ -0,0 +1,156 @@
|
||||
@import "../../themes/ionic.globals.ios";
|
||||
@import "./radio";
|
||||
|
||||
// iOS Radio
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Color of the checked radio
|
||||
$radio-ios-color-on: color($colors-ios, primary) !default;
|
||||
|
||||
/// @prop - Width of the radio icon
|
||||
$radio-ios-icon-width: 16px !default;
|
||||
|
||||
/// @prop - Height of the radio icon
|
||||
$radio-ios-icon-height: 21px !default;
|
||||
|
||||
/// @prop - Border width of the radio icon
|
||||
$radio-ios-icon-border-width: 2px !default;
|
||||
|
||||
/// @prop - Border style of the radio icon
|
||||
$radio-ios-icon-border-style: solid !default;
|
||||
|
||||
/// @prop - Opacity of the disabled radio
|
||||
$radio-ios-disabled-opacity: .3 !default;
|
||||
|
||||
// deprecated
|
||||
$radio-ios-item-left-margin: null !default;
|
||||
|
||||
/// @prop - Margin top of the item-start in a radio
|
||||
$radio-ios-item-start-margin-top: 8px !default;
|
||||
|
||||
/// @prop - Margin end of the item-start in a radio
|
||||
$radio-ios-item-start-margin-end: 21px !default;
|
||||
|
||||
/// @prop - Margin bottom of the item-start in a radio
|
||||
$radio-ios-item-start-margin-bottom: 8px !default;
|
||||
|
||||
/// @prop - Margin start of the item-start in a radio
|
||||
$radio-ios-item-start-margin-start: 3px !default;
|
||||
|
||||
// deprecated
|
||||
$radio-ios-item-right-margin: null !default;
|
||||
|
||||
/// @prop - Margin top of the item-end in a radio
|
||||
$radio-ios-item-end-margin-top: $item-ios-padding-media-top !default;
|
||||
|
||||
/// @prop - Margin end of the item-end in a radio
|
||||
$radio-ios-item-end-margin-end: 11px !default;
|
||||
|
||||
/// @prop - Margin bottom of the item-end in a radio
|
||||
$radio-ios-item-end-margin-bottom: $item-ios-padding-media-bottom !default;
|
||||
|
||||
/// @prop - Margin start of the item-end in a radio
|
||||
$radio-ios-item-end-margin-start: ($item-ios-padding-start / 2) !default;
|
||||
|
||||
|
||||
// iOS Radio Circle: Unchecked
|
||||
// -----------------------------------------
|
||||
|
||||
.radio-ios .radio-icon {
|
||||
position: relative;
|
||||
display: block;
|
||||
|
||||
width: $radio-ios-icon-width;
|
||||
height: $radio-ios-icon-height;
|
||||
}
|
||||
|
||||
|
||||
// iOS Radio Checkmark: Checked
|
||||
// -----------------------------------------
|
||||
|
||||
.radio-ios .radio-checked .radio-inner {
|
||||
@include position(4px, null, null, 7px);
|
||||
|
||||
position: absolute;
|
||||
|
||||
width: 5px;
|
||||
height: 12px;
|
||||
|
||||
border-width: $radio-ios-icon-border-width;
|
||||
border-top-width: 0;
|
||||
border-left-width: 0;
|
||||
border-style: $radio-ios-icon-border-style;
|
||||
border-color: $radio-ios-color-on;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
|
||||
// iOS Radio: Disabled
|
||||
// -----------------------------------------
|
||||
|
||||
.radio-ios.radio-disabled,
|
||||
.item-ios.item-radio-disabled ion-label {
|
||||
opacity: $radio-ios-disabled-opacity;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
// iOS Radio Within An Item
|
||||
// -----------------------------------------
|
||||
|
||||
.item-ios .radio-ios {
|
||||
position: static;
|
||||
display: block;
|
||||
|
||||
@include deprecated-variable(margin, $radio-ios-item-right-margin) {
|
||||
@include margin($radio-ios-item-end-margin-top, $radio-ios-item-end-margin-end, $radio-ios-item-end-margin-bottom, $radio-ios-item-end-margin-start);
|
||||
}
|
||||
|
||||
&[item-left], // deprecated
|
||||
&[item-start] {
|
||||
@include deprecated-variable(margin, $radio-ios-item-left-margin) {
|
||||
@include margin($radio-ios-item-start-margin-top, $radio-ios-item-start-margin-end, $radio-ios-item-start-margin-bottom, $radio-ios-item-start-margin-start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-radio.item-ios ion-label {
|
||||
@include margin-horizontal(0, null);
|
||||
}
|
||||
|
||||
|
||||
// iOS Radio Item Label: Checked
|
||||
// -----------------------------------------
|
||||
|
||||
.item-radio-checked.item-ios ion-label {
|
||||
color: $radio-ios-color-on;
|
||||
}
|
||||
|
||||
|
||||
// iOS Radio Color Mixin
|
||||
// --------------------------------------------------
|
||||
|
||||
@mixin radio-theme-ios($color-name, $color-base) {
|
||||
|
||||
.item-radio-ios-#{$color-name}.item-radio-checked ion-label {
|
||||
color: $color-base;
|
||||
}
|
||||
|
||||
.radio-ios-#{$color-name} .radio-checked {
|
||||
color: $color-base;
|
||||
}
|
||||
|
||||
.radio-ios-#{$color-name} .radio-checked .radio-inner {
|
||||
border-color: $color-base;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Generate iOS Radio Colors
|
||||
// --------------------------------------------------
|
||||
|
||||
@each $color-name, $color-base, $color-contrast in get-colors($colors-ios) {
|
||||
@include radio-theme-ios($color-name, $color-base);
|
||||
}
|
||||
190
packages/core/src/components/radio/radio.md.scss
Normal file
190
packages/core/src/components/radio/radio.md.scss
Normal file
@ -0,0 +1,190 @@
|
||||
@import "../../themes/ionic.globals.md";
|
||||
@import "./radio";
|
||||
|
||||
// Material Design Radio
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Color of the checked radio
|
||||
$radio-md-color-on: color($colors-md, primary) !default;
|
||||
|
||||
/// @prop - Color of the unchecked radio
|
||||
$radio-md-color-off: darken($list-md-border-color, 40%) !default;
|
||||
|
||||
/// @prop - Width of the radio icon
|
||||
$radio-md-icon-width: 16px !default;
|
||||
|
||||
/// @prop - Height of the radio icon
|
||||
$radio-md-icon-height: 16px !default;
|
||||
|
||||
/// @prop - Border width of the radio icon
|
||||
$radio-md-icon-border-width: 2px !default;
|
||||
|
||||
/// @prop - Border style of the radio icon
|
||||
$radio-md-icon-border-style: solid !default;
|
||||
|
||||
/// @prop - Border radius of the radio icon
|
||||
$radio-md-icon-border-radius: 50% !default;
|
||||
|
||||
/// @prop - Transition duration of the radio
|
||||
$radio-md-transition-duration: 280ms !default;
|
||||
|
||||
/// @prop - Transition easing of the radio
|
||||
$radio-md-transition-easing: cubic-bezier(.4, 0, .2, 1) !default;
|
||||
|
||||
/// @prop - Opacity of the disabled radio
|
||||
$radio-md-disabled-opacity: .3 !default;
|
||||
|
||||
// deprecated
|
||||
$radio-md-item-left-margin: null !default;
|
||||
|
||||
/// @prop - Margin top of the item-start in a radio
|
||||
$radio-md-item-start-margin-top: 11px !default;
|
||||
|
||||
/// @prop - Margin end of the item-start in a radio
|
||||
$radio-md-item-start-margin-end: 36px !default;
|
||||
|
||||
/// @prop - Margin bottom of the item-start in a radio
|
||||
$radio-md-item-start-margin-bottom: 10px !default;
|
||||
|
||||
/// @prop - Margin start of the item-start in a radio
|
||||
$radio-md-item-start-margin-start: 4px !default;
|
||||
|
||||
// deprecated
|
||||
$radio-md-item-right-margin: null !default;
|
||||
|
||||
/// @prop - Margin top of the item-end in a radio
|
||||
$radio-md-item-end-margin-top: $item-md-padding-media-top !default;
|
||||
|
||||
/// @prop - Margin end of the item-end in a radio
|
||||
$radio-md-item-end-margin-end: 10px !default;
|
||||
|
||||
/// @prop - Margin bottom of the item-end in a radio
|
||||
$radio-md-item-end-margin-bottom: $item-md-padding-media-bottom !default;
|
||||
|
||||
/// @prop - Margin start of the item-end in a radio
|
||||
$radio-md-item-end-margin-start: 0 !default;
|
||||
|
||||
|
||||
// Material Design Radio Outer Circle: Unchecked
|
||||
// -----------------------------------------
|
||||
|
||||
.radio-md .radio-icon {
|
||||
@include position(0, null, null, 0);
|
||||
@include margin(0);
|
||||
@include border-radius($radio-md-icon-border-radius);
|
||||
|
||||
position: relative;
|
||||
display: block;
|
||||
|
||||
width: $radio-md-icon-width;
|
||||
height: $radio-md-icon-height;
|
||||
|
||||
border-width: $radio-md-icon-border-width;
|
||||
border-style: $radio-md-icon-border-style;
|
||||
border-color: $radio-md-color-off;
|
||||
}
|
||||
|
||||
|
||||
// Material Design Radio Inner Circle: Unchecked
|
||||
// -----------------------------------------
|
||||
|
||||
.radio-md .radio-inner {
|
||||
@include position($radio-md-icon-border-width, null, null, $radio-md-icon-border-width);
|
||||
@include border-radius(50%);
|
||||
|
||||
position: absolute;
|
||||
|
||||
width: $radio-md-icon-width - $radio-md-icon-border-width * 4;
|
||||
height: $radio-md-icon-height - $radio-md-icon-border-width * 4;
|
||||
|
||||
background-color: $radio-md-color-on;
|
||||
transform: scale3d(0, 0, 0);
|
||||
transition: transform $radio-md-transition-duration $radio-md-transition-easing;
|
||||
}
|
||||
|
||||
|
||||
// Material Design Radio Outer Circle: Checked
|
||||
// -----------------------------------------
|
||||
|
||||
.radio-md .radio-checked {
|
||||
border-color: $radio-md-color-on;
|
||||
}
|
||||
|
||||
|
||||
// Material Design Radio Inner Circle: Checked
|
||||
// -----------------------------------------
|
||||
|
||||
.radio-md .radio-checked .radio-inner {
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
// Material Design Radio: Disabled
|
||||
// -----------------------------------------
|
||||
|
||||
.radio-md.radio-disabled,
|
||||
.item-md.item-radio-disabled ion-label {
|
||||
opacity: $radio-md-disabled-opacity;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
// Material Design Radio Within An Item
|
||||
// -----------------------------------------
|
||||
|
||||
.item-md .radio-md {
|
||||
position: static;
|
||||
display: block;
|
||||
|
||||
@include deprecated-variable(margin, $radio-md-item-right-margin) {
|
||||
@include margin($radio-md-item-end-margin-top, $radio-md-item-end-margin-end, $radio-md-item-end-margin-bottom, $radio-md-item-end-margin-start);
|
||||
}
|
||||
|
||||
&[item-left], // deprecated
|
||||
&[item-start] {
|
||||
@include deprecated-variable(margin, $radio-md-item-left-margin) {
|
||||
@include margin($radio-md-item-start-margin-top, $radio-md-item-start-margin-end, $radio-md-item-start-margin-bottom, $radio-md-item-start-margin-start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-radio.item-md ion-label {
|
||||
@include margin-horizontal(0, null);
|
||||
}
|
||||
|
||||
|
||||
// Material Design Radio Item Label: Checked
|
||||
// -----------------------------------------
|
||||
|
||||
.item-radio-checked.item-md ion-label {
|
||||
color: $radio-md-color-on;
|
||||
}
|
||||
|
||||
|
||||
// Material Design Radio Color Mixin
|
||||
// --------------------------------------------------
|
||||
|
||||
@mixin radio-theme-md($color-name, $color-base, $color-contrast) {
|
||||
|
||||
.item-radio-md-#{$color-name}.item-radio-checked ion-label {
|
||||
color: $color-base;
|
||||
}
|
||||
|
||||
.radio-md-#{$color-name} .radio-checked {
|
||||
border-color: $color-base;
|
||||
}
|
||||
|
||||
.radio-md-#{$color-name} .radio-inner {
|
||||
background-color: $color-base;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Generate Material Design Radio Colors
|
||||
// --------------------------------------------------
|
||||
|
||||
@each $color-name, $color-base, $color-contrast in get-colors($colors-md) {
|
||||
@include radio-theme-md($color-name, $color-base, $color-contrast);
|
||||
}
|
||||
22
packages/core/src/components/radio/radio.scss
Normal file
22
packages/core/src/components/radio/radio.scss
Normal file
@ -0,0 +1,22 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
|
||||
// Radio
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
ion-radio {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.radio-cover {
|
||||
@include position(0, null, null, 0);
|
||||
|
||||
position: absolute;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
211
packages/core/src/components/radio/radio.tsx
Normal file
211
packages/core/src/components/radio/radio.tsx
Normal file
@ -0,0 +1,211 @@
|
||||
import { Component, CssClassMap, Element, Event, EventEmitter, Listen, Prop, PropDidChange, State } from '@stencil/core';
|
||||
|
||||
import { createThemedClasses } from '../../utils/theme';
|
||||
|
||||
import { RadioGroup } from './radio-group';
|
||||
|
||||
/**
|
||||
* @description
|
||||
* A radio button is a button that can be either checked or unchecked. A user can tap
|
||||
* the button to check or uncheck it. It can also be checked from the template using
|
||||
* the `checked` property.
|
||||
*
|
||||
* Use an element with a `radio-group` attribute to group a set of radio buttons. When
|
||||
* radio buttons are inside a [radio group](../RadioGroup), exactly one radio button
|
||||
* in the group can be checked at any time. If a radio button is not placed in a group,
|
||||
* they will all have the ability to be checked at the same time.
|
||||
*
|
||||
* See the [Angular Forms Docs](https://angular.io/docs/ts/latest/guide/forms.html) for
|
||||
* more information on forms and input.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <ion-list radio-group [(ngModel)]="relationship">
|
||||
* <ion-item>
|
||||
* <ion-label>Friends</ion-label>
|
||||
* <ion-radio value="friends" checked></ion-radio>
|
||||
* </ion-item>
|
||||
* <ion-item>
|
||||
* <ion-label>Family</ion-label>
|
||||
* <ion-radio value="family"></ion-radio>
|
||||
* </ion-item>
|
||||
* <ion-item>
|
||||
* <ion-label>Enemies</ion-label>
|
||||
* <ion-radio value="enemies" [disabled]="isDisabled"></ion-radio>
|
||||
* </ion-item>
|
||||
* </ion-list>
|
||||
* ```
|
||||
* @demo /docs/demos/src/radio/
|
||||
* @see {@link /docs/components#radio Radio Component Docs}
|
||||
* @see {@link ../RadioGroup RadioGroup API Docs}
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-radio',
|
||||
styleUrls: {
|
||||
ios: 'radio.ios.scss',
|
||||
md: 'radio.md.scss',
|
||||
wp: 'radio.wp.scss'
|
||||
},
|
||||
host: {
|
||||
theme: 'radio'
|
||||
}
|
||||
})
|
||||
export class Radio {
|
||||
mode: string;
|
||||
color: string;
|
||||
group: any;
|
||||
labelId: string;
|
||||
styleTmr: any;
|
||||
|
||||
@Element() el: HTMLElement;
|
||||
|
||||
@State() id: string;
|
||||
|
||||
@State() activated: boolean;
|
||||
|
||||
/**
|
||||
* @output {EventEmitter} Emitted when the styles of the radio change.
|
||||
*/
|
||||
@Event() ionStyle: EventEmitter;
|
||||
|
||||
/**
|
||||
* @output {EventEmitter} Emitted when the radio is selected.
|
||||
*/
|
||||
@Event() ionSelect: EventEmitter;
|
||||
|
||||
/*
|
||||
* @input {boolean} If true, the radio is checked. Default false.
|
||||
*/
|
||||
@Prop({ state: true }) checked: boolean = false;
|
||||
|
||||
/*
|
||||
* @input {boolean} If true, the user cannot interact with this element. Default false.
|
||||
*/
|
||||
@Prop({ state: true }) disabled: boolean = false;
|
||||
|
||||
/**
|
||||
* @input {string} the value of the radio.
|
||||
*/
|
||||
@Prop({ state: true }) value: string;
|
||||
|
||||
|
||||
ionViewWillLoad() {
|
||||
this.emitStyle();
|
||||
}
|
||||
|
||||
ionViewDidLoad() {
|
||||
this.group = this.el.closest('ion-radio-group') as any;
|
||||
|
||||
const item = this.el.closest('ion-item') as any;
|
||||
|
||||
if (item) {
|
||||
// register the input inside of the item
|
||||
// reset to the item's id instead of the radiogroup id
|
||||
this.id = 'rb-' + item.registerInput('radio');
|
||||
this.labelId = 'lbl-' + item.id;
|
||||
}
|
||||
|
||||
// if the value is not defined then use it's unique id
|
||||
this.value = !this.value ? this.id : this.value;
|
||||
}
|
||||
|
||||
@PropDidChange('checked')
|
||||
checkedChanged(val: boolean) {
|
||||
this.ionSelect.emit({ checked: val });
|
||||
|
||||
if (this.group) {
|
||||
this.group.value = this.value;
|
||||
this.group.$instance.ionChange.emit(this);
|
||||
}
|
||||
|
||||
this.emitStyle();
|
||||
}
|
||||
|
||||
@PropDidChange('disabled')
|
||||
disabledChanged() {
|
||||
this.emitStyle();
|
||||
}
|
||||
|
||||
private emitStyle() {
|
||||
clearTimeout(this.styleTmr);
|
||||
|
||||
this.styleTmr = setTimeout(() => {
|
||||
this.ionStyle.emit({
|
||||
...createThemedClasses(this.mode, this.color, 'radio'),
|
||||
'radio-checked': this.checked,
|
||||
'radio-disabled': this.disabled,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Listen('keydown.space')
|
||||
onSpace(ev: KeyboardEvent) {
|
||||
this.toggle();
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.checked = !this.checked;
|
||||
}
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
'radio-checked': this.checked,
|
||||
'radio-disabled': this.disabled
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const radioClasses: CssClassMap = {
|
||||
'radio-icon': true,
|
||||
'radio-checked': this.checked
|
||||
};
|
||||
|
||||
return [
|
||||
<div class={radioClasses}>
|
||||
<div class='radio-inner'></div>
|
||||
</div>,
|
||||
<button
|
||||
class='radio-cover'
|
||||
onClick={() => this.toggle()}
|
||||
id={this.id}
|
||||
aria-checked={this.checked ? 'true' : false}
|
||||
aria-disabled={this.disabled ? 'true' : false}
|
||||
aria-labelledby={this.labelId}
|
||||
role='radio'
|
||||
tabIndex={0}
|
||||
/>
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// constructor() {
|
||||
// if (_group) {
|
||||
// // register with the radiogroup
|
||||
// this.id = 'rb-' + _group.add(this);
|
||||
// }
|
||||
|
||||
// if (_item) {
|
||||
// // register the input inside of the item
|
||||
// // reset to the item's id instead of the radiogroup id
|
||||
// this.id = 'rb-' + _item.registerInput('radio');
|
||||
// this._labelId = 'lbl-' + _item.id;
|
||||
// this._item.setElementClass('item-radio', true);
|
||||
// }
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * @internal
|
||||
// */
|
||||
// ngOnInit() {
|
||||
// if (this._group && isPresent(this._group.value)) {
|
||||
// this.checked = isCheckedProperty(this._group.value, this.value);
|
||||
// }
|
||||
|
||||
// if (this._group && this._group.disabled) {
|
||||
// this.disabled = this._group.disabled;
|
||||
// }
|
||||
// }
|
||||
177
packages/core/src/components/radio/radio.wp.scss
Normal file
177
packages/core/src/components/radio/radio.wp.scss
Normal file
@ -0,0 +1,177 @@
|
||||
@import "../../themes/ionic.globals.wp";
|
||||
@import "./radio";
|
||||
|
||||
// Windows Radio
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Color of the checked radio
|
||||
$radio-wp-color-on: color($colors-wp, primary) !default;
|
||||
|
||||
/// @prop - Color of the unchecked radio
|
||||
$radio-wp-color-off: #333 !default;
|
||||
|
||||
/// @prop - Order of the radio (places to the left of the item)
|
||||
$radio-wp-order: -1 !default;
|
||||
|
||||
/// @prop - Width of the radio icon
|
||||
$radio-wp-icon-width: 16px !default;
|
||||
|
||||
/// @prop - Height of the radio icon
|
||||
$radio-wp-icon-height: 16px !default;
|
||||
|
||||
/// @prop - Border width of the radio icon
|
||||
$radio-wp-icon-border-width: 2px !default;
|
||||
|
||||
/// @prop - Border style of the radio icon
|
||||
$radio-wp-icon-border-style: solid !default;
|
||||
|
||||
/// @prop - Border radius of the radio icon
|
||||
$radio-wp-icon-border-radius: 50% !default;
|
||||
|
||||
/// @prop - Opacity of the disabled radio
|
||||
$radio-wp-disabled-opacity: .3 !default;
|
||||
|
||||
// deprecated
|
||||
$radio-wp-item-left-margin: null !default;
|
||||
|
||||
/// @prop - Margin top of the item-start in a radio
|
||||
$radio-wp-item-start-margin-top: 9px !default;
|
||||
|
||||
/// @prop - Margin end of the item-start in a radio
|
||||
$radio-wp-item-start-margin-end: 20px !default;
|
||||
|
||||
/// @prop - Margin bottom of the item-start in a radio
|
||||
$radio-wp-item-start-margin-bottom: 9px !default;
|
||||
|
||||
/// @prop - Margin start of the item-start in a radio
|
||||
$radio-wp-item-start-margin-start: 4px !default;
|
||||
|
||||
// deprecated
|
||||
$radio-wp-item-right-margin: null !default;
|
||||
|
||||
/// @prop - Margin top of the item-end in a radio
|
||||
$radio-wp-item-end-margin-top: 11px !default;
|
||||
|
||||
/// @prop - Margin end of the item-end in a radio
|
||||
$radio-wp-item-end-margin-end: 10px !default;
|
||||
|
||||
/// @prop - Margin bottom of the item-end in a radio
|
||||
$radio-wp-item-end-margin-bottom: 10px !default;
|
||||
|
||||
/// @prop - Margin start of the item-end in a radio
|
||||
$radio-wp-item-end-margin-start: 0 !default;
|
||||
|
||||
|
||||
// Windows Radio Outer Circle: Unchecked
|
||||
// -----------------------------------------
|
||||
|
||||
.radio-wp .radio-icon {
|
||||
@include position(0, null, null, 0);
|
||||
@include margin(0);
|
||||
@include border-radius($radio-wp-icon-border-radius);
|
||||
|
||||
position: relative;
|
||||
|
||||
display: block;
|
||||
|
||||
width: $radio-wp-icon-width;
|
||||
height: $radio-wp-icon-height;
|
||||
|
||||
border-width: $radio-wp-icon-border-width;
|
||||
border-style: $radio-wp-icon-border-style;
|
||||
border-color: $radio-wp-color-off;
|
||||
}
|
||||
|
||||
|
||||
// Windows Radio Inner Circle: Unchecked
|
||||
// -----------------------------------------
|
||||
|
||||
.radio-wp .radio-inner {
|
||||
@include position($radio-wp-icon-border-width, null, null, $radio-wp-icon-border-width);
|
||||
@include border-radius(50%);
|
||||
|
||||
position: absolute;
|
||||
|
||||
display: none;
|
||||
|
||||
width: $radio-wp-icon-width / 2;
|
||||
height: $radio-wp-icon-height / 2;
|
||||
|
||||
background-color: $radio-wp-color-off;
|
||||
}
|
||||
|
||||
|
||||
// Windows Radio Outer Circle: Checked
|
||||
// -----------------------------------------
|
||||
|
||||
.radio-wp .radio-checked {
|
||||
border-color: $radio-wp-color-on;
|
||||
}
|
||||
|
||||
|
||||
// Windows Radio Inner Circle: Checked
|
||||
// -----------------------------------------
|
||||
|
||||
.radio-wp .radio-checked .radio-inner {
|
||||
// transform: scale3d(1, 1, 1);
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
// Windows Radio: Disabled
|
||||
// -----------------------------------------
|
||||
|
||||
.radio-wp.radio-disabled,
|
||||
.item-wp.item-radio-disabled ion-label {
|
||||
opacity: $radio-wp-disabled-opacity;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
// Windows Radio Within An Item
|
||||
// -----------------------------------------
|
||||
|
||||
.item-wp .radio-wp {
|
||||
position: static;
|
||||
display: block;
|
||||
|
||||
order: $radio-wp-order;
|
||||
|
||||
@include deprecated-variable(margin, $radio-wp-item-left-margin) {
|
||||
@include margin($radio-wp-item-start-margin-top, $radio-wp-item-start-margin-end, $radio-wp-item-start-margin-bottom, $radio-wp-item-start-margin-start);
|
||||
}
|
||||
|
||||
&[item-right], // deprecated
|
||||
&[item-end] {
|
||||
order: 0;
|
||||
|
||||
@include deprecated-variable(margin, $radio-wp-item-right-margin) {
|
||||
@include margin($radio-wp-item-end-margin-top, $radio-wp-item-end-margin-end, $radio-wp-item-end-margin-bottom, $radio-wp-item-end-margin-start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-radio.item-wp ion-label {
|
||||
@include margin-horizontal(0, null);
|
||||
}
|
||||
|
||||
|
||||
// Windows Radio Color Mixin
|
||||
// --------------------------------------------------
|
||||
|
||||
@mixin radio-theme-wp($color-name, $color-base) {
|
||||
|
||||
.radio-wp-#{$color-name} .radio-checked {
|
||||
border-color: $color-base;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Generate Windows Radio Colors
|
||||
// --------------------------------------------------
|
||||
|
||||
@each $color-name, $color-base, $color-contrast in get-colors($colors-wp) {
|
||||
@include radio-theme-wp($color-name, $color-base);
|
||||
}
|
||||
136
packages/core/src/components/radio/test/basic.html
Normal file
136
packages/core/src/components/radio/test/basic.html
Normal file
@ -0,0 +1,136 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ionic Radio</title>
|
||||
<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>
|
||||
</head>
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Radios</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="outer-content">
|
||||
<ion-radio-group>
|
||||
<ion-list>
|
||||
<ion-list-header>
|
||||
Fruits
|
||||
</ion-list-header>
|
||||
<ion-item>
|
||||
<ion-label>Apple</ion-label>
|
||||
<ion-radio slot="start" name="apple" checked></ion-radio>
|
||||
<ion-icon slot="end" name="ionic"></ion-icon>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Banana</ion-label>
|
||||
<ion-radio slot="start" name="banana" checked></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Cherry</ion-label>
|
||||
<ion-radio slot="start" color="danger" name="cherry"></ion-radio>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-radio-group>
|
||||
|
||||
<ion-list>
|
||||
<ion-list-header>
|
||||
Mo Fruits
|
||||
</ion-list-header>
|
||||
<ion-item>
|
||||
<ion-label>Grape, checked, disabled</ion-label>
|
||||
<ion-radio slot="start" id="grapeChecked" name="grape" checked disabled></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Kiwi, (ionChange) Secondary color</ion-label>
|
||||
<ion-radio slot="start" color="secondary" (ionChange)="kiwiChange($event)"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Strawberry, (ionChange) checked="true"</ion-label>
|
||||
<ion-radio slot="start" color="light" (ionChange)="strawberryChange($event)" checked="true"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Checkbox right, checked, really long text that should ellipsis</ion-label>
|
||||
<ion-radio slot="end" color="danger" checked></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Checkbox right side</ion-label>
|
||||
<ion-radio slot="end" checked></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Button w/ right side default icon, really long text that should ellipsis</ion-label>
|
||||
<ion-icon name="information-circle" slot="end"></ion-icon>
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
|
||||
|
||||
<p aria-hidden="true" text-center>
|
||||
<ion-button onClick="toggleBoolean('grapeChecked', 'checked')" outline small>Grape Checked</ion-button>
|
||||
<ion-button onClick="toggleBoolean('grapeChecked', 'disabled')" outline small>Grape Disabled</ion-button>
|
||||
<ion-button onClick="printForm()" outline small>Print Form</ion-button>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<ion-radio id="standAloneChecked"></ion-radio>
|
||||
Stand-alone checkbox: <span id="standAloneCheckedSpan"></span>
|
||||
</p>
|
||||
|
||||
<p aria-hidden="true" padding>
|
||||
<code>appleCtrl.value: <span id="appleCtrlValue"></span></code><br>
|
||||
<code>bananaCtrl.value: <span id="bananaCtrlValue"></span></code><br>
|
||||
<code>cherry.value: <span id="cherryCtrlValue"></span></code><br>
|
||||
<code>grape.value: <span id="grapeCtrlValue"></span></code><br>
|
||||
<code>kiwiValue: <spa id="kiwiValue"></span></code><br>
|
||||
<code>strawberryValue: <span id="strawberryValue">y</span></code><br>
|
||||
</p>
|
||||
|
||||
<pre aria-hidden="true" padding id="formResults"></pre>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Checkbox / Toggle</ion-label>
|
||||
<ion-radio slot="start" id="checked"></ion-radio>
|
||||
<ion-toggle id="checked"></ion-toggle>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>Checked</ion-label>
|
||||
<ion-radio slot="start" checked></ion-radio>
|
||||
<ion-toggle checked></ion-toggle>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>Disabled</ion-label>
|
||||
<ion-radio slot="start" disabled></ion-radio>
|
||||
<ion-toggle disabled></ion-toggle>
|
||||
</ion-item>
|
||||
|
||||
</ion-content>
|
||||
|
||||
<script>
|
||||
function printForm(ev) {
|
||||
console.log('TODO get working with forms');
|
||||
}
|
||||
|
||||
function toggleBoolean(id, prop) {
|
||||
var ele = document.getElementById(id);
|
||||
|
||||
var isTrue = ele[prop] ? false : true;
|
||||
ele[prop] = isTrue;
|
||||
console.log('in toggleBoolean, setting', prop, 'to', isTrue);
|
||||
}
|
||||
</script>
|
||||
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
@ -18,6 +18,25 @@ export function isFunction(v: any): v is (Function) { return typeof v === 'funct
|
||||
|
||||
export function isStringOrNumber(v: any): v is (string | number) { return isString(v) || isNumber(v); }
|
||||
|
||||
/** @hidden */
|
||||
export function isCheckedProperty(a: any, b: any): boolean {
|
||||
if (a === undefined || a === null || a === '') {
|
||||
return (b === undefined || b === null || b === '');
|
||||
|
||||
} else if (a === true || a === 'true') {
|
||||
return (b === true || b === 'true');
|
||||
|
||||
} else if (a === false || a === 'false') {
|
||||
return (b === false || b === 'false');
|
||||
|
||||
} else if (a === 0 || a === '0') {
|
||||
return (b === 0 || b === '0');
|
||||
}
|
||||
|
||||
// not using strict comparison on purpose
|
||||
return (a == b); // tslint:disable-line
|
||||
};
|
||||
|
||||
export function assert(bool: boolean, msg: string) {
|
||||
if (!bool) {
|
||||
console.error(msg);
|
||||
|
||||
Reference in New Issue
Block a user