No Description

ons-speed-dial.js 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. import _setImmediate from 'babel-runtime/core-js/set-immediate';
  2. import _Promise from 'babel-runtime/core-js/promise';
  3. import _Object$getPrototypeOf from 'babel-runtime/core-js/object/get-prototype-of';
  4. import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
  5. import _createClass from 'babel-runtime/helpers/createClass';
  6. import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
  7. import _inherits from 'babel-runtime/helpers/inherits';
  8. /*
  9. Copyright 2013-2015 ASIAL CORPORATION
  10. Licensed under the Apache License, Version 2.0 (the "License");
  11. you may not use this file except in compliance with the License.
  12. You may obtain a copy of the License at
  13. http://www.apache.org/licenses/LICENSE-2.0
  14. Unless required by applicable law or agreed to in writing, software
  15. distributed under the License is distributed on an "AS IS" BASIS,
  16. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. See the License for the specific language governing permissions and
  18. limitations under the License.
  19. */
  20. import onsElements from '../ons/elements';
  21. import util from '../ons/util';
  22. import autoStyle from '../ons/autostyle';
  23. import ModifierUtil from '../ons/internal/modifier-util';
  24. import BaseElement from './base/base-element';
  25. import contentReady from '../ons/content-ready';
  26. import styler from '../ons/styler';
  27. var defaultClassName = 'speed-dial';
  28. var scheme = {
  29. '': 'speed-dial--*'
  30. };
  31. /**
  32. * @element ons-speed-dial
  33. * @category control
  34. * @description
  35. * [en]
  36. * Element that displays a Material Design Speed Dialog component. It is useful when there are more than one primary action that can be performed in a page.
  37. *
  38. * The Speed dial looks like a `<ons-fab>` element but will expand a menu when tapped.
  39. * [/en]
  40. * [ja][/ja]
  41. * @codepen dYQYLg
  42. * @tutorial vanilla/Reference/speed-dial
  43. * @seealso ons-speed-dial-item
  44. * [en]The `<ons-speed-dial-item>` represents a menu item.[/en]
  45. * [ja]ons-speed-dial-itemコンポーネント[/ja]
  46. * @seealso ons-fab
  47. * [en]ons-fab component[/en]
  48. * [ja]ons-fabコンポーネント[/ja]
  49. * @example
  50. * <ons-speed-dial position="left bottom">
  51. * <ons-fab>
  52. * <ons-icon icon="fa-twitter"></ons-icon>
  53. * </ons-fab>
  54. * <ons-speed-dial-item>A</ons-speed-dial-item>
  55. * <ons-speed-dial-item>B</ons-speed-dial-item>
  56. * <ons-speed-dial-item>C</ons-speed-dial-item>
  57. * </ons-speed-dial>
  58. */
  59. var SpeedDialElement = function (_BaseElement) {
  60. _inherits(SpeedDialElement, _BaseElement);
  61. /**
  62. * @event open
  63. * @description
  64. * [en]Fired when the menu items are shown.[/en]
  65. * [ja][/ja]
  66. */
  67. /**
  68. * @event close
  69. * @description
  70. * [en]Fired when the menu items are hidden.[/en]
  71. * [ja][/ja]
  72. */
  73. /**
  74. * @attribute modifier
  75. * @type {String}
  76. * @description
  77. * [en]The appearance of the component.[/en]
  78. * [ja]このコンポーネントの表現を指定します。[/ja]
  79. */
  80. /**
  81. * @attribute ripple
  82. * @description
  83. * [en]If this attribute is defined, the button will have a ripple effect when tapped.[/en]
  84. * [ja][/ja]
  85. */
  86. /**
  87. * @attribute position
  88. * @type {String}
  89. * @description
  90. * [en]
  91. * Specify the vertical and horizontal position of the component.
  92. * I.e. to display it in the top right corner specify "right top".
  93. * Choose from "right", "left", "top" and "bottom".
  94. * [/en]
  95. * [ja]
  96. * この要素を表示する左右と上下の位置を指定します。
  97. * 例えば、右上に表示する場合には"right top"を指定します。
  98. * 左右と上下の位置の指定には、rightとleft、topとbottomがそれぞれ指定できます。
  99. * [/ja]
  100. */
  101. /**
  102. * @attribute direction
  103. * @type {String}
  104. * @description
  105. * [en]Specify the direction the items are displayed. Possible values are "up", "down", "left" and "right".[/en]
  106. * [ja]
  107. * 要素が表示する方向を指定します。up, down, left, rightが指定できます。
  108. * [/ja]
  109. */
  110. /**
  111. * @attribute disabled
  112. * @description
  113. * [en]Specify if button should be disabled.[/en]
  114. * [ja]無効化する場合に指定します。[/ja]
  115. */
  116. function SpeedDialElement() {
  117. _classCallCheck(this, SpeedDialElement);
  118. var _this = _possibleConstructorReturn(this, (SpeedDialElement.__proto__ || _Object$getPrototypeOf(SpeedDialElement)).call(this));
  119. contentReady(_this, function () {
  120. _this._compile();
  121. });
  122. _this._itemShown = false;
  123. _this._boundOnClick = _this._onClick.bind(_this);
  124. return _this;
  125. }
  126. _createClass(SpeedDialElement, [{
  127. key: '_compile',
  128. value: function _compile() {
  129. this.classList.add(defaultClassName);
  130. autoStyle.prepare(this);
  131. this._updateRipple();
  132. ModifierUtil.initModifier(this, scheme);
  133. if (this.hasAttribute('direction')) {
  134. this._updateDirection(this.getAttribute('direction'));
  135. } else {
  136. this._updateDirection('up');
  137. }
  138. this._updatePosition();
  139. }
  140. }, {
  141. key: 'attributeChangedCallback',
  142. value: function attributeChangedCallback(name, last, current) {
  143. var _this2 = this;
  144. switch (name) {
  145. case 'class':
  146. util.restoreClass(this, defaultClassName, scheme);
  147. break;
  148. case 'modifier':
  149. ModifierUtil.onModifierChanged(last, current, this, scheme);
  150. break;
  151. case 'ripple':
  152. contentReady(this, function () {
  153. return _this2._updateRipple();
  154. });
  155. break;
  156. case 'direction':
  157. contentReady(this, function () {
  158. return _this2._updateDirection(current);
  159. });
  160. break;
  161. case 'position':
  162. contentReady(this, function () {
  163. return _this2._updatePosition();
  164. });
  165. break;
  166. }
  167. }
  168. }, {
  169. key: 'connectedCallback',
  170. value: function connectedCallback() {
  171. this.addEventListener('click', this._boundOnClick, false);
  172. }
  173. }, {
  174. key: 'disconnectedCallback',
  175. value: function disconnectedCallback() {
  176. this.removeEventListener('click', this._boundOnClick, false);
  177. }
  178. }, {
  179. key: '_onClick',
  180. value: function _onClick(e) {
  181. if (this.onClick) {
  182. this.onClick.apply(this);
  183. return _Promise.resolve();
  184. } else if (!this.disabled && this.visible) {
  185. return this.toggleItems();
  186. }
  187. }
  188. }, {
  189. key: '_show',
  190. value: function _show() {
  191. if (!this.inline) {
  192. return this.show();
  193. }
  194. return _Promise.resolve();
  195. }
  196. }, {
  197. key: '_hide',
  198. value: function _hide() {
  199. var _this3 = this;
  200. return new _Promise(function (resolve) {
  201. if (!_this3.inline) {
  202. _setImmediate(function () {
  203. return _this3.hide().then(resolve);
  204. });
  205. } else {
  206. resolve();
  207. }
  208. });
  209. }
  210. }, {
  211. key: '_updateRipple',
  212. value: function _updateRipple() {
  213. if (this._fab) {
  214. this.hasAttribute('ripple') ? this._fab.setAttribute('ripple', '') : this._fab.removeAttribute('ripple');
  215. }
  216. }
  217. }, {
  218. key: '_updateDirection',
  219. value: function _updateDirection(direction) {
  220. var children = this.items;
  221. for (var i = 0; i < children.length; i++) {
  222. styler(children[i], {
  223. transitionDelay: 25 * i + 'ms',
  224. bottom: 'auto',
  225. right: 'auto',
  226. top: 'auto',
  227. left: 'auto'
  228. });
  229. }
  230. switch (direction) {
  231. case 'up':
  232. for (var _i = 0; _i < children.length; _i++) {
  233. children[_i].style.bottom = 72 + 56 * _i + 'px';
  234. children[_i].style.right = '8px';
  235. }
  236. break;
  237. case 'down':
  238. for (var _i2 = 0; _i2 < children.length; _i2++) {
  239. children[_i2].style.top = 72 + 56 * _i2 + 'px';
  240. children[_i2].style.left = '8px';
  241. }
  242. break;
  243. case 'left':
  244. for (var _i3 = 0; _i3 < children.length; _i3++) {
  245. children[_i3].style.top = '8px';
  246. children[_i3].style.right = 72 + 56 * _i3 + 'px';
  247. }
  248. break;
  249. case 'right':
  250. for (var _i4 = 0; _i4 < children.length; _i4++) {
  251. children[_i4].style.top = '8px';
  252. children[_i4].style.left = 72 + 56 * _i4 + 'px';
  253. }
  254. break;
  255. default:
  256. util.throw('Argument must be one of up, down, left or right.');
  257. }
  258. }
  259. }, {
  260. key: '_updatePosition',
  261. value: function _updatePosition() {
  262. var position = this.getAttribute('position');
  263. this.classList.remove('fab--top__left', 'fab--bottom__right', 'fab--bottom__left', 'fab--top__right', 'fab--top__center', 'fab--bottom__center');
  264. switch (position) {
  265. case 'top right':
  266. case 'right top':
  267. this.classList.add('fab--top__right');
  268. break;
  269. case 'top left':
  270. case 'left top':
  271. this.classList.add('fab--top__left');
  272. break;
  273. case 'bottom right':
  274. case 'right bottom':
  275. this.classList.add('fab--bottom__right');
  276. break;
  277. case 'bottom left':
  278. case 'left bottom':
  279. this.classList.add('fab--bottom__left');
  280. break;
  281. case 'center top':
  282. case 'top center':
  283. this.classList.add('fab--top__center');
  284. break;
  285. case 'center bottom':
  286. case 'bottom center':
  287. this.classList.add('fab--bottom__center');
  288. break;
  289. default:
  290. break;
  291. }
  292. }
  293. }, {
  294. key: '_getTranslate',
  295. value: function _getTranslate() {
  296. var isBottom = (this.getAttribute('position') || '').indexOf('bottom') >= 0;
  297. var translate = isBottom ? 'translate3d(0px, -' + (util.globals.fabOffset || 0) + 'px, 0px) ' : '';
  298. return translate;
  299. }
  300. /**
  301. * @method show
  302. * @signature show()
  303. * @description
  304. * [en]Show the speed dial.[/en]
  305. * [ja]Speed dialを表示します。[/ja]
  306. */
  307. }, {
  308. key: 'show',
  309. value: function show() {
  310. this._fab.show();
  311. styler(this, { transform: this._getTranslate });
  312. return _Promise.resolve();
  313. }
  314. /**
  315. * @method hide
  316. * @signature hide()
  317. * @description
  318. * [en]Hide the speed dial.[/en]
  319. * [ja]Speed dialを非表示にします。[/ja]
  320. */
  321. }, {
  322. key: 'hide',
  323. value: function hide() {
  324. var _this4 = this;
  325. return this.hideItems().then(function () {
  326. return _this4._fab.hide();
  327. });
  328. }
  329. /**
  330. * @method showItems
  331. * @signature showItems()
  332. * @description
  333. * [en]Show the speed dial items.[/en]
  334. * [ja]Speed dialの子要素を表示します。[/ja]
  335. */
  336. }, {
  337. key: 'showItems',
  338. value: function showItems() {
  339. if (this.hasAttribute('direction')) {
  340. this._updateDirection(this.getAttribute('direction'));
  341. } else {
  342. this._updateDirection('up');
  343. }
  344. var totalDelay = 0;
  345. if (!this._itemShown) {
  346. var children = this.items;
  347. for (var i = 0; i < children.length; i++) {
  348. var delay = 25 * i;
  349. totalDelay += delay;
  350. styler(children[i], {
  351. transform: 'scale(1)',
  352. transitionDelay: delay + 'ms'
  353. });
  354. }
  355. totalDelay += 50;
  356. this._itemShown = true;
  357. util.triggerElementEvent(this, 'open');
  358. }
  359. var deferred = util.defer();
  360. setTimeout(deferred.resolve, totalDelay);
  361. return deferred.promise;
  362. }
  363. /**
  364. * @method hideItems
  365. * @signature hideItems()
  366. * @description
  367. * [en]Hide the speed dial items.[/en]
  368. * [ja]Speed dialの子要素を非表示にします。[/ja]
  369. */
  370. }, {
  371. key: 'hideItems',
  372. value: function hideItems() {
  373. var totalDelay = 0;
  374. if (this._itemShown) {
  375. var children = this.items;
  376. for (var i = 0; i < children.length; i++) {
  377. var delay = 25 * (children.length - i);
  378. totalDelay += delay;
  379. styler(children[i], {
  380. transform: 'scale(0)',
  381. transitionDelay: delay + 'ms'
  382. });
  383. }
  384. totalDelay += 50;
  385. this._itemShown = false;
  386. util.triggerElementEvent(this, 'close');
  387. }
  388. var deferred = util.defer();
  389. setTimeout(deferred.resolve, totalDelay);
  390. return deferred.promise;
  391. }
  392. /**
  393. * @property disabled
  394. * @type {Boolean}
  395. * @description
  396. * [en]Whether the element is disabled or not.[/en]
  397. * [ja]無効化されている場合に`true`。[/ja]
  398. */
  399. }, {
  400. key: 'isOpen',
  401. /**
  402. * @method isOpen
  403. * @signature isOpen()
  404. * @description
  405. * [en]Returns whether the menu is open or not.[/en]
  406. * [ja][/ja]
  407. */
  408. value: function isOpen() {
  409. return this._itemShown;
  410. }
  411. /**
  412. * @method toggle
  413. * @signature toggle()
  414. * @description
  415. * [en]Toggle visibility.[/en]
  416. * [ja]Speed dialの表示非表示を切り替えます。[/ja]
  417. */
  418. }, {
  419. key: 'toggle',
  420. value: function toggle() {
  421. return this.visible ? this.hide() : this.show();
  422. }
  423. /**
  424. * @method toggleItems
  425. * @signature toggleItems()
  426. * @description
  427. * [en]Toggle item visibility.[/en]
  428. * [ja]Speed dialの子要素の表示非表示を切り替えます。[/ja]
  429. */
  430. }, {
  431. key: 'toggleItems',
  432. value: function toggleItems() {
  433. return this.isOpen() ? this.hideItems() : this.showItems();
  434. }
  435. }, {
  436. key: 'items',
  437. get: function get() {
  438. return util.arrayFrom(this.querySelectorAll('ons-speed-dial-item'));
  439. }
  440. }, {
  441. key: '_fab',
  442. get: function get() {
  443. return util.findChild(this, 'ons-fab');
  444. }
  445. }, {
  446. key: 'disabled',
  447. set: function set(value) {
  448. if (value) {
  449. this.hideItems();
  450. }
  451. util.arrayFrom(this.children).forEach(function (e) {
  452. util.match(e, '.fab') && util.toggleAttribute(e, 'disabled', value);
  453. });
  454. return util.toggleAttribute(this, 'disabled', value);
  455. },
  456. get: function get() {
  457. return this.hasAttribute('disabled');
  458. }
  459. /**
  460. * @property inline
  461. * @readonly
  462. * @type {Boolean}
  463. * @description
  464. * [en]Whether the element is inline or not.[/en]
  465. * [ja]インライン要素の場合に`true`。[/ja]
  466. */
  467. }, {
  468. key: 'inline',
  469. get: function get() {
  470. return this.hasAttribute('inline');
  471. }
  472. /**
  473. * @property visible
  474. * @readonly
  475. * @type {Boolean}
  476. * @description
  477. * [en]Whether the element is visible or not.[/en]
  478. * [ja]要素が見える場合に`true`。[/ja]
  479. */
  480. }, {
  481. key: 'visible',
  482. get: function get() {
  483. return this._fab.visible && this.style.display !== 'none';
  484. }
  485. }], [{
  486. key: 'observedAttributes',
  487. get: function get() {
  488. return ['class', 'modifier', 'ripple', 'direction', 'position'];
  489. }
  490. }, {
  491. key: 'events',
  492. get: function get() {
  493. return ['open', 'close'];
  494. }
  495. }]);
  496. return SpeedDialElement;
  497. }(BaseElement);
  498. export default SpeedDialElement;
  499. onsElements.SpeedDial = SpeedDialElement;
  500. customElements.define('ons-speed-dial', SpeedDialElement);