Merge remote-tracking branch 'origin' into chore/noid/merge-master-next

This commit is contained in:
Raimund Schlüßler 2024-01-18 23:53:20 +01:00
Родитель 1acbe0842c 183e127a69
Коммит a02bc611b2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 036FA7EB1A599178
19 изменённых файлов: 275 добавлений и 53 удалений

Просмотреть файл

@ -68,6 +68,9 @@ msgstr "Modrofialová"
msgid "Boston Blue"
msgstr "Bostonská modrá"
msgid "busy"
msgstr "zaneprádněn(a)"
msgid "Cancel changes"
msgstr "Zrušit změny"
@ -101,6 +104,9 @@ msgstr "Zavřít inteligentní výběr"
msgid "Collapse menu"
msgstr "Sbalit nabídku"
msgid "Color picker"
msgstr "Výběr barev"
msgid "Confirm changes"
msgstr "Potvrdit změny"
@ -120,6 +126,9 @@ msgstr "nerušit"
msgid "Edit item"
msgstr "Upravit položku"
msgid "Emoji picker"
msgstr "Výběr emotikon"
msgid "Enter link"
msgstr "Zadat odkaz"

Просмотреть файл

@ -73,6 +73,9 @@ msgstr "Blau Violett"
msgid "Boston Blue"
msgstr "Boston-Blau"
msgid "busy"
msgstr "Beschäftigt"
msgid "Cancel changes"
msgstr "Änderungen verwerfen"

Просмотреть файл

@ -1,7 +1,7 @@
#
# Translators:
# John Molakvoæ <skjnldsv@protonmail.com>, 2023
# Mark Ziegler <mark.ziegler@rakekniven.de>, 2023
# Mark Ziegler <mark.ziegler@rakekniven.de>, 2024
# Mario Siegmann <mario_siegmann@web.de>, 2024
#
msgid ""
@ -72,6 +72,9 @@ msgstr "Blau Violett"
msgid "Boston Blue"
msgstr "Boston-Blau"
msgid "busy"
msgstr "Beschäftigt"
msgid "Cancel changes"
msgstr "Änderungen verwerfen"
@ -105,6 +108,9 @@ msgstr "Smart Picker schließen"
msgid "Collapse menu"
msgstr "Menü einklappen"
msgid "Color picker"
msgstr "Farbauswahl"
msgid "Confirm changes"
msgstr "Änderungen bestätigen"
@ -124,6 +130,9 @@ msgstr "Bitte nicht stören"
msgid "Edit item"
msgstr "Element bearbeiten"
msgid "Emoji picker"
msgstr "Emoji-Auswahl"
msgid "Enter link"
msgstr "Link eingeben"

Просмотреть файл

@ -73,6 +73,9 @@ msgstr "Violeta Azul"
msgid "Boston Blue"
msgstr "Azul Boston"
msgid "busy"
msgstr "ocupado"
msgid "Cancel changes"
msgstr "Cancelar cambios"
@ -225,6 +228,9 @@ msgstr "Abrir menú"
msgid "Open navigation"
msgstr "Abrir navegación"
msgid "Options"
msgstr "Opciones"
msgid "Password is secure"
msgstr "La contraseña es segura"

Просмотреть файл

@ -96,6 +96,9 @@ msgstr ""
msgid "Collapse menu"
msgstr ""
msgid "Color picker"
msgstr ""
msgid "Confirm changes"
msgstr ""
@ -115,6 +118,9 @@ msgstr ""
msgid "Edit item"
msgstr ""
msgid "Emoji picker"
msgstr ""
msgid "Enter link"
msgstr ""

16
package-lock.json сгенерированный
Просмотреть файл

@ -9372,15 +9372,14 @@
"dev": true
},
"node_modules/cypress": {
"version": "13.6.2",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.2.tgz",
"integrity": "sha512-TW3bGdPU4BrfvMQYv1z3oMqj71YI4AlgJgnrycicmPZAXtvywVFZW9DAToshO65D97rCWfG/kqMFsYB6Kp91gQ==",
"version": "13.6.3",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.3.tgz",
"integrity": "sha512-d/pZvgwjAyZsoyJ3FOsJT5lDsqnxQ/clMqnNc++rkHjbkkiF2h9s0JsZSyyH4QXhVFW3zPFg82jD25roFLOdZA==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@cypress/request": "^3.0.0",
"@cypress/xvfb": "^1.2.4",
"@types/node": "^18.17.5",
"@types/sinonjs__fake-timers": "8.1.1",
"@types/sizzle": "^2.3.2",
"arch": "^2.2.0",
@ -9443,15 +9442,6 @@
"cypress": ">=9.7.0"
}
},
"node_modules/cypress/node_modules/@types/node": {
"version": "18.19.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.3.tgz",
"integrity": "sha512-k5fggr14DwAytoA/t8rPrIz++lXK7/DqckthCmoZOKNsEbJkId4Z//BqgApXBUGrGddrigYa1oqheo/7YmW4rg==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/cypress/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",

Просмотреть файл

@ -1264,9 +1264,15 @@ export default {
this.isSemanticMenu = hasMenuItemAction && !hasTextInputAction
// We consider the NcActions to be navigation if it consists some link-like action
this.isSemanticNavigation = hasLinkAction && !hasMenuItemAction && !hasTextInputAction
// If it is no a manu and not a navigation, it is a popover with items: a form or just a text
// If it is not a menu and not a navigation, it is a popover with items: a form or just a text
this.isSemanticPopoverLike = !this.isSemanticMenu && !this.isSemanticNavigation
const popupRole = this.isSemanticMenu
? 'menu'
: hasTextInputAction
? 'dialog'
: 'true'
/**
* Filter and list actions that are allowed to be displayed inline
*/
@ -1348,7 +1354,11 @@ export default {
container: this.container,
...this.manualOpen && { triggers: [] },
popoverBaseClass: 'action-item__popper',
setReturnFocus: this.$refs.menuButton?.$el,
popupRole,
// Menu and navigation should not have focus trap
// Tab should close the menu and move focus to the next UI element
setReturnFocus: !this.isSemanticPopoverLike ? null : this.$refs.menuButton?.$el,
focusTrap: this.isSemanticPopoverLike,
onShow: this.openMenu,
onAfterShow: this.onOpen,
onHide: this.closeMenu,
@ -1360,11 +1370,8 @@ export default {
disabled: this.disabled,
ariaHidden: this.ariaHidden,
ref: 'menuButton',
'aria-haspopup': this.isSemanticMenu ? 'menu' : null,
'aria-label': this.menuName ? null : this.ariaLabel,
'aria-controls': this.opened ? this.randomId : null,
// Do not add aria-expanded="true" when it is closed
'aria-expanded': this.opened ? 'true' : null,
onFocus: this.onFocus,
onBlur: this.onBlur,
}, {
@ -1383,7 +1390,8 @@ export default {
h('ul', {
id: this.randomId,
tabindex: '-1',
role: this.isSemanticMenu ? 'menu' : null,
role: popupRole !== 'true' ? popupRole : undefined,
// TODO: allow to provide dialog aria-label
}, [
actions,
]),

Просмотреть файл

@ -447,6 +447,13 @@ const NATIVE_TYPES = ['submit', 'reset', 'button'] as const
export default defineComponent({
name: 'NcButton',
inject: {
ncPopoverTriggerAttrs: {
from: 'NcPopover:trigger:attrs',
default: () => ({}),
},
},
props: {
/**
* Set the text and icon alignment
@ -619,7 +626,8 @@ export default defineComponent({
this)
}
const renderButton = ({ navigate, isActive }: {navigate?: (ev: Event) => void, isActive?: boolean } = {}) => h((this.to || !this.href) ? 'button' : 'a',
const isLink = (this.to || this.href)
const renderButton = ({ href, navigate, isActive }: {href?: string, navigate?: (ev: Event) => void, isActive?: boolean } = {}) => h(isLink ? 'a' : 'button',
{
class: [
'button-vue',
@ -637,12 +645,14 @@ export default defineComponent({
'aria-label': this.ariaLabel,
'aria-pressed': this.pressed,
disabled: this.disabled,
type: this.href ? null : this.nativeType,
role: this.href ? 'button' : null,
href: (!this.to && this.href) ? this.href : null,
target: (!this.to && this.href) ? '_self' : null,
rel: (!this.to && this.href) ? 'nofollow noreferrer noopener' : null,
type: isLink ? null : this.nativeType,
role: isLink ? 'button' : null,
href: this.to ? href : (this.href || null),
target: isLink ? '_self' : null,
rel: isLink ? 'nofollow noreferrer noopener' : null,
download: (!this.to && this.href && this.download) ? this.download : null,
// If this button is used as a popover trigger, we need to apply trigger attrs, e.g. aria attributes
...this.ncPopoverTriggerAttrs,
onClick: ($event) => {
// Update pressed prop on click if it is set
if (typeof this.pressed === 'boolean') {

Просмотреть файл

@ -89,8 +89,8 @@ export default {
<template>
<div class="container1">
<NcButton @click="open = !open"> Click Me </NcButton>
<NcColorPicker v-model="color" v-model:shown="open">
<div :style="{'background-color': color}" class="color1" />
<NcColorPicker v-model="color" v-model:shown="open" v-slot="{ attrs }">
<div v-bind="attrs" :style="{'background-color': color}" class="color1" />
</NcColorPicker>
</div>
</template>
@ -155,11 +155,14 @@ export default {
</docs>
<template>
<NcPopover @apply-hide="handleClose">
<template #trigger>
<slot />
<NcPopover popup-role="dialog"
@apply-hide="handleClose">
<template #trigger="slotProps">
<slot v-bind="slotProps" />
</template>
<div class="color-picker"
<div role="dialog"
class="color-picker"
:aria-label="t('Color picker')"
:class="{ 'color-picker--advanced-fields': advanced && advancedFields }">
<Transition name="slide" mode="out-in">
<div v-if="!advanced" class="color-picker__simple">

Просмотреть файл

@ -141,23 +141,28 @@ export default {
<template #icon-calendar>
<NcPopover v-if="showTimezoneSelect"
v-model:shown="showTimezonePopover"
popup-role="dialog"
popover-base-class="timezone-select__popper">
<template #trigger>
<template #trigger="{ attrs }">
<button class="datetime-picker-inline-icon"
:class="{'datetime-picker-inline-icon--highlighted': highlightTimezone}"
v-bind="attrs"
@mousedown.stop.prevent="() => {}">
<Web :size="20" />
</button>
</template>
<div class="timezone-popover-wrapper__label">
<strong>
{{ t('Please select a time zone:') }}
</strong>
<div role="dialog"
:aria-labelledby="timezoneDialogHeaderId">
<div class="timezone-popover-wrapper__label">
<strong :id="timezoneDialogHeaderId">
{{ t('Please select a time zone:') }}
</strong>
</div>
<NcTimezonePicker :model-value="tzVal"
class="timezone-popover-wrapper__timezone-select"
@update:model-value="$emit('update:timezone-id', $event)" />
</div>
<NcTimezonePicker :model-value="tzVal"
class="timezone-popover-wrapper__timezone-select"
@update:model-value="$emit('update:timezone-id', $event)" />
</NcPopover>
<CalendarBlank v-else :size="20" />
</template>
@ -169,6 +174,7 @@ export default {
<script>
import { t } from '../../l10n.js'
import GenRandomId from '../../utils/GenRandomId.js'
import NcTimezonePicker from '../NcTimezonePicker/index.js'
import NcPopover from '../NcPopover/index.js'
@ -289,6 +295,12 @@ export default ScopeComponent({
'update:timezone-id',
],
setup() {
return {
timezoneDialogHeaderId: `timezone-dialog-header-${GenRandomId()}`,
}
},
data() {
return {
showTimezonePopover: false,

Просмотреть файл

@ -126,10 +126,11 @@ This component allows the user to pick an emoji.
<template>
<NcPopover v-model:shown="open"
:container="container"
popup-role="dialog"
@after-show="afterShow"
@after-hide="afterHide">
<template #trigger>
<slot />
<template #trigger="slotProps">
<slot v-bind="slotProps" />
</template>
<Picker ref="picker"
:auto-focus="false /* We manage the input focus ourselves */"
@ -143,6 +144,8 @@ This component allows the user to pick an emoji.
:picker-styles="{ width: '320px' }"
:show-preview="showPreview"
:title="previewFallbackName"
role="dialog"
:aria-label="t('Emoji picker')"
v-bind="$attrs"
@select="select">
<template #searchTemplate="slotProps">

Просмотреть файл

@ -357,8 +357,8 @@ export default {
}
&--disabled {
opacity: 0.7;
filter: saturate(0.7);
opacity: 0.4;
filter: saturate(0.4);
}
&__input {

Просмотреть файл

@ -36,18 +36,18 @@ open prop on this component;
### Examples
#### With a `<button>` as a trigger:
#### With a `<NcButton>` as a trigger:
```vue
<template>
<div style="display: flex">
<NcPopover>
<NcPopover popup-role="dialog">
<template #trigger>
<NcButton>I am the trigger</NcButton>
</template>
<template #default>
<form tabindex="0" @submit.prevent>
<h2>this is some content</h2>
<form tabindex="0" role="dialog" aria-labelledby="popover-example-dialog-header-1" @submit.prevent>
<h2 id="popover-example-dialog-header-1">this is some content</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. <br/>
Vestibulum eget placerat velit.
@ -89,7 +89,7 @@ The prop `:focus-trap="false"` help to prevent it when the default behavior is n
```vue
<template>
<div style="display: flex">
<NcPopover container="body" :popper-hide-triggers="(triggers) => [...triggers, 'click']">
<NcPopover container="body" :popper-hide-triggers="(triggers) => [...triggers, 'click']" popup-role="dialog">
<template #trigger>
<NcButton>I am the trigger</NcButton>
</template>
@ -100,18 +100,74 @@ The prop `:focus-trap="false"` help to prevent it when the default behavior is n
</div>
</template>
```
#### With a custom button in as a trigger:
When `<NcButton>` is used as a `<NcPopover>` trigger, it injects required for a11y attributes to the button.
If you are using your own custom button as a trigger make sure to bind `attrs` from the trigger slot props.
See code example below.
```vue
<template>
<div style="display: flex">
<NcPopover>
<!-- Take "attrs" from the slot props -->
<template #trigger="{ attrs }">
<!-- Bind attrs as the button attrs -->
<button v-bind="attrs">
I am a custom button in the trigger
</button>
</template>
Hi! 🚀
</NcPopover>
</div>
````
#### Provide role for the popover content
For accessibility reasons, popover should have a role. Provide it to the `popup-role` and make sure that the popover content is an element with the same role.
See: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-haspopup
```vue
<template>
<div style="display: flex">
<!-- Provide popup role -->
<NcPopover popup-role="dialog">
<template #trigger>
<NcButton>Delete</NcButton>
</template>
<!-- Popover content should has the same role -->
<div role="dialog" aria-labelledby="popover-example-custom-role-1">
<!-- This is not required but better to provide a label -->
<header id="popover-example-custom-role-1">
<strong>Confirm remove</strong>
</header>
<NcButton type="danger">Delete</NcButton>
</div>
</NcPopover>
</div>
</template>
````
</docs>
<template>
<Dropdown ref="popover"
v-model:shown="shownProxy"
:distance="10"
:arrow-padding="10"
:no-auto-focus="true /* Handled by the focus trap */"
:popper-class="popoverBaseClass"
@apply-show="afterShow"
@apply-hide="afterHide">
<!-- This will be the popover target (for the events and position) -->
<slot name="trigger" />
<NcPopoverTriggerProvider v-slot="slotProps" :shown="internalShown" :popup-role="popupRole">
<!-- This will be the popover target (for the events and position) -->
<slot name="trigger" v-bind="slotProps" />
</NcPopoverTriggerProvider>
<!-- This will be the content of the popover -->
<template #popper>
<slot />
@ -123,15 +179,36 @@ The prop `:focus-trap="false"` help to prevent it when the default behavior is n
import { Dropdown } from 'floating-vue'
import { createFocusTrap } from 'focus-trap'
import { getTrapStack } from '../../utils/focusTrap.js'
import NcPopoverTriggerProvider from './NcPopoverTriggerProvider.vue'
export default {
name: 'NcPopover',
components: {
Dropdown,
NcPopoverTriggerProvider,
},
props: {
/**
* Show or hide the popper
* @see https://floating-vue.starpad.dev/api/#shown
*/
shown: {
type: Boolean,
default: false,
},
/**
* Popup role
* @see https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-haspopup#values
*/
popupRole: {
type: String,
default: 'true',
validator: (value) => ['menu', 'listbox', 'tree', 'grid', 'dialog', 'true'].includes(value),
},
popoverBaseClass: {
type: String,
default: '',
@ -157,8 +234,36 @@ export default {
emits: [
'after-show',
'after-hide',
/**
* @see https://floating-vue.starpad.dev/api/#update-shown
*/
'update:shown',
],
data() {
return {
internalShown: this.shown,
}
},
computed: {
shownProxy: {
get() {
return this.internalShown
},
set(value) {
this.internalShown = value
this.$emit('update:shown', value)
},
},
},
watch: {
shown(value) {
this.internalShown = value
},
},
beforeUnmount() {
this.clearFocusTrap()
this.clearEscapeStopPropagation()

Просмотреть файл

@ -0,0 +1,54 @@
<script>
import { computed, defineComponent } from 'vue'
export default defineComponent({
name: 'NcPopoverTriggerProvider',
provide() {
return {
'NcPopover:trigger:shown': computed(() => this.shown),
'NcPopover:trigger:attrs': computed(() => this.triggerAttrs),
}
},
props: {
shown: {
type: Boolean,
required: true,
},
popupRole: {
type: String,
required: true,
},
},
computed: {
triggerAttrs() {
return {
'aria-haspopup': this.popupRole,
'aria-expanded': this.shown.toString(),
}
},
},
mounted() {
if (window.OC?.debug) {
// TODO: Adjust for vue 3. Since vue 3 components can have multiple root elements, this breaks.
/**
const rootElement = this.$el
console.debug(this.$el)
const innerElement = this.$el.querySelector('[aria-expanded][aria-haspopup]')
if (!rootElement.getAttribute('aria-expanded') && !innerElement) {
warn('It looks like you are using a custom button as a <NcPopover> or other popover #trigger. If you are not using <NcButton> as a trigger, you need to bind attrs from the #trigger slot props to your custom button. See <NcPopover> docs for an example.')
}
*/
}
},
render() {
return this.$slots.default?.({
attrs: this.triggerAttrs,
})
},
})
</script>

Просмотреть файл

@ -83,12 +83,13 @@ export default {
class="user-bubble__wrapper"
@update:open="onOpenChange">
<!-- Main userbubble structure -->
<template #trigger>
<template #trigger="{ attrs }">
<component :is="isLinkComponent"
class="user-bubble__content"
:style="styles.content"
:href="hasUrl ? url : null"
:class="{ 'user-bubble__content--primary': primary }"
v-bind="attrs"
@click="onClick">
<!-- NcAvatar -->
<NcAvatar :url="isCustomAvatar && isAvatarUrl ? avatarImage : undefined"

Просмотреть файл

@ -105,6 +105,7 @@ module.exports = async () => {
'src/components/NcDialog*/*.vue',
'src/components/NcEllipsisedOption*/*.vue',
'src/components/NcListItem*/*.vue',
'src/components/NcPopover/NcPopoverTriggerProvider.vue',
'src/components/NcRichContenteditable/!(NcRichContenteditable).vue',
'src/components/NcRichText*/*.vue',
'src/components/NcSelect*/*.vue',

Просмотреть файл

@ -43,7 +43,7 @@
--color-box-shadow: rgba(var(--color-box-shadow-rgb), 0.5);
--color-border: #ededed;
--color-border-dark: #dbdbdb;
--color-border-maxcontrast: #949494;
--color-border-maxcontrast: #7d7d7d;
--font-face: system-ui, -apple-system, 'Segoe UI', Roboto, Oxygen-Sans, Cantarell, Ubuntu, 'Helvetica Neue', 'Noto Sans', 'Liberation Sans', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
--default-font-size: 15px;
--animation-quick: 100ms;

Просмотреть файл

@ -69,6 +69,7 @@ function chunkify(t) {
}
window.OC = {
debug: true,
getCurrentUser() {
return {
uid: 'admin',

Просмотреть файл

@ -1,4 +1,5 @@
export default {
debug: true,
getLanguage() {
return 'en-GB'