Merge branch 'master' into feat/bread
Signed-off-by: John Molakvoæ <skjnldsv@users.noreply.github.com>
This commit is contained in:
Коммит
50f3444878
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -2,6 +2,16 @@
|
|||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [v7.8.5](https://github.com/nextcloud/nextcloud-vue/tree/v7.8.5) (2023-03-28)
|
||||
|
||||
[Full Changelog](https://github.com/nextcloud/nextcloud-vue/compare/v7.8.4...v7.8.5)
|
||||
|
||||
### :bug: Fixed bugs
|
||||
|
||||
- fix\(NcRichContenteditable\): Completely stop event propagation for keyup events [\#3937](https://github.com/nextcloud/nextcloud-vue/pull/3937) ([nickvergessen](https://github.com/nickvergessen))
|
||||
- fix\(NcRichText\): Match IP addresses as links [\#3935](https://github.com/nextcloud/nextcloud-vue/pull/3935) ([nickvergessen](https://github.com/nickvergessen))
|
||||
- fix\(NcRichText\): Fix NcRichText style [\#3932](https://github.com/nextcloud/nextcloud-vue/pull/3932) ([julien-nc](https://github.com/julien-nc))
|
||||
|
||||
## [v7.8.4](https://github.com/nextcloud/nextcloud-vue/tree/v7.8.4) (2023-03-24)
|
||||
|
||||
[Full Changelog](https://github.com/nextcloud/nextcloud-vue/compare/v7.8.3...v7.8.4)
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
{
|
||||
"name": "@nextcloud/vue",
|
||||
"version": "7.8.4",
|
||||
"version": "7.8.5",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@nextcloud/vue",
|
||||
"version": "7.8.4",
|
||||
"version": "7.8.5",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.1.0",
|
||||
"@nextcloud/auth": "^2.0.0",
|
||||
"@nextcloud/axios": "^2.0.0",
|
||||
"@nextcloud/browser-storage": "^0.2.0",
|
||||
"@nextcloud/calendar-js": "^5.0.3",
|
||||
"@nextcloud/calendar-js": "^6.0.0",
|
||||
"@nextcloud/capabilities": "^1.0.4",
|
||||
"@nextcloud/dialogs": "^4.0.0",
|
||||
"@nextcloud/event-bus": "^3.0.0",
|
||||
|
@ -1887,6 +1887,15 @@
|
|||
"node": ">= 0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/@cypress/request/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/@cypress/vue2": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/vue2/-/vue2-2.0.1.tgz",
|
||||
|
@ -2949,16 +2958,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@nextcloud/calendar-js": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@nextcloud/calendar-js/-/calendar-js-5.0.4.tgz",
|
||||
"integrity": "sha512-/vQlQZL59wSEI8YRhae1vumSq4iY7dxueytH7HsPzdE8C1iX3qJdMANQflSQfFpd8u9gUfuyRD+/RsguKjNeKw==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@nextcloud/calendar-js/-/calendar-js-6.0.0.tgz",
|
||||
"integrity": "sha512-kZBRFIG8J3TNU6K92iEpNzBa3r9JbpCr1MZFJHqVy/5+xTtQG9FqsHhqUWptPwLEBhUNMwN+oCCa7QJAnBKKyg==",
|
||||
"engines": {
|
||||
"node": ">=16.0.0",
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ical.js": "^1.5.0",
|
||||
"uuid": "^8.3.2"
|
||||
"uuid": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nextcloud/capabilities": {
|
||||
|
@ -3168,7 +3177,7 @@
|
|||
},
|
||||
"node_modules/@nextcloud/webpack-vue-config": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "git+ssh://git@github.com/nextcloud/webpack-vue-config.git#62f0c1a419579ed950ba39ecbd7ae7119fb565a1",
|
||||
"resolved": "git+ssh://git@github.com/nextcloud/webpack-vue-config.git#841abf3c3505d17d478b2149004448127f0bcc29",
|
||||
"integrity": "sha512-bKlGYEqblSiSHNmpaGM9fz/f9v6JNwHp63V63yaI26gE0Zs+DpZSzJWC6HWWbJ1BgmoKT7wLN1GJc4W/NxvnxQ==",
|
||||
"dev": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
@ -8126,9 +8135,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/cypress": {
|
||||
"version": "12.8.1",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-12.8.1.tgz",
|
||||
"integrity": "sha512-lIFbKdaSYAOarNLHNFa2aPZu6YSF+8UY4VRXMxJrFUnk6RvfG0AWsZ7/qle/aIz30TNUD4aOihz2ZgS4vuQVSA==",
|
||||
"version": "12.9.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-12.9.0.tgz",
|
||||
"integrity": "sha512-Ofe09LbHKgSqX89Iy1xen2WvpgbvNxDzsWx3mgU1mfILouELeXYGwIib3ItCwoRrRifoQwcBFmY54Vs0zw7QCg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
|
@ -9655,9 +9664,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-cypress": {
|
||||
"version": "2.12.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.12.1.tgz",
|
||||
"integrity": "sha512-c2W/uPADl5kospNDihgiLc7n87t5XhUbFDoTl6CfVkmG+kDAb5Ux10V9PoLPu9N+r7znpc+iQlcmAqT1A/89HA==",
|
||||
"version": "2.13.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.13.1.tgz",
|
||||
"integrity": "sha512-THjc7IT3S9H4KwmRhzAhMGQaEqy78/7W75He/gBhJEH0vIuAY16vOI4YSliDo/ZY+Wm6DtvMHR+8uVvICcI3Lw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"globals": "^11.12.0"
|
||||
|
@ -15899,9 +15908,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/linkify-string": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/linkify-string/-/linkify-string-4.1.0.tgz",
|
||||
"integrity": "sha512-mw4KyPoE/vP0lamGbFFtDsutxOw0b+3g2/lH5bwS7X4tRHQyLBoJ60avPVGUoHfU8G1bLS329u13hhpxBIqFiA==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/linkify-string/-/linkify-string-4.1.1.tgz",
|
||||
"integrity": "sha512-9+kj8xr7GLiyNyO9ri7lIxq2ixVYjjqvtomPQpeYNNT56/PxQq6utzXFLm8HxOaGTiMpimj1UAQWwYYPV88L1g==",
|
||||
"peerDependencies": {
|
||||
"linkifyjs": "^4.0.0"
|
||||
}
|
||||
|
@ -21504,6 +21513,15 @@
|
|||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/sockjs/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/source-list-map": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
|
||||
|
@ -23958,9 +23976,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
|
@ -28432,6 +28451,12 @@
|
|||
"combined-stream": "^1.0.6",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -29257,9 +29282,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"@nextcloud/calendar-js": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@nextcloud/calendar-js/-/calendar-js-5.0.4.tgz",
|
||||
"integrity": "sha512-/vQlQZL59wSEI8YRhae1vumSq4iY7dxueytH7HsPzdE8C1iX3qJdMANQflSQfFpd8u9gUfuyRD+/RsguKjNeKw==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@nextcloud/calendar-js/-/calendar-js-6.0.0.tgz",
|
||||
"integrity": "sha512-kZBRFIG8J3TNU6K92iEpNzBa3r9JbpCr1MZFJHqVy/5+xTtQG9FqsHhqUWptPwLEBhUNMwN+oCCa7QJAnBKKyg==",
|
||||
"requires": {}
|
||||
},
|
||||
"@nextcloud/capabilities": {
|
||||
|
@ -29406,10 +29431,10 @@
|
|||
"requires": {}
|
||||
},
|
||||
"@nextcloud/webpack-vue-config": {
|
||||
"version": "git+ssh://git@github.com/nextcloud/webpack-vue-config.git#62f0c1a419579ed950ba39ecbd7ae7119fb565a1",
|
||||
"version": "git+ssh://git@github.com/nextcloud/webpack-vue-config.git#841abf3c3505d17d478b2149004448127f0bcc29",
|
||||
"integrity": "sha512-bKlGYEqblSiSHNmpaGM9fz/f9v6JNwHp63V63yaI26gE0Zs+DpZSzJWC6HWWbJ1BgmoKT7wLN1GJc4W/NxvnxQ==",
|
||||
"dev": true,
|
||||
"from": "@nextcloud/webpack-vue-config@github:nextcloud/webpack-vue-config#62f0c1a419579ed950ba39ecbd7ae7119fb565a1",
|
||||
"from": "@nextcloud/webpack-vue-config@github:nextcloud/webpack-vue-config#841abf3c3505d17d478b2149004448127f0bcc29",
|
||||
"requires": {}
|
||||
},
|
||||
"@nicolo-ribaudo/eslint-scope-5-internals": {
|
||||
|
@ -33291,9 +33316,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"cypress": {
|
||||
"version": "12.8.1",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-12.8.1.tgz",
|
||||
"integrity": "sha512-lIFbKdaSYAOarNLHNFa2aPZu6YSF+8UY4VRXMxJrFUnk6RvfG0AWsZ7/qle/aIz30TNUD4aOihz2ZgS4vuQVSA==",
|
||||
"version": "12.9.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-12.9.0.tgz",
|
||||
"integrity": "sha512-Ofe09LbHKgSqX89Iy1xen2WvpgbvNxDzsWx3mgU1mfILouELeXYGwIib3ItCwoRrRifoQwcBFmY54Vs0zw7QCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@cypress/request": "^2.88.10",
|
||||
|
@ -34657,9 +34682,9 @@
|
|||
}
|
||||
},
|
||||
"eslint-plugin-cypress": {
|
||||
"version": "2.12.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.12.1.tgz",
|
||||
"integrity": "sha512-c2W/uPADl5kospNDihgiLc7n87t5XhUbFDoTl6CfVkmG+kDAb5Ux10V9PoLPu9N+r7znpc+iQlcmAqT1A/89HA==",
|
||||
"version": "2.13.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.13.1.tgz",
|
||||
"integrity": "sha512-THjc7IT3S9H4KwmRhzAhMGQaEqy78/7W75He/gBhJEH0vIuAY16vOI4YSliDo/ZY+Wm6DtvMHR+8uVvICcI3Lw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"globals": "^11.12.0"
|
||||
|
@ -39222,9 +39247,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"linkify-string": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/linkify-string/-/linkify-string-4.1.0.tgz",
|
||||
"integrity": "sha512-mw4KyPoE/vP0lamGbFFtDsutxOw0b+3g2/lH5bwS7X4tRHQyLBoJ60avPVGUoHfU8G1bLS329u13hhpxBIqFiA==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/linkify-string/-/linkify-string-4.1.1.tgz",
|
||||
"integrity": "sha512-9+kj8xr7GLiyNyO9ri7lIxq2ixVYjjqvtomPQpeYNNT56/PxQq6utzXFLm8HxOaGTiMpimj1UAQWwYYPV88L1g==",
|
||||
"requires": {}
|
||||
},
|
||||
"linkifyjs": {
|
||||
|
@ -43423,6 +43448,14 @@
|
|||
"faye-websocket": "^0.11.3",
|
||||
"uuid": "^8.3.2",
|
||||
"websocket-driver": "^0.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"sockjs-client": {
|
||||
|
@ -45340,9 +45373,10 @@
|
|||
"dev": true
|
||||
},
|
||||
"uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
|
||||
"peer": true
|
||||
},
|
||||
"uvu": {
|
||||
"version": "0.5.6",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@nextcloud/vue",
|
||||
"version": "7.8.4",
|
||||
"version": "7.8.5",
|
||||
"description": "Nextcloud vue components",
|
||||
"keywords": [
|
||||
"vuejs",
|
||||
|
@ -44,7 +44,7 @@
|
|||
"@nextcloud/auth": "^2.0.0",
|
||||
"@nextcloud/axios": "^2.0.0",
|
||||
"@nextcloud/browser-storage": "^0.2.0",
|
||||
"@nextcloud/calendar-js": "^5.0.3",
|
||||
"@nextcloud/calendar-js": "^6.0.0",
|
||||
"@nextcloud/capabilities": "^1.0.4",
|
||||
"@nextcloud/dialogs": "^4.0.0",
|
||||
"@nextcloud/event-bus": "^3.0.0",
|
||||
|
|
|
@ -191,6 +191,7 @@ export default {
|
|||
<slot name="icon">
|
||||
<span :class="[isIconUrl ? 'action-button__icon--url' : icon]"
|
||||
:style="{ backgroundImage: isIconUrl ? `url(${icon})` : null }"
|
||||
:aria-hidden="ariaHidden"
|
||||
class="action-button__icon" />
|
||||
</slot>
|
||||
|
||||
|
@ -240,6 +241,13 @@ export default {
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* aria-hidden attribute for the icon slot
|
||||
*/
|
||||
ariaHidden: {
|
||||
type: Boolean,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
|
|
|
@ -132,6 +132,7 @@ For the multiselect component, all events will be passed through. Please see the
|
|||
<slot name="icon">
|
||||
<span :class="[isIconUrl ? 'action-input__icon--url' : icon]"
|
||||
:style="{ backgroundImage: isIconUrl ? `url(${icon})` : null }"
|
||||
:aria-hidden="ariaHidden"
|
||||
class="action-input__icon" />
|
||||
</slot>
|
||||
</span>
|
||||
|
@ -341,6 +342,13 @@ export default {
|
|||
type: String,
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* aria-hidden attribute for the icon slot
|
||||
*/
|
||||
ariaHidden: {
|
||||
type: Boolean,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
emits: [
|
||||
|
|
|
@ -62,6 +62,7 @@ export default {
|
|||
<slot name="icon">
|
||||
<span :class="[isIconUrl ? 'action-link__icon--url' : icon]"
|
||||
:style="{ backgroundImage: isIconUrl ? `url(${icon})` : null }"
|
||||
:aria-hidden="ariaHidden"
|
||||
class="action-link__icon" />
|
||||
</slot>
|
||||
|
||||
|
@ -134,6 +135,20 @@ export default {
|
|||
return value && (!value.startsWith('_') || ['_blank', '_self', '_parent', '_top'].indexOf(value) > -1)
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Declares a native tooltip when not null
|
||||
*/
|
||||
title: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
/**
|
||||
* aria-hidden attribute for the icon slot
|
||||
*/
|
||||
ariaHidden: {
|
||||
type: Boolean,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
<slot name="icon">
|
||||
<span v-if="icon !== ''"
|
||||
:class="[isIconUrl ? 'action-text__icon--url' : icon]"
|
||||
:aria-hidden="ariaHidden"
|
||||
:style="{ backgroundImage: isIconUrl ? `url(${icon})` : null }"
|
||||
class="action-text__icon" />
|
||||
</slot>
|
||||
|
|
|
@ -671,6 +671,14 @@ export default {
|
|||
default: t('Actions'),
|
||||
},
|
||||
|
||||
/**
|
||||
* aria-hidden attribute for the icon slot
|
||||
*/
|
||||
ariaHidden: {
|
||||
type: Boolean,
|
||||
default: null,
|
||||
},
|
||||
|
||||
/**
|
||||
* Wanted direction of the menu
|
||||
*/
|
||||
|
@ -1008,6 +1016,7 @@ export default {
|
|||
// If it has a menuTitle, we use a secondary button
|
||||
type: this.type || (buttonText ? 'secondary' : 'tertiary'),
|
||||
disabled: this.disabled || action?.componentOptions?.propsData?.disabled,
|
||||
ariaHidden: this.ariaHidden,
|
||||
...action?.componentOptions?.propsData,
|
||||
},
|
||||
on: {
|
||||
|
@ -1084,6 +1093,7 @@ export default {
|
|||
props: {
|
||||
type: this.triggerBtnType,
|
||||
disabled: this.disabled,
|
||||
ariaHidden: this.ariaHidden,
|
||||
},
|
||||
slot: 'trigger',
|
||||
ref: 'menuButton',
|
||||
|
|
|
@ -34,6 +34,12 @@ include a standard-header like it's used by the files app.
|
|||
<NcAppSidebar
|
||||
title="cat-picture.jpg"
|
||||
subtitle="last edited 3 weeks ago">
|
||||
<NcAppSidebarTab name="Search" id="search-tab">
|
||||
<template #icon>
|
||||
<Magnify :size="20" />
|
||||
</template>
|
||||
Search tab content
|
||||
</NcAppSidebarTab>
|
||||
<NcAppSidebarTab name="Settings" id="settings-tab">
|
||||
<template #icon>
|
||||
<Cog :size="20" />
|
||||
|
@ -49,11 +55,13 @@ include a standard-header like it's used by the files app.
|
|||
</NcAppSidebar>
|
||||
</template>
|
||||
<script>
|
||||
import Magnify from 'vue-material-design-icons/Magnify'
|
||||
import Cog from 'vue-material-design-icons/Cog'
|
||||
import ShareVariant from 'vue-material-design-icons/ShareVariant'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Magnify,
|
||||
Cog,
|
||||
ShareVariant,
|
||||
},
|
||||
|
@ -61,6 +69,181 @@ include a standard-header like it's used by the files app.
|
|||
</script>
|
||||
```
|
||||
|
||||
### One tab
|
||||
|
||||
Single tab is rendered without navigation.
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<NcAppSidebar
|
||||
title="cat-picture.jpg"
|
||||
subtitle="last edited 3 weeks ago">
|
||||
<NcAppSidebarTab name="Settings" id="settings-tab">
|
||||
<template #icon>
|
||||
<Cog :size="20" />
|
||||
</template>
|
||||
Single tab content
|
||||
</NcAppSidebarTab>
|
||||
</NcAppSidebar>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Cog from 'vue-material-design-icons/Cog'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Cog,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### Dynamic tabs
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<NcCheckboxRadioSwitch :checked.sync="showTabs[0]">Show search tab</NcCheckboxRadioSwitch>
|
||||
<NcCheckboxRadioSwitch :checked.sync="showTabs[1]">Show settings tab</NcCheckboxRadioSwitch>
|
||||
<NcCheckboxRadioSwitch :checked.sync="showTabs[2]">Show sharing tab</NcCheckboxRadioSwitch>
|
||||
<NcAppSidebar
|
||||
title="cat-picture.jpg"
|
||||
subtitle="last edited 3 weeks ago">
|
||||
<NcAppSidebarTab v-if="showTabs[0]" name="Search" id="search-tab">
|
||||
<template #icon>
|
||||
<Magnify :size="20" />
|
||||
</template>
|
||||
Search tab content
|
||||
</NcAppSidebarTab>
|
||||
<NcAppSidebarTab v-if="showTabs[1]" name="Settings" id="settings-tab">
|
||||
<template #icon>
|
||||
<Cog :size="20" />
|
||||
</template>
|
||||
Settings
|
||||
</NcAppSidebarTab>
|
||||
<NcAppSidebarTab v-if="showTabs[2]" name="Sharing" id="share-tab">
|
||||
<template #icon>
|
||||
<ShareVariant :size="20" />
|
||||
</template>
|
||||
Sharing tab content
|
||||
</NcAppSidebarTab>
|
||||
</NcAppSidebar>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Magnify from 'vue-material-design-icons/Magnify'
|
||||
import Cog from 'vue-material-design-icons/Cog'
|
||||
import ShareVariant from 'vue-material-design-icons/ShareVariant'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Magnify,
|
||||
Cog,
|
||||
ShareVariant,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showTabs: [true, true, false],
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### Custom order
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<NcAppSidebar
|
||||
title="cat-picture.jpg"
|
||||
subtitle="last edited 3 weeks ago">
|
||||
<NcAppSidebarTab name="Search" id="search-tab" order="3">
|
||||
<template #icon>
|
||||
<Magnify :size="20" />
|
||||
</template>
|
||||
Search tab content
|
||||
</NcAppSidebarTab>
|
||||
<NcAppSidebarTab name="Settings" id="settings-tab" order="2">
|
||||
<template #icon>
|
||||
<Cog :size="20" />
|
||||
</template>
|
||||
Settings tab content
|
||||
</NcAppSidebarTab>
|
||||
<NcAppSidebarTab name="Sharing" id="share-tab" order="1">
|
||||
<template #icon>
|
||||
<ShareVariant :size="20" />
|
||||
</template>
|
||||
Sharing tab content
|
||||
</NcAppSidebarTab>
|
||||
</NcAppSidebar>
|
||||
</template>
|
||||
<script>
|
||||
import Magnify from 'vue-material-design-icons/Magnify'
|
||||
import Cog from 'vue-material-design-icons/Cog'
|
||||
import ShareVariant from 'vue-material-design-icons/ShareVariant'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Magnify,
|
||||
Cog,
|
||||
ShareVariant,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### Activating tab programmatically
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<NcMultiselect v-model="active" :options="['search-tab', 'settings-tab', 'share-tab']" />
|
||||
<NcAppSidebar
|
||||
title="cat-picture.jpg"
|
||||
subtitle="last edited 3 weeks ago"
|
||||
:active.sync="active">
|
||||
<NcAppSidebarTab name="Search" id="search-tab">
|
||||
<template #icon>
|
||||
<Magnify :size="20" />
|
||||
</template>
|
||||
Search tab content
|
||||
</NcAppSidebarTab>
|
||||
<NcAppSidebarTab name="Settings" id="settings-tab">
|
||||
<template #icon>
|
||||
<Cog :size="20" />
|
||||
</template>
|
||||
Settings
|
||||
</NcAppSidebarTab>
|
||||
<NcAppSidebarTab name="Sharing" id="share-tab">
|
||||
<template #icon>
|
||||
<ShareVariant :size="20" />
|
||||
</template>
|
||||
Sharing tab content
|
||||
</NcAppSidebarTab>
|
||||
</NcAppSidebar>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Magnify from 'vue-material-design-icons/Magnify'
|
||||
import Cog from 'vue-material-design-icons/Cog'
|
||||
import ShareVariant from 'vue-material-design-icons/ShareVariant'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Magnify,
|
||||
Cog,
|
||||
ShareVariant,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
active: 'search-tab',
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### Editable title
|
||||
|
||||
```vue
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
@keydown.left.exact.prevent="focusPreviousTab"
|
||||
@keydown.right.exact.prevent="focusNextTab"
|
||||
@keydown.tab.exact.prevent="focusActiveTabContent"
|
||||
@keydown.home.exact.prevent="focusFirstTab"
|
||||
@keydown.end.exact.prevent="focusLastTab"
|
||||
@keydown.33.exact.prevent="focusFirstTab"
|
||||
@keydown.34.exact.prevent="focusLastTab">
|
||||
<ul>
|
||||
|
@ -43,12 +45,11 @@
|
|||
:class="{ active: activeTab === tab.id }"
|
||||
:data-id="tab.id"
|
||||
:href="`#tab-${tab.id}`"
|
||||
:tabindex="activeTab === tab.id ? undefined : -1"
|
||||
:tabindex="activeTab === tab.id ? 0 : -1"
|
||||
role="tab"
|
||||
@click.prevent="setActive(tab.id)">
|
||||
<span class="app-sidebar-tabs__tab-icon">
|
||||
<NcVNodes v-if="hasMdIcon(tab)" :vnodes="tab.$slots.icon[0]" />
|
||||
<span v-else :class="tab.icon" />
|
||||
<NcVNodes :vnodes="tab.renderIcon()" />
|
||||
</span>
|
||||
{{ tab.name }}
|
||||
</a>
|
||||
|
@ -59,6 +60,7 @@
|
|||
<!-- tabs content -->
|
||||
<div :class="{'app-sidebar-tabs__content--multiple': hasMultipleTabs}"
|
||||
class="app-sidebar-tabs__content">
|
||||
<!-- @slot Tabs content - NcAppSidebarTab components or any content if there is no tabs -->
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -67,24 +69,22 @@
|
|||
<script>
|
||||
import NcVNodes from '../NcVNodes/index.js'
|
||||
|
||||
import Vue from 'vue'
|
||||
|
||||
const IsValidString = function(value) {
|
||||
return value && typeof value === 'string' && value.trim() !== ''
|
||||
}
|
||||
|
||||
const IsValidStringWithoutSpaces = function(value) {
|
||||
return IsValidString(value) && value.indexOf(' ') === -1
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'NcAppSidebarTabs',
|
||||
|
||||
components: {
|
||||
// Component to render the material design icon (vnodes)
|
||||
NcVNodes,
|
||||
},
|
||||
|
||||
provide() {
|
||||
return {
|
||||
registerTab: this.registerTab,
|
||||
unregisterTab: this.unregisterTab,
|
||||
// Getter as an alternative to Vue 2.7 computed(() => this.activeTab)
|
||||
getActiveTab: () => this.activeTab,
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
/**
|
||||
* Id of the tab to activate
|
||||
|
@ -100,26 +100,28 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
/**
|
||||
* The tab component instances to build the tab navbar from.
|
||||
* Tab descriptions from the passed NcSidebarTab components' props to build the tab navbar from.
|
||||
*/
|
||||
tabs: [],
|
||||
/**
|
||||
* The id of the currently active tab.
|
||||
* Local active (open) tab's ID. It allows to use component without active.sync
|
||||
*/
|
||||
activeTab: '',
|
||||
/**
|
||||
* Dummy array to react on slot changes.
|
||||
*/
|
||||
children: [],
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Has multiple tabs. If only one tab - its content is shown without navigation
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasMultipleTabs() {
|
||||
return this.tabs.length > 1
|
||||
},
|
||||
|
||||
currentTabIndex() {
|
||||
return this.tabs.findIndex(tab => tab.id === this.activeTab)
|
||||
return this.tabs.findIndex((tab) => tab.id === this.activeTab)
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -130,18 +132,6 @@ export default {
|
|||
this.updateActive()
|
||||
}
|
||||
},
|
||||
|
||||
children() {
|
||||
this.updateTabs()
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// Init the tabs list
|
||||
this.updateTabs()
|
||||
|
||||
// Let's make the children list reactive
|
||||
this.children = this.$children
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -153,6 +143,9 @@ export default {
|
|||
*/
|
||||
setActive(id) {
|
||||
this.activeTab = id
|
||||
/**
|
||||
* @property {string} active - active tab's id
|
||||
*/
|
||||
this.$emit('update:active', this.activeTab)
|
||||
},
|
||||
|
||||
|
@ -164,7 +157,7 @@ export default {
|
|||
if (this.currentTabIndex > 0) {
|
||||
this.setActive(this.tabs[this.currentTabIndex - 1].id)
|
||||
}
|
||||
this.focusActiveTab() // focus nav item
|
||||
this.focusActiveTab()
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -175,7 +168,7 @@ export default {
|
|||
if (this.currentTabIndex < this.tabs.length - 1) {
|
||||
this.setActive(this.tabs[this.currentTabIndex + 1].id)
|
||||
}
|
||||
this.focusActiveTab() // focus nav item
|
||||
this.focusActiveTab()
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -184,7 +177,7 @@ export default {
|
|||
*/
|
||||
focusFirstTab() {
|
||||
this.setActive(this.tabs[0].id)
|
||||
this.focusActiveTab() // focus nav item
|
||||
this.focusActiveTab()
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -193,7 +186,7 @@ export default {
|
|||
*/
|
||||
focusLastTab() {
|
||||
this.setActive(this.tabs[this.tabs.length - 1].id)
|
||||
this.focusActiveTab() // focus nav item
|
||||
this.focusActiveTab()
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -216,62 +209,42 @@ export default {
|
|||
*/
|
||||
updateActive() {
|
||||
this.activeTab = this.active
|
||||
&& this.tabs.findIndex(tab => tab.id === this.active) !== -1
|
||||
&& this.tabs.some(tab => tab.id === this.active)
|
||||
? this.active
|
||||
: this.tabs.length > 0
|
||||
? this.tabs[0].id
|
||||
: ''
|
||||
},
|
||||
|
||||
hasMdIcon(tab) {
|
||||
return tab?.$slots?.icon
|
||||
/**
|
||||
* Register child tab in the tabs
|
||||
*
|
||||
* @param {object} tab child tab passed to slot
|
||||
*/
|
||||
registerTab(tab) {
|
||||
this.tabs.push(tab)
|
||||
this.tabs.sort((a, b) => {
|
||||
if (a.order === b.order) {
|
||||
return OC.Util.naturalSortCompare(a.name, b.name)
|
||||
}
|
||||
return a.order - b.order
|
||||
})
|
||||
if (!this.activeTab) {
|
||||
this.updateActive()
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Manually update the sidebar tabs according to $slots.default
|
||||
* Unregister child tab from the tabs
|
||||
*
|
||||
* @param {string} id tab's id
|
||||
*/
|
||||
updateTabs() {
|
||||
if (!this.$slots.default) {
|
||||
this.tabs = []
|
||||
return
|
||||
unregisterTab(id) {
|
||||
const tabIndex = this.tabs.findIndex((tab) => tab.id === id)
|
||||
if (tabIndex !== -1) {
|
||||
this.tabs.splice(tabIndex, 1)
|
||||
}
|
||||
|
||||
// Find all valid children (AppSidebarTab, other components, text nodes, etc.)
|
||||
const children = this.$slots.default.filter(elem => elem.tag || elem.text.trim())
|
||||
|
||||
// Find all valid instances of AppSidebarTab
|
||||
const invalidTabs = []
|
||||
const tabs = children.reduce((tabs, tabNode) => {
|
||||
const tab = tabNode.componentInstance
|
||||
// Make sure all required props are provided and valid
|
||||
if (IsValidString(tab?.name)
|
||||
&& IsValidStringWithoutSpaces(tab?.id)
|
||||
&& (IsValidStringWithoutSpaces(tab?.icon) || tab?.$slots?.icon)) {
|
||||
tabs.push(tab)
|
||||
} else {
|
||||
invalidTabs.push(tabNode)
|
||||
}
|
||||
return tabs
|
||||
}, [])
|
||||
|
||||
// Tabs are optional, but you can use either tabs or non-tab-content only
|
||||
if (tabs.length !== 0 && tabs.length !== children.length) {
|
||||
Vue.util.warn('Mixing tabs and non-tab-content is not possible.')
|
||||
invalidTabs.map(invalid => console.debug('Ignoring invalid tab', invalid))
|
||||
}
|
||||
|
||||
// We sort the tabs by their order or by their name
|
||||
this.tabs = tabs.sort((a, b) => {
|
||||
const orderA = a.order || 0
|
||||
const orderB = b.order || 0
|
||||
if (orderA === orderB) {
|
||||
return OC.Util.naturalSortCompare(a.name, b.name)
|
||||
}
|
||||
return orderA - orderB
|
||||
})
|
||||
|
||||
// Init active tab if exists
|
||||
if (this.tabs.length > 0) {
|
||||
if (this.activeTab === id) {
|
||||
this.updateActive()
|
||||
}
|
||||
},
|
||||
|
|
|
@ -35,27 +35,44 @@
|
|||
<h3 class="hidden-visually">
|
||||
{{ name }}
|
||||
</h3>
|
||||
<!-- @slot Tab panel content -->
|
||||
<slot />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { h } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'NcAppSidebarTab',
|
||||
|
||||
inject: ['registerTab', 'unregisterTab', 'getActiveTab'],
|
||||
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
/**
|
||||
* Tab title in navigation
|
||||
*/
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
/**
|
||||
* Tab icon's html class in navigation. Used if #icon slot is not provided
|
||||
*/
|
||||
icon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
|
||||
/**
|
||||
* Tab order in navigation. If not provided, name is used.
|
||||
*/
|
||||
order: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
|
@ -67,24 +84,55 @@ export default {
|
|||
'scroll',
|
||||
],
|
||||
|
||||
expose: ['id', 'name', 'icon', 'order', 'renderIcon'],
|
||||
|
||||
computed: {
|
||||
// TODO: implement a better way to force pass a prop fromm Sidebar
|
||||
/**
|
||||
* Is the current tab an active tab, that should be shown?
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
isActive() {
|
||||
return this.$parent.activeTab === this.id
|
||||
return this.getActiveTab() === this.id
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
// As the tab is created - register it in the tabs component
|
||||
// It's better to provide computed tab object, not component instance as it easy
|
||||
this.registerTab(this)
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
// Unregister the tab from tabs
|
||||
this.unregisterTab(this.id)
|
||||
},
|
||||
|
||||
methods: {
|
||||
onScroll(event) {
|
||||
// Are we scrolled to the very bottom ?
|
||||
if (this.$el.scrollHeight - this.$el.scrollTop === this.$el.clientHeight) {
|
||||
/**
|
||||
* Bottom scroll is reached
|
||||
*
|
||||
* @property {Event} event Native scroll event
|
||||
*/
|
||||
this.$emit('bottom-reached', event)
|
||||
}
|
||||
/**
|
||||
* @property {Event} event Native scroll event
|
||||
*/
|
||||
this.$emit('scroll', event)
|
||||
},
|
||||
|
||||
/**
|
||||
* Render tab's icon from slot or icon prop
|
||||
*
|
||||
* @return {import('vue').VNode|import('vue').VNode[]}
|
||||
*/
|
||||
renderIcon() {
|
||||
return this.$slots.icon || this.$scopedSlots.icon?.() || h('span', { staticClass: this.icon })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -287,6 +287,13 @@ export default {
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* aria-hidden attribute for the icon slot
|
||||
*/
|
||||
ariaHidden: {
|
||||
type: Boolean,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -345,7 +352,16 @@ export default {
|
|||
},
|
||||
[
|
||||
h('span', { class: 'button-vue__wrapper' }, [
|
||||
hasIcon ? h('span', { class: 'button-vue__icon' }, [this.$slots.icon]) : null,
|
||||
hasIcon
|
||||
? h('span', {
|
||||
class: 'button-vue__icon',
|
||||
attrs: {
|
||||
'aria-hidden': this.ariaHidden,
|
||||
},
|
||||
},
|
||||
[this.$slots.icon],
|
||||
)
|
||||
: null,
|
||||
hasText ? h('span', { class: 'button-vue__text' }, [text]) : null,
|
||||
]),
|
||||
]
|
||||
|
@ -429,7 +445,8 @@ export default {
|
|||
&__wrapper {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
|
@ -446,6 +463,9 @@ export default {
|
|||
font-weight: bold;
|
||||
margin-bottom: 1px;
|
||||
padding: 2px 0;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// Icon-only button
|
||||
|
|
|
@ -660,6 +660,7 @@ export default {
|
|||
|
||||
onKeyUp(event) {
|
||||
// prevent tribute from opening on keyup
|
||||
event.stopImmediatePropagation()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ export default {
|
|||
return {
|
||||
text: `Hello {username}. The file {file} was added by {username}. Go visit https://nextcloud.com
|
||||
|
||||
Local IP: http://127.0.0.1/status.php should be clickable
|
||||
|
||||
Some examples for markdown syntax: **bold text** *italic text* ~~strikethrough~~`,
|
||||
autolink: true,
|
||||
useMarkdown: true,
|
||||
|
@ -238,6 +240,9 @@ export default {
|
|||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
/* stylelint-disable-next-line scss/at-import-partial-extension */
|
||||
@import './richtext.scss';
|
||||
|
||||
a:not(.rich-text--component) {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,16 @@
|
|||
/**
|
||||
* Regex pattern to match links to be resolved by the link-reference provider
|
||||
*
|
||||
* @type {RegExp}
|
||||
*/
|
||||
export const URL_PATTERN = /(\s|^)(https?:\/\/)((?:[-A-Z0-9+_]+\.)+[-A-Z]+(?:\/[-A-Z0-9+&@#%?=~_|!:,.;()]*)*)(\s|$)/ig
|
||||
|
||||
// FIXME See if we can merge those two
|
||||
export const URL_PATTERN_AUTOLINK = /(\s|\(|^)((https?:\/\/)((?:[-A-Z0-9+_]+\.)+[-A-Z]+(?::[0-9]+)?(?:\/[-A-Z0-9+&@#%?=~_|!:,.;()]*)*))(?=\s|\)|$)/ig
|
||||
/**
|
||||
* Regex pattern to identify strings as links and then making them clickable
|
||||
* Opposed to the above regex this one also matches IP addresses, which we would like to be clickable,
|
||||
* but in general resolving references for them might mostly not work,
|
||||
* as the link provider checks for local addresses and does not resolve them.
|
||||
*
|
||||
* @type {RegExp}
|
||||
*/
|
||||
export const URL_PATTERN_AUTOLINK = /(\s|\(|^)((https?:\/\/)((?:[-A-Z0-9+_]+\.)+[-A-Z0-9]+(?::[0-9]+)?(?:\/[-A-Z0-9+&@#%?=~_|!:,.;()]*)*))(?=\s|\)|$)/ig
|
||||
|
|
|
@ -44,9 +44,11 @@ export const prepareTextNode = ({ h, context }, text) => {
|
|||
return entry
|
||||
}
|
||||
const { component, props } = entry
|
||||
// do not override class of NcLink
|
||||
const componentClass = component.name === 'NcLink' ? undefined : 'rich-text--component'
|
||||
return h(component, {
|
||||
props,
|
||||
class: 'rich-text--component',
|
||||
class: componentClass,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -67,6 +67,13 @@ export default {
|
|||
type: String,
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* aria-hidden attribute for the icon slot
|
||||
*/
|
||||
ariaHidden: {
|
||||
type: Boolean,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
emits: [
|
||||
|
|
|
@ -189,23 +189,4 @@ describe('NcAppSidebarTabs.vue', () => {
|
|||
})
|
||||
})
|
||||
})
|
||||
describe('when tabs and other elements are mixed', () => {
|
||||
it('Issues a warning and logs to console .', () => {
|
||||
mount(NcAppSidebarTabs, {
|
||||
slots: {
|
||||
default: [
|
||||
'<nc-app-sidebar-tab id="1" icon="icon-details" name="Tab1">Tab1</nc-app-sidebar-tab>',
|
||||
'<NcAppSidebarTab id="2" icon="icon-details" name="Tab2">Tab2</NcAppSidebarTab>',
|
||||
'<div>Non-tab-content</div>',
|
||||
'Test',
|
||||
],
|
||||
},
|
||||
stubs: {
|
||||
NcAppSidebarTab,
|
||||
},
|
||||
})
|
||||
expect(onWarning).toHaveBeenCalledTimes(1)
|
||||
expect(consoleDebug).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -152,6 +152,17 @@ describe('Foo', () => {
|
|||
expect(wrapper.find('a').attributes('href')).toEqual('https://example.com:444')
|
||||
})
|
||||
|
||||
it('properly recognizes an url with an IP address and inserts a link', async() => {
|
||||
const wrapper = mount(NcRichText, {
|
||||
propsData: {
|
||||
text: 'Testwith a link to https://127.0.0.1/status.php - go visit it',
|
||||
autolink: true
|
||||
}
|
||||
})
|
||||
expect(wrapper.text()).toEqual('Testwith a link to https://127.0.0.1/status.php - go visit it')
|
||||
expect(wrapper.find('a').attributes('href')).toEqual('https://127.0.0.1/status.php')
|
||||
})
|
||||
|
||||
it('properly formats markdown', async() => {
|
||||
const wrapper = mount(NcRichText, {
|
||||
propsData: {
|
||||
|
|
Загрузка…
Ссылка в новой задаче