1<!-- 2@license 3Copyright (c) 2016 The Polymer Project Authors. All rights reserved. 4This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 5The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 6The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 7Code distributed by Google as part of the polymer project is also 8subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 9--> 10 11<link rel="import" href="../polymer/polymer.html"> 12<link rel="import" href="../paper-styles/default-theme.html"> 13<link rel="import" href="../paper-behaviors/paper-checked-element-behavior.html"> 14 15<!-- 16Material design: [Checkbox](https://www.google.com/design/spec/components/selection-controls.html#selection-controls-checkbox) 17 18`paper-checkbox` is a button that can be either checked or unchecked. User 19can tap the checkbox to check or uncheck it. Usually you use checkboxes 20to allow user to select multiple options from a set. If you have a single 21ON/OFF option, avoid using a single checkbox and use `paper-toggle-button` 22instead. 23 24Example: 25 26 <paper-checkbox>label</paper-checkbox> 27 28 <paper-checkbox checked> label</paper-checkbox> 29 30### Styling 31 32The following custom properties and mixins are available for styling: 33 34Custom property | Description | Default 35----------------|-------------|---------- 36`--paper-checkbox-unchecked-background-color` | Checkbox background color when the input is not checked | `transparent` 37`--paper-checkbox-unchecked-color` | Checkbox border color when the input is not checked | `--primary-text-color` 38`--paper-checkbox-unchecked-ink-color` | Selected/focus ripple color when the input is not checked | `--primary-text-color` 39`--paper-checkbox-checked-color` | Checkbox color when the input is checked | `--primary-color` 40`--paper-checkbox-checked-ink-color` | Selected/focus ripple color when the input is checked | `--primary-color` 41`--paper-checkbox-checkmark-color` | Checkmark color | `white` 42`--paper-checkbox-label-color` | Label color | `--primary-text-color` 43`--paper-checkbox-label-checked-color` | Label color when the input is checked | `--paper-checkbox-label-color` 44`--paper-checkbox-label-spacing` | Spacing between the label and the checkbox | `8px` 45`--paper-checkbox-label` | Mixin applied to the label | `{}` 46`--paper-checkbox-label-checked` | Mixin applied to the label when the input is checked | `{}` 47`--paper-checkbox-error-color` | Checkbox color when invalid | `--error-color` 48`--paper-checkbox-size` | Size of the checkbox | `18px` 49`--paper-checkbox-ink-size` | Size of the ripple | `48px` 50`--paper-checkbox-margin` | Margin around the checkbox container | `initial` 51`--paper-checkbox-vertical-align` | Vertical alignment of the checkbox container | `middle` 52 53This element applies the mixin `--paper-font-common-base` but does not import `paper-styles/typography.html`. 54In order to apply the `Roboto` font to this element, make sure you've imported `paper-styles/typography.html`. 55 56@demo demo/index.html 57--> 58 59<dom-module id="paper-checkbox"> 60 <template strip-whitespace> 61 <style> 62 :host { 63 display: inline-block; 64 white-space: nowrap; 65 cursor: pointer; 66 --calculated-paper-checkbox-size: var(--paper-checkbox-size, 18px); 67 /* -1px is a sentinel for the default and is replaced in `attached`. */ 68 --calculated-paper-checkbox-ink-size: var(--paper-checkbox-ink-size, -1px); 69 @apply(--paper-font-common-base); 70 line-height: 0; 71 -webkit-tap-highlight-color: transparent; 72 } 73 74 :host([hidden]) { 75 display: none !important; 76 } 77 78 :host(:focus) { 79 outline: none; 80 } 81 82 .hidden { 83 display: none; 84 } 85 86 #checkboxContainer { 87 display: inline-block; 88 position: relative; 89 width: var(--calculated-paper-checkbox-size); 90 height: var(--calculated-paper-checkbox-size); 91 min-width: var(--calculated-paper-checkbox-size); 92 margin: var(--paper-checkbox-margin, initial); 93 vertical-align: var(--paper-checkbox-vertical-align, middle); 94 background-color: var(--paper-checkbox-unchecked-background-color, transparent); 95 } 96 97 #ink { 98 position: absolute; 99 100 /* Center the ripple in the checkbox by negative offsetting it by 101 * (inkWidth - rippleWidth) / 2 */ 102 top: calc(0px - (var(--calculated-paper-checkbox-ink-size) - var(--calculated-paper-checkbox-size)) / 2); 103 left: calc(0px - (var(--calculated-paper-checkbox-ink-size) - var(--calculated-paper-checkbox-size)) / 2); 104 width: var(--calculated-paper-checkbox-ink-size); 105 height: var(--calculated-paper-checkbox-ink-size); 106 color: var(--paper-checkbox-unchecked-ink-color, var(--primary-text-color)); 107 opacity: 0.6; 108 pointer-events: none; 109 } 110 111 :host-context([dir="rtl"]) #ink { 112 right: calc(0px - (var(--calculated-paper-checkbox-ink-size) - var(--calculated-paper-checkbox-size)) / 2); 113 left: auto; 114 } 115 116 #ink[checked] { 117 color: var(--paper-checkbox-checked-ink-color, var(--primary-color)); 118 } 119 120 #checkbox { 121 position: relative; 122 box-sizing: border-box; 123 height: 100%; 124 border: solid 2px; 125 border-color: var(--paper-checkbox-unchecked-color, var(--primary-text-color)); 126 border-radius: 2px; 127 pointer-events: none; 128 -webkit-transition: background-color 140ms, border-color 140ms; 129 transition: background-color 140ms, border-color 140ms; 130 } 131 132 /* checkbox checked animations */ 133 #checkbox.checked #checkmark { 134 -webkit-animation: checkmark-expand 140ms ease-out forwards; 135 animation: checkmark-expand 140ms ease-out forwards; 136 } 137 138 @-webkit-keyframes checkmark-expand { 139 0% { 140 -webkit-transform: scale(0, 0) rotate(45deg); 141 } 142 100% { 143 -webkit-transform: scale(1, 1) rotate(45deg); 144 } 145 } 146 147 @keyframes checkmark-expand { 148 0% { 149 transform: scale(0, 0) rotate(45deg); 150 } 151 100% { 152 transform: scale(1, 1) rotate(45deg); 153 } 154 } 155 156 #checkbox.checked { 157 background-color: var(--paper-checkbox-checked-color, var(--primary-color)); 158 border-color: var(--paper-checkbox-checked-color, var(--primary-color)); 159 } 160 161 #checkmark { 162 position: absolute; 163 width: 36%; 164 height: 70%; 165 border-style: solid; 166 border-top: none; 167 border-left: none; 168 border-right-width: calc(2/15 * var(--calculated-paper-checkbox-size)); 169 border-bottom-width: calc(2/15 * var(--calculated-paper-checkbox-size)); 170 border-color: var(--paper-checkbox-checkmark-color, white); 171 -webkit-transform-origin: 97% 86%; 172 transform-origin: 97% 86%; 173 box-sizing: content-box; /* protect against page-level box-sizing */ 174 } 175 176 :host-context([dir="rtl"]) #checkmark { 177 -webkit-transform-origin: 50% 14%; 178 transform-origin: 50% 14%; 179 } 180 181 /* label */ 182 #checkboxLabel { 183 position: relative; 184 display: inline-block; 185 vertical-align: middle; 186 padding-left: var(--paper-checkbox-label-spacing, 8px); 187 white-space: normal; 188 line-height: normal; 189 color: var(--paper-checkbox-label-color, var(--primary-text-color)); 190 @apply(--paper-checkbox-label); 191 } 192 193 :host([checked]) #checkboxLabel { 194 color: var(--paper-checkbox-label-checked-color, var(--paper-checkbox-label-color, var(--primary-text-color))); 195 @apply(--paper-checkbox-label-checked); 196 } 197 198 :host-context([dir="rtl"]) #checkboxLabel { 199 padding-right: var(--paper-checkbox-label-spacing, 8px); 200 padding-left: 0; 201 } 202 203 #checkboxLabel[hidden] { 204 display: none; 205 } 206 207 /* disabled state */ 208 209 :host([disabled]) #checkbox { 210 opacity: 0.5; 211 border-color: var(--paper-checkbox-unchecked-color, var(--primary-text-color)); 212 } 213 214 :host([disabled][checked]) #checkbox { 215 background-color: var(--paper-checkbox-unchecked-color, var(--primary-text-color)); 216 opacity: 0.5; 217 } 218 219 :host([disabled]) #checkboxLabel { 220 opacity: 0.65; 221 } 222 223 /* invalid state */ 224 #checkbox.invalid:not(.checked) { 225 border-color: var(--paper-checkbox-error-color, var(--error-color)); 226 } 227 </style> 228 229 <div id="checkboxContainer"> 230 <div id="checkbox" class$="[[_computeCheckboxClass(checked, invalid)]]"> 231 <div id="checkmark" class$="[[_computeCheckmarkClass(checked)]]"></div> 232 </div> 233 </div> 234 235 <div id="checkboxLabel"><content></content></div> 236 </template> 237 238</dom-module> 239<script> 240Polymer({ 241 is: 'paper-checkbox', 242 243 behaviors: [ 244 Polymer.PaperCheckedElementBehavior 245 ], 246 247 hostAttributes: { 248 role: 'checkbox', 249 'aria-checked': false, 250 tabindex: 0 251 }, 252 253 properties: { 254 /** 255 * Fired when the checked state changes due to user interaction. 256 * 257 * @event change 258 */ 259 260 /** 261 * Fired when the checked state changes. 262 * 263 * @event iron-change 264 */ 265 ariaActiveAttribute: { 266 type: String, 267 value: 'aria-checked' 268 } 269 }, 270 271 attached: function() { 272 var inkSize = this.getComputedStyleValue('--calculated-paper-checkbox-ink-size').trim(); 273 // If unset, compute and set the default `--paper-checkbox-ink-size`. 274 if (inkSize === '-1px') { 275 var checkboxSize = parseFloat(this.getComputedStyleValue('--calculated-paper-checkbox-size').trim()); 276 var defaultInkSize = Math.floor((8 / 3) * checkboxSize); 277 278 // The checkbox and ripple need to have the same parity so that their 279 // centers align. 280 if (defaultInkSize % 2 !== checkboxSize % 2) { 281 defaultInkSize++; 282 } 283 284 this.customStyle['--paper-checkbox-ink-size'] = defaultInkSize + 'px'; 285 this.updateStyles(); 286 } 287 }, 288 289 _computeCheckboxClass: function(checked, invalid) { 290 var className = ''; 291 if (checked) { 292 className += 'checked '; 293 } 294 if (invalid) { 295 className += 'invalid'; 296 } 297 return className; 298 }, 299 300 _computeCheckmarkClass: function(checked) { 301 return checked ? '' : 'hidden'; 302 }, 303 304 // create ripple inside the checkboxContainer 305 _createRipple: function() { 306 this._rippleContainer = this.$.checkboxContainer; 307 return Polymer.PaperInkyFocusBehaviorImpl._createRipple.call(this); 308 } 309}); 310</script> 311