Browse Source

feat(editor): replace text-input with forms block

Ahmad Kholid 3 years ago
parent
commit
1974574787

+ 1 - 0
package.json

@@ -22,6 +22,7 @@
   "dependencies": {
     "@medv/finder": "^2.1.0",
     "@vuex-orm/core": "^0.36.4",
+    "css-selector-generator": "^3.4.4",
     "dayjs": "^1.10.7",
     "drawflow": "^0.0.49",
     "nanoid": "3.1.28",

+ 2 - 2
src/background/workflow-engine.js

@@ -54,7 +54,7 @@ class WorkflowEngine {
       console.error('A trigger block is required');
       return;
     }
-
+    /* to do: pause if website is loading and inject content-script */
     browser.tabs.onRemoved.addListener(this.tabRemovedListener);
 
     this.blocks = blocks;
@@ -85,7 +85,7 @@ class WorkflowEngine {
 
       return;
     }
-
+    console.log(`${block.name}:`, block);
     const isInteraction = tasks[block.name].category === 'interaction';
     const handlerName = isInteraction
       ? 'interactionHandler'

+ 2 - 2
src/components/newtab/workflow/WorkflowEditBlock.vue

@@ -17,9 +17,9 @@
 </template>
 <script>
 import { computed } from 'vue';
+import EditForms from './edit/EditForms.vue';
 import EditTrigger from './edit/EditTrigger.vue';
 import EditGetText from './edit/EditGetText.vue';
-import EditTextInput from './edit/EditTextInput.vue';
 import EditTriggerEvent from './edit/EditTriggerEvent.vue';
 import EditScrollElement from './edit/EditScrollElement.vue';
 import EditAttributeValue from './edit/EditAttributeValue.vue';
@@ -27,9 +27,9 @@ import EditInteractionBase from './edit/EditInteractionBase.vue';
 
 export default {
   components: {
+    EditForms,
     EditTrigger,
     EditGetText,
-    EditTextInput,
     EditTriggerEvent,
     EditScrollElement,
     EditAttributeValue,

+ 64 - 0
src/components/newtab/workflow/edit/EditForms.vue

@@ -0,0 +1,64 @@
+<template>
+  <edit-interaction-base v-model:data="state">
+    <ui-select
+      v-model="state.type"
+      class="block w-full mt-4 mb-3"
+      placeholder="Form type"
+    >
+      <option v-for="form in forms" :key="form.id" :value="form.id">
+        {{ form.name }}
+      </option>
+    </ui-select>
+    <ui-checkbox
+      v-if="state.type === 'checkbox' || state.type === 'radio'"
+      v-model="state.selected"
+    >
+      Selected
+    </ui-checkbox>
+    <ui-textarea
+      v-if="state.type === 'text-field' || state.type === 'select'"
+      v-model="state.value"
+      placeholder="Value"
+      class="w-full"
+    />
+    <ui-input
+      v-if="state.type === 'text-field'"
+      v-model="state.delay"
+      label="Typing delay (millisecond)(0 to disable)"
+      placeholder="Delay"
+      class="w-full"
+      min="0"
+      type="number"
+    />
+  </edit-interaction-base>
+</template>
+<script setup>
+import { ref, watch } from 'vue';
+import { debounce } from '@/utils/helper';
+import EditInteractionBase from './EditInteractionBase.vue';
+
+const props = defineProps({
+  data: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+const emit = defineEmits(['update:data']);
+
+const forms = [
+  { id: 'text-field', name: 'Text field' },
+  { id: 'select', name: 'Select' },
+  { id: 'checkbox', name: 'Checkbox' },
+  { id: 'radio', name: 'Radio' },
+];
+
+const state = ref(props.data);
+
+watch(
+  state,
+  debounce((value) => {
+    emit('update:data', value);
+  }, 250),
+  { deep: true }
+);
+</script>

+ 0 - 55
src/components/newtab/workflow/edit/EditTextInput.vue

@@ -1,55 +0,0 @@
-<template>
-  <edit-interaction-base v-bind="{ data }" @change="updateData">
-    <ui-textarea
-      :model-value="data.text"
-      placeholder="Text"
-      class="mt-3 w-full"
-      @change="updateData({ text: $event })"
-    />
-    <ui-input
-      :model-value="data.delay"
-      label="Typing delay (millisecond)(0 to disable)"
-      placeholder="Delay"
-      class="w-full"
-      min="0"
-      type="number"
-      @change="updateData({ delay: +$event })"
-    />
-    <div class="mt-3">
-      <p>Trigger these event when input text:</p>
-      <div class="grid grid-cols-2 gap-2 mt-2">
-        <ui-checkbox
-          v-for="event in events"
-          :key="event.id"
-          :model-value="data[event.id]"
-          class="capitalize"
-          @change="updateData({ [event.id]: $event })"
-        >
-          {{ event.name }}
-        </ui-checkbox>
-      </div>
-    </div>
-  </edit-interaction-base>
-</template>
-<script setup>
-import EditInteractionBase from './EditInteractionBase.vue';
-
-const props = defineProps({
-  data: {
-    type: Object,
-    default: () => ({}),
-  },
-});
-const emit = defineEmits(['update:data']);
-
-const events = [
-  { id: 'changeEvent', name: 'Change' },
-  { id: 'inputEvent', name: 'input' },
-  { id: 'keyupEvent', name: 'keyup' },
-  { id: 'keydownEvent', name: 'keydown' },
-];
-
-function updateData(value) {
-  emit('update:data', { ...props.data, ...value });
-}
-</script>

+ 1 - 1
src/components/newtab/workflow/edit/EditTriggerEvent.vue

@@ -3,7 +3,7 @@
     <ui-select
       :model-value="data.eventName"
       class="w-full mt-2"
-      placeholder="Select event"
+      placeholder="Select an event"
       @change="handleEventChange"
     >
       <option v-for="event in eventList" :key="event.id" :value="event.id">

+ 10 - 75
src/content/blocks-handler.js

@@ -1,5 +1,6 @@
 /* eslint-disable consistent-return, no-param-reassign */
 import simulateEvent from '@/utils/simulate-event';
+import handleFormElement from '@/utils/handle-form-element';
 
 function handleElement(data, callback) {
   if (!data.selector) return null;
@@ -11,15 +12,8 @@ function handleElement(data, callback) {
   if (typeof callback === 'boolean' && callback) return element;
 
   if (data.multiple) {
-    element.forEach((el) => {
-      if (el.hasAttribute('ext-auto')) return;
-
-      el.setAttribute('ext-auto', '');
-      callback(el);
-    });
-  } else if (element && !element.hasAttribute('ext-auto')) {
-    element.setAttribute('ext-auto', '');
-
+    element.forEach(callback);
+  } else if (element) {
     callback(element);
   }
 }
@@ -81,62 +75,14 @@ export function attributeValue({ data }) {
   });
 }
 
-function inputText({ data, element, index = 0, callback }) {
-  const noDelay = data.delay === 0;
-  const currentChar = data.text[index];
-
-  if (noDelay) {
-    element.value = data.text;
-  } else {
-    element.value += currentChar;
-  }
-
-  const code = /\s/.test(currentChar)
-    ? 'Space'
-    : `key${currentChar.toUpperCase()}`;
-
-  console.log(code, currentChar, element.value);
-
-  if (data.keydownEvent) {
-    simulateEvent(element, 'keydown', { code, key: currentChar });
-  }
-  if (data.keyupEvent) {
-    simulateEvent(element, 'keyup', { code, key: currentChar });
-  }
-  if (data.changeEvent) {
-    element.dispatchEvent(new Event('change'));
-  }
-  if (data.inputEvent) {
-    element.dispatchEvent(
-      new InputEvent('input', { inputType: 'insertText', data: currentChar })
-    );
-  }
-
-  if (!noDelay && index + 1 !== data.text.length) {
-    setTimeout(() => {
-      inputText({ data, element, callback, index: index + 1 });
-    }, data.delay);
-  } else {
-    callback();
-  }
-}
-export function textInput({ data }) {
+export function forms({ data }) {
   return new Promise((resolve) => {
     const elements = handleElement(data, true);
-    const textFields = ['INPUT', 'TEXTAREA'];
-
+    console.log(elements, 'form');
     if (data.multiple) {
       const promises = Array.from(elements).map((element) => {
         return new Promise((eventResolve) => {
-          if (
-            textFields.includes(element.tagName) ||
-            !element.hasAttribute('ext-auto')
-          ) {
-            inputText({ data, element, callback: eventResolve });
-          } else {
-            element.setAttribute('ext-auto', '');
-            eventResolve();
-          }
+          handleFormElement(element, data, eventResolve);
         });
       });
 
@@ -144,21 +90,10 @@ export function textInput({ data }) {
         console.log('hola amigo');
         resolve('');
       });
-    } else if (
-      elements &&
-      textFields.includes(elements.tagName) &&
-      !elements.hasAttribute('ext-auto')
-    ) {
-      elements.setAttribute('ext-auto', '');
-
-      inputText({
-        data,
-        element: elements,
-        callback: () => {
-          console.log('hola amigo 2');
-          resolve('');
-        },
-      });
+    } else if (elements) {
+      handleFormElement(elements, data, resolve);
+    } else {
+      resolve();
     }
   });
 }

+ 65 - 0
src/utils/handle-form-element.js

@@ -0,0 +1,65 @@
+/* eslint-disable no-param-reassign */
+import simulateEvent from './simulate-event';
+
+function formEvent(element, data) {
+  if (data.type === 'text-field') {
+    const code = /\s/.test(data.value)
+      ? 'Space'
+      : `key${data.value.toUpperCase()}`;
+
+    simulateEvent(element, 'keydown', { code, key: data.value });
+    simulateEvent(element, 'keyup', { code, key: data.value });
+  }
+
+  element.dispatchEvent(new Event('change'));
+  element.dispatchEvent(
+    new InputEvent('input', { inputType: 'insertText', data: data.value })
+  );
+}
+function inputText({ data, element, index = 0, callback }) {
+  const noDelay = data.delay === 0;
+  const currentChar = data.value[index];
+
+  if (noDelay) {
+    element.value = data.value;
+  } else {
+    element.value += currentChar;
+  }
+
+  formEvent(element, { ...data, value: currentChar });
+
+  if (!noDelay && index + 1 !== data.value.length) {
+    setTimeout(() => {
+      inputText({ data, element, callback, index: index + 1 });
+    }, data.delay);
+  } else {
+    callback();
+  }
+}
+
+export default function (element, data, callback) {
+  const textFields = ['INPUT', 'TEXTAREA'];
+
+  if (data.type === 'text-field' && textFields.includes(element.tagName)) {
+    console.log('inputtt');
+    inputText({ data, element, callback });
+    return;
+  }
+
+  if (data.type === 'checkbox' || data.type === 'radio') {
+    console.log('checkbox|radio', data.selected);
+    element.checked = data.selected;
+    formEvent(element, { type: data.type, value: data.selected });
+    callback();
+    return;
+  }
+
+  if (data.type === 'select') {
+    element.value = data.value;
+    formEvent(element, data);
+    callback();
+    return;
+  }
+
+  callback();
+}

+ 7 - 8
src/utils/shared.js

@@ -127,11 +127,11 @@ export const tasks = {
       url: '',
     },
   },
-  'text-input': {
-    name: 'Text input',
+  forms: {
+    name: 'Forms',
     icon: 'riInputCursorMove',
     component: 'BlockBasic',
-    editComponent: 'EditTextInput',
+    editComponent: 'EditForms',
     category: 'interaction',
     inputs: 1,
     outputs: 1,
@@ -141,12 +141,11 @@ export const tasks = {
       description: '',
       selector: '',
       multiple: false,
-      text: '',
+      selected: true,
+      type: 'text-field',
+      value: '',
       delay: 0,
-      keyupEvent: true,
-      keydownEvent: true,
-      changeEvent: true,
-      inputEvent: true,
+      events: [],
     },
   },
   'repeat-task': {

+ 21 - 1
yarn.lock

@@ -1808,6 +1808,13 @@ caniuse-lite@^1.0.30001259, caniuse-lite@^1.0.30001260:
   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001261.tgz#96d89813c076ea061209a4e040d8dcf0c66a1d01"
   integrity sha512-vM8D9Uvp7bHIN0fZ2KQ4wnmYFpJo/Etb4Vwsuc+ka0tfGDHvOPrFm6S/7CCNLSOkAUjenT2HnUPESdOIL91FaA==
 
+cartesian@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/cartesian/-/cartesian-1.0.1.tgz#ae3fc8a63e2ba7e2c4989ce696207457bcae65af"
+  integrity sha1-rj/Ipj4rp+LEmJzmliB0V7yuZa8=
+  dependencies:
+    xtend "^4.0.1"
+
 chalk@^2.0.0, chalk@^2.0.1:
   version "2.4.2"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
@@ -2177,6 +2184,14 @@ css-select@^4.1.3:
     domutils "^2.6.0"
     nth-check "^2.0.0"
 
+css-selector-generator@^3.4.4:
+  version "3.4.4"
+  resolved "https://registry.yarnpkg.com/css-selector-generator/-/css-selector-generator-3.4.4.tgz#daba9fdd0d61de5efeda8cdafb57ae6f8defb44f"
+  integrity sha512-opjk54O5JZaKf8UrxUciqBdyIggH3D/TSZYI/AAJd0KNb6hBhjJtCI034rWDEHn0IIBsZEuAIuVR7N4j9n75lg==
+  dependencies:
+    cartesian "^1.0.1"
+    iselement "^1.1.4"
+
 css-unit-converter@^1.1.1:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.2.tgz#4c77f5a1954e6dbff60695ecb214e3270436ab21"
@@ -3956,6 +3971,11 @@ isarray@1.0.0, isarray@~1.0.0:
   resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
   integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
 
+iselement@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/iselement/-/iselement-1.1.4.tgz#7e55b52a8ebca50a7e2e80e5b8d2840f32353146"
+  integrity sha1-flW1Ko68pQp+LoDluNKEDzI1MUY=
+
 isexe@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@@ -6786,7 +6806,7 @@ ws@^6.2.1:
   dependencies:
     async-limiter "~1.0.0"
 
-xtend@^4.0.2:
+xtend@^4.0.1, xtend@^4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
   integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==