logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://hacktivis.me/git/pleroma-fe.git
commit: 71d9bbb56bf0927ce29e926e6e717c5ac8304f20
parent e0aeab04b8c1c296d2e16943f7333e05f5b2450b
Author: Weblate <noreply@weblate.org>
Date:   Sun, 31 Jul 2022 23:00:53 +0000

Merge remote-tracking branch 'origin/develop' into develop

Diffstat:

MCHANGELOG.md12+++++++++++-
Mpackage.json24++++++++++++------------
Msrc/App.js3+--
Msrc/App.scss11+++++++++--
Msrc/App.vue3++-
Msrc/boot/after_store.js3+++
Msrc/components/basic_user_card/basic_user_card.js12++----------
Msrc/components/basic_user_card/basic_user_card.vue31++++++++++++++-----------------
Msrc/components/chat_message/chat_message.js8+++-----
Msrc/components/chat_message/chat_message.vue8++++----
Msrc/components/chat_title/chat_title.js10+++-------
Msrc/components/chat_title/chat_title.vue6+++---
Msrc/components/desktop_nav/desktop_nav.scss1+
Msrc/components/desktop_nav/desktop_nav.vue2+-
Msrc/components/emoji_picker/emoji_picker.scss3++-
Msrc/components/extra_buttons/extra_buttons.js3+++
Msrc/components/extra_buttons/extra_buttons.vue50++++++++++++++++++++++++++------------------------
Msrc/components/global_notice_list/global_notice_list.vue2+-
Msrc/components/media_modal/media_modal.vue2+-
Msrc/components/mention_link/mention_link.js24+++++++++++++++++++++---
Msrc/components/mention_link/mention_link.scss29+++++++++++++++--------------
Msrc/components/mention_link/mention_link.vue84++++++++++++++++++++++++++++++++++++-------------------------------------------
Msrc/components/mentions_line/mentions_line.vue3+--
Msrc/components/mobile_nav/mobile_nav.vue6++++--
Msrc/components/modal/modal.vue5++++-
Msrc/components/nav_panel/nav_panel.vue2++
Msrc/components/notification/notification.js4+++-
Msrc/components/notification/notification.vue25+++++++++++++------------
Msrc/components/notifications/notifications.js10++++++++++
Msrc/components/poll/poll_form.vue2+-
Msrc/components/popover/popover.js221++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/components/popover/popover.vue48++++++++++++++++++++++++++++--------------------
Msrc/components/react_button/react_button.vue5+++--
Msrc/components/settings_modal/helpers/modified_indicator.vue10+++++-----
Msrc/components/settings_modal/helpers/server_side_indicator.vue10+++++-----
Msrc/components/settings_modal/tabs/general_tab.vue30++++++++++++++++++------------
Msrc/components/shout_panel/shout_panel.vue2+-
Msrc/components/side_drawer/side_drawer.vue2+-
Msrc/components/status/status.js6+++---
Msrc/components/status/status.vue32++++++++++++++------------------
Msrc/components/status_popover/status_popover.js7+++++++
Msrc/components/status_popover/status_popover.vue4++--
Msrc/components/timeline_menu/timeline_menu.vue50++++++++++++++++++--------------------------------
Msrc/components/user_card/user_card.js30++++++++++++++++++++++++------
Msrc/components/user_card/user_card.scss51++++++++++++++++++++++++++++++++++++++-------------
Msrc/components/user_card/user_card.vue50+++++++++++++++++++++++++++++++++++++++++---------
Asrc/components/user_popover/user_popover.js23+++++++++++++++++++++++
Asrc/components/user_popover/user_popover.vue33+++++++++++++++++++++++++++++++++
Msrc/components/user_profile/user_profile.vue2+-
Msrc/i18n/en.json4+++-
Msrc/modules/config.js2++
Mtest/unit/specs/components/rich_content.spec.js28++++++++++++----------------
Myarn.lock316++++++++++++++++++++++++++++++++++++++++++-------------------------------------
53 files changed, 838 insertions(+), 516 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md @@ -16,17 +16,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Attachments are ALWAYS in same order as user uploaded, no more "videos first" - Attachment description is prefilled with backend-provided default when uploading - Proper visual feedback that next image is loading when browsing +- UI no longer lags when switching between mobile and desktop mode +- Popovers no longer constrained by DOM hierarchy, shouldn't be cut off by anything +- "Always show mobile button" is working now ### Changed +- Using Vue 3 now - (You)s are optional (opt-in) now, bolding your nickname is also optional (opt-out) - User highlight background now also covers the `@` - Reverted back to textual `@`, svg version is opt-in. -- Settings window has been throughly rearranged to make make more sense and make navication settings easier. +- Settings window has been thoroughly rearranged to make more sense and make navigation settings easier. - Uploaded attachments are uniform with displayed attachments - Flash is watchable in media-modal (takes up nearly full screen though due to sizing issues) - Notifications about likes/repeats/emoji reacts are now minimized so they always take up same amount of space irrelevant to size of post. +- Slight width/spacing adjustments +- More sizing stuff is font-size dependent now +- Scrollbars are styled/colorized now +- Scrollbars are toggleable (for stuff that didn't have visible scrollbars before) (opt-in) ### Added +- 3 column mode: only enables when there's space for it (opt-out, customizable) - Options to show domains in mentions - Option to show user avatars in mention links (opt-in) - Option to disable the tooltip for mentions @@ -37,6 +46,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Media modal now also displays description and counter position in gallery (i.e. 1/5) - Ability to rearrange order of attachments when uploading - Enabled users to zoom and pan images in media viewer with mouse and touch +- Timelines/panels and conversations have sticky headers now - Added frontend ui for account migration diff --git a/package.json b/package.json @@ -18,15 +18,15 @@ "dependencies": { "@babel/runtime": "7.18.9", "@chenfengyuan/vue-qrcode": "2.0.0", - "@fortawesome/fontawesome-svg-core": "6.1.1", - "@fortawesome/free-regular-svg-icons": "6.1.1", - "@fortawesome/free-solid-svg-icons": "6.1.1", + "@fortawesome/fontawesome-svg-core": "6.1.2", + "@fortawesome/free-regular-svg-icons": "6.1.2", + "@fortawesome/free-solid-svg-icons": "6.1.2", "@fortawesome/vue-fontawesome": "3.0.1", "@kazvmoe-infra/pinch-zoom-element": "1.2.0", "@ruffle-rs/ruffle": "^0.1.0-nightly.2022.7.12", "@vuelidate/core": "2.0.0-alpha.43", - "@vuelidate/validators": "2.0.0-alpha.30", - "body-scroll-lock": "2.7.1", + "@vuelidate/validators": "2.0.0-alpha.31", + "body-scroll-lock": "3.1.5", "chromatism": "3.0.0", "click-outside-vue3": "4.0.1", "cropperjs": "1.5.12", @@ -39,10 +39,10 @@ "punycode.js": "2.1.0", "qrcode": "1", "utf8": "^3.0.0", - "vue": "^3.2.31", + "vue": "3.2.37", "vue-i18n": "9.2.0-beta.40", "vue-router": "4.1.2", - "vue-template-compiler": "2.6.11", + "vue-template-compiler": "2.7.8", "vuex": "4.0.2" }, "devDependencies": { @@ -54,15 +54,15 @@ "@ungap/event-target": "0.2.3", "@vue/babel-helper-vue-jsx-merge-props": "1.2.1", "@vue/babel-plugin-jsx": "1.1.1", - "@vue/compiler-sfc": "^3.1.0", - "@vue/test-utils": "2.0.0-rc.17", + "@vue/compiler-sfc": "3.2.37", + "@vue/test-utils": "2.0.2", "autoprefixer": "6.7.7", "babel-eslint": "7.2.3", "babel-loader": "8.2.5", "babel-plugin-lodash": "3.3.4", "chai": "3.5.0", "chalk": "1.1.3", - "chromedriver": "87.0.7", + "chromedriver": "103.0.0", "connect-history-api-fallback": "1.6.0", "copy-webpack-plugin": "6.4.1", "cross-spawn": "4.0.2", @@ -105,7 +105,7 @@ "ora": "0.4.1", "postcss-loader": "3.0.0", "raw-loader": "0.5.1", - "sass": "1.53.0", + "sass": "1.54.0", "sass-loader": "7.3.1", "selenium-server": "2.53.1", "semver": "5.7.1", @@ -118,7 +118,7 @@ "stylelint-rscss": "0.4.0", "url-loader": "1.1.2", "vue-loader": "^16.0.0", - "vue-style-loader": "4.1.2", + "vue-style-loader": "4.1.3", "webpack": "4.46.0", "webpack-dev-middleware": "3.7.3", "webpack-hot-middleware": "2.25.1", diff --git a/src/App.js b/src/App.js @@ -4,7 +4,6 @@ import InstanceSpecificPanel from './components/instance_specific_panel/instance import FeaturesPanel from './components/features_panel/features_panel.vue' import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue' import ShoutPanel from './components/shout_panel/shout_panel.vue' -import SettingsModal from './components/settings_modal/settings_modal.vue' import MediaModal from './components/media_modal/media_modal.vue' import SideDrawer from './components/side_drawer/side_drawer.vue' import MobilePostStatusButton from './components/mobile_post_status_button/mobile_post_status_button.vue' @@ -32,7 +31,7 @@ export default { MobilePostStatusButton, MobileNav, DesktopNav, - SettingsModal, + SettingsModal: defineAsyncComponent(() => import('./components/settings_modal/settings_modal.vue')), UserReportingModal, PostStatusModal, GlobalNoticeList diff --git a/src/App.scss b/src/App.scss @@ -4,6 +4,13 @@ :root { --navbar-height: 3.5rem; --post-line-height: 1.4; + // Z-Index stuff + --ZI_media_modal: 90000; + --ZI_modals_popovers: 85000; + --ZI_modals: 80000; + --ZI_navbar_popovers: 75000; + --ZI_navbar: 70000; + --ZI_popovers: 60000; } html { @@ -117,7 +124,7 @@ i[class*=icon-], } nav { - z-index: 1000; + z-index: var(--ZI_navbar); color: var(--topBarText); background-color: $fallback--fg; background-color: var(--topBar, $fallback--fg); @@ -828,7 +835,7 @@ option { // Vue transitions .fade-enter-active, .fade-leave-active { - transition: opacity 0.2s; + transition: opacity 0.3s; } .fade-enter-from, diff --git a/src/App.vue b/src/App.vue @@ -42,7 +42,7 @@ </div> <div id="notifs-column" class="column -scrollable" :class="{ '-show-scrollbar': showScrollbars }"/> </div> - <media-modal /> + <MediaModal /> <shout-panel v-if="currentUser && shout && !hideShoutbox" :floating="true" @@ -55,6 +55,7 @@ <SettingsModal /> <div id="modal" /> <GlobalNoticeList /> + <div id="popovers" /> </div> </template> diff --git a/src/boot/after_store.js b/src/boot/after_store.js @@ -396,6 +396,9 @@ const afterStoreSetup = async ({ store, i18n }) => { app.component('FAIcon', FontAwesomeIcon) app.component('FALayers', FontAwesomeLayers) + // remove after vue 3.3 + app.config.unwrapInjectedRef = true + app.mount('#app') return app diff --git a/src/components/basic_user_card/basic_user_card.js b/src/components/basic_user_card/basic_user_card.js @@ -1,4 +1,4 @@ -import UserCard from '../user_card/user_card.vue' +import UserPopover from '../user_popover/user_popover.vue' import UserAvatar from '../user_avatar/user_avatar.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -7,20 +7,12 @@ const BasicUserCard = { props: [ 'user' ], - data () { - return { - userExpanded: false - } - }, components: { - UserCard, + UserPopover, UserAvatar, RichContent }, methods: { - toggleUserExpanded () { - this.userExpanded = !this.userExpanded - }, userProfileLink (user) { return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames) } diff --git a/src/components/basic_user_card/basic_user_card.vue b/src/components/basic_user_card/basic_user_card.vue @@ -1,24 +1,19 @@ <template> <div class="basic-user-card"> - <router-link :to="userProfileLink(user)"> - <UserAvatar - class="avatar" - :user="user" - @click.prevent="toggleUserExpanded" - /> + <router-link @click.prevent :to="userProfileLink(user)"> + <UserPopover + :userId="user.id" + :overlayCenters="true" + overlayCentersSelector=".avatar" + > + <UserAvatar + class="user-avatar avatar" + :user="user" + @click.prevent + /> + </UserPopover> </router-link> <div - v-if="userExpanded" - class="basic-user-card-expanded-content" - > - <UserCard - :user-id="user.id" - :rounded="true" - :bordered="true" - /> - </div> - <div - v-else class="basic-user-card-collapsed-content" > <div @@ -53,6 +48,8 @@ margin: 0; padding: 0.6em 1em; + --emoji-size: 14px; + &-collapsed-content { margin-left: 0.7em; text-align: left; diff --git a/src/components/chat_message/chat_message.js b/src/components/chat_message/chat_message.js @@ -6,7 +6,7 @@ import Gallery from '../gallery/gallery.vue' import LinkPreview from '../link-preview/link-preview.vue' import StatusContent from '../status_content/status_content.vue' import ChatMessageDate from '../chat_message_date/chat_message_date.vue' -import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' +import { defineAsyncComponent } from 'vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes, @@ -35,7 +35,8 @@ const ChatMessage = { UserAvatar, Gallery, LinkPreview, - ChatMessageDate + ChatMessageDate, + UserPopover: defineAsyncComponent(() => import('../user_popover/user_popover.vue')) }, computed: { // Returns HH:MM (hours and minutes) in local time. @@ -49,9 +50,6 @@ const ChatMessage = { message () { return this.chatViewItem.data }, - userProfileLink () { - return generateProfileLink(this.author.id, this.author.screen_name, this.$store.state.instance.restrictedNicknames) - }, isMessage () { return this.chatViewItem.type === 'message' }, diff --git a/src/components/chat_message/chat_message.vue b/src/components/chat_message/chat_message.vue @@ -14,16 +14,16 @@ v-if="!isCurrentUser" class="avatar-wrapper" > - <router-link + <UserPopover v-if="chatViewItem.isHead" - :to="userProfileLink" + :userId="author.id" > <UserAvatar :compact="true" :better-shadow="betterShadow" :user="author" /> - </router-link> + </UserPopover> </div> <div class="chat-message-inner"> <div @@ -44,7 +44,7 @@ <Popover trigger="click" placement="top" - :bound-to-selector="isCurrentUser ? '' : '.scrollable-message-list'" + bound-to-selector=".chat-view-inner" :bound-to="{ x: 'container' }" :margin="popoverMarginStyle" @show="menuOpened = true" diff --git a/src/components/chat_title/chat_title.js b/src/components/chat_title/chat_title.js @@ -1,12 +1,13 @@ -import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import UserAvatar from '../user_avatar/user_avatar.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' +import { defineAsyncComponent } from 'vue' export default { name: 'ChatTitle', components: { UserAvatar, - RichContent + RichContent, + UserPopover: defineAsyncComponent(() => import('../user_popover/user_popover.vue')) }, props: [ 'user', 'withAvatar' @@ -18,10 +19,5 @@ export default { htmlTitle () { return this.user ? this.user.name_html : '' } - }, - methods: { - getUserProfileLink (user) { - return generateProfileLink(user.id, user.screen_name) - } } } diff --git a/src/components/chat_title/chat_title.vue b/src/components/chat_title/chat_title.vue @@ -3,16 +3,16 @@ class="chat-title" :title="title" > - <router-link + <UserPopover class="avatar-container" v-if="withAvatar && user" - :to="getUserProfileLink(user)" + :userId="user.id" > <UserAvatar class="titlebar-avatar" :user="user" /> - </router-link> + </UserPopover> <RichContent v-if="user" class="username" diff --git a/src/components/desktop_nav/desktop_nav.scss b/src/components/desktop_nav/desktop_nav.scss @@ -2,6 +2,7 @@ .DesktopNav { width: 100%; + z-index: var(--ZI_navbar); input { color: var(--inputTopbarText, var(--inputText)); diff --git a/src/components/desktop_nav/desktop_nav.vue b/src/components/desktop_nav/desktop_nav.vue @@ -38,7 +38,7 @@ /> <button class="button-unstyled nav-icon" - @click.stop="openSettingsModal" + @click="openSettingsModal" > <FAIcon fixed-width diff --git a/src/components/emoji_picker/emoji_picker.scss b/src/components/emoji_picker/emoji_picker.scss @@ -7,7 +7,8 @@ right: 0; left: 0; margin: 0 !important; - z-index: 100; + // TODO: actually use popover in emoji picker + z-index: var(--ZI_popovers); background-color: $fallback--bg; background-color: var(--popover, $fallback--bg); color: $fallback--link; diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js @@ -89,6 +89,9 @@ const ExtraButtons = { canMute () { return !!this.currentUser }, + canBookmark () { + return !!this.currentUser + }, statusLink () { return `${this.$store.state.instance.server}${this.$router.resolve({ name: 'conversation', params: { id: this.status.id } }).href}` } diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue @@ -51,28 +51,30 @@ icon="thumbtack" /><span>{{ $t("status.unpin") }}</span> </button> - <button - v-if="!status.bookmarked" - class="button-default dropdown-item dropdown-item-icon" - @click.prevent="bookmarkStatus" - @click="close" - > - <FAIcon - fixed-width - :icon="['far', 'bookmark']" - /><span>{{ $t("status.bookmark") }}</span> - </button> - <button - v-if="status.bookmarked" - class="button-default dropdown-item dropdown-item-icon" - @click.prevent="unbookmarkStatus" - @click="close" - > - <FAIcon - fixed-width - icon="bookmark" - /><span>{{ $t("status.unbookmark") }}</span> - </button> + <template v-if="canBookmark"> + <button + v-if="!status.bookmarked" + class="button-default dropdown-item dropdown-item-icon" + @click.prevent="bookmarkStatus" + @click="close" + > + <FAIcon + fixed-width + :icon="['far', 'bookmark']" + /><span>{{ $t("status.bookmark") }}</span> + </button> + <button + v-if="status.bookmarked" + class="button-default dropdown-item dropdown-item-icon" + @click.prevent="unbookmarkStatus" + @click="close" + > + <FAIcon + fixed-width + icon="bookmark" + /><span>{{ $t("status.unbookmark") }}</span> + </button> + </template> <button v-if="canDelete" class="button-default dropdown-item dropdown-item-icon" @@ -119,12 +121,12 @@ </div> </template> <template v-slot:trigger> - <button class="button-unstyled popover-trigger"> + <span class="button-unstyled popover-trigger"> <FAIcon class="fa-scale-110 fa-old-padding" icon="ellipsis-h" /> - </button> + </span> </template> </Popover> </template> diff --git a/src/components/global_notice_list/global_notice_list.vue b/src/components/global_notice_list/global_notice_list.vue @@ -32,7 +32,7 @@ top: 50px; width: 100%; pointer-events: none; - z-index: 1001; + z-index: var(--ZI_popovers); display: flex; flex-direction: column; align-items: center; diff --git a/src/components/media_modal/media_modal.vue b/src/components/media_modal/media_modal.vue @@ -121,7 +121,7 @@ $modal-view-button-icon-width: 3em; $modal-view-button-icon-margin: 0.5em; .modal-view.media-modal-view { - z-index: 9000; + z-index: var(--ZI_media_modal); flex-direction: column; .modal-view-button-arrow, diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js @@ -2,6 +2,7 @@ import generateProfileLink from 'src/services/user_profile_link_generator/user_p import { mapGetters, mapState } from 'vuex' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import UserAvatar from '../user_avatar/user_avatar.vue' +import { defineAsyncComponent } from 'vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faAt @@ -14,7 +15,8 @@ library.add( const MentionLink = { name: 'MentionLink', components: { - UserAvatar + UserAvatar, + UserPopover: defineAsyncComponent(() => import('../user_popover/user_popover.vue')) }, props: { url: { @@ -34,15 +36,30 @@ const MentionLink = { type: String } }, + data () { + return { + hasSelection: false + } + }, methods: { onClick () { + if (this.shouldShowTooltip) return const link = generateProfileLink( this.userId || this.user.id, this.userScreenName || this.user.screen_name ) this.$router.push(link) + }, + handleSelection () { + this.hasSelection = document.getSelection().containsNode(this.$refs.full, true) } }, + mounted () { + document.addEventListener('selectionchange', this.handleSelection) + }, + unmounted () { + document.removeEventListener('selectionchange', this.handleSelection) + }, computed: { user () { return this.url && this.$store && this.$store.getters.findUserByUrl(this.url) @@ -88,7 +105,8 @@ const MentionLink = { return [ { '-you': this.isYou && this.shouldBoldenYou, - '-highlighted': this.highlight + '-highlighted': this.highlight, + '-has-selection': this.hasSelection }, this.highlightType ] @@ -110,7 +128,7 @@ const MentionLink = { } }, shouldShowTooltip () { - return this.mergedConfig.mentionLinkShowTooltip && this.mergedConfig.mentionLinkDisplay === 'short' && this.isRemote + return this.mergedConfig.mentionLinkShowTooltip }, shouldShowAvatar () { return this.mergedConfig.mentionLinkShowAvatar diff --git a/src/components/mention_link/mention_link.scss b/src/components/mention_link/mention_link.scss @@ -55,11 +55,14 @@ .new { &.-you { - & .shortName, - & .full { + .shortName { font-weight: 600; } } + &.-has-selection { + color: var(--alertNeutralText, $fallback--text); + background-color: var(--alertNeutral, $fallback--fg); + } .at { color: var(--link); @@ -72,8 +75,7 @@ } &.-striped { - & .shortName, - & .full { + & .shortName { background-image: repeating-linear-gradient( 135deg, @@ -86,30 +88,29 @@ } &.-solid { - & .shortName, - & .full { + .shortName { background-image: linear-gradient(var(--____highlight-tintColor2), var(--____highlight-tintColor2)); } } &.-side { - & .shortName, - & .userNameFull { + .shortName { box-shadow: 0 -5px 3px -4px inset var(--____highlight-solidColor); } } } - &:hover .new .full { - opacity: 1; - pointer-events: initial; + .full { + pointer-events: none; } .serverName.-faded { color: var(--faintLink, $fallback--link); } +} - .full .-faded { - color: var(--faint, $fallback--faint); - } +.mention-link-popover { + max-width: 70ch; + max-height: 20rem; + overflow: hidden; } diff --git a/src/components/mention_link/mention_link.vue b/src/components/mention_link/mention_link.vue @@ -9,66 +9,58 @@ class="original" target="_blank" v-html="content" - /><!-- eslint-enable vue/no-v-html --><span - v-if="user" - class="new" - :style="style" - :class="classnames" + /><!-- eslint-enable vue/no-v-html --> + <UserPopover + v-else + :userId="user.id" + :disabled="!shouldShowTooltip" > - <a - class="short button-unstyled" - :class="{ '-with-tooltip': shouldShowTooltip }" - :href="url" - @click.prevent="onClick" + <span + v-if="user" + class="new" + :style="style" + :class="classnames" > - <!-- eslint-disable vue/no-v-html --> - <UserAvatar - v-if="shouldShowAvatar" - class="mention-avatar" - :user="user" - /><span - class="shortName" - ><FAIcon - v-if="useAtIcon" - size="sm" - icon="at" - class="at" - />{{ !useAtIcon ? '@' : '' }}<span - class="userName" - v-html="userName" - /><span - v-if="shouldShowFullUserName" - class="serverName" - :class="{ '-faded': shouldFadeDomain }" - v-html="'@' + serverName" - /> - </span> - <span - v-if="isYou && shouldShowYous" - :class="{ '-you': shouldBoldenYou }" - > {{ ' ' + $t('status.you') }}</span> - <!-- eslint-enable vue/no-v-html --> - </a><span - v-if="shouldShowTooltip" - class="full popover-default" - :class="[highlightType]" - > - <span - class="userNameFull" + <a + class="short button-unstyled" + :class="{ '-with-tooltip': shouldShowTooltip }" + :href="url" + @click.prevent="onClick" > <!-- eslint-disable vue/no-v-html --> - @<span + <UserAvatar + v-if="shouldShowAvatar" + class="mention-avatar" + :user="user" + /><span + class="shortName" + ><FAIcon + v-if="useAtIcon" + size="sm" + icon="at" + class="at" + />{{ !useAtIcon ? '@' : '' }}<span class="userName" v-html="userName" /><span + v-if="shouldShowFullUserName" class="serverName" :class="{ '-faded': shouldFadeDomain }" v-html="'@' + serverName" /> + </span> + <span + v-if="isYou && shouldShowYous" + :class="{ '-you': shouldBoldenYou }" + > {{ ' ' + $t('status.you') }}</span> <!-- eslint-enable vue/no-v-html --> + </a><span class="full" ref="full"> + <!-- eslint-disable vue/no-v-html --> + @<span v-html="userName" /><span v-html="'@' + serverName" /> + <!-- eslint-enable vue/no-v-html --> </span> </span> - </span> + </UserPopover> </span> </template> diff --git a/src/components/mentions_line/mentions_line.vue b/src/components/mentions_line/mentions_line.vue @@ -13,8 +13,7 @@ <span v-if="expanded" class="fullExtraMentions" - > - <MentionLink + >{{ ' ' }}<MentionLink v-for="mention in extraMentions" :key="mention.index" class="mention-link" diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue @@ -86,6 +86,8 @@ @import '../../_variables.scss'; .MobileNav { + z-index: var(--ZI_navbar); + .mobile-nav { display: grid; line-height: var(--navbar-height); @@ -147,7 +149,7 @@ transition-property: transform; transition-duration: 0.25s; transform: translateX(0); - z-index: 1001; + z-index: var(--ZI_navbar); -webkit-overflow-scrolling: touch; &.-closed { @@ -160,7 +162,7 @@ display: flex; align-items: center; justify-content: space-between; - z-index: 1; + z-index: calc(var(--ZI_navbar) + 100); width: 100%; height: 50px; line-height: 50px; diff --git a/src/components/modal/modal.vue b/src/components/modal/modal.vue @@ -22,6 +22,9 @@ export default { default: false } }, + provide: { + popoversZLayer: 'modals' + }, computed: { classes () { return { @@ -35,7 +38,7 @@ export default { <style lang="scss"> .modal-view { - z-index: 2000; + z-index: var(--ZI_modals); position: fixed; top: 0; left: 0; diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue @@ -113,7 +113,9 @@ border-color: $fallback--border; border-color: var(--border, $fallback--border); padding: 0; + } + > li { &:first-child .menu-item { border-top-right-radius: $fallback--panelRadius; border-top-right-radius: var(--panelRadius, $fallback--panelRadius); diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js @@ -5,6 +5,7 @@ import UserAvatar from '../user_avatar/user_avatar.vue' import UserCard from '../user_card/user_card.vue' import Timeago from '../timeago/timeago.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' +import UserPopover from '../user_popover/user_popover.vue' import { isStatusNotification } from '../../services/notification_utils/notification_utils.js' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -46,7 +47,8 @@ const Notification = { UserCard, Timeago, Status, - RichContent + RichContent, + UserPopover }, methods: { toggleUserExpanded () { diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue @@ -34,21 +34,22 @@ <a class="avatar-container" :href="$router.resolve(userProfileLink).href" - @click.stop.prevent.capture="toggleUserExpanded" + @click.prevent > - <UserAvatar - :compact="true" - :better-shadow="betterShadow" - :user="notification.from_profile" - /> + <UserPopover + :userId="notification.from_profile.id" + :overlayCenters="true" + > + <UserAvatar + class="post-avatar" + :bot="botIndicator" + :compact="true" + :better-shadow="betterShadow" + :user="notification.from_profile" + /> + </UserPopover> </a> <div class="notification-right"> - <UserCard - v-if="userExpanded" - :user-id="getUser(notification).id" - :rounded="true" - :bordered="true" - /> <span class="notification-details"> <div class="name-and-action"> <!-- eslint-disable vue/no-v-html --> diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js @@ -1,3 +1,4 @@ +import { computed } from 'vue' import { mapGetters } from 'vuex' import Notification from '../notification/notification.vue' import NotificationFilters from './notification_filters.vue' @@ -40,6 +41,11 @@ const Notifications = { seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT } }, + provide () { + return { + popoversZLayer: computed(() => this.popoversZLayer) + } + }, computed: { mainClass () { return this.minimalMode ? '' : 'panel panel-default' @@ -77,6 +83,10 @@ const Notifications = { } return map[layoutType] || '#notifs-sidebar' }, + popoversZLayer () { + const { layoutType } = this.$store.state.interface + return layoutType === 'mobile' ? 'navbar' : null + }, notificationsToDisplay () { return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount) }, diff --git a/src/components/poll/poll_form.vue b/src/components/poll/poll_form.vue @@ -84,7 +84,7 @@ :key="unit" :value="unit" > - {{ $t(`time.${unit}_short`, ['']) }} + {{ $tc(`time.unit.${unit}_short`, expiryAmount, ['']) }} </option> </Select> </div> diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js @@ -31,13 +31,35 @@ const Popover = { // If true, subtract padding when calculating position for the popover, // use it when popover offset looks to be different on top vs bottom. - removePadding: Boolean + removePadding: Boolean, + + // self-explanatory (i hope) + disabled: Boolean, + + // Instead of putting popover next to anchor, overlay popover's center on top of anchor's center + overlayCenters: Boolean, + + // What selector (witin popover!) to use for determining center of popover + overlayCentersSelector: String, + + // Lets hover popover stay when clicking inside of it + stayOnClick: Boolean }, + inject: ['popoversZLayer'], // override popover z layer data () { return { + // lockReEntry is a flag that is set when mouse cursor is leaving the popover's content + // so that if mouse goes back into popover it won't be re-shown again to prevent annoyance + // with popovers refusing to be hidden when user wants to interact with something in below popover + lockReEntry: false, hidden: true, - styles: { opacity: 0 }, - oldSize: { width: 0, height: 0 } + styles: {}, + oldSize: { width: 0, height: 0 }, + scrollable: null, + // used to avoid blinking if hovered onto popover + graceTimeout: null, + parentPopover: null, + childrenShown: new Set() } }, methods: { @@ -47,9 +69,7 @@ const Popover = { }, updateStyles () { if (this.hidden) { - this.styles = { - opacity: 0 - } + this.styles = {} return } @@ -57,14 +77,26 @@ const Popover = { // its children are what are inside the slot. Expect only one v-slot:trigger. const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el // SVGs don't have offsetWidth/Height, use fallback - const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth const anchorHeight = anchorEl.offsetHeight || anchorEl.clientHeight - const screenBox = anchorEl.getBoundingClientRect() - // Screen position of the origin point for popover - const origin = { x: screenBox.left + screenBox.width * 0.5, y: screenBox.top } + const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth + const anchorScreenBox = anchorEl.getBoundingClientRect() + + const anchorStyle = getComputedStyle(anchorEl) + const topPadding = parseFloat(anchorStyle.paddingTop) + const bottomPadding = parseFloat(anchorStyle.paddingBottom) + + // Screen position of the origin point for popover = center of the anchor + const origin = { + x: anchorScreenBox.left + anchorWidth * 0.5, + y: anchorScreenBox.top + anchorHeight * 0.5 + } const content = this.$refs.content + const overlayCenter = this.overlayCenters + ? this.$refs.content.querySelector(this.overlayCentersSelector) + : null + // Minor optimization, don't call a slow reflow call if we don't have to - const parentBounds = this.boundTo && + const parentScreenBox = this.boundTo && (this.boundTo.x === 'container' || this.boundTo.y === 'container') && this.containerBoundingClientRect() @@ -73,81 +105,151 @@ const Popover = { // What are the screen bounds for the popover? Viewport vs container // when using viewport, using default margin values to dodge the navbar const xBounds = this.boundTo && this.boundTo.x === 'container' ? { - min: parentBounds.left + (margin.left || 0), - max: parentBounds.right - (margin.right || 0) + min: parentScreenBox.left + (margin.left || 0), + max: parentScreenBox.right - (margin.right || 0) } : { min: 0 + (margin.left || 10), max: window.innerWidth - (margin.right || 10) } const yBounds = this.boundTo && this.boundTo.y === 'container' ? { - min: parentBounds.top + (margin.top || 0), - max: parentBounds.bottom - (margin.bottom || 0) + min: parentScreenBox.top + (margin.top || 0), + max: parentScreenBox.bottom - (margin.bottom || 0) } : { min: 0 + (margin.top || 50), max: window.innerHeight - (margin.bottom || 5) } let horizOffset = 0 + let vertOffset = 0 + + if (overlayCenter) { + const box = content.getBoundingClientRect() + const overlayCenterScreenBox = overlayCenter.getBoundingClientRect() + const leftInnerOffset = overlayCenterScreenBox.left - box.left + const topInnerOffset = overlayCenterScreenBox.top - box.top + horizOffset = -leftInnerOffset - overlayCenter.offsetWidth * 0.5 + vertOffset = -topInnerOffset - overlayCenter.offsetHeight * 0.5 + } else { + horizOffset = content.offsetWidth * -0.5 + vertOffset = content.offsetHeight * -0.5 + } + + const leftBorder = origin.x + horizOffset + const rightBorder = leftBorder + content.offsetWidth + const topBorder = origin.y + vertOffset + const bottomBorder = topBorder + content.offsetHeight // If overflowing from left, move it so that it doesn't - if ((origin.x - content.offsetWidth * 0.5) < xBounds.min) { - horizOffset += -(origin.x - content.offsetWidth * 0.5) + xBounds.min + if (leftBorder < xBounds.min) { + horizOffset += xBounds.min - leftBorder } // If overflowing from right, move it so that it doesn't - if ((origin.x + horizOffset + content.offsetWidth * 0.5) > xBounds.max) { - horizOffset -= (origin.x + horizOffset + content.offsetWidth * 0.5) - xBounds.max + if (rightBorder > xBounds.max) { + horizOffset -= rightBorder - xBounds.max } - // Default to whatever user wished with placement prop - let usingTop = this.placement !== 'bottom' - - // Handle special cases, first force to displaying on top if there's not space on bottom, - // regardless of what placement value was. Then check if there's not space on top, and - // force to bottom, again regardless of what placement value was. - if (origin.y + content.offsetHeight > yBounds.max) usingTop = true - if (origin.y - content.offsetHeight < yBounds.min) usingTop = false + // If overflowing from top, move it so that it doesn't + if (topBorder < yBounds.min) { + vertOffset += yBounds.min - topBorder + } - let vPadding = 0 - if (this.removePadding && usingTop) { - const anchorStyle = getComputedStyle(anchorEl) - vPadding = parseFloat(anchorStyle.paddingTop) + parseFloat(anchorStyle.paddingBottom) + // If overflowing from bottom, move it so that it doesn't + if (bottomBorder > yBounds.max) { + vertOffset -= bottomBorder - yBounds.max } - const yOffset = (this.offset && this.offset.y) || 0 - const translateY = usingTop - ? -anchorHeight + vPadding - yOffset - content.offsetHeight - : yOffset + let translateX = 0 + let translateY = 0 + + if (overlayCenter) { + translateX = origin.x + horizOffset + translateY = origin.y + vertOffset + } else { + // Default to whatever user wished with placement prop + let usingTop = this.placement !== 'bottom' + + // Handle special cases, first force to displaying on top if there's not space on bottom, + // regardless of what placement value was. Then check if there's not space on top, and + // force to bottom, again regardless of what placement value was. + const topBoundary = origin.y - anchorHeight * 0.5 + (this.removePadding ? topPadding : 0) + const bottomBoundary = origin.y + anchorHeight * 0.5 - (this.removePadding ? bottomPadding : 0) + if (bottomBoundary + content.offsetHeight > yBounds.max) usingTop = true + if (topBoundary - content.offsetHeight < yBounds.min) usingTop = false - const xOffset = (this.offset && this.offset.x) || 0 - const translateX = anchorWidth * 0.5 - content.offsetWidth * 0.5 + horizOffset + xOffset + const yOffset = (this.offset && this.offset.y) || 0 + translateY = usingTop + ? topBoundary - yOffset - content.offsetHeight + : bottomBoundary + yOffset + + const xOffset = (this.offset && this.offset.x) || 0 + translateX = origin.x + horizOffset + xOffset + } - // Note, separate translateX and translateY avoids blurry text on chromium, - // single translate or translate3d resulted in blurry text. this.styles = { - opacity: 1, - transform: `translateX(${Math.round(translateX)}px) translateY(${Math.round(translateY)}px)` + left: `${Math.round(translateX)}px`, + top: `${Math.round(translateY)}px` + } + + if (this.popoversZLayer) { + this.styles['--ZI_popover_override'] = `var(--ZI_${this.popoversZLayer}_popovers)` + } + if (parentScreenBox) { + this.styles.maxWidth = `${Math.round(parentScreenBox.width)}px` } }, showPopover () { + if (this.disabled) return const wasHidden = this.hidden this.hidden = false + this.parentPopover && this.parentPopover.onChildPopoverState(this, true) + if (this.trigger === 'click' || this.stayOnClick) { + document.addEventListener('click', this.onClickOutside) + } + this.scrollable.addEventListener('scroll', this.onScroll) + this.scrollable.addEventListener('resize', this.onResize) this.$nextTick(() => { if (wasHidden) this.$emit('show') this.updateStyles() }) }, hidePopover () { + if (this.disabled) return if (!this.hidden) this.$emit('close') this.hidden = true - this.styles = { opacity: 0 } + this.parentPopover && this.parentPopover.onChildPopoverState(this, false) + if (this.trigger === 'click') { + document.removeEventListener('click', this.onClickOutside) + } + this.scrollable.removeEventListener('scroll', this.onScroll) + this.scrollable.removeEventListener('resize', this.onResize) }, onMouseenter (e) { - if (this.trigger === 'hover') this.showPopover() + if (this.trigger === 'hover') { + this.lockReEntry = false + clearTimeout(this.graceTimeout) + this.graceTimeout = null + this.showPopover() + } }, onMouseleave (e) { - if (this.trigger === 'hover') this.hidePopover() + if (this.trigger === 'hover' && this.childrenShown.size === 0) { + this.graceTimeout = setTimeout(() => this.hidePopover(), 1) + } + }, + onMouseenterContent (e) { + if (this.trigger === 'hover' && !this.lockReEntry) { + this.lockReEntry = true + clearTimeout(this.graceTimeout) + this.graceTimeout = null + this.showPopover() + } + }, + onMouseleaveContent (e) { + if (this.trigger === 'hover' && this.childrenShown.size === 0) { + this.graceTimeout = setTimeout(() => this.hidePopover(), 1) + } }, onClick (e) { if (this.trigger === 'click') { @@ -160,8 +262,24 @@ const Popover = { }, onClickOutside (e) { if (this.hidden) return + if (this.$refs.content && this.$refs.content.contains(e.target)) return if (this.$el.contains(e.target)) return + if (this.childrenShown.size > 0) return this.hidePopover() + if (this.parentPopover) this.parentPopover.onClickOutside(e) + }, + onScroll (e) { + this.updateStyles() + }, + onResize (e) { + this.updateStyles() + }, + onChildPopoverState (childRef, state) { + if (state) { + this.childrenShown.add(childRef) + } else { + this.childrenShown.delete(childRef) + } } }, updated () { @@ -175,11 +293,18 @@ const Popover = { this.oldSize = { width: content.offsetWidth, height: content.offsetHeight } } }, - created () { - document.addEventListener('click', this.onClickOutside) + mounted () { + let scrollable = this.$refs.trigger.closest('.column.-scrollable') || + this.$refs.trigger.closest('.mobile-notifications') + if (!scrollable) scrollable = window + this.scrollable = scrollable + let parent = this.$parent + while (parent && parent.$.type.name !== 'Popover') { + parent = parent.$parent + } + this.parentPopover = parent }, - unmounted () { - document.removeEventListener('click', this.onClickOutside) + beforeUnmount () { this.hidePopover() } } diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue @@ -1,5 +1,5 @@ <template> - <div + <span @mouseenter="onMouseenter" @mouseleave="onMouseleave" > @@ -11,20 +11,27 @@ > <slot name="trigger" /> </button> - <div - v-if="!hidden" - ref="content" - :style="styles" - class="popover" - :class="popoverClass || 'popover-default'" - > - <slot - name="content" - class="popover-inner" - :close="hidePopover" - /> - </div> - </div> + <teleport to="#popovers"> + <transition name="fade"> + <div + v-if="!hidden" + ref="content" + :style="styles" + class="popover" + :class="popoverClass || 'popover-default'" + @mouseenter="onMouseenterContent" + @mouseleave="onMouseleaveContent" + @click="onClickContent" + > + <slot + name="content" + class="popover-inner" + :close="hidePopover" + /> + </div> + </transition> + </teleport> + </span> </template> <script src="./popover.js" /> @@ -37,14 +44,15 @@ } .popover { - z-index: 500; - position: absolute; + z-index: var(--ZI_popover_override, var(--ZI_popovers)); + position: fixed; min-width: 0; + max-width: calc(100vw - 20px); + box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5); + box-shadow: var(--popupShadow); } .popover-default { - transition: opacity 0.3s; - &:after { content: ''; position: absolute; @@ -80,7 +88,7 @@ text-align: left; list-style: none; max-width: 100vw; - z-index: 200; + z-index: var(--ZI_popover_override, var(--ZI_popovers)); white-space: nowrap; .dropdown-divider { diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue @@ -6,6 +6,7 @@ :offset="{ y: 5 }" :bound-to="{ x: 'container' }" remove-padding + popover-class="ReactButton popover-default" @show="focusInput" > <template v-slot:content="{close}"> @@ -41,7 +42,7 @@ </div> </template> <template v-slot:trigger> - <button + <span class="button-unstyled popover-trigger" :title="$t('tool_tip.add_reaction')" > @@ -49,7 +50,7 @@ class="fa-scale-110 fa-old-padding" :icon="['far', 'smile-beam']" /> - </button> + </span> </template> </Popover> </template> diff --git a/src/components/settings_modal/helpers/modified_indicator.vue b/src/components/settings_modal/helpers/modified_indicator.vue @@ -41,11 +41,11 @@ export default { .ModifiedIndicator { display: inline-block; position: relative; +} - .modified-tooltip { - margin: 0.5em 1em; - min-width: 10em; - text-align: center; - } +.modified-tooltip { + margin: 0.5em 1em; + min-width: 10em; + text-align: center; } </style> diff --git a/src/components/settings_modal/helpers/server_side_indicator.vue b/src/components/settings_modal/helpers/server_side_indicator.vue @@ -41,11 +41,11 @@ export default { .ServerSideIndicator { display: inline-block; position: relative; +} - .serverside-tooltip { - margin: 0.5em 1em; - min-width: 10em; - text-align: center; - } +.serverside-tooltip { + margin: 0.5em 1em; + min-width: 10em; + text-align: center; } </style> diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue @@ -75,6 +75,16 @@ </BooleanSetting> </li> <li> + <BooleanSetting path="userPopoverZoom" expert="1"> + {{ $t('settings.user_popover_avatar_zoom') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="userPopoverOverlay" expert="1"> + {{ $t('settings.user_popover_avatar_overlay') }} + </BooleanSetting> + </li> + <li> <ChoiceSetting v-if="user" id="thirdColumnMode" @@ -261,18 +271,14 @@ {{ $t('settings.mention_link_display') }} </ChoiceSetting> </li> - <ul - class="setting-list suboptions" - > - <li v-if="mentionLinkDisplay === 'short'"> - <BooleanSetting - path="mentionLinkShowTooltip" - expert="1" - > - {{ $t('settings.mention_link_show_tooltip') }} - </BooleanSetting> - </li> - </ul> + <li> + <BooleanSetting + path="mentionLinkShowTooltip" + expert="1" + > + {{ $t('settings.mention_link_use_tooltip') }} + </BooleanSetting> + </li> <li> <BooleanSetting path="useAtIcon" diff --git a/src/components/shout_panel/shout_panel.vue b/src/components/shout_panel/shout_panel.vue @@ -80,7 +80,7 @@ .floating-shout { position: fixed; bottom: 0.5em; - z-index: 1000; + z-index: var(--ZI_popovers); max-width: 25em; &.-left { diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue @@ -211,7 +211,7 @@ .side-drawer-container { position: fixed; - z-index: 1000; + z-index: var(--ZI_navbar); top: 0; left: 0; width: 100%; diff --git a/src/components/status/status.js b/src/components/status/status.js @@ -4,13 +4,13 @@ import ReactButton from '../react_button/react_button.vue' import RetweetButton from '../retweet_button/retweet_button.vue' import ExtraButtons from '../extra_buttons/extra_buttons.vue' import PostStatusForm from '../post_status_form/post_status_form.vue' -import UserCard from '../user_card/user_card.vue' import UserAvatar from '../user_avatar/user_avatar.vue' import AvatarList from '../avatar_list/avatar_list.vue' import Timeago from '../timeago/timeago.vue' import StatusContent from '../status_content/status_content.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' import StatusPopover from '../status_popover/status_popover.vue' +import UserPopover from '../user_popover/user_popover.vue' import UserListPopover from '../user_list_popover/user_list_popover.vue' import EmojiReactions from '../emoji_reactions/emoji_reactions.vue' import MentionsLine from 'src/components/mentions_line/mentions_line.vue' @@ -105,7 +105,6 @@ const Status = { RetweetButton, ExtraButtons, PostStatusForm, - UserCard, UserAvatar, AvatarList, Timeago, @@ -115,7 +114,8 @@ const Status = { StatusContent, RichContent, MentionLink, - MentionsLine + MentionsLine, + UserPopover }, props: [ 'statusoid', diff --git a/src/components/status/status.vue b/src/components/status/status.vue @@ -122,27 +122,22 @@ v-if="!noHeading" class="left-side" > - <a - :href="$router.resolve(userProfileLink).href" - @click.stop.prevent.capture="toggleUserExpanded" - > - <UserAvatar - class="post-avatar" - :bot="botIndicator" - :compact="compact" - :better-shadow="betterShadow" - :user="status.user" - /> + <a :href="$router.resolve(userProfileLink).href" @click.prevent> + <UserPopover + :userId="status.user.id" + :overlayCenters="true" + > + <UserAvatar + class="post-avatar" + :bot="botIndicator" + :compact="compact" + :better-shadow="betterShadow" + :user="status.user" + /> + </UserPopover> </a> </div> <div class="right-side"> - <UserCard - v-if="userExpanded" - :user-id="status.user.id" - :rounded="true" - :bordered="true" - class="usercard" - /> <div v-if="!noHeading" class="status-heading" @@ -322,6 +317,7 @@ class="mentions-line-first" /> </span> + {{ ' ' }} <MentionsLine v-if="hasMentionsLine" :mentions="mentionsLine.slice(1)" diff --git a/src/components/status_popover/status_popover.js b/src/components/status_popover/status_popover.js @@ -38,6 +38,13 @@ const StatusPopover = { .catch(e => (this.error = true)) } } + }, + watch: { + status (newStatus, oldStatus) { + if (newStatus !== oldStatus) { + this.$nextTick(() => this.$refs.popover.updateStyles()) + } + } } } diff --git a/src/components/status_popover/status_popover.vue b/src/components/status_popover/status_popover.vue @@ -1,9 +1,11 @@ <template> <Popover trigger="hover" + :stay-on-click="true" popover-class="popover-default status-popover" :bound-to="{ x: 'container' }" @show="enter" + ref="popover" > <template v-slot:trigger> <slot /> @@ -52,8 +54,6 @@ border-width: 1px; border-radius: $fallback--tooltipRadius; border-radius: var(--tooltipRadius, $fallback--tooltipRadius); - box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5); - box-shadow: var(--popupShadow); /* TODO cleanup this */ .Status.Status { diff --git a/src/components/timeline_menu/timeline_menu.vue b/src/components/timeline_menu/timeline_menu.vue @@ -3,19 +3,17 @@ trigger="click" class="TimelineMenu" :class="{ 'open': isOpen }" - :margin="{ left: -15, right: -200 }" :bound-to="{ x: 'container' }" - popover-class="timeline-menu-popover-wrap" + bound-to-selector=".Timeline" + popover-class="timeline-menu-popover popover-default" @show="openMenu" @close="() => isOpen = false" > <template v-slot:content> - <div class="timeline-menu-popover popover-default"> - <TimelineMenuContent /> - </div> + <TimelineMenuContent /> </template> <template v-slot:trigger> - <button class="button-unstyled title timeline-menu-title"> + <span class="button-unstyled title timeline-menu-title"> <span class="timeline-title">{{ timelineName() }}</span> <span> <FAIcon @@ -27,7 +25,7 @@ class="click-blocker" @click="blockOpen" /> - </button> + </span> </template> </Popover> </template> @@ -38,42 +36,18 @@ @import '../../_variables.scss'; .TimelineMenu { - flex-shrink: 1; margin-right: auto; min-width: 0; - width: 24rem; .popover-trigger-button { vertical-align: bottom; } - .timeline-menu-popover-wrap { - overflow: hidden; - // Match panel heading padding to line up menu with bottom of heading - margin-top: 0.6rem; - padding: 0 15px 15px 15px; - } - - .timeline-menu-popover { - width: 24rem; - max-width: 100vw; - margin: 0; - font-size: 1rem; - border-top-right-radius: 0; - border-top-left-radius: 0; - transform: translateY(-100%); - transition: transform 100ms; - } - .panel::after { border-top-right-radius: 0; border-top-left-radius: 0; } - &.open .timeline-menu-popover { - transform: translateY(0); - } - .timeline-menu-title { margin: 0; cursor: pointer; @@ -108,6 +82,16 @@ box-shadow: var(--popoverShadow); } +} + +.timeline-menu-popover { + min-width: 24rem; + max-width: 100vw; + margin-top: 0.6rem; + font-size: 1rem; + border-top-right-radius: 0; + border-top-left-radius: 0; + ul { list-style: none; margin: 0; @@ -134,7 +118,9 @@ a { display: block; - padding: 0.6em 0.65em; + padding: 0 0.65em; + height: 3.5em; + line-height: 3.5em; &:hover { background-color: $fallback--lightBg; diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js @@ -14,7 +14,9 @@ import { faRss, faSearchPlus, faExternalLinkAlt, - faEdit + faEdit, + faTimes, + faExpandAlt } from '@fortawesome/free-solid-svg-icons' library.add( @@ -22,12 +24,21 @@ library.add( faBell, faSearchPlus, faExternalLinkAlt, - faEdit + faEdit, + faTimes, + faExpandAlt ) export default { props: [ - 'userId', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered', 'allowZoomingAvatar' + 'userId', + 'switcher', + 'selected', + 'hideBio', + 'rounded', + 'bordered', + 'avatarAction', // default - open profile, 'zoom' - zoom, function - call function + 'onClose' ], data () { return { @@ -47,9 +58,10 @@ export default { }, classes () { return [{ - 'user-card-rounded-t': this.rounded === 'top', // set border-top-left-radius and border-top-right-radius - 'user-card-rounded': this.rounded === true, // set border-radius for all sides - 'user-card-bordered': this.bordered === true // set border for all sides + '-rounded-t': this.rounded === 'top', // set border-top-left-radius and border-top-right-radius + '-rounded': this.rounded === true, // set border-radius for all sides + '-bordered': this.bordered === true, // set border for all sides + '-popover': !!this.onClose // set popover rounding }] }, style () { @@ -170,6 +182,12 @@ export default { }, mentionUser () { this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user }) + }, + onAvatarClickHandler (e) { + if (this.onAvatarClick) { + e.preventDefault() + this.onAvatarClick() + } } } } diff --git a/src/components/user_card/user_card.scss b/src/components/user_card/user_card.scss @@ -42,8 +42,10 @@ mask-composite: exclude; background-size: cover; mask-size: 100% 60%; - border-top-left-radius: calc(var(--panelRadius) - 1px); - border-top-right-radius: calc(var(--panelRadius) - 1px); + border-top-left-radius: calc(var(--__roundnessTop, --panelRadius) - 1px); + border-top-right-radius: calc(var(--__roundnessTop, --panelRadius) - 1px); + border-bottom-left-radius: calc(var(--__roundnessBottom, --panelRadius) - 1px); + border-bottom-right-radius: calc(var(--__roundnessBottom, --panelRadius) - 1px); background-color: var(--profileBg); z-index: -2; @@ -72,21 +74,33 @@ } } - // Modifiers - - &-rounded-t { + &.-rounded-t { border-top-left-radius: $fallback--panelRadius; border-top-left-radius: var(--panelRadius, $fallback--panelRadius); border-top-right-radius: $fallback--panelRadius; border-top-right-radius: var(--panelRadius, $fallback--panelRadius); + + --__roundnessTop: var(--panelRadius); + --__roundnessBottom: 0; } - &-rounded { + &.-rounded { border-radius: $fallback--panelRadius; border-radius: var(--panelRadius, $fallback--panelRadius); + + --__roundnessTop: var(--panelRadius); + --__roundnessBottom: var(--panelRadius); } - &-bordered { + &.-popover { + border-radius: $fallback--tooltipRadius; + border-radius: var(--tooltipRadius, $fallback--tooltipRadius); + + --__roundnessTop: var(--tooltipRadius); + --__roundnessBottom: var(--tooltipRadius); + } + + &.-bordered { border-width: 1px; border-style: solid; border-color: $fallback--border; @@ -99,6 +113,15 @@ color: var(--lightText, $fallback--lightText); padding: 0 26px; + a { + color: $fallback--lightText; + color: var(--lightText, $fallback--lightText); + + &:hover { + color: var(--icon); + } + } + .container { min-width: 0; padding: 16px 0 6px; @@ -110,23 +133,27 @@ min-width: 0; } + > a { + vertical-align: middle; + display: flex; + } + .Avatar { --_avatarShadowBox: var(--avatarShadow); --_avatarShadowFilter: var(--avatarShadowFilter); --_avatarShadowInset: var(--avatarShadowInset); - flex: 1 0 100%; width: 56px; height: 56px; object-fit: cover; } } - &-avatar-link { + &-avatar { position: relative; cursor: pointer; - &-overlay { + &.-overlay { position: absolute; left: 0; top: 0; @@ -146,7 +173,7 @@ } } - &:hover &-overlay { + &:hover &.-overlay { opacity: 1; } } @@ -206,8 +233,6 @@ flex: 0 1 auto; text-overflow: ellipsis; overflow: hidden; - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); } .dailyAvg { diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue @@ -8,25 +8,32 @@ :style="style" class="background-image" /> - <div class="panel-heading -flexible-height"> + <div :class="onClose ? '' : panel-heading -flexible-height"> <div class="user-info"> <div class="container"> <a - v-if="allowZoomingAvatar" - class="user-info-avatar-link" + v-if="avatarAction === 'zoom'" + class="user-info-avatar -link" @click="zoomAvatar" > <UserAvatar :better-shadow="betterShadow" :user="user" /> - <div class="user-info-avatar-link-overlay"> + <div class="user-info-avatar -link -overlay"> <FAIcon class="fa-scale-110 fa-old-padding" icon="search-plus" /> </div> </a> + <UserAvatar + v-else-if="typeof avatarAction === 'function'" + @click="avatarAction" + class="user-info-avatar" + :better-shadow="betterShadow" + :user="user" + /> <router-link v-else :to="userProfileLink(user)" @@ -38,12 +45,16 @@ </router-link> <div class="user-summary"> <div class="top-line"> - <RichContent - :title="user.name" + <router-link + :to="userProfileLink(user)" class="user-name" - :html="user.name" - :emoji="user.emoji" - /> + > + <RichContent + :title="user.name" + :html="user.name" + :emoji="user.emoji" + /> + </router-link> <button v-if="!isOtherUser && user.is_local" class="button-unstyled edit-profile-button" @@ -72,6 +83,27 @@ :user="user" :relationship="relationship" /> + <router-link + v-if="onClose" + :to="userProfileLink(user)" + class="button-unstyled external-link-button" + @click="onClose" + > + <FAIcon + class="icon" + icon="expand-alt" + /> + </router-link> + <button + v-if="onClose" + class="button-unstyled external-link-button" + @click="onClose" + > + <FAIcon + class="icon" + icon="times" + /> + </button> </div> <div class="bottom-line"> <router-link diff --git a/src/components/user_popover/user_popover.js b/src/components/user_popover/user_popover.js @@ -0,0 +1,23 @@ +import UserCard from '../user_card/user_card.vue' +import { defineAsyncComponent } from 'vue' + +const UserPopover = { + name: 'UserPopover', + props: [ + 'userId', 'overlayCenters', 'disabled', 'overlayCentersSelector' + ], + components: { + UserCard, + Popover: defineAsyncComponent(() => import('../popover/popover.vue')) + }, + computed: { + userPopoverZoom () { + return this.$store.getters.mergedConfig.userPopoverZoom + }, + userPopoverOverlay () { + return this.$store.getters.mergedConfig.userPopoverOverlay + } + } +} + +export default UserPopover diff --git a/src/components/user_popover/user_popover.vue b/src/components/user_popover/user_popover.vue @@ -0,0 +1,33 @@ +<template> +<Popover + trigger="click" + popover-class="popover-default user-popover" + :overlay-centers-selector="overlayCentersSelector || '.user-info .Avatar'" + :overlay-centers="overlayCenters && userPopoverOverlay" + :disabled="disabled" +> + <template v-slot:trigger> + <slot /> + </template> + <template v-slot:content={close}> + <UserCard + class="user-popover" + :user-id="userId" + :hide-bio="true" + :avatar-action="userPopoverZoom ? 'zoom' : close" + :on-close="close" + /> + </template> +</Popover> +</template> + +<script src="./user_popover.js" ></script> + +<style lang="scss"> +@import '../../_variables.scss'; + +/* popover styles load on-demand, so we need to override */ +.user-popover.popover { +} + +</style> diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue @@ -8,7 +8,7 @@ :user-id="userId" :switcher="true" :selected="timeline.viewing" - :allow-zooming-avatar="true" + avatar-action="zoom" rounded="top" /> <div diff --git a/src/i18n/en.json b/src/i18n/en.json @@ -546,10 +546,12 @@ "mention_link_display_short": "always as short names (e.g. {'@'}foo)", "mention_link_display_full_for_remote": "as full names only for remote users (e.g. {'@'}foo{'@'}example.org)", "mention_link_display_full": "always as full names (e.g. {'@'}foo{'@'}example.org)", - "mention_link_show_tooltip": "Show full user names as tooltip for remote users", + "mention_link_use_tooltip": "Show user card when clicking mention links", "mention_link_show_avatar": "Show user avatar beside the link", "mention_link_fade_domain": "Fade domains (e.g. {'@'}example.org in {'@'}foo{'@'}example.org)", "mention_link_bolden_you": "Highlight mention of you when you are mentioned", + "user_popover_avatar_zoom": "Clicking on user avatar in popover zooms it instead of closing the popover", + "user_popover_avatar_overlay": "Show user popover over user avatar", "fun": "Fun", "greentext": "Meme arrows", "show_yous": "Show (You)s", diff --git a/src/modules/config.js b/src/modules/config.js @@ -81,6 +81,8 @@ export const defaultState = { useContainFit: true, disableStickyHeaders: false, showScrollbars: false, + userPopoverZoom: false, + userPopoverOverlay: true, greentext: undefined, // instance default useAtIcon: undefined, // instance default mentionLinkDisplay: undefined, // instance default diff --git a/test/unit/specs/components/rich_content.spec.js b/test/unit/specs/components/rich_content.spec.js @@ -4,7 +4,15 @@ import RichContent from 'src/components/rich_content/rich_content.jsx' const attentions = [] const global = { mocks: { - '$store': null + '$store': { + state: {}, + getters: { + mergedConfig: () => ({ + mentionLinkShowTooltip: true + }), + findUserByUrl: () => null + } + } }, stubs: { FAIcon: true @@ -131,8 +139,7 @@ describe('RichContent', () => { ].join(''), [ makeMention('John'), - makeMention('Josh'), - makeMention('Jeremy') + makeMention('Josh'), makeMention('Jeremy') ].join('') ].join('\n') @@ -349,7 +356,6 @@ describe('RichContent', () => { p( '<span class="MentionsLine">', '<span class="MentionLink mention-link">', - '<!-- eslint-disable vue/no-v-html -->', '<a href="lol" class="original" target="_blank">', '<span>', 'https://</span>', @@ -358,10 +364,7 @@ describe('RichContent', () => { '<span>', '</span>', '</a>', - '<!-- eslint-enable vue/no-v-html -->', - '<!--v-if-->', // v-if placeholder, mentionlink's "new" (i.e. rich) display '</span>', - '<!--v-if-->', // v-if placeholder, mentionsline's extra mentions and stuff '</span>' ), p( @@ -380,7 +383,7 @@ describe('RichContent', () => { } }) - expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected)) + expect(wrapper.html().replace(/\n/g, '').replace(/<!--.*?-->/g, '')).to.eql(compwrap(expected)) }) it('rich contents of nested mentions are handled properly', () => { @@ -412,7 +415,6 @@ describe('RichContent', () => { '<span class="poast-style">', '<span class="MentionsLine">', '<span class="MentionLink mention-link">', - '<!-- eslint-disable vue/no-v-html -->', '<a href="lol" class="original" target="_blank">', '<span>', 'https://</span>', @@ -421,11 +423,8 @@ describe('RichContent', () => { '<span>', '</span>', '</a>', - '<!-- eslint-enable vue/no-v-html -->', - '<!--v-if-->', // v-if placeholder, mentionlink's "new" (i.e. rich) display '</span>', '<span class="MentionLink mention-link">', - '<!-- eslint-disable vue/no-v-html -->', '<a href="lol" class="original" target="_blank">', '<span>', 'https://</span>', @@ -434,10 +433,7 @@ describe('RichContent', () => { '<span>', '</span>', '</a>', - '<!-- eslint-enable vue/no-v-html -->', - '<!--v-if-->', // v-if placeholder, mentionlink's "new" (i.e. rich) display '</span>', - '<!--v-if-->', // v-if placeholder, mentionsline's extra mentions and stuff '</span>', ' ', '</span>', @@ -455,7 +451,7 @@ describe('RichContent', () => { } }) - expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected)) + expect(wrapper.html().replace(/\n/g, '').replace(/<!--.*?-->/g, '')).to.eql(compwrap(expected)) }) it('rich contents of a link are handled properly', () => { diff --git a/yarn.lock b/yarn.lock @@ -1294,31 +1294,31 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@fortawesome/fontawesome-common-types@6.1.1": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.1.1.tgz#7dc996042d21fc1ae850e3173b5c67b0549f9105" - integrity sha512-wVn5WJPirFTnzN6tR95abCx+ocH+3IFLXAgyavnf9hUmN0CfWoDjPT/BAWsUVwSlYYVBeCLJxaqi7ZGe4uSjBA== +"@fortawesome/fontawesome-common-types@6.1.2": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.1.2.tgz#c1095b1bbabf19f37f9ff0719db38d92a410bcfe" + integrity sha512-wBaAPGz1Awxg05e0PBRkDRuTsy4B3dpBm+zreTTyd9TH4uUM27cAL4xWyWR0rLJCrRwzVsQ4hF3FvM6rqydKPA== -"@fortawesome/fontawesome-svg-core@6.1.1": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.1.1.tgz#3424ec6182515951816be9b11665d67efdce5b5f" - integrity sha512-NCg0w2YIp81f4V6cMGD9iomfsIj7GWrqmsa0ZsPh59G7PKiGN1KymZNxmF00ssuAlo/VZmpK6xazsGOwzKYUMg== +"@fortawesome/fontawesome-svg-core@6.1.2": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.1.2.tgz#11e2e8583a7dea75d734e4d0e53d91c63fae7511" + integrity sha512-853G/Htp0BOdXnPoeCPTjFrVwyrJHpe8MhjB/DYE9XjwhnNDfuBCd3aKc2YUYbEfHEcBws4UAA0kA9dymZKGjA== dependencies: - "@fortawesome/fontawesome-common-types" "6.1.1" + "@fortawesome/fontawesome-common-types" "6.1.2" -"@fortawesome/free-regular-svg-icons@6.1.1": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.1.1.tgz#3f2f58262a839edf0643cbacee7a8a8230061c98" - integrity sha512-xXiW7hcpgwmWtndKPOzG+43fPH7ZjxOaoeyooptSztGmJxCAflHZxXNK0GcT0uEsR4jTGQAfGklDZE5NHoBhKg== +"@fortawesome/free-regular-svg-icons@6.1.2": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.1.2.tgz#9f04009098addcc11d0d185126f058ed042c3099" + integrity sha512-xR4hA+tAwsaTHGfb+25H1gVU/aJ0Rzu+xIUfnyrhaL13yNQ7TWiI2RvzniAaB+VGHDU2a+Pk96Ve+pkN3/+TTQ== dependencies: - "@fortawesome/fontawesome-common-types" "6.1.1" + "@fortawesome/fontawesome-common-types" "6.1.2" -"@fortawesome/free-solid-svg-icons@6.1.1": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.1.1.tgz#3369e673f8fe8be2fba30b1ec274d47490a830a6" - integrity sha512-0/5exxavOhI/D4Ovm2r3vxNojGZioPwmFrKg0ZUH69Q68uFhFPs6+dhAToh6VEQBntxPRYPuT5Cg1tpNa9JUPg== +"@fortawesome/free-solid-svg-icons@6.1.2": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.1.2.tgz#491d668b8a6603698d0ce1ac620f66fd22b74c84" + integrity sha512-lTgZz+cMpzjkHmCwOG3E1ilUZrnINYdqMmrkv30EC3XbRsGlbIOL8H9LaNp5SV4g0pNJDfQ4EdTWWaMvdwyLiQ== dependencies: - "@fortawesome/fontawesome-common-types" "6.1.1" + "@fortawesome/fontawesome-common-types" "6.1.2" "@fortawesome/vue-fontawesome@3.0.1": version "3.0.1" @@ -1499,10 +1499,10 @@ remark "^13.0.0" unist-util-find-all-after "^3.0.2" -"@testim/chrome-version@^1.0.7": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.0.7.tgz#0cd915785ec4190f08a3a6acc9b61fc38fb5f1a9" - integrity sha512-8UT/J+xqCYfn3fKtOznAibsHpiuDshCb0fwgWxRazTT19Igp9ovoXMPhXyLD6m3CKQGTMHgqoxaFfMWaL40Rnw== +"@testim/chrome-version@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.1.2.tgz#092005c5b77bd3bb6576a4677110a11485e11864" + integrity sha512-1c4ZOETSRpI0iBfIFUqU4KqwBAB2lHUAlBjZz/YqOHqwM9dTTzjV6Km0ZkiEiSCx/tLr1BtESIKyWWMww+RUqw== "@types/color-name@^1.1.1": version "1.1.1" @@ -1625,47 +1625,47 @@ html-tags "^3.1.0" svg-tags "^1.0.0" -"@vue/compiler-core@3.2.31": - version "3.2.31" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.31.tgz#d38f06c2cf845742403b523ab4596a3fda152e89" - integrity sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ== +"@vue/compiler-core@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.37.tgz#b3c42e04c0e0f2c496ff1784e543fbefe91e215a" + integrity sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg== dependencies: "@babel/parser" "^7.16.4" - "@vue/shared" "3.2.31" + "@vue/shared" "3.2.37" estree-walker "^2.0.2" source-map "^0.6.1" -"@vue/compiler-dom@3.2.31": - version "3.2.31" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz#b1b7dfad55c96c8cc2b919cd7eb5fd7e4ddbf00e" - integrity sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg== +"@vue/compiler-dom@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz#10d2427a789e7c707c872da9d678c82a0c6582b5" + integrity sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ== dependencies: - "@vue/compiler-core" "3.2.31" - "@vue/shared" "3.2.31" + "@vue/compiler-core" "3.2.37" + "@vue/shared" "3.2.37" -"@vue/compiler-sfc@3.2.31", "@vue/compiler-sfc@^3.1.0": - version "3.2.31" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.31.tgz#d02b29c3fe34d599a52c5ae1c6937b4d69f11c2f" - integrity sha512-748adc9msSPGzXgibHiO6T7RWgfnDcVQD+VVwYgSsyyY8Ans64tALHZANrKtOzvkwznV/F4H7OAod/jIlp/dkQ== +"@vue/compiler-sfc@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz#3103af3da2f40286edcd85ea495dcb35bc7f5ff4" + integrity sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg== dependencies: "@babel/parser" "^7.16.4" - "@vue/compiler-core" "3.2.31" - "@vue/compiler-dom" "3.2.31" - "@vue/compiler-ssr" "3.2.31" - "@vue/reactivity-transform" "3.2.31" - "@vue/shared" "3.2.31" + "@vue/compiler-core" "3.2.37" + "@vue/compiler-dom" "3.2.37" + "@vue/compiler-ssr" "3.2.37" + "@vue/reactivity-transform" "3.2.37" + "@vue/shared" "3.2.37" estree-walker "^2.0.2" magic-string "^0.25.7" postcss "^8.1.10" source-map "^0.6.1" -"@vue/compiler-ssr@3.2.31": - version "3.2.31" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.31.tgz#4fa00f486c9c4580b40a4177871ebbd650ecb99c" - integrity sha512-mjN0rqig+A8TVDnsGPYJM5dpbjlXeHUm2oZHZwGyMYiGT/F4fhJf/cXy8QpjnLQK4Y9Et4GWzHn9PS8AHUnSkw== +"@vue/compiler-ssr@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz#4899d19f3a5fafd61524a9d1aee8eb0505313cff" + integrity sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw== dependencies: - "@vue/compiler-dom" "3.2.31" - "@vue/shared" "3.2.31" + "@vue/compiler-dom" "3.2.37" + "@vue/shared" "3.2.37" "@vue/devtools-api@^6.0.0-beta.11": version "6.1.3" @@ -1677,58 +1677,58 @@ resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.2.1.tgz#6f2948ff002ec46df01420dfeff91de16c5b4092" integrity sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ== -"@vue/reactivity-transform@3.2.31": - version "3.2.31" - resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.31.tgz#0f5b25c24e70edab2b613d5305c465b50fc00911" - integrity sha512-uS4l4z/W7wXdI+Va5pgVxBJ345wyGFKvpPYtdSgvfJfX/x2Ymm6ophQlXXB6acqGHtXuBqNyyO3zVp9b1r0MOA== +"@vue/reactivity-transform@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz#0caa47c4344df4ae59f5a05dde2a8758829f8eca" + integrity sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg== dependencies: "@babel/parser" "^7.16.4" - "@vue/compiler-core" "3.2.31" - "@vue/shared" "3.2.31" + "@vue/compiler-core" "3.2.37" + "@vue/shared" "3.2.37" estree-walker "^2.0.2" magic-string "^0.25.7" -"@vue/reactivity@3.2.31": - version "3.2.31" - resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.31.tgz#fc90aa2cdf695418b79e534783aca90d63a46bbd" - integrity sha512-HVr0l211gbhpEKYr2hYe7hRsV91uIVGFYNHj73njbARVGHQvIojkImKMaZNDdoDZOIkMsBc9a1sMqR+WZwfSCw== +"@vue/reactivity@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.37.tgz#5bc3847ac58828e2b78526e08219e0a1089f8848" + integrity sha512-/7WRafBOshOc6m3F7plwzPeCu/RCVv9uMpOwa/5PiY1Zz+WLVRWiy0MYKwmg19KBdGtFWsmZ4cD+LOdVPcs52A== dependencies: - "@vue/shared" "3.2.31" + "@vue/shared" "3.2.37" -"@vue/runtime-core@3.2.31": - version "3.2.31" - resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.31.tgz#9d284c382f5f981b7a7b5971052a1dc4ef39ac7a" - integrity sha512-Kcog5XmSY7VHFEMuk4+Gap8gUssYMZ2+w+cmGI6OpZWYOEIcbE0TPzzPHi+8XTzAgx1w/ZxDFcXhZeXN5eKWsA== +"@vue/runtime-core@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.37.tgz#7ba7c54bb56e5d70edfc2f05766e1ca8519966e3" + integrity sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ== dependencies: - "@vue/reactivity" "3.2.31" - "@vue/shared" "3.2.31" + "@vue/reactivity" "3.2.37" + "@vue/shared" "3.2.37" -"@vue/runtime-dom@3.2.31": - version "3.2.31" - resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.31.tgz#79ce01817cb3caf2c9d923f669b738d2d7953eff" - integrity sha512-N+o0sICVLScUjfLG7u9u5XCjvmsexAiPt17GNnaWHJUfsKed5e85/A3SWgKxzlxx2SW/Hw7RQxzxbXez9PtY3g== +"@vue/runtime-dom@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.37.tgz#002bdc8228fa63949317756fb1e92cdd3f9f4bbd" + integrity sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw== dependencies: - "@vue/runtime-core" "3.2.31" - "@vue/shared" "3.2.31" + "@vue/runtime-core" "3.2.37" + "@vue/shared" "3.2.37" csstype "^2.6.8" -"@vue/server-renderer@3.2.31": - version "3.2.31" - resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.31.tgz#201e9d6ce735847d5989403af81ef80960da7141" - integrity sha512-8CN3Zj2HyR2LQQBHZ61HexF5NReqngLT3oahyiVRfSSvak+oAvVmu8iNLSu6XR77Ili2AOpnAt1y8ywjjqtmkg== +"@vue/server-renderer@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.37.tgz#840a29c8dcc29bddd9b5f5ffa22b95c0e72afdfc" + integrity sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA== dependencies: - "@vue/compiler-ssr" "3.2.31" - "@vue/shared" "3.2.31" + "@vue/compiler-ssr" "3.2.37" + "@vue/shared" "3.2.37" -"@vue/shared@3.2.31": - version "3.2.31" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.31.tgz#c90de7126d833dcd3a4c7534d534be2fb41faa4e" - integrity sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ== +"@vue/shared@3.2.37": + version "3.2.37" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.37.tgz#8e6adc3f2759af52f0e85863dfb0b711ecc5c702" + integrity sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw== -"@vue/test-utils@2.0.0-rc.17": - version "2.0.0-rc.17" - resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.0.0-rc.17.tgz#e6dcf5b5bd3ae23595bdb154b9b578ebcdffd698" - integrity sha512-7LHZKsFRV/HqDoMVY+cJamFzgHgsrmQFalROHC5FMWrzPzd+utG5e11krj1tVsnxYufGA2ABShX4nlcHXED+zQ== +"@vue/test-utils@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.0.2.tgz#0b5edd683366153d5bc5a91edc62f292118710eb" + integrity sha512-E2P4oXSaWDqTZNbmKZFVLrNN/siVN78YkEqs7pHryWerrlZR9bBFLWdJwRoguX45Ru6HxIflzKl4vQvwRMwm5g== "@vuelidate/core@2.0.0-alpha.43": version "2.0.0-alpha.43" @@ -1737,12 +1737,12 @@ dependencies: vue-demi "^0.13.4" -"@vuelidate/validators@2.0.0-alpha.30": - version "2.0.0-alpha.30" - resolved "https://registry.yarnpkg.com/@vuelidate/validators/-/validators-2.0.0-alpha.30.tgz#978e676b5b5dc160e6a83fdf8c1bf26052f46e88" - integrity sha512-XH0oIU1+6bTZ1Kd1RNf7AMDsAahj1hUjLhbFUIrDhKIUKMFvG4658pqYATePNqhAegENFA+RDAPhsDXV/MB2wQ== +"@vuelidate/validators@2.0.0-alpha.31": + version "2.0.0-alpha.31" + resolved "https://registry.yarnpkg.com/@vuelidate/validators/-/validators-2.0.0-alpha.31.tgz#04d63307bc0a12db9f7ad94243350b83aacee998" + integrity sha512-+MFA9nZ7Y9zCpq383/voPDk/hiAmu6KqiJJhLOYB/FmrUPVoyKnuKnI9Bwiq8ok9GZlVkI8BnIrKPKGj9QpwiQ== dependencies: - vue-demi "^0.12.0" + vue-demi "^0.13.4" "@webassemblyjs/ast@1.9.0": version "1.9.0" @@ -2229,6 +2229,11 @@ async@^2.5.0: dependencies: lodash "^4.17.10" +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + atob@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" @@ -2258,12 +2263,13 @@ autoprefixer@^9.8.6: postcss "^7.0.32" postcss-value-parser "^4.1.0" -axios@^0.21.1: - version "0.21.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== dependencies: - follow-redirects "^1.14.0" + follow-redirects "^1.14.9" + form-data "^4.0.0" babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" @@ -2540,10 +2546,10 @@ body-parser@^1.19.0: raw-body "2.4.3" type-is "~1.6.18" -body-scroll-lock@2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/body-scroll-lock/-/body-scroll-lock-2.7.1.tgz#caf3f9c91773af1ffb684cd66ed9137b5b737014" - integrity sha512-hS53SQ8RhM0e4DsQ3PKz6Gr2O7Kpdh59TWU98GHjaQznL7y4dFycEPk7pFQAikqBaUSCArkc5E3pe7CWIt2fZA== +body-scroll-lock@3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz#c1392d9217ed2c3e237fee1e910f6cdd80b7aaec" + integrity sha512-Yi1Xaml0EvNA0OYWxXiYNqY24AfWkbA6w5vxE7GWxtKfzIbZM+Qw+aSmkgsbWzbHiy/RCSkUZBplVxTA+E4jJg== boolbase@~1.0.0: version "1.0.0" @@ -3033,17 +3039,16 @@ chrome-trace-event@^1.0.2: resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== -chromedriver@87.0.7: - version "87.0.7" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-87.0.7.tgz#74041e02ff7f633e91b98eb707e2476f713dc4ca" - integrity sha512-7J7iN2rJuSDsKb9BUUMewJt07PuTlZYd809D10dUCT1rjMD3i2jUw7dum9RxdC1xO3aFwMd8TwZ5NR82T+S+Dg== +chromedriver@103.0.0: + version "103.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-103.0.0.tgz#2ef086d62076e3ff6df6cfb84895d11d2c18d9cf" + integrity sha512-7BHf6HWt0PeOHCzWO8qlnD13sARzr5AKTtG8Csn+czsuAsajwPxdLNtry5GPh8HYFyl+i0M+yg3bT43AGfgU9w== dependencies: - "@testim/chrome-version" "^1.0.7" - axios "^0.21.1" + "@testim/chrome-version" "^1.1.2" + axios "^0.27.2" del "^6.0.0" extract-zip "^2.0.1" https-proxy-agent "^5.0.0" - mkdirp "^1.0.4" proxy-from-env "^1.1.0" tcp-port-used "^1.0.1" @@ -3211,6 +3216,13 @@ colors@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@2.17.x, commander@~2.17.1: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" @@ -3756,6 +3768,11 @@ del@^6.0.0: rimraf "^3.0.2" slash "^3.0.0" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" @@ -4756,15 +4773,24 @@ follow-redirects@^1.0.0: dependencies: debug "=3.1.0" -follow-redirects@^1.14.0: - version "1.14.9" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" - integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== +follow-redirects@^1.14.9: + version "1.15.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" + integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + formatio@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.2.0.tgz#f3b2167d9068c4698a8d51f4f760a39a54d818eb" @@ -5230,7 +5256,7 @@ he@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" -he@1.2.x, he@^1.1.0: +he@1.2.x, he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== @@ -6948,19 +6974,19 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@~2.1.24: - version "2.1.24" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" - dependencies: - mime-db "1.40.0" - -mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" +mime-types@~2.1.24: + version "2.1.24" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" + dependencies: + mime-db "1.40.0" + mime@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -9026,10 +9052,10 @@ sass-loader@7.3.1: pify "^4.0.1" semver "^6.3.0" -sass@1.53.0: - version "1.53.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.53.0.tgz#eab73a7baac045cc57ddc1d1ff501ad2659952eb" - integrity sha512-zb/oMirbKhUgRQ0/GFz8TSAwRq2IlR29vOUJZOx0l8sV+CkHUfHa4u5nqrG+1VceZp7Jfj59SVW9ogdhTvJDcQ== +sass@1.54.0: + version "1.54.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.54.0.tgz#24873673265e2a4fe3d3a997f714971db2fba1f4" + integrity sha512-C4zp79GCXZfK0yoHZg+GxF818/aclhp9F48XBu/+bm9vXEVAYov9iU3FBVRMq3Hx3OA4jfKL+p2K9180mEh0xQ== dependencies: chokidar ">=3.0.0 <4.0.0" immutable "^4.0.0" @@ -10342,11 +10368,6 @@ void-elements@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" -vue-demi@^0.12.0: - version "0.12.4" - resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.12.4.tgz#420dd17628f95f1bbce1102ad3c51074713a8049" - integrity sha512-ztPDkFt0TSUdoq1ZI6oD730vgztBkiByhUW7L1cOTebiSBqSYfSQgnhYakYigBkyAybqCTH7h44yZuDJf2xILQ== - vue-demi@^0.13.4: version "0.13.5" resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.13.5.tgz#d5eddbc9eaefb89ce5995269d1fa6b0486312092" @@ -10389,31 +10410,32 @@ vue-router@4.1.2: dependencies: "@vue/devtools-api" "^6.1.4" -vue-style-loader@4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.2.tgz#dedf349806f25ceb4e64f3ad7c0a44fba735fcf8" +vue-style-loader@4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz#6d55863a51fa757ab24e89d9371465072aa7bc35" + integrity sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg== dependencies: hash-sum "^1.0.2" loader-utils "^1.0.2" -vue-template-compiler@2.6.11: - version "2.6.11" - resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz#c04704ef8f498b153130018993e56309d4698080" - integrity sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA== +vue-template-compiler@2.7.8: + version "2.7.8" + resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.8.tgz#eadd54ed8fbff55b7deb07093a976c07f451a1dc" + integrity sha512-eQqdcUpJKJpBRPDdxCNsqUoT0edNvdt1jFjtVnVS/LPPmr0BU2jWzXlrf6BVMeODtdLewB3j8j3WjNiB+V+giw== dependencies: de-indent "^1.0.2" - he "^1.1.0" - -vue@^3.2.31: - version "3.2.31" - resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.31.tgz#e0c49924335e9f188352816788a4cca10f817ce6" - integrity sha512-odT3W2tcffTiQCy57nOT93INw1auq5lYLLYtWpPYQQYQOOdHiqFct9Xhna6GJ+pJQaF67yZABraH47oywkJgFw== - dependencies: - "@vue/compiler-dom" "3.2.31" - "@vue/compiler-sfc" "3.2.31" - "@vue/runtime-dom" "3.2.31" - "@vue/server-renderer" "3.2.31" - "@vue/shared" "3.2.31" + he "^1.2.0" + +vue@3.2.37: + version "3.2.37" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.37.tgz#da220ccb618d78579d25b06c7c21498ca4e5452e" + integrity sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ== + dependencies: + "@vue/compiler-dom" "3.2.37" + "@vue/compiler-sfc" "3.2.37" + "@vue/runtime-dom" "3.2.37" + "@vue/server-renderer" "3.2.37" + "@vue/shared" "3.2.37" vuex@4.0.2: version "4.0.2"