]> source.dussan.org Git - nextcloud-server.git/commitdiff
Use svg icons
authorLouis Chemineau <louis@chmn.me>
Wed, 9 Nov 2022 12:10:42 +0000 (13:10 +0100)
committerLouis Chemineau <louis@chmn.me>
Mon, 28 Nov 2022 16:31:27 +0000 (17:31 +0100)
Signed-off-by: Louis Chemineau <louis@chmn.me>
apps/comments/src/comments-tab.js
apps/files/src/models/Tab.js
apps/files/src/views/Sidebar.vue
apps/files_sharing/src/files_sharing_tab.js
apps/files_versions/src/files_versions_tab.js
apps/files_versions/src/views/VersionTab.vue
package-lock.json
package.json
webpack.common.js

index 7b12de6de4f96c53a670b7e14029a66e12cc2cc2..ef17ce984a4f8bdec66f97b92d566f5a18a37092 100644 (file)
  *
  */
 
-import MessageReplyText from 'vue-material-design-icons/MessageReplyText.vue'
+import MessageReplyText from '@mdi/svg/svg/message-reply-text.svg?raw'
 
 // Init Comments tab component
 let TabInstance = null
 const commentTab = new OCA.Files.Sidebar.Tab({
        id: 'comments',
        name: t('comments', 'Comments'),
-       icon: 'icon-comment',
+       iconSvg: MessageReplyText,
 
        async mount(el, fileInfo, context) {
                if (TabInstance) {
@@ -53,7 +53,7 @@ const commentTab = new OCA.Files.Sidebar.Tab({
        },
 })
 
-window.addEventListener('DOMContentLoaded', function() {
+window.addEventListener('DOMContentLoaded', function () {
        if (OCA.Files && OCA.Files.Sidebar) {
                OCA.Files.Sidebar.registerTab(commentTab)
        }
index 9fd38f71bd735a39056392ea3d5aafc358e9dd76..cbf35c77dcb461b99d91004cda8d6333feee938c 100644 (file)
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  *
  */
+import { sanitizeSVG } from '@skjnldsv/sanitize-svg'
 
 export default class Tab {
 
        _id
        _name
        _icon
+       _iconSvgSanitized
        _mount
        _update
        _destroy
@@ -37,19 +39,20 @@ export default class Tab {
         * @param {object} options destructuring object
         * @param {string} options.id the unique id of this tab
         * @param {string} options.name the translated tab name
-        * @param {string} options.icon the vue component
+        * @param {?string} options.icon the icon css class
+        * @param {?string} options.iconSvg the icon in svg format
         * @param {Function} options.mount function to mount the tab
         * @param {Function} options.update function to update the tab
         * @param {Function} options.destroy function to destroy the tab
         * @param {Function} [options.enabled] define conditions whether this tab is active. Must returns a boolean
         * @param {Function} [options.scrollBottomReached] executed when the tab is scrolled to the bottom
         */
-       constructor({ id, name, icon, mount, update, destroy, enabled, scrollBottomReached } = {}) {
+       constructor({ id, name, icon, iconSvg, mount, update, destroy, enabled, scrollBottomReached } = {}) {
                if (enabled === undefined) {
                        enabled = () => true
                }
                if (scrollBottomReached === undefined) {
-                       scrollBottomReached = () => {}
+                       scrollBottomReached = () => { }
                }
 
                // Sanity checks
@@ -59,8 +62,8 @@ export default class Tab {
                if (typeof name !== 'string' || name.trim() === '') {
                        throw new Error('The name argument is not a valid string')
                }
-               if ((typeof icon !== 'string' || icon.trim() === '') && typeof icon !== 'object') {
-                       throw new Error('The icon argument is not a valid string or vuejs component')
+               if ((typeof icon !== 'string' || icon.trim() === '') && typeof iconSvg !== 'string') {
+                       throw new Error('Missing valid string for icon or iconSvg argument')
                }
                if (typeof mount !== 'function') {
                        throw new Error('The mount argument should be a function')
@@ -81,12 +84,20 @@ export default class Tab {
                this._id = id
                this._name = name
                this._icon = icon
+               this._iconSvg = iconSvg
                this._mount = mount
                this._update = update
                this._destroy = destroy
                this._enabled = enabled
                this._scrollBottomReached = scrollBottomReached
 
+               if (typeof iconSvg === 'string') {
+                       sanitizeSVG(iconSvg)
+                               .then(sanitizedSvg => {
+                                       this._iconSvgSanitized = sanitizedSvg
+                               })
+               }
+
        }
 
        get id() {
@@ -97,14 +108,14 @@ export default class Tab {
                return this._name
        }
 
-       get isIconClass() {
-               return typeof this._icon === 'string'
-       }
-
        get icon() {
                return this._icon
        }
 
+       get iconSvg() {
+               return this._iconSvgSanitized
+       }
+
        get mount() {
                return this._mount
        }
index 7c5ac8f0fdbba8b31c5dce5a3d52f82855b500c9..6c7d391b3c1b3275464582f90ab312b1a62d1dfa 100644 (file)
                                :id="tab.id"
                                :key="tab.id"
                                :name="tab.name"
-                               :icon="tab.isIconClass ? tab.icon : undefined"
+                               :icon="tab.icon"
                                :on-mount="tab.mount"
                                :on-update="tab.update"
                                :on-destroy="tab.destroy"
                                :on-scroll-bottom-reached="tab.scrollBottomReached"
                                :file-info="fileInfo">
-                               <template #icon v-if="!tab.isIconClass">
-                                       <component :is="tab.icon" />
+                               <template v-if="tab.iconSvg !== undefined" #icon>
+                                       <!-- eslint-disable-next-line vue/no-v-html -->
+                                       <span class="svg-icon" v-html="tab.iconSvg" />
                                </template>
                        </SidebarTab>
                </template>
@@ -512,5 +513,13 @@ export default {
                top: 0 !important;
                height: 100% !important;
        }
+
+       .svg-icon {
+               ::v-deep svg {
+                       width: 20px;
+                       height: 20px;
+                       fill: var(--color-main-text);
+               }
+       }
 }
 </style>
index 9694e2a953916add9ab9fe36a9049bc478baff0c..ed515e86e01c5fd72276d42c214417bff4624c6c 100644 (file)
@@ -31,7 +31,7 @@ import ExternalLinkActions from './services/ExternalLinkActions.js'
 import ExternalShareActions from './services/ExternalShareActions.js'
 import TabSections from './services/TabSections.js'
 
-import ShareVariant from 'vue-material-design-icons/ShareVariant.vue'
+import ShareVariant from '@mdi/svg/svg/share-variant.svg?raw'
 
 // Init Sharing Tab Service
 if (!window.OCA.Sharing) {
@@ -50,12 +50,12 @@ Vue.use(VueClipboard)
 const View = Vue.extend(SharingTab)
 let TabInstance = null
 
-window.addEventListener('DOMContentLoaded', function() {
+window.addEventListener('DOMContentLoaded', function () {
        if (OCA.Files && OCA.Files.Sidebar) {
                OCA.Files.Sidebar.registerTab(new OCA.Files.Sidebar.Tab({
                        id: 'sharing',
                        name: t('files_sharing', 'Sharing'),
-                       icon: ShareVariant,
+                       iconSvg: ShareVariant,
 
                        async mount(el, fileInfo, context) {
                                if (TabInstance) {
index d293a68510c3399af6d0845a8d421e13b36ae2ee..b4ab075b7a89926486984f9bfca2d1d486b1f731 100644 (file)
@@ -22,7 +22,7 @@ import { translate as t, translatePlural as n } from '@nextcloud/l10n'
 
 import VersionTab from './views/VersionTab.vue'
 import VTooltip from 'v-tooltip'
-import BackupRestore from 'vue-material-design-icons/BackupRestore.vue'
+import BackupRestore from '@mdi/svg/svg/backup-restore.svg?raw'
 
 Vue.prototype.t = t
 Vue.prototype.n = n
@@ -33,12 +33,12 @@ Vue.use(VTooltip)
 const View = Vue.extend(VersionTab)
 let TabInstance = null
 
-window.addEventListener('DOMContentLoaded', function() {
+window.addEventListener('DOMContentLoaded', function () {
        if (OCA.Files && OCA.Files.Sidebar) {
                OCA.Files.Sidebar.registerTab(new OCA.Files.Sidebar.Tab({
                        id: 'version_vue',
                        name: t('files_versions', 'Version'),
-                       icon: BackupRestore,
+                       iconSvg: BackupRestore,
 
                        async mount(el, fileInfo, context) {
                                if (TabInstance) {
index 119dc95a60d03488a218bb74e292bb1f93b0df05..90664491941339f34ff100a63bb6ad32ed74fd35 100644 (file)
@@ -19,8 +19,8 @@
        <div>
                <ul>
                        <NcListItem v-for="version in versions"
+                               :key="version.dateTime.unix()"
                                class="version"
-                               key="version.url"
                                :title="version.title"
                                :href="version.url">
                                <template #icon>
@@ -47,7 +47,7 @@
                                                </template>
                                                {{ t('files_versions', 'Download version') }}
                                        </NcActionLink>
-                                       <NcActionButton @click="restoreVersion(version)" v-if="!version.isCurrent">
+                                       <NcActionButton v-if="!version.isCurrent" @click="restoreVersion(version)">
                                                <template #icon>
                                                        <BackupRestore :size="22" />
                                                </template>
@@ -73,7 +73,6 @@ import { generateRemoteUrl, generateUrl } from '@nextcloud/router'
 import { getCurrentUser } from '@nextcloud/auth'
 import BackupRestore from 'vue-material-design-icons/BackupRestore.vue'
 import Download from 'vue-material-design-icons/Download.vue'
-import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
 import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
 import NcActionLink from '@nextcloud/vue/dist/Components/NcActionLink.js'
 import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js'
@@ -108,6 +107,9 @@ function getDavRequest() {
 
 /**
  * Format version
+ *
+ * @param version
+ * @param fileInfo
  */
 function formatVersion(version, fileInfo) {
        const fileVersion = basename(version.filename)
@@ -117,7 +119,8 @@ function formatVersion(version, fileInfo) {
                ? generateUrl('/core/preview?fileId={fileId}&c={fileEtag}&x=250&y=250&forceIcon=0&a=0', {
                        fileId: fileInfo.id,
                        fileEtag: fileInfo.etag,
-               }) : generateUrl('/apps/files_versions/preview?file={file}&version={fileVersion}', {
+               })
+               : generateUrl('/apps/files_versions/preview?file={file}&version={fileVersion}', {
                        file: joinPaths(fileInfo.path, fileInfo.name),
                        fileVersion,
                })
@@ -151,7 +154,6 @@ const client = createClient(remote)
 export default {
        name: 'VersionTab',
        components: {
-               NcButton,
                NcEmptyContent,
                NcActionLink,
                NcActionButton,
@@ -192,7 +194,7 @@ export default {
                                this.versions = response.map(version => formatVersion(version, this.fileInfo))
                                this.loading = false
                        } catch (exception) {
-                               logger.error('Could not fetch version', {exception})
+                               logger.error('Could not fetch version', { exception })
                                this.loading = false
                        }
                },
@@ -205,14 +207,14 @@ export default {
                async restoreVersion(version) {
                        try {
                                logger.debug('restoring version', version.url)
-                               const response = await client.moveFile(
+                               await client.moveFile(
                                        `/versions/${getCurrentUser().uid}/versions/${this.fileInfo.id}/${version.fileVersion}`,
                                        `/versions/${getCurrentUser().uid}/restore/target`
                                )
                                showSuccess(t('files_versions', 'Version restored'))
                                await this.fetchVersions()
                        } catch (exception) {
-                               logger.error('Could not restore version', {exception})
+                               logger.error('Could not restore version', { exception })
                                showError(t('files_versions', 'Could not restore version'))
                        }
                },
index a27c887e51b3a97802ff7d5ebb2f1653d2d4d8bc..91136685f190d4424f84d3cb911c1b073c00f4f0 100644 (file)
@@ -10,6 +10,7 @@
       "license": "AGPL-3.0-or-later",
       "dependencies": {
         "@chenfengyuan/vue-qrcode": "^1.0.2",
+        "@mdi/svg": "^7.0.96",
         "@nextcloud/auth": "^1.3.0",
         "@nextcloud/axios": "^1.10.0",
         "@nextcloud/browser-storage": "^0.1.1",
@@ -29,6 +30,7 @@
         "@nextcloud/sharing": "^0.1.0",
         "@nextcloud/vue": "^7.1.0-beta.2",
         "@nextcloud/vue-dashboard": "^2.0.1",
+        "@skjnldsv/sanitize-svg": "^1.0.2",
         "autosize": "^5.0.1",
         "backbone": "^1.4.1",
         "blueimp-md5": "^2.19.0",
         "@jridgewell/sourcemap-codec": "^1.4.10"
       }
     },
+    "node_modules/@mdi/svg": {
+      "version": "7.0.96",
+      "resolved": "https://registry.npmjs.org/@mdi/svg/-/svg-7.0.96.tgz",
+      "integrity": "sha512-5DC+w7Kl2C82j4aTWCUf6wtHzgY60WBf1gT1qrpkLaMNcH6Vj9FpYPAXdSmtdkmSMvVMs8i1Rtv9cXWcHFQYpw=="
+    },
     "node_modules/@nextcloud/auth": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/@nextcloud/auth/-/auth-1.3.0.tgz",
       "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
       "dev": true
     },
+    "node_modules/@skjnldsv/sanitize-svg": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@skjnldsv/sanitize-svg/-/sanitize-svg-1.0.2.tgz",
+      "integrity": "sha512-blfdQZ9jr4K9IOhifF0FVhKf9LCFH0L8wWR/vEgdA53q8DGNEbjUGMNo4VU1QugglaoQdFy65O2abODRFflsSg==",
+      "dependencies": {
+        "is-svg": "^4.3.2"
+      },
+      "engines": {
+        "node": "^14.0.0",
+        "npm": "^7.0.0"
+      }
+    },
     "node_modules/@socket.io/component-emitter": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-svg": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-4.3.2.tgz",
+      "integrity": "sha512-mM90duy00JGMyjqIVHu9gNTjywdZV+8qNasX8cm/EEYZ53PHDgajvbBwNVvty5dwSAxLUD3p3bdo+7sR/UMrpw==",
+      "dependencies": {
+        "fast-xml-parser": "^3.19.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/is-symbol": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
         "@jridgewell/sourcemap-codec": "^1.4.10"
       }
     },
+    "@mdi/svg": {
+      "version": "7.0.96",
+      "resolved": "https://registry.npmjs.org/@mdi/svg/-/svg-7.0.96.tgz",
+      "integrity": "sha512-5DC+w7Kl2C82j4aTWCUf6wtHzgY60WBf1gT1qrpkLaMNcH6Vj9FpYPAXdSmtdkmSMvVMs8i1Rtv9cXWcHFQYpw=="
+    },
     "@nextcloud/auth": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/@nextcloud/auth/-/auth-1.3.0.tgz",
       "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
       "dev": true
     },
+    "@skjnldsv/sanitize-svg": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@skjnldsv/sanitize-svg/-/sanitize-svg-1.0.2.tgz",
+      "integrity": "sha512-blfdQZ9jr4K9IOhifF0FVhKf9LCFH0L8wWR/vEgdA53q8DGNEbjUGMNo4VU1QugglaoQdFy65O2abODRFflsSg==",
+      "requires": {
+        "is-svg": "^4.3.2"
+      }
+    },
     "@socket.io/component-emitter": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
         "has-tostringtag": "^1.0.0"
       }
     },
+    "is-svg": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-4.3.2.tgz",
+      "integrity": "sha512-mM90duy00JGMyjqIVHu9gNTjywdZV+8qNasX8cm/EEYZ53PHDgajvbBwNVvty5dwSAxLUD3p3bdo+7sR/UMrpw==",
+      "requires": {
+        "fast-xml-parser": "^3.19.0"
+      }
+    },
     "is-symbol": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
index f61edbd7d2352db391100c1412383ad18ce65559..0f0e4d5e5c7fd88b768d104885f68a8bbf28ac2b 100644 (file)
@@ -30,6 +30,7 @@
   "license": "AGPL-3.0-or-later",
   "dependencies": {
     "@chenfengyuan/vue-qrcode": "^1.0.2",
+    "@mdi/svg": "^7.0.96",
     "@nextcloud/auth": "^1.3.0",
     "@nextcloud/axios": "^1.10.0",
     "@nextcloud/browser-storage": "^0.1.1",
@@ -49,6 +50,7 @@
     "@nextcloud/sharing": "^0.1.0",
     "@nextcloud/vue": "^7.1.0-beta.2",
     "@nextcloud/vue-dashboard": "^2.0.1",
+    "@skjnldsv/sanitize-svg": "^1.0.2",
     "autosize": "^5.0.1",
     "backbone": "^1.4.1",
     "blueimp-md5": "^2.19.0",
index a064cd2a2a67bfafad732c0ff4bb3a6f23948e33..c28bd764e4ec5cc845c69d9cb2086528cc60cce5 100644 (file)
@@ -116,7 +116,10 @@ module.exports = {
                                test: /\.handlebars/,
                                loader: 'handlebars-loader',
                        },
-
+                       {
+                               resourceQuery: /raw/,
+                               type: 'asset/source',
+                       },
                ],
        },