diff --git a/external/lang b/external/lang index 35894f6b..1d3e85e7 160000 --- a/external/lang +++ b/external/lang @@ -1 +1 @@ -Subproject commit 35894f6b1ecadc9df4ab77a5de9c9d5256a36582 +Subproject commit 1d3e85e7f6d0ad7a590854e240d7c47291f3e2cf diff --git a/package.json b/package.json index 32828ce4..95fe0374 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ } }, "dependencies": { + "color-rgba": "^2.3.0", "fs-extra": "^10.0.0", "klaw": "^3.0.0", "react-beautiful-dnd": "^13.1.0", @@ -95,6 +96,7 @@ "@traptitech/markdown-it-katex": "^3.4.3", "@traptitech/markdown-it-spoiler": "^1.1.6", "@trivago/prettier-plugin-sort-imports": "^2.0.2", + "@types/color-rgba": "^2.1.0", "@types/lodash.defaultsdeep": "^4.6.6", "@types/lodash.isequal": "^4.5.5", "@types/markdown-it": "^12.0.2", diff --git a/src/assets/emojis.ts b/src/assets/emojis.ts index 2819f634..79c89c33 100644 --- a/src/assets/emojis.ts +++ b/src/assets/emojis.ts @@ -673,6 +673,7 @@ export const emojiDictionary = { mandarin: "🍊", lemon: "🍋", banana: "🍌", + nanner: "🍌", pineapple: "🍍", mango: "🥭", apple: "🍎", @@ -876,6 +877,7 @@ export const emojiDictionary = { train: "🚋", bus: "🚌", oncoming_bus: "🚍", + trolley: "🚎", trolleybus: "🚎", minibus: "🚐", ambulance: "🚑", diff --git a/src/components/common/ServerHeader.tsx b/src/components/common/ServerHeader.tsx index 804f139d..68579ba1 100644 --- a/src/components/common/ServerHeader.tsx +++ b/src/components/common/ServerHeader.tsx @@ -4,84 +4,135 @@ import { observer } from "mobx-react-lite"; import { Link } from "react-router-dom"; import { ServerPermission } from "revolt.js/dist/api/permissions"; import { Server } from "revolt.js/dist/maps/Servers"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; import { Text } from "preact-i18n"; -import Header from "../ui/Header"; +import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice"; + import IconButton from "../ui/IconButton"; import Tooltip from "./Tooltip"; interface Props { server: Server; + background?: boolean; } -const ServerName = styled.div` - flex-grow: 1; +const ServerBanner = styled.div>` + flex-shrink: 0; + display: flex; + flex-direction: column; + justify-content: end; + + background-size: cover; + background-repeat: norepeat; + background-position: center center; + + ${(props) => + props.background + ? css` + height: 120px; + + .container { + background: linear-gradient( + 0deg, + var(--secondary-background), + transparent + ); + } + ` + : css` + background-color: var(--secondary-header); + `} + + .container { + height: var(--header-height); + + display: flex; + align-items: center; + padding: 0 14px; + font-weight: 600; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + gap: 8px; + + .title { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex-grow: 1; + } + } `; export default observer(({ server }: Props) => { const bannerURL = server.generateBannerURL({ width: 480 }); return ( -
- {server.flags && server.flags & 1 ? ( - } - placement={"bottom-start"}> - - - - - - ) : undefined} - {server.flags && server.flags & 2 ? ( - } - placement={"bottom-start"}> - - - - - - - - ) : undefined} - - {server.name} - {(server.permission & ServerPermission.ManageServer) > 0 && ( -
+
+ {server.flags && server.flags & 1 ? ( + + } + placement={"bottom-start"}> + + + + + + ) : undefined} + {server.flags && server.flags & 2 ? ( + + } + placement={"bottom-start"}> + + + + + + + + ) : undefined} +
{server.name}
+ {(server.permission & ServerPermission.ManageServer) > 0 && ( - + - - )} - + )} + + ); }); diff --git a/src/components/common/messaging/MessageBox.tsx b/src/components/common/messaging/MessageBox.tsx index e03e3b45..9cfa9f53 100644 --- a/src/components/common/messaging/MessageBox.tsx +++ b/src/components/common/messaging/MessageBox.tsx @@ -80,7 +80,7 @@ const Blocked = styled.div` color: var(--tertiary-foreground); .text { - padding: 14px 14px 14px 0; + padding: 14px; } svg { @@ -89,13 +89,17 @@ const Blocked = styled.div` `; const Action = styled.div` - display: flex; - place-items: center; - > div { height: 48px; - width: 48px; - padding: 12px; + width: 34px; + display: flex; + align-items: center; + justify-content: end; + /*padding: 14px 0 14px 14px;*/ + } + + .mobile { + justify-content: start; } ${() => diff --git a/src/components/common/messaging/bars/JumpToBottom.tsx b/src/components/common/messaging/bars/JumpToBottom.tsx index e6373a86..726ae61f 100644 --- a/src/components/common/messaging/bars/JumpToBottom.tsx +++ b/src/components/common/messaging/bars/JumpToBottom.tsx @@ -13,19 +13,25 @@ export const Bar = styled.div<{ position: "top" | "bottom"; accent?: boolean }>` z-index: 10; position: relative; + ${(props) => + props.position === "top" && + css` + top: 0; + `} + + ${(props) => + props.position === "bottom" && + css` + top: 65px; + + ${() => + isTouchscreenDevice && + css` + top: -90px; + `} + `} + > div { - ${(props) => - props.position === "bottom" && - css` - top: -26px; - - ${() => - isTouchscreenDevice && - css` - top: -32px; - `} - `} - height: 28px; width: 100%; position: absolute; @@ -52,6 +58,7 @@ export const Bar = styled.div<{ position: "top" | "bottom"; accent?: boolean }>` ${(props) => props.position === "top" ? css` + top: 48px; border-radius: 0 0 var(--border-radius) var(--border-radius); ` @@ -60,6 +67,12 @@ export const Bar = styled.div<{ position: "top" | "bottom"; accent?: boolean }>` 0; `} + ${() => + isTouchscreenDevice && + css` + top: 56px; + `} + > div { display: flex; align-items: center; diff --git a/src/components/common/messaging/bars/NewMessages.tsx b/src/components/common/messaging/bars/NewMessages.tsx index 0ea6dfa7..030afb93 100644 --- a/src/components/common/messaging/bars/NewMessages.tsx +++ b/src/components/common/messaging/bars/NewMessages.tsx @@ -49,8 +49,12 @@ export default observer( } }}>
- New messages since{" "} - {dayjs(decodeTime(last_id)).fromNow()} +
diff --git a/src/components/common/messaging/bars/TypingIndicator.tsx b/src/components/common/messaging/bars/TypingIndicator.tsx index 31e9a10c..4ccb40ab 100644 --- a/src/components/common/messaging/bars/TypingIndicator.tsx +++ b/src/components/common/messaging/bars/TypingIndicator.tsx @@ -25,7 +25,11 @@ const Base = styled.div` flex-direction: row; width: calc(100% - var(--scrollbar-thickness)); color: var(--secondary-foreground); - background: var(--secondary-background); + background-color: rgba( + var(--secondary-background-rgb), + max(var(--min-opacity), 0.75) + ); + backdrop-filter: blur(10px); } .avatars { diff --git a/src/components/common/messaging/embed/EmbedInvite.tsx b/src/components/common/messaging/embed/EmbedInvite.tsx index 165a6f7b..1873f14f 100644 --- a/src/components/common/messaging/embed/EmbedInvite.tsx +++ b/src/components/common/messaging/embed/EmbedInvite.tsx @@ -1,3 +1,4 @@ +import { Group } from "@styled-icons/boxicons-solid"; import { autorun } from "mobx"; import { observer } from "mobx-react-lite"; import { useHistory } from "react-router-dom"; @@ -47,7 +48,7 @@ const EmbedInviteBase = styled.div` const EmbedInviteDetails = styled.div` flex-grow: 1; - padding-left: 12px; + padding-inline-start: 12px; ${() => isTouchscreenDevice && css` @@ -63,7 +64,14 @@ const EmbedInviteName = styled.div` `; const EmbedInviteMemberCount = styled.div` + display: flex; + align-items: center; + gap: 2px; font-size: 0.8em; + + > svg { + color: var(--secondary-foreground); + } `; type Props = { @@ -119,6 +127,7 @@ export function EmbedInvite({ code }: Props) { {invite.server_name} + {invite.member_count.toLocaleString()}{" "} {invite.member_count === 1 ? "member" : "members"} diff --git a/src/components/navigation/BottomNavigation.tsx b/src/components/navigation/BottomNavigation.tsx index 48d684a3..90de4515 100644 --- a/src/components/navigation/BottomNavigation.tsx +++ b/src/components/navigation/BottomNavigation.tsx @@ -17,10 +17,10 @@ const Base = styled.div` `; const Navbar = styled.div` - z-index: 100; - max-width: 500px; - margin: 0 auto; + z-index: 500; display: flex; + margin: 0 auto; + max-width: 500px; height: var(--bottom-navigation-height); `; @@ -71,7 +71,12 @@ export default observer(() => { } } - history.push(layout.getLastHomePath()); + const path = layout.getLastHomePath(); + if (path === "/friends") { + history.push("/"); + } else { + history.push(path); + } }}> diff --git a/src/components/navigation/SidebarBase.tsx b/src/components/navigation/SidebarBase.tsx index dece8af5..b16f1a6c 100644 --- a/src/components/navigation/SidebarBase.tsx +++ b/src/components/navigation/SidebarBase.tsx @@ -8,6 +8,13 @@ export default styled.div` user-select: none; flex-direction: row; align-items: stretch; + /*background: var(--background);*/ + + background-color: rgba( + var(--background-rgb), + max(var(--min-opacity), 0.75) + ); + backdrop-filter: blur(20px); `; export const GenericSidebarBase = styled.div<{ @@ -21,10 +28,15 @@ export const GenericSidebarBase = styled.div<{ /*border-end-start-radius: 8px;*/ background: var(--secondary-background); - > :nth-child(1) { - border-end-start-radius: 8px; + /*> :nth-child(1) { + //border-end-start-radius: 8px; } + > :nth-child(2) { + margin-top: 48px; + background: red; + }*/ + ${(props) => props.mobilePadding && isTouchscreenDevice && diff --git a/src/components/navigation/items/ButtonItem.tsx b/src/components/navigation/items/ButtonItem.tsx index 58fe2745..d0bef93d 100644 --- a/src/components/navigation/items/ButtonItem.tsx +++ b/src/components/navigation/items/ButtonItem.tsx @@ -1,4 +1,5 @@ -import { X, Crown } from "@styled-icons/boxicons-regular"; +import { X } from "@styled-icons/boxicons-regular"; +import { Crown } from "@styled-icons/boxicons-solid"; import { observer } from "mobx-react-lite"; import { Presence } from "revolt-api/types/Users"; import { Channel } from "revolt.js/dist/maps/Channels"; diff --git a/src/components/navigation/left/HomeSidebar.tsx b/src/components/navigation/left/HomeSidebar.tsx index 3bccf881..9b6bde0a 100644 --- a/src/components/navigation/left/HomeSidebar.tsx +++ b/src/components/navigation/left/HomeSidebar.tsx @@ -7,6 +7,7 @@ import { import { observer } from "mobx-react-lite"; import { Link, useLocation, useParams } from "react-router-dom"; import { RelationshipStatus } from "revolt-api/types/Users"; +import styled, { css } from "styled-components"; import { Text } from "preact-i18n"; import { useContext, useEffect } from "preact/hooks"; @@ -27,6 +28,21 @@ import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase"; import ButtonItem, { ChannelButton } from "../items/ButtonItem"; import ConnectionStatus from "../items/ConnectionStatus"; +const Navbar = styled.div` + display: flex; + align-items: center; + padding: 0 14px; + font-weight: 600; + flex-shrink: 0; + height: 48px; + + ${() => + isTouchscreenDevice && + css` + height: 56px; + `} +`; + export default observer(() => { const { pathname } = useLocation(); const client = useContext(AppContext); @@ -55,6 +71,9 @@ export default observer(() => { return ( + + + diff --git a/src/components/navigation/left/ServerListSidebar.tsx b/src/components/navigation/left/ServerListSidebar.tsx index 6b531ef9..9ba4eb60 100644 --- a/src/components/navigation/left/ServerListSidebar.tsx +++ b/src/components/navigation/left/ServerListSidebar.tsx @@ -95,6 +95,7 @@ const ServerList = styled.div` overflow-y: scroll; padding-bottom: 20px; flex-direction: column; + margin-top: -2px; scrollbar-width: none; @@ -168,6 +169,7 @@ const ServerCircle = styled.div` display: flex; align-items: center; justify-content: center; + flex-shrink: 0; .circle { display: flex; @@ -384,7 +386,7 @@ export default observer(() => {
- + {/* { gap: "4px", }}>
Discover Public Servers
- } placement="right">
-
+
*/} {!isTouchscreenDevice && ( diff --git a/src/components/navigation/left/ServerSidebar.tsx b/src/components/navigation/left/ServerSidebar.tsx index e42b39b6..c7b4f74c 100644 --- a/src/components/navigation/left/ServerSidebar.tsx +++ b/src/components/navigation/left/ServerSidebar.tsx @@ -1,5 +1,6 @@ import { observer } from "mobx-react-lite"; import { Redirect, useParams } from "react-router"; +import { Server } from "revolt.js/dist/maps/Servers"; import styled, { css } from "styled-components"; import { attachContextMenu } from "preact-context-menu"; @@ -48,6 +49,10 @@ const ServerList = styled.div` } `; +interface Props { + server: Server; +} + export default observer(() => { const client = useClient(); const state = useApplicationState(); diff --git a/src/components/navigation/right/MemberSidebar.tsx b/src/components/navigation/right/MemberSidebar.tsx index 4e2ebbdd..72dc9f3d 100644 --- a/src/components/navigation/right/MemberSidebar.tsx +++ b/src/components/navigation/right/MemberSidebar.tsx @@ -6,9 +6,12 @@ import { Presence } from "revolt-api/types/Users"; import { Channel } from "revolt.js/dist/maps/Channels"; import { Server } from "revolt.js/dist/maps/Servers"; import { User } from "revolt.js/dist/maps/Users"; +import styled, { css } from "styled-components"; import { useContext, useEffect, useMemo } from "preact/hooks"; +import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice"; + import { ClientStatus, StatusContext, @@ -18,6 +21,15 @@ import { import { GenericSidebarBase } from "../SidebarBase"; import MemberList, { MemberListGroup } from "./MemberList"; +export const Container = styled.div` + padding-top: 48px; + + ${isTouchscreenDevice && + css` + padding-top: 0; + `} +`; + export default function MemberSidebar() { const channel = useClient().channels.get( useParams<{ channel: string }>().channel, @@ -157,6 +169,10 @@ export const GroupMemberSidebar = observer( return ( + + {/*{isTouchscreenDevice &&
Group settings go here
}*/} +
+
); @@ -180,6 +196,9 @@ export const ServerMemberSidebar = observer( return ( + + {/*{isTouchscreenDevice &&
Server settings go here
}*/} +
); diff --git a/src/components/settings/AppearanceShims.tsx b/src/components/settings/AppearanceShims.tsx index b9da94ab..41f2d876 100644 --- a/src/components/settings/AppearanceShims.tsx +++ b/src/components/settings/AppearanceShims.tsx @@ -218,28 +218,32 @@ export const DisplaySeasonalShim = observer(() => { const settings = useApplicationState().settings; return ( - <> -

- -

- {/* TOFIX: WIP feature - follows system theme */} - {/* settings.set("appearance:seasonal", v)} - description={ - - }> - - */} - settings.set("appearance:seasonal", v)} - description={ - - }> - - - + settings.set("appearance:seasonal", v)} + description={ + + }> + + + ); +}); + +/** + * Component providing a way to toggle transparency effects. + */ +export const DisplayTransparencyShim = observer(() => { + const settings = useApplicationState().settings; + + return ( + settings.set("appearance:transparency", v)} + description={ + + }> + + ); }); diff --git a/src/components/ui/ComboBox.tsx b/src/components/ui/ComboBox.tsx index d64db866..417f337f 100644 --- a/src/components/ui/ComboBox.tsx +++ b/src/components/ui/ComboBox.tsx @@ -19,4 +19,8 @@ export default styled.select` &:focus { box-shadow: 0 0 0 1.5pt var(--accent); } + + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; `; diff --git a/src/components/ui/Details.tsx b/src/components/ui/Details.tsx index 05f2d71a..98f7f140 100644 --- a/src/components/ui/Details.tsx +++ b/src/components/ui/Details.tsx @@ -1,13 +1,20 @@ import styled, { css } from "styled-components"; +import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice"; + export default styled.details<{ sticky?: boolean; large?: boolean }>` summary { ${(props) => props.sticky && css` - top: -1px; + top: 48px; z-index: 10; position: sticky; + ${() => + isTouchscreenDevice && + css` + top: 56px; + `} `} ${(props) => diff --git a/src/components/ui/Header.tsx b/src/components/ui/Header.tsx index 6f21f66b..511e17ac 100644 --- a/src/components/ui/Header.tsx +++ b/src/components/ui/Header.tsx @@ -4,6 +4,7 @@ import { Menu, } from "@styled-icons/boxicons-regular"; import { observer } from "mobx-react-lite"; +import { useLocation } from "react-router-dom"; import styled, { css } from "styled-components"; import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice"; @@ -14,14 +15,16 @@ import { SIDEBAR_CHANNELS } from "../../mobx/stores/Layout"; import { Children } from "../../types/Preact"; interface Props { - borders?: boolean; + topBorder?: boolean; + bottomBorder?: boolean; + background?: boolean; + transparent?: boolean; placement: "primary" | "secondary"; } const Header = styled.div` gap: 10px; - height: 48px; flex: 0 auto; display: flex; flex-shrink: 0; @@ -29,15 +32,11 @@ const Header = styled.div` font-weight: 600; user-select: none; align-items: center; + + height: var(--header-height); + background-size: cover !important; background-position: center !important; - background-color: var(--primary-header); - - /*> div { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - }*/ svg { flex-shrink: 0; @@ -48,11 +47,21 @@ const Header = styled.div` color: var(--secondary-foreground); } - ${() => - isTouchscreenDevice && - css` - height: 56px; - `} + ${(props) => + props.transparent + ? css` + background-color: rgba( + var(--primary-header-rgb), + max(var(--min-opacity), 0.75) + ); + backdrop-filter: blur(10px); + z-index: 20; + position: absolute; + width: 100%; + ` + : css` + background-color: var(--primary-header); + `} ${(props) => props.background && @@ -71,10 +80,16 @@ const Header = styled.div` `} ${(props) => - props.borders && + props.topBorder && css` border-start-start-radius: 8px; `} + + ${(props) => + props.bottomBorder && + css` + border-end-start-radius: 8px; + `} `; export default Header; @@ -98,19 +113,24 @@ const IconContainer = styled.div` `} `; -interface PageHeaderProps { +type PageHeaderProps = Omit & { noBurger?: boolean; children: Children; icon: Children; -} +}; export const PageHeader = observer( - ({ children, icon, noBurger }: PageHeaderProps) => { + ({ children, icon, noBurger, ...props }: PageHeaderProps) => { const layout = useApplicationState().layout; const visible = layout.getSectionState(SIDEBAR_CHANNELS, true); + const { pathname } = useLocation(); return ( -
+
{!noBurger && } @@ -135,7 +155,7 @@ export function HamburgerAction() { function openSidebar() { document - .querySelector("#app > div > div") + .querySelector("#app > div > div > div") ?.scrollTo({ behavior: "smooth", left: 0 }); } diff --git a/src/components/ui/LineDivider.tsx b/src/components/ui/LineDivider.tsx index 58a9c7a4..fab42452 100644 --- a/src/components/ui/LineDivider.tsx +++ b/src/components/ui/LineDivider.tsx @@ -1,9 +1,9 @@ import styled from "styled-components"; export default styled.div` - height: 0px; + height: 0; opacity: 0.6; flex-shrink: 0; - margin: 8px 10px; + margin: 8px 15px; border-top: 1px solid var(--tertiary-foreground); `; diff --git a/src/context/Theme.tsx b/src/context/Theme.tsx index e5697881..ceb2a401 100644 --- a/src/context/Theme.tsx +++ b/src/context/Theme.tsx @@ -1,3 +1,4 @@ +import rgba from "color-rgba"; import { observer } from "mobx-react-lite"; import { Helmet } from "react-helmet"; import { createGlobalStyle } from "styled-components"; @@ -71,6 +72,12 @@ export type Theme = Overrides & { font?: Fonts; css?: string; monospaceFont?: MonospaceFonts; + "min-opacity"?: number; +}; + +export type ComputedVariables = Theme & { + "header-height"?: string; + "effective-bottom-offset"?: string; }; export interface ThemeOptions { @@ -287,7 +294,13 @@ const GlobalTheme = createGlobalStyle<{ theme: Theme }>` export const generateVariables = (theme: Theme) => { return (Object.keys(theme) as Variables[]).map((key) => { - return `--${key}: ${theme[key]};`; + const colour = rgba(theme[key]); + if (colour) { + const [r, g, b] = colour; + return `--${key}: ${theme[key]}; --${key}-rgb: ${r}, ${g}, ${b};`; + } else { + return `--${key}: ${theme[key]};`; + } }); }; diff --git a/src/lib/defer.ts b/src/lib/defer.ts index 79de63ae..73663ac6 100644 --- a/src/lib/defer.ts +++ b/src/lib/defer.ts @@ -1 +1,11 @@ +/** + * Schedule a task at the end of the Event Loop + * @param cb Callback + */ export const defer = (cb: () => void) => setTimeout(cb, 0); + +/** + * Schedule a task at the end of the second Event Loop + * @param cb Callback + */ +export const chainedDefer = (cb: () => void) => defer(() => defer(cb)); diff --git a/src/mobx/stores/Layout.ts b/src/mobx/stores/Layout.ts index 324626ad..1c572333 100644 --- a/src/mobx/stores/Layout.ts +++ b/src/mobx/stores/Layout.ts @@ -138,6 +138,16 @@ export default class Layout implements Store, Persistent { return this.lastHomePath; } + /** + * Get the last path the user had open. + * @returns Last path + */ + @computed getLastPath() { + return this.lastSection === "home" + ? this.lastHomePath + : this.getLastOpened(this.lastSection); + } + /** * Set the current path open in the home tab. * @param path Pathname diff --git a/src/mobx/stores/Settings.ts b/src/mobx/stores/Settings.ts index 1a90dbe4..8fed5083 100644 --- a/src/mobx/stores/Settings.ts +++ b/src/mobx/stores/Settings.ts @@ -29,6 +29,7 @@ export interface ISettings { "appearance:emoji": EmojiPack; "appearance:ligatures": boolean; "appearance:seasonal": boolean; + "appearance:transparency": boolean; "appearance:theme:base": "dark" | "light"; "appearance:theme:overrides": Partial; @@ -140,6 +141,7 @@ export default class Settings if (key === "appearance") { this.remove("appearance:emoji"); this.remove("appearance:seasonal"); + this.remove("appearance:transparency"); } else { this.remove("appearance:ligatures"); this.remove("appearance:theme:base"); @@ -169,6 +171,7 @@ export default class Settings appearance: this.pullKeys([ "appearance:emoji", "appearance:seasonal", + "appearance:transparency", ]), theme: this.pullKeys([ "appearance:ligatures", diff --git a/src/mobx/stores/helpers/STheme.ts b/src/mobx/stores/helpers/STheme.ts index b2b20c4c..c4ebb173 100644 --- a/src/mobx/stores/helpers/STheme.ts +++ b/src/mobx/stores/helpers/STheme.ts @@ -1,5 +1,8 @@ +import rgba from "color-rgba"; import { makeAutoObservable, computed, action } from "mobx"; +import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice"; + import { Theme, PRESETS, @@ -8,6 +11,7 @@ import { DEFAULT_MONO_FONT, Fonts, MonospaceFonts, + ComputedVariables, } from "../../../context/Theme"; import Settings from "../Settings"; @@ -96,10 +100,10 @@ export default class STheme { }; } - @computed computeVariables(): Theme { + @computed computeVariables(): ComputedVariables { const variables = this.getVariables() as Record< string, - string | boolean + string | boolean | number >; for (const key of Object.keys(variables)) { @@ -109,7 +113,16 @@ export default class STheme { } } - return variables as unknown as Theme; + return { + ...(variables as unknown as Theme), + "min-opacity": this.settings.get("appearance:transparency", true) + ? 0 + : 1, + "header-height": isTouchscreenDevice ? "56px" : "48px", + "effective-bottom-offset": isTouchscreenDevice + ? "var(--bottom-navigation-height)" + : "0px", + }; } @action setVariable(key: Variables, value: string) { @@ -204,15 +217,11 @@ export default class STheme { function getContrastingColour(hex: string, fallback?: string): string { if (typeof hex !== "string") return "black"; - // TODO: Switch to color-parse - // Try parse hex value. - hex = hex.replace(/#/g, ""); - const r = parseInt(hex.substr(0, 2), 16) / 255; - const g = parseInt(hex.substr(2, 2), 16) / 255; - const b = parseInt(hex.substr(4, 2), 16) / 255; + const colour = rgba(hex); + if (!colour) return fallback ? getContrastingColour(fallback) : "black"; - if (isNaN(r) || isNaN(g) || isNaN(b)) - return fallback ? getContrastingColour(fallback) : "black"; - - return r * 0.299 + g * 0.587 + b * 0.114 >= 0.186 ? "black" : "white"; + const [r, g, b] = colour; + return (r / 255) * 0.299 + (g / 255) * 0.587 + (b / 255) * 0.114 >= 0.186 + ? "black" + : "white"; } diff --git a/src/pages/RevoltApp.tsx b/src/pages/RevoltApp.tsx index 296a556a..6c98b269 100644 --- a/src/pages/RevoltApp.tsx +++ b/src/pages/RevoltApp.tsx @@ -1,14 +1,16 @@ import { Docked, OverlappingPanels, ShowIf } from "react-overlapping-panels"; import { Switch, Route, useLocation } from "react-router-dom"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; import ContextMenus from "../lib/ContextMenus"; import { isTouchscreenDevice } from "../lib/isTouchscreenDevice"; +import { useApplicationState } from "../mobx/State"; +import { SIDEBAR_CHANNELS } from "../mobx/stores/Layout"; + import Popovers from "../context/intermediate/Popovers"; import Notifications from "../context/revoltjs/Notifications"; import StateMonitor from "../context/revoltjs/StateMonitor"; -import SyncManager from "../context/revoltjs/SyncManager"; import { Titlebar } from "../components/native/Titlebar"; import BottomNavigation from "../components/navigation/BottomNavigation"; @@ -24,12 +26,54 @@ import ChannelSettings from "./settings/ChannelSettings"; import ServerSettings from "./settings/ServerSettings"; import Settings from "./settings/Settings"; -const Routes = styled.div` +const AppContainer = styled.div` + background-size: cover !important; + background-position: center center !important; +`; + +const StatusBar = styled.div` + height: 40px; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + font-size: 13px; + gap: 14px; + + .button { + padding: 5px; + border: 1px solid white; + border-radius: var(--border-radius); + } +`; + +const Routes = styled.div.attrs({ "data-component": "routes" })<{ + borders: boolean; +}>` min-width: 0; display: flex; - overflow: hidden; + position: relative; flex-direction: column; + background: var(--primary-background); + + /*background-color: rgba( + var(--primary-background-rgb), + max(var(--min-opacity), 0.75) + );*/ + //backdrop-filter: blur(10px); + + ${() => + isTouchscreenDevice && + css` + overflow: hidden; + `} + + ${(props) => + props.borders && + css` + border-start-start-radius: 8px; + `} `; export default function App() { @@ -37,6 +81,7 @@ export default function App() { const fixedBottomNav = path === "/" || path === "/settings" || path.startsWith("/friends"); const inChannel = path.includes("/channel"); + const inServer = path.includes("/server"); const inSpecial = (path.startsWith("/friends") && isTouchscreenDevice) || path.startsWith("/invite") || @@ -44,90 +89,102 @@ export default function App() { return ( <> - {window.isNative && !window.native.getConfig().frame && ( - - )} - } - } - rightPanel={ - !inSpecial && inChannel - ? { width: 236, component: } - : undefined - } - bottomNav={{ - component: , - showIf: fixedBottomNav ? ShowIf.Always : ShowIf.Left, - height: 50, - }} - docked={isTouchscreenDevice ? Docked.None : Docked.Left}> - - - - - - - - + {/* +
Planned outage: CDN (~2 hours)
+
View status
+
*/} + + {window.isNative && !window.native.getConfig().frame && ( + + )} + } + } + rightPanel={ + !inSpecial && inChannel + ? { width: 236, component: } + : undefined + } + bottomNav={{ + component: , + showIf: fixedBottomNav ? ShowIf.Always : ShowIf.Left, + height: 50, + }} + docked={isTouchscreenDevice ? Docked.None : Docked.Left}> + + + + + + + + - - + + - - - + + + - - + + - - - - - - - - - - - - + + + + + +
+
+ + + + +
+ ); } diff --git a/src/pages/channels/Channel.tsx b/src/pages/channels/Channel.tsx index 1e0b4f99..9ff0a59a 100644 --- a/src/pages/channels/Channel.tsx +++ b/src/pages/channels/Channel.tsx @@ -29,7 +29,7 @@ import ChannelHeader from "./ChannelHeader"; import { MessageArea } from "./messaging/MessageArea"; import VoiceHeader from "./voice/VoiceHeader"; -const ChannelMain = styled.div` +const ChannelMain = styled.div.attrs({ "data-component": "channel" })` flex-grow: 1; display: flex; min-height: 0; @@ -37,7 +37,9 @@ const ChannelMain = styled.div` flex-direction: row; `; -const ChannelContent = styled.div` +const ChannelContent = styled.div.attrs({ + "data-component": "content", +})` flex-grow: 1; display: flex; overflow: hidden; diff --git a/src/pages/channels/ChannelHeader.tsx b/src/pages/channels/ChannelHeader.tsx index 70b8f34d..8c8244fc 100644 --- a/src/pages/channels/ChannelHeader.tsx +++ b/src/pages/channels/ChannelHeader.tsx @@ -78,7 +78,6 @@ const Info = styled.div` export default observer(({ channel }: ChannelHeaderProps) => { const { openScreen } = useIntermediate(); - const layout = useApplicationState().layout; const name = getChannelName(channel); let icon, recipient: User | undefined; @@ -99,7 +98,7 @@ export default observer(({ channel }: ChannelHeaderProps) => { } return ( - + {name} {isTouchscreenDevice && diff --git a/src/pages/channels/actions/HeaderActions.tsx b/src/pages/channels/actions/HeaderActions.tsx index 0466e0ca..2127b4bb 100644 --- a/src/pages/channels/actions/HeaderActions.tsx +++ b/src/pages/channels/actions/HeaderActions.tsx @@ -10,6 +10,7 @@ import { import { observer } from "mobx-react-lite"; import { useHistory } from "react-router-dom"; +import { chainedDefer, defer } from "../../../lib/defer"; import { internalEmit } from "../../../lib/eventEmitter"; import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice"; import { voiceState, VoiceStatus } from "../../../lib/vortex/VoiceState"; @@ -29,20 +30,34 @@ export default function HeaderActions({ channel }: ChannelHeaderProps) { const { openScreen } = useIntermediate(); const history = useHistory(); - function openRightSidebar() { - const panels = document.querySelector("#app > div > div"); + function slideOpen() { + if (!isTouchscreenDevice) return; + const panels = document.querySelector("#app > div > div > div"); panels?.scrollTo({ behavior: "smooth", left: panels.clientWidth * 3, }); } - function openSidebar() { - if (isTouchscreenDevice) { - openRightSidebar(); - } else { + function openSearch() { + if ( + !isTouchscreenDevice && + !layout.getSectionState(SIDEBAR_MEMBERS, true) + ) { layout.toggleSectionState(SIDEBAR_MEMBERS, true); } + + slideOpen(); + chainedDefer(() => internalEmit("RightSidebar", "open", "search")); + } + + function openMembers() { + if (!isTouchscreenDevice) { + layout.toggleSectionState(SIDEBAR_MEMBERS, true); + } + + slideOpen(); + chainedDefer(() => internalEmit("RightSidebar", "open", undefined)); } return ( @@ -74,17 +89,13 @@ export default function HeaderActions({ channel }: ChannelHeaderProps) { )} {channel.channel_type !== "VoiceChannel" && ( - { - internalEmit("RightSidebar", "open", "search"); - openRightSidebar(); - }}> + )} {(channel.channel_type === "Group" || channel.channel_type === "TextChannel") && ( - + )} diff --git a/src/pages/channels/messaging/MessageArea.tsx b/src/pages/channels/messaging/MessageArea.tsx index 1b2c0278..ee356c38 100644 --- a/src/pages/channels/messaging/MessageArea.tsx +++ b/src/pages/channels/messaging/MessageArea.tsx @@ -33,13 +33,18 @@ import Preloader from "../../../components/ui/Preloader"; import ConversationStart from "./ConversationStart"; import MessageRenderer from "./MessageRenderer"; -const Area = styled.div` +const Area = styled.div.attrs({ "data-scroll-offset": "with-padding" })` height: 100%; flex-grow: 1; min-height: 0; + word-break: break-word; + overflow-x: hidden; overflow-y: scroll; - word-break: break-word; + + &::-webkit-scrollbar-thumb { + min-height: 150px; + } > div { display: flex; diff --git a/src/pages/channels/voice/VoiceHeader.tsx b/src/pages/channels/voice/VoiceHeader.tsx index 84ad85e6..312073ee 100644 --- a/src/pages/channels/voice/VoiceHeader.tsx +++ b/src/pages/channels/voice/VoiceHeader.tsx @@ -30,6 +30,7 @@ interface Props { } const VoiceBase = styled.div` + margin-top: 48px; padding: 20px; background: var(--secondary-background); diff --git a/src/pages/friends/Friend.module.scss b/src/pages/friends/Friend.module.scss index b2b3d3b4..0316dab6 100644 --- a/src/pages/friends/Friend.module.scss +++ b/src/pages/friends/Friend.module.scss @@ -13,7 +13,6 @@ .list { padding: 0 10px 10px 10px; user-select: none; - overflow-y: scroll; &[data-empty="true"] { img { @@ -185,12 +184,9 @@ } } +// Hide the remove friend button on smaller screens. @media only screen and (max-width: 768px) { - .list { - padding: 0 8px 8px 8px; - } - - .remove { + .list .remove { display: none; } } diff --git a/src/pages/friends/Friends.tsx b/src/pages/friends/Friends.tsx index 1a0bea04..24fdfafa 100644 --- a/src/pages/friends/Friends.tsx +++ b/src/pages/friends/Friends.tsx @@ -3,8 +3,10 @@ import { UserDetail, MessageAdd, UserPlus } from "@styled-icons/boxicons-solid"; import { observer } from "mobx-react-lite"; import { RelationshipStatus, Presence } from "revolt-api/types/Users"; import { User } from "revolt.js/dist/maps/Users"; +import styled, { css } from "styled-components"; import styles from "./Friend.module.scss"; +import classNames from "classnames"; import { Text } from "preact-i18n"; import { TextReact } from "../../lib/i18n"; @@ -16,7 +18,7 @@ import { useClient } from "../../context/revoltjs/RevoltClient"; import CollapsibleSection from "../../components/common/CollapsibleSection"; import Tooltip from "../../components/common/Tooltip"; import UserIcon from "../../components/common/user/UserIcon"; -import Header, { PageHeader } from "../../components/ui/Header"; +import { PageHeader } from "../../components/ui/Header"; import IconButton from "../../components/ui/IconButton"; import { Children } from "../../types/Preact"; @@ -72,7 +74,7 @@ export default observer(() => { const isEmpty = lists.reduce((p: number, n) => p + n.length, 0) === 0; return ( <> - } noBurger> + } transparent noBurger>
@@ -115,99 +117,104 @@ export default observer(() => { */}
-
- {isEmpty && ( - <> - - - - )} +
+
+ {isEmpty && ( + <> + + + + )} - {incoming.length > 0 && ( -
- openScreen({ - id: "pending_requests", - users: incoming, - }) - }> -
- {incoming.map( - (x, i) => - i < 3 && ( - - ), - )} -
-
-
- {" "} - {incoming.length} -
- - {incoming.length > 3 ? ( - - ) : incoming.length > 1 ? ( - - ) : ( - - )} - -
- -
- )} - - {lists.map(([i18n, list, section_id], index) => { - if (index === 0) return; - if (list.length === 0) return; - - return ( - - — {list.length} -
+ {incoming.length > 0 && ( +
+ openScreen({ + id: "pending_requests", + users: incoming, + }) }> - {list.map((x) => ( - - ))} - - ); - })} +
+ {incoming.map( + (x, i) => + i < 3 && ( + + ), + )} +
+
+
+ {" "} + {incoming.length} +
+ + {incoming.length > 3 ? ( + + ) : incoming.length > 1 ? ( + + ) : ( + + )} + +
+ +
+ )} + + {lists.map(([i18n, list, section_id], index) => { + if (index === 0) return; + if (list.length === 0) return; + + return ( + + — {list.length} +
+ }> + {list.map((x) => ( + + ))} + + ); + })} +
); diff --git a/src/pages/home/Home.module.scss b/src/pages/home/Home.module.scss index 501cc92a..e1aef42a 100644 --- a/src/pages/home/Home.module.scss +++ b/src/pages/home/Home.module.scss @@ -8,7 +8,7 @@ flex-direction: column; justify-content: center; align-items: center; - height: 95%; + height: 100%; padding: 12px; h3 { @@ -30,15 +30,11 @@ grid-template-columns: repeat(2, 1fr); gap: 16px; max-width: 650px; - margin-bottom: 20px; + margin-bottom: 30px; a { width: 100%; - &:nth-child(4) { - margin-bottom: 20px; - } - div { margin: 0; } diff --git a/src/pages/home/Home.tsx b/src/pages/home/Home.tsx index 86872b44..22e70269 100644 --- a/src/pages/home/Home.tsx +++ b/src/pages/home/Home.tsx @@ -83,7 +83,7 @@ export default observer(() => { )}
- }> + } transparent>
@@ -103,8 +103,8 @@ export default observer(() => { Create a group - { }> Join a community - + */} {client.servers.get( "01F7ZSBSFHQ8TA81725KQCSDDP", @@ -159,24 +159,24 @@ export default observer(() => { rel="noreferrer"> }> - - - }> - - }> - - - -
+ }> + + }> + + + + {isDecember && ( Turn {seasonalTheme ? "off" : "on"} homescreen diff --git a/src/pages/settings/GenericSettings.tsx b/src/pages/settings/GenericSettings.tsx index a3480b49..12a4665c 100644 --- a/src/pages/settings/GenericSettings.tsx +++ b/src/pages/settings/GenericSettings.tsx @@ -102,7 +102,7 @@ export function GenericSettings({ /> {isTouchscreenDevice && ( -
+
{typeof page === "undefined" ? ( <> {showExitButton && ( @@ -168,6 +168,9 @@ export function GenericSettings({
{ // Force scroll to top if page changes. if (ref) { diff --git a/src/pages/settings/Settings.module.scss b/src/pages/settings/Settings.module.scss index 1e2863f3..f18c0ee8 100644 --- a/src/pages/settings/Settings.module.scss +++ b/src/pages/settings/Settings.module.scss @@ -43,18 +43,13 @@ background: var(--primary-background); } - .scrollbox { - &::-webkit-scrollbar-thumb { - border-top: none; - } - } - /* Sidebar */ .sidebar { overflow-y: auto; .container { - padding: 20px 8px calc(var(--bottom-navigation-height) + 30px); + padding: calc(var(--header-height) + 4px) 8px + calc(var(--bottom-navigation-height) + 30px); min-width: 218px; } @@ -76,7 +71,7 @@ .contentcontainer { max-width: unset !important; - padding: 16px 12px var(--bottom-navigation-height) !important; + padding: 72px 12px var(--bottom-navigation-height) !important; } } } @@ -117,6 +112,7 @@ // All children receive custom scrollbar. > * > ::-webkit-scrollbar-thumb { + min-height: 100px; width: 4px; background-clip: content-box; border-top: 80px solid transparent; diff --git a/src/pages/settings/Settings.tsx b/src/pages/settings/Settings.tsx index 8e914055..977870c4 100644 --- a/src/pages/settings/Settings.tsx +++ b/src/pages/settings/Settings.tsx @@ -17,6 +17,7 @@ import { Speaker, Store, Bot, + Trash, } from "@styled-icons/boxicons-solid"; import { observer } from "mobx-react-lite"; import { Route, Switch, useHistory } from "react-router-dom"; @@ -24,16 +25,19 @@ import { LIBRARY_VERSION } from "revolt.js"; import styled from "styled-components"; import styles from "./Settings.module.scss"; +import { openContextMenu } from "preact-context-menu"; import { Text } from "preact-i18n"; import { useContext } from "preact/hooks"; import { useApplicationState } from "../../mobx/State"; +import { useIntermediate } from "../../context/intermediate/Intermediate"; import RequiresOnline from "../../context/revoltjs/RequiresOnline"; import { AppContext, LogOutContext } from "../../context/revoltjs/RevoltClient"; import UserIcon from "../../components/common/user/UserIcon"; import { Username } from "../../components/common/user/UserShort"; +import UserStatus from "../../components/common/user/UserStatus"; import LineDivider from "../../components/ui/LineDivider"; import ButtonItem from "../../components/navigation/items/ButtonItem"; @@ -54,19 +58,67 @@ import { Sessions } from "./panes/Sessions"; import { Sync } from "./panes/Sync"; import { ThemeShop } from "./panes/ThemeShop"; -const IndexHeader = styled.div` +const AccountHeader = styled.div` display: flex; - background: var(--secondary-background); + flex-direction: column; border-radius: var(--border-radius); - padding: 20px; - align-items: center; - gap: 10px; + overflow: hidden; + margin-bottom: 10px; + + .account { + padding: 20px; + gap: 10px; + align-items: center; + display: flex; + background: var(--secondary-background); + + .details { + display: flex; + flex-direction: column; + font-size: 12px; + gap: 2px; + + > span { + font-size: 20px; + font-weight: 600; + } + } + } + + .statusChanger { + display: flex; + align-items: center; + background: var(--tertiary-background); + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + .status { + padding-inline-start: 12px; + height: 48px; + display: flex; + align-items: center; + color: var(--secondary-foreground); + flex-grow: 1; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + svg { + width: 48px; + flex-shrink: 0; + } + } `; export default observer(() => { const history = useHistory(); const client = useContext(AppContext); const logout = useContext(LogOutContext); + const { openScreen } = useIntermediate(); const experiments = useApplicationState().experiments; function switchPage(to?: string) { @@ -270,10 +322,40 @@ export default observer(() => { } indexHeader={ - - - - + +
+ openContextMenu("Status")} + /> +
+ + +
+
+
+ + openScreen({ + id: "special_input", + type: "set_custom_status", + }) + }> + Change your status... + + {client.user!.status?.text && ( + + client.users.edit({ remove: "StatusText" }) + } + /> + )} +
+ } /> ); diff --git a/src/pages/settings/panes/Appearance.tsx b/src/pages/settings/panes/Appearance.tsx index c68c410f..a38c9cb8 100644 --- a/src/pages/settings/panes/Appearance.tsx +++ b/src/pages/settings/panes/Appearance.tsx @@ -15,6 +15,7 @@ import { DisplayEmojiShim, ThemeCustomCSSShim, DisplaySeasonalShim, + DisplayTransparencyShim, } from "../../../components/settings/AppearanceShims"; import ThemeOverrides from "../../../components/settings/appearance/ThemeOverrides"; import ThemeTools from "../../../components/settings/appearance/ThemeTools"; @@ -27,6 +28,10 @@ export const Appearance = observer(() => {

+

+ +

+
diff --git a/src/pages/settings/server/Overview.tsx b/src/pages/settings/server/Overview.tsx index 28141891..9086e5fb 100644 --- a/src/pages/settings/server/Overview.tsx +++ b/src/pages/settings/server/Overview.tsx @@ -126,7 +126,6 @@ export const Overview = observer(({ server }: Props) => { alignItems: "center", }}> {i18n} - Sends a message when someone joins your server span, @@ -23,7 +28,11 @@ cursor: pointer; &:hover { - background: var(--secondary-background); + background-color: rgba( + var(--secondary-background-rgb), + max(var(--min-opacity), 0.75) + ); + backdrop-filter: blur(10px); } } diff --git a/src/styles/_elements.scss b/src/styles/_elements.scss index 3a18014c..35d28c9b 100644 --- a/src/styles/_elements.scss +++ b/src/styles/_elements.scss @@ -13,9 +13,32 @@ } ::-webkit-scrollbar-thumb { + min-height: 30px; + min-width: 30px; + + background-clip: content-box; background: var(--scrollbar-thumb); } +[data-scroll-offset] { + overflow-y: scroll; +} + +[data-scroll-offset="with-padding"], +[data-scroll-offset] .with-padding { + padding-top: var(--header-height); +} + +[data-scroll-offset]::-webkit-scrollbar-thumb { + background-clip: content-box; + border-top: var(--header-height) solid transparent; +} + +[data-avoids-navigation]::-webkit-scrollbar-thumb { + background-clip: content-box; + border-bottom: var(--effective-bottom-offset) solid transparent; +} + ::-webkit-scrollbar-corner { background: transparent; } diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index 62b7a03a..7bf369bf 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -30,7 +30,7 @@ --input-border-width: 2px; --textarea-padding: 16px; --textarea-line-height: 20px; - --message-box-padding: 14px 14px 14px 0; + --message-box-padding: 14px; --attachment-max-width: 480px; --attachment-max-height: 640px; diff --git a/yarn.lock b/yarn.lock index 5ade8187..bf3102f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1303,6 +1303,11 @@ javascript-natural-sort "0.7.1" lodash "4.17.21" +"@types/color-rgba@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@types/color-rgba/-/color-rgba-2.1.0.tgz#0182795370deae5c2c62f71ea6e91c6bab87394d" + integrity sha512-tWcJLEiKdZ3ihJdThfLCe6Kw5vo0lgGcuucGkbtzcp1zifDA1E2Z96wxeSS/r+ytpHD15NCAWabX8GV911ywCA== + "@types/debug@^4.1.6": version "4.1.7" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82" @@ -1642,6 +1647,11 @@ ajv@^8.0.1, ajv@^8.6.0: require-from-string "^2.0.2" uri-js "^4.2.2" +almost-equal@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/almost-equal/-/almost-equal-1.1.0.tgz#f851c631138757994276aa2efbe8dfa3066cccdd" + integrity sha1-+FHGMROHV5lCdqou++jfowZszN0= + ansi-colors@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" @@ -1934,11 +1944,34 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -color-name@~1.1.4: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-parse@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/color-parse/-/color-parse-1.4.2.tgz#78651f5d34df1a57f997643d86f7f87268ad4eb5" + integrity sha512-RI7s49/8yqDj3fECFZjUI1Yi0z/Gq1py43oNJivAIIDSyJiOZLfYCRQEgn8HEVAj++PcRe8AnL2XF0fRJ3BTnA== + dependencies: + color-name "^1.0.0" + +color-rgba@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/color-rgba/-/color-rgba-2.3.0.tgz#d5eb481d7933d2542d1f222ea10ad40d159e9d35" + integrity sha512-z/5fMOY8/IzrBHPBk+n3ATNSM/1atXcHCRPTGPLlzYJ4fn7CRD46zzt3lkLtQ44cL8UIUU4JBXDVrhWj1khiwg== + dependencies: + color-parse "^1.4.1" + color-space "^1.14.6" + +color-space@^1.14.6: + version "1.16.0" + resolved "https://registry.yarnpkg.com/color-space/-/color-space-1.16.0.tgz#611781bca41cd8582a1466fd9e28a7d3d89772a2" + integrity sha512-A6WMiFzunQ8KEPFmj02OnnoUnqhmSaHaZ/0LVFcPTdlvm8+3aMJ5x1HRHy3bDHPkovkf4sS0f4wsVvwk71fKkg== + dependencies: + hsluv "^0.0.3" + mumath "^3.3.4" + colorette@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.3.0.tgz#ff45d2f0edb244069d3b772adeb04fed38d0a0af" @@ -2742,6 +2775,11 @@ hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react- dependencies: react-is "^16.7.0" +hsluv@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/hsluv/-/hsluv-0.0.3.tgz#829107dafb4a9f8b52a1809ed02e091eade6754c" + integrity sha1-gpEH2vtKn4tSoYCe0C4JHq3mdUw= + idb@^6.0.0: version "6.1.2" resolved "https://registry.yarnpkg.com/idb/-/idb-6.1.2.tgz#82ef5c951b8e1f47875d36ccafa4bedafc62f2f1" @@ -3276,6 +3314,13 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +mumath@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/mumath/-/mumath-3.3.4.tgz#48d4a0f0fd8cad4e7b32096ee89b161a63d30bbf" + integrity sha1-SNSg8P2MrU57Mglu6JsWGmPTC78= + dependencies: + almost-equal "^1.1.0" + nanoid@^3.1.30: version "3.1.30" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362"