summaryrefslogtreecommitdiff
path: root/chromium/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_behavior.js
blob: 20105f0117a18cc8d0a4b0433590cf40c5197512 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview Behavior for cr-radio-button-like elements.
 */

// clang-format off
import {PaperRippleBehavior} from 'chrome://resources/polymer/v3_0/paper-behaviors/paper-ripple-behavior.js';
// clang-format on

/** @polymerBehavior */
const CrRadioButtonBehaviorImpl = {
  properties: {
    checked: {
      type: Boolean,
      value: false,
      reflectToAttribute: true,
    },

    disabled: {
      type: Boolean,
      value: false,
      reflectToAttribute: true,
      notify: true,
    },

    /**
     * Whether the radio button should be focusable or not. Toggling this
     * property sets the corresponding tabindex of the button itself as well
     * as any links in the button description.
     */
    focusable: {
      type: Boolean,
      value: false,
      observer: 'onFocusableChanged_',
    },

    label: {
      type: String,
      value: '',  // Allows the hidden$= binding to run without being set.
    },

    name: {
      type: String,
      notify: true,
      reflectToAttribute: true,
    },

    /**
     * Holds the tabIndex for the radio button.
     * @private {number}
     */
    buttonTabIndex_: {
      type: Number,
      computed: 'getTabIndex_(focusable)',
    },
  },

  listeners: {
    blur: 'hideRipple_',
    focus: 'onFocus_',
    up: 'hideRipple_',
  },

  focus() {
    this.$.button.focus();
  },

  /** @private */
  onFocusableChanged_() {
    const links = this.querySelectorAll('a');
    links.forEach((link) => {
      // Remove the tab stop on any links when the row is unchecked. Since the
      // row is not tabbable, any links within the row should not be either.
      link.tabIndex = this.checked ? 0 : -1;
    });
  },

  /** @private */
  onFocus_() {
    this.getRipple().showAndHoldDown();
  },

  /** @private */
  hideRipple_() {
    this.getRipple().clear();
  },

  /**
   * @return {string}
   * @private
   */
  getAriaChecked_() {
    return this.checked ? 'true' : 'false';
  },

  /**
   * @return {string}
   * @private
   */
  getAriaDisabled_() {
    return this.disabled ? 'true' : 'false';
  },

  /**
   * @return {number}
   * @private
   */
  getTabIndex_() {
    return this.focusable ? 0 : -1;
  },

  /**
   * When shift-tab is pressed, first bring the focus to the host element.
   * This accomplishes 2 things:
   * 1) Host doesn't get focused when the browser moves the focus backward.
   * 2) focus now escaped the shadow-dom of this element, so that it'll
   *    correctly obey non-zero tabindex ordering of the containing document.
   * @param {!Event} e
   * @private
   */
  onInputKeydown_(e) {
    if (e.shiftKey && e.key === 'Tab') {
      this.focus();
    }
  },

  // customize the element's ripple
  _createRipple() {
    this._rippleContainer = this.$$('.disc-wrapper');
    const ripple = PaperRippleBehavior._createRipple();
    ripple.id = 'ink';
    ripple.setAttribute('recenters', '');
    ripple.classList.add('circle', 'toggle-ink');
    return ripple;
  },
};


/** @polymerBehavior */
export const CrRadioButtonBehavior = [
  PaperRippleBehavior,
  CrRadioButtonBehaviorImpl,
];

/** @interface */
export class CrRadioButtonBehaviorInterface {
  constructor() {
    /** @type {boolean} */
    this.checked;

    /** @type {boolean} */
    this.disabled;
  }

  /** @return  {!PaperRippleElement} */
  getRipple() {}
}