aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/about/about.vue2
-rw-r--r--src/components/account_actions/account_actions.vue4
-rw-r--r--src/components/attachment/attachment.scss2
-rw-r--r--src/components/chat/chat.js89
-rw-r--r--src/components/chat/chat.scss92
-rw-r--r--src/components/chat/chat.vue13
-rw-r--r--src/components/chat/chat_layout_utils.js27
-rw-r--r--src/components/chat_list/chat_list.vue2
-rw-r--r--src/components/chat_new/chat_new.vue1
-rw-r--r--src/components/chat_title/chat_title.vue19
-rw-r--r--src/components/conversation/conversation.vue6
-rw-r--r--src/components/desktop_nav/desktop_nav.scss12
-rw-r--r--src/components/emoji_picker/emoji_picker.scss2
-rw-r--r--src/components/media_modal/media_modal.vue4
-rw-r--r--src/components/media_upload/media_upload.vue2
-rw-r--r--src/components/mobile_nav/mobile_nav.js3
-rw-r--r--src/components/mobile_nav/mobile_nav.vue16
-rw-r--r--src/components/mobile_post_status_button/mobile_post_status_button.js2
-rw-r--r--src/components/modal/modal.vue2
-rw-r--r--src/components/notifications/notification_filters.vue13
-rw-r--r--src/components/notifications/notifications.js14
-rw-r--r--src/components/notifications/notifications.vue116
-rw-r--r--src/components/password_reset/password_reset.vue3
-rw-r--r--src/components/popover/popover.vue42
-rw-r--r--src/components/post_status_form/post_status_form.js2
-rw-r--r--src/components/post_status_form/post_status_form.vue19
-rw-r--r--src/components/registration/registration.vue5
-rw-r--r--src/components/settings_modal/settings_modal.scss12
-rw-r--r--src/components/settings_modal/settings_modal.vue23
-rw-r--r--src/components/settings_modal/tabs/general_tab.js5
-rw-r--r--src/components/settings_modal/tabs/general_tab.vue19
-rw-r--r--src/components/settings_modal/tabs/theme_tab/theme_tab.scss17
-rw-r--r--src/components/status/status.scss4
-rw-r--r--src/components/timeline/timeline.js5
-rw-r--r--src/components/timeline/timeline.scss27
-rw-r--r--src/components/timeline/timeline.vue4
-rw-r--r--src/components/timeline/timeline_quick_settings.vue23
-rw-r--r--src/components/user_card/user_card.scss324
-rw-r--r--src/components/user_card/user_card.vue320
-rw-r--r--src/components/user_panel/user_panel.vue1
-rw-r--r--src/components/user_profile/user_profile.vue18
-rw-r--r--src/components/user_reporting_modal/user_reporting_modal.vue11
42 files changed, 612 insertions, 715 deletions
diff --git a/src/components/about/about.vue b/src/components/about/about.vue
index 518f6184..5d5d6479 100644
--- a/src/components/about/about.vue
+++ b/src/components/about/about.vue
@@ -1,5 +1,5 @@
<template>
- <div class="sidebar">
+ <div class="column-inner">
<instance-specific-panel v-if="showInstanceSpecificPanel" />
<staff-panel />
<terms-of-service-panel />
diff --git a/src/components/account_actions/account_actions.vue b/src/components/account_actions/account_actions.vue
index 1e31151c..c35d01af 100644
--- a/src/components/account_actions/account_actions.vue
+++ b/src/components/account_actions/account_actions.vue
@@ -74,10 +74,6 @@
<style lang="scss">
@import '../../_variables.scss';
.AccountActions {
- button.dropdown-item {
- margin-left: 0;
- }
-
.ellipsis-button {
width: 2.5em;
margin: -0.5em 0;
diff --git a/src/components/attachment/attachment.scss b/src/components/attachment/attachment.scss
index dfda15bf..b2dea98d 100644
--- a/src/components/attachment/attachment.scss
+++ b/src/components/attachment/attachment.scss
@@ -173,7 +173,7 @@
margin: 8px;
word-break: break-all;
h1 {
- font-size: 14px;
+ font-size: 1rem;
margin: 0px;
}
}
diff --git a/src/components/chat/chat.js b/src/components/chat/chat.js
index aef11712..9a0ae107 100644
--- a/src/components/chat/chat.js
+++ b/src/components/chat/chat.js
@@ -6,7 +6,7 @@ import PostStatusForm from '../post_status_form/post_status_form.vue'
import ChatTitle from '../chat_title/chat_title.vue'
import chatService from '../../services/chat_service/chat_service.js'
import { promiseInterval } from '../../services/promise_interval/promise_interval.js'
-import { getScrollPosition, getNewTopPosition, isBottomedOut, scrollableContainerHeight, isScrollable } from './chat_layout_utils.js'
+import { getScrollPosition, getNewTopPosition, isBottomedOut, isScrollable } from './chat_layout_utils.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faChevronDown,
@@ -20,7 +20,7 @@ library.add(
)
const BOTTOMED_OUT_OFFSET = 10
-const JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET = 150
+const JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET = 10
const SAFE_RESIZE_TIME_OFFSET = 100
const MARK_AS_READ_DELAY = 1500
const MAX_RETRIES = 10
@@ -43,7 +43,6 @@ const Chat = {
},
created () {
this.startFetching()
- window.addEventListener('resize', this.handleLayoutChange)
},
mounted () {
window.addEventListener('scroll', this.handleScroll)
@@ -52,15 +51,11 @@ const Chat = {
}
this.$nextTick(() => {
- this.updateScrollableContainerHeight()
this.handleResize()
})
- this.setChatLayout()
},
unmounted () {
window.removeEventListener('scroll', this.handleScroll)
- window.removeEventListener('resize', this.handleLayoutChange)
- this.unsetChatLayout()
if (typeof document.hidden !== 'undefined') document.removeEventListener('visibilitychange', this.handleVisibilityChange, false)
this.$store.dispatch('clearCurrentChat')
},
@@ -96,8 +91,7 @@ const Chat = {
...mapState({
backendInteractor: state => state.api.backendInteractor,
mastoUserSocketStatus: state => state.api.mastoUserSocketStatus,
- mobileLayout: state => state.interface.mobileLayout,
- layoutHeight: state => state.interface.layoutHeight,
+ mobileLayout: state => state.interface.layoutType === 'mobile',
currentUser: state => state.users.currentUser
})
},
@@ -115,9 +109,6 @@ const Chat = {
'$route': function () {
this.startFetching()
},
- layoutHeight () {
- this.handleResize({ expand: true })
- },
mastoUserSocketStatus (newValue) {
if (newValue === WSConnectionStatus.JOINED) {
this.fetchChat({ isFirstFetch: true })
@@ -132,7 +123,6 @@ const Chat = {
onFilesDropped () {
this.$nextTick(() => {
this.handleResize()
- this.updateScrollableContainerHeight()
})
},
handleVisibilityChange () {
@@ -142,42 +132,6 @@ const Chat = {
}
})
},
- setChatLayout () {
- // This is a hacky way to adjust the global layout to the mobile chat (without modifying the rest of the app).
- // This layout prevents empty spaces from being visible at the bottom
- // of the chat on iOS Safari (`safe-area-inset`) when
- // - the on-screen keyboard appears and the user starts typing
- // - the user selects the text inside the input area
- // - the user selects and deletes the text that is multiple lines long
- // TODO: unify the chat layout with the global layout.
- let html = document.querySelector('html')
- if (html) {
- html.classList.add('chat-layout')
- }
-
- this.$nextTick(() => {
- this.updateScrollableContainerHeight()
- })
- },
- unsetChatLayout () {
- let html = document.querySelector('html')
- if (html) {
- html.classList.remove('chat-layout')
- }
- },
- handleLayoutChange () {
- this.$nextTick(() => {
- this.updateScrollableContainerHeight()
- this.scrollDown()
- })
- },
- // Ensures the proper position of the posting form in the mobile layout (the mobile browser panel does not overlap or hide it)
- updateScrollableContainerHeight () {
- const header = this.$refs.header
- const footer = this.$refs.footer
- const inner = this.mobileLayout ? window.document.body : this.$refs.inner
- this.scrollableContainerHeight = scrollableContainerHeight(inner, header, footer) + 'px'
- },
// Preserves the scroll position when OSK appears or the posting form changes its height.
handleResize (opts = {}) {
const { expand = false, delayed = false } = opts
@@ -190,29 +144,21 @@ const Chat = {
}
this.$nextTick(() => {
- this.updateScrollableContainerHeight()
-
const { offsetHeight = undefined } = this.lastScrollPosition
- this.lastScrollPosition = getScrollPosition(this.$refs.scrollable)
+ this.lastScrollPosition = getScrollPosition()
const diff = this.lastScrollPosition.offsetHeight - offsetHeight
if (diff < 0 || (!this.bottomedOut() && expand)) {
this.$nextTick(() => {
- this.updateScrollableContainerHeight()
- this.$refs.scrollable.scrollTo({
- top: this.$refs.scrollable.scrollTop - diff,
- left: 0
- })
+ window.scrollTo({ top: window.scrollY - diff })
})
}
})
},
scrollDown (options = {}) {
const { behavior = 'auto', forceRead = false } = options
- const scrollable = this.$refs.scrollable
- if (!scrollable) { return }
this.$nextTick(() => {
- scrollable.scrollTo({ top: scrollable.scrollHeight, left: 0, behavior })
+ window.scrollTo({ top: document.documentElement.scrollHeight, behavior })
})
if (forceRead) {
this.readChat()
@@ -228,11 +174,10 @@ const Chat = {
})
},
bottomedOut (offset) {
- return isBottomedOut(this.$refs.scrollable, offset)
+ return isBottomedOut(offset)
},
reachedTop () {
- const scrollable = this.$refs.scrollable
- return scrollable && scrollable.scrollTop <= 0
+ return window.scrollY <= 0
},
cullOlderCheck () {
window.setTimeout(() => {
@@ -263,10 +208,9 @@ const Chat = {
}
}, 200),
handleScrollUp (positionBeforeLoading) {
- const positionAfterLoading = getScrollPosition(this.$refs.scrollable)
- this.$refs.scrollable.scrollTo({
- top: getNewTopPosition(positionBeforeLoading, positionAfterLoading),
- left: 0
+ const positionAfterLoading = getScrollPosition()
+ window.scrollTo({
+ top: getNewTopPosition(positionBeforeLoading, positionAfterLoading)
})
},
fetchChat ({ isFirstFetch = false, fetchLatest = false, maxId }) {
@@ -285,22 +229,18 @@ const Chat = {
chatService.clear(chatMessageService)
}
- const positionBeforeUpdate = getScrollPosition(this.$refs.scrollable)
+ const positionBeforeUpdate = getScrollPosition()
this.$store.dispatch('addChatMessages', { chatId, messages }).then(() => {
this.$nextTick(() => {
if (fetchOlderMessages) {
this.handleScrollUp(positionBeforeUpdate)
}
- if (isFirstFetch) {
- this.updateScrollableContainerHeight()
- }
-
// In vertical screens, the first batch of fetched messages may not always take the
// full height of the scrollable container.
// If this is the case, we want to fetch the messages until the scrollable container
// is fully populated so that the user has the ability to scroll up and load the history.
- if (!isScrollable(this.$refs.scrollable) && messages.length > 0) {
+ if (!isScrollable() && messages.length > 0) {
this.fetchChat({ maxId: this.currentChatMessageService.minId })
}
})
@@ -336,9 +276,6 @@ const Chat = {
this.handleResize()
// When the posting form size changes because of a media attachment, we need an extra resize
// to account for the potential delay in the DOM update.
- setTimeout(() => {
- this.updateScrollableContainerHeight()
- }, SAFE_RESIZE_TIME_OFFSET)
this.scrollDown({ forceRead: true })
})
},
diff --git a/src/components/chat/chat.scss b/src/components/chat/chat.scss
index 3a26686c..63fd2a1f 100644
--- a/src/components/chat/chat.scss
+++ b/src/components/chat/chat.scss
@@ -1,42 +1,39 @@
.chat-view {
display: flex;
- height: calc(100vh - 60px);
- width: 100%;
-
- .chat-title {
- // prevents chat header jumping on when the user avatar loads
- height: 28px;
- }
+ height: 100%;
+ // Stick the form to the top/bottom of screen
+ margin-bottom: calc(var(--___columnMargin) / -2);
+ margin-top: calc(var(--___columnMargin) / -2);
.chat-view-inner {
height: auto;
width: 100%;
overflow: visible;
display: flex;
- margin: 0.5em 0.5em 0 0.5em;
}
.chat-view-body {
+ box-sizing: border-box;
background-color: var(--chatBg, $fallback--bg);
display: flex;
flex-direction: column;
width: 100%;
overflow: visible;
- min-height: 100%;
+ min-height: calc(100vh - var(--navbar-height));
margin: 0 0 0 0;
border-radius: 10px 10px 0 0;
border-radius: var(--panelRadius, 10px) var(--panelRadius, 10px) 0 0;
+ margin-bottom: calc(var(--___columnMargin) * -1);
+ padding-bottom: var(--___columnMargin);
&::after {
border-radius: 0;
}
}
- .scrollable-message-list {
+ .message-list {
padding: 0 0.8em;
height: 100%;
- overflow-y: scroll;
- overflow-x: hidden;
display: flex;
flex-direction: column;
}
@@ -44,24 +41,21 @@
.footer {
position: sticky;
bottom: 0;
+ background-color: $fallback--bg;
+ background-color: var(--bg, $fallback--bg);
+ z-index: 1;
}
.chat-view-heading {
- align-items: center;
- justify-content: space-between;
- top: 50px;
- display: flex;
- z-index: 2;
- position: sticky;
- overflow: hidden;
+ grid-template-columns: auto 1fr;
}
.go-back-button {
- cursor: pointer;
- width: 28px;
text-align: center;
- padding: 0.6em;
- margin: -0.6em 0.6em -0.6em -0.6em;
+ line-height: 100%;
+ height: 100%;
+ align-self: start;
+ width: var(--__panel-heading-height-inner);
}
.jump-to-bottom-button {
@@ -115,56 +109,4 @@
}
}
}
-
- @media all and (max-width: 800px) {
- height: 100%;
- overflow: hidden;
-
- .chat-view-inner {
- overflow: hidden;
- height: 100%;
- margin-top: 0;
- margin-left: 0;
- margin-right: 0;
- }
-
- .chat-view-body {
- display: flex;
- min-height: auto;
- overflow: hidden;
- height: 100%;
- margin: 0;
- border-radius: 0;
- }
-
- .chat-view-heading {
- box-sizing: border-box;
- position: static;
- z-index: 9999;
- top: 0;
- margin-top: 0;
- border-radius: 0;
-
- /* This practically overlays the panel heading color over panel background
- * color. This is needed because we allow transparent panel background and
- * it doesn't work well in this "disjointed panel header" case
- */
- background:
- linear-gradient(to top, var(--panel), var(--panel)),
- linear-gradient(to top, var(--bg), var(--bg));
- height: 50px;
- }
-
- .scrollable-message-list {
- display: unset;
- overflow-y: scroll;
- overflow-x: hidden;
- -webkit-overflow-scrolling: touch;
- }
-
- .footer {
- position: sticky;
- bottom: auto;
- }
- }
}
diff --git a/src/components/chat/chat.vue b/src/components/chat/chat.vue
index 493c5d5a..31eecbc0 100644
--- a/src/components/chat/chat.vue
+++ b/src/components/chat/chat.vue
@@ -2,23 +2,22 @@
<div class="chat-view">
<div class="chat-view-inner">
<div
- id="nav"
ref="inner"
class="panel-default panel chat-view-body"
>
<div
ref="header"
- class="panel-heading chat-view-heading mobile-hidden"
+ class="panel-heading -sticky chat-view-heading mobile-hidden"
>
- <a
- class="go-back-button"
+ <button
+ class="button-unstyled go-back-button"
@click="goBack"
>
<FAIcon
size="lg"
icon="chevron-left"
/>
- </a>
+ </button>
<div class="title text-center">
<ChatTitle
:user="recipient"
@@ -27,10 +26,8 @@
</div>
</div>
<div
- ref="scrollable"
- class="scrollable-message-list"
+ class="message-list"
:style="{ height: scrollableContainerHeight }"
- @scroll="handleScroll"
>
<template v-if="!errorLoadingChat">
<ChatMessage
diff --git a/src/components/chat/chat_layout_utils.js b/src/components/chat/chat_layout_utils.js
index 50a933ac..c187892d 100644
--- a/src/components/chat/chat_layout_utils.js
+++ b/src/components/chat/chat_layout_utils.js
@@ -1,9 +1,9 @@
// Captures a scroll position
-export const getScrollPosition = (el) => {
+export const getScrollPosition = () => {
return {
- scrollTop: el.scrollTop,
- scrollHeight: el.scrollHeight,
- offsetHeight: el.offsetHeight
+ scrollTop: window.scrollY,
+ scrollHeight: document.documentElement.scrollHeight,
+ offsetHeight: window.innerHeight
}
}
@@ -13,21 +13,12 @@ export const getNewTopPosition = (previousPosition, newPosition) => {
return previousPosition.scrollTop + (newPosition.scrollHeight - previousPosition.scrollHeight)
}
-export const isBottomedOut = (el, offset = 0) => {
- if (!el) { return }
- const scrollHeight = el.scrollTop + offset
- const totalHeight = el.scrollHeight - el.offsetHeight
+export const isBottomedOut = (offset = 0) => {
+ const scrollHeight = window.scrollY + offset
+ const totalHeight = document.documentElement.scrollHeight - window.innerHeight
return totalHeight <= scrollHeight
}
-
-// Height of the scrollable container. The dynamic height is needed to ensure the mobile browser panel doesn't overlap or hide the posting form.
-export const scrollableContainerHeight = (inner, header, footer) => {
- return inner.offsetHeight - header.clientHeight - footer.clientHeight
-}
-
// Returns whether or not the scrollbar is visible.
-export const isScrollable = (el) => {
- if (!el) return
-
- return el.scrollHeight > el.clientHeight
+export const isScrollable = () => {
+ return document.documentElement.scrollHeight > window.innerHeight
}
diff --git a/src/components/chat_list/chat_list.vue b/src/components/chat_list/chat_list.vue
index f98b7ed2..58e8d0b3 100644
--- a/src/components/chat_list/chat_list.vue
+++ b/src/components/chat_list/chat_list.vue
@@ -6,7 +6,7 @@
v-else
class="chat-list panel panel-default"
>
- <div class="panel-heading">
+ <div class="panel-heading -sticky">
<span class="title">
{{ $t("chats.chats") }}
</span>
diff --git a/src/components/chat_new/chat_new.vue b/src/components/chat_new/chat_new.vue
index f3894a3a..0790bb37 100644
--- a/src/components/chat_new/chat_new.vue
+++ b/src/components/chat_new/chat_new.vue
@@ -1,6 +1,5 @@
<template>
<div
- id="nav"
class="panel-default panel chat-new"
>
<div
diff --git a/src/components/chat_title/chat_title.vue b/src/components/chat_title/chat_title.vue
index f4706caf..7f6aaaa4 100644
--- a/src/components/chat_title/chat_title.vue
+++ b/src/components/chat_title/chat_title.vue
@@ -4,19 +4,19 @@
:title="title"
>
<router-link
+ class="avatar-container"
v-if="withAvatar && user"
:to="getUserProfileLink(user)"
>
<UserAvatar
+ class="titlebar-avatar"
:user="user"
- width="23px"
- height="23px"
/>
</router-link>
<RichContent
v-if="user"
class="username"
- :title="'@'+user.screen_name_ui"
+ :title="'@'+(user && user.screen_name_ui)"
:html="htmlTitle"
:emoji="user.emoji || []"
/>
@@ -33,7 +33,6 @@
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- align-items: center;
--emoji-size: 14px;
@@ -46,11 +45,15 @@
overflow: hidden;
}
- .Avatar {
- width: 23px;
- height: 23px;
- margin-right: 0.5em;
+ .avatar-container {
+ align-self: center;
+ line-height: 1;
+ }
+ .titlebar-avatar {
+ margin-right: 0.5em;
+ height: 1.5em;
+ width: 1.5em;
border-radius: $fallback--avatarAltRadius;
border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue
index ccc523a2..c2c323c1 100644
--- a/src/components/conversation/conversation.vue
+++ b/src/components/conversation/conversation.vue
@@ -7,7 +7,7 @@
>
<div
v-if="isExpanded"
- class="panel-heading conversation-heading"
+ class="panel-heading conversation-heading -sticky"
>
<span class="title"> {{ $t('timeline.conversation') }} </span>
<button
@@ -270,5 +270,9 @@
border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius);
border-bottom: 1px solid var(--border, $fallback--border);
}
+
+ &.-expanded.status-fadein {
+ margin: calc(var(--status-margin, $status-margin) / 2);
+ }
}
</style>
diff --git a/src/components/desktop_nav/desktop_nav.scss b/src/components/desktop_nav/desktop_nav.scss
index 2d468588..0ca9802e 100644
--- a/src/components/desktop_nav/desktop_nav.scss
+++ b/src/components/desktop_nav/desktop_nav.scss
@@ -1,9 +1,7 @@
@import '../../_variables.scss';
.DesktopNav {
- height: 50px;
width: 100%;
- position: fixed;
a {
color: var(--topBarLink, $fallback--link);
@@ -11,7 +9,7 @@
.inner-nav {
display: grid;
- grid-template-rows: 50px;
+ grid-template-rows: var(--navbar-height);
grid-template-columns: 2fr auto 2fr;
grid-template-areas: "sitename logo actions";
box-sizing: border-box;
@@ -20,7 +18,7 @@
max-width: 980px;
}
- &.-logoLeft {
+ &.-logoLeft .inner-nav {
grid-template-columns: auto 2fr 2fr;
grid-template-areas: "logo sitename actions";
}
@@ -77,7 +75,7 @@
img {
display: inline-block;
- height: 50px;
+ height: var(--navbar-height);
}
}
@@ -103,8 +101,8 @@
.item {
flex: 1;
- line-height: 50px;
- height: 50px;
+ line-height: var(--navbar-height);
+ height: var(--navbar-height);
overflow: hidden;
display: flex;
flex-wrap: wrap;
diff --git a/src/components/emoji_picker/emoji_picker.scss b/src/components/emoji_picker/emoji_picker.scss
index ec711758..1a696584 100644
--- a/src/components/emoji_picker/emoji_picker.scss
+++ b/src/components/emoji_picker/emoji_picker.scss
@@ -7,7 +7,7 @@
right: 0;
left: 0;
margin: 0 !important;
- z-index: 1;
+ z-index: 100;
background-color: $fallback--bg;
background-color: var(--popover, $fallback--bg);
color: $fallback--link;
diff --git a/src/components/media_modal/media_modal.vue b/src/components/media_modal/media_modal.vue
index 708a43c6..8b76aafb 100644
--- 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: 1001;
+ z-index: 9000;
flex-direction: column;
.modal-view-button-arrow,
@@ -234,7 +234,7 @@ $modal-view-button-icon-margin: 0.5em;
position: absolute;
height: $modal-view-button-icon-height;
width: $modal-view-button-icon-width;
- font-size: 14px;
+ font-size: 1rem;
line-height: $modal-view-button-icon-height;
color: #FFF;
text-align: center;
diff --git a/src/components/media_upload/media_upload.vue b/src/components/media_upload/media_upload.vue
index e955aa72..2920387e 100644
--- a/src/components/media_upload/media_upload.vue
+++ b/src/components/media_upload/media_upload.vue
@@ -19,7 +19,7 @@
v-if="uploadReady"
:disabled="disabled"
type="file"
- style="position: fixed; top: -100em"
+ style="position: fixed; top: -100em; max-width: 0; max-height: 0"
multiple="true"
@change="change"
>
diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js
index 9e736cfb..877d52a9 100644
--- a/src/components/mobile_nav/mobile_nav.js
+++ b/src/components/mobile_nav/mobile_nav.js
@@ -78,7 +78,8 @@ const MobileNav = {
this.$store.dispatch('logout')
},
markNotificationsAsSeen () {
- this.$refs.notifications.markAsSeen()
+ // this.$refs.notifications.markAsSeen()
+ this.$store.dispatch('markNotificationsAsSeen')
},
onScroll ({ target: { scrollTop, clientHeight, scrollHeight } }) {
if (scrollTop + clientHeight >= scrollHeight) {
diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue
index f5279b3e..362441bb 100644
--- a/src/components/mobile_nav/mobile_nav.vue
+++ b/src/components/mobile_nav/mobile_nav.vue
@@ -51,7 +51,7 @@
<div
v-if="currentUser"
class="mobile-notifications-drawer"
- :class="{ 'closed': !notificationsOpen }"
+ :class="{ '-closed': !notificationsOpen }"
@touchstart.stop="notificationsTouchStart"
@touchmove.stop="notificationsTouchMove"
>
@@ -69,12 +69,9 @@
</div>
<div
class="mobile-notifications"
+ id="mobile-notifications"
@scroll="onScroll"
>
- <Notifications
- ref="notifications"
- :no-heading="true"
- />
</div>
</div>
<SideDrawer
@@ -92,12 +89,10 @@
.MobileNav {
.mobile-nav {
display: grid;
- line-height: 50px;
- height: 50px;
+ line-height: var(--navbar-height);
grid-template-rows: 50px;
grid-template-columns: 2fr auto;
width: 100%;
- position: fixed;
box-sizing: border-box;
a {
color: var(--topBarLink, $fallback--link);
@@ -156,8 +151,9 @@
z-index: 1001;
-webkit-overflow-scrolling: touch;
- &.closed {
+ &.-closed {
transform: translateX(100%);
+ box-shadow: none;
}
}
@@ -185,7 +181,7 @@
.mobile-notifications {
margin-top: 50px;
width: 100vw;
- height: calc(100vh - 50px);
+ height: calc(100vh - var(--navbar-height));
overflow-x: hidden;
overflow-y: scroll;
diff --git a/src/components/mobile_post_status_button/mobile_post_status_button.js b/src/components/mobile_post_status_button/mobile_post_status_button.js
index 4866ac57..ecf79b64 100644
--- a/src/components/mobile_post_status_button/mobile_post_status_button.js
+++ b/src/components/mobile_post_status_button/mobile_post_status_button.js
@@ -45,7 +45,7 @@ const MobilePostStatusButton = {
return this.autohideFloatingPostButton && (this.hidden || this.inputActive)
},
isPersistent () {
- return !!this.$store.getters.mergedConfig.showNewPostButton
+ return !!this.$store.getters.mergedConfig.alwaysShowNewPostButton
},
autohideFloatingPostButton () {
return !!this.$store.getters.mergedConfig.autohideFloatingPostButton
diff --git a/src/components/modal/modal.vue b/src/components/modal/modal.vue
index 2b58913f..9394efff 100644
--- a/src/components/modal/modal.vue
+++ b/src/components/modal/modal.vue
@@ -35,7 +35,7 @@ export default {
<style lang="scss">
.modal-view {
- z-index: 1000;
+ z-index: 2000;
position: fixed;
top: 0;
left: 0;
diff --git a/src/components/notifications/notification_filters.vue b/src/components/notifications/notification_filters.vue
index ba0e90a0..d0b11a13 100644
--- a/src/components/notifications/notification_filters.vue
+++ b/src/components/notifications/notification_filters.vue
@@ -64,7 +64,7 @@
</div>
</template>
<template v-slot:trigger>
- <button class="button-unstyled">
+ <button class="filter-trigger-button button-unstyled">
<FAIcon icon="filter" />
</button>
</template>
@@ -107,15 +107,14 @@ export default {
align-self: stretch;
> button {
- font-size: 1.2em;
- padding-left: 0.7em;
- padding-right: 0.2em;
line-height: 100%;
height: 100%;
- }
+ width: var(--__panel-heading-height-inner);
+ text-align: center;
- .dropdown-item {
- margin: 0;
+ svg {
+ font-size: 1.2em;
+ }
}
}
diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js
index c8f1ebcb..fb2579a5 100644
--- a/src/components/notifications/notifications.js
+++ b/src/components/notifications/notifications.js
@@ -23,8 +23,6 @@ const Notifications = {
NotificationFilters
},
props: {
- // Disables display of panel header
- noHeading: Boolean,
// Disables panel styles, unread mark, potentially other notification-related actions
// meant for "Interactions" timeline
minimalMode: Boolean,
@@ -65,6 +63,18 @@ const Notifications = {
loading () {
return this.$store.state.statuses.notifications.loading
},
+ noHeading () {
+ const { layoutType } = this.$store.state.interface
+ return this.minimalMode || layoutType === 'mobile'
+ },
+ teleportTarget () {
+ const { layoutType } = this.$store.state.interface
+ const map = {
+ wide: '#notifs-column',
+ mobile: '#mobile-notifications'
+ }
+ return map[layoutType] || '#notifs-sidebar'
+ },
notificationsToDisplay () {
return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount)
},
diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue
index 2ce5d56f..bfc97dc0 100644
--- a/src/components/notifications/notifications.vue
+++ b/src/components/notifications/notifications.vue
@@ -1,69 +1,71 @@
<template>
- <div
- :class="{ minimal: minimalMode }"
- class="Notifications"
- >
- <div :class="mainClass">
- <div
- v-if="!noHeading"
- class="panel-heading"
- >
- <div class="title">
- {{ $t('notifications.notifications') }}
- <span
- v-if="unseenCount"
- class="badge badge-notification unseen-count"
- >{{ unseenCount }}</span>
- </div>
- <button
- v-if="unseenCount"
- class="button-default read-button"
- @click.prevent="markAsSeen"
- >
- {{ $t('notifications.read') }}
- </button>
- <NotificationFilters />
- </div>
- <div class="panel-body">
+ <teleport :disabled="minimalMode" :to="teleportTarget">
+ <div
+ :class="{ minimal: minimalMode }"
+ class="Notifications"
+ >
+ <div :class="mainClass">
<div
- v-for="notification in notificationsToDisplay"
- :key="notification.id"
- class="notification"
- :class="{&quot;unseen&quot;: !minimalMode && !notification.seen}"
+ v-if="!noHeading"
+ class="notifications-heading panel-heading -sticky"
>
- <div class="notification-overlay" />
- <notification :notification="notification" />
+ <div class="title">
+ {{ $t('notifications.notifications') }}
+ <span
+ v-if="unseenCount"
+ class="badge badge-notification unseen-count"
+ >{{ unseenCount }}</span>
+ </div>
+ <button
+ v-if="unseenCount"
+ class="button-default read-button"
+ @click.prevent="markAsSeen"
+ >
+ {{ $t('notifications.read') }}
+ </button>
+ <NotificationFilters />
</div>
- </div>
- <div class="panel-footer notifications-footer">
- <div
- v-if="bottomedOut"
- class="new-status-notification text-center faint"
- >
- {{ $t('notifications.no_more_notifications') }}
+ <div class="panel-body">
+ <div
+ v-for="notification in notificationsToDisplay"
+ :key="notification.id"
+ class="notification"
+ :class="{&quot;unseen&quot;: !minimalMode && !notification.seen}"
+ >
+ <div class="notification-overlay" />
+ <notification :notification="notification" />
+ </div>
</div>
- <button
- v-else-if="!loading"
- class="button-unstyled -link -fullwidth"
- @click.prevent="fetchOlderNotifications()"
- >
- <div class="new-status-notification text-center">
- {{ minimalMode ? $t('interactions.load_older') : $t('notifications.load_older') }}
+ <div class="panel-footer notifications-footer">
+ <div
+ v-if="bottomedOut"
+ class="new-status-notification text-center faint"
+ >
+ {{ $t('notifications.no_more_notifications') }}
+ </div>
+ <button
+ v-else-if="!loading"
+ class="button-unstyled -link -fullwidth"
+ @click.prevent="fetchOlderNotifications()"
+ >
+ <div class="new-status-notification text-center">
+ {{ minimalMode ? $t('interactions.load_older') : $t('notifications.load_older') }}
+ </div>
+ </button>
+ <div
+ v-else
+ class="new-status-notification text-center"
+ >
+ <FAIcon
+ icon="circle-notch"
+ spin
+ size="lg"
+ />
</div>
- </button>
- <div
- v-else
- class="new-status-notification text-center"
- >
- <FAIcon
- icon="circle-notch"
- spin
- size="lg"
- />
</div>
</div>
</div>
- </div>
+ </teleport>
</template>
<script src="./notifications.js"></script>
diff --git a/src/components/password_reset/password_reset.vue b/src/components/password_reset/password_reset.vue
index 3ffa5425..e0579568 100644
--- a/src/components/password_reset/password_reset.vue
+++ b/src/components/password_reset/password_reset.vue
@@ -91,6 +91,9 @@
flex-direction: column;
margin-top: 0.6em;
max-width: 18rem;
+ > * {
+ min-width: 0;
+ }
}
.form-group {
diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue
index 332a0398..d98ca8e7 100644
--- a/src/components/popover/popover.vue
+++ b/src/components/popover/popover.vue
@@ -5,7 +5,7 @@
>
<button
ref="trigger"
- class="button-unstyled -fullwidth popover-trigger-button"
+ class="button-unstyled popover-trigger-button"
type="button"
@click="onClick"
>
@@ -37,7 +37,7 @@
}
.popover {
- z-index: 8;
+ z-index: 500;
position: absolute;
min-width: 0;
}
@@ -45,8 +45,19 @@
.popover-default {
transition: opacity 0.3s;
- box-shadow: 1px 1px 4px rgba(0,0,0,.6);
- box-shadow: var(--panelShadow);
+ &:after {
+ content: '';
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ z-index: 3;
+ box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6);
+ box-shadow: var(--panelShadow);
+ pointer-events: none;
+ }
+
border-radius: $fallback--btnRadius;
border-radius: var(--btnRadius, $fallback--btnRadius);
@@ -65,11 +76,11 @@
.dropdown-menu {
display: block;
padding: .5rem 0;
- font-size: 1rem;
+ font-size: 1em;
text-align: left;
list-style: none;
max-width: 100vw;
- z-index: 10;
+ z-index: 200;
white-space: nowrap;
.dropdown-divider {
@@ -82,9 +93,9 @@
.dropdown-item {
line-height: 21px;
- overflow: auto;
+ overflow: hidden;
display: block;
- padding: .5em 0.75em;
+ padding: 0.25em 0.75em;
clear: both;
font-weight: 400;
text-align: inherit;
@@ -110,14 +121,15 @@
&:active, &:hover {
background-color: $fallback--lightBg;
background-color: var(--selectedMenuPopover, $fallback--lightBg);
- color: $fallback--link;
- color: var(--selectedMenuPopoverText, $fallback--link);
+ box-shadow: none;
+ --btnText: var(--selectedMenuPopoverText, $fallback--link);
--faint: var(--selectedMenuPopoverFaintText, $fallback--faint);
--faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint);
--lightText: var(--selectedMenuPopoverLightText, $fallback--lightText);
--icon: var(--selectedMenuPopoverIcon, $fallback--icon);
svg {
color: var(--selectedMenuPopoverIcon, $fallback--icon);
+ --icon: var(--selectedMenuPopoverIcon, $fallback--icon);
}
}
@@ -142,9 +154,13 @@
content: '✓';
}
- &.menu-checkbox-radio::after {
- font-size: 2em;
- content: '•';
+ &.-radio {
+ border-radius: 9999px;
+
+ &.menu-checkbox-checked::after {
+ font-size: 2em;
+ content: '•';
+ }
}
}
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index 84a9e29e..2febf226 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -486,7 +486,7 @@ const PostStatusForm = {
const bottomBottomPaddingStr = window.getComputedStyle(bottomRef)['padding-bottom']
const bottomBottomPadding = pxStringToNumber(bottomBottomPaddingStr)
- const scrollerRef = this.$el.closest('.sidebar-scroller') ||
+ const scrollerRef = this.$el.closest('.column.-scrollable') ||
this.$el.closest('.post-form-modal-view') ||
window
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 0fdb6dc7..9f2e8846 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -8,15 +8,6 @@
@submit.prevent
@dragover.prevent="fileDrag"
>
- <div
- v-show="showDropIcon !== 'hide'"
- :style="{ animation: showDropIcon === 'show' ? 'fade-in 0.25s' : 'fade-out 0.5s' }"
- class="drop-indicator"
- @dragleave="fileDragStop"
- @drop.stop="fileDrop"
- >
- <FAIcon :icon="uploadFileLimitReached ? 'ban' : 'upload'" />
- </div>
<div class="form-group">
<i18n-t
v-if="!$store.state.users.currentUser.locked && newStatus.visibility == 'private' && !disableLockWarning"
@@ -278,6 +269,15 @@
</button>
</div>
<div
+ v-show="showDropIcon !== 'hide'"
+ :style="{ animation: showDropIcon === 'show' ? 'fade-in 0.25s' : 'fade-out 0.5s' }"
+ class="drop-indicator"
+ @dragleave="fileDragStop"
+ @drop.stop="fileDrop"
+ >
+ <FAIcon :icon="uploadFileLimitReached ? 'ban' : 'upload'" />
+ </div>
+ <div
v-if="error"
class="alert error"
>
@@ -571,7 +571,6 @@
.drop-indicator {
position: absolute;
- z-index: 1;
width: 100%;
height: 100%;
font-size: 5em;
diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue
index 3d409109..1e735c69 100644
--- a/src/components/registration/registration.vue
+++ b/src/components/registration/registration.vue
@@ -271,7 +271,10 @@ $validations-cRed: #f04124;
.container {
display: flex;
flex-direction: row;
- //margin-bottom: 1em;
+
+ > * {
+ min-width: 0;
+ }
}
.terms-of-service {
diff --git a/src/components/settings_modal/settings_modal.scss b/src/components/settings_modal/settings_modal.scss
index 2f7649a9..f3738a8c 100644
--- a/src/components/settings_modal/settings_modal.scss
+++ b/src/components/settings_modal/settings_modal.scss
@@ -2,6 +2,18 @@
.settings-modal {
overflow: hidden;
+ .setting-list,
+ .option-list {
+ list-style-type: none;
+ padding-left: 2em;
+ li {
+ margin-bottom: 0.5em;
+ }
+ .suboptions {
+ margin-top: 0.3em
+ }
+ }
+
&.peek {
.settings-modal-panel {
/* Explanation:
diff --git a/src/components/settings_modal/settings_modal.vue b/src/components/settings_modal/settings_modal.vue
index e0e8304d..d3bed061 100644
--- a/src/components/settings_modal/settings_modal.vue
+++ b/src/components/settings_modal/settings_modal.vue
@@ -11,22 +11,13 @@
{{ $t('settings.settings') }}
</span>
<transition name="fade">
- <div v-if="currentSaveStateNotice">
- <div
- v-if="currentSaveStateNotice.error"
- class="alert error"
- @click.prevent
- >
- {{ $t('settings.saving_err') }}
- </div>
-
- <div
- v-if="!currentSaveStateNotice.error"
- class="alert transparent"
- @click.prevent
- >
- {{ $t('settings.saving_ok') }}
- </div>
+ <div
+ v-if="currentSaveStateNotice"
+ class="alert"
+ :class="{ transparent: !currentSaveStateNotice.error, error: currentSaveStateNotice.error}"
+ @click.prevent
+ >
+ {{ currentSaveStateNotice.error ? $t('settings.saving_err') : $t('settings.saving_ok') }}
</div>
</transition>
<button
diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index 62d86176..c97b3410 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -38,6 +38,11 @@ const GeneralTab = {
value: mode,
label: this.$t(`settings.mention_link_display_${mode}`)
})),
+ thirdColumnModeOptions: ['none', 'notifications', 'postform'].map(mode => ({
+ key: mode,
+ value: mode,
+ label: this.$t(`settings.third_column_mode_${mode}`)
+ })),
loopSilentAvailable:
// Firefox
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index a2c6bffa..db8718cf 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -61,6 +61,25 @@
</BooleanSetting>
</li>
<li>
+ <BooleanSetting path="disableStickyHeaders">
+ {{ $t('settings.disable_sticky_headers') }}
+ </BooleanSetting>
+ </li>
+ <li>
+ <BooleanSetting path="showScrollbars">
+ {{ $t('settings.show_scrollbars') }}
+ </BooleanSetting>
+ </li>
+ <li>
+ <ChoiceSetting
+ id="thirdColumnMode"
+ path="thirdColumnMode"
+ :options="thirdColumnModeOptions"
+ >
+ {{ $t('settings.third_column_mode') }}
+ </ChoiceSetting>
+ </li>
+ <li>
<BooleanSetting
path="alwaysShowNewPostButton"
expert="1"
diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.scss b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss
index 21b49a32..cb8e477c 100644
--- a/src/components/settings_modal/tabs/theme_tab/theme_tab.scss
+++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss
@@ -245,25 +245,8 @@
border-color: var(--border, $fallback--border);
}
- .panel-heading {
- .badge, .alert, .btn, .faint {
- margin-left: 1em;
- white-space: nowrap;
- }
- .faint {
- text-overflow: ellipsis;
- min-width: 2em;
- overflow-x: hidden;
- }
- .flex-spacer {
- flex: 1;
- }
- }
.btn {
- margin-left: 0;
- padding: 0 1em;
min-width: 3em;
- min-height: 30px;
}
}
}
diff --git a/src/components/status/status.scss b/src/components/status/status.scss
index 5ed43e0d..e38e2a53 100644
--- a/src/components/status/status.scss
+++ b/src/components/status/status.scss
@@ -42,6 +42,10 @@
display: flex;
padding: var(--status-margin, $status-margin);
+ > * {
+ min-width: 0;
+ }
+
&.-repeat {
padding-top: 0;
}
diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
index 8ec5d1e5..f79b5167 100644
--- a/src/components/timeline/timeline.js
+++ b/src/components/timeline/timeline.js
@@ -60,11 +60,11 @@ const Timeline = {
}
},
classes () {
- let rootClasses = !this.embedded ? ['panel', 'panel-default'] : []
+ let rootClasses = !this.embedded ? ['panel', 'panel-default'] : ['-nonpanel']
if (this.blockingClicks) rootClasses = rootClasses.concat(['-blocked', '_misclick-prevention'])
return {
root: rootClasses,
- header: ['timeline-heading'].concat(!this.embedded ? ['panel-heading'] : []),
+ header: ['timeline-heading'].concat(!this.embedded ? ['panel-heading', '-sticky'] : []),
body: ['timeline-body'].concat(!this.embedded ? ['panel-body'] : []),
footer: ['timeline-footer'].concat(!this.embedded ? ['panel-footer'] : [])
}
@@ -141,6 +141,7 @@ const Timeline = {
this.$store.commit('showNewStatuses', { timeline: this.timelineName })
this.paused = false
}
+ window.scrollTo({ top: 0 })
},
fetchOlderStatuses: throttle(function () {
const store = this.$store
diff --git a/src/components/timeline/timeline.scss b/src/components/timeline/timeline.scss
index 2c5a67e2..4c58e686 100644
--- a/src/components/timeline/timeline.scss
+++ b/src/components/timeline/timeline.scss
@@ -13,19 +13,26 @@
max-width: 100%;
flex-wrap: nowrap;
align-items: center;
- position: relative;
-
- .loadmore-button {
- flex-shrink: 0;
- }
-
- .loadmore-text {
- flex-shrink: 0;
- line-height: 1em;
- }
}
.timeline-footer {
border: none;
}
+
+ .conversation-heading {
+ top: calc(var(--panel-heading-height) * 2);
+ }
+
+ &.-nonpanel {
+ .timeline-heading {
+ text-align: center;
+ line-height: 2.75em;
+ padding: 0 0.5em;
+ }
+
+ .button-default, .alert {
+ line-height: 2em;
+ width: 100%;
+ }
+ }
}
diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue
index d37a9e2a..958808f6 100644
--- a/src/components/timeline/timeline.vue
+++ b/src/components/timeline/timeline.vue
@@ -1,5 +1,5 @@
<template>
- <div :class="[classes.root, 'Timeline']">
+ <div :class="['Timeline', classes.root]">
<div :class="classes.header">
<TimelineMenu v-if="!embedded" />
<button
@@ -60,7 +60,7 @@
</div>
<button
v-else-if="!timeline.loading"
- class="button-unstyled -link -fullwidth"
+ class="button-unstyled -link"
@click.prevent="fetchOlderStatuses()"
>
<div class="new-status-notification text-center">
diff --git a/src/components/timeline/timeline_quick_settings.vue b/src/components/timeline/timeline_quick_settings.vue
index 4d67e06b..5efc5d7c 100644
--- a/src/components/timeline/timeline_quick_settings.vue
+++ b/src/components/timeline/timeline_quick_settings.vue
@@ -12,8 +12,8 @@
@click="replyVisibilityAll = true"
>
<span
- class="menu-checkbox"
- :class="{ 'menu-checkbox-radio': replyVisibilityAll }"
+ class="menu-checkbox -radio"
+ :class="{ 'menu-checkbox-checked': replyVisibilityAll }"
/>{{ $t('settings.reply_visibility_all') }}
</button>
<button
@@ -21,8 +21,8 @@
@click="replyVisibilityFollowing = true"
>
<span
- class="menu-checkbox"
- :class="{ 'menu-checkbox-radio': replyVisibilityFollowing }"
+ class="menu-checkbox -radio"
+ :class="{ 'menu-checkbox-checked': replyVisibilityFollowing }"
/>{{ $t('settings.reply_visibility_following_short') }}
</button>
<button
@@ -30,8 +30,8 @@
@click="replyVisibilitySelf = true"
>
<span
- class="menu-checkbox"
- :class="{ 'menu-checkbox-radio': replyVisibilitySelf }"
+ class="menu-checkbox -radio"
+ :class="{ 'menu-checkbox-checked': replyVisibilitySelf }"
/>{{ $t('settings.reply_visibility_self_short') }}
</button>
<div
@@ -96,15 +96,14 @@
align-self: stretch;
> button {
- font-size: 1.2em;
- padding-left: 0.7em;
- padding-right: 0.2em;
line-height: 100%;
height: 100%;
- }
+ width: var(--__panel-heading-height-inner);
+ text-align: center;
- .dropdown-item {
- margin: 0;
+ svg {
+ font-size: 1.2em;
+ }
}
}
diff --git a/src/components/user_card/user_card.scss b/src/components/user_card/user_card.scss
new file mode 100644
index 00000000..4dabafa2
--- /dev/null
+++ b/src/components/user_card/user_card.scss
@@ -0,0 +1,324 @@
+@import '../../_variables.scss';
+
+.user-card {
+ position: relative;
+ z-index: 1;
+
+ &:hover {
+ --_still-image-img-visibility: visible;
+ --_still-image-canvas-visibility: hidden;
+ --_still-image-label-visibility: hidden;
+ }
+
+ .panel-heading {
+ padding: .5em 0;
+ text-align: center;
+ box-shadow: none;
+ background: transparent;
+ flex-direction: column;
+ align-items: stretch;
+ // create new stacking context
+ position: relative;
+ }
+
+ .panel-body {
+ word-wrap: break-word;
+ border-bottom-right-radius: inherit;
+ border-bottom-left-radius: inherit;
+ // create new stacking context
+ position: relative;
+ }
+
+ .background-image {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ mask: linear-gradient(to top, white, transparent) bottom no-repeat,
+ linear-gradient(to top, white, white);
+ // Autoprefixer seem to ignore this one, and also syntax is different
+ -webkit-mask-composite: xor;
+ 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);
+ background-color: var(--profileBg);
+ z-index: -2;
+
+ &.hide-bio {
+ mask-size: 100% 40px;
+ }
+ }
+
+ &-bio {
+ text-align: center;
+ display: block;
+ line-height: 18px;
+ padding: 1em;
+ margin: 0;
+
+ a {
+ color: $fallback--link;
+ color: var(--postLink, $fallback--link);
+ }
+
+ img {
+ object-fit: contain;
+ vertical-align: middle;
+ max-width: 100%;
+ max-height: 400px;
+ }
+ }
+
+ // Modifiers
+
+ &-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);
+ }
+
+ &-rounded {
+ border-radius: $fallback--panelRadius;
+ border-radius: var(--panelRadius, $fallback--panelRadius);
+ }
+
+ &-bordered {
+ border-width: 1px;
+ border-style: solid;
+ border-color: $fallback--border;
+ border-color: var(--border, $fallback--border);
+ }
+}
+
+.user-info {
+ color: $fallback--lightText;
+ color: var(--lightText, $fallback--lightText);
+ padding: 0 26px;
+
+ .container {
+ min-width: 0;
+ padding: 16px 0 6px;
+ display: flex;
+ align-items: flex-start;
+ max-height: 56px;
+
+ > * {
+ min-width: 0;
+ }
+
+ .Avatar {
+ --_avatarShadowBox: var(--avatarShadow);
+ --_avatarShadowFilter: var(--avatarShadowFilter);
+ --_avatarShadowInset: var(--avatarShadowInset);
+
+ flex: 1 0 100%;
+ width: 56px;
+ height: 56px;
+ object-fit: cover;
+ }
+ }
+
+ &-avatar-link {
+ position: relative;
+ cursor: pointer;
+
+ &-overlay {
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.3);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-radius: $fallback--avatarRadius;
+ border-radius: var(--avatarRadius, $fallback--avatarRadius);
+ opacity: 0;
+ transition: opacity .2s ease;
+
+ svg {
+ color: #FFF;
+ }
+ }
+
+ &:hover &-overlay {
+ opacity: 1;
+ }
+ }
+
+ .external-link-button, .edit-profile-button {
+ cursor: pointer;
+ width: 2.5em;
+ text-align: center;
+ margin: -0.5em 0;
+ padding: 0.5em 0;
+
+ &:not(:hover) .icon {
+ color: $fallback--lightText;
+ color: var(--lightText, $fallback--lightText);
+ }
+ }
+
+ .user-summary {
+ display: block;
+ margin-left: 0.6em;
+ text-align: left;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ flex: 1 1 0;
+ // This is so that text doesn't get overlapped by avatar's shadow if it has
+ // big one
+ z-index: 1;
+ line-height: 2em;
+
+ --emoji-size: 1.7em;
+
+ .top-line,
+ .bottom-line {
+ display: flex;
+ }
+ }
+
+ .user-name {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ flex: 1 1 auto;
+ margin-right: 1em;
+ font-size: 15px;
+ }
+
+ .bottom-line {
+ font-weight: light;
+ font-size: 15px;
+ align-items: baseline;
+
+ .lock-icon {
+ margin-left: 0.5em;
+ }
+
+ .user-screen-name {
+ min-width: 1px;
+ flex: 0 1 auto;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ color: $fallback--lightText;
+ color: var(--lightText, $fallback--lightText);
+ }
+
+ .dailyAvg {
+ min-width: 1px;
+ flex: 0 0 auto;
+ margin-left: 1em;
+ font-size: 0.7em;
+ color: $fallback--text;
+ color: var(--text, $fallback--text);
+ }
+
+ .user-role {
+ flex: none;
+ color: $fallback--text;
+ color: var(--alertNeutralText, $fallback--text);
+ background-color: $fallback--fg;
+ background-color: var(--alertNeutral, $fallback--fg);
+ }
+ }
+
+ .user-meta {
+ margin-bottom: .15em;
+ display: flex;
+ align-items: baseline;
+ font-size: 14px;
+ line-height: 22px;
+ flex-wrap: wrap;
+
+ .following {
+ flex: 1 0 auto;
+ margin: 0;
+ margin-bottom: .25em;
+ text-align: left;
+ }
+
+ .highlighter {
+ flex: 0 1 auto;
+ display: flex;
+ flex-wrap: wrap;
+ margin-right: -.5em;
+ align-self: start;
+
+ .userHighlightCl {
+ padding: 2px 10px;
+ flex: 1 0 auto;
+ }
+
+ .userHighlightSel {
+ padding-top: 0;
+ padding-bottom: 0;
+ flex: 1 0 auto;
+ }
+
+ .userHighlightText {
+ width: 70px;
+ flex: 1 0 auto;
+ }
+
+ .userHighlightCl,
+ .userHighlightText,
+ .userHighlightSel {
+ vertical-align: top;
+ margin-right: .5em;
+ margin-bottom: .25em;
+ }
+ }
+ }
+ .user-interactions {
+ position: relative;
+ display: flex;
+ flex-flow: row wrap;
+ margin-right: -.75em;
+
+ > * {
+ margin: 0 .75em .6em 0;
+ white-space: nowrap;
+ min-width: 95px;
+ }
+
+ button {
+ margin: 0;
+ }
+ }
+}
+
+.sidebar .edit-profile-button {
+ display: none;
+}
+
+.user-counts {
+ display: flex;
+ line-height:16px;
+ padding: .5em 1.5em 0em 1.5em;
+ text-align: center;
+ justify-content: space-between;
+ color: $fallback--lightText;
+ color: var(--lightText, $fallback--lightText);
+ flex-wrap: wrap;
+}
+
+.user-count {
+ flex: 1 0 auto;
+ padding: .5em 0 .5em 0;
+ margin: 0 .5em;
+
+ h5 {
+ font-size:1em;
+ font-weight: bolder;
+ margin: 0 0 0.25em;
+ }
+ a {
+ text-decoration: none;
+ }
+}
diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue
index 14b4643a..67837845 100644
--- a/src/components/user_card/user_card.vue
+++ b/src/components/user_card/user_card.vue
@@ -8,7 +8,7 @@
:style="style"
class="background-image"
/>
- <div class="panel-heading">
+ <div class="panel-heading -flexible-height">
<div class="user-info">
<div class="container">
<a
@@ -284,320 +284,4 @@
<script src="./user_card.js"></script>
-<style lang="scss">
-@import '../../_variables.scss';
-
-.user-card {
- position: relative;
-
- &:hover {
- --_still-image-img-visibility: visible;
- --_still-image-canvas-visibility: hidden;
- --_still-image-label-visibility: hidden;
- }
-
- .panel-heading {
- padding: .5em 0;
- text-align: center;
- box-shadow: none;
- background: transparent;
- flex-direction: column;
- align-items: stretch;
- // create new stacking context
- position: relative;
- }
-
- .panel-body {
- word-wrap: break-word;
- border-bottom-right-radius: inherit;
- border-bottom-left-radius: inherit;
- // create new stacking context
- position: relative;
- }
-
- .background-image {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- mask: linear-gradient(to top, white, transparent) bottom no-repeat,
- linear-gradient(to top, white, white);
- // Autoprefixed seem to ignore this one, and also syntax is different
- -webkit-mask-composite: xor;
- 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);
- background-color: var(--profileBg);
-
- &.hide-bio {
- mask-size: 100% 40px;
- }
- }
-
- &-bio {
- text-align: center;
- display: block;
- line-height: 18px;
- padding: 1em;
- margin: 0;
-
- a {
- color: $fallback--link;
- color: var(--postLink, $fallback--link);
- }
-
- img {
- object-fit: contain;
- vertical-align: middle;
- max-width: 100%;
- max-height: 400px;
- }
- }
-
- // Modifiers
-
- &-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);
- }
-
- &-rounded {
- border-radius: $fallback--panelRadius;
- border-radius: var(--panelRadius, $fallback--panelRadius);
- }
-
- &-bordered {
- border-width: 1px;
- border-style: solid;
- border-color: $fallback--border;
- border-color: var(--border, $fallback--border);
- }
-}
-
-.user-info {
- color: $fallback--lightText;
- color: var(--lightText, $fallback--lightText);
- padding: 0 26px;
-
- .container {
- padding: 16px 0 6px;
- display: flex;
- align-items: flex-start;
- max-height: 56px;
-
- .Avatar {
- --_avatarShadowBox: var(--avatarShadow);
- --_avatarShadowFilter: var(--avatarShadowFilter);
- --_avatarShadowInset: var(--avatarShadowInset);
-
- flex: 1 0 100%;
- width: 56px;
- height: 56px;
- object-fit: cover;
- }
- }
-
- &-avatar-link {
- position: relative;
- cursor: pointer;
-
- &-overlay {
- position: absolute;
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- background-color: rgba(0, 0, 0, 0.3);
- display: flex;
- justify-content: center;
- align-items: center;
- border-radius: $fallback--avatarRadius;
- border-radius: var(--avatarRadius, $fallback--avatarRadius);
- opacity: 0;
- transition: opacity .2s ease;
-
- svg {
- color: #FFF;
- }
- }
-
- &:hover &-overlay {
- opacity: 1;
- }
- }
-
- .external-link-button, .edit-profile-button {
- cursor: pointer;
- width: 2.5em;
- text-align: center;
- margin: -0.5em 0;
- padding: 0.5em 0;
-
- &:not(:hover) .icon {
- color: $fallback--lightText;
- color: var(--lightText, $fallback--lightText);
- }
- }
-
- .user-summary {
- display: block;
- margin-left: 0.6em;
- text-align: left;
- text-overflow: ellipsis;
- white-space: nowrap;
- flex: 1 1 0;
- // This is so that text doesn't get overlapped by avatar's shadow if it has
- // big one
- z-index: 1;
-
- .top-line {
- display: flex;
- }
- }
-
- .user-name {
- text-overflow: ellipsis;
- overflow: hidden;
- flex: 1 1 auto;
- margin-right: 1em;
- font-size: 15px;
-
- --emoji-size: 14px;
- }
-
- .bottom-line {
- display: flex;
- font-weight: light;
- font-size: 15px;
-
- .lock-icon {
- margin-left: 0.5em;
- }
-
- .user-screen-name {
- min-width: 1px;
- flex: 0 1 auto;
- text-overflow: ellipsis;
- overflow: hidden;
- color: $fallback--lightText;
- color: var(--lightText, $fallback--lightText);
- }
-
- .dailyAvg {
- min-width: 1px;
- flex: 0 0 auto;
- margin-left: 1em;
- font-size: 0.7em;
- color: $fallback--text;
- color: var(--text, $fallback--text);
- }
-
- .user-role {
- flex: none;
- color: $fallback--text;
- color: var(--alertNeutralText, $fallback--text);
- background-color: $fallback--fg;
- background-color: var(--alertNeutral, $fallback--fg);
- }
- }
-
- .user-meta {
- margin-bottom: .15em;
- display: flex;
- align-items: baseline;
- font-size: 14px;
- line-height: 22px;
- flex-wrap: wrap;
-
- .following {
- flex: 1 0 auto;
- margin: 0;
- margin-bottom: .25em;
- text-align: left;
- }
-
- .highlighter {
- flex: 0 1 auto;
- display: flex;
- flex-wrap: wrap;
- margin-right: -.5em;
- align-self: start;
-
- .userHighlightCl {
- padding: 2px 10px;
- flex: 1 0 auto;
- }
-
- .userHighlightSel {
- padding-top: 0;
- padding-bottom: 0;
- flex: 1 0 auto;
- }
-
- .userHighlightText {
- width: 70px;
- flex: 1 0 auto;
- }
-
- .userHighlightCl,
- .userHighlightText,
- .userHighlightSel {
- vertical-align: top;
- margin-right: .5em;
- margin-bottom: .25em;
- }
- }
- }
- .user-interactions {
- position: relative;
- display: flex;
- flex-flow: row wrap;
- margin-right: -.75em;
-
- > * {
- margin: 0 .75em .6em 0;
- white-space: nowrap;
- min-width: 95px;
- }
-
- button {
- margin: 0;
- }
- }
-}
-
-.sidebar .edit-profile-button {
- display: none;
-}
-
-.user-counts {
- display: flex;
- line-height:16px;
- padding: .5em 1.5em 0em 1.5em;
- text-align: center;
- justify-content: space-between;
- color: $fallback--lightText;
- color: var(--lightText, $fallback--lightText);
- flex-wrap: wrap;
-}
-
-.user-count {
- flex: 1 0 auto;
- padding: .5em 0 .5em 0;
- margin: 0 .5em;
-
- h5 {
- font-size:1em;
- font-weight: bolder;
- margin: 0 0 0.25em;
- }
- a {
- text-decoration: none;
- }
-}
-</style>
+<style lang="scss" src="./user_card.scss" />
diff --git a/src/components/user_panel/user_panel.vue b/src/components/user_panel/user_panel.vue
index 50949b98..243de387 100644
--- a/src/components/user_panel/user_panel.vue
+++ b/src/components/user_panel/user_panel.vue
@@ -24,5 +24,6 @@
<style lang="scss">
.user-panel .signed-in {
overflow: visible;
+ z-index: 10;
}
</style>
diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue
index 726216ff..f1e3cb72 100644
--- a/src/components/user_profile/user_profile.vue
+++ b/src/components/user_profile/user_profile.vue
@@ -192,24 +192,6 @@
align-items: middle;
padding: 2em;
}
-
- .timeline-heading {
- display: flex;
- justify-content: center;
-
- .loadmore-button, .alert {
- flex: 1;
- }
-
- .loadmore-button {
- height: 28px;
- margin: 10px .6em;
- }
-
- .title, .loadmore-text {
- display: none
- }
- }
}
.user-profile-placeholder {
.panel-body {
diff --git a/src/components/user_reporting_modal/user_reporting_modal.vue b/src/components/user_reporting_modal/user_reporting_modal.vue
index cc456365..ba12a246 100644
--- a/src/components/user_reporting_modal/user_reporting_modal.vue
+++ b/src/components/user_reporting_modal/user_reporting_modal.vue
@@ -76,17 +76,6 @@
min-height: 20vh;
max-height: 80vh;
- .panel-heading {
- .title {
- text-align: center;
- // TODO: Consider making these as default of panel
- flex: 1;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- }
-
.panel-body {
display: flex;
flex-direction: column-reverse;