Browse Source

feat: select multiple elements in automa element selector

Ahmad Kholid 3 years ago
parent
commit
5e9685af79

+ 1 - 0
package.json

@@ -36,6 +36,7 @@
     "@vuex-orm/core": "^0.36.4",
     "compare-versions": "^4.1.2",
     "crypto-js": "^4.1.1",
+    "css-selector-generator": "^3.6.0",
     "dayjs": "^1.10.7",
     "defu": "^5.0.1",
     "drawflow": "^0.0.51",

+ 36 - 13
src/content/element-selector/App.vue

@@ -151,7 +151,7 @@
 </template>
 <script setup>
 import { reactive, ref, watch, inject, nextTick } from 'vue';
-import { finder } from '@medv/finder';
+import { getCssSelector } from 'css-selector-generator';
 import { debounce } from '@/utils/helper';
 import AppBlocks from './AppBlocks.vue';
 import AppSelector from './AppSelector.vue';
@@ -193,7 +193,9 @@ const cardRect = reactive({
 
 /* eslint-disable  no-use-before-define */
 const getElementSelector = (element) =>
-  state.selectorType === 'css' ? finder(element) : generateXPath(element);
+  state.selectorType === 'css'
+    ? getCssSelector(element)
+    : generateXPath(element);
 
 function generateXPath(element) {
   if (!element) return null;
@@ -299,7 +301,7 @@ function handleMouseMove({ clientX, clientY, target }) {
   Object.assign(hoverElementRect, getElementRect(target));
 }
 function handleClick(event) {
-  const { target, path } = event;
+  const { target, path, ctrlKey, shiftKey } = event;
 
   if (target === rootElement || state.hide || state.isExecuting) return;
 
@@ -310,16 +312,37 @@ function handleClick(event) {
     name,
     value,
   }));
-  state.selectedElements = [
-    {
-      ...getElementRect(target),
-      attributes,
-      element: target,
-      highlight: false,
-    },
-  ];
-
-  state.elSelector = getElementSelector(target);
+
+  let targetElement = target;
+  const targetElementDetail = {
+    ...getElementRect(target),
+    attributes,
+    element: target,
+    highlight: false,
+  };
+
+  if ((state.selectorType === 'css' && ctrlKey) || shiftKey) {
+    let elementIndex = -1;
+    const elements = state.selectedElements.map(({ element }, index) => {
+      if (element === targetElement) {
+        elementIndex = index;
+      }
+
+      return element;
+    });
+
+    if (elementIndex === -1) {
+      targetElement = [...elements, target];
+      state.selectedElements.push(targetElementDetail);
+    } else {
+      targetElement = elements.splice(elementIndex, 1);
+      state.selectedElements.splice(elementIndex, 1);
+    }
+  } else {
+    state.selectedElements = [targetElementDetail];
+  }
+
+  state.elSelector = getElementSelector(targetElement);
 
   selectedElement.index = 0;
   selectedElement.path = path;

+ 5 - 5
src/content/services/record-workflow.js

@@ -1,4 +1,4 @@
-import { finder } from '@medv/finder';
+import { getCssSelector } from 'css-selector-generator';
 import browser from 'webextension-polyfill';
 import { debounce } from '@/utils/helper';
 
@@ -27,7 +27,7 @@ function changeListener({ target }) {
   if (execludeInput) return;
 
   let block = null;
-  const selector = finder(target);
+  const selector = getCssSelector(target);
   const isSelectEl = target.tagName === 'SELECT';
   const elementName = target.ariaLabel || target.name;
 
@@ -94,7 +94,7 @@ function keyEventListener({
 
   if (isTextField) return;
 
-  const selector = finder(target);
+  const selector = getCssSelector(target);
 
   addBlock({
     id: 'trigger-event',
@@ -125,7 +125,7 @@ function clickListener(event) {
 
   if (isTextField) return;
 
-  const selector = finder(target);
+  const selector = getCssSelector(target);
 
   if (target.tagName === 'A') {
     if (event.ctrlKey || event.metaKey) return;
@@ -167,7 +167,7 @@ function clickListener(event) {
 const scrollListener = debounce(({ target }) => {
   const isDocument = target === document;
   const element = isDocument ? document.documentElement : target;
-  const selector = isDocument ? 'html' : finder(target);
+  const selector = isDocument ? 'html' : getCssSelector(target);
 
   addBlock((recording) => {
     const lastFlow = recording.flows[recording.flows.length - 1];

+ 1 - 1
src/popup/pages/Recording.vue

@@ -160,7 +160,7 @@ async function stopRecording() {
     const tabs = (await browser.tabs.query({})).filter((tab) =>
       tab.url.startsWith('http')
     );
-    await Promise.allSettled(
+    Promise.allSettled(
       tabs.map(({ id }) =>
         browser.tabs.sendMessage(id, { type: 'recording:stop' })
       )

+ 21 - 1
yarn.lock

@@ -2637,6 +2637,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"
@@ -3031,6 +3038,14 @@ css-select@^4.1.3:
     domutils "^2.6.0"
     nth-check "^2.0.0"
 
+css-selector-generator@^3.6.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/css-selector-generator/-/css-selector-generator-3.6.0.tgz#06293fb3d9303cc4b55f2d8e40aa6b85bf1c405b"
+  integrity sha512-t1wTGaASlIyCd5nYA0sptNgn97bkAWlZlVSvQKXbR9xhWANDvTu9blgAFyZ8db8xGyTzUIDYjngjAmwmMNM+KQ==
+  dependencies:
+    cartesian "^1.0.1"
+    iselement "^1.1.4"
+
 css-what@^5.0.0:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad"
@@ -4804,6 +4819,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"
@@ -7662,7 +7682,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==