From df22cd390879266240a51f1ecf7abf99811e055b Mon Sep 17 00:00:00 2001 From: malkja Date: Mon, 27 May 2024 09:54:54 +0200 Subject: [PATCH 1/8] refactor: clean code --- package-lock.json | 90 ++++++++++++++++++++++----------- package.json | 3 ++ src/store/contents/actions.js | 2 - src/store/contents/getters.js | 31 ------------ src/store/contents/mutations.js | 12 ----- 5 files changed, 64 insertions(+), 74 deletions(-) diff --git a/package-lock.json b/package-lock.json index 175f9adb..b45cc0b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "@subugoe/tido", "version": "3.3.0", "license": "AGPL-3.0-or-later", + "dependencies": { + "pinia": "^2.1.7" + }, "devDependencies": { "@vitejs/plugin-vue": "^5.0.4", "@vueuse/core": "^10.9.0", @@ -25,7 +28,6 @@ "standard-version": "^9.5.0", "start-server-and-test": "^2.0.3", "tailwindcss": "^3.4.1", - "typescript": "^5.4.5", "vite": "^5.2.6", "vue": "^3.4.20", "vue-i18n": "^9.2.0-beta.35", @@ -169,7 +171,6 @@ "version": "7.24.1", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", - "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -891,8 +892,7 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", @@ -1224,7 +1224,6 @@ "version": "3.4.21", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.21.tgz", "integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==", - "dev": true, "dependencies": { "@babel/parser": "^7.23.9", "@vue/shared": "3.4.21", @@ -1237,7 +1236,6 @@ "version": "3.4.21", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz", "integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==", - "dev": true, "dependencies": { "@vue/compiler-core": "3.4.21", "@vue/shared": "3.4.21" @@ -1247,7 +1245,6 @@ "version": "3.4.21", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz", "integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==", - "dev": true, "dependencies": { "@babel/parser": "^7.23.9", "@vue/compiler-core": "3.4.21", @@ -1264,7 +1261,6 @@ "version": "3.4.21", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz", "integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==", - "dev": true, "dependencies": { "@vue/compiler-dom": "3.4.21", "@vue/shared": "3.4.21" @@ -1273,14 +1269,12 @@ "node_modules/@vue/devtools-api": { "version": "6.6.1", "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.1.tgz", - "integrity": "sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==", - "dev": true + "integrity": "sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==" }, "node_modules/@vue/reactivity": { "version": "3.4.21", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.21.tgz", "integrity": "sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==", - "dev": true, "dependencies": { "@vue/shared": "3.4.21" } @@ -1289,7 +1283,6 @@ "version": "3.4.21", "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.21.tgz", "integrity": "sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==", - "dev": true, "dependencies": { "@vue/reactivity": "3.4.21", "@vue/shared": "3.4.21" @@ -1299,7 +1292,6 @@ "version": "3.4.21", "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.21.tgz", "integrity": "sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==", - "dev": true, "dependencies": { "@vue/runtime-core": "3.4.21", "@vue/shared": "3.4.21", @@ -1310,7 +1302,6 @@ "version": "3.4.21", "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.21.tgz", "integrity": "sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==", - "dev": true, "dependencies": { "@vue/compiler-ssr": "3.4.21", "@vue/shared": "3.4.21" @@ -1322,8 +1313,7 @@ "node_modules/@vue/shared": { "version": "3.4.21", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz", - "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==", - "dev": true + "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==" }, "node_modules/@vueuse/core": { "version": "10.9.0", @@ -2676,8 +2666,7 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/cypress": { "version": "13.7.0", @@ -3111,7 +3100,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, "engines": { "node": ">=0.12" }, @@ -3612,8 +3600,7 @@ "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" }, "node_modules/esutils": { "version": "2.0.3", @@ -5475,7 +5462,6 @@ "version": "0.30.8", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", - "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, @@ -5850,7 +5836,6 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, "funding": [ { "type": "github", @@ -6312,8 +6297,7 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -6336,6 +6320,56 @@ "node": ">=0.10.0" } }, + "node_modules/pinia": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz", + "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==", + "dependencies": { + "@vue/devtools-api": "^6.5.0", + "vue-demi": ">=0.14.5" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@vue/composition-api": "^1.4.0", + "typescript": ">=4.4.4", + "vue": "^2.6.14 || ^3.3.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/vue-demi": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", + "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", @@ -6390,7 +6424,6 @@ "version": "8.4.38", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -7234,7 +7267,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -8055,7 +8087,8 @@ "version": "5.4.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "dev": true, + "optional": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8282,7 +8315,6 @@ "version": "3.4.21", "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz", "integrity": "sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==", - "dev": true, "dependencies": { "@vue/compiler-dom": "3.4.21", "@vue/compiler-sfc": "3.4.21", diff --git a/package.json b/package.json index 2a52e281..2e950385 100644 --- a/package.json +++ b/package.json @@ -90,5 +90,8 @@ ], "publishConfig": { "@subugoe:registry": "https://gitlab.gwdg.de/api/v4/projects/10921/packages/npm/" + }, + "dependencies": { + "pinia": "^2.1.7" } } diff --git a/src/store/contents/actions.js b/src/store/contents/actions.js index 59136ef9..7ce4d4da 100644 --- a/src/store/contents/actions.js +++ b/src/store/contents/actions.js @@ -105,8 +105,6 @@ export const initCollection = async ({ activeManifest = manifests[manifestIndex]; itemUrl = activeManifest.sequence[itemIndex].id; - if ('p' in resultConfig) commit('setPanels', resultConfig.p); - if ('s' in resultConfig) commit('setShow', resultConfig.s); const { support } = activeManifest; if (support && support.length > 0) { diff --git a/src/store/contents/getters.js b/src/store/contents/getters.js index a8120db0..69489916 100644 --- a/src/store/contents/getters.js +++ b/src/store/contents/getters.js @@ -21,37 +21,6 @@ export const itemUrl = (state) => state.itemUrl; export const manifests = (state) => state.manifests; -export const selectedItemIndex = (state) => { - const selectedItemUrl = encodeURI(decodeURI(state.itemUrl)); - const index = state.itemUrls.findIndex((item) => encodeURI(decodeURI(item)) === selectedItemUrl); - return index > -1 ? index : 0; -}; - -export const selectedManifest = (state) => state.manifests.find((manifest) => { - manifest = { ...manifest }; - const selectedItemUrl = encodeURI(decodeURI(state.itemUrl)); - if (!Array.isArray(manifest.sequence)) { - manifest.sequence = [manifest.sequence]; - } - return manifest.sequence.find((manifestItem) => encodeURI(decodeURI(manifestItem.id)) === selectedItemUrl); -}); - -export const selectedSequenceIndex = (state, getters) => { - const item = getters.selectedManifest; - if (!item) { - return null; - } - - const { label } = item; - let index = null; - state.manifests.forEach((manifest, idx) => { - if (manifest.label === label) { - index = idx; - } - }); - return index; -}; - export const item = (state) => state.item; export const manifest = (state) => state.manifest; diff --git a/src/store/contents/mutations.js b/src/store/contents/mutations.js index 692eeb7f..02ec2133 100644 --- a/src/store/contents/mutations.js +++ b/src/store/contents/mutations.js @@ -2,14 +2,6 @@ export const setCollection = (state, payload) => { state.collection = { ...payload }; }; -export const setCollectionTitle = (state, title) => { - state.collectionTitle = title; -}; - -export const setConnectorValues = (state, payload) => { - state.connectorValues = payload; -}; - export const setItem = (state, payload) => { state.item = payload; }; @@ -22,10 +14,6 @@ export const setManifests = (state, payload) => { state.manifests = payload; }; -export const setPanels = (state, payload) => { - state.panels = payload; -}; - export const setManifest = (state, payload) => { if (!Array.isArray(payload.sequence)) payload.sequence = [payload.sequence]; state.manifest = { ...payload }; From de1f895ce873dfb241c34dd04eba6742582d5fbf Mon Sep 17 00:00:00 2001 From: malkja Date: Mon, 27 May 2024 10:09:43 +0200 Subject: [PATCH 2/8] refactor: create a 'config' pinia store and a 'config' interface in Typescript --- src/stores/config.ts | 596 +++++++++++++++++++++++++++++++++++++++++++ src/types.d.ts | 85 +++++- 2 files changed, 680 insertions(+), 1 deletion(-) create mode 100644 src/stores/config.ts diff --git a/src/stores/config.ts b/src/stores/config.ts new file mode 100644 index 00000000..a81a7228 --- /dev/null +++ b/src/stores/config.ts @@ -0,0 +1,596 @@ +import { defineStore } from 'pinia' +import { + computed, ref, + } from 'vue'; + +import messages from 'src/i18n'; +import { isUrl } from '@/utils'; +import BookmarkService from '@/services/bookmark'; +import { i18n } from '@/i18n'; + + export const useConfigStore = defineStore('config', () => { + // States ('Setup Pinia': refs) + const instanceId = ref(null) + const config = ref({ + container: '#app', + collection: '', + manifest: '', + item: '', + panels: [ + { + label: 'contents', + toggle: true, + show: true, + views: [ + { + id: 'tree', + label: 'contents', + connector: { + id: 1, + options: { + labels: { + item: 'Sheet', + manifest: 'Manuscript', + }, + }, + }, + }, + ], + }, + { + label: 'metadata', + show: true, + toggle: true, + views: [ + { + id: 'metadata', + label: 'metadata', + connector: { + id: 2, + options: { + collection: { + all: true, + }, + manifest: { + all: true, + }, + item: { + all: true, + }, + }, + }, + }, + ], + }, + { + label: 'image', + show: true, + toggle: true, + views: [ + { + id: 'image', + label: 'Image', + connector: { + id: 3, + }, + }], + }, + { + label: 'text', + show: true, + toggle: true, + views: [ + { + id: 'text1', + label: 'Transcription', + default: true, + connector: { + id: 4, + }, + }, + ], + }, + { + label: 'annotations', + show: true, + toggle: true, + views: [ + { + id: 'annotations1', + label: 'annotations', + connector: { + id: 5, + options: { + types: [], + }, + }, + }, + ], + }, + ], + colors: { + forceMode: 'none', + primary: '', + secondary: '', + accent: '', + }, + header: { + show: true, + navigation: true, + panelsToggle: true, + languageSwitch: false, + }, + labels: { + item: 'Sheet', + manifest: 'Manuscript', + }, + lang: 'en', + meta: { + collection: { + all: true, + }, + manifest: { + all: true, + }, + item: { + all: true, + }, + }, + notificationColors: { + info: 'blue-400', + warning: 'red-400', + }, + }) + const activeViews = ref([]) + const isValid = ref(false) + + + const defaultPanel = { + label: 'Panel', + show: true, + toggle: true, + views: [], + }; + + const defaultView = { + id: 'view', + name: 'View', + default: false, + connector: { + id: 1, + options: {}, + }, + }; + + // Getters ('Setup Pinia' computed()) + const activeContentType = computed(() => { + const contentConnectorId = 4; + const panelIndex = config.value.panels.findIndex(({ views }) => views.find(({ connector }) => contentConnectorId === connector.id)); + + if (panelIndex === -1) return -1; + + const viewIndex = activeViews.value[panelIndex]; + return config.value.panels[panelIndex].views[viewIndex].connector.options.type; + }) + + // I think it doesn't matter whether getIconByType is a function or a computed property, since its only being called by actions in config + function getIconByType(type) { + const annotationsConnectorId = 5; + const panelIndex = config.value.panels.findIndex(({ views }) => views.find(({ connector }) => annotationsConnectorId === connector.id)); + + if (panelIndex === -1) return -1; + + const viewIndex = activeViews.value[panelIndex]; + const types = config.value.panels[panelIndex].views[viewIndex].connector.options?.types; + return types.find(({ name }) => name === type)?.icon || 'biPencilSquare'; + } + + + + // Functions (mutators and actions in Vuex are now converted to functions in Pinia) + + function setConfig(payload) { + config.value = payload; + } + + async function setActivePanelView(viewIndex: number, panelIndex: number) { + if(activeViews.value[panelIndex] !== undefined) { + activeViews.value[panelIndex] = viewIndex; + } + await BookmarkService.updatePanels(activeViews.value); + } + + function setPanels(panels) { + config.value.panels = panels; + } + + function setShowPanelSetter(index: number, show: boolean) { + config.value.panels[index].show = show; + } + + function loadConfig(config, isValid) { + config.value = config; + isValid.value = isValid; + } + + function setActiveViews(payload) { + activeViews.value = payload; + } + + function setInstanceId(payload) { + instanceId.value = payload; + } + + // Actions to functions + + + function validateCollection(value) { + return !!(value); + } + + function validateManifest(value) { + return !!(value); + } + + function validateItem(value) { + return !!(value); + } + + function validateTranslations(value) { + return !!(value) && Object.keys(value).every((key) => key === 'en' || key === 'de'); + } + + function validatePanels(value) { + return !!(value) && Array.isArray(value); + } + + function validateLang(value) { + return !!(value); + } + + function validateColors(value) { + return !!(value); + } + + function validateContainer(value) { + return !!(value); + } + + function validateHeader(value, defaultValue) { + if (!value) return false; + + const defaultKeys = Object.keys(defaultValue); + const invalidKeys = Object.keys(value) + .filter((key) => defaultKeys.findIndex((defaultKey) => defaultKey === key) === -1); + return invalidKeys.length === 0; + } + + function validateLabels(labels, validLabels: Labels) { + // valid labels are the labels from the default config + // we consider the custom labels, in the case when all the keys have a value, otherwise we would have the button with empty text i.e for the following scenario + // when the item is '' + if (!labels || !validLabels) return false; + + let isValid = true; + Object.keys(labels).forEach((key) => { + if (!(key in validLabels) || labels[key] === '') { + isValid = false; + } + }); + + return isValid; + } + + function createDefaultActiveViews(panelsConfig) { + return panelsConfig + .filter((p) => p.views && p.views.length > 0) + .map((panel) => { + const defaultIndex = panel.views.findIndex((view) => view.default === true); + return defaultIndex > -1 ? defaultIndex : 0; + }) + .reduce((acc, cur, i) => { + acc[i] = cur; + return acc; + }, {}); + } + + + // URL Config + + function splitUrlParts(urlQuery: string, attributes: Array): Array { + if (urlQuery === '') { + return [undefined, undefined, undefined, undefined]; + } + const arrayAttributes = urlQuery.split('_'); + const manifestPart = arrayAttributes.find((element) => element[0].includes(attributes[0])); // index of manifest part in the splitted array: element[0] is 'm' the first letter of the part ? + const itemPart = arrayAttributes.find((element) => element[0].includes(attributes[1])); + const panelsPart = arrayAttributes.find((element) => element[0].includes(attributes[2])); + const showPart = arrayAttributes.find((element) => element[0].includes(attributes[3])); + return [manifestPart, itemPart, panelsPart, showPart]; + } + + function isManifestPartValid(manifestPart: string): boolean { + const regexManifest = /m\d+$/; + return regexManifest.exec(manifestPart) !== null; + } + + function isItemPartValid(itemPart: string): boolean { + const regexItem = /i\d+$/; + return regexItem.exec(itemPart) !== null; + } + + function isPanelsPartValid(panelsPart, panelsValue, numberPanels) { + const numbersPartArray = panelsValue.split('-'); + const regexNumber = /^\d+$/; + if (panelsPart[0] !== 'p' || numbersPartArray.length !== numberPanels) { + return false; + } + + for (let i = 0; i < numbersPartArray.length; i++) { + const panelTabPair = numbersPartArray[i]; + if (panelTabPair.length !== 3 + || regexNumber.test(panelTabPair[0]) === false + || regexNumber.test(panelTabPair[2]) === false + || panelTabPair[1] !== '.') { + return false; + } + } + return true; + } + + + function isShowPartValid(showValue, numberPanels) { + const showValueAsArray = showValue.split('-'); + const regexNumbersPart = /\d\-/; + if (showValueAsArray.length > numberPanels) { + return false; + } + for (let i = 0; i < showValueAsArray.length - 1; i++) { + // if s0-2 is given and there are in total 4 panels, then it is still fine, since we can show less number of panels than the total one + // match the couples of (d-) -> a digit followed by a "-" character. In total there are (s.length - 1) - so number of panels we want to open - 1 + const groupMatch = showValue.slice(i * 2, i * 2 + 2).match(regexNumbersPart); + if (groupMatch === null) { + return false; + } + } + const lastNumberString = showValue.slice(-1)[0]; + const lastNumberInt = parseInt(lastNumberString, 10); + // last character must have only digits and not be greater than number of max panels + if (/^\d+$/.test(lastNumberString) === false || (lastNumberInt >= numberPanels || lastNumberInt < 0)) { + return false; + } + return true; + } + + function createDefaultPanelValue(numberPanels) { + // get the number of panels and then create as many couples of (panel_index.0) until n_panels-1, the last couple need not have the '-' symbol + let p = ''; + for (let j = 0; j < numberPanels; j++) { + if (j !== numberPanels - 1) { + p += `${j}.0-`; + } else { + p += `${j}.0`; + } + } + return p; + } + + function createActiveViewsFromPanelsArray(panelsArray) { + // converts 'panelsArray' to an object with key, value: 'panel index: visible tab index' + return panelsArray.reduce((acc, cur) => { + // eslint-disable-next-line no-shadow + const [panelIndex, viewIndex] = cur.split('.').map((i) => parseInt(i, 10)); + acc[panelIndex] = viewIndex; + return acc; + }, {}); + } + + function discoverCustomConfig(customConfig, defaultConfig) { + const { + container, translations, collection, manifest, item, panels, lang, colors, header, labels + } = customConfig; + + return { + ...(validateContainer(container) && { container }), + ...(validateCollection(collection) && { collection }), + ...(validateManifest(manifest) && { manifest }), + ...(validateItem(item) && { item }), + ...(validateTranslations(translations) && { translations }), + ...(validatePanels(panels) && { panels }), + ...(validateLang(lang) && { lang }), + ...(validateColors(colors) && { colors }), + ...(validateHeader(header, defaultConfig.header) && { header }), + ...(validateLabels(labels, defaultConfig.labels) && { labels }), + }; + } + + function discoverUrlConfig(config) { + // split the url based on '_' + // get the part of attribute: get the attribute name and the value based on the type of attribute + // add each attribute to UrlConfig as key value + let urlConfig = {}; + const urlQuery = BookmarkService.getQuery(); + const attributes = ['m', 'i', 'p', 's']; + // values of manifest, item Indices ... + let [m, i, p, s] = [undefined, undefined, undefined, undefined]; + const numberPanels = config.panels ? config.panels.length : 0; + const [manifestPart, itemPart, panelsPart, showPart] = splitUrlParts(urlQuery, attributes); + /* + if (isUrl(item)) urlConfig.item = item; + if (isUrl(manifest)) urlConfig.manifest = manifest; + if (isUrl(collection)) urlConfig.collection = collection; + */ + // here we will validate for the structure of each component:, not their value range + if (manifestPart !== undefined) { + if (!isManifestPartValid(manifestPart)) { + throw new Error(i18n.global.t('error_manifestpart_tido_url')); + } else { + urlConfig.m = parseInt(manifestPart.slice(1), 10); + } + } + if (itemPart !== undefined) { + if (!isItemPartValid(itemPart)) { + throw new Error(i18n.global.t('error_itempart_tido_url')); + } else { + urlConfig.i = parseInt(itemPart.slice(1), 10); + } + } + if (panelsPart !== undefined) { + const panelsValue = panelsPart.slice(1); + if (!isPanelsPartValid(panelsPart, panelsValue, numberPanels)) { + throw new Error(i18n.global.t('error_panelspart_tido_url')); + } else { + p = panelsValue; + } + } else { + p = createDefaultPanelValue(numberPanels); + } + const panelsArray = p !== '' ? p.split('-') : []; + urlConfig.activeViews = createActiveViewsFromPanelsArray(panelsArray); + + if (showPart !== undefined) { + const showValue = showPart.slice(1); + if (!isShowPartValid(showValue, numberPanels)) { + throw new Error(i18n.global.t('error_showpart_tido_url')); + } else { + // showValue needs to be an array of opened panel indices (Integers) + s = showValue.split('-').map(Number); + } + } + if (s === undefined) { + // If 's' is not given in URL, then we open all the panels which are given in config + urlConfig.show = Array.from({ length: numberPanels }, (value, index) => index); + } else { + urlConfig.show = s; + } + return urlConfig; + } + + function discoverDefaultConfig(config) { + return { + ...JSON.parse(JSON.stringify(config)), + activeViews: createDefaultActiveViews(config.panels), + }; + } + + function load(custConfig) { + const customConfig = discoverCustomConfig(custConfig, config.value); + const urlConfig = discoverUrlConfig(custConfig); + const defaultConfig = discoverDefaultConfig(config.value); + + const header = { + ...defaultConfig.header, + ...customConfig.header, + }; + + if (customConfig.panels) { + // If the custom config provide panels config, we still need to check if it's valid. + // Here we enhance the potentially missing parts with default panel/view config. + // Hint: Not to confuse with the "defaultConfig" which provides an out-of-the-box panels setup + + customConfig.panels = customConfig.panels.map((panel) => { + if (panel.views) { + panel.views = panel.views.map((view) => ({ + ...defaultView, + ...view, + })); + } + + return { + ...defaultPanel, + ...panel, + }; + }); + } + + const resultConfig = { + ...defaultConfig, + ...customConfig, + ...urlConfig, + header, + }; + + const activeViews = urlConfig.activeViews || defaultConfig.activeViews; + setActiveViews(activeViews) + + if (resultConfig.show && resultConfig.show.length > 0) { + // Set visible panels + // First hide all + resultConfig.panels.map((panel, i) => resultConfig.panels[i].show = false); + + // Next show configured + resultConfig.show.forEach((panelIndex) => { + if (!Number.isInteger(panelIndex)) return; + const panel = resultConfig.panels[panelIndex]; + if (!panel) return; + + resultConfig.panels[panelIndex].show = true; + }); + } + + if (resultConfig.translations) { + const locales = Object.keys(resultConfig.translations); + + locales.forEach((locale) => { + i18n.global.setLocaleMessage(locale, { ...(messages[locale] ? messages[locale] : {}), ...resultConfig.translations[locale] }); + }); + } + setConfig(resultConfig) + + if (urlConfig.activeViews) setActiveViews(activeViews) + else setDefaultActiveViews(false) //dispatch('setDefaultActiveViews', false); + } + + + function setShowPanel( {index, show} ) { + setShowPanelSetter(index, show) + + let panelIndexes = config.value.panels.reduce((acc, cur, i) => (cur.show ? [...acc, i] : acc), []); + if (panelIndexes.length === config.value.panels.length) panelIndexes = []; + + BookmarkService.updateShow(panelIndexes); + }; + + function setContentType( type) { + const newConfig = { ...config.value }; + + newConfig.panels[3].views[0].connector.options = { type }; + setConfig(newConfig) + }; + + + async function setDefaultActiveViews (bookmark = true){ + const activeViews = []; + + config.value.panels.forEach(({ views }, panelIndex) => { + let defaultViewIndex = views.findIndex((view) => !!(view.default)); + if (defaultViewIndex === -1) defaultViewIndex = 0; + activeViews[panelIndex] = defaultViewIndex; + }); + + if (bookmark) await BookmarkService.updatePanels(activeViews); + setActiveViews(activeViews) + } + + return { + instanceId, config, activeViews, isValid, + activeContentType, getIconByType, + setConfig, setActivePanelView, setPanels, setShowPanelSetter, loadConfig, setActiveViews, setInstanceId, + validateCollection, validateManifest, validateItem, validateTranslations, validatePanels, validateLang, validateColors, validateContainer, validateLabels, validateHeader, + createDefaultActiveViews, splitUrlParts, isManifestPartValid, isItemPartValid, isShowPartValid, isPanelsPartValid, + createDefaultPanelValue, createActiveViewsFromPanelsArray, discoverCustomConfig, discoverUrlConfig, discoverDefaultConfig, + load, setShowPanel, setContentType, setDefaultActiveViews + } + }) + + + + + + + + + + + + \ No newline at end of file diff --git a/src/types.d.ts b/src/types.d.ts index 66a51234..3450db66 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -48,7 +48,52 @@ declare global { total?: number, annotationCollection?: string, modules?: Module[] - + } + + interface Colors { + forceMode: string, + primary: string, + secondary: string, + accent: string + } + + interface ConnectorViewPanel { + id: number, + options?: Option_1_ConnectorViewPanel | Option_2_ConnectorViewPanel | Option_3_ConnectorViewPanel + } + + type Option_1_ConnectorViewPanel = { + collection: { + all: boolean, + }, + manifest: { + all: boolean, + }, + item: { + all: boolean, + }, + } + + type Option_2_ConnectorViewPanel = { + labels: Labels + } + + type Option_3_ConnectorViewPanel = { + types: Array + } + + interface Config { + container: string, + collection: string, + manifest: string, + item: string, + panels: Panel[], + colors: Colors, + header: Header, + labels: Labels, + lang: string, + meta: MetaConfig, + notificationColors: NotificationColors } interface Content { @@ -69,6 +114,13 @@ declare global { value: string } + interface Header { + show: boolean, + navigation: boolean, + panelsToggle: boolean, + languageSwitch: boolean + } + interface Idref { '@context': string, base?: string, @@ -136,11 +188,35 @@ declare global { metadata?: Metadata[] } + interface MetaConfig { + collection: { + all: boolean + }, + manifest: { + all: boolean + }, + item: { + all: boolean + } + } + interface Module { editionManuscripts?: boolean, editionPrints?: boolean } + interface NotificationColors { + info: string, + warning: string + } + + interface Panel { + label: string, + toggle: boolean, + show: boolean, + views: ViewPanel[] + } + type RangeSelector = { type: 'RangeSelector', startSelector: CssSelector, @@ -179,6 +255,13 @@ declare global { } type TitleType = 'main' | 'sub'; + interface ViewPanel { + id: string, + label: string, + connector: ConnectorViewPanel, + default?: boolean + } + } export {} \ No newline at end of file From d162621d9d5b91d1dfa31080bab1f7f3ce313b90 Mon Sep 17 00:00:00 2001 From: malkja Date: Mon, 27 May 2024 10:43:05 +0200 Subject: [PATCH 3/8] refactor: remove the usage of 'config' Vuex module and instead use 'config' Pinia store --- src/App.vue | 31 ++++++++++++++++--------------- src/main.js | 6 +++++- src/store/annotations/actions.js | 13 +++++++++---- src/store/contents/actions.js | 22 ++++++++++++++-------- 4 files changed, 44 insertions(+), 28 deletions(-) diff --git a/src/App.vue b/src/App.vue index b88de9f0..40777fa2 100644 --- a/src/App.vue +++ b/src/App.vue @@ -26,17 +26,19 @@ - - + \ No newline at end of file diff --git a/src/main.js b/src/main.js index 1ae38cf1..0914c20d 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,6 @@ import { createApp, h } from 'vue'; import PrimeVue from 'primevue/config'; +import { createPinia } from 'pinia' import store from './store'; import { i18n } from './i18n'; import App from './App.vue'; @@ -8,6 +9,8 @@ import './css/style.css'; import './css/style.scss'; import { getRGBColor } from '@/utils/color'; +const pinia = createPinia() + function generateId() { return Math.random().toString(36).slice(2, 16); } @@ -23,6 +26,7 @@ window.Tido = function Tido(config = {}) { this.app.provide('config', this.config); this.app.use(PrimeVue); + this.app.use(pinia); this.app.use(store); this.app.use(i18n); @@ -63,4 +67,4 @@ window.Tido = function Tido(config = {}) { this.mount(container); }; -export default window.Tido; +export default window.Tido; \ No newline at end of file diff --git a/src/store/annotations/actions.js b/src/store/annotations/actions.js index c73cf3ea..dfdacebe 100644 --- a/src/store/annotations/actions.js +++ b/src/store/annotations/actions.js @@ -4,7 +4,10 @@ import * as Utils from '@/utils'; import { scrollIntoViewIfNeeded } from '@/utils'; import { getAnnotationListElement } from '@/utils/annotations'; +import { useConfigStore } from '../../stores/config'; + export const addActiveAnnotation = ({ getters, rootGetters, dispatch }, id) => { + const configStore = useConfigStore() const { activeAnnotations, annotations } = getters; const newActiveAnnotation = annotations.find((annotation) => annotation.id === id); @@ -12,7 +15,7 @@ export const addActiveAnnotation = ({ getters, rootGetters, dispatch }, id) => { return; } - const iconName = rootGetters['config/getIconByType'](newActiveAnnotation.body['x-content-type']); + const iconName = configStore.getIconByType(newActiveAnnotation.body['x-content-type']); const activeAnnotationsList = { ...activeAnnotations }; @@ -37,7 +40,8 @@ export const setActiveAnnotations = ({ commit }, activeAnnotations) => { export const setFilteredAnnotations = ({ commit, getters, rootGetters }, types) => { const { annotations } = getters; - const activeContentType = rootGetters['config/activeContentType']; + const configStore = useConfigStore() + const activeContentType = configStore.activeContentType let filteredAnnotations = []; if (annotations !== null) { @@ -143,6 +147,7 @@ export const addHighlightHoverListeners = ({ getters, rootGetters }) => { const annotationElements = Array.from(document.querySelectorAll('[data-annotation]')); const tooltipEl = null; + const configStore = useConfigStore() // Annotations can be nested, so we filter out all outer elements from this selection and // iterate over the deepest elements @@ -161,7 +166,7 @@ export const addHighlightHoverListeners = ({ getters, rootGetters }) => { const { filteredAnnotations } = getters; const annotationTooltipModels = filteredAnnotations.reduce((acc, curr) => { const { id } = curr; - const name = rootGetters['config/getIconByType'](curr.body['x-content-type']); + const name = configStore.getIconByType(curr.body['x-content-type']) acc[id] = { value: curr.body.value, name, @@ -283,4 +288,4 @@ function discoverChildAnnotationIds(el, annotationIds = {}) { } }); return annotationIds; -} +} \ No newline at end of file diff --git a/src/store/contents/actions.js b/src/store/contents/actions.js index 7ce4d4da..496200e7 100644 --- a/src/store/contents/actions.js +++ b/src/store/contents/actions.js @@ -3,6 +3,9 @@ import { i18n } from '@/i18n'; import BookmarkService from '@/services/bookmark'; import { loadCss, loadFont } from '../../utils'; +import { useConfigStore } from '../../stores/config'; + + export const getItemIndex = async ({ getters }, itemUrl) => { const { manifest } = getters; if (!manifest) return -1; @@ -44,9 +47,10 @@ function isItemPartInsideRangeValue(i, numberItems) { export const initCollection = async ({ commit, dispatch, getters, rootGetters, }, url) => { - const { item } = getters; - const resultConfig = rootGetters['config/config']; - let { item: itemUrl } = rootGetters['config/config']; + const configStore = useConfigStore() + const resultConfig = configStore.config; + let item = resultConfig.item; + let itemUrl; let collection = ''; let activeManifest = ''; let manifestIndex; @@ -111,14 +115,14 @@ export const initCollection = async ({ await dispatch('getSupport', support); } commit('setManifest', activeManifest); - - if (!item) dispatch('initItem', itemUrl); + dispatch('initItem', itemUrl); } }; export const initManifest = async ({ commit, dispatch, rootGetters, }, url) => { + const configStore = useConfigStore() let manifest = ''; try { manifest = await request(url); @@ -131,7 +135,7 @@ export const initManifest = async ({ const numberItems = manifest.sequence.length; commit('setManifest', manifest); - const resultConfig = rootGetters['config/config']; + const resultConfig = configStore.config; const { item } = resultConfig; let itemIndex; @@ -193,6 +197,7 @@ export const initItem = async ({ commit, dispatch, getters }, url) => { m, i, } : { i }; + await BookmarkService.updateQuery(query); }; @@ -206,11 +211,12 @@ export const initAnnotations = async ({ commit }, url) => { }; export const getSupport = ({ rootGetters }, support) => { - const { container } = rootGetters['config/config']; + const configStore = useConfigStore() + const { container } = configStore.config; support.forEach((s) => { const hasElement = document.getElementById(s.url); if (s.type === 'font' && !hasElement) loadFont(s.url, container); if (s.type !== 'font' && !hasElement) loadCss(s.url); }); -}; +}; \ No newline at end of file From 003ec7c34a7d2e6c780d48a0f12c894c8aad8317 Mon Sep 17 00:00:00 2001 From: malkja Date: Mon, 27 May 2024 11:22:26 +0200 Subject: [PATCH 4/8] refactor: adapt other components to now use 'config' Pinia store --- src/components/ContentView.vue | 6 ++++-- src/components/Notification.vue | 6 ++++-- src/components/TreeView.vue | 9 ++++++--- src/components/annotations/AnnotationsView.vue | 8 ++++++-- src/components/base/BaseDropdown.vue | 7 +++++-- src/components/header/GlobalHeader.vue | 6 ++++-- src/components/header/Language.vue | 4 +++- src/components/header/Navbar.vue | 10 ++++++---- src/components/header/PanelsToggle.vue | 13 ++++++++----- src/components/panels/PanelsWrapper.vue | 12 ++++++++---- 10 files changed, 54 insertions(+), 27 deletions(-) diff --git a/src/components/ContentView.vue b/src/components/ContentView.vue index b3dde870..c8184f75 100644 --- a/src/components/ContentView.vue +++ b/src/components/ContentView.vue @@ -22,6 +22,7 @@ import { computed, readonly, ref, watch, } from 'vue'; import { useStore } from 'vuex'; +import { useConfigStore } from '../../src/stores/config'; import Notification from '@/components/Notification.vue'; import { request } from '@/utils/http'; import { domParser, delay } from '@/utils'; @@ -34,12 +35,13 @@ const props = defineProps({ const emit = defineEmits(['loading']); const store = useStore(); +const configStore = useConfigStore() const content = ref(''); const errorTextMessage = ref(null); const notificationMessage = readonly(errorTextMessage); -const config = computed(() => store.getters['config/config']); +const config = computed(() => configStore.config); const contentStyle = computed(() => ({ fontSize: `${props.fontSize}px`, })); @@ -118,4 +120,4 @@ function isValidTextContent(text) { .rtl { direction: rtl; } - + \ No newline at end of file diff --git a/src/components/Notification.vue b/src/components/Notification.vue index 69cd8cd9..ab0e1c76 100644 --- a/src/components/Notification.vue +++ b/src/components/Notification.vue @@ -18,6 +18,7 @@ + \ No newline at end of file diff --git a/src/components/TreeView.vue b/src/components/TreeView.vue index 88d99583..41742112 100644 --- a/src/components/TreeView.vue +++ b/src/components/TreeView.vue @@ -48,6 +48,7 @@ import { computed, nextTick, onMounted, ref, watch, } from 'vue'; import { useStore } from 'vuex'; +import { useConfigStore } from '../../src/stores/config'; import { useI18n } from 'vue-i18n'; import { request } from '@/utils/http'; import { isElementVisible } from '@/utils'; @@ -55,6 +56,7 @@ import { isElementVisible } from '@/utils'; const emit = defineEmits(['loading']); const store = useStore(); +const configStore = useConfigStore() const { t } = useI18n(); const expanded = ref({}); @@ -62,7 +64,7 @@ const selected = ref(null); const tree = ref([]); const containerRef = ref(null); -const config = computed(() => store.getters['config/config']); +const config = computed(() => configStore.config); const collectionTitle = computed(() => store.getters['contents/collectionTitle']); const collection = computed(() => store.getters['contents/collection']); const labels = computed(() => (config.value && config.value.labels) || {}); @@ -162,10 +164,11 @@ async function onNodeExpand(node) { } async function onNodeSelect(node) { + const configStore = useConfigStore() if (currentManifest.value.id !== node.parent) { // If we selected an item from a different manifest await store.dispatch('contents/initManifest', node.parent); - await store.dispatch('config/setDefaultActiveViews'); + await configStore.setDefaultActiveViews() } await store.dispatch('contents/initItem', node.key); @@ -204,4 +207,4 @@ function getNodeByKey(key, root) { return root.children.find((child) => !!(getNodeByKey(key, child))); } - + \ No newline at end of file diff --git a/src/components/annotations/AnnotationsView.vue b/src/components/annotations/AnnotationsView.vue index 54c11513..ee1c717a 100644 --- a/src/components/annotations/AnnotationsView.vue +++ b/src/components/annotations/AnnotationsView.vue @@ -28,6 +28,10 @@ import AnnotationsList from '@/components/annotations/AnnotationsList.vue'; import Notification from '@/components/Notification.vue'; import * as AnnotationUtils from '@/utils/annotations'; +import { useConfigStore } from '../../stores/config'; + +const configStore = useConfigStore() + const props = defineProps({ url: String, types: Array, @@ -36,7 +40,7 @@ const props = defineProps({ const store = useStore(); const message = ref('no_annotations_in_view'); -const config = computed(() => store.getters['config/config']); +const config = computed(() => configStore.config); const annotations = computed(() => store.getters['annotations/annotations']); const activeAnnotations = computed(() => store.getters['annotations/activeAnnotations']); const filteredAnnotations = computed(() => store.getters['annotations/filteredAnnotations']); @@ -95,4 +99,4 @@ function highlightTargetsLevel0() { + \ No newline at end of file diff --git a/src/components/base/BaseDropdown.vue b/src/components/base/BaseDropdown.vue index 81117de6..85c6d9d9 100644 --- a/src/components/base/BaseDropdown.vue +++ b/src/components/base/BaseDropdown.vue @@ -18,12 +18,15 @@ + \ No newline at end of file diff --git a/src/components/header/GlobalHeader.vue b/src/components/header/GlobalHeader.vue index dcd98324..d955b9d5 100644 --- a/src/components/header/GlobalHeader.vue +++ b/src/components/header/GlobalHeader.vue @@ -19,6 +19,7 @@ + \ No newline at end of file diff --git a/src/components/header/Language.vue b/src/components/header/Language.vue index 99a65423..b14af5e9 100644 --- a/src/components/header/Language.vue +++ b/src/components/header/Language.vue @@ -22,6 +22,7 @@ import { computed, onMounted, ref, watch, } from 'vue'; import { useStore } from 'vuex'; +import { useConfigStore } from '../../stores/config'; import { useI18n } from 'vue-i18n'; import BaseDropdown from '@/components/base/BaseDropdown.vue'; @@ -31,6 +32,7 @@ interface Language { } const store = useStore(); +const configStore = useConfigStore() const { locale: i18nLocale } = useI18n(); const langs = ref([ @@ -39,7 +41,7 @@ const langs = ref([ ]); const selectedLang = ref(langs.value[0]); const showDropdown = ref(false); -const config = computed(() => store.getters['config/config']); +const config = computed(() => configStore.config); watch( selectedLang, diff --git a/src/components/header/Navbar.vue b/src/components/header/Navbar.vue index 6846a2ca..90ee27bc 100644 --- a/src/components/header/Navbar.vue +++ b/src/components/header/Navbar.vue @@ -26,10 +26,12 @@ + \ No newline at end of file diff --git a/src/components/header/PanelsToggle.vue b/src/components/header/PanelsToggle.vue index 425aefe1..4e850800 100644 --- a/src/components/header/PanelsToggle.vue +++ b/src/components/header/PanelsToggle.vue @@ -63,14 +63,15 @@ import { isMobile } from '@/utils/is-mobile'; import BaseCheckbox from '@/components/base/BaseCheckbox.vue'; import BaseButton from '@/components/base/BaseButton.vue'; import BaseDropdown from '@/components/base/BaseDropdown.vue'; +import { useConfigStore } from '../../stores/config'; const store = useStore(); +const configStore = useConfigStore() const { t } = useI18n(); const toggles = ref([]); const showDropdown = ref(false); - -const panels = computed(() => store.getters['config/config'].panels); +const panels = computed(() => configStore.config.panels ); //store.getters['config/config'].panels); const resetColor = computed(() => (toggles.value.filter(({ show }) => !show).length > 0 ? 'primary' : 'grey-7')); watch( @@ -84,6 +85,7 @@ watch( ); function update(index, show) { + const configStore = useConfigStore() if (show === false) { let numberClosedPanels = 0; // count the number of closed panels, except the current action @@ -103,13 +105,14 @@ function update(index, show) { } toggles.value[index].show = show; - store.dispatch('config/setShowPanel', { index, show }); + configStore.setShowPanel({ index, show }) } function reset() { + const configStore = useConfigStore() toggles.value.forEach((toggle, index) => { toggles.value[index].show = true; - store.dispatch('config/setShowPanel', { index, show: true }); + configStore.setShowPanel({ index, show: true }) }); } @@ -122,4 +125,4 @@ function handleToggleTitle(idx) { ? `${t('hide')} ${titleUpper} Panel` : `${t('show')} ${titleUpper} Panel`; } - + \ No newline at end of file diff --git a/src/components/panels/PanelsWrapper.vue b/src/components/panels/PanelsWrapper.vue index 3c164a4d..844d9daa 100644 --- a/src/components/panels/PanelsWrapper.vue +++ b/src/components/panels/PanelsWrapper.vue @@ -16,22 +16,26 @@ import { computed } from 'vue'; import { useStore } from 'vuex'; +import { useConfigStore } from '../../stores/config'; + import Panel from '@/components/panels/Panel.vue'; const store = useStore(); +const configStore = useConfigStore() + const panels = computed(() => { const { panels } = config.value; return panels; }); -const config = computed(() => store.getters['config/config']); -const activeViews = computed(() => store.getters['config/activeViews']); +const config = computed(() => configStore.config); +const activeViews = computed(() => configStore.activeViews); function onActiveViewChange(viewIndex, panelIndex) { - store.dispatch('config/setActivePanelView', { viewIndex, panelIndex }); + configStore.setActivePanelView(viewIndex, panelIndex) } function getActiveView(panelIndex) { return activeViews.value[panelIndex]; } - + \ No newline at end of file From e53b29233e53950fa708d55126c67634c0fc82a9 Mon Sep 17 00:00:00 2001 From: malkja Date: Thu, 30 May 2024 13:17:17 +0200 Subject: [PATCH 5/8] refactor: minor --- package.json | 4 +--- src/App.vue | 2 +- src/components/ContentView.vue | 2 +- src/components/Notification.vue | 2 +- src/components/TreeView.vue | 2 +- src/components/annotations/AnnotationsView.vue | 2 +- src/components/base/BaseDropdown.vue | 2 +- src/components/header/GlobalHeader.vue | 2 +- src/components/header/Language.vue | 2 +- src/components/header/Navbar.vue | 2 +- src/components/header/PanelsToggle.vue | 2 +- src/components/panels/PanelsWrapper.vue | 2 +- src/store/annotations/actions.js | 2 +- src/store/contents/actions.js | 2 +- 14 files changed, 14 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 2e950385..90622cfb 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "http-server": "^14.1.1", "ncp": "^2.0.0", "openseadragon": "^3.1.0", + "pinia": "^2.1.7", "primevue": "^3.49.1", "sass": "^1.71.1", "standard-version": "^9.5.0", @@ -90,8 +91,5 @@ ], "publishConfig": { "@subugoe:registry": "https://gitlab.gwdg.de/api/v4/projects/10921/packages/npm/" - }, - "dependencies": { - "pinia": "^2.1.7" } } diff --git a/src/App.vue b/src/App.vue index 40777fa2..f653905a 100644 --- a/src/App.vue +++ b/src/App.vue @@ -37,7 +37,7 @@ import { computed, inject, onMounted, ref, } from 'vue'; import { useStore } from 'vuex'; -import { useConfigStore } from './stores/config'; +import { useConfigStore } from '@/stores/config'; import { useI18n } from 'vue-i18n'; import GlobalHeader from '@/components/header/GlobalHeader.vue'; diff --git a/src/components/ContentView.vue b/src/components/ContentView.vue index c8184f75..8beac3fb 100644 --- a/src/components/ContentView.vue +++ b/src/components/ContentView.vue @@ -22,7 +22,7 @@ import { computed, readonly, ref, watch, } from 'vue'; import { useStore } from 'vuex'; -import { useConfigStore } from '../../src/stores/config'; +import { useConfigStore } from '@/stores/config'; import Notification from '@/components/Notification.vue'; import { request } from '@/utils/http'; import { domParser, delay } from '@/utils'; diff --git a/src/components/Notification.vue b/src/components/Notification.vue index ab0e1c76..7b6c52e1 100644 --- a/src/components/Notification.vue +++ b/src/components/Notification.vue @@ -18,7 +18,7 @@ diff --git a/src/components/base/BaseCheckbox.vue b/src/components/base/BaseCheckbox.vue index 59116499..654dbff3 100644 --- a/src/components/base/BaseCheckbox.vue +++ b/src/components/base/BaseCheckbox.vue @@ -1,6 +1,5 @@ diff --git a/src/components/panels/Panel.vue b/src/components/panels/Panel.vue index 104bffc0..bed3c1eb 100644 --- a/src/components/panels/Panel.vue +++ b/src/components/panels/Panel.vue @@ -95,6 +95,7 @@ import { computed, nextTick, ref, watch, } from 'vue'; import { useStore } from 'vuex'; +import { useConfigStore } from '@/stores/config'; import { useI18n } from 'vue-i18n'; import TabView from 'primevue/tabview'; import TabPanel from 'primevue/tabpanel'; @@ -136,6 +137,7 @@ export default { }, setup(props, { emit }) { const store = useStore(); + const configStore = useConfigStore() const { t } = useI18n(); const tabs = ref([]); @@ -317,7 +319,7 @@ export default { [contentItem] = item.value.content; // TODO: this should be moved to loading time in order dynamically recognize all content types // instead of only the first one - store.dispatch('config/setContentType', contentItem.type.split('type=')[1]); + configStore.setContentType(contentItem.type.split('type=')[1]); } contentItem = item.value.content.find((c) => c.type.split('type=')[1] === type); diff --git a/src/components/panels/PanelsWrapper.vue b/src/components/panels/PanelsWrapper.vue index 72a5af94..49645dd5 100644 --- a/src/components/panels/PanelsWrapper.vue +++ b/src/components/panels/PanelsWrapper.vue @@ -15,12 +15,10 @@