handleFormElement.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import { sleep, objectHasKey } from '@/utils/helper';
  2. import { keyDefinitions } from '@/utils/USKeyboardLayout';
  3. import simulateEvent from './simulateEvent';
  4. const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
  5. window.HTMLInputElement.prototype,
  6. 'value'
  7. ).set;
  8. function reactJsEvent(element, value) {
  9. console.dir(element);
  10. console.log(
  11. objectHasKey(element, '_valueTracker'),
  12. element.parentElement.hasChildNodes(element)
  13. );
  14. console.log(!element._valueTracker, element._valueTracker, element);
  15. if (!element._valueTracker) return;
  16. const previousValue = element.value;
  17. nativeInputValueSetter.call(element, value);
  18. element._valueTracker.setValue(previousValue);
  19. }
  20. function formEvent(element, data) {
  21. if (data.type === 'text-field') {
  22. const currentKey = /\s/.test(data.value) ? 'Space' : data.value;
  23. const { key, keyCode, code } = keyDefinitions[currentKey] || {
  24. key: currentKey,
  25. keyCode: 0,
  26. code: `Key${currentKey}`,
  27. };
  28. simulateEvent(element, 'keydown', {
  29. key,
  30. code,
  31. keyCode,
  32. bubbles: true,
  33. cancelable: true,
  34. });
  35. simulateEvent(element, 'keyup', {
  36. key,
  37. code,
  38. keyCode,
  39. bubbles: true,
  40. cancelable: true,
  41. });
  42. }
  43. simulateEvent(element, 'input', {
  44. inputType: 'insertText',
  45. data: data.value,
  46. bubbles: true,
  47. cancelable: true,
  48. });
  49. if (data.type !== 'text-field') {
  50. element.dispatchEvent(
  51. new Event('change', { bubbles: true, cancelable: true })
  52. );
  53. }
  54. }
  55. async function inputText({ data, element, isEditable }) {
  56. element?.focus();
  57. element?.click();
  58. const elementKey = isEditable ? 'textContent' : 'value';
  59. if (data.delay > 0 && !document.hidden) {
  60. for (let index = 0; index < data.value.length; index += 1) {
  61. if (elementKey === 'value') reactJsEvent(element, element.value);
  62. const currentChar = data.value[index];
  63. element[elementKey] += currentChar;
  64. formEvent(element, {
  65. type: 'text-field',
  66. value: currentChar,
  67. isEditable,
  68. });
  69. await sleep(data.delay);
  70. }
  71. } else {
  72. if (elementKey === 'value') reactJsEvent(element, element.value);
  73. element[elementKey] += data.value;
  74. formEvent(element, {
  75. isEditable,
  76. type: 'text-field',
  77. value: data.value[0] ?? '',
  78. });
  79. }
  80. element.dispatchEvent(
  81. new Event('change', { bubbles: true, cancelable: true })
  82. );
  83. element?.blur();
  84. }
  85. export default async function (element, data) {
  86. const textFields = ['INPUT', 'TEXTAREA'];
  87. const isEditable =
  88. element.hasAttribute('contenteditable') && element.isContentEditable;
  89. if (isEditable) {
  90. if (data.clearValue) element.innerText = '';
  91. await inputText({ data, element, isEditable });
  92. return;
  93. }
  94. if (data.type === 'text-field' && textFields.includes(element.tagName)) {
  95. if (data.clearValue) {
  96. element?.select();
  97. reactJsEvent(element, '');
  98. element.value = '';
  99. }
  100. await inputText({ data, element });
  101. return;
  102. }
  103. element?.focus();
  104. if (data.type === 'checkbox' || data.type === 'radio') {
  105. element.checked = data.selected;
  106. formEvent(element, { type: data.type, value: data.selected });
  107. } else if (data.type === 'select') {
  108. let optionValue = data.value;
  109. const options = element.querySelectorAll('option');
  110. const getOptionValue = (index) => {
  111. if (!options) return element.value;
  112. let optionIndex = index;
  113. const maxIndex = options.length - 1;
  114. if (index < 0) optionIndex = 0;
  115. else if (index > maxIndex) optionIndex = maxIndex;
  116. return options[optionIndex]?.value || element.value;
  117. };
  118. switch (data.selectOptionBy) {
  119. case 'first-option':
  120. optionValue = getOptionValue(0);
  121. break;
  122. case 'last-option':
  123. optionValue = getOptionValue(options.length - 1);
  124. break;
  125. case 'custom-position':
  126. optionValue = getOptionValue(+data.optionPosition - 1 ?? 0);
  127. break;
  128. default:
  129. }
  130. if (optionValue) {
  131. element.value = optionValue;
  132. formEvent(element, data);
  133. }
  134. }
  135. element?.blur();
  136. }