diff --git a/changelog.d/better-scroll-button.add b/changelog.d/better-scroll-button.add new file mode 100644 index 000000000..b206869d1 --- /dev/null +++ b/changelog.d/better-scroll-button.add @@ -0,0 +1 @@ +Add support for detachable scrollTop button diff --git a/changelog.d/fix-wrap.skip b/changelog.d/fix-wrap.skip new file mode 100644 index 000000000..e69de29bb diff --git a/package.json b/package.json index 1fd54f055..8d74faee4 100644 --- a/package.json +++ b/package.json @@ -72,19 +72,20 @@ "babel-plugin-lodash": "3.3.4", "chai": "5.2.0", "chalk": "5.4.1", - "chromedriver": "134.0.5", + "chromedriver": "135.0.0", "connect-history-api-fallback": "2.0.0", "cross-spawn": "7.0.6", "custom-event-polyfill": "1.0.7", - "eslint": "9.23.0", + "eslint": "9.24.0", + "vue-eslint-parser": "10.1.3", "eslint-config-standard": "17.1.0", "eslint-formatter-friendly": "7.0.0", "eslint-plugin-import": "2.31.0", "eslint-plugin-n": "17.17.0", "eslint-plugin-promise": "7.2.1", - "eslint-plugin-vue": "9.33.0", + "eslint-plugin-vue": "10.0.0", "eventsource-polyfill": "0.9.6", - "express": "4.21.2", + "express": "5.1.0", "function-bind": "1.1.2", "http-proxy-middleware": "3.0.3", "iso-639-1": "3.1.5", @@ -95,19 +96,19 @@ "postcss": "8.5.3", "postcss-html": "^1.5.0", "postcss-scss": "^4.0.6", - "sass": "1.86.1", + "sass": "1.86.3", "selenium-server": "3.141.59", "semver": "7.7.1", "serve-static": "2.2.0", "shelljs": "0.9.2", "sinon": "20.0.0", "sinon-chai": "4.0.0", - "stylelint": "16.17.0", + "stylelint": "16.18.0", "stylelint-config-html": "^1.1.0", - "stylelint-config-recommended": "^14.0.0", + "stylelint-config-recommended": "^16.0.0", "stylelint-config-recommended-scss": "^14.0.0", "stylelint-config-recommended-vue": "^1.6.0", - "stylelint-config-standard": "37.0.0", + "stylelint-config-standard": "38.0.0", "vite": "^6.1.0", "vite-plugin-eslint2": "^5.0.3", "vite-plugin-stylelint": "^6.0.0", diff --git a/src/App.js b/src/App.js index 1c1b3a8c2..6bb9ba04b 100644 --- a/src/App.js +++ b/src/App.js @@ -20,6 +20,8 @@ import { defineAsyncComponent } from 'vue' import { useShoutStore } from './stores/shout' import { useInterfaceStore } from './stores/interface' +import { throttle } from 'lodash' + export default { name: 'app', components: { @@ -58,16 +60,23 @@ export default { // Load the locale from the storage const val = this.$store.getters.mergedConfig.interfaceLanguage this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val }) - window.addEventListener('resize', this.updateMobileState) document.getElementById('modal').classList = ['-' + this.layoutType] + + // Create bound handlers + this.updateScrollState = throttle(this.scrollHandler, 200) + this.updateMobileState = throttle(this.resizeHandler, 200) }, mounted () { + window.addEventListener('resize', this.updateMobileState) + this.scrollParent.addEventListener('scroll', this.updateScrollState) + if (useInterfaceStore().themeApplied) { this.removeSplash() } }, unmounted () { window.removeEventListener('resize', this.updateMobileState) + this.scrollParent.removeEventListener('scroll', this.updateScrollState) }, computed: { themeApplied () { @@ -146,13 +155,23 @@ export default { }, noSticky () { return this.$store.getters.mergedConfig.disableStickyHeaders }, showScrollbars () { return this.$store.getters.mergedConfig.showScrollbars }, + scrollParent () { return window; /* this.$refs.appContentRef */ }, ...mapGetters(['mergedConfig']) }, methods: { - updateMobileState () { + resizeHandler () { useInterfaceStore().setLayoutWidth(windowWidth()) useInterfaceStore().setLayoutHeight(windowHeight()) }, + scrollHandler () { + const scrollPosition = this.scrollParent === window ? window.scrollY : this.scrollParent.scrollTop + + if (scrollPosition != 0) { + this.$refs.appContentRef.classList.add(['-scrolled']) + } else { + this.$refs.appContentRef.classList.remove(['-scrolled']) + } + }, removeSplash () { document.querySelector('#status').textContent = this.$t('splash.fun_' + Math.ceil(Math.random() * 4)) const splashscreenRoot = document.querySelector('#splash') diff --git a/src/App.vue b/src/App.vue index 34cc6b804..e5e088bc3 100644 --- a/src/App.vue +++ b/src/App.vue @@ -16,6 +16,7 @@
diff --git a/src/components/attachment/attachment.scss b/src/components/attachment/attachment.scss index 13afbe641..16346c97c 100644 --- a/src/components/attachment/attachment.scss +++ b/src/components/attachment/attachment.scss @@ -177,7 +177,8 @@ .text { flex: 2; margin: 8px; - word-break: break-all; + overflow-wrap: break-word; + text-wrap: pretty; h1 { font-size: 1rem; diff --git a/src/components/chat_list_item/chat_list_item.scss b/src/components/chat_list_item/chat_list_item.scss index 9711b41d6..dcef6380e 100644 --- a/src/components/chat_list_item/chat_list_item.scss +++ b/src/components/chat_list_item/chat_list_item.scss @@ -17,7 +17,6 @@ width: 100%; box-sizing: border-box; overflow: hidden; - word-wrap: break-word; } .heading { diff --git a/src/components/chat_title/chat_title.vue b/src/components/chat_title/chat_title.vue index 0213b86fb..72660cca0 100644 --- a/src/components/chat_title/chat_title.vue +++ b/src/components/chat_title/chat_title.vue @@ -39,7 +39,6 @@ text-overflow: ellipsis; white-space: nowrap; display: inline; - word-wrap: break-word; overflow: hidden; } diff --git a/src/components/component_preview/component_preview.vue b/src/components/component_preview/component_preview.vue index 6a77d6c5d..be9d25ac5 100644 --- a/src/components/component_preview/component_preview.vue +++ b/src/components/component_preview/component_preview.vue @@ -199,7 +199,7 @@ export default { inset: 0; display: grid; place-items: center center; - background-color: rgba(100 0 0 / 50%); + background-color: rgb(100 0 0 / 50%); .alert { padding: 0.5em 1em; @@ -261,14 +261,14 @@ export default { .preview-window { --__grid-color1: rgb(102 102 102); --__grid-color2: rgb(153 153 153); - --__grid-color1-disabled: rgba(102 102 102 / 20%); - --__grid-color2-disabled: rgba(153 153 153 / 20%); + --__grid-color1-disabled: rgb(102 102 102 / 20%); + --__grid-color2-disabled: rgb(153 153 153 / 20%); &.-light-grid { --__grid-color1: rgb(205 205 205); --__grid-color2: rgb(255 255 255); - --__grid-color1-disabled: rgba(205 205 205 / 20%); - --__grid-color2-disabled: rgba(255 255 255 / 20%); + --__grid-color1-disabled: rgb(205 205 205 / 20%); + --__grid-color2-disabled: rgb(255 255 255 / 20%); } position: relative; diff --git a/src/components/draft/draft.vue b/src/components/draft/draft.vue index 9f8d8c14e..433ebae31 100644 --- a/src/components/draft/draft.vue +++ b/src/components/draft/draft.vue @@ -127,7 +127,6 @@ max-width: 100%; p { - word-wrap: break-word; white-space: normal; overflow-x: hidden; } diff --git a/src/components/flash/flash.vue b/src/components/flash/flash.vue index 3196e9bc4..57de5b2ee 100644 --- a/src/components/flash/flash.vue +++ b/src/components/flash/flash.vue @@ -72,7 +72,6 @@ flex: 1 1 0; line-height: 1.2; white-space: normal; - word-wrap: normal; } .hidden { diff --git a/src/components/image_cropper/image_cropper.vue b/src/components/image_cropper/image_cropper.vue index 6d11ad4ec..0122d1216 100644 --- a/src/components/image_cropper/image_cropper.vue +++ b/src/components/image_cropper/image_cropper.vue @@ -2,21 +2,24 @@
diff --git a/src/components/link-preview/link-preview.vue b/src/components/link-preview/link-preview.vue index 5ef63a51e..18fc77df4 100644 --- a/src/components/link-preview/link-preview.vue +++ b/src/components/link-preview/link-preview.vue @@ -68,7 +68,8 @@ margin: 0.5em 0 0; overflow: hidden; text-overflow: ellipsis; - word-break: break-all; + overflow-wrap: break-word; + text-wrap: pretty; line-height: 1.2em; /* cap description at 3 lines, the 1px is to clean up some stray pixels diff --git a/src/components/login_form/login_form.vue b/src/components/login_form/login_form.vue index acf3e82bd..eb5b3432d 100644 --- a/src/components/login_form/login_form.vue +++ b/src/components/login_form/login_form.vue @@ -93,4 +93,4 @@ - diff --git a/src/components/status/status.scss b/src/components/status/status.scss index b6a7256a7..93253b1a7 100644 --- a/src/components/status/status.scss +++ b/src/components/status/status.scss @@ -1,8 +1,8 @@ .Status { min-width: 0; white-space: normal; - word-wrap: break-word; - word-break: break-all; + overflow-wrap: break-word; + text-wrap: pretty; &:hover { --_still-image-img-visibility: visible; @@ -92,7 +92,10 @@ a { display: inline-block; - word-break: break-all; + white-space: nowrap; + text-overflow: ellipsis; + overflow-x: hidden; + width: 100% } } @@ -283,8 +286,6 @@ & .status-username, & .mute-reason { - word-wrap: normal; - word-break: normal; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; diff --git a/src/components/status_body/status_body.scss b/src/components/status_body/status_body.scss index 56e1d7b9f..07fc8ef45 100644 --- a/src/components/status_body/status_body.scss +++ b/src/components/status_body/status_body.scss @@ -14,8 +14,7 @@ & .summary { white-space: pre-wrap; overflow-wrap: break-word; - word-wrap: break-word; - word-break: break-all; + text-wrap: pretty; line-height: var(--post-line-height); } @@ -54,19 +53,22 @@ } .text-wrapper { + text-overflow: ellipsis; + overflow-wrap: break-word; + overflow: hidden; display: flex; flex-flow: column nowrap; &.-tall-status { position: relative; - height: 220px; + height: 16em; overflow: hidden; z-index: 1; .media-body { min-height: 0; mask: - linear-gradient(to top, white, transparent) bottom/100% 70px no-repeat, + linear-gradient(to top, white, transparent) bottom/100% 8em no-repeat, linear-gradient(to top, white, white); /* Autoprefixed seem to ignore this one, and also syntax is different */ @@ -87,16 +89,17 @@ & .status-unhider, & .cw-status-hider { display: inline-block; - word-break: break-all; + overflow-wrap: break-word; + text-wrap: pretty; width: 100%; text-align: center; } .tall-status-hider { position: absolute; - height: 70px; - margin-top: 150px; - line-height: 110px; + height: 5em; + margin-top: 10em; + line-height: 8em; z-index: 2; } @@ -107,7 +110,8 @@ & .status-unhider, & .cw-status-hider { - word-break: break-all; + overflow-wrap: break-word; + text-wrap: pretty; svg { color: inherit; diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index 2bc811b58..61cc8affd 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -5,6 +5,7 @@ import Conversation from '../conversation/conversation.vue' import TimelineMenu from '../timeline_menu/timeline_menu.vue' import QuickFilterSettings from '../quick_filter_settings/quick_filter_settings.vue' import QuickViewSettings from '../quick_view_settings/quick_view_settings.vue' +import ScrollTopButton from '../scroll_top_button/scroll_top_button.vue' import { debounce, throttle, keyBy } from 'lodash' import { library } from '@fortawesome/fontawesome-svg-core' import { faCircleNotch, faCirclePlus, faCog, faMinus, faArrowUp, faCheck } from '@fortawesome/free-solid-svg-icons' @@ -47,6 +48,7 @@ const Timeline = { }, components: { Status, + ScrollTopButton, Conversation, TimelineMenu, QuickFilterSettings, @@ -144,9 +146,6 @@ const Timeline = { this.$store.commit('setLoading', { timeline: this.timelineName, value: false }) }, methods: { - scrollToTop () { - window.scrollTo({ top: this.$el.offsetTop }) - }, stopBlockingClicks: debounce(function () { this.blockingClicks = false }, 1000), @@ -251,7 +250,6 @@ const Timeline = { } }, handleScroll: throttle(function (e) { - this.showScrollTop = this.$el.offsetTop < window.scrollY this.determineVisibleStatuses() this.scrollLoad(e) }, 200), diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index e34c42d9f..81dad3bd3 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -8,25 +8,7 @@ v-if="!embedded" :timeline-name="timelineName" /> -
- -
+