Browse Source

feat: add selector settings

Ahmad Kholid 2 years ago
parent
commit
cd740803e5

+ 23 - 10
src/components/content/selector/SelectorQuery.vue

@@ -10,16 +10,27 @@
         <option value="css">CSS Selector</option>
         <option value="xpath">XPath</option>
       </ui-select>
-      <ui-button
-        v-if="selectorType === 'css'"
-        :class="{ 'text-primary': selectList }"
-        icon
-        class="ml-2"
-        title="Select a list of elements"
-        @click.stop.prevent="$emit('update:selectList', !selectList)"
-      >
-        <v-remixicon name="riListUnordered" />
-      </ui-button>
+      <template v-if="selectorType === 'css'">
+        <ui-button
+          :class="{ 'text-primary': selectList }"
+          icon
+          class="ml-2"
+          title="Select a list of elements"
+          @click.stop.prevent="$emit('update:selectList', !selectList)"
+        >
+          <v-remixicon name="riListUnordered" />
+        </ui-button>
+        <ui-button
+          icon
+          class="ml-2"
+          title="Selector settings"
+          @click="$emit('settings', !settingsActive)"
+        >
+          <v-remixicon
+            :name="settingsActive ? 'riCloseLine' : 'riSettings3Line'"
+          />
+        </ui-button>
+      </template>
     </div>
     <div class="mt-2 flex items-center">
       <ui-input
@@ -73,6 +84,7 @@ const props = defineProps({
     type: Boolean,
     default: false,
   },
+  settingsActive: Boolean,
 });
 defineEmits([
   'change',
@@ -80,6 +92,7 @@ defineEmits([
   'parent',
   'child',
   'selector',
+  'settings',
   'update:selectorType',
   'update:selectList',
 ]);

+ 8 - 1
src/components/content/shared/SharedElementSelector.vue

@@ -57,6 +57,10 @@ const props = defineProps({
     type: Array,
     default: () => [],
   },
+  selectorSettings: {
+    type: Object,
+    default: () => ({}),
+  },
   list: Boolean,
   hide: Boolean,
   pause: Boolean,
@@ -170,11 +174,13 @@ function retrieveElementsRect({ clientX, clientY, target: eventTarget }, type) {
         withAttributes: props.withAttributes,
       };
 
-      if (type === 'selected')
+      if (type === 'selected') {
         Object.assign(payload, {
           click: true,
           selectorType: props.selectorType,
+          selectorSettings: props.selectorSettings,
         });
+      }
 
       target.contentWindow.postMessage(payload, '*');
       frameElement = target;
@@ -221,6 +227,7 @@ function retrieveElementsRect({ clientX, clientY, target: eventTarget }, type) {
       hoveredElements,
       list: isSelectList,
       selectorType: props.selectorType,
+      selectorSettings: props.selectorSettings,
     });
 
     if (frameElement) {

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

@@ -23,7 +23,7 @@
           <edit-autocomplete class="mr-2">
             <ui-input
               v-model="filePaths[index]"
-              :placeholder="t('workflow.blocks.upload-file.filePath')"
+              placeholder="URL/File path/base64"
               autocomplete="off"
               class="w-full"
             />

+ 6 - 0
src/components/newtab/workflow/editor/EditorLocalActions.vue

@@ -586,6 +586,12 @@ async function publishWorkflow() {
     const result = await response.json();
 
     if (!response.ok) {
+      if (response.status === 404) {
+        await teamWorkflowStore.delete(teamId, props.workflow.id);
+        router.replace('/');
+        return;
+      }
+
       throw new Error(result.message);
     }
   } catch (error) {

+ 1 - 1
src/content/blocksHandler/handlerEventClick.js

@@ -5,7 +5,7 @@ import handleSelector from '../handleSelector';
 function eventClick(block) {
   return new Promise((resolve, reject) => {
     const dispatchClickEvents = (element, eventFn) => {
-      const eventOpts = { bubbles: true };
+      const eventOpts = { bubbles: true, view: window };
 
       element.dispatchEvent(new MouseEvent('mousedown', eventOpts));
       element.dispatchEvent(new MouseEvent('mouseup', eventOpts));

+ 88 - 3
src/content/elementSelector/App.vue

@@ -52,13 +52,19 @@
           v-model:selectorType="state.selectorType"
           v-model:selectList="state.selectList"
           :selector="state.elSelector"
+          :settings-active="state.showSettings"
           :selected-count="state.selectedElements.length"
+          @settings="state.showSettings = $event"
           @selector="updateSelector"
           @parent="selectElementPath('up')"
           @child="selectElementPath('down')"
         />
         <selector-elements-detail
-          v-if="!state.hide && state.selectedElements.length > 0"
+          v-if="
+            !state.showSettings &&
+            !state.hide &&
+            state.selectedElements.length > 0
+          "
           v-model:active-tab="state.activeTab"
           v-bind="{
             elSelector: state.elSelector,
@@ -68,6 +74,52 @@
           @highlight="toggleHighlightElement"
           @execute="state.isExecuting = $event"
         />
+        <div v-if="state.showSettings && !state.hide" class="mt-4">
+          <p class="font-semibold mb-4">Selector settings</p>
+          <ul class="space-y-4">
+            <li>
+              <label class="flex items-center space-x-2">
+                <ui-switch v-model="selectorSettings.idName" />
+                <p>Include element id</p>
+              </label>
+            </li>
+            <li>
+              <label class="flex items-center space-x-2">
+                <ui-switch v-model="selectorSettings.tagName" />
+                <p>Include tag name</p>
+              </label>
+            </li>
+            <li>
+              <label class="flex items-center space-x-2">
+                <ui-switch v-model="selectorSettings.className" />
+                <p>Include class name</p>
+              </label>
+            </li>
+            <li>
+              <label class="flex items-center space-x-2">
+                <ui-switch v-model="selectorSettings.attr" />
+                <p>Include attributes</p>
+              </label>
+              <template v-if="selectorSettings.attr">
+                <label
+                  class="ml-1 text-sm text-gray-600 mt-2 block"
+                  for="automa-attribute-names"
+                >
+                  Attribute names
+                </label>
+                <ui-textarea
+                  id="automa-attribute-names"
+                  v-model="selectorSettings.attrNames"
+                  label="Attribute name"
+                  placeholder="data-testid, aria-label, type"
+                />
+                <span class="text-sm">
+                  Use commas to separate the attribute
+                </span>
+              </template>
+            </li>
+          </ul>
+        </div>
       </div>
     </div>
   </div>
@@ -77,18 +129,29 @@
     :list="state.selectList"
     :selector-type="state.selectorType"
     :selected-els="state.selectedElements"
+    :selector-settings="getSelectorOptions(selectorSettings)"
     with-attributes
     @selected="onElementsSelected"
   />
 </template>
 <script setup>
-import { reactive, ref, watch, inject, onMounted, onBeforeUnmount } from 'vue';
+import {
+  reactive,
+  ref,
+  watch,
+  inject,
+  onMounted,
+  onBeforeUnmount,
+  toRaw,
+} from 'vue';
+import browser from 'webextension-polyfill';
 import { debounce } from '@/utils/helper';
 import findSelector from '@/lib/findSelector';
 import FindElement from '@/utils/FindElement';
 import SelectorQuery from '@/components/content/selector/SelectorQuery.vue';
 import SharedElementSelector from '@/components/content/shared/SharedElementSelector.vue';
 import SelectorElementsDetail from '@/components/content/selector/SelectorElementsDetail.vue';
+import getSelectorOptions from './getSelectorOptions';
 import { getElementRect } from '../utils';
 
 const originalFontSize = document.documentElement.style.fontSize;
@@ -109,6 +172,7 @@ const state = reactive({
   selectList: false,
   isExecuting: false,
   selectElements: [],
+  showSettings: false,
   selectorType: 'css',
   selectedElements: [],
   activeTab: 'attributes',
@@ -119,6 +183,13 @@ const cardRect = reactive({
   height: 0,
   width: 0,
 });
+const selectorSettings = reactive({
+  idName: true,
+  tagName: true,
+  attr: true,
+  className: true,
+  attrNames: 'data-testid',
+});
 
 const cardElementObserver = new ResizeObserver(([entry]) => {
   const { height, width } = entry.contentRect;
@@ -227,7 +298,7 @@ function selectElementPath(type) {
   state.selectedElements = [getElementRect(element, true)];
   state.elSelector = selectedElement.cache.has(element)
     ? selectedElement.cache.get(element)
-    : findSelector(element);
+    : findSelector(element, getSelectorOptions(selectorSettings));
 }
 function onMouseup() {
   if (state.isDragging) state.isDragging = false;
@@ -278,8 +349,22 @@ watch(
     document.body.toggleAttribute('automa-isDragging', value);
   }
 );
+watch(
+  selectorSettings,
+  (settings) => {
+    browser.storage.local.set({
+      selectorSettings: toRaw(settings),
+    });
+  },
+  { deep: true }
+);
 
 onMounted(() => {
+  browser.storage.local.get('selectorSettings').then((storage) => {
+    const settings = storage.selectorSettings || {};
+    Object.assign(selectorSettings, settings);
+  });
+
   setTimeout(() => {
     const { height, width } = cardEl.value.getBoundingClientRect();
 

+ 2 - 0
src/content/elementSelector/compsUi.js

@@ -5,6 +5,7 @@ import UiInput from '@/components/ui/UiInput.vue';
 import UiButton from '@/components/ui/UiButton.vue';
 import UiSelect from '@/components/ui/UiSelect.vue';
 import UiExpand from '@/components/ui/UiExpand.vue';
+import UiSwitch from '@/components/ui/UiSwitch.vue';
 import UiTextarea from '@/components/ui/UiTextarea.vue';
 import UiCheckbox from '@/components/ui/UiCheckbox.vue';
 import UiTabPanel from '@/components/ui/UiTabPanel.vue';
@@ -17,6 +18,7 @@ export default function (app) {
   app.component('UiInput', UiInput);
   app.component('UiButton', UiButton);
   app.component('UiSelect', UiSelect);
+  app.component('UiSwitch', UiSwitch);
   app.component('UiExpand', UiExpand);
   app.component('UiTextarea', UiTextarea);
   app.component('UiCheckbox', UiCheckbox);

+ 5 - 2
src/content/elementSelector/generateElementsSelector.js

@@ -5,13 +5,15 @@ export default function ({
   list,
   target,
   selectorType,
-  hoveredElements,
   frameElement,
+  hoveredElements,
+  selectorSettings,
 }) {
   let selector = '';
 
+  const selectorOptions = selectorSettings || {};
   const [selectedElement] = hoveredElements;
-  const finderOptions = {};
+  const finderOptions = { ...selectorOptions };
   let documentCtx = document;
 
   if (frameElement) {
@@ -25,6 +27,7 @@ export default function ({
     if (isInList) {
       const childSelector = findSelector(target, {
         root: isInList,
+        ...selectorOptions,
         idName: () => false,
       });
       const listSelector = isInList.getAttribute('automa-el-list');

+ 10 - 0
src/content/elementSelector/getSelectorOptions.js

@@ -0,0 +1,10 @@
+export default function ({ idName, tagName, className, attr, attrNames }) {
+  const attrs = attr ? attrNames.split(',').map((item) => item.trim()) : false;
+
+  return {
+    idName: () => idName ?? true,
+    tagName: () => tagName ?? true,
+    className: () => className ?? true,
+    attr: (name) => (attr ? false : attrs.includes(name)),
+  };
+}

+ 2 - 0
src/content/elementSelector/icons.js

@@ -5,6 +5,7 @@ import {
   riEyeOffLine,
   riFileCopyLine,
   riDragMoveLine,
+  riSettings3Line,
   riListUnordered,
   riArrowLeftLine,
   riArrowLeftSLine,
@@ -19,6 +20,7 @@ export default {
   riEyeOffLine,
   riFileCopyLine,
   riDragMoveLine,
+  riSettings3Line,
   riListUnordered,
   riArrowLeftLine,
   riArrowLeftSLine,

+ 5 - 1
src/content/elementSelector/listSelector.js

@@ -83,7 +83,10 @@ export function getElementList(el, maxDepth = 50, paths = []) {
   return siblings;
 }
 
-export default function (target, { frameElement, onlyInList } = {}) {
+export default function (
+  target,
+  { frameElement, onlyInList, selectorSettings } = {}
+) {
   if (!target) return [];
 
   const automaListEl = target.closest('[automa-el-list]');
@@ -98,6 +101,7 @@ export default function (target, { frameElement, onlyInList } = {}) {
 
     const childSelector = findSelector(target, {
       root: automaListEl,
+      ...(selectorSettings || {}),
       idName: () => false,
     });
     const elements = documentCtx.querySelectorAll(

+ 0 - 1
src/lib/findSelector.js

@@ -1,6 +1,5 @@
 import { finder as finderLib } from '@medv/finder';
 
-// const ariaAttrs = ['aria-label', 'aria-labelledby'];
 const ariaAttrs = ['data-testid'];
 
 export const finder = finderLib;