parents.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. import $ from 'jquery';
  2. import Finder from '../../utils/finderjs';
  3. import { config as gravConfig } from 'grav-config';
  4. let XHRUUID = 0;
  5. export const Instances = {};
  6. export class Parents {
  7. constructor(container, field, data) {
  8. this.container = $(container);
  9. this.fieldName = field.attr('name');
  10. this.field = $(`[name="${this.fieldName}"]`);
  11. this.data = data;
  12. this.parentLabel = $(`[data-parents-field-label="${this.fieldName}"]`);
  13. this.parentName = $(`[data-parents-field-name="${this.fieldName}"]`);
  14. const dataLoad = this.dataLoad;
  15. this.finder = new Finder(
  16. this.container,
  17. (parent, callback) => {
  18. return dataLoad.call(this, parent, callback);
  19. },
  20. {
  21. labelKey: 'name',
  22. defaultPath: this.field.val(),
  23. createItemContent: function(item) {
  24. return Parents.createItemContent(this.config, item);
  25. }
  26. }
  27. );
  28. /*
  29. this.finder.$emitter.on('leaf-selected', (item) => {
  30. console.log('selected', item);
  31. this.finder.emit('create-column', () => this.createSimpleColumn(item));
  32. });
  33. this.finder.$emitter.on('item-selected', (selected) => {
  34. console.log('selected', selected);
  35. // for future use only - create column-card creation for file with details like in macOS finder
  36. // this.finder.$emitter('create-column', () => this.createSimpleColumn(selected));
  37. }); */
  38. this.finder.$emitter.on('column-created', () => {
  39. this.container[0].scrollLeft = this.container[0].scrollWidth - this.container[0].clientWidth;
  40. });
  41. }
  42. static createItemContent(config, item) {
  43. const frag = document.createDocumentFragment();
  44. const label = $(`<span title="${item[config.labelKey]}" />`);
  45. const infoContainer = $('<span class="info-container" />');
  46. const iconPrepend = $('<i />');
  47. const iconAppend = $('<i />');
  48. const badge = $('<span class="badge" />');
  49. const prependClasses = ['fa'];
  50. const appendClasses = ['fa'];
  51. // prepend icon
  52. if (item.children || item.type === 'dir') {
  53. prependClasses.push('fa-folder');
  54. } else if (item.type === 'root') {
  55. prependClasses.push('fa-sitemap');
  56. } else if (item.type === 'file') {
  57. prependClasses.push('fa-file-o');
  58. }
  59. iconPrepend.addClass(prependClasses.join(' '));
  60. // text label
  61. label.text(item[config.labelKey]).prepend(iconPrepend);
  62. label.appendTo(frag);
  63. // append icon
  64. if (item.children || item['has-children']) {
  65. appendClasses.push('fa-caret-right');
  66. badge.text(item.size || item.count || 0);
  67. badge.appendTo(infoContainer);
  68. }
  69. iconAppend.addClass(appendClasses.join(' '));
  70. iconAppend.appendTo(infoContainer);
  71. infoContainer.appendTo(frag);
  72. return frag;
  73. }
  74. static createLoadingColumn() {
  75. return $(`
  76. <div class="fjs-col leaf-col" style="overflow: hidden;">
  77. <div class="leaf-row">
  78. <div class="grav-loading"><div class="grav-loader">Loading...</div></div>
  79. </div>
  80. </div>
  81. `);
  82. }
  83. static createErrorColumn(error) {
  84. return $(`
  85. <div class="fjs-col leaf-col" style="overflow: hidden;">
  86. <div class="leaf-row error">
  87. <i class="fa fa-fw fa-warning"></i>
  88. <span>${error}</span>
  89. </div>
  90. </div>
  91. `);
  92. }
  93. createSimpleColumn(item) {}
  94. dataLoad(parent, callback) {
  95. if (!parent) {
  96. return callback(this.data);
  97. }
  98. if (parent.type !== 'dir' || !parent['has-children']) {
  99. return false;
  100. }
  101. const UUID = ++XHRUUID;
  102. this.startLoader();
  103. $.ajax({
  104. url: `${gravConfig.current_url}`,
  105. method: 'post',
  106. data: Object.assign({}, getExtraFormData(this.container), {
  107. route: b64_encode_unicode(parent.value),
  108. field: this.field.data('fieldName'),
  109. action: 'getLevelListing',
  110. 'admin-nonce': gravConfig.admin_nonce
  111. }),
  112. success: (response) => {
  113. this.stopLoader();
  114. if (response.status === 'error') {
  115. this.finder.$emitter.emit('create-column', Parents.createErrorColumn(response.message)[0]);
  116. return false;
  117. }
  118. // stale request
  119. if (UUID !== XHRUUID) {
  120. return false;
  121. }
  122. return callback(response.data);
  123. }
  124. });
  125. }
  126. startLoader() {
  127. this.loadingIndicator = Parents.createLoadingColumn();
  128. this.finder.$emitter.emit('create-column', this.loadingIndicator[0]);
  129. return this.loadingIndicator;
  130. }
  131. stopLoader() {
  132. return this.loadingIndicator && this.loadingIndicator.remove();
  133. }
  134. }
  135. export const b64_encode_unicode = (str) => {
  136. return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
  137. function toSolidBytes(match, p1) {
  138. return String.fromCharCode('0x' + p1);
  139. }));
  140. };
  141. export const b64_decode_unicode = (str) => {
  142. return decodeURIComponent(atob(str).split('').map(function(c) {
  143. return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
  144. }).join(''));
  145. };
  146. const getExtraFormData = (container) => {
  147. let form = container.closest('form');
  148. if (container.closest('[data-remodal-id]').length) {
  149. form = $('form#blueprints');
  150. }
  151. const data = {};
  152. const unique_id = form.find('[name="__unique_form_id__"]');
  153. data['__form-name__'] = form.find('[name="__form-name__"]').val();
  154. data['form-nonce'] = form.find('[name="form-nonce"]').val();
  155. if (unique_id.length) {
  156. data['__unique_form_id__'] = unique_id.val();
  157. }
  158. return data;
  159. };
  160. $(document).on('click', '[data-parents]', (event) => {
  161. event.preventDefault();
  162. event.stopPropagation();
  163. const target = $(event.currentTarget);
  164. let field = target.closest('.parents-wrapper').find('input[name]');
  165. let fieldName = field.attr('name');
  166. if (!field.length) {
  167. fieldName = target.data('parents');
  168. field = $(`[name="${target.data('parents')}"]`).first();
  169. }
  170. const modal = $(`[data-remodal-id="${target.data('remodalTarget') || 'parents'}"]`);
  171. const loader = modal.find('.grav-loading');
  172. const content = modal.find('.parents-content');
  173. loader.css('display', 'block');
  174. content.html('');
  175. $.ajax({
  176. url: `${gravConfig.current_url}`,
  177. method: 'post',
  178. data: Object.assign({}, getExtraFormData(target), {
  179. route: b64_encode_unicode(field.val()),
  180. field: field.data('fieldName'),
  181. action: 'getLevelListing',
  182. 'admin-nonce': gravConfig.admin_nonce,
  183. initial: true
  184. }),
  185. success(response) {
  186. loader.css('display', 'none');
  187. if (response.status === 'error') {
  188. content.html(response.message);
  189. return true;
  190. }
  191. if (!Instances[`${fieldName}-${modal.data('remodalId')}`]) {
  192. Instances[`${fieldName}-${modal.data('remodalId')}`] = new Parents(content, field, response.data);
  193. } else {
  194. Instances[`${fieldName}-${modal.data('remodalId')}`].finder.reload(response.data);
  195. }
  196. modal.data('parents', Instances[`${fieldName}-${modal.data('remodalId')}`]);
  197. }
  198. });
  199. });
  200. // apply finder selection to field
  201. $(document).on('click', '[data-remodal-id].parents-container [data-parents-select]', (event) => {
  202. const modal = $(event.currentTarget).closest('[data-remodal-id]');
  203. const parents = modal.data('parents');
  204. const finder = parents.finder;
  205. const field = parents.field;
  206. const parentLabel = parents.parentLabel;
  207. const parentName = parents.parentName;
  208. const selection = finder.findLastActive().item[0];
  209. const value = selection._item[finder.config.valueKey];
  210. const name = selection._item[finder.config.labelKey];
  211. field.val(value);
  212. parentLabel.text(value);
  213. parentName.text(name);
  214. finder.config.defaultPath = value;
  215. const remodal = $.remodal.lookup[$(`[data-remodal-id="${modal.data('remodalId')}"]`).data('remodal')];
  216. remodal.close();
  217. });