Browse Source

feat: add editor settings

Ahmad Kholid 3 years ago
parent
commit
7999806116

+ 1 - 1
package.json

@@ -39,7 +39,7 @@
     "crypto-js": "^4.1.1",
     "css-selector-generator": "^3.6.0",
     "dayjs": "^1.10.7",
-    "defu": "^5.0.1",
+    "defu": "^6.0.0",
     "drawflow": "^0.0.51",
     "idb": "^7.0.0",
     "lodash.clonedeep": "^4.5.0",

BIN
src/assets/images/curvature.png


BIN
src/assets/images/no-curvature.png


+ 9 - 0
src/components/newtab/workflow/WorkflowBuilder.vue

@@ -1,6 +1,7 @@
 <template>
   <div
     id="drawflow"
+    :class="{ 'with-arrow': $store.state.settings.editor.arrow }"
     class="parent-drawflow relative"
     @drop="dropHandler"
     @dragover.prevent="handleDragOver"
@@ -531,4 +532,12 @@ export default {
 .drawflow .drawflow-node {
   @apply dark:bg-gray-800;
 }
+#drawflow.with-arrow .drawflow-node .input {
+  background-color: transparent !important;
+  border-top: 10px solid transparent;
+  border-radius: 0;
+  border-left: 10px solid white;
+  border-right: 10px solid transparent;
+  border-bottom: 10px solid transparent;
+}
 </style>

+ 81 - 4
src/lib/drawflow.js

@@ -8,10 +8,87 @@ export default function (element, { context, options = {} }) {
   const editor = new Drawflow(element, { render, version: 3, h }, context);
 
   editor.useuuid = true;
-  editor.curvature = 0;
-  editor.reroute_curvature = 0;
-  editor.reroute_curvature_start_end = 0;
-  editor.reroute_fix_curvature = true;
+  editor.createCurvature = (
+    startPosX,
+    startPosY,
+    endPosX,
+    endPosY,
+    curvature,
+    type
+  ) => {
+    const generateCurvature = (start = false) => {
+      const curvatureVal = options.disableCurvature ? 0 : curvature;
+
+      if (start) {
+        return startPosX + Math.abs(endPosX - startPosX) * curvatureVal;
+      }
+
+      return endPosX - Math.abs(endPosX - startPosX) * curvature;
+    };
+
+    switch (type) {
+      case 'open': {
+        const hx1 = generateCurvature(true);
+        let hx2 = generateCurvature();
+
+        if (startPosX >= endPosX) {
+          hx2 = endPosX - Math.abs(endPosX - startPosX) * (curvature * -1);
+        }
+
+        return ` M ${startPosX} ${startPosY} C ${hx1} ${startPosY} ${hx2} ${endPosY} ${endPosX}  ${endPosY}`;
+      }
+      case 'close': {
+        let hx1 = generateCurvature(true);
+        const hx2 = generateCurvature();
+
+        if (startPosX >= endPosX) {
+          hx1 = startPosX + Math.abs(endPosX - startPosX) * (curvature * -1);
+        }
+
+        const posX = options.arrow ? endPosX - 10 : endPosX;
+
+        return ` M ${startPosX} ${startPosY} C ${hx1} ${startPosY} ${hx2} ${endPosY} ${posX} ${endPosY}`;
+      }
+      case 'other': {
+        let hx1 = generateCurvature(true);
+        let hx2 = generateCurvature();
+
+        if (startPosX >= endPosX) {
+          hx1 = startPosX + Math.abs(endPosX - startPosX) * (curvature * -1);
+          hx2 = endPosX - Math.abs(endPosX - startPosX) * (curvature * -1);
+        }
+
+        return ` M ${startPosX} ${startPosY} C ${hx1} ${startPosY} ${hx2} ${endPosY} ${endPosX} ${endPosY}`;
+      }
+      default: {
+        let line = '';
+        const posX = options.arrow ? endPosX - 10 : endPosX;
+
+        if (!options.disableCurvature) {
+          const hx1 = generateCurvature(true);
+          const hx2 = generateCurvature();
+
+          line = `M${startPosX} ${startPosY} C${hx1} ${startPosY} ${hx2} ${endPosY} ${posX} ${endPosY}`;
+        } else {
+          const centerX =
+            Math.abs(endPosX - startPosX) < 300
+              ? (endPosX - startPosX) / 2 + startPosX
+              : startPosX + 150;
+          let firstLine = `L${centerX} ${startPosY} L${centerX} ${endPosY}`;
+
+          if (startPosX >= endPosX) {
+            const centerY = (endPosY - startPosY) / 2 + startPosY;
+
+            firstLine = ` L${startPosX} ${startPosY} L${startPosX} ${centerY} L${posX} ${centerY}`;
+          }
+
+          line = `M ${startPosX} ${startPosY} ${firstLine} L${posX} ${endPosY}`;
+        }
+
+        return line;
+      }
+    }
+  };
 
   Object.entries(options).forEach(([key, value]) => {
     editor[key] = value;

+ 6 - 1
src/locales/en/newtab.json

@@ -25,12 +25,17 @@
       "duplicate": "Shortcut already use by \"{name}\""
     },
     "editor": {
+      "title": "Title",
       "curvature": {
-        "title": "Editor Line Curvature",
+        "title": "Line Curvature",
         "line": "Line",
         "reroute": "Reroute",
         "rerouteFirstLast": "Reroute first & last point"
       },
+      "arrow": {
+        "title": "Line arrow",
+        "description": "Add an arrow at the end of the line"
+      }
     },
     "language": {
       "label": "Language",

+ 1 - 0
src/newtab/pages/Settings.vue

@@ -39,6 +39,7 @@ const { t } = useI18n();
 const menus = [
   { id: 'general', path: '/settings', icon: 'riSettings3Line' },
   { id: 'backup', path: '/backup', icon: 'riDatabase2Line' },
+  { id: 'editor', path: '/editor', icon: 'riMindMap' },
   { id: 'shortcuts', path: '/shortcuts', icon: 'riKeyboardLine' },
   { id: 'about', path: '/about', icon: 'riInformationLine' },
 ];

+ 103 - 0
src/newtab/pages/settings/SettingsEditor.vue

@@ -0,0 +1,103 @@
+<template>
+  <div class="max-w-2xl">
+    <p class="font-semibold">
+      {{ t('settings.editor.curvature.title') }}
+    </p>
+    <div class="flex items-center space-x-4 mt-2">
+      <div
+        v-for="item in curvatureOptions"
+        :key="item.id"
+        class="cursor-pointer"
+        role="button"
+        @click="updateSetting('disableCurvature', item.value)"
+      >
+        <div
+          :class="{
+            'ring ring-accent': item.value === settings.editor.disableCurvature,
+          }"
+          class="p-0.5 rounded-lg"
+        >
+          <img
+            :src="require(`@/assets/images/${item.id}.png`).default"
+            width="140"
+            class="rounded-lg"
+          />
+        </div>
+        <span class="text-sm text-gray-600 dark:text-gray-200 ml-1">
+          {{ t(`common.${item.name}`) }}
+        </span>
+      </div>
+    </div>
+    <transition-expand>
+      <div
+        v-if="!settings.editor.disableCurvature"
+        class="flex space-x-2 items-end mt-1"
+      >
+        <ui-input
+          v-for="item in curvatureSettings"
+          :key="item.id"
+          :model-value="settings.editor[item.key]"
+          :label="t(`settings.editor.curvature.${item.id}`)"
+          type="number"
+          min="0"
+          max="1"
+          class="w-full"
+          placeholder="0.5"
+          @change="updateSetting(item.key, curvatureLimit($event))"
+        />
+      </div>
+    </transition-expand>
+    <ui-list class="mt-8">
+      <ui-list-item small>
+        <ui-switch
+          :model-value="settings.editor.arrow"
+          @change="updateSetting('arrow', $event)"
+        />
+        <div class="flex-1 ml-4">
+          <p class="leading-tight">
+            {{ t('settings.editor.arrow.title') }}
+          </p>
+          <p class="text-gray-600 text-sm leading-tight dark:text-gray-200">
+            {{ t('settings.editor.arrow.description') }}
+          </p>
+        </div>
+      </ui-list-item>
+    </ui-list>
+  </div>
+</template>
+<script setup>
+import { computed } from 'vue';
+import { useStore } from 'vuex';
+import { useI18n } from 'vue-i18n';
+import browser from 'webextension-polyfill';
+
+const curvatureSettings = [
+  { id: 'line', key: 'curvature' },
+  { id: 'reroute', key: 'reroute_curvature' },
+  { id: 'rerouteFirstLast', key: 'reroute_curvature_start_end' },
+];
+const curvatureOptions = [
+  { id: 'curvature', value: false, name: 'enable' },
+  { id: 'no-curvature', value: true, name: 'disable' },
+];
+
+const { t } = useI18n();
+const store = useStore();
+
+const settings = computed(() => store.state.settings);
+
+function updateSetting(path, value) {
+  store.commit('updateStateNested', {
+    value,
+    path: `settings.editor.${path}`,
+  });
+
+  browser.storage.local.set({ settings: settings.value });
+}
+function curvatureLimit(value) {
+  if (value > 1) return 1;
+  if (value < 0) return 0;
+
+  return value;
+}
+</script>

+ 0 - 31
src/newtab/pages/settings/SettingsIndex.vue

@@ -54,25 +54,6 @@
       {{ t('settings.language.reloadPage') }}
     </p>
   </div>
-  <div class="mt-12 max-w-2xl">
-    <p class="font-semibold">
-      {{ t('settings.editor.curvature.title') }}
-    </p>
-    <div class="flex space-x-2 items-end">
-      <ui-input
-        v-for="item in curvatureSettings"
-        :key="item.id"
-        :model-value="settings.editor[item.key]"
-        :label="t(`settings.editor.curvature.${item.id}`)"
-        type="number"
-        min="0"
-        max="1"
-        class="w-full"
-        placeholder="0.5"
-        @change="updateSetting(`editor.${item.key}`, curvatureLimit($event))"
-      />
-    </div>
-  </div>
 </template>
 <script setup>
 import { computed, ref } from 'vue';
@@ -82,12 +63,6 @@ import browser from 'webextension-polyfill';
 import { useTheme } from '@/composable/theme';
 import { supportLocales } from '@/utils/shared';
 
-const curvatureSettings = [
-  { id: 'line', key: 'curvature' },
-  { id: 'reroute', key: 'reroute_curvature' },
-  { id: 'rerouteFirstLast', key: 'reroute_curvature_start_end' },
-];
-
 const { t } = useI18n();
 const store = useStore();
 const theme = useTheme();
@@ -95,12 +70,6 @@ const theme = useTheme();
 const isLangChange = ref(false);
 const settings = computed(() => store.state.settings);
 
-function curvatureLimit(value) {
-  if (value > 1) return 1;
-  if (value < 0) return 0;
-
-  return value;
-}
 function updateSetting(path, value) {
   store.commit('updateStateNested', {
     value,

+ 2 - 0
src/newtab/router.js

@@ -13,6 +13,7 @@ import SettingsIndex from './pages/settings/SettingsIndex.vue';
 import SettingsAbout from './pages/settings/SettingsAbout.vue';
 import SettingsShortcuts from './pages/settings/SettingsShortcuts.vue';
 import SettingsBackup from './pages/settings/SettingsBackup.vue';
+import SettingsEditor from './pages/settings/SettingsEditor.vue';
 
 const routes = [
   {
@@ -67,6 +68,7 @@ const routes = [
       { path: '', component: SettingsIndex },
       { path: '/about', component: SettingsAbout },
       { path: '/backup', component: SettingsBackup },
+      { path: '/editor', component: SettingsEditor },
       { path: '/shortcuts', component: SettingsShortcuts },
     ],
   },

+ 4 - 1
src/store/index.js

@@ -2,6 +2,7 @@ import { createStore } from 'vuex';
 import objectPath from 'object-path';
 import browser from 'webextension-polyfill';
 import vuexORM from '@/lib/vuex-orm';
+import defu from 'defu';
 import * as models from '@/models';
 import { firstWorkflows } from '@/utils/shared';
 import { fetchApi } from '@/utils/api';
@@ -21,6 +22,8 @@ const store = createStore({
     settings: {
       locale: 'en',
       editor: {
+        arrow: false,
+        disableCurvature: false,
         curvature: 0.5,
         reroute_curvature: 0.5,
         reroute_curvature_start_end: 0.5,
@@ -69,7 +72,7 @@ const store = createStore({
 
         commit('updateState', {
           key: 'settings',
-          value: { ...state.settings, ...(settings || {}) },
+          value: defu(settings || {}, state.settings),
         });
         commit('updateState', {
           key: 'workflowHosts',

+ 4 - 4
yarn.lock

@@ -3076,10 +3076,10 @@ defined@^1.0.0:
   resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
   integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=
 
-defu@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/defu/-/defu-5.0.1.tgz#a034278f9b032bf0845d261aa75e9ad98da878ac"
-  integrity sha512-EPS1carKg+dkEVy3qNTqIdp2qV7mUP08nIsupfwQpz++slCVRw7qbQyWvSTig+kFPwz2XXp5/kIIkH+CwrJKkQ==
+defu@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/defu/-/defu-6.0.0.tgz#b397a6709a2f3202747a3d9daf9446e41ad0c5fc"
+  integrity sha512-t2MZGLf1V2rV4VBZbWIaXKdX/mUcYW0n2znQZoADBkGGxYL8EWqCuCZBmJPJ/Yy9fofJkyuuSuo5GSwo0XdEgw==
 
 del@^4.1.1:
   version "4.1.1"