1 | /*
|
2 | Copyright 2013-2015 ASIAL CORPORATION
|
3 | Licensed under the Apache License, Version 2.0 (the "License");
|
4 | you may not use this file except in compliance with the License.
|
5 | You may obtain a copy of the License at
|
6 | http://www.apache.org/licenses/LICENSE-2.0
|
7 | Unless required by applicable law or agreed to in writing, software
|
8 | distributed under the License is distributed on an "AS IS" BASIS,
|
9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10 | See the License for the specific language governing permissions and
|
11 | limitations under the License.
|
12 | */
|
13 |
|
14 | import onsElements from '../ons/elements.js';
|
15 | import util from '../ons/util.js';
|
16 | import styler from '../ons/styler.js';
|
17 | import autoStyle from '../ons/autostyle.js';
|
18 | import ModifierUtil from '../ons/internal/modifier-util.js';
|
19 | import BaseElement from './base/base-element.js';
|
20 | import contentReady from '../ons/content-ready.js';
|
21 |
|
22 | const defaultClassName = 'fab';
|
23 |
|
24 | const 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 | */
|
48 | export 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 |
|
263 | util.defineBooleanProperties(FabElement, ['disabled', 'ripple']);
|
264 |
|
265 | onsElements.Fab = FabElement;
|
266 | customElements.define('ons-fab', FabElement);
|