Browse Source

refactor: edit trigger component

Ahmad Kholid 3 years ago
parent
commit
b1f97f8d7e

+ 21 - 336
src/components/newtab/workflow/edit/EditTrigger.vue

@@ -11,209 +11,30 @@
       :model-value="data.type || 'manual'"
       :model-value="data.type || 'manual'"
       :placeholder="t('workflow.blocks.trigger.forms.triggerWorkflow')"
       :placeholder="t('workflow.blocks.trigger.forms.triggerWorkflow')"
       class="w-full"
       class="w-full"
-      @change="handleSelectChange"
+      @change="updateData({ type: $event })"
     >
     >
-      <option v-for="trigger in triggers" :key="trigger" :value="trigger">
+      <option v-for="(_, trigger) in triggers" :key="trigger" :value="trigger">
         {{ t(`workflow.blocks.trigger.items.${trigger}`) }}
         {{ t(`workflow.blocks.trigger.items.${trigger}`) }}
       </option>
       </option>
     </ui-select>
     </ui-select>
     <transition-expand mode="out-in">
     <transition-expand mode="out-in">
-      <div v-if="data.type === 'interval'">
-        <div class="flex items-center mt-1">
-          <ui-input
-            :model-value="data.interval"
-            :label="t('workflow.blocks.trigger.forms.interval')"
-            type="number"
-            class="w-full"
-            placeholder="1-120"
-            min="1"
-            max="120"
-            @change="
-              updateIntervalInput($event, { key: 'interval', min: 1, max: 120 })
-            "
-          />
-          <ui-input
-            v-if="!data.fixedDelay"
-            :model-value="data.delay"
-            type="number"
-            class="w-full ml-2"
-            :label="t('workflow.blocks.trigger.forms.delay')"
-            min="0"
-            max="20"
-            placeholder="0-20"
-            @change="
-              updateIntervalInput($event, { key: 'delay', min: 0, max: 20 })
-            "
-          />
-        </div>
-        <ui-checkbox
-          :model-value="data.fixedDelay"
-          block
-          class="mt-2"
-          @change="updateData({ fixedDelay: $event })"
-        >
-          {{ t('workflow.blocks.trigger.fixedDelay') }}
-        </ui-checkbox>
-      </div>
-      <div v-else-if="data.type === 'date'" class="mt-2">
-        <ui-input
-          :model-value="data.date"
-          :max="maxDate"
-          :min="minDate"
-          :placeholder="t('workflow.blocks.trigger.forms.date')"
-          class="w-full"
-          type="date"
-          @change="updateDate({ date: $event })"
+      <keep-alive>
+        <component
+          :is="triggers[data.type]"
+          :data="data"
+          @update="updateData"
         />
         />
-        <ui-input
-          :model-value="data.time"
-          :placeholder="t('workflow.blocks.trigger.forms.time')"
-          type="time"
-          class="w-full mt-2"
-          @change="updateData({ time: $event || '00:00' })"
-        />
-      </div>
-      <div v-else-if="data.type === 'specific-day'" class="mt-4">
-        <ui-popover
-          :options="{ animation: null }"
-          trigger-width
-          class="w-full mb-2"
-          trigger-class="w-full"
-        >
-          <template #trigger>
-            <ui-button class="w-full">
-              <p class="text-left flex-1 text-overflow mr-2">
-                {{
-                  tempDate.days.length === 0
-                    ? t('workflow.blocks.trigger.selectDay')
-                    : getDaysText(tempDate.days)
-                }}
-              </p>
-              <v-remixicon
-                size="28"
-                name="riArrowDropDownLine"
-                class="text-gray-600 dark:text-gray-200 -mr-2"
-              />
-            </ui-button>
-          </template>
-          <div class="grid gap-2 grid-cols-2">
-            <ui-checkbox
-              v-for="(day, id) in days"
-              :key="id"
-              :model-value="data.days?.includes(id)"
-              @change="onSelectDayChange($event, id)"
-            >
-              {{ t(`workflow.blocks.trigger.days.${id}`) }}
-            </ui-checkbox>
-          </div>
-        </ui-popover>
-        <div class="flex items-center">
-          <ui-input v-model="tempDate.time" type="time" class="flex-1 mr-2" />
-          <ui-button variant="accent" @click="addTime">
-            {{ t('workflow.blocks.trigger.addTime') }}
-          </ui-button>
-        </div>
-        <div class="my-2">
-          <ui-expand
-            v-for="(day, index) in sortedDaysArr"
-            :key="day.id"
-            header-class="focus:ring-0 flex items-center py-2 w-full group text-left"
-            type="time"
-            class="w-full"
-          >
-            <template #header>
-              <p class="flex-1">
-                {{ t(`workflow.blocks.trigger.days.${day.id}`) }}
-              </p>
-              <span class="text-gray-600 dark:text-gray-200">
-                <v-remixicon
-                  name="riDeleteBin7Line"
-                  class="mr-1 group invisible group-hover:visible inline-block"
-                  @click="daysArr.splice(index, 1)"
-                />
-                {{ day.times.length }}x
-              </span>
-            </template>
-            <div class="grid grid-cols-2 gap-1 mb-1">
-              <div
-                v-for="(time, timeIndex) in day.times"
-                :key="time"
-                class="flex items-center px-4 py-2 border rounded-lg group"
-              >
-                <span class="flex-1"> {{ formatTime(time) }} </span>
-                <v-remixicon
-                  name="riDeleteBin7Line"
-                  class="cursor-pointer"
-                  @click="removeDayTime(index, timeIndex)"
-                />
-              </div>
-            </div>
-          </ui-expand>
-        </div>
-      </div>
-      <div v-else-if="data.type === 'visit-web'" class="mt-2">
-        <ui-input
-          :model-value="data.url"
-          :placeholder="t('workflow.blocks.trigger.forms.url')"
-          class="w-full"
-          @change="updateData({ url: $event })"
-        />
-        <ui-checkbox
-          :model-value="data.isUrlRegex"
-          class="mt-1"
-          @change="updateData({ isUrlRegex: $event })"
-        >
-          {{ t('workflow.blocks.trigger.useRegex') }}
-        </ui-checkbox>
-      </div>
-      <div v-else-if="data.type === 'keyboard-shortcut'" class="mt-2">
-        <div class="flex items-center mb-2">
-          <ui-input
-            :model-value="recordKeys.keys"
-            readonly
-            class="flex-1 mr-2"
-            :placeholder="t('workflow.blocks.trigger.forms.shortcut')"
-          />
-          <ui-button
-            v-tooltip="
-              t(
-                `workflow.blocks.trigger.shortcut.${
-                  recordKeys.isRecording ? 'stopRecord' : 'tooltip'
-                }`
-              )
-            "
-            icon
-            @click="toggleRecordKeys"
-          >
-            <v-remixicon
-              :name="
-                recordKeys.isRecording ? 'riStopLine' : 'riRecordCircleLine'
-              "
-            />
-          </ui-button>
-        </div>
-        <ui-checkbox
-          :model-value="data.activeInInput"
-          class="mb-1"
-          :title="t('workflow.blocks.trigger.shortcut.checkboxTitle')"
-          @change="updateData({ activeInInput: $event })"
-        >
-          {{ t('workflow.blocks.trigger.shortcut.checkbox') }}
-        </ui-checkbox>
-        <p class="mt-4 leading-tight text-gray-600 dark:text-gray-200">
-          {{ t('workflow.blocks.trigger.shortcut.note') }}
-        </p>
-      </div>
+      </keep-alive>
     </transition-expand>
     </transition-expand>
   </div>
   </div>
 </template>
 </template>
 <script setup>
 <script setup>
-import { reactive, ref, computed, watch, onMounted, onUnmounted } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useI18n } from 'vue-i18n';
-import { useToast } from 'vue-toastification';
-import dayjs from 'dayjs';
-import { isObject } from '@/utils/helper';
-import { recordShortcut } from '@/utils/recordKeys';
+import TriggerDate from './Trigger/TriggerDate.vue';
+import TriggerInterval from './Trigger/TriggerInterval.vue';
+import TriggerVisitWeb from './Trigger/TriggerVisitWeb.vue';
+import TriggerSpecificDay from './Trigger/TriggerSpecificDay.vue';
+import TriggerKeyboardShortcut from './Trigger/TriggerKeyboardShortcut.vue';
 
 
 const props = defineProps({
 const props = defineProps({
   data: {
   data: {
@@ -224,154 +45,18 @@ const props = defineProps({
 const emit = defineEmits(['update:data']);
 const emit = defineEmits(['update:data']);
 
 
 const { t } = useI18n();
 const { t } = useI18n();
-const toast = useToast();
 
 
-const triggers = [
-  'manual',
-  'interval',
-  'date',
-  'specific-day',
-  'on-startup',
-  'visit-web',
-  'keyboard-shortcut',
-];
-const days = {
-  0: 'Sunday',
-  1: 'Monday',
-  2: 'Tuesday',
-  3: 'Wednesday',
-  4: 'Thursday',
-  5: 'Friday',
-  6: 'Saturday',
+const triggers = {
+  manual: null,
+  interval: TriggerInterval,
+  date: TriggerDate,
+  'specific-day': TriggerSpecificDay,
+  'on-startup': null,
+  'visit-web': TriggerVisitWeb,
+  'keyboard-shortcut': TriggerKeyboardShortcut,
 };
 };
-const maxDate = dayjs().add(30, 'day').format('YYYY-MM-DD');
-const minDate = dayjs().format('YYYY-MM-DD');
-
-const recordKeys = reactive({
-  isRecording: false,
-  keys: props.data.shortcut,
-});
-const tempDate = reactive({
-  days: [],
-  time: '00:00',
-});
-const daysArr = ref(null);
-
-const sortedDaysArr = computed(() =>
-  daysArr.value ? daysArr.value.slice().sort((a, b) => a.id - b.id) : []
-);
 
 
 function updateData(value) {
 function updateData(value) {
   emit('update:data', { ...props.data, ...value });
   emit('update:data', { ...props.data, ...value });
 }
 }
-function getDaysText(dayIds) {
-  return dayIds
-    .map((day) => t(`workflow.blocks.trigger.days.${day}`))
-    .join(', ');
-}
-function formatTime(time) {
-  const [hour, minute] = time.split(':');
-
-  return dayjs().hour(hour).minute(minute).format('hh:mm A');
-}
-function removeDayTime(index, timeIndex) {
-  daysArr.value[index].times.splice(timeIndex, 1);
-
-  if (daysArr.value[index].times.length === 0) {
-    daysArr.value.splice(index, 1);
-  }
-}
-function onSelectDayChange(value, id) {
-  if (value) tempDate.days.push(+id);
-  else tempDate.days.splice(tempDate.days.indexOf(+id), 1);
-}
-function addTime() {
-  tempDate.days.forEach((dayId) => {
-    const dayIndex = daysArr.value.findIndex(({ id }) => id === dayId);
-
-    if (dayIndex === -1) {
-      daysArr.value.push({
-        id: dayId,
-        times: [tempDate.time],
-      });
-    } else {
-      const isTimeExist = daysArr.value[dayIndex].times.includes(tempDate.time);
-
-      if (isTimeExist) {
-        const message = t('workflow.blocks.trigger.timeExist', {
-          time: formatTime(tempDate.time),
-          day: t(`workflow.blocks.trigger.days.${dayId}`),
-        });
-
-        toast.error(message);
-
-        return;
-      }
-
-      daysArr.value[dayIndex].times.push(tempDate.time);
-    }
-  });
-}
-function handleKeydownEvent(event) {
-  recordShortcut(event, (keys) => {
-    recordKeys.keys = keys.join('+');
-    updateData({ shortcut: recordKeys.keys });
-  });
-}
-function toggleRecordKeys() {
-  if (recordKeys.isRecording) {
-    window.removeEventListener('keydown', handleKeydownEvent);
-  } else {
-    window.addEventListener('keydown', handleKeydownEvent);
-  }
-
-  recordKeys.isRecording = !recordKeys.isRecording;
-}
-function handleSelectChange(type) {
-  if (recordKeys.isRecording) {
-    window.removeEventListener('keydown', handleKeydownEvent);
-    recordKeys.isRecording = false;
-  }
-
-  updateData({ type });
-}
-function updateIntervalInput(value, { key, min, max }) {
-  let num = +value;
-
-  if (num < min) num = min;
-  else if (num > max) num = max;
-
-  updateData({ [key]: num });
-}
-function updateDate(value) {
-  if (!value) return;
-
-  let date = value?.date ?? minDate;
-
-  if (dayjs(minDate).isAfter(date)) date = minDate;
-  else if (dayjs(maxDate).isBefore(date)) date = maxDate;
-
-  updateData({ date });
-}
-
-watch(
-  daysArr,
-  (value, oldValue) => {
-    if (!oldValue) return;
-
-    updateData({ days: value });
-  },
-  { deep: true }
-);
-
-onMounted(() => {
-  const isStringDay =
-    props.data.days.length > 0 && !isObject(props.data.days[0]);
-  daysArr.value = isStringDay
-    ? props.data.days.map((day) => ({ id: day, times: [props.data.time] }))
-    : props.data.days;
-});
-onUnmounted(() => {
-  window.removeEventListener('keydown', handleKeydownEvent);
-});
 </script>
 </script>

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

@@ -63,11 +63,11 @@ import { useI18n } from 'vue-i18n';
 import { eventList } from '@/utils/shared';
 import { eventList } from '@/utils/shared';
 import { toCamelCase } from '@/utils/helper';
 import { toCamelCase } from '@/utils/helper';
 import EditInteractionBase from './EditInteractionBase.vue';
 import EditInteractionBase from './EditInteractionBase.vue';
-import TriggerEventMouse from './TriggerEventMouse.vue';
-import TriggerEventTouch from './TriggerEventTouch.vue';
-import TriggerEventWheel from './TriggerEventWheel.vue';
-import TriggerEventInput from './TriggerEventInput.vue';
-import TriggerEventKeyboard from './TriggerEventKeyboard.vue';
+import TriggerEventMouse from './TriggerEvent/TriggerEventMouse.vue';
+import TriggerEventTouch from './TriggerEvent/TriggerEventTouch.vue';
+import TriggerEventWheel from './TriggerEvent/TriggerEventWheel.vue';
+import TriggerEventInput from './TriggerEvent/TriggerEventInput.vue';
+import TriggerEventKeyboard from './TriggerEvent/TriggerEventKeyboard.vue';
 
 
 const props = defineProps({
 const props = defineProps({
   data: {
   data: {

+ 47 - 0
src/components/newtab/workflow/edit/Trigger/TriggerDate.vue

@@ -0,0 +1,47 @@
+<template>
+  <div class="mt-2">
+    <ui-input
+      :model-value="data.date"
+      :max="maxDate"
+      :min="minDate"
+      :placeholder="t('workflow.blocks.trigger.forms.date')"
+      class="w-full"
+      type="date"
+      @change="updateDate({ date: $event })"
+    />
+    <ui-input
+      :model-value="data.time"
+      :placeholder="t('workflow.blocks.trigger.forms.time')"
+      type="time"
+      class="w-full mt-2"
+      @change="$emit('update', { time: $event || '00:00' })"
+    />
+  </div>
+</template>
+<script setup>
+import { useI18n } from 'vue-i18n';
+import dayjs from '@/lib/dayjs';
+
+defineProps({
+  data: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+const emit = defineEmits(['update']);
+
+const { t } = useI18n();
+const maxDate = dayjs().add(30, 'day').format('YYYY-MM-DD');
+const minDate = dayjs().format('YYYY-MM-DD');
+
+function updateDate(value) {
+  if (!value) return;
+
+  let date = value?.date ?? minDate;
+
+  if (dayjs(minDate).isAfter(date)) date = minDate;
+  else if (dayjs(maxDate).isBefore(date)) date = maxDate;
+
+  emit('update', { date });
+}
+</script>

+ 59 - 0
src/components/newtab/workflow/edit/Trigger/TriggerInterval.vue

@@ -0,0 +1,59 @@
+<template>
+  <div>
+    <div class="flex items-center mt-1">
+      <ui-input
+        :model-value="data.interval"
+        :label="t('workflow.blocks.trigger.forms.interval')"
+        type="number"
+        class="w-full"
+        placeholder="1-120"
+        min="1"
+        max="120"
+        @change="
+          updateIntervalInput($event, { key: 'interval', min: 1, max: 120 })
+        "
+      />
+      <ui-input
+        v-if="!data.fixedDelay"
+        :model-value="data.delay"
+        type="number"
+        class="w-full ml-2"
+        :label="t('workflow.blocks.trigger.forms.delay')"
+        min="0"
+        max="20"
+        placeholder="0-20"
+        @change="updateIntervalInput($event, { key: 'delay', min: 0, max: 20 })"
+      />
+    </div>
+    <ui-checkbox
+      :model-value="data.fixedDelay"
+      block
+      class="mt-2"
+      @change="emit('update', { fixedDelay: $event })"
+    >
+      {{ t('workflow.blocks.trigger.fixedDelay') }}
+    </ui-checkbox>
+  </div>
+</template>
+<script setup>
+import { useI18n } from 'vue-i18n';
+
+defineProps({
+  data: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+defineEmits(['update']);
+
+const { t } = useI18n();
+
+function updateIntervalInput(value, { key, min, max }) {
+  let num = +value;
+
+  if (num < min) num = min;
+  else if (num > max) num = max;
+
+  emit('update', { [key]: num });
+}
+</script>

+ 92 - 0
src/components/newtab/workflow/edit/Trigger/TriggerKeyboardShortcut.vue

@@ -0,0 +1,92 @@
+<template>
+  <div class="mt-2">
+    <div class="flex items-center mb-2">
+      <ui-input
+        :model-value="getReadableShortcut(recordKeys.keys)"
+        readonly
+        class="flex-1 mr-2"
+        :placeholder="t('workflow.blocks.trigger.forms.shortcut')"
+      />
+      <ui-button
+        v-tooltip="
+          t(
+            `workflow.blocks.trigger.shortcut.${
+              recordKeys.isRecording ? 'stopRecord' : 'tooltip'
+            }`
+          )
+        "
+        icon
+        @click="toggleRecordKeys"
+      >
+        <v-remixicon
+          :name="recordKeys.isRecording ? 'riStopLine' : 'riRecordCircleLine'"
+        />
+      </ui-button>
+    </div>
+    <ui-checkbox
+      :model-value="data.activeInInput"
+      class="mb-1"
+      :title="t('workflow.blocks.trigger.shortcut.checkboxTitle')"
+      @change="$emit('update', { activeInInput: $event })"
+    >
+      {{ t('workflow.blocks.trigger.shortcut.checkbox') }}
+    </ui-checkbox>
+    <p class="mt-4 leading-tight text-gray-600 dark:text-gray-200">
+      {{ t('workflow.blocks.trigger.shortcut.note') }}
+    </p>
+  </div>
+</template>
+<script setup>
+import { reactive, onBeforeUnmount, onDeactivated } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { recordShortcut } from '@/utils/recordKeys';
+import { getReadableShortcut } from '@/composable/shortcut';
+
+const props = defineProps({
+  data: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+const emit = defineEmits(['update']);
+
+const { t } = useI18n();
+
+const recordKeys = reactive({
+  isRecording: false,
+  keys: `${props.data.shortcut}`,
+});
+
+function onKeydown(event) {
+  event.preventDefault();
+  event.stopPropagation();
+
+  recordShortcut(event, (keys) => {
+    recordKeys.keys = keys.join('+');
+    emit('update', { shortcut: recordKeys.keys });
+  });
+}
+function attachKeyEvents() {
+  window.addEventListener('keydown', onKeydown);
+  /* eslint-disable-next-line */
+  window.addEventListener('keyup', detachKeyEvents);
+}
+function detachKeyEvents() {
+  recordKeys.isRecording = false;
+
+  window.removeEventListener('keydown', onKeydown);
+  window.removeEventListener('keyup', detachKeyEvents);
+}
+function toggleRecordKeys() {
+  recordKeys.isRecording = !recordKeys.isRecording;
+
+  if (recordKeys.isRecording) {
+    attachKeyEvents();
+  } else {
+    detachKeyEvents();
+  }
+}
+
+onDeactivated(detachKeyEvents);
+onBeforeUnmount(detachKeyEvents);
+</script>

+ 185 - 0
src/components/newtab/workflow/edit/Trigger/TriggerSpecificDay.vue

@@ -0,0 +1,185 @@
+<template>
+  <div class="mt-4">
+    <ui-popover
+      :options="{ animation: null }"
+      trigger-width
+      class="w-full mb-2"
+      trigger-class="w-full"
+    >
+      <template #trigger>
+        <ui-button class="w-full">
+          <p class="text-left flex-1 text-overflow mr-2">
+            {{
+              tempDate.days.length === 0
+                ? t('workflow.blocks.trigger.selectDay')
+                : getDaysText(tempDate.days)
+            }}
+          </p>
+          <v-remixicon
+            size="28"
+            name="riArrowDropDownLine"
+            class="text-gray-600 dark:text-gray-200 -mr-2"
+          />
+        </ui-button>
+      </template>
+      <div class="grid gap-2 grid-cols-2">
+        <ui-checkbox
+          v-for="(day, id) in days"
+          :key="id"
+          :model-value="data.days?.includes(id)"
+          @change="onSelectDayChange($event, id)"
+        >
+          {{ t(`workflow.blocks.trigger.days.${id}`) }}
+        </ui-checkbox>
+      </div>
+    </ui-popover>
+    <div class="flex items-center">
+      <ui-input v-model="tempDate.time" type="time" class="flex-1 mr-2" />
+      <ui-button variant="accent" @click="addTime">
+        {{ t('workflow.blocks.trigger.addTime') }}
+      </ui-button>
+    </div>
+    <div class="my-2">
+      <ui-expand
+        v-for="(day, index) in sortedDaysArr"
+        :key="day.id"
+        header-class="focus:ring-0 flex items-center py-2 w-full group text-left"
+        type="time"
+        class="w-full"
+      >
+        <template #header>
+          <p class="flex-1">
+            {{ t(`workflow.blocks.trigger.days.${day.id}`) }}
+          </p>
+          <span class="text-gray-600 dark:text-gray-200">
+            <v-remixicon
+              name="riDeleteBin7Line"
+              class="mr-1 group invisible group-hover:visible inline-block"
+              @click="daysArr.splice(index, 1)"
+            />
+            {{ day.times.length }}x
+          </span>
+        </template>
+        <div class="grid grid-cols-2 gap-1 mb-1">
+          <div
+            v-for="(time, timeIndex) in day.times"
+            :key="time"
+            class="flex items-center px-4 py-2 border rounded-lg group"
+          >
+            <span class="flex-1"> {{ formatTime(time) }} </span>
+            <v-remixicon
+              name="riDeleteBin7Line"
+              class="cursor-pointer"
+              @click="removeDayTime(index, timeIndex)"
+            />
+          </div>
+        </div>
+      </ui-expand>
+    </div>
+  </div>
+</template>
+<script setup>
+import { reactive, computed, ref, watch, onMounted } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { useToast } from 'vue-toastification';
+import dayjs from '@/lib/dayjs';
+import { isObject } from '@/utils/helper';
+
+const props = defineProps({
+  data: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+const emit = defineEmits(['update']);
+
+const days = {
+  0: 'Sunday',
+  1: 'Monday',
+  2: 'Tuesday',
+  3: 'Wednesday',
+  4: 'Thursday',
+  5: 'Friday',
+  6: 'Saturday',
+};
+
+const { t } = useI18n();
+const toast = useToast();
+
+const daysArr = ref(null);
+const tempDate = reactive({
+  days: [],
+  time: '00:00',
+});
+
+const sortedDaysArr = computed(() =>
+  daysArr.value ? daysArr.value.slice().sort((a, b) => a.id - b.id) : []
+);
+
+function formatTime(time) {
+  const [hour, minute] = time.split(':');
+
+  return dayjs().hour(hour).minute(minute).format('hh:mm A');
+}
+function removeDayTime(index, timeIndex) {
+  daysArr.value[index].times.splice(timeIndex, 1);
+
+  if (daysArr.value[index].times.length === 0) {
+    daysArr.value.splice(index, 1);
+  }
+}
+function addTime() {
+  tempDate.days.forEach((dayId) => {
+    const dayIndex = daysArr.value.findIndex(({ id }) => id === dayId);
+
+    if (dayIndex === -1) {
+      daysArr.value.push({
+        id: dayId,
+        times: [tempDate.time],
+      });
+    } else {
+      const isTimeExist = daysArr.value[dayIndex].times.includes(tempDate.time);
+
+      if (isTimeExist) {
+        const message = t('workflow.blocks.trigger.timeExist', {
+          time: formatTime(tempDate.time),
+          day: t(`workflow.blocks.trigger.days.${dayId}`),
+        });
+
+        toast.error(message);
+
+        return;
+      }
+
+      daysArr.value[dayIndex].times.push(tempDate.time);
+    }
+  });
+}
+function onSelectDayChange(value, id) {
+  if (value) tempDate.days.push(+id);
+  else tempDate.days.splice(tempDate.days.indexOf(+id), 1);
+}
+function getDaysText(dayIds) {
+  return dayIds
+    .map((day) => t(`workflow.blocks.trigger.days.${day}`))
+    .join(', ');
+}
+
+watch(
+  daysArr,
+  (value, oldValue) => {
+    if (!oldValue) return;
+
+    emit('update', { days: value });
+  },
+  { deep: true }
+);
+
+onMounted(() => {
+  const isStringDay =
+    props.data.days.length > 0 && !isObject(props.data.days[0]);
+  daysArr.value = isStringDay
+    ? props.data.days.map((day) => ({ id: day, times: [props.data.time] }))
+    : props.data.days;
+});
+</script>

+ 30 - 0
src/components/newtab/workflow/edit/Trigger/TriggerVisitWeb.vue

@@ -0,0 +1,30 @@
+<template>
+  <div class="mt-2">
+    <ui-input
+      :model-value="data.url"
+      :placeholder="t('workflow.blocks.trigger.forms.url')"
+      class="w-full"
+      @change="$emit('update', { url: $event })"
+    />
+    <ui-checkbox
+      :model-value="data.isUrlRegex"
+      class="mt-1"
+      @change="$emit('update', { isUrlRegex: $event })"
+    >
+      {{ t('workflow.blocks.trigger.useRegex') }}
+    </ui-checkbox>
+  </div>
+</template>
+<script setup>
+import { useI18n } from 'vue-i18n';
+
+defineProps({
+  data: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+defineEmits(['update']);
+
+const { t } = useI18n();
+</script>

+ 0 - 0
src/components/newtab/workflow/edit/TriggerEventInput.vue → src/components/newtab/workflow/edit/TriggerEvent/TriggerEventInput.vue


+ 0 - 0
src/components/newtab/workflow/edit/TriggerEventKeyboard.vue → src/components/newtab/workflow/edit/TriggerEvent/TriggerEventKeyboard.vue


+ 0 - 0
src/components/newtab/workflow/edit/TriggerEventMouse.vue → src/components/newtab/workflow/edit/TriggerEvent/TriggerEventMouse.vue


+ 0 - 0
src/components/newtab/workflow/edit/TriggerEventTouch.vue → src/components/newtab/workflow/edit/TriggerEvent/TriggerEventTouch.vue


+ 0 - 0
src/components/newtab/workflow/edit/TriggerEventWheel.vue → src/components/newtab/workflow/edit/TriggerEvent/TriggerEventWheel.vue


+ 2 - 1
src/manifest.chrome.json

@@ -47,7 +47,8 @@
   ],
   ],
   "optional_permissions": [
   "optional_permissions": [
     "clipboardRead",
     "clipboardRead",
-    "downloads"
+    "downloads",
+    "contextMenus"
   ],
   ],
   "permissions": [
   "permissions": [
     "tabs",
     "tabs",

+ 2 - 1
src/manifest.firefox.json

@@ -51,7 +51,8 @@
   ],
   ],
   "optional_permissions": [
   "optional_permissions": [
     "clipboardRead",
     "clipboardRead",
-    "downloads"
+    "downloads",
+    "contextMenus"
   ],
   ],
   "permissions": [
   "permissions": [
     "tabs",
     "tabs",

+ 5 - 6
src/utils/recordKeys.js

@@ -33,14 +33,13 @@ const allowedKeys = {
   Escape: 'escape',
   Escape: 'escape',
   Enter: 'enter',
   Enter: 'enter',
 };
 };
-export function recordShortcut(event, callback) {
-  event.preventDefault();
-  event.stopPropagation();
-
-  if (event.repeat) return;
+export function recordShortcut(
+  { ctrlKey, altKey, metaKey, shiftKey, key, repeat },
+  callback
+) {
+  if (repeat) return;
 
 
   const keys = [];
   const keys = [];
-  const { ctrlKey, altKey, metaKey, shiftKey, key } = event;
 
 
   if (ctrlKey || metaKey) keys.push('mod');
   if (ctrlKey || metaKey) keys.push('mod');
   if (altKey) keys.push('option');
   if (altKey) keys.push('option');