iconpicker.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. import $ from 'jquery';
  2. /* Icon Picker by QueryLoop
  3. * Author: @eliorivero
  4. * URL: http://queryloop.com/
  5. * License: GPLv2
  6. */
  7. var defaults = {
  8. 'mode': 'dialog', // show overlay 'dialog' panel or slide down 'inline' panel
  9. 'closeOnPick': true, // whether to close panel after picking or 'no'
  10. 'save': 'class', // save icon 'class' or 'code'
  11. 'size': '',
  12. 'classes': {
  13. 'launcher': '', // extra classes for launcher buttons
  14. 'clear': 'remove-times', // extra classes for button that removes preview and clears field
  15. 'highlight': '', // extra classes when highlighting an icon
  16. 'close': '' // extra classes for close button
  17. },
  18. 'iconSets': { // example data structure. Used to specify which launchers will be created
  19. 'genericon': 'Genericon', // create a launcher to pick genericon icons
  20. 'fa': 'FontAwesome' // create a launcher to pick fontawesome icons
  21. }
  22. };
  23. class QL_Icon_Picker {
  24. constructor(element, options) {
  25. this.iconSet = '';
  26. this.iconSetName = '';
  27. this.$field = '';
  28. this.element = element;
  29. this.settings = $.extend({}, defaults, options);
  30. this._defaults = defaults;
  31. this.init();
  32. }
  33. init() {
  34. var $brick = $(this.element);
  35. var pickerId = $brick.data('pickerid');
  36. var $preview = $('<div class="icon-preview icon-preview-' + pickerId + '" />');
  37. this.$field = $brick.find('input');
  38. // Add preview area
  39. this.makePreview($brick, pickerId, $preview);
  40. // Make button to clear field and remove preview
  41. this.makeClear(pickerId, $preview);
  42. // Make buttons that open the panel of icons
  43. this.makeLaunchers($brick, pickerId);
  44. // Prepare display styles, inline and dialog
  45. this.makeDisplay($brick);
  46. }
  47. makePreview($brick, pickerId, $preview) {
  48. var $icon = $('<i />');
  49. var iconValue = this.$field.val();
  50. $preview.prependTo($brick);
  51. $icon.prependTo($preview);
  52. if (iconValue !== '') {
  53. $preview.addClass('icon-preview-on');
  54. $icon.addClass(iconValue);
  55. }
  56. }
  57. makeClear(pickerId, $preview) {
  58. var base = this;
  59. var $clear = $('<a class="remove-icon ' + base.settings.classes.clear + '" />');
  60. // Hide button to remove icon and preview and append it to preview area
  61. $clear.hide().prependTo($preview);
  62. // If there's a icon saved in the field, show remove icon button
  63. if (base.$field.val() !== '') {
  64. $clear.show();
  65. }
  66. $preview.on('click', '.remove-icon', function(e) {
  67. e.preventDefault();
  68. base.$field.val('');
  69. $preview.removeClass('icon-preview-on').find('i').removeClass();
  70. $(this).hide();
  71. });
  72. }
  73. makeDisplay($brick) {
  74. var base = this;
  75. var close = base.settings.classes.close;
  76. var $body = $('body');
  77. var $close = $('<a href="#" class="icon-picker-close"/>');
  78. if (base.settings.mode === 'inline') {
  79. $brick.find('.icon-set').append($close).removeClass('dialog').addClass('ip-inline ' + base.settings.size).parent().addClass('icon-set-wrap');
  80. } else if (base.settings.mode === 'dialog') {
  81. $('.icon-set').addClass('dialog ' + base.settings.size);
  82. if ($('.icon-picker-overlay').length <= 0) {
  83. $body.append('<div class="icon-picker-overlay"/>').append($close);
  84. }
  85. }
  86. $body
  87. .on('click', '.icon-picker-close, .icon-picker-overlay', function(e) {
  88. e.preventDefault();
  89. base.closePicker($brick, $(base.iconSet), base.settings.mode);
  90. })
  91. .on('mouseenter mouseleave', '.icon-picker-close', function(e) {
  92. if (e.type === 'mouseenter') {
  93. $(this).addClass(close);
  94. } else {
  95. $(this).removeClass(close);
  96. }
  97. });
  98. }
  99. makeLaunchers($brick) {
  100. var base = this;
  101. var dataIconSets = $brick.data('iconsets');
  102. var iconSet;
  103. if (typeof dataIconSets === 'undefined') {
  104. dataIconSets = base.settings.iconSets;
  105. }
  106. for (iconSet in dataIconSets) {
  107. if (dataIconSets.hasOwnProperty(iconSet)) {
  108. $brick.append('<a class="launch-icons button ' + base.settings.classes.launcher + '" data-icons="' + iconSet + '">' + dataIconSets[iconSet] + '</a>');
  109. }
  110. }
  111. $brick.find('.launch-icons').on('click', function(e) {
  112. e.preventDefault();
  113. var $self = $(this);
  114. var theseIcons = $self.data('icons');
  115. base.iconSetName = theseIcons;
  116. base.iconSet = '.' + theseIcons + '-set';
  117. // Initialize picker
  118. base.iconPick($brick);
  119. // Show icon picker
  120. base.showPicker($brick, $(base.iconSet), base.settings.mode);
  121. });
  122. }
  123. iconPick($brick) {
  124. var base = this;
  125. var highlight = 'icon-highlight ' + base.settings.classes.highlight;
  126. $(base.iconSet).on('click', 'li', function(e) {
  127. e.preventDefault();
  128. var $icon = $(this);
  129. var icon = $icon.data(base.settings.save);
  130. // Mark as selected
  131. $('.icon-selected').removeClass('icon-selected');
  132. $icon.addClass('icon-selected');
  133. if (base.$field.data('format') === 'short') {
  134. icon = icon.slice(6);
  135. }
  136. // Save icon value to field
  137. base.$field.val(icon);
  138. // Close icon picker
  139. if (base.settings.closeOnPick) {
  140. base.closePicker($brick, $icon.closest(base.iconSet), base.settings.mode);
  141. }
  142. // Set preview
  143. base.setPreview($icon.data('class'));
  144. // Broadcast event passing the selected icon.
  145. $('body').trigger('iconselected.queryloop', icon);
  146. });
  147. $(base.iconSet).on('mouseenter mouseleave', 'li', function(e) {
  148. if (e.type === 'mouseenter') {
  149. $(this).addClass(highlight);
  150. } else {
  151. $(this).removeClass(highlight);
  152. }
  153. });
  154. }
  155. showPicker($brick, $icons, mode) {
  156. if (mode === 'inline') {
  157. $('.icon-set').removeClass('ip-inline-open');
  158. $brick.find($icons).toggleClass('ip-inline-open');
  159. } else if (mode === 'dialog') {
  160. $brick.find('.icon-picker-close').addClass('make-visible');
  161. $brick.find('.icon-picker-overlay').addClass('make-visible');
  162. $icons.addClass('dialog-open');
  163. }
  164. $icons.find('.icon-selected').removeClass('icon-selected');
  165. var selectedIcon = this.$field.val().replace(' ', '.');
  166. if (selectedIcon !== '') {
  167. if (this.settings.save === 'class') {
  168. $icons.find('.' + selectedIcon).addClass('icon-selected');
  169. } else {
  170. $icons.find('[data-code="' + selectedIcon + '"]').addClass('icon-selected');
  171. }
  172. }
  173. // Broadcast event when the picker is shown passing the picker mode.
  174. $('body').trigger('iconpickershow.queryloop', mode);
  175. }
  176. closePicker($brick, $icons, mode) {
  177. // Remove event so they don't fire from a different picker
  178. $(this.iconSet).off('click', 'li');
  179. if (mode === 'inline') {
  180. $brick.find($icons).removeClass('ip-inline-open');
  181. } else if (mode === 'dialog') {
  182. $('.icon-picker-close, .icon-picker-overlay').removeClass('make-visible');
  183. }
  184. // Broadcast event when the picker is closed passing the picker mode.
  185. $('body').trigger('iconpickerclose.queryloop', mode);
  186. $('.icon-set').removeClass('dialog-open');
  187. }
  188. setPreview(preview) {
  189. var $preview = $(this.element).find('.icon-preview');
  190. $preview.addClass('icon-preview-on').find('i').removeClass()
  191. .addClass(this.iconSetName)
  192. .addClass(preview);
  193. $preview.find('a').show();
  194. }
  195. }
  196. /* Grav */
  197. // extend $ with 3rd party QL Icon Picker
  198. $.fn.qlIconPicker = function(options) {
  199. this.each(function() {
  200. if (!$.data(this, 'plugin_qlIconPicker')) {
  201. $.data(this, 'plugin_qlIconPicker', new QL_Icon_Picker(this, options));
  202. }
  203. });
  204. return this;
  205. };
  206. export default class IconpickerField {
  207. constructor(options) {
  208. this.items = $();
  209. this.options = Object.assign({}, this.defaults, options);
  210. $('[data-grav-iconpicker]').each((index, element) => this.addItem(element));
  211. $('body').on('mutation._grav', this._onAddedNodes.bind(this));
  212. }
  213. _onAddedNodes(event, target/* , record, instance */) {
  214. let fields = $(target).find('[data-grav-iconpicker]');
  215. if (!fields.length) { return; }
  216. fields.each((index, field) => {
  217. field = $(field);
  218. if (!~this.items.index(field)) {
  219. this.addItem(field);
  220. }
  221. });
  222. }
  223. addItem(element) {
  224. element = $(element);
  225. this.items = this.items.add(element);
  226. element.find('.icon-picker').qlIconPicker({
  227. 'save': 'class'
  228. });
  229. // hack to remove extra icon sets that are just copies
  230. $('.icon-set:not(:first)').remove();
  231. }
  232. }
  233. export let Instance = new IconpickerField();
  234. // Fix to close the dialog when clicking outside
  235. $(document).on('click', (event) => {
  236. const target = $(event.target);
  237. const match = '.icon-set.dialog-open, .launch-icons[data-icons]';
  238. if (!target.is(match) && !target.closest(match).length) {
  239. const dialogs = $('.icon-set.dialog-open');
  240. // skip if there's no dialog open
  241. if (dialogs.length) {
  242. dialogs.each((index, dialog) => {
  243. const picker = $(dialog).siblings('.icon-picker');
  244. const data = picker.data('plugin_qlIconPicker');
  245. data.closePicker(picker, $(data.iconSet), data.settings.mode);
  246. });
  247. }
  248. }
  249. });