snackbar.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. // Generated by CoffeeScript 2.0.0-beta4
  2. (function() {
  3. // ------------------------------------------------------------------------
  4. // 變數與常數設置
  5. // ------------------------------------------------------------------------
  6. // 模組名稱。
  7. var Attribute, ClassName, EVENT_NAMESPACE, Error, Event, MODULE_NAMESPACE, Metadata, NAME, Selector, Settings, Template, Timer, duration;
  8. NAME = 'snackbar';
  9. // 模組事件鍵名。
  10. EVENT_NAMESPACE = `.${NAME}`;
  11. // 模組命名空間。
  12. MODULE_NAMESPACE = `module-${NAME}`;
  13. // 模組設定。
  14. Settings = {
  15. // 消音所有提示,甚至是錯誤訊息。
  16. silent: false,
  17. // 顯示除錯訊息。
  18. debug: true,
  19. // 監聽 DOM 結構異動並自動重整快取。
  20. observeChanges: true,
  21. // 主要內容文字。
  22. content: '',
  23. // 動作設置。
  24. action: {
  25. // 動作按鈕的文字。
  26. text: '',
  27. // 動作按鈕的語意。
  28. emphasis: ''
  29. },
  30. onVisible: () => {},
  31. onShow: () => {},
  32. onHide: () => {},
  33. onHidden: () => {},
  34. // 當點心條關閉時所會呼叫的回呼函式。
  35. onClose: () => {},
  36. // 當點心條因為放置而自動關閉時所會呼叫的回呼函式。
  37. onIgnore: () => {},
  38. // 當動作按鈕被按下時所呼叫的回呼函式。
  39. onAction: () => {},
  40. // 點心條到自動消失所耗費的毫秒時間,如果設為 `0` 則表示永不自動消失。
  41. delay: 3500,
  42. // 點心條可否手動忽略,例如:滑開、點擊點心條關閉。
  43. // closable : true
  44. // 點心條出現的螢幕位置,如:`top left`、`top right`、`bottom left`、`bottom right`
  45. position: 'bottom left',
  46. // 點心條是否應該在滑鼠指向時,持續顯示避免自動消失。
  47. hoverStay: true
  48. };
  49. // 中繼資料。
  50. Metadata = {
  51. STAY: 'stay'
  52. };
  53. // 計時器。
  54. Timer = {
  55. AUTO_CLOSE: 'autoClose'
  56. };
  57. // 事件名稱。
  58. Event = {
  59. CLOSE: `close${EVENT_NAMESPACE}`,
  60. IGNORE: `open${EVENT_NAMESPACE}`,
  61. ACTION: `closing${EVENT_NAMESPACE}`,
  62. CLICK: `click${EVENT_NAMESPACE}`,
  63. MOUSEENTER: `mouseenter${EVENT_NAMESPACE}`,
  64. MOUSELEAVE: `mouseleave${EVENT_NAMESPACE}`,
  65. ANIMATIONEND: 'animationend'
  66. };
  67. // 標籤名稱。
  68. Attribute = {
  69. TEMPORARY: 'data-snackbar-temporary'
  70. };
  71. // 模板。
  72. Template = {
  73. SNACKBAR: "<div class=\"content\"></div>\n<a class=\"action href=\"#!\"></a>"
  74. };
  75. // 樣式名稱。
  76. ClassName = {
  77. SNACKBAR: 'ts snackbar',
  78. ACTIVE: 'active',
  79. ANIMATING: 'animating',
  80. PRIMARY: 'primary',
  81. INFO: 'info',
  82. WARNING: 'warning',
  83. POSITIVE: 'positive',
  84. NEGATIVE: 'negative',
  85. TOP: 'top',
  86. LEFT: 'left',
  87. BOTTOM: 'bottom',
  88. RIGHT: 'right'
  89. };
  90. // 選擇器名稱。
  91. Selector = {
  92. SNACKBAR: `.ts.snackbar[${Attribute.TEMPORARY}]`,
  93. ACTIVE_SNACKBAR: '.ts.active.snackbar',
  94. CONTENT: '.content',
  95. ACTION: '.action',
  96. BODY: 'body'
  97. };
  98. // 錯誤訊息。
  99. Error = {};
  100. // 過場動畫毫秒。
  101. duration = 300;
  102. // ------------------------------------------------------------------------
  103. // 模組註冊
  104. // ------------------------------------------------------------------------
  105. ts.register({NAME, MODULE_NAMESPACE, Error, Settings}, ({$allModules, $this, element, debug, settings}) => {
  106. var $action, $content, module;
  107. // ------------------------------------------------------------------------
  108. // 區域變數
  109. // ------------------------------------------------------------------------
  110. $action = ts();
  111. $content = ts();
  112. // ------------------------------------------------------------------------
  113. // 模組定義
  114. // ------------------------------------------------------------------------
  115. return module = {
  116. show: () => {
  117. var ref, ref1;
  118. module.reset.timer();
  119. module.reset.action();
  120. module.set.stay(false);
  121. module.repaint();
  122. module.animate.show(() => {
  123. return module.set.animating(false);
  124. });
  125. if (settings.content !== '') {
  126. module.set.content(settings.content);
  127. }
  128. if ((((ref = settings.action) != null ? ref.text : void 0) != null) && settings.action.text !== '') {
  129. module.set.action(settings.action.text, (ref1 = settings.action) != null ? ref1.emphasis : void 0);
  130. }
  131. if (settings.delay !== 0) {
  132. module.set.timer();
  133. }
  134. return $allModules;
  135. },
  136. hide: (type, callback) => {
  137. if (module.is.hidden()) {
  138. return;
  139. }
  140. module.reset.timer();
  141. switch (type) {
  142. case Event.ACTION:
  143. if (!module.trigger.action()) {
  144. return;
  145. }
  146. break;
  147. case Event.IGNORE:
  148. module.trigger.ignore();
  149. }
  150. module.animate.hide(() => {
  151. module.remove();
  152. module.trigger.close();
  153. if (callback != null) {
  154. return callback.call();
  155. }
  156. });
  157. return $allModules;
  158. },
  159. remove: () => {
  160. return $this.remove();
  161. },
  162. repaint: () => {
  163. return $this.repaint();
  164. },
  165. get: {
  166. $content: () => {
  167. return $this.find(Selector.CONTENT);
  168. },
  169. $action: () => {
  170. return $this.find(Selector.ACTION);
  171. }
  172. },
  173. trigger: {
  174. action: () => {
  175. return $this.trigger(Event.ACTION, element);
  176. },
  177. ignore: () => {
  178. return $this.trigger(Event.IGNORE, element);
  179. },
  180. close: () => {
  181. return $this.trigger(Event.CLOSE, element);
  182. }
  183. },
  184. reset: {
  185. timer: () => {
  186. return $this.removeTimer(Timer.AUTO_CLOSE);
  187. },
  188. action: () => {
  189. return $action.html('').removeClass(`${ClassName.PRIMARY} ${ClassName.INFO} ${ClassName.WARNING} ${ClassName.NEGATIVE} ${ClassName.POSITIVE}`);
  190. },
  191. position: () => {
  192. return $this.removeClass(`${ClassName.TOP} ${ClassName.LEFT} ${ClassName.BOTTOM} ${ClassName.RIGHT}`);
  193. }
  194. },
  195. animate: {
  196. show: (callback) => {
  197. return $this.addClass(`${ClassName.ACTIVE} ${ClassName.ANIMATING} ${settings.position}`).off(Event.ANIMATIONEND).one(Event.ANIMATIONEND, () => {
  198. if (callback != null) {
  199. return callback.call();
  200. }
  201. }).emulate(Event.ANIMATIONEND, duration);
  202. },
  203. hide: (callback) => {
  204. return $this.removeClass(ClassName.ACTIVE).addClass(ClassName.ANIMATING).one(Event.ANIMATIONEND, () => {
  205. if (callback != null) {
  206. return callback.call();
  207. }
  208. }).emulate(Event.ANIMATIONEND, duration);
  209. }
  210. },
  211. is: {
  212. visible: () => {
  213. return $this.hasClass(ClassName.ACTIVE);
  214. },
  215. hidden: () => {
  216. return !module.is.visible();
  217. }
  218. },
  219. should: {
  220. stay: () => {
  221. return $this.data(Metadata.STAY) === true && settings.hoverStay === true;
  222. }
  223. },
  224. set: {
  225. stay: (value) => {
  226. return $this.data(Metadata.STAY, value);
  227. },
  228. content: (content) => {
  229. return $content.html(content);
  230. },
  231. animating: (value) => {
  232. if (value) {
  233. return $this.addClass(ClassName.ANIMATING);
  234. } else {
  235. return $this.removeClass(ClassName.ANIMATING);
  236. }
  237. },
  238. action: (text, emphasis) => {
  239. $action.html(settings.action.text);
  240. if (emphasis) {
  241. return $action.addClass(settings.action.emphasis);
  242. }
  243. },
  244. timer: () => {
  245. var timer;
  246. timer = {
  247. name: Timer.AUTO_CLOSE,
  248. callback: () => {
  249. if (module.should.stay()) {
  250. debug('延長點心條的存在時間', element);
  251. return $this.setTimer(timer);
  252. } else {
  253. return module.hide(Event.IGNORE);
  254. }
  255. },
  256. interval: settings.delay,
  257. visible: true
  258. };
  259. return $this.setTimer(timer);
  260. }
  261. },
  262. bind: {
  263. stay: () => {
  264. $this.on(Event.MOUSEENTER, () => {
  265. debug('發生 MOUSEENTER 事件', element);
  266. return module.set.stay(true);
  267. });
  268. return $this.on(Event.MOUSELEAVE, () => {
  269. debug('發生 MOUSELEAVE 事件', element);
  270. return module.set.stay(false);
  271. });
  272. },
  273. events: () => {
  274. $this.on(Event.CLICK, Selector.ACTION, (event, context) => {
  275. debug('發生 CLICK 事件', element);
  276. module.hide(Event.ACTION);
  277. return settings.onAction.call(context, event);
  278. });
  279. $this.on(Event.ACTION, (event, context) => {
  280. debug('發生 ACTION 事件', context);
  281. return settings.onAction.call(context, event);
  282. });
  283. $this.on(Event.IGNORE, (event, context) => {
  284. debug('發生 IGNORE 事件', context);
  285. return settings.onIgnore.call(context, event);
  286. });
  287. return $this.on(Event.CLOSE, (event, context) => {
  288. debug('發生 CLOSE 事件', context);
  289. return settings.onClose.call(context, event);
  290. });
  291. }
  292. },
  293. // ------------------------------------------------------------------------
  294. // 基礎方法
  295. // ------------------------------------------------------------------------
  296. initialize: () => {
  297. debug('初始化點心條', element);
  298. module.bind.events();
  299. if (settings.hoverStay === true) {
  300. module.bind.stay();
  301. }
  302. $action = $this.find(Selector.ACTION);
  303. return $content = $this.find(Selector.CONTENT);
  304. },
  305. instantiate: () => {
  306. return debug('實例化點心條', element);
  307. },
  308. refresh: () => {
  309. return $allModules;
  310. },
  311. destroy: () => {
  312. debug('摧毀點心條', element);
  313. $this.removeData(MODULE_NAMESPACE).off(EVENT_NAMESPACE);
  314. return $allModules;
  315. }
  316. };
  317. });
  318. // ------------------------------------------------------------------------
  319. // 模組直接註冊
  320. // ------------------------------------------------------------------------
  321. ts.snackbar = (options) => {
  322. var $snackbar, create;
  323. $snackbar = ts(Selector.SNACKBAR);
  324. switch (options) {
  325. case 'hide':
  326. return $snackbar.snackbar('hide');
  327. case 'is hidden':
  328. if (!$snackbar.exists()) {
  329. return true;
  330. }
  331. return $snackbar.snackbar('is hidden');
  332. case 'is visible':
  333. if (!$snackbar.exists()) {
  334. return false;
  335. }
  336. return $snackbar.snackbar('is visible');
  337. case 'destroy':
  338. return $snackbar.snackbar('destroy');
  339. }
  340. create = () => {
  341. return $snackbar = ts('<div>').attr(Attribute.TEMPORARY, 'true').addClass(ClassName.SNACKBAR).html(Template.SNACKBAR).appendTo(Selector.BODY).snackbar(Object.assign({}, options, {
  342. onClose: () => {
  343. var ref;
  344. if ((ref = options.onClose) != null) {
  345. ref.call($snackbar.get());
  346. }
  347. $snackbar.repaint();
  348. return $snackbar.remove();
  349. }
  350. })).snackbar('show');
  351. };
  352. if (!$snackbar.exists()) {
  353. create();
  354. return;
  355. }
  356. return $snackbar.snackbar('hide', Event.IGNORE, () => {
  357. $snackbar.remove();
  358. return create();
  359. });
  360. };
  361. }).call(this);