UNPKG

7.13 kBJavaScriptView Raw
1/*
2Copyright 2013-2015 ASIAL CORPORATION
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6 http://www.apache.org/licenses/LICENSE-2.0
7Unless required by applicable law or agreed to in writing, software
8distributed under the License is distributed on an "AS IS" BASIS,
9WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10See the License for the specific language governing permissions and
11limitations under the License.
12*/
13
14import onsElements from '../ons/elements.js';
15import util from '../ons/util.js';
16import styler from '../ons/styler.js';
17import autoStyle from '../ons/autostyle.js';
18import ModifierUtil from '../ons/internal/modifier-util.js';
19import BaseElement from './base/base-element.js';
20import contentReady from '../ons/content-ready.js';
21
22const defaultClassName = 'fab';
23
24const scheme = {
25 '': 'fab--*',
26 '.fab__icon': 'fab--*__icon'
27};
28
29/**
30 * @element ons-fab
31 * @category form
32 * @description
33 * [en]
34 * The Floating action button is a circular button defined in the [Material Design specification](https://www.google.com/design/spec/components/buttons-floating-action-button.html). They are often used to promote the primary action of the app.
35 *
36 * It can be displayed either as an inline element or in one of the corners. Normally it will be positioned in the lower right corner of the screen.
37 * [/en]
38 * [ja][/ja]
39 * @tutorial vanilla/Reference/fab
40 * @modifier mini
41 * [en]Makes the `ons-fab` smaller.[/en]
42 * [ja][/ja]
43 * @guide theming.html#cross-platform-styling-autostyling [en]Information about cross platform styling[/en][ja]Information about cross platform styling[/ja]
44 * @seealso ons-speed-dial
45 * [en]The `<ons-speed-dial>` component is a Floating action button that displays a menu when tapped.[/en]
46 * [ja][/ja]
47 */
48export default class FabElement extends BaseElement {
49
50 /**
51 * @attribute modifier
52 * @type {String}
53 * @description
54 * [en]The appearance of the button.[/en]
55 * [ja]ボタンの表現を指定します。[/ja]
56 */
57
58 /**
59 * @attribute ripple
60 * @description
61 * [en]If this attribute is defined, the button will have a ripple effect when tapped.[/en]
62 * [ja][/ja]
63 */
64
65 /**
66 * @property ripple
67 * @type {Boolean}
68 * @description
69 * [en]If this property is defined, the button will have a ripple effect when tapped.[/en]
70 * [ja][/ja]
71 */
72
73 /**
74 * @attribute position
75 * @type {String}
76 * @description
77 * [en]The position of the button. Should be a string like `"bottom right"` or `"top left"`. If this attribute is not defined it will be displayed as an inline element.[/en]
78 * [ja][/ja]
79 */
80
81 /**
82 * @attribute disabled
83 * @description
84 * [en]Specify if button should be disabled.[/en]
85 * [ja]ボタンを無効化する場合は指定します。[/ja]
86 */
87
88 constructor() {
89 super();
90
91 // The following statements can be executed before contentReady
92 // since these do not access the children
93 this._hide();
94 this.classList.add(defaultClassName);
95
96 contentReady(this, () => {
97 this._compile();
98 });
99 }
100
101 _compile() {
102 autoStyle.prepare(this);
103
104 if (!util.findChild(this, '.fab__icon')) {
105 const content = document.createElement('span');
106 content.classList.add('fab__icon');
107
108 util.arrayFrom(this.childNodes).forEach(element => {
109 if (!element.tagName || element.tagName.toLowerCase() !== 'ons-ripple') {
110 content.appendChild(element);
111 }
112 });
113 this.appendChild(content);
114 }
115
116 this._updateRipple();
117
118 ModifierUtil.initModifier(this, scheme);
119
120 this._updatePosition();
121 }
122
123 connectedCallback() {
124 setImmediate(() => this._show());
125 }
126
127 static get observedAttributes() {
128 return ['modifier', 'ripple', 'position', 'class'];
129 }
130
131 attributeChangedCallback(name, last, current) {
132 switch (name) {
133 case 'class':
134 util.restoreClass(this, defaultClassName, scheme);
135 break;
136 case 'modifier':
137 ModifierUtil.onModifierChanged(last, current, this, scheme);
138 break;
139 case 'ripple':
140 this._updateRipple();
141 break;
142 case 'position':
143 this._updatePosition();
144 break;
145 }
146 }
147
148 _show() {
149 if (!this._manuallyHidden) { // if user has not called ons-fab.hide()
150 this._toggle(true);
151 }
152 }
153
154 _hide() {
155 setImmediate(() => this._toggle(false));
156 }
157
158 _updateRipple() {
159 util.updateRipple(this);
160 }
161
162 _updatePosition() {
163 const position = this.getAttribute('position');
164 this.classList.remove(
165 'fab--top__left',
166 'fab--bottom__right',
167 'fab--bottom__left',
168 'fab--top__right',
169 'fab--top__center',
170 'fab--bottom__center');
171 switch (position) {
172 case 'top right':
173 case 'right top':
174 this.classList.add('fab--top__right');
175 break;
176 case 'top left':
177 case 'left top':
178 this.classList.add('fab--top__left');
179 break;
180 case 'bottom right':
181 case 'right bottom':
182 this.classList.add('fab--bottom__right');
183 break;
184 case 'bottom left':
185 case 'left bottom':
186 this.classList.add('fab--bottom__left');
187 break;
188 case 'center top':
189 case 'top center':
190 this.classList.add('fab--top__center');
191 break;
192 case 'center bottom':
193 case 'bottom center':
194 this.classList.add('fab--bottom__center');
195 break;
196 default:
197 break;
198 }
199 }
200
201 /**
202 * @method show
203 * @signature show()
204 * @description
205 * [en]Show the floating action button.[/en]
206 * [ja][/ja]
207 */
208 show() {
209 this.toggle(true);
210 }
211
212 /**
213 * @method hide
214 * @signature hide()
215 * @description
216 * [en]Hide the floating action button.[/en]
217 * [ja][/ja]
218 */
219 hide() {
220 this.toggle(false);
221 }
222
223 /**
224 * @method toggle
225 * @signature toggle()
226 * @description
227 * [en]Toggle the visibility of the button.[/en]
228 * [ja][/ja]
229 */
230 toggle(action = !this.visible) {
231 this._manuallyHidden = !action;
232 this._toggle(action);
233 }
234
235 _toggle(action = !this.visible) {
236 const isBottom = (this.getAttribute('position') || '').indexOf('bottom') >= 0;
237 const translate = isBottom ? `translate3d(0px, -${util.globals.fabOffset || 0}px, 0px)` : '';
238
239 styler(this, { transform: `${translate} scale(${Number(action)})` });
240 }
241
242 /**
243 * @property disabled
244 * @type {Boolean}
245 * @description
246 * [en]Whether the element is disabled or not.[/en]
247 * [ja]無効化されている場合に`true`。[/ja]
248 */
249
250 /**
251 * @property visible
252 * @readonly
253 * @type {Boolean}
254 * @description
255 * [en]Whether the element is visible or not.[/en]
256 * [ja]要素が見える場合に`true`。[/ja]
257 */
258 get visible() {
259 return this.style.transform.indexOf('scale(0)') === -1 && this.style.display !== 'none';
260 }
261}
262
263util.defineBooleanProperties(FabElement, ['disabled', 'ripple']);
264
265onsElements.Fab = FabElement;
266customElements.define('ons-fab', FabElement);