array.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. import $ from 'jquery';
  2. import Sortable from 'sortablejs';
  3. let body = $('body');
  4. class Template {
  5. constructor(container) {
  6. this.container = $(container);
  7. if (this.getName() === undefined) {
  8. this.container = this.container.closest('[data-grav-array-name]');
  9. }
  10. }
  11. getName() {
  12. return this.container.data('grav-array-name') || '';
  13. }
  14. getKeyPlaceholder() {
  15. return this.container.data('grav-array-keyname') || 'Key';
  16. }
  17. getValuePlaceholder() {
  18. return this.container.data('grav-array-valuename') || 'Value';
  19. }
  20. isValueOnly() {
  21. return this.container.find('[data-grav-array-mode="value_only"]:first').length || false;
  22. }
  23. isTextArea() {
  24. return this.container.data('grav-array-textarea') || false;
  25. }
  26. shouldBeDisabled() {
  27. // check for toggleables, if field is toggleable and it's not enabled, render disabled
  28. let toggle = this.container.closest('.form-field').find('[data-grav-field="toggleable"] input[type="checkbox"]');
  29. return toggle.length && toggle.is(':not(:checked)');
  30. }
  31. getNewRow() {
  32. let tpl = '';
  33. const value = this.isTextArea()
  34. ? `<textarea ${this.shouldBeDisabled() ? 'disabled="disabled"' : ''} data-grav-array-type="value" name="" placeholder="${this.getValuePlaceholder()}"></textarea>`
  35. : `<input ${this.shouldBeDisabled() ? 'disabled="disabled"' : ''} data-grav-array-type="value" type="text" name="" value="" placeholder="${this.getValuePlaceholder()}" />`;
  36. if (this.isValueOnly()) {
  37. tpl += `
  38. <div class="form-row array-field-value_only" data-grav-array-type="row">
  39. <span data-grav-array-action="sort" class="fa fa-bars"></span>
  40. ${value}
  41. `;
  42. } else {
  43. tpl += `
  44. <div class="form-row" data-grav-array-type="row">
  45. <span data-grav-array-action="sort" class="fa fa-bars"></span>
  46. <input ${this.shouldBeDisabled() ? 'disabled="disabled"' : ''} data-grav-array-type="key" type="text" value="" placeholder="${this.getKeyPlaceholder()}" />
  47. ${value}
  48. `;
  49. }
  50. tpl += `
  51. <span data-grav-array-action="rem" class="fa fa-minus"></span>
  52. <span data-grav-array-action="add" class="fa fa-plus"></span>
  53. </div>`;
  54. return tpl;
  55. }
  56. }
  57. export default class ArrayField {
  58. constructor() {
  59. body.on('input', '[data-grav-array-type="key"], [data-grav-array-type="value"]', (event) => this.actionInput(event));
  60. body.on('click touch', '[data-grav-array-action]:not([data-grav-array-action="sort"])', (event) => this.actionEvent(event));
  61. this.arrays = $();
  62. $('[data-grav-field="array"]').each((index, list) => this.addArray(list));
  63. $('body').on('mutation._grav', this._onAddedNodes.bind(this));
  64. }
  65. addArray(list) {
  66. list = $(list);
  67. list.find('[data-grav-array-type="container"]').each((index, container) => {
  68. container = $(container);
  69. if (container.data('array-sort') || container[0].hasAttribute('data-array-nosort')) { return; }
  70. container.data('array-sort', new Sortable(container.get(0), {
  71. handle: '.fa-bars',
  72. animation: 150
  73. }));
  74. });
  75. }
  76. actionInput(event) {
  77. let element = $(event.target);
  78. let type = element.data('grav-array-type');
  79. this._setTemplate(element);
  80. let template = element.data('array-template');
  81. let keyElement = type === 'key' ? element : element.siblings('[data-grav-array-type="key"]:first');
  82. let valueElement = type === 'value' ? element : element.siblings('[data-grav-array-type="value"]:first');
  83. let escaped_name = !template.isValueOnly() ? keyElement.val() : this.getIndexFor(element);
  84. escaped_name = escaped_name.toString().replace(/\[/g, '%5B').replace(/]/g, '%5D');
  85. let name = `${template.getName()}[${escaped_name}]`;
  86. valueElement.attr('name', !valueElement.val() ? template.getName() : name);
  87. this.refreshNames(template);
  88. }
  89. actionEvent(event) {
  90. event && event.preventDefault();
  91. let element = $(event.target);
  92. let action = element.data('grav-array-action');
  93. let container = element.parents('[data-grav-array-type="container"]');
  94. this._setTemplate(element);
  95. this[`${action}Action`](element);
  96. let siblings = container.find('> div');
  97. container[siblings.length > 1 ? 'removeClass' : 'addClass']('one-child');
  98. }
  99. addAction(element) {
  100. let template = element.data('array-template');
  101. let row = element.closest('[data-grav-array-type="row"]');
  102. row.after(template.getNewRow());
  103. }
  104. remAction(element) {
  105. let template = element.data('array-template');
  106. let row = element.closest('[data-grav-array-type="row"]');
  107. let isLast = !row.siblings().length;
  108. if (isLast) {
  109. let newRow = $(template.getNewRow());
  110. row.after(newRow);
  111. newRow.find('[data-grav-array-type="value"]:last').attr('name', template.getName());
  112. }
  113. row.remove();
  114. this.refreshNames(template);
  115. }
  116. refreshNames(template) {
  117. if (!template.isValueOnly()) { return; }
  118. let row = template.container.find('> div > [data-grav-array-type="row"]');
  119. let inputs = row.find('[name]:not([name=""])');
  120. inputs.each((index, input) => {
  121. input = $(input);
  122. let name = input.attr('name');
  123. name = name.replace(/\[\d+\]$/, `[${index}]`);
  124. input.attr('name', name);
  125. });
  126. if (!inputs.length) {
  127. row.find('[data-grav-array-type="value"]').attr('name', template.getName());
  128. }
  129. }
  130. getIndexFor(element) {
  131. let template = element.data('array-template');
  132. let row = element.closest('[data-grav-array-type="row"]');
  133. return template.container.find(`${template.isValueOnly() ? '> div ' : ''} > [data-grav-array-type="row"]`).index(row);
  134. }
  135. _setTemplate(element) {
  136. if (!element.data('array-template')) {
  137. element.data('array-template', new Template(element.closest('[data-grav-array-name]')));
  138. }
  139. }
  140. _onAddedNodes(event, target/* , record, instance */) {
  141. let arrays = $(target).find('[data-grav-field="array"]');
  142. if (!arrays.length) { return; }
  143. arrays.each((index, list) => {
  144. list = $(list);
  145. if (!~this.arrays.index(list)) {
  146. this.addArray(list);
  147. }
  148. });
  149. }
  150. }
  151. export let Instance = new ArrayField();