mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 05:04:57 +08:00
Spaces quick settings (#7196)
This commit is contained in:
parent
138f6685d4
commit
9fefeefc8c
@ -22,6 +22,7 @@
|
||||
@import "./structures/_MyGroups.scss";
|
||||
@import "./structures/_NonUrgentToastContainer.scss";
|
||||
@import "./structures/_NotificationPanel.scss";
|
||||
@import "./structures/_QuickSettingsButton.scss";
|
||||
@import "./structures/_RightPanel.scss";
|
||||
@import "./structures/_RoomDirectory.scss";
|
||||
@import "./structures/_RoomSearch.scss";
|
||||
|
176
res/css/structures/_QuickSettingsButton.scss
Normal file
176
res/css/structures/_QuickSettingsButton.scss
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_QuickSettingsButton {
|
||||
flex: 0 0 auto;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
margin: 12px auto;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
mask-image: url('$(res)/img/element-icons/settings.svg');
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: center;
|
||||
mask-size: 16px;
|
||||
background: $secondary-content;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $quaternary-content;
|
||||
|
||||
&::before {
|
||||
background-color: $primary-content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx_QuickSettingsButton_ContextMenuWrapper .mx_ContextualMenu {
|
||||
padding: 16px;
|
||||
width: max-content;
|
||||
min-width: 200px;
|
||||
contain: unset; // let the dropdown paint beyond the context menu
|
||||
|
||||
> div > h2 {
|
||||
font-weight: $font-semi-bold;
|
||||
font-size: $font-15px;
|
||||
line-height: $font-24px;
|
||||
color: $primary-content;
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
|
||||
.mx_AccessibleButton_kind_primary_outline {
|
||||
display: block;
|
||||
}
|
||||
|
||||
> div > h4 {
|
||||
font-weight: $font-semi-bold;
|
||||
font-size: $font-12px;
|
||||
line-height: $font-15px;
|
||||
text-transform: uppercase;
|
||||
color: $tertiary-content;
|
||||
margin: 20px 0 12px;
|
||||
}
|
||||
|
||||
.mx_QuickSettingsButton_pinToSidebarHeading {
|
||||
padding-left: 24px;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
background-color: $secondary-content;
|
||||
content: "";
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: center;
|
||||
mask-size: contain;
|
||||
mask-image: url('$(res)/img/element-icons/room/pin-upright.svg');
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.mx_Checkbox {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mx_QuickSettingsButton_favouritesCheckbox,
|
||||
.mx_QuickSettingsButton_peopleCheckbox {
|
||||
.mx_Checkbox_background + div {
|
||||
padding-left: 22px;
|
||||
position: relative;
|
||||
margin-left: 6px;
|
||||
font-size: $font-15px;
|
||||
line-height: $font-24px;
|
||||
color: $secondary-content;
|
||||
|
||||
&::before {
|
||||
background-color: $secondary-content;
|
||||
content: "";
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: center;
|
||||
mask-size: contain;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx_QuickSettingsButton_favouritesCheckbox .mx_Checkbox_background + div::before {
|
||||
mask-image: url('$(res)/img/element-icons/roomlist/favorite.svg');
|
||||
}
|
||||
|
||||
.mx_QuickSettingsButton_peopleCheckbox .mx_Checkbox_background + div::before {
|
||||
mask-image: url('$(res)/img/element-icons/room/members.svg');
|
||||
}
|
||||
|
||||
.mx_QuickSettingsButton_moreOptionsButton {
|
||||
padding-left: 22px;
|
||||
margin-left: 22px;
|
||||
font-size: $font-15px;
|
||||
line-height: $font-24px;
|
||||
color: $secondary-content;
|
||||
position: relative;
|
||||
margin-bottom: 16px;
|
||||
|
||||
&::before {
|
||||
background-color: $secondary-content;
|
||||
content: "";
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: center;
|
||||
mask-size: contain;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
mask-image: url('$(res)/img/element-icons/room/ellipsis.svg');
|
||||
}
|
||||
}
|
||||
|
||||
.mx_QuickSettingsButton_themePicker {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> h4 {
|
||||
font-weight: $font-semi-bold;
|
||||
font-size: $font-12px;
|
||||
line-height: $font-15px;
|
||||
color: $secondary-content;
|
||||
text-transform: uppercase;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mx_Dropdown {
|
||||
min-width: 100px;
|
||||
margin-left: auto;
|
||||
height: min-content;
|
||||
}
|
||||
}
|
||||
}
|
@ -48,7 +48,6 @@ $activeBorderColor: $secondary-content;
|
||||
mask-size: 32px;
|
||||
mask-repeat: no-repeat;
|
||||
margin-left: $gutterSize;
|
||||
margin-bottom: 12px;
|
||||
background-color: $tertiary-content;
|
||||
mask-image: url('$(res)/img/element-icons/expand-space-panel.svg');
|
||||
|
||||
|
@ -178,26 +178,20 @@ export default class Dropdown extends React.Component<IProps, IState> {
|
||||
this.ignoreEvent = ev;
|
||||
};
|
||||
|
||||
private onChevronClick = (ev: React.MouseEvent) => {
|
||||
if (this.state.expanded) {
|
||||
this.setState({ expanded: false });
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
private onAccessibleButtonClick = (ev: ButtonEvent) => {
|
||||
if (this.props.disabled) return;
|
||||
|
||||
if (!this.state.expanded) {
|
||||
this.setState({
|
||||
expanded: true,
|
||||
});
|
||||
this.setState({ expanded: true });
|
||||
ev.preventDefault();
|
||||
} else if ((ev as React.KeyboardEvent).key === Key.ENTER) {
|
||||
// the accessible button consumes enter onKeyDown for firing onClick, so handle it here
|
||||
this.props.onOptionChange(this.state.highlightedOption);
|
||||
this.close();
|
||||
} else if (!(ev as React.KeyboardEvent).key) {
|
||||
// collapse on other non-keyboard event activations
|
||||
this.setState({ expanded: false });
|
||||
ev.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
@ -383,7 +377,7 @@ export default class Dropdown extends React.Component<IProps, IState> {
|
||||
onKeyDown={this.onKeyDown}
|
||||
>
|
||||
{ currentValue }
|
||||
<span onClick={this.onChevronClick} className="mx_Dropdown_arrow" />
|
||||
<span className="mx_Dropdown_arrow" />
|
||||
{ menu }
|
||||
</AccessibleButton>
|
||||
</div>;
|
||||
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||
import React from 'react';
|
||||
import { _t } from "../../../languageHandler";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { enumerateThemes, findHighContrastTheme, findNonHighContrastTheme, isHighContrastTheme } from "../../../theme";
|
||||
import { findHighContrastTheme, findNonHighContrastTheme, getOrderedThemes, isHighContrastTheme } from "../../../theme";
|
||||
import ThemeWatcher from "../../../settings/watchers/ThemeWatcher";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
@ -28,7 +28,6 @@ import Field from '../elements/Field';
|
||||
import StyledRadioGroup from "../elements/StyledRadioGroup";
|
||||
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { compare } from "../../../utils/strings";
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
@ -58,13 +57,13 @@ export default class ThemeChoicePanel extends React.Component<IProps, IState> {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
...this.calculateThemeState(),
|
||||
...ThemeChoicePanel.calculateThemeState(),
|
||||
customThemeUrl: "",
|
||||
customThemeMessage: { isError: false, text: "" },
|
||||
};
|
||||
}
|
||||
|
||||
private calculateThemeState(): IThemeState {
|
||||
public static calculateThemeState(): IThemeState {
|
||||
// We have to mirror the logic from ThemeWatcher.getEffectiveTheme so we
|
||||
// show the right values for things.
|
||||
|
||||
@ -238,14 +237,7 @@ export default class ThemeChoicePanel extends React.Component<IProps, IState> {
|
||||
);
|
||||
}
|
||||
|
||||
// XXX: replace any type here
|
||||
const themes = Object.entries<any>(enumerateThemes())
|
||||
.map(p => ({ id: p[0], name: p[1] })) // convert pairs to objects for code readability
|
||||
.filter(p => !isHighContrastTheme(p.id));
|
||||
const builtInThemes = themes.filter(p => !p.id.startsWith("custom-"));
|
||||
const customThemes = themes.filter(p => !builtInThemes.includes(p))
|
||||
.sort((a, b) => compare(a.name, b.name));
|
||||
const orderedThemes = [...builtInThemes, ...customThemes];
|
||||
const orderedThemes = getOrderedThemes();
|
||||
return (
|
||||
<div className="mx_SettingsTab_section mx_ThemeChoicePanel">
|
||||
<span className="mx_SettingsTab_subheading">{ _t("Theme") }</span>
|
||||
|
@ -23,7 +23,7 @@ import StyledCheckbox from "../../../elements/StyledCheckbox";
|
||||
import { useSettingValue } from "../../../../../hooks/useSettings";
|
||||
import { MetaSpace } from "../../../../../stores/spaces";
|
||||
|
||||
const onMetaSpaceChangeFactory = (metaSpace: MetaSpace) => (e: ChangeEvent<HTMLInputElement>) => {
|
||||
export const onMetaSpaceChangeFactory = (metaSpace: MetaSpace) => (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const currentValue = SettingsStore.getValue("Spaces.enabledMetaSpaces");
|
||||
SettingsStore.setValue("Spaces.enabledMetaSpaces", null, SettingLevel.ACCOUNT, {
|
||||
...currentValue,
|
||||
|
150
src/components/views/spaces/QuickSettingsButton.tsx
Normal file
150
src/components/views/spaces/QuickSettingsButton.tsx
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useMemo } from "react";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
import { alwaysAboveRightOf, ChevronFace, ContextMenu, useContextMenu } from "../../structures/ContextMenu";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import StyledCheckbox from "../elements/StyledCheckbox";
|
||||
import { MetaSpace } from "../../../stores/spaces";
|
||||
import { useSettingValue } from "../../../hooks/useSettings";
|
||||
import { onMetaSpaceChangeFactory } from "../settings/tabs/user/SidebarUserSettingsTab";
|
||||
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import { UserTab } from "../dialogs/UserSettingsDialog";
|
||||
import { findNonHighContrastTheme, getOrderedThemes } from "../../../theme";
|
||||
import Dropdown from "../elements/Dropdown";
|
||||
import ThemeChoicePanel from "../settings/ThemeChoicePanel";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import { RecheckThemePayload } from "../../../dispatcher/payloads/RecheckThemePayload";
|
||||
|
||||
const QuickSettingsButton = () => {
|
||||
const orderedThemes = useMemo(getOrderedThemes, []);
|
||||
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLDivElement>();
|
||||
|
||||
const {
|
||||
[MetaSpace.Favourites]: favouritesEnabled,
|
||||
[MetaSpace.People]: peopleEnabled,
|
||||
} = useSettingValue<Record<MetaSpace, boolean>>("Spaces.enabledMetaSpaces");
|
||||
|
||||
let contextMenu: JSX.Element;
|
||||
if (menuDisplayed) {
|
||||
const themeState = ThemeChoicePanel.calculateThemeState();
|
||||
const nonHighContrast = findNonHighContrastTheme(themeState.theme);
|
||||
const theme = nonHighContrast ? nonHighContrast : themeState.theme;
|
||||
|
||||
contextMenu = <ContextMenu
|
||||
{...alwaysAboveRightOf(handle.current.getBoundingClientRect(), ChevronFace.None, 16)}
|
||||
wrapperClassName="mx_QuickSettingsButton_ContextMenuWrapper"
|
||||
onFinished={closeMenu}
|
||||
managed={false}
|
||||
focusLock={true}
|
||||
>
|
||||
<h2>{ _t("Quick settings") }</h2>
|
||||
|
||||
<AccessibleButton
|
||||
onClick={() => {
|
||||
closeMenu();
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.ViewUserSettings,
|
||||
initialTabId: UserTab.Sidebar,
|
||||
});
|
||||
}}
|
||||
kind="primary_outline"
|
||||
>
|
||||
{ _t("All settings") }
|
||||
</AccessibleButton>
|
||||
|
||||
<h4 className="mx_QuickSettingsButton_pinToSidebarHeading">{ _t("Pin to sidebar") }</h4>
|
||||
|
||||
<StyledCheckbox
|
||||
className="mx_QuickSettingsButton_favouritesCheckbox"
|
||||
checked={!!favouritesEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(MetaSpace.Favourites)}
|
||||
>
|
||||
{ _t("Favourites") }
|
||||
</StyledCheckbox>
|
||||
<StyledCheckbox
|
||||
className="mx_QuickSettingsButton_peopleCheckbox"
|
||||
checked={!!peopleEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(MetaSpace.People)}
|
||||
>
|
||||
{ _t("People") }
|
||||
</StyledCheckbox>
|
||||
<AccessibleButton
|
||||
className="mx_QuickSettingsButton_moreOptionsButton"
|
||||
onClick={() => {
|
||||
closeMenu();
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.ViewUserSettings,
|
||||
initialTabId: UserTab.Sidebar,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{ _t("More options") }
|
||||
</AccessibleButton>
|
||||
|
||||
<div className="mx_QuickSettingsButton_themePicker">
|
||||
<h4>{ _t("Theme") }</h4>
|
||||
<Dropdown
|
||||
id="mx_QuickSettingsButton_themePickerDropdown"
|
||||
onOptionChange={async (newTheme: string) => {
|
||||
// XXX: mostly copied from ThemeChoicePanel
|
||||
// doing getValue in the .catch will still return the value we failed to set,
|
||||
// so remember what the value was before we tried to set it so we can revert
|
||||
// const oldTheme: string = SettingsStore.getValue("theme");
|
||||
SettingsStore.setValue("theme", null, SettingLevel.DEVICE, newTheme).catch(() => {
|
||||
dis.dispatch<RecheckThemePayload>({ action: Action.RecheckTheme });
|
||||
});
|
||||
// The settings watcher doesn't fire until the echo comes back from the
|
||||
// server, so to make the theme change immediately we need to manually
|
||||
// do the dispatch now
|
||||
// XXX: The local echoed value appears to be unreliable, in particular
|
||||
// when settings custom themes(!) so adding forceTheme to override
|
||||
// the value from settings.
|
||||
dis.dispatch<RecheckThemePayload>({ action: Action.RecheckTheme, forceTheme: newTheme });
|
||||
closeMenu();
|
||||
}}
|
||||
value={theme}
|
||||
label={_t("Space selection")}
|
||||
>
|
||||
{ orderedThemes.map((theme) => (
|
||||
<div key={theme.id}>
|
||||
{ theme.name }
|
||||
</div>
|
||||
)) }
|
||||
</Dropdown>
|
||||
</div>
|
||||
</ContextMenu>;
|
||||
}
|
||||
|
||||
return <>
|
||||
<AccessibleTooltipButton
|
||||
className="mx_QuickSettingsButton"
|
||||
onClick={openMenu}
|
||||
title={_t("Quick settings")}
|
||||
inputRef={handle}
|
||||
/>
|
||||
|
||||
{ contextMenu }
|
||||
</>;
|
||||
};
|
||||
|
||||
export default QuickSettingsButton;
|
@ -54,6 +54,8 @@ import IconizedContextMenu, {
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||
import UIStore from "../../../stores/UIStore";
|
||||
import QuickSettingsButton from "./QuickSettingsButton";
|
||||
import { useSettingValue } from "../../../hooks/useSettings";
|
||||
|
||||
const useSpaces = (): [Room[], MetaSpace[], Room[], SpaceKey] => {
|
||||
const invites = useEventEmitterState<Room[]>(SpaceStore.instance, UPDATE_INVITED_SPACES, () => {
|
||||
@ -277,6 +279,7 @@ const InnerSpacePanel = React.memo<IInnerSpacePanelProps>(({ children, isPanelCo
|
||||
});
|
||||
|
||||
const SpacePanel = () => {
|
||||
const metaSpacesEnabled = useSettingValue("feature_spaces_metaspaces");
|
||||
const [isPanelCollapsed, setPanelCollapsed] = useState(true);
|
||||
const ref = useRef<HTMLUListElement>();
|
||||
useLayoutEffect(() => {
|
||||
@ -322,6 +325,7 @@ const SpacePanel = () => {
|
||||
onClick={() => setPanelCollapsed(!isPanelCollapsed)}
|
||||
title={isPanelCollapsed ? _t("Expand space panel") : _t("Collapse space panel")}
|
||||
/>
|
||||
{ metaSpacesEnabled && <QuickSettingsButton /> }
|
||||
</ul>
|
||||
) }
|
||||
</RovingTabIndexProvider>
|
||||
|
@ -1045,6 +1045,14 @@
|
||||
"Your server isn't responding to some <a>requests</a>.": "Your server isn't responding to some <a>requests</a>.",
|
||||
"Decline (%(counter)s)": "Decline (%(counter)s)",
|
||||
"Accept <policyLink /> to continue:": "Accept <policyLink /> to continue:",
|
||||
"Quick settings": "Quick settings",
|
||||
"All settings": "All settings",
|
||||
"Pin to sidebar": "Pin to sidebar",
|
||||
"Favourites": "Favourites",
|
||||
"People": "People",
|
||||
"More options": "More options",
|
||||
"Theme": "Theme",
|
||||
"Space selection": "Space selection",
|
||||
"Delete avatar": "Delete avatar",
|
||||
"Delete": "Delete",
|
||||
"Upload avatar": "Upload avatar",
|
||||
@ -1081,8 +1089,6 @@
|
||||
"Show all rooms": "Show all rooms",
|
||||
"All rooms": "All rooms",
|
||||
"Options": "Options",
|
||||
"Favourites": "Favourites",
|
||||
"People": "People",
|
||||
"Other rooms": "Other rooms",
|
||||
"Expand space panel": "Expand space panel",
|
||||
"Collapse space panel": "Collapse space panel",
|
||||
@ -1325,7 +1331,6 @@
|
||||
"Use high contrast": "Use high contrast",
|
||||
"Custom theme URL": "Custom theme URL",
|
||||
"Add theme": "Add theme",
|
||||
"Theme": "Theme",
|
||||
"Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).",
|
||||
"Checking for an update...": "Checking for an update...",
|
||||
"No update available.": "No update available.",
|
||||
@ -1647,7 +1652,6 @@
|
||||
"Show Stickers": "Show Stickers",
|
||||
"Send a sticker": "Send a sticker",
|
||||
"Send voice message": "Send voice message",
|
||||
"More options": "More options",
|
||||
"The conversation continues here.": "The conversation continues here.",
|
||||
"This room has been replaced and is no longer active.": "This room has been replaced and is no longer active.",
|
||||
"You do not have permission to post to this room": "You do not have permission to post to this room",
|
||||
@ -2259,7 +2263,6 @@
|
||||
"Adding rooms... (%(progress)s out of %(count)s)|other": "Adding rooms... (%(progress)s out of %(count)s)",
|
||||
"Adding rooms... (%(progress)s out of %(count)s)|one": "Adding room...",
|
||||
"Direct Messages": "Direct Messages",
|
||||
"Space selection": "Space selection",
|
||||
"Add existing rooms": "Add existing rooms",
|
||||
"Want to add a new room instead?": "Want to add a new room instead?",
|
||||
"Create a new room": "Create a new room",
|
||||
@ -3066,7 +3069,6 @@
|
||||
"New here? <a>Create an account</a>": "New here? <a>Create an account</a>",
|
||||
"Notification settings": "Notification settings",
|
||||
"Security & privacy": "Security & privacy",
|
||||
"All settings": "All settings",
|
||||
"Community settings": "Community settings",
|
||||
"User settings": "User settings",
|
||||
"Switch to light mode": "Switch to light mode",
|
||||
|
16
src/theme.ts
16
src/theme.ts
@ -19,6 +19,7 @@ import { _t } from "./languageHandler";
|
||||
|
||||
import SettingsStore from "./settings/SettingsStore";
|
||||
import ThemeWatcher from "./settings/watchers/ThemeWatcher";
|
||||
import { compare } from "./utils/strings";
|
||||
|
||||
export const DEFAULT_THEME = "light";
|
||||
const HIGH_CONTRAST_THEMES = {
|
||||
@ -86,6 +87,21 @@ export function enumerateThemes(): {[key: string]: string} {
|
||||
return Object.assign({}, customThemeNames, BUILTIN_THEMES);
|
||||
}
|
||||
|
||||
interface ITheme {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export function getOrderedThemes(): ITheme[] {
|
||||
const themes = Object.entries(enumerateThemes())
|
||||
.map(p => ({ id: p[0], name: p[1] })) // convert pairs to objects for code readability
|
||||
.filter(p => !isHighContrastTheme(p.id));
|
||||
const builtInThemes = themes.filter(p => !p.id.startsWith("custom-"));
|
||||
const customThemes = themes.filter(p => !builtInThemes.includes(p))
|
||||
.sort((a, b) => compare(a.name, b.name));
|
||||
return [...builtInThemes, ...customThemes];
|
||||
}
|
||||
|
||||
function clearCustomTheme(): void {
|
||||
// remove all css variables, we assume these are there because of the custom theme
|
||||
const inlineStyleProps = Object.values(document.body.style);
|
||||
|
Loading…
Reference in New Issue
Block a user