Browse Source

feat: add select element button

Ahmad Kholid 2 years ago
parent
commit
40dca5dcf0

+ 3 - 1
src/components/content/selector/SelectorElementsDetail.vue

@@ -1,5 +1,6 @@
 <template>
 <template>
   <ui-tabs
   <ui-tabs
+    v-if="!hideBlocks || selectElements.length > 0"
     :model-value="activeTab"
     :model-value="activeTab"
     class="mt-2"
     class="mt-2"
     fill
     fill
@@ -7,7 +8,7 @@
   >
   >
     <ui-tab value="attributes"> Attributes </ui-tab>
     <ui-tab value="attributes"> Attributes </ui-tab>
     <ui-tab v-if="selectElements.length > 0" value="options"> Options </ui-tab>
     <ui-tab v-if="selectElements.length > 0" value="options"> Options </ui-tab>
-    <ui-tab value="blocks"> Blocks </ui-tab>
+    <ui-tab v-if="!hideBlocks" value="blocks"> Blocks </ui-tab>
   </ui-tabs>
   </ui-tabs>
   <ui-tab-panels
   <ui-tab-panels
     :model-value="activeTab"
     :model-value="activeTab"
@@ -118,6 +119,7 @@ const props = defineProps({
     type: String,
     type: String,
     default: '',
     default: '',
   },
   },
+  hideBlocks: Boolean,
 });
 });
 defineEmits(['update:activeTab', 'execute', 'highlight', 'update']);
 defineEmits(['update:activeTab', 'execute', 'highlight', 'update']);
 
 

+ 26 - 11
src/components/newtab/workflow/edit/EditInteractionBase.vue

@@ -10,17 +10,25 @@
         @change="updateData({ description: $event })"
         @change="updateData({ description: $event })"
       />
       />
       <slot name="prepend:selector" />
       <slot name="prepend:selector" />
-      <ui-select
-        v-if="!hideSelector"
-        :model-value="data.findBy || 'cssSelector'"
-        :placeholder="t('workflow.blocks.base.findElement.placeholder')"
-        class="w-full mb-2"
-        @change="updateData({ findBy: $event })"
-      >
-        <option v-for="type in selectorTypes" :key="type" :value="type">
-          {{ t(`workflow.blocks.base.findElement.options.${type}`) }}
-        </option>
-      </ui-select>
+      <div v-if="!hideSelector" class="flex items-center mb-2">
+        <ui-select
+          :model-value="data.findBy || 'cssSelector'"
+          :placeholder="t('workflow.blocks.base.findElement.placeholder')"
+          class="flex-1 mr-2"
+          @change="updateData({ findBy: $event })"
+        >
+          <option v-for="type in selectorTypes" :key="type" :value="type">
+            {{ t(`workflow.blocks.base.findElement.options.${type}`) }}
+          </option>
+        </ui-select>
+        <ui-button
+          v-tooltip.group="t('workflow.blocks.base.element.select')"
+          icon
+          @click="selectElement"
+        >
+          <v-remixicon name="riFocus3Line" />
+        </ui-button>
+      </div>
       <edit-autocomplete v-if="!hideSelector" class="mb-1">
       <edit-autocomplete v-if="!hideSelector" class="mb-1">
         <ui-input
         <ui-input
           v-if="!hideSelector"
           v-if="!hideSelector"
@@ -88,6 +96,7 @@
 <script setup>
 <script setup>
 import { onMounted } from 'vue';
 import { onMounted } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useI18n } from 'vue-i18n';
+import elementSelector from '@/newtab/utils/elementSelector';
 import EditAutocomplete from './EditAutocomplete.vue';
 import EditAutocomplete from './EditAutocomplete.vue';
 
 
 const props = defineProps({
 const props = defineProps({
@@ -125,6 +134,12 @@ function updateData(value) {
   emit('update:data', payload);
   emit('update:data', payload);
   emit('change', payload);
   emit('change', payload);
 }
 }
+function selectElement() {
+  /* eslint-disable-next-line */
+  elementSelector.selectElement().then((selector) => {
+    updateData({ selector });
+  });
+}
 
 
 onMounted(() => {
 onMounted(() => {
   if (!props.data.findBy) {
   if (!props.data.findBy) {

+ 44 - 11
src/content/elementSelector/App.vue

@@ -23,21 +23,15 @@
         />
         />
       </div>
       </div>
       <div class="flex px-4 pt-4 items-center">
       <div class="flex px-4 pt-4 items-center">
-        <ui-tabs
-          v-if="false"
-          v-model="mainActiveTab"
-          type="fill"
-          class="main-tab"
-        >
-          <ui-tab value="selector"> Selector </ui-tab>
-          <ui-tab value="workflow"> Workflow </ui-tab>
-        </ui-tabs>
         <p class="text-lg font-semibold">Automa</p>
         <p class="text-lg font-semibold">Automa</p>
         <div class="flex-grow"></div>
         <div class="flex-grow"></div>
         <button
         <button
           class="mr-2 hoverable p-1 rounded-md transition"
           class="mr-2 hoverable p-1 rounded-md transition"
           @mousedown.stop.prevent
           @mousedown.stop.prevent
-          @click.stop.prevent="state.hide = !state.hide"
+          @click.stop.prevent="
+            state.hide = !state.hide;
+            clearConnectedPort();
+          "
         >
         >
           <v-remixicon :name="state.hide ? 'riEyeOffLine' : 'riEyeLine'" />
           <v-remixicon :name="state.hide ? 'riEyeOffLine' : 'riEyeLine'" />
         </button>
         </button>
@@ -61,6 +55,14 @@
           @parent="selectElementPath('up')"
           @parent="selectElementPath('up')"
           @child="selectElementPath('down')"
           @child="selectElementPath('down')"
         />
         />
+        <ui-button
+          v-if="state.isSelectBlockElement && state.elSelector"
+          variant="accent"
+          class="w-full mt-4"
+          @click="saveSelector"
+        >
+          Select Element
+        </ui-button>
         <selector-elements-detail
         <selector-elements-detail
           v-if="
           v-if="
             !state.showSettings &&
             !state.showSettings &&
@@ -71,6 +73,7 @@
           v-bind="{
           v-bind="{
             elSelector: state.elSelector,
             elSelector: state.elSelector,
             selectElements: state.selectElements,
             selectElements: state.selectElements,
+            hideBlocks: state.isSelectBlockElement,
             selectedElements: state.selectedElements,
             selectedElements: state.selectedElements,
           }"
           }"
           @highlight="toggleHighlightElement"
           @highlight="toggleHighlightElement"
@@ -161,6 +164,7 @@ import SelectorElementsDetail from '@/components/content/selector/SelectorElemen
 import getSelectorOptions from './getSelectorOptions';
 import getSelectorOptions from './getSelectorOptions';
 import { getElementRect } from '../utils';
 import { getElementRect } from '../utils';
 
 
+let connectedPort = null;
 const originalFontSize = document.documentElement.style.fontSize;
 const originalFontSize = document.documentElement.style.fontSize;
 const selectedElement = {
 const selectedElement = {
   path: [],
   path: [],
@@ -171,7 +175,6 @@ const selectedElement = {
 const rootElement = inject('rootElement');
 const rootElement = inject('rootElement');
 
 
 const cardEl = ref('cardEl');
 const cardEl = ref('cardEl');
-const mainActiveTab = ref('selector');
 const state = reactive({
 const state = reactive({
   hide: false,
   hide: false,
   elSelector: '',
   elSelector: '',
@@ -183,6 +186,7 @@ const state = reactive({
   selectorType: 'css',
   selectorType: 'css',
   selectedElements: [],
   selectedElements: [],
   activeTab: 'attributes',
   activeTab: 'attributes',
+  isSelectBlockElement: false,
 });
 });
 const cardRect = reactive({
 const cardRect = reactive({
   x: 0,
   x: 0,
@@ -335,12 +339,31 @@ function destroy() {
 
 
   document.documentElement.style.fontSize = originalFontSize;
   document.documentElement.style.fontSize = originalFontSize;
 }
 }
+function clearConnectedPort() {
+  if (!connectedPort) return;
+
+  connectedPort = null;
+  state.isSelectBlockElement = false;
+}
+function onVisivilityChange() {
+  if (!connectedPort || document.visibilityState !== 'hidden') return;
+
+  clearConnectedPort();
+}
+function saveSelector() {
+  if (!connectedPort) return;
+
+  connectedPort.postMessage(state.elSelector);
+  clearConnectedPort();
+  destroy();
+}
 function attachListeners() {
 function attachListeners() {
   cardElementObserver.observe(cardEl.value);
   cardElementObserver.observe(cardEl.value);
 
 
   window.addEventListener('message', onMessage);
   window.addEventListener('message', onMessage);
   window.addEventListener('mouseup', onMouseup);
   window.addEventListener('mouseup', onMouseup);
   window.addEventListener('mousemove', onMousemove);
   window.addEventListener('mousemove', onMousemove);
+  document.addEventListener('visibilitychange', onVisivilityChange);
 }
 }
 function detachListeners() {
 function detachListeners() {
   cardElementObserver.disconnect();
   cardElementObserver.disconnect();
@@ -348,6 +371,7 @@ function detachListeners() {
   window.removeEventListener('message', onMessage);
   window.removeEventListener('message', onMessage);
   window.removeEventListener('mouseup', onMouseup);
   window.removeEventListener('mouseup', onMouseup);
   window.removeEventListener('mousemove', onMousemove);
   window.removeEventListener('mousemove', onMousemove);
+  document.removeEventListener('visibilitychange', onVisivilityChange);
 }
 }
 
 
 watch(
 watch(
@@ -366,6 +390,15 @@ watch(
   { deep: true }
   { deep: true }
 );
 );
 
 
+browser.runtime.onConnect.addListener((port) => {
+  clearConnectedPort();
+
+  connectedPort = port;
+  state.isSelectBlockElement = true;
+
+  port.onDisconnect.addListener(clearConnectedPort);
+});
+
 onMounted(() => {
 onMounted(() => {
   browser.storage.local.get('selectorSettings').then((storage) => {
   browser.storage.local.get('selectorSettings').then((storage) => {
     const settings = storage.selectorSettings || {};
     const settings = storage.selectorSettings || {};

+ 4 - 0
src/locales/en/blocks.json

@@ -18,6 +18,10 @@
         "noPermission": "Automa don't have enough permission to do this action",
         "noPermission": "Automa don't have enough permission to do this action",
         "grantPermission": "Grant permission",
         "grantPermission": "Grant permission",
         "action": "Action",
         "action": "Action",
+        "element": {
+          "select": "Select an element",
+          "verify": "Verify element"
+        },
         "settings": {
         "settings": {
           "title": "Block settings",
           "title": "Block settings",
           "line": {
           "line": {

+ 75 - 0
src/newtab/utils/elementSelector.js

@@ -0,0 +1,75 @@
+import browser from 'webextension-polyfill';
+
+async function initElementSelector(tab = null) {
+  let activeTab = tab;
+
+  if (!tab) {
+    const [queryTab] = await browser.tabs.query({
+      active: true,
+      url: '*://*/*',
+    });
+    activeTab = queryTab;
+  }
+
+  const result = await browser.tabs.sendMessage(activeTab.id, {
+    type: 'automa-element-selector',
+  });
+
+  if (!result) {
+    await browser.scripting.executeScript({
+      target: {
+        allFrames: true,
+        tabId: activeTab.id,
+      },
+      files: ['./elementSelector.bundle.js'],
+    });
+  }
+
+  await browser.tabs.update(activeTab.id, { active: true });
+  await browser.windows.update(activeTab.windowId, { focused: true });
+}
+
+export function verifySelector() {}
+
+export async function selectElement(name) {
+  const [tab] = await browser.tabs.query({
+    active: true,
+    url: '*://*/*',
+  });
+  if (!tab) throw new Error('No active tab');
+
+  await initElementSelector(tab);
+
+  const port = await browser.tabs.connect(tab.id, { name });
+  const getSelector = () => {
+    return new Promise((resolve, reject) => {
+      port.onDisconnect.addListener(() => {
+        reject(new Error('Port closed'));
+      });
+      port.onMessage.addListener(async (message) => {
+        try {
+          const [currentTab] = await browser.tabs.query({
+            active: true,
+            currentWindow: true,
+          });
+          await browser.windows.update(currentTab.windowId, {
+            focused: true,
+          });
+        } catch (error) {
+          console.error(error);
+        } finally {
+          resolve(message);
+        }
+      });
+    });
+  };
+
+  const selector = await getSelector();
+
+  return selector;
+}
+
+export default {
+  selectElement,
+  verifySelector,
+};