array.js 6.4 KB

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