index.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import { chooserModalOnloadHandlers } from '../../includes/chooserModal';
  2. export class Chooser {
  3. modalOnloadHandlers = chooserModalOnloadHandlers;
  4. titleStateKey = 'title'; // key used in the 'state' dictionary to hold the human-readable title
  5. editUrlStateKey = 'edit_url'; // key used in the 'state' dictionary to hold the URL of the edit page
  6. chosenResponseName = 'chosen'; // identifier for the ModalWorkflow response that indicates an item was chosen
  7. constructor(id) {
  8. this.initHTMLElements(id);
  9. this.state = this.getStateFromHTML();
  10. for (const btn of this.chooserElement.querySelectorAll('.action-choose')) {
  11. btn.addEventListener('click', () => {
  12. this.openChooserModal();
  13. });
  14. }
  15. for (const btn of this.chooserElement.querySelectorAll('.action-clear')) {
  16. btn.addEventListener('click', () => {
  17. this.clear();
  18. });
  19. }
  20. }
  21. initHTMLElements(id) {
  22. this.chooserElement = document.getElementById(`${id}-chooser`);
  23. this.titleElement = this.chooserElement.querySelector(
  24. '[data-chooser-title]',
  25. );
  26. this.input = document.getElementById(id);
  27. this.editLink = this.chooserElement.querySelector('.edit-link');
  28. this.chooserBaseUrl = this.chooserElement.dataset.chooserUrl;
  29. }
  30. getStateFromHTML() {
  31. /*
  32. Construct initial state of the chooser from the rendered (static) HTML.
  33. State is either null (= no item chosen) or a dict of id, title and edit_url.
  34. The result returned from the chooser modal (see get_chosen_response_data in
  35. wagtail.admin.views.generic.chooser.ChosenView) is a superset of this, and can therefore be
  36. passed directly to chooser.setState.
  37. */
  38. if (this.input.value) {
  39. const state = {
  40. id: this.input.value,
  41. };
  42. if (this.titleElement && this.titleStateKey) {
  43. state[this.titleStateKey] = this.titleElement.textContent;
  44. }
  45. if (this.editLink && this.editUrlStateKey) {
  46. state[this.editUrlStateKey] = this.editLink.getAttribute('href');
  47. }
  48. return state;
  49. } else {
  50. return null;
  51. }
  52. }
  53. getState() {
  54. return this.state;
  55. }
  56. getValue() {
  57. return this.state && this.state.id;
  58. }
  59. setState(newState) {
  60. this.state = newState;
  61. if (newState) {
  62. this.renderState(newState);
  63. } else {
  64. this.renderEmptyState();
  65. }
  66. }
  67. clear() {
  68. this.setState(null);
  69. }
  70. renderEmptyState() {
  71. this.input.setAttribute('value', '');
  72. this.chooserElement.classList.add('blank');
  73. }
  74. renderState(newState) {
  75. this.input.setAttribute('value', newState.id);
  76. if (this.titleElement && this.titleStateKey) {
  77. this.titleElement.textContent = newState[this.titleStateKey];
  78. }
  79. this.chooserElement.classList.remove('blank');
  80. if (this.editLink) {
  81. const editUrl = newState[this.editUrlStateKey];
  82. if (editUrl) {
  83. this.editLink.setAttribute('href', editUrl);
  84. this.editLink.classList.remove('u-hidden');
  85. } else {
  86. this.editLink.classList.add('u-hidden');
  87. }
  88. }
  89. }
  90. getTextLabel(opts) {
  91. if (!this.state) return null;
  92. const result = this.state[this.titleStateKey];
  93. if (opts && opts.maxLength && result.length > opts.maxLength) {
  94. return result.substring(0, opts.maxLength - 1) + '…';
  95. }
  96. return result;
  97. }
  98. focus() {
  99. if (this.state) {
  100. this.chooserElement.querySelector('.chosen .action-choose').focus();
  101. } else {
  102. this.chooserElement.querySelector('.unchosen .action-choose').focus();
  103. }
  104. }
  105. getModalUrl() {
  106. return this.chooserBaseUrl;
  107. }
  108. getModalUrlParams() {
  109. return null;
  110. }
  111. openChooserModal() {
  112. // eslint-disable-next-line no-undef
  113. ModalWorkflow({
  114. url: this.getModalUrl(),
  115. urlParams: this.getModalUrlParams(),
  116. onload: this.modalOnloadHandlers,
  117. responses: {
  118. [this.chosenResponseName]: (result) => {
  119. this.setState(result);
  120. },
  121. },
  122. });
  123. }
  124. }
  125. export class ChooserFactory {
  126. widgetClass = Chooser;
  127. constructor(html, idPattern) {
  128. this.html = html;
  129. this.idPattern = idPattern;
  130. }
  131. render(placeholder, name, id, initialState) {
  132. const html = this.html.replace(/__NAME__/g, name).replace(/__ID__/g, id);
  133. // eslint-disable-next-line no-param-reassign
  134. placeholder.outerHTML = html;
  135. // eslint-disable-next-line new-cap
  136. const chooser = new this.widgetClass(id);
  137. chooser.setState(initialState);
  138. return chooser;
  139. }
  140. }