Compare commits

..

14 commits
develop ... 7fw

165 changed files with 2291 additions and 2124 deletions

View file

@ -1,22 +1,22 @@
# Akkoma-FE # Pleroma-FE
![English OK](https://img.shields.io/badge/English-OK-blueviolet) ![日本語OK](https://img.shields.io/badge/%E6%97%A5%E6%9C%AC%E8%AA%9E-OK-blueviolet) ![English OK](https://img.shields.io/badge/English-OK-blueviolet) ![日本語OK](https://img.shields.io/badge/%E6%97%A5%E6%9C%AC%E8%AA%9E-OK-blueviolet)
This is a fork of Akkoma-FE from the Pleroma project, with support for new Akkoma features such as: This is a fork of Pleroma-FE from the Pleroma project, with support for new Akkoma features such as:
- MFM support via [marked-mfm](https://akkoma.dev/sfr/marked-mfm) - MFM support via [marked-mfm](https://akkoma.dev/sfr/marked-mfm)
- Custom emoji reactions - Custom emoji reactions
# For Translators # For Translators
The [Weblate UI](https://translate.akkoma.dev/projects/akkoma/pleroma-fe/) is recommended for adding or modifying translations for Akkoma-FE. The [Weblate UI](https://translate.akkoma.dev/projects/akkoma/pleroma-fe/) is recommended for adding or modifying translations for Pleroma-FE.
Alternatively, edit/create `src/i18n/$LANGUAGE_CODE.json` (where `$LANGUAGE_CODE` is the [ISO 639-1 code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) for your language), then add your language to [src/i18n/messages.js](https://akkoma.dev/AkkomaGang/pleroma-fe/src/branch/develop/src/i18n/messages.js) if it doesn't already exist there. Alternatively, edit/create `src/i18n/$LANGUAGE_CODE.json` (where `$LANGUAGE_CODE` is the [ISO 639-1 code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) for your language), then add your language to [src/i18n/messages.js](https://akkoma.dev/AkkomaGang/pleroma-fe/src/branch/develop/src/i18n/messages.js) if it doesn't already exist there.
Akkoma-FE will set your language by your browser locale, but you can temporarily force it in the code by changing the locale in main.js. Pleroma-FE will set your language by your browser locale, but you can temporarily force it in the code by changing the locale in main.js.
# FOR ADMINS # FOR ADMINS
To use Akkoma-FE in Akkoma, use the [frontend](https://docs.akkoma.dev/stable/administration/CLI_tasks/frontend/) CLI task to install Akkoma-FE, then modify your configuration as described in the [Frontend Management](https://docs.akkoma.dev/stable/configuration/frontend_management/) doc. To use Pleroma-FE in Akkoma, use the [frontend](https://docs.akkoma.dev/stable/administration/CLI_tasks/frontend/) CLI task to install Pleroma-FE, then modify your configuration as described in the [Frontend Management](https://docs.akkoma.dev/stable/configuration/frontend_management/) doc.
## Build Setup ## Build Setup
@ -52,4 +52,4 @@ Edit config.json for configuration.
### Login methods ### Login methods
```loginMethod``` can be set to either ```password``` (the default) or ```token```, which will use the full oauth redirection flow, which is useful for SSO situations. ```loginMethod``` can be set to either ```password``` (the default) or ```token```, which will use the full oauth redirection flow, which is useful for SSO situations.

View file

@ -10,7 +10,6 @@
<link rel="stylesheet" href="/static/font/css/lato.css"> <link rel="stylesheet" href="/static/font/css/lato.css">
<link rel="stylesheet" href="/static/mfm.css"> <link rel="stylesheet" href="/static/mfm.css">
<link rel="stylesheet" href="/static/custom.css"> <link rel="stylesheet" href="/static/custom.css">
<link rel="stylesheet" href="/static/theme-holder.css" id="theme-holder">
<!--server-generated-meta--> <!--server-generated-meta-->
<link rel="icon" type="image/png" href="/favicon.png"> <link rel="icon" type="image/png" href="/favicon.png">
<link rel="manifest" href="/manifest.json"> <link rel="manifest" href="/manifest.json">

View file

@ -18,21 +18,19 @@
"dependencies": { "dependencies": {
"@babel/runtime": "7.17.8", "@babel/runtime": "7.17.8",
"@chenfengyuan/vue-qrcode": "2.0.0", "@chenfengyuan/vue-qrcode": "2.0.0",
"@floatingghost/pinch-zoom-element": "^1.3.1",
"@fortawesome/fontawesome-svg-core": "1.3.0", "@fortawesome/fontawesome-svg-core": "1.3.0",
"@fortawesome/free-regular-svg-icons": "^6.1.2", "@fortawesome/free-regular-svg-icons": "^6.1.2",
"@fortawesome/free-solid-svg-icons": "^6.2.0", "@fortawesome/free-solid-svg-icons": "^6.2.0",
"@fortawesome/vue-fontawesome": "3.0.1", "@fortawesome/vue-fontawesome": "3.0.1",
"@kazvmoe-infra/pinch-zoom-element": "1.2.0",
"@vuelidate/core": "^2.0.0", "@vuelidate/core": "^2.0.0",
"@vuelidate/validators": "^2.0.0", "@vuelidate/validators": "^2.0.0",
"blurhash": "^2.0.4",
"body-scroll-lock": "2.7.1", "body-scroll-lock": "2.7.1",
"chromatism": "3.0.0", "chromatism": "3.0.0",
"click-outside-vue3": "4.0.1", "click-outside-vue3": "4.0.1",
"cropperjs": "1.5.12", "cropperjs": "1.5.12",
"diff": "3.5.0", "diff": "3.5.0",
"escape-html": "1.0.3", "escape-html": "1.0.3",
"iso-639-1": "^2.1.15",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"localforage": "1.10.0", "localforage": "1.10.0",
"parse-link-header": "^2.0.0", "parse-link-header": "^2.0.0",
@ -84,6 +82,7 @@
"html-webpack-plugin": "^5.5.0", "html-webpack-plugin": "^5.5.0",
"http-proxy-middleware": "0.21.0", "http-proxy-middleware": "0.21.0",
"inject-loader": "2.0.1", "inject-loader": "2.0.1",
"iso-639-1": "2.1.15",
"isparta-loader": "2.0.0", "isparta-loader": "2.0.0",
"json-loader": "0.5.7", "json-loader": "0.5.7",
"karma": "6.3.17", "karma": "6.3.17",

View file

@ -1,9 +1,9 @@
// stylelint-disable rscss/class-format // stylelint-disable rscss/class-format
@import './_variables.scss'; @import './_variables.scss';
@import '@fortawesome/fontawesome-svg-core/styles.css'; @import './_logical.scss';
@import '@floatingghost/pinch-zoom-element/dist/pinch-zoom.css';
:root { :root {
--navbar-height: 3.5rem; --navbar-block-size: 3.5rem;
--post-line-height: 1.4; --post-line-height: 1.4;
} }
@ -20,9 +20,9 @@ body {
color: var(--text, $fallback--text); color: var(--text, $fallback--text);
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
overscroll-behavior-y: none; @include overscroll-behavior-block(none);
overflow-x: clip; @include overflow-inline(clip);
overflow-y: scroll; @include overflow-block(scroll);
&.hidden { &.hidden {
display: none; display: none;
@ -127,7 +127,7 @@ nav {
box-shadow: 0 0 4px rgba(0, 0, 0, 0.6); box-shadow: 0 0 4px rgba(0, 0, 0, 0.6);
box-shadow: var(--topBarShadow); box-shadow: var(--topBarShadow);
box-sizing: border-box; box-sizing: border-box;
height: var(--navbar-height); block-size: var(--navbar-block-size);
position: fixed; position: fixed;
} }
@ -136,7 +136,7 @@ nav {
} }
.column.-scrollable { .column.-scrollable {
top: var(--navbar-height); inset-block-start: var(--navbar-block-size);
position: sticky; position: sticky;
} }
@ -151,11 +151,11 @@ nav {
.app-bg-wrapper { .app-bg-wrapper {
position: fixed; position: fixed;
height: 100%; block-size: 100%;
top: var(--navbar-height); inset-block-start: var(--navbar-block-size);
z-index: -1000; z-index: -1000;
left: 0; inset-inline-start: 0;
right: -20px; inset-inline-end: -20px;
background-size: cover; background-size: cover;
background-repeat: no-repeat; background-repeat: no-repeat;
background-color: var(--wallpaper); background-color: var(--wallpaper);
@ -186,12 +186,13 @@ nav {
grid-template-areas: "sidebar content"; grid-template-areas: "sidebar content";
grid-template-rows: 1fr; grid-template-rows: 1fr;
box-sizing: border-box; box-sizing: border-box;
margin: 0 auto; margin-block: 0;
margin-inline: auto;
align-content: flex-start; align-content: flex-start;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: center; justify-content: center;
min-height: 100vh; min-block-size: 100vb;
overflow-x: clip; @include overflow-inline(clip);
.column { .column {
--___columnMargin: var(--columnGap); --___columnMargin: var(--columnGap);
@ -201,13 +202,15 @@ nav {
box-sizing: border-box; box-sizing: border-box;
grid-row-start: 1; grid-row-start: 1;
grid-row-end: 1; grid-row-end: 1;
margin: 0 calc(var(--___columnMargin) / 2); margin-block: 0;
padding: calc(var(--___columnMargin)) 0; margin-inline: calc(var(--___columnMargin) / 2);
padding-block: calc(var(--___columnMargin));
padding-inline: 0;
row-gap: var(--___columnMargin); row-gap: var(--___columnMargin);
align-content: start; align-content: start;
&:not(.-scrollable) { &:not(.-scrollable) {
margin-top: var(--navbar-height); margin-block-start: var(--navbar-block-size);
} }
&:hover { &:hover {
@ -215,46 +218,46 @@ nav {
} }
&.-full-height { &.-full-height {
margin-bottom: 0; margin-block-end: 0;
padding-top: 0; padding-block-start: 0;
padding-bottom: 0; padding-block-end: 0;
} }
&.-scrollable { &.-scrollable {
--___paddingIncrease: calc(var(--columnGap) / 2); --___paddingIncrease: calc(var(--columnGap) / 2);
position: sticky; position: sticky;
top: var(--navbar-height); inset-block-start: var(--navbar-block-size);
max-height: calc(100vh - var(--navbar-height)); max-block-size: calc(100vb - var(--navbar-block-size));
overflow-y: auto; @include overflow-block(auto);
overflow-x: hidden; @include overflow-inline(hidden);
margin-left: calc(var(--___paddingIncrease) * -1); margin-inline-start: calc(var(--___paddingIncrease) * -1);
padding-left: calc(var(--___paddingIncrease) + var(--___columnMargin) / 2); padding-inline-start: calc(var(--___paddingIncrease) + var(--___columnMargin) / 2);
// On browsers that don't support hiding scrollbars we enforce "show scrolbars" mode // On browsers that don't support hiding scrollbars we enforce "show scrolbars" mode
// might implement old style of hiding scrollbars later if there's demand // might implement old style of hiding scrollbars later if there's demand
@supports (scrollbar-width: none) or (-webkit-text-fill-color: initial) { @supports (scrollbar-width: none) or (-webkit-text-fill-color: initial) {
&:not(.-show-scrollbar) { &:not(.-show-scrollbar) {
scrollbar-width: none; scrollbar-width: none;
margin-right: calc(var(--___paddingIncrease) * -1); margin-inline-end: calc(var(--___paddingIncrease) * -1);
padding-right: calc(var(--___paddingIncrease) + var(--___columnMargin) / 2); padding-inline-end: calc(var(--___paddingIncrease) + var(--___columnMargin) / 2);
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: block; display: block;
width: 0; inline-size: 0;
} }
} }
} }
.panel-heading.-sticky { .panel-heading.-sticky {
top: calc(var(--columnGap) / -1); inset-block-start: calc(var(--columnGap) / -1);
} }
} }
} }
&.-has-new-post-button { &.-has-new-post-button {
.column { .column {
padding-bottom: 10rem; padding-block-end: 10rem;
} }
} }
@ -262,7 +265,7 @@ nav {
.column { .column {
.panel-heading.-sticky { .panel-heading.-sticky {
position: relative; position: relative;
top: 0; inset-block-start: 0;
} }
} }
} }
@ -290,16 +293,16 @@ nav {
} }
&.-mobile { &.-mobile {
grid-template-columns: 100vw; grid-template-columns: 100vi;
grid-template-areas: "content"; grid-template-areas: "content";
padding: 0; padding: 0;
.column { .column {
margin-left: 0; margin-inline-start: 0;
margin-right: 0; margin-inline-end: 0;
padding-top: 0; padding-block-start: 0;
margin-top: var(--navbar-height); margin-block-start: var(--navbar-block-size);
margin-bottom: 0; margin-block-end: 0;
} }
.panel-heading, .panel-heading,
@ -307,8 +310,8 @@ nav {
.panel-heading::before, .panel-heading::before,
.panel, .panel,
.panel::after { .panel::after {
border-top-left-radius: 0; border-start-start-radius: 0;
border-top-right-radius: 0; border-start-end-radius: 0;
} }
#sidebar, #sidebar,
@ -436,7 +439,7 @@ nav {
} }
&.-fullwidth { &.-fullwidth {
width: 100%; inline-size: 100%;
} }
&.-hover-highlight { &.-hover-highlight {
@ -454,7 +457,7 @@ textarea,
border-radius: 0; border-radius: 0;
background: none; background: none;
box-shadow: none; box-shadow: none;
height: unset; block-size: unset;
} }
--_padding: 0.5em; --_padding: 0.5em;
@ -477,7 +480,8 @@ textarea,
position: relative; position: relative;
line-height: 2; line-height: 2;
hyphens: none; hyphens: none;
padding: 0 var(--_padding); padding-block: 0;
padding-inline: var(--_padding);
&:disabled, &:disabled,
&[disabled=disabled], &[disabled=disabled],
@ -516,12 +520,12 @@ textarea,
display: inline-block; display: inline-block;
content: ''; content: '';
transition: box-shadow 200ms; transition: box-shadow 200ms;
width: 1.1em; inline-size: 1.1em;
height: 1.1em; block-size: 1.1em;
border-radius: 100%; // Radio buttons should always be circle border-radius: 100%; // Radio buttons should always be circle
box-shadow: 0 0 2px black inset; box-shadow: 0 0 2px black inset;
box-shadow: var(--inputShadow); box-shadow: var(--inputShadow);
margin-right: 0.5em; margin-inline-end: 0.5em;
background-color: $fallback--fg; background-color: $fallback--fg;
background-color: var(--input, $fallback--fg); background-color: var(--input, $fallback--fg);
vertical-align: top; vertical-align: top;
@ -555,13 +559,13 @@ textarea,
display: inline-block; display: inline-block;
content: ''; content: '';
transition: color 200ms; transition: color 200ms;
width: 1.1em; inline-size: 1.1em;
height: 1.1em; block-size: 1.1em;
border-radius: $fallback--checkboxRadius; border-radius: $fallback--checkboxRadius;
border-radius: var(--checkboxRadius, $fallback--checkboxRadius); border-radius: var(--checkboxRadius, $fallback--checkboxRadius);
box-shadow: 0 0 2px black inset; box-shadow: 0 0 2px black inset;
box-shadow: var(--inputShadow); box-shadow: var(--inputShadow);
margin-right: 0.5em; margin-inline-end: 0.5em;
background-color: $fallback--fg; background-color: $fallback--fg;
background-color: var(--input, $fallback--fg); background-color: var(--input, $fallback--fg);
vertical-align: top; vertical-align: top;
@ -574,8 +578,8 @@ textarea,
} }
} }
&.resize-height { &.resize-block-size {
resize: vertical; @include resize(block);
} }
} }
@ -594,6 +598,7 @@ option {
.hide-number-spinner { .hide-number-spinner {
-moz-appearance: textfield; -moz-appearance: textfield;
appearance: textfield;
&[type=number]::-webkit-inner-spin-button, &[type=number]::-webkit-inner-spin-button,
&[type=number]::-webkit-outer-spin-button { &[type=number]::-webkit-outer-spin-button {
@ -604,7 +609,7 @@ option {
.btn-block { .btn-block {
display: block; display: block;
width: 100%; inline-size: 100%;
} }
.btn-group { .btn-group {
@ -617,13 +622,13 @@ option {
flex: 1 1 auto; flex: 1 1 auto;
&:not(:last-child) { &:not(:last-child) {
border-top-right-radius: 0; border-start-end-radius: 0;
border-bottom-right-radius: 0; border-end-end-radius: 0;
} }
&:not(:first-child) { &:not(:first-child) {
border-top-left-radius: 0; border-start-start-radius: 0;
border-bottom-left-radius: 0; border-end-start-radius: 0;
} }
} }
} }
@ -642,10 +647,11 @@ option {
box-sizing: border-box; box-sizing: border-box;
display: inline-block; display: inline-block;
border-radius: 99px; border-radius: 99px;
max-width: 10em; max-inline-size: 10em;
min-width: 1.7em; min-inline-size: 1.7em;
height: 1.3em; block-size: 1.3em;
padding: 0.15em 0.15em; padding-block: 0.15em;
padding-inline: 0.15em;
vertical-align: middle; vertical-align: middle;
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
@ -665,8 +671,10 @@ option {
} }
.alert { .alert {
margin: 0 0.35em; margin-block: 0;
padding: 0 0.25em; margin-inline: 0.35em;
padding-block: 0;
padding-inline: 0.25em;
border-radius: $fallback--tooltipRadius; border-radius: $fallback--tooltipRadius;
border-radius: var(--tooltipRadius, $fallback--tooltipRadius); border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
@ -727,13 +735,13 @@ option {
} }
.notice-dismissible { .notice-dismissible {
padding-right: 4rem; padding-inline-end: 4rem;
position: relative; position: relative;
.dismiss { .dismiss {
position: absolute; position: absolute;
top: 0; inset-block-start: 0;
right: 0; inset-inline-end: 0;
padding: 0.5em; padding: 0.5em;
color: inherit; color: inherit;
} }
@ -747,7 +755,8 @@ option {
.fa-old-padding { .fa-old-padding {
&.svg-inline--fa { &.svg-inline--fa {
padding: 0 0.3em; padding-block: 0;
padding-inline: 0.3em;
} }
} }
@ -757,16 +766,22 @@ option {
@media all and (min-width: 801px) { @media all and (min-width: 801px) {
display: none; display: none;
} }
@media all and (min-height: 801px) {
[class^=#{$writing-mode-vertical}] & {
display: none;
}
}
a { a {
display: inline-block; display: inline-block;
padding: 1em 0; padding-block: 1em;
width: 100%; padding-inline: 0;
inline-size: 100%;
} }
} }
.btn.button-default { .btn.button-default {
min-height: 2em; min-block-size: 2em;
} }
.new-status-notification { .new-status-notification {
@ -781,6 +796,11 @@ option {
display: none; display: none;
} }
} }
@media all and (max-height: 800px) {
[class^=#{$writing-mode-vertical}] .mobile-hidden {
display: none;
}
}
@keyframes spin { @keyframes spin {
0% { 0% {

View file

@ -2,6 +2,7 @@
<div <div
id="app-loaded" id="app-loaded"
:style="bgStyle" :style="bgStyle"
:dir="$t('_locale.dir')"
> >
<div <div
id="app_bg_wrapper" id="app_bg_wrapper"

44
src/_logical.scss Normal file
View file

@ -0,0 +1,44 @@
$writing-mode-prefix: "writing-mode-";
$writing-mode-horizontal: "#{$writing-mode-prefix}h";
$writing-mode-vertical: "#{$writing-mode-prefix}v";
$writing-mode-sideways: "#{$writing-mode-prefix}s";
.#{$writing-mode-horizontal}tb { writing-mode: horizontal-tb; }
.#{$writing-mode-vertical}rl { writing-mode: vertical-rl; }
.#{$writing-mode-vertical}lr { writing-mode: vertical-lr; }
$resize-translate: ("block": ("vertical", "horizontal"), "inline": ("horizontal", "vertical"));
$axis-translate: ("block": ("y", "x"), "inline": ("x", "y"));
@mixin resize($value) {
resize: $value;
@if map-has-key($resize-translate, $value) {
@supports not (resize: $value) {
resize: nth(map-get($resize-translate, $value), 1);
[class^=#{$writing-mode-prefix}]:not([class^=#{$writing-mode-horizontal}]) &,
&[class^=#{$writing-mode-prefix}]:not([class^=#{$writing-mode-horizontal}]) {
resize: nth(map-get($resize-translate, $value), 2);
}
}
}
}
@mixin xy($prop, $axis, $value) {
#{$prop}-#{$axis}: $value;
@if map-has-key($axis-translate, $axis) {
@supports not (#{$prop}-#{$axis}: $value) {
#{$prop}-#{nth(map-get($axis-translate, $axis), 1)}: $value;
[class^=#{$writing-mode-prefix}]:not([class^=#{$writing-mode-horizontal}]) &,
&[class^=#{$writing-mode-prefix}]:not([class^=#{$writing-mode-horizontal}]) {
#{$prop}-#{nth(map-get($axis-translate, $axis), 2)}: $value;
}
}
}
}
@mixin overflow-block($value) { @include xy("overflow", "block", $value); }
@mixin overflow-inline($value) { @include xy("overflow", "inline", $value); }
@mixin overscroll-behavior-block($value) { @include xy("overscroll-behavior", "block", $value); }
@mixin overscroll-behavior-inline($value) { @include xy("overscroll-behavior", "inline", $value); }

View file

@ -4,8 +4,6 @@ import { createRouter, createWebHistory } from 'vue-router'
import vClickOutside from 'click-outside-vue3' import vClickOutside from 'click-outside-vue3'
import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome' import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome'
import { config } from '@fortawesome/fontawesome-svg-core';
config.autoAddCss = false
import App from '../App.vue' import App from '../App.vue'
import routes from './routes' import routes from './routes'

View file

@ -110,9 +110,11 @@
@import '../../_variables.scss'; @import '../../_variables.scss';
.AccountActions { .AccountActions {
.ellipsis-button { .ellipsis-button {
width: 2.5em; inline-size: 2.5em;
margin: -0.5em 0; margin-block: -0.5em;
padding: 0.5em 0; margin-inline: 0;
padding-block: 0.5em;
padding-inline: 0;
text-align: center; text-align: center;
&:not(:hover) .icon { &:not(:hover) .icon {

View file

@ -102,14 +102,14 @@
@import "../../variables"; @import "../../variables";
.announcement { .announcement {
border-bottom-width: 1px; border-block-end-width: 1px;
border-bottom-style: solid; border-block-end-style: solid;
border-bottom-color: var(--border, $fallback--border); border-block-end-color: var(--border, $fallback--border);
border-radius: 0; border-radius: 0;
padding: var(--status-margin, $status-margin); padding: var(--status-margin, $status-margin);
.heading, .body { .heading, .body {
margin-bottom: var(--status-margin, $status-margin); margin-block-end: var(--status-margin, $status-margin);
} }
.footer { .footer {
@ -129,7 +129,7 @@
.btn { .btn {
flex: 1; flex: 1;
margin: 1em; margin: 1em;
max-width: 10em; max-inline-size: 10em;
} }
} }
} }

View file

@ -41,18 +41,20 @@
<script src="./announcement_editor.js"></script> <script src="./announcement_editor.js"></script>
<style lang="scss"> <style lang="scss">
@import '../../_logical.scss';
.announcement-editor { .announcement-editor {
display: flex; display: flex;
align-items: stretch; align-items: stretch;
flex-direction: column; flex-direction: column;
.announcement-metadata { .announcement-metadata {
margin-top: 0.5em; margin-block-start: 0.5em;
} }
.post-textarea { .post-textarea {
resize: vertical; @include resize(block);
height: 10em; block-size: 10em;
overflow: none; overflow: none;
box-sizing: content-box; box-sizing: content-box;
} }

View file

@ -68,11 +68,11 @@
padding: var(--status-margin, $status-margin); padding: var(--status-margin, $status-margin);
.heading, .body { .heading, .body {
margin-bottom: var(--status-margin, $status-margin); margin-block-end: var(--status-margin, $status-margin);
} }
.post-button { .post-button {
min-width: 10em; min-inline-size: 10em;
} }
} }
} }

View file

@ -31,12 +31,13 @@ export default {
<style lang="scss"> <style lang="scss">
.async-component-error { .async-component-error {
display: flex; display: flex;
height: 100%; block-size: 100%;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.btn { .btn {
margin: .5em; margin: .5em;
padding: .5em 2em; padding-block: .5em;
padding-inline: 2em;
} }
} }
</style> </style>

View file

@ -18,7 +18,6 @@ import {
faPencilAlt, faPencilAlt,
faAlignRight faAlignRight
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import Blurhash from '../blurhash/Blurhash.vue'
library.add( library.add(
faFile, faFile,
@ -64,8 +63,7 @@ const Attachment = {
components: { components: {
Flash, Flash,
StillImage, StillImage,
VideoAttachment, VideoAttachment
Blurhash
}, },
computed: { computed: {
classNames () { classNames () {
@ -86,9 +84,6 @@ const Attachment = {
useContainFit () { useContainFit () {
return this.$store.getters.mergedConfig.useContainFit return this.$store.getters.mergedConfig.useContainFit
}, },
useBlurhash () {
return this.$store.getters.mergedConfig.useBlurhash
},
placeholderName () { placeholderName () {
if (this.attachment.description === '' || !this.attachment.description) { if (this.attachment.description === '' || !this.attachment.description) {
return this.type.toUpperCase() return this.type.toUpperCase()

View file

@ -1,4 +1,5 @@
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_logical.scss';
.Attachment { .Attachment {
display: inline-flex; display: inline-flex;
@ -6,7 +7,7 @@
position: relative; position: relative;
align-self: flex-start; align-self: flex-start;
line-height: 0; line-height: 0;
height: 100%; block-size: 100%;
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
border-radius: $fallback--attachmentRadius; border-radius: $fallback--attachmentRadius;
@ -16,7 +17,7 @@
.attachment-wrapper { .attachment-wrapper {
flex: 1 1 auto; flex: 1 1 auto;
height: 200px; block-size: 200px;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
} }
@ -24,9 +25,9 @@
.description-container { .description-container {
flex: 0 1 0; flex: 0 1 0;
display: flex; display: flex;
padding-top: 0.5em; padding-block-start: 0.5em;
z-index: 1; z-index: 1;
max-height: 50%; max-block-size: 50%;
p { p {
flex: 1; flex: 1;
@ -42,10 +43,10 @@
&.-static { &.-static {
position: absolute; position: absolute;
left: 0; inset-inline-start: 0;
right: 0; inset-inline-end: 0;
bottom: 0; inset-block-end: 0;
padding-top: 0; padding-block-start: 0;
background: var(--popover); background: var(--popover);
box-shadow: var(--popupShadow); box-shadow: var(--popupShadow);
} }
@ -53,19 +54,19 @@
.description-field { .description-field {
flex: 1; flex: 1;
min-width: 0; min-inline-size: 0;
box-sizing: content-box; box-sizing: content-box;
overflow: hidden; overflow: hidden;
transition: min-height 200ms 100ms; transition: min-block-size 200ms 100ms;
padding-bottom: var(--_padding); padding-block-end: var(--_padding);
height: calc(var(--post-line-height) * 1em); block-size: calc(var(--post-line-height) * 1em);
min-height: calc(var(--post-line-height) * 1em); min-block-size: calc(var(--post-line-height) * 1em);
resize: none; resize: none;
&.scrollable-form { &.scrollable-form {
overflow-y: auto; @include overflow-block(auto);
} }
} }
@ -77,14 +78,14 @@
& .oembed-container { & .oembed-container {
display: flex; display: flex;
justify-content: center; justify-content: center;
width: 100%; inline-size: 100%;
height: 100%; block-size: 100%;
} }
.image-container { .image-container {
.image { .image {
width: 100%; inline-size: 100%;
height: 100%; block-size: 100%;
} }
} }
@ -92,8 +93,8 @@
& .video-container { & .video-container {
& .flash, & .flash,
& video { & video {
width: 100%; inline-size: 100%;
height: 100%; block-size: 100%;
object-fit: contain; object-fit: contain;
align-self: center; align-self: center;
} }
@ -104,8 +105,8 @@
align-items: flex-end; align-items: flex-end;
audio { audio {
width: 100%; inline-size: 100%;
height: 100%; block-size: 100%;
} }
} }
@ -114,15 +115,15 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding-top: 0.5em; padding-block-start: 0.5em;
} }
.play-icon { .play-icon {
position: absolute; position: absolute;
font-size: 64px; font-size: 64px;
top: calc(50% - 32px); inset-block-start: calc(50% - 32px);
left: calc(50% - 32px); inset-inline-start: calc(50% - 32px);
color: rgba(255, 255, 255, 0.75); color: rgba(255, 255, 255, 0.75);
text-shadow: 0 0 2px rgba(0, 0, 0, 0.4); text-shadow: 0 0 2px rgba(0, 0, 0, 0.4);
@ -134,10 +135,10 @@
.attachment-buttons { .attachment-buttons {
display: flex; display: flex;
position: absolute; position: absolute;
right: 0; inset-inline-end: 0;
top: 0; inset-block-start: 0;
margin-top: 0.5em; margin-block-start: 0.5em;
margin-right: 0.5em; margin-inline-end: 0.5em;
z-index: 1; z-index: 1;
.attachment-button { .attachment-button {
@ -145,9 +146,9 @@
border-radius: $fallback--tooltipRadius; border-radius: $fallback--tooltipRadius;
border-radius: var(--tooltipRadius, $fallback--tooltipRadius); border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
text-align: center; text-align: center;
width: 2em; inline-size: 2em;
height: 2em; block-size: 2em;
margin-left: 0.5em; margin-inline-start: 0.5em;
font-size: 1.25em; font-size: 1.25em;
// TODO: theming? hard to theme with unknown background image color // TODO: theming? hard to theme with unknown background image color
background: rgba(230, 230, 230, 0.7); background: rgba(230, 230, 230, 0.7);
@ -165,12 +166,12 @@
.oembed-container { .oembed-container {
line-height: 1.2em; line-height: 1.2em;
flex: 1 0 100%; flex: 1 0 100%;
width: 100%; inline-size: 100%;
margin-right: 15px; margin-inline-end: 15px;
display: flex; display: flex;
img { img {
width: 100%; inline-size: 100%;
} }
.image { .image {
@ -178,7 +179,7 @@
img { img {
border: 0px; border: 0px;
border-radius: 5px; border-radius: 5px;
height: 100%; block-size: 100%;
object-fit: cover; object-fit: cover;
} }
} }
@ -221,7 +222,7 @@
color: var(--postLink, $fallback--link); color: var(--postLink, $fallback--link);
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
height: auto; block-size: auto;
line-height: 1.5; line-height: 1.5;
&:not(.-editable) { &:not(.-editable) {
@ -242,7 +243,7 @@
.description-container { .description-container {
flex: 1; flex: 1;
padding-left: 0.5em; padding-inline-start: 0.5em;
} }
.attachment-buttons { .attachment-buttons {
@ -253,7 +254,7 @@
a { a {
display: inline-block; display: inline-block;
max-width: 100%; max-inline-size: 100%;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }

View file

@ -64,15 +64,7 @@
:title="attachment.description" :title="attachment.description"
@click.prevent.stop="toggleHidden" @click.prevent.stop="toggleHidden"
> >
<Blurhash
v-if="useBlurhash && attachment.blurhash"
:height="512"
:width="1024"
:hash="attachment.blurhash"
:punch="1"
/>
<img <img
v-else
:key="nsfwImage" :key="nsfwImage"
class="nsfw" class="nsfw"
:src="nsfwImage" :src="nsfwImage"

View file

@ -25,21 +25,22 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_logical.scss';
.autosuggest { .autosuggest {
position: relative; position: relative;
&-input { &-input {
display: block; display: block;
width: 100%; inline-size: 100%;
} }
&-results { &-results {
position: absolute; position: absolute;
left: 0; inset-inline-start: 0;
top: 100%; inset-block-start: 100%;
right: 0; inset-inline-end: 0;
max-height: 400px; max-block-size: 400px;
background-color: $fallback--bg; background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg); background-color: var(--bg, $fallback--bg);
border-style: solid; border-style: solid;
@ -48,11 +49,11 @@
border-color: var(--border, $fallback--border); border-color: var(--border, $fallback--border);
border-radius: $fallback--inputRadius; border-radius: $fallback--inputRadius;
border-radius: var(--inputRadius, $fallback--inputRadius); border-radius: var(--inputRadius, $fallback--inputRadius);
border-top-left-radius: 0; border-start-start-radius: 0;
border-top-right-radius: 0; border-start-end-radius: 0;
box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6); box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6);
box-shadow: var(--panelShadow); box-shadow: var(--panelShadow);
overflow-y: auto; @include overflow-block(auto);
z-index: 1; z-index: 1;
} }
} }

View file

@ -26,20 +26,21 @@
// For hiding overflowing elements // For hiding overflowing elements
flex-wrap: wrap; flex-wrap: wrap;
height: 24px; block-size: 24px;
.avatars-item { .avatars-item {
margin: 0 0 5px 5px; margin-block: 0 5px;
margin-inline: 5px 0;
&:first-child { &:first-child {
padding-left: 5px; padding-inline-start: 5px;
} }
.avatar-small { .avatar-small {
border-radius: $fallback--avatarAltRadius; border-radius: $fallback--avatarAltRadius;
border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
height: 24px; block-size: 24px;
width: 24px; inline-size: 24px;
} }
} }
} }

View file

@ -51,20 +51,21 @@
display: flex; display: flex;
flex: 1 0; flex: 1 0;
margin: 0; margin: 0;
padding: 0.6em 1em; padding-block: 0.6em;
padding-inline: 1em;
&-collapsed-content { &-collapsed-content {
margin-left: 0.7em; margin-inline-start: 0.7em;
text-align: left; text-align: start;
flex: 1; flex: 1;
min-width: 0; min-inline-size: 0;
} }
&-user-name { &-user-name {
img { img {
object-fit: contain; object-fit: contain;
height: 16px; block-size: 16px;
width: 16px; inline-size: 16px;
vertical-align: middle; vertical-align: middle;
} }
} }
@ -72,7 +73,7 @@
&-user-name-value, &-user-name-value,
&-screen-name { &-screen-name {
display: inline-block; display: inline-block;
max-width: 100%; max-inline-size: 100%;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -80,8 +81,8 @@
&-expanded-content { &-expanded-content {
flex: 1; flex: 1;
margin-left: 0.7em; margin-inline-start: 0.7em;
min-width: 0; min-inline-size: 0;
} }
} }
</style> </style>

View file

@ -35,10 +35,10 @@
<style lang="scss"> <style lang="scss">
.block-card-content-container { .block-card-content-container {
margin-top: 0.5em; margin-block-start: 0.5em;
text-align: right; text-align: end;
button { button {
width: 10em; inline-size: 10em;
} }
} }
</style> </style>

View file

@ -1,66 +0,0 @@
<template>
<canvas
ref="canvas"
class="blurhash"
/>
</template>
<script>
import { decode } from "blurhash";
export default {
name: 'Blurhash',
props: {
hash: {
type: String,
required: true,
},
width: {
type: Number,
required: true,
},
height: {
type: Number,
required: true,
},
punch: {
type: Number,
default: null,
},
},
data() {
return {
canvas: null,
ctx: null,
};
},
mounted() {
this.canvas = this.$refs.canvas;
this.ctx = this.canvas.getContext('2d');
this.canvas.width = 1024;
this.canvas.height = 512;
this.draw();
},
methods: {
draw() {
const pixels = decode(this.hash, this.width, this.height, this.punch);
const imageData = this.ctx.createImageData(this.width, this.height);
imageData.data.set(pixels);
this.ctx.putImageData(imageData, 0, 0);
fetch("/static/blurhash-overlay.png")
.then((response) => response.blob())
.then((blob) => {
const img = new Image();
img.src = URL.createObjectURL(blob);
img.onload = () => {
this.ctx.drawImage(img, 0, 0, this.width, this.height);
};
});
},
}
}
</script>
<style scoped>
</style>

View file

@ -37,22 +37,22 @@ export default {
.checkbox { .checkbox {
position: relative; position: relative;
display: inline-block; display: inline-block;
min-height: 1.2em; min-block-size: 1.2em;
&-indicator { &-indicator {
position: relative; position: relative;
padding-left: 1.2em; padding-inline-start: 1.2em;
} }
&-indicator::before { &-indicator::before {
position: absolute; position: absolute;
right: 0; inset-inline-end: 0;
top: 0; inset-block-start: 0;
display: block; display: block;
content: '✓'; content: '✓';
transition: color 200ms; transition: color 200ms;
width: 1.1em; inline-size: 1.1em;
height: 1.1em; block-size: 1.1em;
border-radius: $fallback--checkboxRadius; border-radius: $fallback--checkboxRadius;
border-radius: var(--checkboxRadius, $fallback--checkboxRadius); border-radius: var(--checkboxRadius, $fallback--checkboxRadius);
box-shadow: 0px 0px 2px black inset; box-shadow: 0px 0px 2px black inset;
@ -96,7 +96,7 @@ export default {
} }
& > span { & > span {
margin-left: .5em; margin-inline-start: .5em;
} }
} }
</style> </style>

View file

@ -6,9 +6,10 @@
&-field.input { &-field.input {
display: inline-flex; display: inline-flex;
flex: 0 0 0; flex: 0 0 0;
max-width: 9em; max-inline-size: 9em;
align-items: stretch; align-items: stretch;
padding: .2em 8px; padding-block: .2em;
padding-inline: 8px;
input { input {
background: none; background: none;
@ -20,23 +21,23 @@
&.textColor { &.textColor {
flex: 1 0 3em; flex: 1 0 3em;
min-width: 3em; min-inline-size: 3em;
padding: 0; padding: 0;
} }
&.nativeColor { &.nativeColor {
flex: 0 0 2em; flex: 0 0 2em;
min-width: 2em; min-inline-size: 2em;
align-self: center; align-self: center;
height: 2em; block-size: 2em;
} }
} }
.computedIndicator, .computedIndicator,
.transparentIndicator { .transparentIndicator {
flex: 0 0 2em; flex: 0 0 2em;
min-width: 2em; min-inline-size: 2em;
align-self: center; align-self: center;
height: 100%; block-size: 100%;
} }
.transparentIndicator { .transparentIndicator {
// forgot to install counter-strike source, ooops // forgot to install counter-strike source, ooops
@ -47,16 +48,16 @@
content: ''; content: '';
background-color: #000000; background-color: #000000;
position: absolute; position: absolute;
height: 50%; block-size: 50%;
width: 50%; inline-size: 50%;
} }
&::after { &::after {
top: 0; inset-block-start: 0;
left: 0; inset-inline-start: 0;
} }
&::before { &::before {
bottom: 0; inset-block-end: 0;
right: 0; inset-inline-end: 0;
} }
} }
} }

View file

@ -112,7 +112,7 @@ export default {
<style lang="scss"> <style lang="scss">
.color-control { .color-control {
input.text-input { input.text-input {
max-width: 7em; max-inline-size: 7em;
flex: 1; flex: 1;
} }
} }

View file

@ -88,17 +88,17 @@ export default {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
margin-top: -4px; margin-block-start: -4px;
margin-bottom: 5px; margin-block-end: 5px;
.label { .label {
margin-right: 1em; margin-inline-end: 1em;
} }
.rating { .rating {
display: inline-block; display: inline-block;
text-align: center; text-align: center;
margin-left: 0.5em; margin-inline-start: 0.5em;
} }
} }
</style> </style>

View file

@ -205,9 +205,9 @@
.conversation-dive-to-top-level-box { .conversation-dive-to-top-level-box {
padding: var(--status-margin, $status-margin); padding: var(--status-margin, $status-margin);
border-bottom-width: 1px; border-block-end-width: 1px;
border-bottom-style: solid; border-block-end-style: solid;
border-bottom-color: var(--border, $fallback--border); border-block-end-color: var(--border, $fallback--border);
border-radius: 0; border-radius: 0;
/* Make the button stretch along the whole row */ /* Make the button stretch along the whole row */
display: flex; display: flex;
@ -216,8 +216,8 @@
} }
.thread-ancestors { .thread-ancestors {
margin-left: var(--status-margin, $status-margin); margin-inline-start: var(--status-margin, $status-margin);
border-left: 2px solid var(--border, $fallback--border); border-inline-start: 2px solid var(--border, $fallback--border);
} }
.thread-ancestor.-faded .StatusContent { .thread-ancestor.-faded .StatusContent {
@ -227,10 +227,10 @@
} }
.thread-ancestor-dive-box { .thread-ancestor-dive-box {
padding-left: var(--status-margin, $status-margin); padding-inline-start: var(--status-margin, $status-margin);
border-bottom-width: 1px; border-block-end-width: 1px;
border-bottom-style: solid; border-block-end-style: solid;
border-bottom-color: var(--border, $fallback--border); border-block-end-color: var(--border, $fallback--border);
border-radius: 0; border-radius: 0;
/* Make the button stretch along the whole row */ /* Make the button stretch along the whole row */
&, &-inner { &, &-inner {
@ -244,9 +244,9 @@
} }
.conversation-status { .conversation-status {
border-bottom-width: 1px; border-block-end-width: 1px;
border-bottom-style: solid; border-block-end-style: solid;
border-bottom-color: var(--border, $fallback--border); border-block-end-color: var(--border, $fallback--border);
border-radius: 0; border-radius: 0;
} }
@ -255,24 +255,30 @@
.thread-ancestor:last-child .thread-ancestor-dive-box, .thread-ancestor:last-child .thread-ancestor-dive-box,
&:last-child .conversation-status, &:last-child .conversation-status,
&.-expanded .thread-tree .conversation-status { &.-expanded .thread-tree .conversation-status {
border-bottom: none; border-block-end: none;
} }
.thread-ancestors + .thread-tree > .conversation-status { .thread-ancestors + .thread-tree > .conversation-status {
border-top-width: 1px; border-block-start-width: 1px;
border-top-style: solid; border-block-start-style: solid;
border-top-color: var(--border, $fallback--border); border-block-start-color: var(--border, $fallback--border);
} }
/* expanded conversation in timeline */ /* expanded conversation in timeline */
&.status-fadein.-expanded .thread-body { &.status-fadein.-expanded .thread-body {
border-left-width: 4px; border-inline-start-width: 4px;
border-left-style: solid; border-inline-start-style: solid;
border-left-color: $fallback--cRed; border-inline-start-color: $fallback--cRed;
border-left-color: var(--cRed, $fallback--cRed); border-inline-start-color: var(--cRed, $fallback--cRed);
border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius; border-start-start-radius: 0;
border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius); border-start-end-radius: 0;
border-bottom: 1px solid var(--border, $fallback--border); border-end-start-radius: $fallback--panelRadius;
border-end-end-radius: $fallback--panelRadius;
border-start-start-radius: 0;
border-start-end-radius: 0;
border-end-start-radius: var(--panelRadius, $fallback--panelRadius);
border-end-end-radius: var(--panelRadius, $fallback--panelRadius);
border-block-end: 1px solid var(--border, $fallback--border);
} }
&.-expanded.status-fadein { &.-expanded.status-fadein {

View file

@ -1,7 +1,8 @@
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_logical.scss';
.DesktopNav { .DesktopNav {
width: 100%; inline-size: 100%;
input { input {
color: var(--inputTopbarText, var(--inputText)); color: var(--inputTopbarText, var(--inputText));
@ -13,13 +14,14 @@
.inner-nav { .inner-nav {
display: grid; display: grid;
grid-template-rows: var(--navbar-height); grid-template-rows: var(--navbar-block-size);
grid-template-columns: 2fr auto 2fr; grid-template-columns: 2fr auto 2fr;
grid-template-areas: "nav-left logo actions"; grid-template-areas: "nav-left logo actions";
box-sizing: border-box; box-sizing: border-box;
padding: 0 1.2em; padding-block: 0;
padding-inline: 1.2em;
margin: auto; margin: auto;
max-width: 1110px; max-inline-size: 1110px;
} }
&.-logoLeft .inner-nav { &.-logoLeft .inner-nav {
@ -63,6 +65,11 @@
@media all and (min-width: 800px) { @media all and (min-width: 800px) {
opacity: 1 !important; opacity: 1 !important;
} }
@media all and (min-height: 800px) {
[class^=#{$writing-mode-vertical}] & {
opacity: 1 !important;
}
}
.mask { .mask {
mask-repeat: no-repeat; mask-repeat: no-repeat;
@ -71,27 +78,27 @@
background-color: $fallback--fg; background-color: $fallback--fg;
background-color: var(--topBarText, $fallback--fg); background-color: var(--topBarText, $fallback--fg);
position: absolute; position: absolute;
top: 0; inset-block-start: 0;
bottom: 0; inset-block-end: 0;
left: 0; inset-inline-start: 0;
right: 0; inset-inline-end: 0;
} }
img { img {
display: inline-block; display: inline-block;
height: var(--navbar-height); block-size: var(--navbar-block-size);
} }
} }
.nav-icon { .nav-icon {
margin-left: 0.2em; margin-inline-start: 0.2em;
width: 2em; inline-size: 2em;
height: 100%; block-size: 100%;
text-align: center; text-align: center;
&.router-link-active { &.router-link-active {
font-size: 1.2em; font-size: 1.2em;
margin-top: 0.05em; margin-block-start: 0.05em;
.svg-inline--fa { .svg-inline--fa {
font-weight: bolder; font-weight: bolder;
@ -109,12 +116,12 @@
.-wide { .-wide {
.nav-icon { .nav-icon {
margin-left: 0.7em; margin-inline-start: 0.7em;
} }
} }
.left { .left {
padding-left: 5px; padding-inline-start: 5px;
display: flex; display: flex;
} }
@ -122,9 +129,9 @@
grid-area: nav-left; grid-area: nav-left;
.favicon { .favicon {
height: 28px; block-size: 28px;
vertical-align: middle; vertical-align: middle;
padding-right: 5px; padding-inline-end: 5px;
} }
} }
@ -134,15 +141,15 @@
.item { .item {
flex: 1; flex: 1;
line-height: var(--navbar-height); line-height: var(--navbar-block-size);
height: var(--navbar-height); block-size: var(--navbar-block-size);
overflow: hidden; overflow: hidden;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
&.right { &.right {
justify-content: flex-end; justify-content: flex-end;
text-align: right; text-align: end;
} }
} }
} }

View file

@ -30,33 +30,37 @@
// TODO: unify with other modals. // TODO: unify with other modals.
.dark-overlay { .dark-overlay {
&::before { &::before {
bottom: 0; inset-block-end: 0;
content: " "; content: " ";
display: block; display: block;
cursor: default; cursor: default;
left: 0; inset-inline-start: 0;
position: fixed; position: fixed;
right: 0; inset-inline-end: 0;
top: 0; inset-block-start: 0;
background: rgba(27,31,35,.5); background: rgba(27,31,35,.5);
z-index: 2000; z-index: 2000;
} }
} }
.dialog-modal.panel { .dialog-modal.panel {
top: 0; inset-block-start: 0;
left: 50%; inset-inline-start: 50%;
max-height: 80vh; max-block-size: 80vb;
max-width: 90vw; max-inline-size: 90vi;
margin: 15vh auto; margin-block: 15vb;
margin-inline: auto;
position: fixed; position: fixed;
transform: translateX(-50%); transform: translateX(-50%);
z-index: 2001; z-index: 2001;
cursor: default; cursor: default;
display: block; display: block;
width: max-content; inline-size: max-content;
background-color: $fallback--bg; background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg); background-color: var(--bg, $fallback--bg);
[dir="rtl"] & {
transform: translateX(50%);
}
.dialog-modal-heading { .dialog-modal-heading {
.title { .title {
@ -66,7 +70,8 @@
.dialog-modal-content { .dialog-modal-content {
margin: 0; margin: 0;
padding: 1rem 1rem; padding-block: 1rem;
padding-inline: 1rem;
background-color: $fallback--bg; background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg); background-color: var(--bg, $fallback--bg);
white-space: normal; white-space: normal;
@ -74,17 +79,18 @@
.dialog-modal-footer { .dialog-modal-footer {
margin: 0; margin: 0;
padding: .5em .5em; padding-block: .5em;
padding-inline: .5em;
background-color: $fallback--bg; background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg); background-color: var(--bg, $fallback--bg);
border-top: 1px solid $fallback--border; border-block-start: 1px solid $fallback--border;
border-top: 1px solid var(--border, $fallback--border); border-block-start: 1px solid var(--border, $fallback--border);
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
button { button {
width: auto; inline-size: auto;
margin-left: .5rem; margin-inline-start: .5rem;
} }
} }
} }

View file

@ -34,20 +34,21 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 0.6em 1em 0.6em 0; padding-block: 0.6em 0.6em;
padding-inline: 0 1em;
&-domain { &-domain {
margin-right: 1em; margin-inline-end: 1em;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
button { button {
width: 10em; inline-size: 10em;
} }
.autosuggest-results & { .autosuggest-results & {
padding-left: 1em; padding-inline-start: 1em;
} }
} }
</style> </style>

View file

@ -28,20 +28,20 @@
} }
.edit-form-modal-panel { .edit-form-modal-panel {
flex-shrink: 0; flex-shrink: 0;
margin-top: 25%; margin-block-start: 25%;
margin-bottom: 2em; margin-block-end: 2em;
width: 100%; inline-size: 100%;
max-width: 700px; max-inline-size: 700px;
@media (orientation: landscape) { @media (orientation: landscape) {
margin-top: 8%; margin-block-start: 8%;
} }
.form-bottom-left { .form-bottom-left {
max-width: 6.5em; max-inline-size: 6.5em;
.emoji-icon { .emoji-icon {
justify-content: right; justify-content: end;
} }
} }
} }

View file

@ -1,133 +0,0 @@
const EMOJI_SIZE = 32 + 8
const GROUP_TITLE_HEIGHT = 24
const BUFFER_SIZE = 3 * EMOJI_SIZE
const EmojiGrid = {
props: {
groups: {
required: true,
type: Array
}
},
data () {
return {
containerWidth: 0,
containerHeight: 0,
scrollPos: 0,
resizeObserver: null
}
},
mounted () {
const rect = this.$refs.container.getBoundingClientRect()
this.containerWidth = rect.width
this.containerHeight = rect.height
this.resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
this.containerWidth = entry.contentRect.width
this.containerHeight = entry.contentRect.height
}
})
this.resizeObserver.observe(this.$refs.container)
},
beforeUnmount () {
this.resizeObserver.disconnect()
this.resizeObserver = null
},
watch: {
groups () {
// Scroll to top when grid content changes
if (this.$refs.container) {
this.$refs.container.scrollTo(0, 0)
}
},
activeGroup (group) {
this.$emit('activeGroup', group)
}
},
methods: {
onScroll () {
this.scrollPos = this.$refs.container.scrollTop
},
onEmoji (emoji) {
this.$emit('emoji', emoji)
},
scrollToItem (itemId) {
const container = this.$refs.container
if (!container) return
for (const item of this.itemList) {
if (item.id === itemId) {
container.scrollTo(0, item.position.y)
return
}
}
}
},
computed: {
// Total height of scroller content
gridHeight () {
if (this.itemList.length === 0) return 0
const lastItem = this.itemList[this.itemList.length - 1]
return (
lastItem.position.y +
('title' in lastItem ? GROUP_TITLE_HEIGHT : EMOJI_SIZE)
)
},
activeGroup () {
const items = this.itemList
for (let i = items.length - 1; i >= 0; i--) {
const item = items[i]
if ('title' in item && item.position.y <= this.scrollPos) {
return item.id
}
}
return null
},
itemList () {
const items = []
let x = 0
let y = 0
for (const group of this.groups) {
items.push({ position: { x, y }, id: group.id, title: group.text })
if (group.text.length) {
y += GROUP_TITLE_HEIGHT
}
for (const emoji of group.emojis) {
items.push({
position: { x, y },
id: `${group.id}-${emoji.displayText}`,
emoji
})
x += EMOJI_SIZE
if (x + EMOJI_SIZE > this.containerWidth) {
y += EMOJI_SIZE
x = 0
}
}
if (x > 0) {
y += EMOJI_SIZE
x = 0
}
}
return items
},
visibleItems () {
const startPos = this.scrollPos - BUFFER_SIZE
const endPos = this.scrollPos + this.containerHeight + BUFFER_SIZE
return this.itemList.filter((i) => {
return i.position.y >= startPos && i.position.y < endPos
})
},
scrolledClass () {
if (this.scrollPos <= 5) {
return 'scrolled-top'
} else if (this.scrollPos >= this.gridHeight - this.containerHeight - 5) {
return 'scrolled-bottom'
} else {
return 'scrolled-middle'
}
}
}
}
export default EmojiGrid

View file

@ -1,60 +0,0 @@
.emoji {
&-grid {
flex: 1 1 1px;
position: relative;
overflow: auto;
user-select: none;
mask: linear-gradient(to top, white 0, transparent 100%) bottom no-repeat,
linear-gradient(to bottom, white 0, transparent 100%) top no-repeat,
linear-gradient(to top, white, white);
transition: mask-size 150ms;
mask-size: 100% 20px, 100% 20px, auto;
// Autoprefixed seem to ignore this one, and also syntax is different
-webkit-mask-composite: xor;
mask-composite: exclude;
&.scrolled {
&-top {
mask-size: 100% 20px, 100% 0, auto;
}
&-bottom {
mask-size: 100% 0, 100% 20px, auto;
}
}
margin-left: 5px;
min-height: 200px;
}
&-group-title {
position: absolute;
font-size: 0.85em;
width: 100%;
margin: 0;
height: 24px;
display: flex;
align-items: end;
&.disabled {
display: none;
}
}
&-item {
position: absolute;
width: 32px;
height: 32px;
box-sizing: border-box;
display: flex;
font-size: 32px;
align-items: center;
justify-content: center;
margin: 4px;
cursor: pointer;
img {
object-fit: contain;
max-width: 100%;
max-height: 100%;
}
}
}

View file

@ -1,48 +0,0 @@
<template>
<div
ref="container"
class="emoji-grid"
:class="scrolledClass"
@scroll.passive="onScroll"
>
<div
:style="{
height: `${gridHeight}px`,
}"
>
<template v-for="item in visibleItems">
<h6
v-if="'title' in item && item.title.length"
:key="'title-' + item.id"
class="emoji-group-title"
:style="{
top: item.position.y + 'px',
left: item.position.x + 'px'
}"
>
{{ item.title }}
</h6>
<span
v-else-if="'emoji' in item"
:key="'emoji-' + item.id"
class="emoji-item"
:title="item.emoji.displayText"
:style="{
top: item.position.y + 'px',
left: item.position.x + 'px'
}"
@click.stop.prevent="onEmoji(item.emoji)"
>
<span v-if="!item.emoji.imageUrl">{{ item.emoji.replacement }}</span>
<img
v-else
:src="item.emoji.imageUrl"
>
</span>
</template>
</div>
</div>
</template>
<script src="./emoji_grid.js"></script>
<style lang="scss" src="./emoji_grid.scss"></style>

View file

@ -7,6 +7,9 @@ import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
faSmileBeam faSmileBeam
} from '@fortawesome/free-regular-svg-icons' } from '@fortawesome/free-regular-svg-icons'
import {
getViewport, isVertical, isVerticalRL, blockSizeOf, offsetBlockSizeOf, blockEndOf, offsetBlockEndOf
} from '../../lib/logical_dimensions'
library.add( library.add(
faSmileBeam faSmileBeam
@ -205,6 +208,7 @@ const EmojiInput = {
}, },
triggerShowPicker () { triggerShowPicker () {
this.showPicker = true this.showPicker = true
this.$refs.picker.startEmojiLoad()
this.$nextTick(() => { this.$nextTick(() => {
this.scrollIntoView() this.scrollIntoView()
this.focusPickerInput() this.focusPickerInput()
@ -222,6 +226,7 @@ const EmojiInput = {
this.showPicker = !this.showPicker this.showPicker = !this.showPicker
if (this.showPicker) { if (this.showPicker) {
this.scrollIntoView() this.scrollIntoView()
this.$refs.picker.startEmojiLoad()
this.$nextTick(this.focusPickerInput) this.$nextTick(this.focusPickerInput)
} }
}, },
@ -319,42 +324,53 @@ const EmojiInput = {
}, },
scrollIntoView () { scrollIntoView () {
const rootRef = this.$refs['picker'].$el const rootRef = this.$refs['picker'].$el
/* Scroller is either `window` (replies in TL), sidebar (main post form, /* Scroller is either HTML (replies in TL), sidebar (main post form,
* replies in notifs) or mobile post form. Note that getting and setting * replies in notifs) or mobile post form.
* scroll is different for `Window` and `Element`s
*/ */
const scrollerRef = this.$el.closest('.sidebar-scroller') || const scrollerRef = this.$el.closest('.sidebar-scroller') ||
this.$el.closest('.post-form-modal-view') || this.$el.closest('.post-form-modal-view') ||
window document.documentElement
const currentScroll = scrollerRef === window const viewport = getViewport()
? scrollerRef.scrollY const isVrt = isVertical()
const isVRL = isVerticalRL()
const currentScroll = isVrt
? scrollerRef.scrollLeft
: scrollerRef.scrollTop : scrollerRef.scrollTop
const scrollerHeight = scrollerRef === window const scrollerHeight = scrollerRef === document.documentElement
? scrollerRef.innerHeight ? blockSizeOf(viewport)
: scrollerRef.offsetHeight : offsetBlockSizeOf(scrollerRef)
const scrollerBottomBorder = currentScroll + scrollerHeight const scrollerBottomBorder = currentScroll + scrollerHeight
// We check where the bottom border of root element is, this uses findOffset // We check where the bottom border of root element is, this uses findOffset
// to find offset relative to scrollable container (scroller) // to find offset relative to scrollable container (scroller)
const rootBottomBorder = rootRef.offsetHeight + findOffset(rootRef, scrollerRef).top const offset = findOffset(rootRef, scrollerRef)
const rootBottomBorder = isVRL ? offset.left : offsetBlockSizeOf(rootRef) + (isVrt ? offset.left : offset.top)
const bottomDelta = Math.max(0, rootBottomBorder - scrollerBottomBorder) const bottomDelta = Math.max(0, rootBottomBorder - scrollerBottomBorder)
// could also check top delta but there's no case for it // could also check top delta but there's no case for it
const targetScroll = currentScroll + bottomDelta const targetScroll = currentScroll + bottomDelta
if (scrollerRef === window) { if (scrollerRef === document.documentElement) {
scrollerRef.scroll(0, targetScroll) if (isVrt) {
window.scroll(targetScroll, 0)
} else {
window.scroll(0, targetScroll)
}
} else { } else {
scrollerRef.scrollTop = targetScroll if (isVrt) {
scrollerRef.scrollLeft = targetScroll
} else {
scrollerRef.scrollTop = targetScroll
}
} }
this.$nextTick(() => { this.$nextTick(() => {
const { offsetHeight } = this.input const offsetHeight = offsetBlockSizeOf(this.input)
const { picker } = this.$refs const { picker } = this.$refs
const pickerBottom = picker.$el.getBoundingClientRect().bottom const pickerBottom = blockEndOf(picker.$el.getBoundingClientRect())
if (pickerBottom > window.innerHeight) { if (pickerBottom > blockSizeOf(viewport)) {
picker.$el.style.top = 'auto' picker.$el.style.insetBlockStart = 'auto'
picker.$el.style.bottom = offsetHeight + 'px' picker.$el.style.insetBlockEnd = offsetHeight + 'px'
} }
}) })
}, },
@ -480,24 +496,24 @@ const EmojiInput = {
if (!panel) return if (!panel) return
const picker = this.$refs.picker.$el const picker = this.$refs.picker.$el
const panelBody = this.$refs['panel-body'] const panelBody = this.$refs['panel-body']
const { offsetHeight, offsetTop } = this.input const offsetBottom = offsetBlockEndOf(this.input)
const offsetBottom = offsetTop + offsetHeight
this.setPlacement(panelBody, panel, offsetBottom) this.setPlacement(panelBody, panel, offsetBottom)
this.setPlacement(picker, picker, offsetBottom) this.setPlacement(picker, picker, offsetBottom)
}, },
// the argument offsetBottom actually receives logical offset block end position
setPlacement (container, target, offsetBottom) { setPlacement (container, target, offsetBottom) {
if (!container || !target) return if (!container || !target) return
if (this.placement === 'bottom' || (this.placement === 'auto' && !this.overflowsBottom(container))) { if (this.placement === 'bottom' || (this.placement === 'auto' && !this.overflowsBottom(container))) {
target.style.top = offsetBottom + 'px' target.style.insetBlockStart = offsetBottom + 'px'
target.style.bottom = 'auto' target.style.insetBlockEnd = 'auto'
} else { } else {
target.style.top = 'auto' target.style.insetBlockStart = 'auto'
target.style.bottom = this.input.offsetHeight + 'px' target.style.insetBlockEnd = offsetBlockSizeOf(this.input) + 'px'
} }
}, },
overflowsBottom (el) { overflowsBottom (el) {
return el.getBoundingClientRect().bottom > window.innerHeight return blockEndOf(el.getBoundingClientRect()) > blockSizeOf(getViewport())
} }
} }
} }

View file

@ -18,7 +18,6 @@
<EmojiPicker <EmojiPicker
v-if="enableEmojiPicker" v-if="enableEmojiPicker"
ref="picker" ref="picker"
show-keep-open
:class="{ hide: !showPicker }" :class="{ hide: !showPicker }"
:enable-sticker-picker="enableStickerPicker" :enable-sticker-picker="enableStickerPicker"
class="emoji-picker-panel" class="emoji-picker-panel"
@ -71,14 +70,15 @@
position: relative; position: relative;
&.with-picker input { &.with-picker input {
padding-right: 30px; padding-inline-end: 30px;
} }
.emoji-picker-icon { .emoji-picker-icon {
position: absolute; position: absolute;
top: 0; inset-block-start: 0;
right: 0; inset-inline-end: 0;
margin: .2em .25em; margin-block: .2em;
margin-inline: .25em;
font-size: 1.3em; font-size: 1.3em;
cursor: pointer; cursor: pointer;
line-height: 24px; line-height: 24px;
@ -91,7 +91,7 @@
.emoji-picker-panel { .emoji-picker-panel {
position: absolute; position: absolute;
z-index: 20; z-index: 20;
margin-top: 2px; margin-block-start: 2px;
&.hide { &.hide {
display: none display: none
@ -102,19 +102,20 @@
&-panel { &-panel {
position: absolute; position: absolute;
z-index: 20; z-index: 20;
margin-top: 2px; margin-block-start: 2px;
&.hide { &.hide {
display: none display: none
} }
&-body { &-body {
margin: 0 0.5em 0 0.5em; margin-block: 0 0;
margin-inline: 0.5em 0.5em;
border-radius: $fallback--tooltipRadius; border-radius: $fallback--tooltipRadius;
border-radius: var(--tooltipRadius, $fallback--tooltipRadius); border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5); box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5);
box-shadow: var(--popupShadow); box-shadow: var(--popupShadow);
min-width: 75%; min-inline-size: 75%;
background-color: $fallback--bg; background-color: $fallback--bg;
background-color: var(--popover, $fallback--bg); background-color: var(--popover, $fallback--bg);
color: $fallback--link; color: $fallback--link;
@ -131,22 +132,23 @@
&-item { &-item {
display: flex; display: flex;
cursor: pointer; cursor: pointer;
padding: 0.2em 0.4em; padding-block: 0.2em;
border-bottom: 1px solid rgba(0, 0, 0, 0.4); padding-inline: 0.4em;
height: 32px; border-block-end: 1px solid rgba(0, 0, 0, 0.4);
block-size: 32px;
.image { .image {
width: 32px; inline-size: 32px;
height: 32px; block-size: 32px;
line-height: 32px; line-height: 32px;
text-align: center; text-align: center;
font-size: 32px; font-size: 32px;
margin-right: 4px; margin-inline-end: 4px;
img { img {
width: 32px; inline-size: 32px;
height: 32px; block-size: 32px;
object-fit: contain; object-fit: contain;
} }
} }
@ -155,7 +157,8 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
margin: 0 0.1em 0 0.2em; margin-block: 0 0;
margin-inline: 0.2em 0.1em;
.displayText { .displayText {
line-height: 1.5; line-height: 1.5;

View file

@ -1,6 +1,5 @@
import { defineAsyncComponent } from 'vue' import { defineAsyncComponent } from 'vue'
import Checkbox from '../checkbox/checkbox.vue' import Checkbox from '../checkbox/checkbox.vue'
import EmojiGrid from '../emoji_grid/emoji_grid.vue'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
faBoxOpen, faBoxOpen,
@ -8,6 +7,7 @@ import {
faSmileBeam faSmileBeam
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { trim, escapeRegExp, startCase } from 'lodash' import { trim, escapeRegExp, startCase } from 'lodash'
import { isVertical, isVerticalRL, offsetBlockEndOf, offsetBlockSizeOf, offsetBlockStartOf, scrollBlockSizeOf, scrollBlockStartMaxSizeOf, scrollBlockStartOf } from '../../lib/logical_dimensions'
library.add( library.add(
faBoxOpen, faBoxOpen,
@ -15,17 +15,19 @@ library.add(
faSmileBeam faSmileBeam
) )
// At widest, approximately 20 emoji are visible in a row,
// loading 3 rows, could be overkill for narrow picker
const LOAD_EMOJI_BY = 60
// When to start loading new batch emoji, in pixels
const LOAD_EMOJI_MARGIN = 64
const EmojiPicker = { const EmojiPicker = {
props: { props: {
enableStickerPicker: { enableStickerPicker: {
required: false, required: false,
type: Boolean, type: Boolean,
default: false default: false
},
showKeepOpen: {
required: false,
type: Boolean,
default: false
} }
}, },
data () { data () {
@ -33,13 +35,16 @@ const EmojiPicker = {
keyword: '', keyword: '',
activeGroup: 'standard', activeGroup: 'standard',
showingStickers: false, showingStickers: false,
keepOpen: false groupsScrolledClass: 'scrolled-top',
keepOpen: false,
customEmojiBufferSlice: LOAD_EMOJI_BY,
customEmojiTimeout: null,
customEmojiLoadAllConfirmed: false
} }
}, },
components: { components: {
StickerPicker: defineAsyncComponent(() => import('../sticker_picker/sticker_picker.vue')), StickerPicker: defineAsyncComponent(() => import('../sticker_picker/sticker_picker.vue')),
Checkbox, Checkbox
EmojiGrid
}, },
methods: { methods: {
onStickerUploaded (e) { onStickerUploaded (e) {
@ -52,19 +57,88 @@ const EmojiPicker = {
const value = emoji.imageUrl ? `:${emoji.displayText}:` : emoji.replacement const value = emoji.imageUrl ? `:${emoji.displayText}:` : emoji.replacement
this.$emit('emoji', { insertion: value, keepOpen: this.keepOpen }) this.$emit('emoji', { insertion: value, keepOpen: this.keepOpen })
}, },
onScroll (e) {
const target = (e && e.target) || this.$refs['emoji-groups']
this.updateScrolledClass(target)
this.scrolledGroup(target)
this.triggerLoadMore(target)
},
onWheel (e) { onWheel (e) {
e.preventDefault() e.preventDefault()
this.$refs['emoji-tabs'].scrollBy(e.deltaY, 0) if (isVertical()) {
this.$refs['emoji-tabs'].scrollBy(0, (isVerticalRL() ? -1 : 1) * e.deltaY)
} else {
this.$refs['emoji-tabs'].scrollBy(e.deltaY, 0)
}
}, },
highlight (key) { highlight (key) {
this.setShowStickers(false) this.setShowStickers(false)
this.activeGroup = key this.activeGroup = key
if (this.keyword.length) { },
this.$refs.emojiGrid.scrollToItem(key) updateScrolledClass (target) {
const scrollStart = scrollBlockStartOf(target)
if (Math.abs(scrollStart) <= 5) {
this.groupsScrolledClass = 'scrolled-top'
// Element.scrollTopMax is not on the standard track
// } else if (target.scrollTop >= target.scrollTopMax - 5) {
} else if (Math.abs(scrollStart) >= scrollBlockStartMaxSizeOf(target) - 5) {
this.groupsScrolledClass = 'scrolled-bottom'
} else {
this.groupsScrolledClass = 'scrolled-middle'
} }
}, },
onActiveGroup (group) { triggerLoadMore (target) {
this.activeGroup = group const ref = this.$refs['group-end-custom']
if (!ref) return
const bottom = offsetBlockEndOf(ref)
const scrollerBottom = Math.abs(scrollBlockStartOf(target)) + scrollBlockSizeOf(target)
const scrollerTop = Math.abs(scrollBlockStartOf(target))
const scrollerMax = scrollBlockSizeOf(target)
// Loads more emoji when they come into view
const approachingBottom = bottom - scrollerBottom < LOAD_EMOJI_MARGIN
// Always load when at the very top in case there's no scroll space yet
const atTop = scrollerTop < 5
// Don't load when looking at unicode category or at the very bottom
const bottomAboveViewport = bottom < scrollerTop || scrollerBottom === scrollerMax
if (!bottomAboveViewport && (approachingBottom || atTop)) {
this.loadEmoji()
}
},
scrolledGroup (target) {
const top = Math.abs(scrollBlockStartOf(target)) + 5
this.$nextTick(() => {
this.emojisView.forEach(group => {
const ref = this.$refs['group-' + group.id]
if (offsetBlockStartOf(ref) <= top) {
this.activeGroup = group.id
}
})
})
},
loadEmoji () {
const allLoaded = this.customEmojiBuffer.length === this.filteredEmoji.length
if (allLoaded) {
return
}
this.customEmojiBufferSlice += LOAD_EMOJI_BY
},
startEmojiLoad (forceUpdate = false) {
if (!forceUpdate) {
this.keyword = ''
}
this.$nextTick(() => {
this.$refs['emoji-groups'].scrollTop = 0
})
const bufferSize = this.customEmojiBuffer.length
const bufferPrefilledAll = bufferSize === this.filteredEmoji.length
if (bufferPrefilledAll && !forceUpdate) {
return
}
this.customEmojiBufferSlice = LOAD_EMOJI_BY
}, },
toggleStickers () { toggleStickers () {
this.showingStickers = !this.showingStickers this.showingStickers = !this.showingStickers
@ -80,6 +154,13 @@ const EmojiPicker = {
}) })
} }
}, },
watch: {
keyword () {
this.customEmojiLoadAllConfirmed = false
this.onScroll()
this.startEmojiLoad(true)
}
},
computed: { computed: {
activeGroupView () { activeGroupView () {
return this.showingStickers ? '' : this.activeGroup return this.showingStickers ? '' : this.activeGroup
@ -95,6 +176,9 @@ const EmojiPicker = {
this.$store.state.instance.customEmoji || [] this.$store.state.instance.customEmoji || []
) )
}, },
customEmojiBuffer () {
return this.filteredEmoji.slice(0, this.customEmojiBufferSlice)
},
emojis () { emojis () {
const standardEmojis = this.$store.state.instance.emoji || [] const standardEmojis = this.$store.state.instance.emoji || []
const customEmojis = this.sortedEmoji const customEmojis = this.sortedEmoji

View file

@ -1,37 +1,37 @@
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_logical.scss';
// The worst query selector ever
// selects ONLY emojis pickers in replies in notifications
// who thought this was a good idea?
.notification > .Status > .status-container > .post-status-form > form > .form-group > .emoji-input > .emoji-picker {
max-width: 100%;
left: 0;
@media (min-width: 1300px) {
left: -30px;
}
}
.Notification { .Notification {
.emoji-picker { .emoji-picker {
min-width: 160%; min-inline-size: 160%;
width: 150%; inline-size: 150%;
overflow: hidden; overflow: hidden;
left: -70%; inset-inline-start: -70%;
max-width: 100%; max-inline-size: 100%;
@media (min-width: 800px) and (max-width: 1280px) { @media (min-width: 800px) and (max-width: 1300px) {
left: -50%; inset-inline-start: -50%;
min-width: 50%; min-inline-size: 50%;
max-width: 130%; max-inline-size: 130%;
}
@media (min-height: 800px) and (max-height: 1300px) {
[class^=#{$writing-mode-vertical}] & {
inset-inline-start: -50%;
min-inline-size: 50%;
max-inline-size: 130%;
}
} }
@media (max-width: 800px) { @media (max-width: 800px) {
left: -10%; inset-inline-start: -10%;
min-width: 50%; min-inline-size: 50%;
max-width: 130%; max-inline-size: 130%;
} }
@media (max-height: 800px) {
.Status > .emoji-picker { [class^=#{$writing-mode-vertical}] & {
z-index: 1000; inset-inline-start: -10%;
min-inline-size: 50%;
max-inline-size: 130%;
}
} }
} }
} }
@ -39,8 +39,8 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: absolute; position: absolute;
right: 0; inset-inline-end: 0;
left: 0; inset-inline-start: 0;
margin: 0 !important; margin: 0 !important;
z-index: 100; z-index: 100;
background-color: $fallback--bg; background-color: $fallback--bg;
@ -65,42 +65,46 @@
} }
.keep-open-label { .keep-open-label {
padding: 0 7px; padding-block: 0;
padding-inline: 7px;
display: flex; display: flex;
} }
.heading { .heading {
margin-top: 10px; margin-block-start: 10px;
height: 4.8em; block-size: 4.8em;
} }
.content { .content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1 1 auto; flex: 1 1 auto;
min-height: 0px; min-block-size: 0px;
} }
.emoji-tabs { .emoji-tabs {
flex-grow: 1; flex-grow: 1;
} }
.emoji-groups {
min-block-size: 200px;
}
.additional-tabs { .additional-tabs {
border-left: 1px solid; border-inline-start: 1px solid;
border-left-color: $fallback--icon; border-inline-start-color: $fallback--icon;
border-left-color: var(--icon, $fallback--icon); border-inline-start-color: var(--icon, $fallback--icon);
padding-left: 7px; padding-inline-start: 7px;
flex: 0 0 auto; flex: 0 0 auto;
} }
.additional-tabs, .additional-tabs,
.emoji-tabs { .emoji-tabs {
position: absolute;
display: block; display: block;
flex-wrap: nowrap; flex-wrap: nowrap;
overflow: auto; overflow: auto;
width: 100%; inline-size: 100%;
white-space: nowrap; white-space: nowrap;
@ -109,14 +113,14 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 32px; inline-size: 32px;
height: 32px; block-size: 32px;
padding: .4em; padding: .4em;
cursor: pointer; cursor: pointer;
img { img {
max-width: 100%; max-inline-size: 100%;
max-height: 100%; max-block-size: 100%;
object-fit: contain; object-fit: contain;
} }
@ -130,7 +134,7 @@
} }
&.active { &.active {
border-bottom: 4px solid; border-block-end: 4px solid;
svg { svg {
color: $fallback--lightText; color: $fallback--lightText;
@ -153,7 +157,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1 1 auto; flex: 1 1 auto;
min-height: 0; min-block-size: 0;
&.hidden { &.hidden {
opacity: 0; opacity: 0;
@ -163,12 +167,97 @@
} }
} }
.emoji-search { .emoji {
padding: 5px; &-search {
flex: 0 0 auto; padding: 5px;
flex: 0 0 auto;
input { input {
width: 100%; inline-size: 100%;
}
} }
&-groups {
flex: 1 1 1px;
position: relative;
overflow: auto;
user-select: none;
mask: linear-gradient(to top, white 0, transparent 100%) bottom no-repeat,
linear-gradient(to bottom, white 0, transparent 100%) top no-repeat,
linear-gradient(to top, white, white);
transition: mask-size 150ms;
mask-size: 100% 20px, 100% 20px, auto;
// Autoprefixed seem to ignore this one, and also syntax is different
-webkit-mask-composite: xor;
mask-composite: exclude;
.#{$writing-mode-vertical}lr & {
mask: linear-gradient(to left, white 0, transparent 100%) right no-repeat,
linear-gradient(to right, white 0, transparent 100%) left no-repeat,
linear-gradient(to left, white, white);
mask-size: 20px 100%, 20px 100%, auto;
mask-composite: exclude;
}
.#{$writing-mode-vertical}rl & {
mask: linear-gradient(to right, white 0, transparent 100%) left no-repeat,
linear-gradient(to left, white 0, transparent 100%) right no-repeat,
linear-gradient(to right, white, white);
mask-size: 20px 100%, 20px 100%, auto;
mask-composite: exclude;
}
&.scrolled {
&-top {
mask-size: 100% 20px, 100% 0, auto;
[class^=#{$writing-mode-vertical}] & {
mask-size: 20px 100%, 0 100%, auto;
}
}
&-bottom {
mask-size: 100% 0, 100% 20px, auto;
[class^=#{$writing-mode-vertical}] & {
mask-size: 0 100%, 20px 100%, auto;
}
}
}
}
&-group {
display: flex;
align-items: center;
flex-wrap: wrap;
padding-inline-start: 5px;
justify-content: start;
&-title {
font-size: 0.85em;
inline-size: 100%;
margin: 0;
&.disabled {
display: none;
}
}
}
&-item {
inline-size: 32px;
block-size: 32px;
box-sizing: border-box;
display: flex;
font-size: 32px;
align-items: center;
justify-content: center;
margin: 4px;
cursor: pointer;
img {
object-fit: contain;
max-inline-size: 100%;
max-block-size: 100%;
}
}
} }
} }

View file

@ -2,9 +2,9 @@
<div class="emoji-picker panel panel-default panel-body"> <div class="emoji-picker panel panel-default panel-body">
<div class="heading"> <div class="heading">
<span <span
ref="emoji-tabs"
class="emoji-tabs" class="emoji-tabs"
@wheel="onWheel" @wheel="onWheel"
ref="emoji-tabs"
> >
<span <span
v-for="group in emojis" v-for="group in emojis"
@ -51,16 +51,40 @@
@input="$event.target.composing = false" @input="$event.target.composing = false"
> >
</div> </div>
<EmojiGrid
ref="emojiGrid"
:groups="emojisView"
@emoji="onEmoji"
@active-group="onActiveGroup"
/>
<div <div
v-if="showKeepOpen" ref="emoji-groups"
class="keep-open" class="emoji-groups"
:class="groupsScrolledClass"
@scroll="onScroll"
> >
<div
v-for="group in emojisView"
:key="group.id"
class="emoji-group"
>
<h6
:ref="'group-' + group.id"
class="emoji-group-title"
>
{{ group.text }}
</h6>
<span
v-for="emoji in group.emojis"
:key="group.id + emoji.displayText"
:title="emoji.displayText"
class="emoji-item"
@click.stop.prevent="onEmoji(emoji)"
>
<span v-if="!emoji.imageUrl">{{ emoji.replacement }}</span>
<img
v-else
:src="emoji.imageUrl"
>
</span>
<span :ref="'group-end-' + group.id" />
</div>
</div>
<div class="keep-open">
<Checkbox v-model="keepOpen"> <Checkbox v-model="keepOpen">
{{ $t('emoji.keep_open') }} {{ $t('emoji.keep_open') }}
</Checkbox> </Checkbox>

View file

@ -19,7 +19,6 @@
:title="reaction.name" :title="reaction.name"
class="reaction-emoji" class="reaction-emoji"
width="2.55em" width="2.55em"
height="2.55em"
> >
{{ reaction.count }} {{ reaction.count }}
</span> </span>
@ -48,7 +47,7 @@
.emoji-reactions { .emoji-reactions {
display: flex; display: flex;
margin-top: 0.25em; margin-block-start: 0.25em;
flex-wrap: wrap; flex-wrap: wrap;
} }
@ -57,17 +56,17 @@
} }
.emoji-reaction { .emoji-reaction {
padding: 0 0.5em; padding-block: 0;
margin-right: 0.5em; padding-inline: 0.5em;
margin-top: 0.5em; margin-inline-end: 0.5em;
margin-block-start: 0.5em;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-sizing: border-box; box-sizing: border-box;
.reaction-emoji { .reaction-emoji {
width: 2.55em !important; inline-size: 2.55em !important;
height: 2.55em !important; margin-inline-end: 0.25em;
margin-right: 0.25em;
} }
&:focus { &:focus {
outline: none; outline: none;
@ -83,9 +82,10 @@
} }
.emoji-reaction-expand { .emoji-reaction-expand {
padding: 0 0.5em; padding-block: 0;
margin-right: 0.5em; padding-inline: 0.5em;
margin-top: 0.5em; margin-inline-end: 0.5em;
margin-block-start: 0.5em;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -96,8 +96,8 @@
.button-default.picked-reaction { .button-default.picked-reaction {
border: 1px solid var(--accent, $fallback--link); border: 1px solid var(--accent, $fallback--link);
margin-left: -1px; // offset the border, can't use inset shadows either margin-inline-start: -1px; // offset the border, can't use inset shadows either
margin-right: calc(0.5em - 1px); margin-inline-end: calc(0.5em - 1px);
} }
</style> </style>

View file

@ -144,7 +144,6 @@ const ExtraButtons = {
statusPoll: this.status.poll, statusPoll: this.status.poll,
statusFiles: [...this.status.attachments], statusFiles: [...this.status.attachments],
statusScope: this.status.visibility, statusScope: this.status.visibility,
statusLanguage: this.status.language,
statusContentType: data.content_type statusContentType: data.content_type
})) }))
this.doDeleteStatus() this.doDeleteStatus()

View file

@ -213,7 +213,7 @@
.ExtraButtons { .ExtraButtons {
/* override of popover internal stuff */ /* override of popover internal stuff */
.popover-trigger-button { .popover-trigger-button {
width: auto; inline-size: auto;
} }
.popover-trigger { .popover-trigger {

View file

@ -45,7 +45,8 @@
> :first-child { > :first-child {
padding: 10px; padding: 10px;
margin: -10px -8px -10px -10px; margin-block: -10px -10px;
margin-inline: -10px -8px;
} }
.action-counter { .action-counter {

View file

@ -45,18 +45,18 @@
@import '../../_variables.scss'; @import '../../_variables.scss';
.Flash { .Flash {
display: inline-block; display: inline-block;
width: 100%; inline-size: 100%;
height: 100%; block-size: 100%;
position: relative; position: relative;
.player { .player {
height: 100%; block-size: 100%;
width: 100%; inline-size: 100%;
} }
.placeholder { .placeholder {
height: 100%; block-size: 100%;
width: 100%; inline-size: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -65,7 +65,7 @@
} }
.hider { .hider {
top: 0; inset-block-start: 0;
} }
.label { .label {

View file

@ -46,15 +46,16 @@
} }
&-button { &-button {
margin-top: 0.5em; margin-block-start: 0.5em;
padding: 0 1.5em; padding-block: 0;
margin-left: 1em; padding-inline: 1.5em;
margin-inline-start: 1em;
} }
&-follow-button { &-follow-button {
margin-top: 0.5em; margin-block-start: 0.5em;
margin-left: auto; margin-inline-start: auto;
width: 10em; inline-size: 10em;
} }
} }
</style> </style>

View file

@ -43,7 +43,6 @@ const FollowRequestCard = {
doApprove () { doApprove () {
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
this.$store.dispatch('removeFollowRequest', this.user) this.$store.dispatch('removeFollowRequest', this.user)
this.$store.dispatch('decrementFollowRequestsCount')
const notifId = this.findFollowRequestNotificationId() const notifId = this.findFollowRequestNotificationId()
this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId }) this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId })
@ -67,7 +66,6 @@ const FollowRequestCard = {
this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) this.$store.state.api.backendInteractor.denyUser({ id: this.user.id })
.then(() => { .then(() => {
this.$store.dispatch('dismissNotificationLocal', { id: notifId }) this.$store.dispatch('dismissNotificationLocal', { id: notifId })
this.$store.dispatch('decrementFollowRequestsCount')
this.$store.dispatch('removeFollowRequest', this.user) this.$store.dispatch('removeFollowRequest', this.user)
}) })
this.hideDenyConfirmDialog() this.hideDenyConfirmDialog()
@ -82,11 +80,6 @@ const FollowRequestCard = {
}, },
shouldConfirmDeny () { shouldConfirmDeny () {
return this.mergedConfig.modalOnDenyFollow return this.mergedConfig.modalOnDenyFollow
},
show () {
const notifId = this.$store.state.api.followRequests.find(req => req.id === this.user.id)
return notifId !== undefined
} }
} }
} }

View file

@ -1,5 +1,5 @@
<template> <template>
<basic-user-card :user="user" v-if="show"> <basic-user-card :user="user">
<div class="follow-request-card-content-container"> <div class="follow-request-card-content-container">
<button <button
class="btn button-default" class="btn button-default"
@ -47,14 +47,14 @@
flex-direction: row; flex-direction: row;
flex-wrap: wrap; flex-wrap: wrap;
button { button {
margin-top: 0.5em; margin-block-start: 0.5em;
margin-right: 0.5em; margin-inline-end: 0.5em;
flex: 1 1; flex: 1 1;
max-width: 12em; max-inline-size: 12em;
min-width: 8em; min-inline-size: 8em;
&:last-child { &:last-child {
margin-right: 0; margin-inline-end: 0;
} }
} }
} }

View file

@ -1,26 +1,10 @@
import FollowRequestCard from '../follow_request_card/follow_request_card.vue' import FollowRequestCard from '../follow_request_card/follow_request_card.vue'
import withLoadMore from '../../hocs/with_load_more/with_load_more'
import List from '../list/list.vue'
import get from 'lodash/get'
const FollowRequestList = withLoadMore({
fetch: (props, $store) => $store.dispatch('fetchFollowRequests'),
select: (props, $store) => get($store.state.api, 'followRequests', []).map(req => $store.getters.findUser(req.id)),
destroy: (props, $store) => $store.dispatch('clearFollowRequests'),
childPropName: 'items',
additionalPropNames: ['userId']
})(List);
const FollowRequests = { const FollowRequests = {
components: { components: {
FollowRequestCard, FollowRequestCard
FollowRequestList
}, },
computed: { computed: {
userId () {
return this.$store.state.users.currentUser.id
},
requests () { requests () {
return this.$store.state.api.followRequests return this.$store.state.api.followRequests
} }

View file

@ -6,11 +6,12 @@
</div> </div>
</div> </div>
<div class="panel-body"> <div class="panel-body">
<FollowRequestList :user-id="userId"> <FollowRequestCard
<template #item="{item}"> v-for="request in requests"
<FollowRequestCard :user="item" /> :key="request.id"
</template> :user="request"
</FollowRequestList> class="list-item"
/>
</div> </div>
</div> </div>
</template> </template>

View file

@ -1,77 +0,0 @@
<template>
<div class="followed-tag-card">
<span>
<router-link :to="{ name: 'tag-timeline', params: {tag: tag.name}}">
<span class="tag-link">#{{ tag.name }}</span>
</router-link>
<span class="unfollow-tag">
<button
v-if="isFollowing"
class="button-default unfollow-tag-button"
:title="$t('user_card.unfollow_tag')"
@click="unfollowTag(tag.name)"
>
{{ $t('user_card.unfollow_tag') }}
</button>
<button
v-else
class="button-default follow-tag-button"
:title="$t('user_card.follow_tag')"
@click="followTag(tag.name)"
>
{{ $t('user_card.follow_tag') }}
</button>
</span>
</span>
</div>
</template>
<script>
export default {
name: 'FollowedTagCard',
props: {
tag: {
type: Object,
required: true
},
},
// this is a hack to update the state of the button
// for some reason, List does not update on changes to the tag object
data: () => ({
isFollowing: true
}),
mounted () {
this.isFollowing = this.tag.following
},
methods: {
unfollowTag (tag) {
this.$store.dispatch('unfollowTag', tag)
this.isFollowing = false
},
followTag (tag) {
this.$store.dispatch('followTag', tag)
this.isFollowing = true
}
}
}
</script>
<style scoped>
.followed-tag-card {
margin-left: 1rem;
margin-top: 1rem;
margin-bottom: 1rem;
}
.unfollow-tag {
position: absolute;
right: 1rem;
}
.tag-link {
font-size: large;
}
.unfollow-tag-button, .follow-tag-button {
font-size: medium;
}
</style>

View file

@ -53,17 +53,17 @@
@import '../../_variables.scss'; @import '../../_variables.scss';
.font-control { .font-control {
input.custom-font { input.custom-font {
min-width: 10em; min-inline-size: 10em;
} }
&.custom { &.custom {
/* TODO Should make proper joiners... */ /* TODO Should make proper joiners... */
.font-switcher { .font-switcher {
border-top-right-radius: 0; border-start-end-radius: 0;
border-bottom-right-radius: 0; border-end-end-radius: 0;
} }
.custom-font { .custom-font {
border-top-left-radius: 0; border-start-start-radius: 0;
border-bottom-left-radius: 0; border-end-start-radius: 0;
} }
} }
} }

View file

@ -89,9 +89,9 @@ const Gallery = {
}, },
rowStyle (row) { rowStyle (row) {
if (row.audio) { if (row.audio) {
return { 'padding-bottom': '25%' } // fixed reduced height for audio return { 'padding-block-end': '25%' } // fixed reduced height for audio
} else if (!row.minimal && !row.grid) { } else if (!row.minimal && !row.grid) {
return { 'padding-bottom': `${(100 / (row.items.length + 0.6))}%` } return { 'padding-block-end': `${(100 / (row.items.length + 0.6))}%` }
} }
}, },
itemStyle (id, row) { itemStyle (id, row) {

View file

@ -87,6 +87,7 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_logical.scss';
.Gallery { .Gallery {
.gallery-rows { .gallery-rows {
@ -96,18 +97,18 @@
.gallery-row { .gallery-row {
position: relative; position: relative;
height: 0; block-size: 0;
width: 100%; inline-size: 100%;
flex-grow: 1; flex-grow: 1;
&:not(:first-child) { &:not(:first-child) {
margin-top: 0.5em; margin-block-start: 0.5em;
} }
} }
&.-long { &.-long {
.gallery-rows { .gallery-rows {
max-height: 25em; max-block-size: 25em;
overflow: hidden; overflow: hidden;
mask: mask:
linear-gradient(to top, white, transparent) bottom/100% 70px no-repeat, linear-gradient(to top, white, transparent) bottom/100% 70px no-repeat,
@ -116,6 +117,18 @@
/* Autoprefixed seem to ignore this one, and also syntax is different */ /* Autoprefixed seem to ignore this one, and also syntax is different */
-webkit-mask-composite: xor; -webkit-mask-composite: xor;
mask-composite: exclude; mask-composite: exclude;
.#{$writing-mode-vertical}lr & {
mask:
linear-gradient(to left, white, transparent) right/70px 100% no-repeat,
linear-gradient(to left, white, white);
mask-composite: exclude;
}
.#{$writing-mode-vertical}rl & {
mask:
linear-gradient(to right, white, transparent) left/70px 100% no-repeat,
linear-gradient(to right, white, white);
mask-composite: exclude;
}
} }
} }
@ -135,14 +148,15 @@
line-height: 2; line-height: 2;
button { button {
padding: 0 2em; padding-block: 0;
padding-inline: 2em;
} }
} }
.gallery-row { .gallery-row {
&.-grid, &.-grid,
&.-minimal { &.-minimal {
height: auto; block-size: auto;
.gallery-row-inner { .gallery-row-inner {
position: relative; position: relative;
} }
@ -151,18 +165,18 @@
.gallery-row-inner { .gallery-row-inner {
position: absolute; position: absolute;
top: 0; inset-block-start: 0;
left: 0; inset-inline-start: 0;
right: 0; inset-inline-end: 0;
bottom: 0; inset-block-end: 0;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex-wrap: nowrap; flex-wrap: nowrap;
align-content: stretch; align-content: stretch;
&.-grid { &.-grid {
width: 100%; inline-size: 100%;
height: auto; block-size: auto;
position: relative; position: relative;
display: grid; display: grid;
grid-column-gap: 0.5em; grid-column-gap: 0.5em;
@ -176,12 +190,13 @@
} }
.gallery-item { .gallery-item {
margin: 0 0.5em 0 0; margin-block: 0 0;
margin-inline: 0 0.5em;
flex-grow: 1; flex-grow: 1;
height: 100%; block-size: 100%;
box-sizing: border-box; box-sizing: border-box;
// to make failed images a bit more noticeable on chromium // to make failed images a bit more noticeable on chromium
min-width: 2em; min-inline-size: 2em;
&:last-child { &:last-child {
margin: 0; margin: 0;
} }

View file

@ -29,8 +29,8 @@
.global-notice-list { .global-notice-list {
position: fixed; position: fixed;
top: 50px; inset-block-start: 50px;
width: 100%; inline-size: 100%;
pointer-events: none; pointer-events: none;
z-index: 1001; z-index: 1001;
display: flex; display: flex;
@ -40,12 +40,12 @@
.global-notice { .global-notice {
pointer-events: auto; pointer-events: auto;
text-align: center; text-align: center;
width: 40em; inline-size: 40em;
max-width: calc(100% - 3em); max-inline-size: calc(100% - 3em);
display: flex; display: flex;
padding-left: 1.5em; padding-inline-start: 1.5em;
line-height: 2; line-height: 2;
margin-bottom: 0.5em; margin-block-end: 0.5em;
.notice-message { .notice-message {
flex: 1 1 100%; flex: 1 1 100%;
@ -87,7 +87,7 @@
} }
.close-notice { .close-notice {
padding-right: 0.2em; padding-inline-end: 0.2em;
.svg-inline--fa:hover { .svg-inline--fa:hover {
opacity: 0.6; opacity: 0.6;
} }

View file

@ -60,15 +60,15 @@
img { img {
display: block; display: block;
max-width: 100%; max-inline-size: 100%;
} }
} }
&-buttons-wrapper { &-buttons-wrapper {
margin-top: 10px; margin-block-start: 10px;
button { button {
margin-top: 5px; margin-block-start: 5px;
} }
} }
} }

View file

@ -40,15 +40,15 @@
flex-direction: row; flex-direction: row;
cursor: pointer; cursor: pointer;
overflow: hidden; overflow: hidden;
margin-top: 0.5em; margin-block-start: 0.5em;
.card-image { .card-image {
flex-shrink: 0; flex-shrink: 0;
width: 120px; inline-size: 120px;
max-width: 25%; max-inline-size: 25%;
img { img {
width: 100%; inline-size: 100%;
height: 100%; block-size: 100%;
object-fit: cover; object-fit: cover;
border-radius: $fallback--attachmentRadius; border-radius: $fallback--attachmentRadius;
border-radius: var(--attachmentRadius, $fallback--attachmentRadius); border-radius: var(--attachmentRadius, $fallback--attachmentRadius);
@ -56,7 +56,7 @@
} }
.card-content { .card-content {
max-height: 100%; max-block-size: 100%;
margin: 0.5em; margin: 0.5em;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -67,18 +67,20 @@
} }
.card-description { .card-description {
margin: 0.5em 0 0 0; margin-block: 0.5em 0;
margin-inline: 0 0;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
word-break: break-word; word-break: break-word;
line-height: 1.2em; line-height: 1.2em;
// cap description at 3 lines, the 1px is to clean up some stray pixels // cap description at 3 lines, the 1px is to clean up some stray pixels
// TODO: fancier fade-out at the bottom to show off that it's too long? // TODO: fancier fade-out at the bottom to show off that it's too long?
max-height: calc(1.2em * 3 - 1px); max-block-size: calc(1.2em * 3 - 1px);
} }
.nsfw-alert { .nsfw-alert {
margin: 2em 0; margin-block: 2em;
margin-inline: 0;
} }
color: $fallback--text; color: $fallback--text;

View file

@ -39,9 +39,9 @@ export default {
.list { .list {
&-item:not(:last-child) { &-item:not(:last-child) {
border-bottom: 1px solid; border-block-end: 1px solid;
border-bottom-color: $fallback--border; border-block-end-color: $fallback--border;
border-bottom-color: var(--border, $fallback--border); border-block-end-color: var(--border, $fallback--border);
} }
&-empty-content { &-empty-content {

View file

@ -72,19 +72,20 @@
.list-edit { .list-edit {
.input-wrap { .input-wrap {
display: flex; display: flex;
margin: 0.7em 0.5em 0.7em 0.5em; margin-block: 0.7em 0.7em;
margin-inline: 0.5em 0.5em;
input { input {
width: 100%; inline-size: 100%;
} }
} }
.search-icon { .search-icon {
margin-right: 0.3em; margin-inline-end: 0.3em;
} }
.member-list { .member-list {
padding-bottom: 0.7rem; padding-block-end: 0.7rem;
} }
.basic-user-card:hover, .basic-user-card:hover,
@ -96,9 +97,9 @@
.go-back-button { .go-back-button {
text-align: center; text-align: center;
line-height: 1; line-height: 1;
height: 100%; block-size: 100%;
align-self: start; align-self: start;
width: var(--__panel-heading-height-inner); inline-size: var(--__panel-heading-block-size-inner);
} }
.btn { .btn {

View file

@ -67,11 +67,11 @@
.list-new { .list-new {
.search-icon { .search-icon {
margin-right: 0.3em; margin-inline-end: 0.3em;
} }
.member-list { .member-list {
padding-bottom: 0.7rem; padding-block-end: 0.7rem;
} }
.basic-user-card:hover, .basic-user-card:hover,
@ -83,9 +83,9 @@
.go-back-button { .go-back-button {
text-align: center; text-align: center;
line-height: 1; line-height: 1;
height: 100%; block-size: 100%;
align-self: start; align-self: start;
width: var(--__panel-heading-height-inner); inline-size: var(--__panel-heading-block-size-inner);
} }
.btn { .btn {

View file

@ -31,15 +31,16 @@
.input-wrap { .input-wrap {
display: flex; display: flex;
margin: 0.7em 0.5em 0.7em 0.5em; margin-block: 0.7em 0.7em;
margin-inline: 0.5em 0.5em;
input { input {
width: 100%; inline-size: 100%;
} }
} }
.search-icon { .search-icon {
margin-right: 0.3em; margin-inline-end: 0.3em;
} }
</style> </style>

View file

@ -2,20 +2,20 @@
margin: 1em; margin: 1em;
table { table {
width:100%; inline-size:100%;
text-align: left; text-align: start;
padding-left:10px; padding-inline-start:10px;
padding-bottom:20px; padding-block-end:20px;
th, td { th, td {
width: 180px; inline-size: 180px;
max-width: 360px; max-inline-size: 360px;
overflow: hidden; overflow: hidden;
vertical-align: text-top; vertical-align: text-block-start;
} }
th+th, td+td { th+th, td+td {
width: auto; inline-size: auto;
} }
} }
} }

View file

@ -101,8 +101,8 @@
padding: 0.6em; padding: 0.6em;
.btn { .btn {
min-height: 2em; min-block-size: 2em;
width: 10em; inline-size: 10em;
} }
.register { .register {
@ -110,7 +110,7 @@
} }
.login-bottom { .login-bottom {
margin-top: 1.0em; margin-block-start: 1.0em;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
@ -120,17 +120,18 @@
.form-group { .form-group {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 0.3em 0.5em 0.6em; padding-block: 0.3em 0.6em;
padding-inline: 0.5em;
line-height:24px; line-height:24px;
} }
.form-bottom { .form-bottom {
display: flex; display: flex;
padding: 0.5em; padding: 0.5em;
height: 32px; block-size: 32px;
button { button {
width: 10em; inline-size: 10em;
} }
p { p {

View file

@ -115,9 +115,11 @@
<script src="./media_modal.js"></script> <script src="./media_modal.js"></script>
<style lang="scss"> <style lang="scss">
$modal-view-button-icon-height: 3em; @import '../../_logical.scss';
$modal-view-button-icon-half-height: calc(#{$modal-view-button-icon-height} / 2);
$modal-view-button-icon-width: 3em; $modal-view-button-icon-block-size: 3em;
$modal-view-button-icon-half-block-size: calc(#{$modal-view-button-icon-block-size} / 2);
$modal-view-button-icon-block-size: 3em;
$modal-view-button-icon-margin: 0.5em; $modal-view-button-icon-margin: 0.5em;
.modal-view.media-modal-view { .modal-view.media-modal-view {
@ -156,16 +158,16 @@ $modal-view-button-icon-margin: 0.5em;
overflow: hidden; overflow: hidden;
align-items: center; align-items: center;
flex-direction: column; flex-direction: column;
max-width: 100%; max-inline-size: 100%;
max-height: 100%; max-block-size: 100%;
width: 100%; inline-size: 100%;
height: 100%; block-size: 100%;
flex-grow: 1; flex-grow: 1;
justify-content: center; justify-content: center;
&-inner { &-inner {
width: 100%; inline-size: 100%;
height: 100%; block-size: 100%;
flex-grow: 1; flex-grow: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -178,24 +180,25 @@ $modal-view-button-icon-margin: 0.5em;
.counter { .counter {
/* Hardcoded since background is also hardcoded */ /* Hardcoded since background is also hardcoded */
color: white; color: white;
margin-top: 1em; margin-block-start: 1em;
text-shadow: 0 0 10px black, 0 0 10px black; text-shadow: 0 0 10px black, 0 0 10px black;
padding: 0.2em 2em; padding-block: 0.2em;
padding-inline: 2em;
} }
.description { .description {
flex: 0 0 auto; flex: 0 0 auto;
overflow-y: auto; @include overflow-block(auto);
min-height: 1em; min-block-size: 1em;
max-width: 500px; max-inline-size: 500px;
max-height: 9.5em; max-block-size: 9.5em;
word-break: break-word; word-break: break-word;
white-space: pre-line; white-space: pre-line;
} }
.modal-image { .modal-image {
max-width: 100%; max-inline-size: 100%;
max-height: 100%; max-block-size: 100%;
image-orientation: from-image; // NOTE: only FF supports this image-orientation: from-image; // NOTE: only FF supports this
animation: 0.1s cubic-bezier(0.7, 0, 1, 0.6) media-fadein; animation: 0.1s cubic-bezier(0.7, 0, 1, 0.6) media-fadein;
@ -205,8 +208,8 @@ $modal-view-button-icon-margin: 0.5em;
} }
.loading-spinner { .loading-spinner {
width: 100%; inline-size: 100%;
height: 100%; block-size: 100%;
position: absolute; position: absolute;
pointer-events: none; pointer-events: none;
display: flex; display: flex;
@ -228,15 +231,15 @@ $modal-view-button-icon-margin: 0.5em;
overflow: visible; overflow: visible;
cursor: pointer; cursor: pointer;
transition: opacity 333ms cubic-bezier(.4,0,.22,1); transition: opacity 333ms cubic-bezier(.4,0,.22,1);
height: $modal-view-button-icon-height; block-size: $modal-view-button-icon-block-size;
width: $modal-view-button-icon-width; inline-size: $modal-view-button-icon-block-size;
.button-icon { .button-icon {
position: absolute; position: absolute;
height: $modal-view-button-icon-height; block-size: $modal-view-button-icon-block-size;
width: $modal-view-button-icon-width; inline-size: $modal-view-button-icon-block-size;
font-size: 1rem; font-size: 1rem;
line-height: $modal-view-button-icon-height; line-height: $modal-view-button-icon-block-size;
color: #FFF; color: #FFF;
text-align: center; text-align: center;
background-color: rgba(0,0,0,.3); background-color: rgba(0,0,0,.3);
@ -246,42 +249,42 @@ $modal-view-button-icon-margin: 0.5em;
.modal-view-button-arrow { .modal-view-button-arrow {
position: absolute; position: absolute;
display: block; display: block;
top: 50%; inset-block-start: 50%;
margin-top: $modal-view-button-icon-half-height; margin-block-start: $modal-view-button-icon-half-block-size;
width: $modal-view-button-icon-width; inline-size: $modal-view-button-icon-block-size;
height: $modal-view-button-icon-height; block-size: $modal-view-button-icon-block-size;
.arrow-icon { .arrow-icon {
position: absolute; position: absolute;
top: 0; inset-block-start: 0;
line-height: $modal-view-button-icon-height; line-height: $modal-view-button-icon-block-size;
color: #FFF; color: #FFF;
text-align: center; text-align: center;
background-color: rgba(0,0,0,.3); background-color: rgba(0,0,0,.3);
} }
&--prev { &--prev {
left: 0; inset-inline-start: 0;
.arrow-icon { .arrow-icon {
left: $modal-view-button-icon-margin; inset-inline-start: $modal-view-button-icon-margin;
} }
} }
&--next { &--next {
right: 0; inset-inline-end: 0;
.arrow-icon { .arrow-icon {
right: $modal-view-button-icon-margin; inset-inline-end: $modal-view-button-icon-margin;
} }
} }
} }
.modal-view-button-hide { .modal-view-button-hide {
position: absolute; position: absolute;
top: 0; inset-block-start: 0;
right: 0; inset-inline-end: 0;
.button-icon { .button-icon {
top: $modal-view-button-icon-margin; inset-block-start: $modal-view-button-icon-margin;
right: $modal-view-button-icon-margin; inset-inline-end: $modal-view-button-icon-margin;
} }
} }
} }

View file

@ -15,11 +15,11 @@
.mention-avatar { .mention-avatar {
border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
width: 1.5em; inline-size: 1.5em;
height: 1.5em; block-size: 1.5em;
vertical-align: middle; vertical-align: middle;
user-select: none; user-select: none;
margin-right: 0.2em; margin-inline-end: 0.2em;
} }
.full { .full {
@ -27,14 +27,14 @@
display: inline-block; display: inline-block;
pointer-events: none; pointer-events: none;
opacity: 0; opacity: 0;
top: 100%; inset-block-start: 100%;
left: 0; inset-inline-start: 0;
height: 100%; block-size: 100%;
word-wrap: normal; word-wrap: normal;
white-space: nowrap; white-space: nowrap;
transition: opacity 0.2s ease; transition: opacity 0.2s ease;
z-index: 1; z-index: 1;
margin-top: 0.25em; margin-block-start: 0.25em;
padding: 0.5em; padding: 0.5em;
user-select: all; user-select: all;
} }
@ -66,7 +66,8 @@
opacity: 0.8; opacity: 0.8;
display: inline-block; display: inline-block;
line-height: 1; line-height: 1;
padding: 0 0.1em; padding-block: 0;
padding-inline: 0.1em;
vertical-align: -25%; vertical-align: -25%;
margin: 0; margin: 0;
} }

View file

@ -6,7 +6,7 @@
} }
.showMoreLess { .showMoreLess {
margin-left: 0.5em; margin-inline-start: 0.5em;
white-space: normal; white-space: normal;
color: var(--link); color: var(--link);
} }

View file

@ -95,14 +95,15 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_logical.scss';
.MobileNav { .MobileNav {
.mobile-nav { .mobile-nav {
display: grid; display: grid;
line-height: var(--navbar-height); line-height: var(--navbar-block-size);
grid-template-rows: 50px; grid-template-rows: 50px;
grid-template-columns: 2fr auto; grid-template-columns: 2fr auto;
width: 100%; inline-size: 100%;
box-sizing: border-box; box-sizing: border-box;
a { a {
color: var(--topBarLink, $fallback--link); color: var(--topBarLink, $fallback--link);
@ -110,7 +111,7 @@
} }
.mobile-inner-nav { .mobile-inner-nav {
width: 100%; inline-size: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
} }
@ -118,13 +119,15 @@
.mobile-nav-button { .mobile-nav-button {
display: inline-block; display: inline-block;
text-align: center; text-align: center;
padding: 0 1em; padding-block: 0;
padding-inline: 1em;
position: relative; position: relative;
cursor: pointer; cursor: pointer;
} }
.site-name { .site-name {
padding: 0 .3em; padding-block: 0;
padding-inline: .3em;
display: inline-block; display: inline-block;
} }
@ -135,24 +138,24 @@
.alert-dot { .alert-dot {
border-radius: 100%; border-radius: 100%;
height: 8px; block-size: 8px;
width: 8px; inline-size: 8px;
position: absolute; position: absolute;
left: calc(50% - 4px); inset-inline-start: calc(50% - 4px);
top: calc(50% - 4px); inset-block-start: calc(50% - 4px);
margin-left: 6px; margin-inline-start: 6px;
margin-top: -6px; margin-block-start: -6px;
background-color: $fallback--cRed; background-color: $fallback--cRed;
background-color: var(--badgeNotification, $fallback--cRed); background-color: var(--badgeNotification, $fallback--cRed);
} }
.mobile-notifications-drawer { .mobile-notifications-drawer {
width: 100%; inline-size: 100%;
height: 100vh; block-size: 100vb;
overflow-x: hidden; @include overflow-inline(hidden);
position: fixed; position: fixed;
top: 0; inset-block-start: 0;
left: 0; inset-inline-start: 0;
box-shadow: 1px 1px 4px rgba(0,0,0,.6); box-shadow: 1px 1px 4px rgba(0,0,0,.6);
box-shadow: var(--panelShadow); box-shadow: var(--panelShadow);
transition-property: transform; transition-property: transform;
@ -164,6 +167,9 @@
&.-closed { &.-closed {
transform: translateX(100%); transform: translateX(100%);
box-shadow: none; box-shadow: none;
[dir="rtl"] & {
transform: translate(-100%);
}
} }
} }
@ -172,8 +178,8 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
z-index: 1; z-index: 1;
width: 100%; inline-size: 100%;
height: 50px; block-size: 50px;
line-height: 50px; line-height: 50px;
position: absolute; position: absolute;
color: var(--topBarText); color: var(--topBarText);
@ -184,16 +190,16 @@
.title { .title {
font-size: 1.3em; font-size: 1.3em;
margin-left: 0.6em; margin-inline-start: 0.6em;
} }
} }
.mobile-notifications { .mobile-notifications {
margin-top: 50px; margin-block-start: 50px;
width: 100vw; inline-size: 100vi;
height: calc(100vh - var(--navbar-height)); block-size: calc(100vb - var(--navbar-block-size));
overflow-x: hidden; @include overflow-inline(hidden);
overflow-y: scroll; @include overflow-block(scroll);
color: $fallback--text; color: $fallback--text;
color: var(--text, $fallback--text); color: var(--text, $fallback--text);

View file

@ -13,15 +13,16 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_logical.scss';
.MobilePostButton { .MobilePostButton {
&.button-default { &.button-default {
width: 5em; inline-size: 5em;
height: 5em; block-size: 5em;
border-radius: 100%; border-radius: 100%;
position: fixed; position: fixed;
bottom: 1.5em; inset-block-end: 1.5em;
right: 1.5em; inset-inline-end: 1.5em;
// TODO: this needs its own color, it has to stand out enough and link color // TODO: this needs its own color, it has to stand out enough and link color
// is not very optimal for this particular use. // is not very optimal for this particular use.
background-color: $fallback--fg; background-color: $fallback--fg;
@ -52,5 +53,10 @@
display: none; display: none;
} }
} }
@media all and (min-height: 801px) {
[class^=#{$writing-mode-vertical}] .new-status-button:not(.always-show) {
display: none;
}
}
</style> </style>

View file

@ -1,4 +1,5 @@
@import 'src/_variables.scss'; @import 'src/_variables.scss';
@import 'src/_logical.scss';
.mod-modal { .mod-modal {
overflow: hidden; overflow: hidden;
@ -20,6 +21,19 @@
*/ */
transform: translateY(calc(100% - 50px)); transform: translateY(calc(100% - 50px));
} }
.#{$writing-mode-vertical}rl & {
transform: translateX(calc(50px - ((100vw - 100%) / 2 + 100%)));
@media all and (max-height: 800px) {
transform: translateX(calc(50px - 100%));
}
}
.#{$writing-mode-vertical}lr & {
transform: translateX(calc(((100vw - 100%) / 2 + 100%) - 50px));
@media all and (max-height: 800px) {
transform: translateX(calc(100% - 50px));
}
}
} }
} }
@ -28,17 +42,23 @@
transition: transform; transition: transform;
transition-timing-function: ease-in-out; transition-timing-function: ease-in-out;
transition-duration: 300ms; transition-duration: 300ms;
width: 1000px; inline-size: 1000px;
max-width: 90vw; max-inline-size: 90vi;
height: 90vh; block-size: 90vb;
@media all and (max-width: 800px) { @media all and (max-width: 800px) {
max-width: 100vw; max-inline-size: 100vi;
height: 100%; block-size: 100%;
}
@media all and (max-height: 800px) {
[class^=#{$writing-mode-vertical}] & {
max-inline-size: 100vi;
block-size: 100%;
}
} }
.panel-body { .panel-body {
height: inherit; block-size: inherit;
} }
} }
} }

View file

@ -1,21 +1,22 @@
@import 'src/_variables.scss'; @import 'src/_variables.scss';
.mod_tab-switcher { .mod_tab-switcher {
height: 100%; block-size: 100%;
.content { .content {
margin: 1em 1em 1.4em; margin-block: 1em 1.4em;
margin-inline: 1em;
> div { > div {
margin-bottom: .5em; margin-block-end: .5em;
&:last-child { &:last-child {
margin-bottom: 0; margin-block-end: 0;
} }
} }
textarea { textarea {
width: 100%; inline-size: 100%;
max-width: 100%; max-inline-size: 100%;
height: 100px; block-size: 100px;
} }
} }
} }

View file

@ -6,8 +6,8 @@
} }
& > :not(:last-child) { & > :not(:last-child) {
border-bottom: 1px solid; border-block-end: 1px solid;
border-bottom-color: var(--border, #222); border-block-end-color: var(--border, #222);
} }
.report-content { .report-content {
@ -15,12 +15,12 @@
} }
.report-author { .report-author {
padding-top: 0.5em; padding-block-start: 0.5em;
} }
.small-avatar { .small-avatar {
height: 25px; block-size: 25px;
width: 25px; inline-size: 25px;
padding-right: 0.4em; padding-inline-end: 0.4em;
vertical-align: middle; vertical-align: middle;
} }
@ -46,11 +46,11 @@
.note-header { .note-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
padding-bottom: 0.5em; padding-block-end: 0.5em;
} }
button { button {
margin-left: 0.5em; margin-inline-start: 0.5em;
} }
} }
@ -60,10 +60,11 @@
} }
button { button {
min-height: 2em; min-block-size: 2em;
min-width: 10em; min-inline-size: 10em;
padding: 0 2em; padding-block: 0;
margin-top: 0.5em; padding-inline: 2em;
margin-block-start: 0.5em;
} }
} }
} }
@ -71,7 +72,7 @@
.panel-footer { .panel-footer {
display: flex; display: flex;
& > * { & > * {
margin-right: 0.5em; margin-inline-end: 0.5em;
} }
} }
} }

View file

@ -37,10 +37,10 @@ export default {
.modal-view { .modal-view {
z-index: 2000; z-index: 2000;
position: fixed; position: fixed;
top: 0; inset-block-start: 0;
left: 0; inset-inline-start: 0;
right: 0; inset-inline-end: 0;
bottom: 0; inset-block-end: 0;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;

View file

@ -166,11 +166,11 @@
@import '../../_variables.scss'; @import '../../_variables.scss';
.moderation-tools-popover { .moderation-tools-popover {
height: 100%; block-size: 100%;
z-index: 999; z-index: 999;
.trigger { .trigger {
display: flex !important; display: flex !important;
height: 100%; block-size: 100%;
} }
} }

View file

@ -2,20 +2,20 @@
margin: 1em; margin: 1em;
table { table {
width:100%; inline-size:100%;
text-align: left; text-align: start;
padding-left:10px; padding-inline-start:10px;
padding-bottom:20px; padding-block-end:20px;
th, td { th, td {
width: 180px; inline-size: 180px;
max-width: 360px; max-inline-size: 360px;
overflow: hidden; overflow: hidden;
vertical-align: text-top; vertical-align: text-block-start;
} }
th+th, td+td { th+th, td+td {
width: auto; inline-size: auto;
} }
} }
} }

View file

@ -35,10 +35,10 @@
<style lang="scss"> <style lang="scss">
.mute-card-content-container { .mute-card-content-container {
margin-top: 0.5em; margin-block-start: 0.5em;
text-align: right; text-align: end;
button { button {
width: 10em; inline-size: 10em;
} }
} }
</style> </style>

View file

@ -33,6 +33,11 @@ library.add(
) )
const NavPanel = { const NavPanel = {
created () {
if (this.currentUser && this.currentUser.locked) {
this.$store.dispatch('startFetchingFollowRequests')
}
},
components: { components: {
TimelineMenuContent TimelineMenuContent
}, },
@ -49,13 +54,11 @@ const NavPanel = {
computed: { computed: {
...mapState({ ...mapState({
currentUser: state => state.users.currentUser, currentUser: state => state.users.currentUser,
followRequestCount: state => state.api.followRequests.length,
privateMode: state => state.instance.private, privateMode: state => state.instance.private,
federating: state => state.instance.federating federating: state => state.instance.federating
}), }),
...mapGetters(['unreadAnnouncementCount']), ...mapGetters(['unreadAnnouncementCount'])
followRequestCount () {
return this.$store.state.users.currentUser.follow_requests_count
}
} }
} }

View file

@ -121,23 +121,23 @@
li { li {
position: relative; position: relative;
border-bottom: 1px solid; border-block-end: 1px solid;
border-color: $fallback--border; border-color: $fallback--border;
border-color: var(--border, $fallback--border); border-color: var(--border, $fallback--border);
padding: 0; padding: 0;
&:first-child .menu-item { &:first-child .menu-item {
border-top-right-radius: $fallback--panelRadius; border-start-end-radius: $fallback--panelRadius;
border-top-right-radius: var(--panelRadius, $fallback--panelRadius); border-start-end-radius: var(--panelRadius, $fallback--panelRadius);
border-top-left-radius: $fallback--panelRadius; border-start-start-radius: $fallback--panelRadius;
border-top-left-radius: var(--panelRadius, $fallback--panelRadius); border-start-start-radius: var(--panelRadius, $fallback--panelRadius);
} }
&:last-child .menu-item { &:last-child .menu-item {
border-bottom-right-radius: $fallback--panelRadius; border-end-end-radius: $fallback--panelRadius;
border-bottom-right-radius: var(--panelRadius, $fallback--panelRadius); border-end-end-radius: var(--panelRadius, $fallback--panelRadius);
border-bottom-left-radius: $fallback--panelRadius; border-end-start-radius: $fallback--panelRadius;
border-bottom-left-radius: var(--panelRadius, $fallback--panelRadius); border-end-start-radius: var(--panelRadius, $fallback--panelRadius);
} }
} }
@ -148,10 +148,11 @@
.menu-item { .menu-item {
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
height: 3.5em; block-size: 3.5em;
line-height: 3.5em; line-height: 3.5em;
padding: 0 1em; padding-block: 0;
width: 100%; padding-inline: 1em;
inline-size: 100%;
color: $fallback--link; color: $fallback--link;
color: var(--link, $fallback--link); color: var(--link, $fallback--link);
@ -184,15 +185,16 @@
} }
.timelines-chevron { .timelines-chevron {
margin-left: 0.8em; margin-inline-start: 0.8em;
font-size: 1.1em; font-size: 1.1em;
} }
.timelines-background { .timelines-background {
padding: 0 0 0 0.6em; padding-block: 0 0;
padding-inline: 0.6em 0;
background-color: $fallback--lightBg; background-color: $fallback--lightBg;
background-color: var(--selectedMenu, $fallback--lightBg); background-color: var(--selectedMenu, $fallback--lightBg);
border-top: 1px solid; border-block-start: 1px solid;
border-color: $fallback--border; border-color: $fallback--border;
border-color: var(--border, $fallback--border); border-color: var(--border, $fallback--border);
} }
@ -203,13 +205,13 @@
} }
.fa-scale-110 { .fa-scale-110 {
margin-right: 0.8em; margin-inline-end: 0.8em;
} }
.badge { .badge {
position: absolute; position: absolute;
right: 0.6rem; inset-inline-end: 0.6rem;
top: 1.25em; inset-block-start: 1.25em;
} }
} }
</style> </style>

View file

@ -1,7 +1,7 @@
@import '../../_variables.scss'; @import '../../_variables.scss';
.notification-reaction-emoji { .notification-reaction-emoji {
width: 40px; inline-size: 40px;
display: inline-flex; display: inline-flex;
vertical-align: middle; vertical-align: middle;
flex-direction: column; flex-direction: column;
@ -9,7 +9,7 @@
// TODO Copypaste from Status, should unify it somehow // TODO Copypaste from Status, should unify it somehow
.Notification { .Notification {
border-bottom: 1px solid; border-block-end: 1px solid;
border-color: $fallback--border; border-color: $fallback--border;
border-color: var(--border, $fallback--border); border-color: var(--border, $fallback--border);
word-wrap: break-word; word-wrap: break-word;
@ -23,8 +23,9 @@
} }
&.-muted { &.-muted {
padding: 0.25em 0.6em; padding-block: 0.25em;
height: 1.2em; padding-inline: 0.6em;
block-size: 1.2em;
line-height: 1.2em; line-height: 1.2em;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
@ -48,7 +49,7 @@
.status-username { .status-username {
font-weight: normal; font-weight: normal;
flex: 0 1 auto; flex: 0 1 auto;
margin-right: 0.2em; margin-inline-end: 0.2em;
font-size: smaller; font-size: smaller;
} }
@ -58,7 +59,7 @@
.mute-words { .mute-words {
flex: 1 0 5em; flex: 1 0 5em;
margin-left: 0.2em; margin-inline-start: 0.2em;
&::before { &::before {
content: ' '; content: ' ';
@ -67,13 +68,14 @@
.unmute { .unmute {
flex: 0 0 auto; flex: 0 0 auto;
margin-left: auto; margin-inline-start: auto;
display: block; display: block;
} }
} }
.type-icon { .type-icon {
margin: 0 0.1em; margin-block: 0;
margin-inline: 0.1em;
} }
&.-type--repeat .type-icon { &.-type--repeat .type-icon {

View file

@ -117,8 +117,8 @@ export default {
> button { > button {
line-height: 100%; line-height: 100%;
height: 100%; block-size: 100%;
width: var(--__panel-heading-height-inner); inline-size: var(--__panel-heading-block-size-inner);
text-align: center; text-align: center;
svg { svg {

View file

@ -3,7 +3,7 @@
.Notifications { .Notifications {
&:not(.minimal) { &:not(.minimal) {
// a bit of a hack to allow scrolling below notifications // a bit of a hack to allow scrolling below notifications
padding-bottom: 15em; padding-block-end: 15em;
} }
.loadmore-error { .loadmore-error {
@ -16,10 +16,10 @@
.notification-overlay { .notification-overlay {
position: absolute; position: absolute;
top: 0; inset-block-start: 0;
right: 0; inset-inline-end: 0;
left: 0; inset-inline-start: 0;
bottom: 0; inset-block-end: 0;
pointer-events: none; pointer-events: none;
} }
@ -44,7 +44,7 @@
} }
&:last-child .Notification { &:last-child .Notification {
border-bottom: none; border-block-end: none;
} }
.non-mention { .non-mention {
@ -52,11 +52,11 @@
flex: 1; flex: 1;
flex-wrap: nowrap; flex-wrap: nowrap;
padding: 0.6em; padding: 0.6em;
min-width: 0; min-inline-size: 0;
.avatar-container { .avatar-container {
width: 32px; inline-size: 32px;
height: 32px; block-size: 32px;
} }
--link: var(--faintLink); --link: var(--faintLink);
@ -78,14 +78,15 @@
} }
.follow-text, .move-text { .follow-text, .move-text {
padding: 0.5em 0; padding-block: 0.5em;
padding-inline: 0;
overflow-wrap: break-word; overflow-wrap: break-word;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
.follow-name { .follow-name {
display: block; display: block;
max-width: 100%; max-inline-size: 100%;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@ -103,12 +104,12 @@
.notification-right { .notification-right {
flex: 1; flex: 1;
padding-left: 0.8em; padding-inline-start: 0.8em;
min-width: 0; min-inline-size: 0;
.timeago { .timeago {
min-width: 3em; min-inline-size: 3em;
text-align: right; text-align: end;
} }
} }
@ -117,12 +118,12 @@
} }
.notification-details { .notification-details {
min-width: 0; min-inline-size: 0;
word-wrap: break-word; word-wrap: break-word;
line-height: var(--post-line-height); line-height: var(--post-line-height);
position: relative; position: relative;
overflow: hidden; overflow: hidden;
width: 100%; inline-size: 100%;
flex: 1 1 0; flex: 1 1 0;
display: flex; display: flex;
flex-wrap: nowrap; flex-wrap: nowrap;
@ -136,23 +137,24 @@
.username { .username {
font-weight: bolder; font-weight: bolder;
max-width: 100%; max-inline-size: 100%;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.timeago { .timeago {
margin-right: 0.2em; margin-inline-end: 0.2em;
} }
.status-content { .status-content {
margin: 0; margin: 0;
max-height: 300px; max-block-size: 300px;
} }
h1 { h1 {
word-break: break-all; word-break: break-all;
margin: 0 0 0.3em; margin-block: 0 0.3em;
margin-inline: 0;
padding: 0; padding: 0;
font-size: 1em; font-size: 1em;
line-height: 1.5; line-height: 1.5;
@ -164,8 +166,8 @@
p { p {
margin: 0; margin: 0;
margin-top: 0; margin-block-start: 0;
margin-bottom: 0.3em; margin-block-end: 0.3em;
} }
} }
} }

View file

@ -27,7 +27,7 @@ export default {}
.panel-loading { .panel-loading {
display: flex; display: flex;
height: 100%; block-size: 100%;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: 2em; font-size: 2em;

View file

@ -89,19 +89,20 @@
display: flex; display: flex;
flex: 1 0; flex: 1 0;
flex-direction: column; flex-direction: column;
margin-top: 0.6em; margin-block-start: 0.6em;
max-width: 18rem; max-inline-size: 18rem;
> * { > * {
min-width: 0; min-inline-size: 0;
} }
} }
.form-group { .form-group {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-bottom: 1em; margin-block-end: 1em;
padding: 0.3em 0; padding-block: 0.3em;
padding-inline: 0;
line-height: 1.85em; line-height: 1.85em;
} }
@ -114,16 +115,18 @@
.alert { .alert {
padding: 0.5em; padding: 0.5em;
margin: 0.3em 0 1em; margin-block: 0.3em 1em;
margin-inline: 0;
} }
.password-reset-required { .password-reset-required {
background-color: var(--alertError, $fallback--alertError); background-color: var(--alertError, $fallback--alertError);
padding: 10px 0; padding-block: 10px;
padding-inline: 0;
} }
.notice-dismissible { .notice-dismissible {
padding-right: 2rem; padding-inline-end: 2rem;
} }
.dismiss { .dismiss {

View file

@ -1,4 +1,4 @@
import PinchZoom from '@floatingghost/pinch-zoom-element' import PinchZoom from '@kazvmoe-infra/pinch-zoom-element'
export default { export default {
methods: { methods: {

View file

@ -25,7 +25,7 @@
</div> </div>
<div <div
class="result-fill" class="result-fill"
:style="{ 'width': `${percentageForOption(option.votes_count)}%` }" :style="{ 'block-size': `${percentageForOption(option.votes_count)}%` }"
/> />
</div> </div>
<div <div
@ -96,13 +96,15 @@
.votes { .votes {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin: 0 0 0.5em; margin-block: 0 0.5em;
margin-inline: 0;
} }
.poll-option { .poll-option {
margin: 0.75em 0.5em; margin-block: 0.75em;
margin-inline: 0.5em;
} }
.option-result { .option-result {
height: 100%; block-size: 100%;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
position: relative; position: relative;
@ -112,16 +114,17 @@
.option-result-label { .option-result-label {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0.1em 0.25em; padding-block: 0.1em;
padding-inline: 0.25em;
z-index: 1; z-index: 1;
word-break: break-word; word-break: break-word;
} }
.result-percentage { .result-percentage {
width: 3.5em; inline-size: 3.5em;
flex-shrink: 0; flex-shrink: 0;
} }
.result-fill { .result-fill {
height: 100%; block-size: 100%;
position: absolute; position: absolute;
color: $fallback--text; color: $fallback--text;
color: var(--pollText, $fallback--text); color: var(--pollText, $fallback--text);
@ -129,16 +132,16 @@
background-color: var(--poll, $fallback--lightBg); background-color: var(--poll, $fallback--lightBg);
border-radius: $fallback--panelRadius; border-radius: $fallback--panelRadius;
border-radius: var(--panelRadius, $fallback--panelRadius); border-radius: var(--panelRadius, $fallback--panelRadius);
top: 0; inset-block-start: 0;
left: 0; inset-inline-start: 0;
transition: width 0.5s; transition: block-size 0.5s;
} }
.option-vote { .option-vote {
display: flex; display: flex;
align-items: center; align-items: center;
} }
input { input {
width: 3.5em; inline-size: 3.5em;
} }
.footer { .footer {
display: flex; display: flex;
@ -148,8 +151,9 @@
cursor: progress; cursor: progress;
} }
.poll-vote-button { .poll-vote-button {
padding: 0 0.5em; padding-block: 0;
margin-right: 0.5em; padding-inline: 0.5em;
margin-inline-end: 0.5em;
} }
} }
</style> </style>

View file

@ -100,49 +100,50 @@
.poll-form { .poll-form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 0 0.5em 0.5em; padding-block: 0 0.5em;
padding-inline: 0.5em;
.add-option { .add-option {
align-self: flex-start; align-self: flex-start;
padding-top: 0.25em; padding-block-start: 0.25em;
padding-left: 0.1em; padding-inline-start: 0.1em;
} }
.poll-option { .poll-option {
display: flex; display: flex;
align-items: baseline; align-items: baseline;
justify-content: space-between; justify-content: space-between;
margin-bottom: 0.25em; margin-block-end: 0.25em;
} }
.input-container { .input-container {
width: 100%; inline-size: 100%;
input { input {
// Hack: dodge the floating X icon // Hack: dodge the floating X icon
padding-right: 2.5em; padding-inline-end: 2.5em;
width: 100%; inline-size: 100%;
} }
} }
.delete-option { .delete-option {
// Hack: Move the icon over the input box // Hack: Move the icon over the input box
width: 1.5em; inline-size: 1.5em;
margin-left: -1.5em; margin-inline-start: -1.5em;
z-index: 1; z-index: 1;
} }
.poll-type-expiry { .poll-type-expiry {
margin-top: 0.5em; margin-block-start: 0.5em;
display: flex; display: flex;
width: 100%; inline-size: 100%;
} }
.poll-type { .poll-type {
margin-right: 0.75em; margin-inline-end: 0.75em;
flex: 1 1 60%; flex: 1 1 60%;
.poll-type-select { .poll-type-select {
padding-right: 0.75em; padding-inline-end: 0.75em;
} }
} }
@ -150,8 +151,8 @@
display: flex; display: flex;
.expiry-amount { .expiry-amount {
width: 3em; inline-size: 3em;
text-align: right; text-align: end;
} }
} }
} }

View file

@ -1,3 +1,17 @@
import {
getViewport,
isVertical,
isVerticalRL,
inlineStartOf,
inlineEndOf,
blockStartOf,
blockEndOf,
inlineSizeOf,
blockSizeOf,
offsetInlineSizeOf,
offsetBlockSizeOf
} from '../../lib/logical_dimensions'
const Popover = { const Popover = {
name: 'Popover', name: 'Popover',
props: { props: {
@ -53,51 +67,62 @@ const Popover = {
return return
} }
const viewport = getViewport()
const isRTL = this.$t('_locale.dir') == "rtl"
const isVrt = isVertical()
const isVRL = isVerticalRL()
// Popover will be anchored around this element, trigger ref is the container, so // Popover will be anchored around this element, trigger ref is the container, so
// its children are what are inside the slot. Expect only one v-slot:trigger. // 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 const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el
// SVGs don't have offsetWidth/Height, use fallback // SVGs don't have offsetWidth/Height, use fallback
const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth
const anchorHeight = anchorEl.offsetHeight || anchorEl.clientHeight const anchorHeight = anchorEl.offsetHeight || anchorEl.clientHeight
const anchorInlineSize = isVrt ? anchorHeight : anchorWidth
const anchorBlockSize = isVrt ? anchorWidth : anchorHeight
const screenBox = anchorEl.getBoundingClientRect() const screenBox = anchorEl.getBoundingClientRect()
// Screen position of the origin point for popover // Screen position of the origin point for popover
const origin = { x: screenBox.left + screenBox.width * 0.5, y: screenBox.top } const origin = { x: inlineStartOf(screenBox, isRTL) + inlineSizeOf(screenBox) * 0.5, y: blockStartOf(screenBox) }
const content = this.$refs.content const content = this.$refs.content
// Minor optimization, don't call a slow reflow call if we don't have to // Minor optimization, don't call a slow reflow call if we don't have to
const parentBounds = this.boundTo && const parentBounds = this.boundTo &&
(this.boundTo.x === 'container' || this.boundTo.y === 'container') && (this.boundTo.x === 'container' || this.boundTo.y === 'container') &&
this.containerBoundingClientRect() this.containerBoundingClientRect()
// margin is repurposed as an abstract value set for compatibility, so that:
// left = inline start, right = inline end, top = block start, and bottom = block end
const margin = this.margin || {} const margin = this.margin || {}
// What are the screen bounds for the popover? Viewport vs container // What are the screen bounds for the popover? Viewport vs container
// when using viewport, using default margin values to dodge the navbar // when using viewport, using default margin values to dodge the navbar
const xBounds = this.boundTo && this.boundTo.x === 'container' ? { const xBounds = this.boundTo && this.boundTo.x === 'container' ? {
min: parentBounds.left + (margin.left || 0), min: inlineStartOf(parentBounds, isRTL) + (margin.left || 0),
max: parentBounds.right - (margin.right || 0) max: inlineEndOf(parentBounds, isRTL) - (margin.right || 0)
} : { } : {
min: 0 + (margin.left || 10), min: 0 + (margin.left || 10),
max: window.innerWidth - (margin.right || 10) max: inlineSizeOf(viewport) - (margin.right || 10)
} }
const yBounds = this.boundTo && this.boundTo.y === 'container' ? { const yBounds = this.boundTo && this.boundTo.y === 'container' ? {
min: parentBounds.top + (margin.top || 0), min: blockStartOf(parentBounds) + (margin.top || 0),
max: parentBounds.bottom - (margin.bottom || 0) max: blockEndOf(parentBounds) - (margin.bottom || 0)
} : { } : {
min: 0 + (margin.top || 50), min: 0 + (margin.top || 50),
max: window.innerHeight - (margin.bottom || 5) max: blockSizeOf(viewport) - (margin.bottom || 5)
} }
let horizOffset = 0 let horizOffset = 0
const contentOffsetInlineSize = offsetInlineSizeOf(content)
const contentOffsetBlockSize = offsetBlockSizeOf(content)
// If overflowing from left, move it so that it doesn't // If overflowing from left, move it so that it doesn't
if ((origin.x - content.offsetWidth * 0.5) < xBounds.min) { if ((origin.x - contentOffsetInlineSize * 0.5) < xBounds.min) {
horizOffset += -(origin.x - content.offsetWidth * 0.5) + xBounds.min horizOffset += -(origin.x - contentOffsetInlineSize * 0.5) + xBounds.min
} }
// If overflowing from right, move it so that it doesn't // If overflowing from right, move it so that it doesn't
if ((origin.x + horizOffset + content.offsetWidth * 0.5) > xBounds.max) { if ((origin.x + horizOffset + contentOffsetInlineSize * 0.5) > xBounds.max) {
horizOffset -= (origin.x + horizOffset + content.offsetWidth * 0.5) - xBounds.max horizOffset -= (origin.x + horizOffset + contentOffsetInlineSize * 0.5) - xBounds.max
} }
// Default to whatever user wished with placement prop // Default to whatever user wished with placement prop
@ -106,28 +131,32 @@ const Popover = {
// Handle special cases, first force to displaying on top if there's not space on 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 // 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. // force to bottom, again regardless of what placement value was.
if (origin.y + content.offsetHeight > yBounds.max) usingTop = true if (origin.y + contentOffsetBlockSize > yBounds.max) usingTop = true
if (origin.y - content.offsetHeight < yBounds.min) usingTop = false if (origin.y - contentOffsetBlockSize < yBounds.min) usingTop = false
let vPadding = 0 let vPadding = 0
if (this.removePadding && usingTop) { if (this.removePadding && usingTop) {
const anchorStyle = getComputedStyle(anchorEl) const anchorStyle = getComputedStyle(anchorEl)
vPadding = parseFloat(anchorStyle.paddingTop) + parseFloat(anchorStyle.paddingBottom) vPadding = parseFloat(anchorStyle.paddingBlockStart) + parseFloat(anchorStyle.paddingBlockEnd)
} }
// offset is repurposed as an abstract value set, as well as margin
const yOffset = (this.offset && this.offset.y) || 0 const yOffset = (this.offset && this.offset.y) || 0
const translateY = usingTop const translateY = usingTop
? -anchorHeight + vPadding - yOffset - content.offsetHeight ? -anchorBlockSize + vPadding - yOffset - contentOffsetBlockSize
: yOffset : yOffset
const translateYLogical = isVRL ? -translateY : translateY
const xOffset = (this.offset && this.offset.x) || 0 const xOffset = (this.offset && this.offset.x) || 0
const translateX = anchorWidth * 0.5 - content.offsetWidth * 0.5 + horizOffset + xOffset const translateX = anchorInlineSize * 0.5 - contentOffsetInlineSize * 0.5 + horizOffset + xOffset
const translateXLogical = isRTL ? -translateX : translateX
const finalTrans = isVrt ? { x: translateYLogical, y: translateXLogical } : { x: translateXLogical, y: translateYLogical }
// Note, separate translateX and translateY avoids blurry text on chromium, // Note, separate translateX and translateY avoids blurry text on chromium,
// single translate or translate3d resulted in blurry text. // single translate or translate3d resulted in blurry text.
this.styles = { this.styles = {
opacity: 1, opacity: 1,
transform: `translateX(${Math.round(translateX)}px) translateY(${Math.round(translateY)}px)` transform: `translateX(${Math.round(finalTrans.x)}px) translateY(${Math.round(finalTrans.y)}px)`
} }
}, },
showPopover () { showPopover () {

View file

@ -39,7 +39,7 @@
.popover { .popover {
z-index: 500; z-index: 500;
position: absolute; position: absolute;
min-width: 0; min-inline-size: 0;
} }
.popover-default { .popover-default {
@ -48,10 +48,10 @@
&:after { &:after {
content: ''; content: '';
position: absolute; position: absolute;
top: 0; inset-block-start: 0;
bottom: 0; inset-block-end: 0;
left: 0; inset-inline-start: 0;
right: 0; inset-inline-end: 0;
z-index: 3; z-index: 3;
box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6); box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6);
box-shadow: var(--panelShadow); box-shadow: var(--panelShadow);
@ -75,27 +75,30 @@
.dropdown-menu { .dropdown-menu {
display: block; display: block;
padding: .5rem 0; padding-block: .5rem;
padding-inline: 0;
font-size: 1em; font-size: 1em;
text-align: left; text-align: start;
list-style: none; list-style: none;
max-width: 100vw; max-inline-size: 100vi;
z-index: 200; z-index: 200;
white-space: nowrap; white-space: nowrap;
.dropdown-divider { .dropdown-divider {
height: 0; block-size: 0;
margin: .5rem 0; margin-block: .5rem;
margin-inline: 0;
overflow: hidden; overflow: hidden;
border-top: 1px solid $fallback--border; border-block-start: 1px solid $fallback--border;
border-top: 1px solid var(--border, $fallback--border); border-block-start: 1px solid var(--border, $fallback--border);
} }
.dropdown-item { .dropdown-item {
line-height: 21px; line-height: 21px;
overflow: hidden; overflow: hidden;
display: block; display: block;
padding: 0.5em 0.75em; padding-block: 0.5em;
padding-inline: 0.75em;
clear: both; clear: both;
font-weight: 400; font-weight: 400;
text-align: inherit; text-align: inherit;
@ -104,16 +107,16 @@
border-radius: 0px; border-radius: 0px;
background-color: transparent; background-color: transparent;
box-shadow: none; box-shadow: none;
width: 100%; inline-size: 100%;
height: 100%; block-size: 100%;
box-sizing: border-box; box-sizing: border-box;
--btnText: var(--popoverText, $fallback--text); --btnText: var(--popoverText, $fallback--text);
&-icon { &-icon {
svg { svg {
width: 22px; inline-size: 22px;
margin-right: 0.75rem; margin-inline-end: 0.75rem;
color: var(--menuPopoverIcon, $fallback--icon) color: var(--menuPopoverIcon, $fallback--icon)
} }
} }
@ -136,10 +139,10 @@
.menu-checkbox { .menu-checkbox {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
min-width: 22px; min-inline-size: 22px;
max-width: 22px; max-inline-size: 22px;
min-height: 22px; min-block-size: 22px;
max-height: 22px; max-block-size: 22px;
line-height: 22px; line-height: 22px;
text-align: center; text-align: center;
border-radius: 0px; border-radius: 0px;
@ -147,7 +150,7 @@
background-color: var(--input, $fallback--fg); background-color: var(--input, $fallback--fg);
box-shadow: 0px 0px 2px black inset; box-shadow: 0px 0px 2px black inset;
box-shadow: var(--inputShadow); box-shadow: var(--inputShadow);
margin-right: 0.75em; margin-inline-end: 0.75em;
&.menu-checkbox-checked::after { &.menu-checkbox-checked::after {
font-size: 1.25em; font-size: 1.25em;

View file

@ -13,7 +13,6 @@ import suggestor from '../emoji_input/suggestor.js'
import { mapGetters, mapState } from 'vuex' import { mapGetters, mapState } from 'vuex'
import Checkbox from '../checkbox/checkbox.vue' import Checkbox from '../checkbox/checkbox.vue'
import Select from '../select/select.vue' import Select from '../select/select.vue'
import iso6391 from 'iso-639-1'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
@ -64,7 +63,6 @@ const PostStatusForm = {
'statusMediaDescriptions', 'statusMediaDescriptions',
'statusScope', 'statusScope',
'statusContentType', 'statusContentType',
'statusLanguage',
'replyTo', 'replyTo',
'quoteId', 'quoteId',
'repliedUser', 'repliedUser',
@ -130,7 +128,7 @@ const PostStatusForm = {
statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser) statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser)
} }
const { postContentType: contentType, sensitiveByDefault, sensitiveIfSubject, interfaceLanguage } = this.$store.getters.mergedConfig const { postContentType: contentType, sensitiveByDefault, sensitiveIfSubject } = this.$store.getters.mergedConfig
let statusParams = { let statusParams = {
spoilerText: this.subject || '', spoilerText: this.subject || '',
@ -141,7 +139,6 @@ const PostStatusForm = {
poll: {}, poll: {},
mediaDescriptions: {}, mediaDescriptions: {},
visibility: this.suggestedVisibility(), visibility: this.suggestedVisibility(),
language: interfaceLanguage,
contentType contentType
} }
@ -156,7 +153,6 @@ const PostStatusForm = {
poll: this.statusPoll || {}, poll: this.statusPoll || {},
mediaDescriptions: this.statusMediaDescriptions || {}, mediaDescriptions: this.statusMediaDescriptions || {},
visibility: this.statusScope || this.suggestedVisibility(), visibility: this.statusScope || this.suggestedVisibility(),
language: this.statusLanguage || interfaceLanguage,
contentType: statusContentType contentType: statusContentType
} }
} }
@ -263,10 +259,7 @@ const PostStatusForm = {
...mapGetters(['mergedConfig']), ...mapGetters(['mergedConfig']),
...mapState({ ...mapState({
mobileLayout: state => state.interface.mobileLayout mobileLayout: state => state.interface.mobileLayout
}), })
isoLanguages () {
return iso6391.getAllCodes();
}
}, },
watch: { watch: {
'newStatus': { 'newStatus': {
@ -289,7 +282,6 @@ const PostStatusForm = {
files: [], files: [],
visibility: newStatus.visibility, visibility: newStatus.visibility,
contentType: newStatus.contentType, contentType: newStatus.contentType,
language: newStatus.language,
poll: {}, poll: {},
mediaDescriptions: {} mediaDescriptions: {}
} }
@ -349,7 +341,6 @@ const PostStatusForm = {
inReplyToStatusId: this.replyTo, inReplyToStatusId: this.replyTo,
quoteId: this.quoteId, quoteId: this.quoteId,
contentType: newStatus.contentType, contentType: newStatus.contentType,
language: newStatus.language,
poll, poll,
idempotencyKey: this.idempotencyKey idempotencyKey: this.idempotencyKey
} }
@ -384,7 +375,6 @@ const PostStatusForm = {
inReplyToStatusId: this.replyTo, inReplyToStatusId: this.replyTo,
quoteId: this.quoteId, quoteId: this.quoteId,
contentType: newStatus.contentType, contentType: newStatus.contentType,
language: newStatus.language,
poll: {}, poll: {},
preview: true preview: true
}).then((data) => { }).then((data) => {

View file

@ -194,23 +194,6 @@
:on-scope-change="changeVis" :on-scope-change="changeVis"
/> />
<div
class="language-selector"
>
<Select
id="post-language"
v-model="newStatus.language"
class="form-control"
>
<option
v-for="language in isoLanguages"
:key="language"
:value="language"
>
{{ language }}
</option>
</Select>
</div>
<div <div
v-if="postFormats.length > 1" v-if="postFormats.length > 1"
class="text-format" class="text-format"
@ -358,22 +341,23 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_logical.scss';
.post-status-form { .post-status-form {
position: relative; position: relative;
.attachments { .attachments {
margin-bottom: 0.5em; margin-block-end: 0.5em;
} }
.form-bottom { .form-bottom {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
padding: 0.5em; padding: 0.5em;
height: 2.5em; block-size: 2.5em;
button { button {
width: 10em; inline-size: 10em;
} }
p { p {
@ -386,14 +370,14 @@
.form-bottom-left { .form-bottom-left {
display: flex; display: flex;
flex: 1; flex: 1;
padding-right: 7px; padding-inline-end: 7px;
margin-right: 7px; margin-inline-end: 7px;
max-width: 10em; max-inline-size: 10em;
} }
.preview-heading { .preview-heading {
display: flex; display: flex;
padding-left: 0.5em; padding-inline-start: 0.5em;
} }
.preview-toggle { .preview-toggle {
@ -405,14 +389,14 @@
text-decoration: underline; text-decoration: underline;
} }
svg, i { svg, i {
margin-left: 0.2em; margin-inline-start: 0.2em;
font-size: 0.8em; font-size: 0.8em;
transform: rotate(90deg); transform: rotate(90deg);
} }
} }
.preview-container { .preview-container {
margin-bottom: 1em; margin-block-end: 1em;
} }
.preview-error { .preview-error {
@ -440,17 +424,17 @@
.visibility-tray { .visibility-tray {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
padding-top: 5px; padding-block-start: 5px;
align-items: baseline; align-items: baseline;
} }
.visibility-notice.edit-warning { .visibility-notice.edit-warning {
> :first-child { > :first-child {
margin-top: 0; margin-block-start: 0;
} }
> :last-child { > :last-child {
margin-bottom: 0; margin-block-end: 0;
} }
} }
@ -458,7 +442,8 @@
font-size: 1.85em; font-size: 1.85em;
line-height: 1.1; line-height: 1.1;
flex: 1; flex: 1;
padding: 0 0.1em; padding-block: 0;
padding-inline: 0.1em;
display: flex; display: flex;
align-items: center; align-items: center;
@ -487,7 +472,7 @@
// Order is not necessary but a good indicator // Order is not necessary but a good indicator
.media-upload-icon { .media-upload-icon {
order: 1; order: 1;
justify-content: left; justify-content: start;
} }
.emoji-icon { .emoji-icon {
@ -497,7 +482,7 @@
.poll-icon { .poll-icon {
order: 3; order: 3;
justify-content: right; justify-content: end;
} }
.error { .error {
@ -505,29 +490,29 @@
} }
.media-upload-wrapper { .media-upload-wrapper {
margin-right: .2em; margin-inline-end: .2em;
margin-bottom: .5em; margin-block-end: .5em;
width: 18em; inline-size: 18em;
img, video { img, video {
object-fit: contain; object-fit: contain;
max-height: 10em; max-block-size: 10em;
} }
.video { .video {
max-height: 10em; max-block-size: 10em;
} }
input { input {
flex: 1; flex: 1;
width: 100%; inline-size: 100%;
} }
} }
.status-input-wrapper { .status-input-wrapper {
display: flex; display: flex;
position: relative; position: relative;
width: 100%; inline-size: 100%;
flex-direction: column; flex-direction: column;
} }
@ -545,7 +530,8 @@
.form-group { .form-group {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 0.25em 0.5em 0.5em; padding-block: 0.25em 0.5em;
padding-inline: 0.5em;
line-height: 1.85; line-height: 1.85;
} }
@ -553,16 +539,16 @@
// TODO: make a resizable textarea component? // TODO: make a resizable textarea component?
box-sizing: content-box; // needed for easier computation of dynamic size box-sizing: content-box; // needed for easier computation of dynamic size
overflow: hidden; overflow: hidden;
transition: min-height 200ms 100ms; transition: min-block-size 200ms 100ms;
// stock padding + 1 line of text (for counter) // stock padding + 1 line of text (for counter)
padding-bottom: calc(var(--_padding) + var(--post-line-height) * 1em); padding-block-end: calc(var(--_padding) + var(--post-line-height) * 1em);
// two lines of text // two lines of text
height: calc(var(--post-line-height) * 1em); block-size: calc(var(--post-line-height) * 1em);
min-height: calc(var(--post-line-height) * 1em); min-block-size: calc(var(--post-line-height) * 1em);
resize: none; resize: none;
&.scrollable-form { &.scrollable-form {
overflow-y: auto; @include overflow-block(auto);
} }
} }
@ -572,10 +558,11 @@
.character-counter { .character-counter {
position: absolute; position: absolute;
bottom: 0; inset-block-end: 0;
right: 0; inset-inline-end: 0;
padding: 0; padding: 0;
margin: 0 0.5em; margin-block: 0;
margin-inline: 0.5em;
&.error { &.error {
color: $fallback--cRed; color: $fallback--cRed;
@ -599,8 +586,8 @@
.drop-indicator { .drop-indicator {
position: absolute; position: absolute;
width: 100%; inline-size: 100%;
height: 100%; block-size: 100%;
font-size: 5em; font-size: 5em;
display: flex; display: flex;
align-items: center; align-items: center;

View file

@ -27,13 +27,13 @@
.post-form-modal-panel { .post-form-modal-panel {
flex-shrink: 0; flex-shrink: 0;
margin-top: 25%; margin-block-start: 25%;
margin-bottom: 2em; margin-block-end: 2em;
width: 100%; inline-size: 100%;
max-width: 700px; max-inline-size: 700px;
@media (orientation: landscape) { @media (orientation: landscape) {
margin-top: 8%; margin-block-start: 8%;
} }
} }
</style> </style>

View file

@ -27,7 +27,8 @@
> :first-child { > :first-child {
padding: 10px; padding: 10px;
margin: -10px -8px -10px -10px; margin-block: -10px -10px;
margin-inline: -10px -8px;
} }
.action-counter { .action-counter {

View file

@ -21,15 +21,15 @@
flex-direction: column; flex-direction: column;
cursor: pointer; cursor: pointer;
overflow: hidden; overflow: hidden;
margin-top: 0.5em; margin-block-start: 0.5em;
.card-image { .card-image {
flex-shrink: 0; flex-shrink: 0;
width: 120px; inline-size: 120px;
max-width: 25%; max-inline-size: 25%;
img { img {
width: 100%; inline-size: 100%;
height: 100%; block-size: 100%;
object-fit: cover; object-fit: cover;
border-radius: $fallback--attachmentRadius; border-radius: $fallback--attachmentRadius;
border-radius: var(--attachmentRadius, $fallback--attachmentRadius); border-radius: var(--attachmentRadius, $fallback--attachmentRadius);
@ -37,7 +37,7 @@
} }
.card-content { .card-content {
max-height: 100%; max-block-size: 100%;
margin: 0.5em; margin: 0.5em;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -48,18 +48,20 @@
} }
.card-description { .card-description {
margin: 0.5em 0 0 0; margin-block: 0.5em 0;
margin-inline: 0 0;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
word-break: break-word; word-break: break-word;
line-height: 1.2em; line-height: 1.2em;
// cap description at 3 lines, the 1px is to clean up some stray pixels // cap description at 3 lines, the 1px is to clean up some stray pixels
// TODO: fancier fade-out at the bottom to show off that it's too long? // TODO: fancier fade-out at the bottom to show off that it's too long?
max-height: calc(1.2em * 3 - 1px); max-block-size: calc(1.2em * 3 - 1px);
} }
.nsfw-alert { .nsfw-alert {
margin: 2em 0; margin-block: 2em;
margin-inline: 0;
} }
color: $fallback--text; color: $fallback--text;

View file

@ -32,9 +32,10 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_logical.scss';
.custom-reaction { .custom-reaction {
width: 30px !important; inline-size: 30px !important;
} }
.ReactButton { .ReactButton {
@ -48,17 +49,18 @@
} }
.reaction-picker-divider { .reaction-picker-divider {
height: 1px; block-size: 1px;
width: 100%; inline-size: 100%;
margin: 0.5em; margin: 0.5em;
background-color: var(--border, $fallback--border); background-color: var(--border, $fallback--border);
} }
/* style unused: see emoji_picker.scss */
.reaction-picker { .reaction-picker {
width: 10em; inline-size: 10em;
height: 9em; block-size: 9em;
font-size: 1.5em; font-size: 1.5em;
overflow-y: scroll; @include overflow-block(scroll);
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
padding: 0.5em; padding: 0.5em;
@ -91,7 +93,7 @@
/* override of popover internal stuff */ /* override of popover internal stuff */
.popover-trigger-button { .popover-trigger-button {
width: auto; inline-size: auto;
} }
.popover-trigger { .popover-trigger {
@ -106,7 +108,10 @@
.popover { .popover {
transform: translateX(-64px) translateY(5px); transform: translateX(-64px) translateY(5px);
min-width: 70%; min-inline-size: 70%;
[dir="rtl"] & {
transform: translateX(64px) translateY(5px);
}
} }
} }

View file

@ -274,6 +274,7 @@
<script src="./registration.js"></script> <script src="./registration.js"></script>
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_logical.scss';
$validations-cRed: #f04124; $validations-cRed: #f04124;
.registration-form { .registration-form {
@ -286,7 +287,7 @@ $validations-cRed: #f04124;
flex-direction: row; flex-direction: row;
> * { > * {
min-width: 0; min-inline-size: 0;
} }
} }
@ -296,23 +297,24 @@ $validations-cRed: #f04124;
} }
.text-fields { .text-fields {
margin-top: 0.6em; margin-block-start: 0.6em;
flex: 1 0; flex: 1 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
textarea { textarea {
min-height: 100px; min-block-size: 100px;
resize: vertical; @include resize(block);
} }
.form-group { .form-group {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 0.3em 0; padding-block: 0.3em;
padding-inline: 0;
line-height: 2; line-height: 2;
margin-bottom: 1em; margin-block-end: 1em;
} }
.form-group--error { .form-group--error {
@ -327,8 +329,8 @@ $validations-cRed: #f04124;
} }
.form-error { .form-error {
margin-top: -0.7em; margin-block-start: -0.7em;
text-align: left; text-align: start;
span { span {
font-size: 0.85em; font-size: 0.85em;
@ -337,8 +339,9 @@ $validations-cRed: #f04124;
.form-error ul { .form-error ul {
list-style: none; list-style: none;
padding: 0 0 0 5px; padding-block: 0 0;
margin-top: 0; padding-inline: 5px 0;
margin-block-start: 0;
li::before { li::before {
content: "• "; content: "• ";
@ -347,17 +350,17 @@ $validations-cRed: #f04124;
form textarea { form textarea {
line-height:16px; line-height:16px;
resize: vertical; @include resize(block);
} }
.captcha { .captcha {
max-width: 350px; max-inline-size: 350px;
margin-bottom: 0.4em; margin-block-end: 0.4em;
} }
.btn { .btn {
margin-top: 0.6em; margin-block-start: 0.6em;
height: 2em; block-size: 2em;
} }
.error { .error {
@ -370,4 +373,9 @@ $validations-cRed: #f04124;
flex-direction: column-reverse; flex-direction: column-reverse;
} }
} }
@media all and (max-height: 800px) {
[class^=#{$writing-mode-vertical}] .registration-form .container {
flex-direction: column-reverse;
}
}
</style> </style>

View file

@ -28,11 +28,11 @@
<style lang="scss"> <style lang="scss">
.remote-follow { .remote-follow {
max-width: 220px; max-inline-size: 220px;
.remote-button { .remote-button {
width: 100%; inline-size: 100%;
min-height: 2em; min-block-size: 2em;
} }
} }
</style> </style>

View file

@ -44,7 +44,8 @@
> :first-child { > :first-child {
padding: 10px; padding: 10px;
margin: -10px -8px -10px -10px; margin-block: -10px -10px;
margin-inline: -10px -8px;
} }
.action-counter { .action-counter {

View file

@ -64,7 +64,8 @@
> :first-child { > :first-child {
padding: 10px; padding: 10px;
margin: -10px -8px -10px -10px; margin-block: -10px -10px;
margin-inline: -10px -8px;
} }
.action-counter { .action-counter {

View file

@ -1,6 +1,7 @@
.RichContent { .RichContent {
blockquote { blockquote {
margin: 0.2em 0 0.2em 2em; margin-block: 0.2em 0.2em;
margin-inline: 2em 0;
font-style: italic; font-style: italic;
} }
@ -17,31 +18,37 @@
} }
p { p {
margin: 0 0 1em 0; margin-block: 0 1em;
margin-inline: 0 0;
} }
p:last-child { p:last-child {
margin: 0 0 0 0; margin-block: 0 0;
margin-inline: 0 0;
} }
h1 { h1 {
font-size: 1.1em; font-size: 1.1em;
line-height: 1.2em; line-height: 1.2em;
margin: 1.4em 0; margin-block: 1.4em;
margin-inline: 0;
} }
h2 { h2 {
font-size: 1.1em; font-size: 1.1em;
margin: 1em 0; margin-block: 1em;
margin-inline: 0;
} }
h3 { h3 {
font-size: 1em; font-size: 1em;
margin: 1.2em 0; margin-block: 1.2em;
margin-inline: 0;
} }
h4 { h4 {
margin: 1.1em 0; margin-block: 1.1em;
margin-inline: 0;
} }
.img { .img {
@ -50,14 +57,14 @@
.emoji { .emoji {
display: inline-block; display: inline-block;
width: var(--emoji-size, 32px); inline-size: var(--emoji-size, 32px);
height: var(--emoji-size, 32px); block-size: var(--emoji-size, 32px);
} }
.img, .img,
video { video {
max-width: 100%; max-inline-size: 100%;
max-height: 400px; max-block-size: 400px;
vertical-align: middle; vertical-align: middle;
object-fit: contain; object-fit: contain;
} }

View file

@ -81,10 +81,10 @@
.scope { .scope {
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
min-width: 1.3em; min-inline-size: 1.3em;
min-height: 1.3em; min-block-size: 1.3em;
text-align: center; text-align: center;
margin-right: 0.4em; margin-inline-end: 0.4em;
&.selected svg { &.selected svg {
color: $fallback--lightText; color: $fallback--lightText;

View file

@ -128,6 +128,7 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_logical.scss';
.search-result-heading { .search-result-heading {
color: $fallback--faint; color: $fallback--faint;
@ -146,10 +147,20 @@
} }
} }
} }
@media all and (max-height: 800px) {
[class^=#{$writing-mode-vertical}] .search-nav-heading {
.tab-switcher .tabs .tab-wrapper {
display: block;
justify-content: center;
flex: 1 1 auto;
text-align: center;
}
}
}
.search-result { .search-result {
box-sizing: border-box; box-sizing: border-box;
border-bottom: 1px solid; border-block-end: 1px solid;
border-color: $fallback--border; border-color: $fallback--border;
border-color: var(--border, $fallback--border); border-color: var(--border, $fallback--border);
} }
@ -169,7 +180,7 @@
justify-content: center; justify-content: center;
.search-input { .search-input {
width: 100%; inline-size: 100%;
line-height: 1.125rem; line-height: 1.125rem;
font-size: 1rem; font-size: 1rem;
padding: 0.5rem; padding: 0.5rem;
@ -177,7 +188,7 @@
} }
.search-button { .search-button {
margin-left: 0.5em; margin-inline-start: 0.5em;
} }
} }
@ -200,7 +211,7 @@
.count { .count {
flex: 0 0 auto; flex: 0 0 auto;
width: 2rem; inline-size: 2rem;
font-size: 1.5rem; font-size: 1.5rem;
line-height: 2.25rem; line-height: 2.25rem;
font-weight: 500; font-weight: 500;

View file

@ -63,21 +63,21 @@
justify-content: flex-end; justify-content: flex-end;
&.-expanded { &.-expanded {
width: 100%; inline-size: 100%;
} }
.search-bar-input, .search-bar-input,
.search-button { .search-button {
height: 29px; block-size: 29px;
} }
.search-bar-input { .search-bar-input {
flex: 1 0 auto; flex: 1 0 auto;
margin-left: 0.5em; margin-inline-start: 0.5em;
} }
.cancel-search { .cancel-search {
height: 50px; block-size: 50px;
} }
.cancel-icon { .cancel-icon {

View file

@ -36,23 +36,24 @@ label.Select {
color: $fallback--text; color: $fallback--text;
color: var(--inputText, --text, $fallback--text); color: var(--inputText, --text, $fallback--text);
margin: 0; margin: 0;
padding: 0 2em 0 .2em; padding-block: 0 0;
padding-inline: .2em 2em;
font-family: sans-serif; font-family: sans-serif;
font-family: var(--inputFont, sans-serif); font-family: var(--inputFont, sans-serif);
font-size: 1em; font-size: 1em;
width: 100%; inline-size: 100%;
z-index: 1; z-index: 1;
height: 2em; block-size: 2em;
line-height: 16px; line-height: 16px;
} }
.select-down-icon { .select-down-icon {
position: absolute; position: absolute;
top: 0; inset-block-start: 0;
bottom: 0; inset-block-end: 0;
right: 5px; inset-inline-end: 5px;
height: 100%; block-size: 100%;
width: 0.875em; inline-size: 0.875em;
color: $fallback--text; color: $fallback--text;
color: var(--inputText, $fallback--text); color: var(--inputText, $fallback--text);
line-height: 2; line-height: 2;

View file

@ -59,7 +59,7 @@
align-items: center; align-items: center;
> * { > * {
min-width: 0; min-inline-size: 0;
} }
} }
@ -76,10 +76,11 @@
&-header { &-header {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0.6em 0; padding-block: 0.6em;
border-bottom: 2px solid; padding-inline: 0;
border-bottom-color: $fallback--border; border-block-end: 2px solid;
border-bottom-color: var(--border, $fallback--border); border-block-end-color: $fallback--border;
border-block-end-color: var(--border, $fallback--border);
&-actions { &-actions {
flex: 1; flex: 1;
@ -87,7 +88,8 @@
} }
&-checkbox-wrapper { &-checkbox-wrapper {
padding: 0 10px; padding-block: 0;
padding-inline: 10px;
flex: none; flex: none;
} }
} }

Some files were not shown because too many files have changed in this diff Show more