summaryrefslogtreecommitdiffhomepage
path: root/gui/src/renderer/components
diff options
context:
space:
mode:
Diffstat (limited to 'gui/src/renderer/components')
-rw-r--r--gui/src/renderer/components/CustomScrollbars.tsx42
-rw-r--r--gui/src/renderer/components/MacOsScrollbarDetection.tsx41
-rw-r--r--gui/src/renderer/components/NavigationBar.tsx6
-rw-r--r--gui/src/renderer/components/SelectLanguage.tsx4
-rw-r--r--gui/src/renderer/components/SelectLocation.tsx4
-rw-r--r--gui/src/renderer/components/SplitTunnelingSettings.tsx4
6 files changed, 80 insertions, 21 deletions
diff --git a/gui/src/renderer/components/CustomScrollbars.tsx b/gui/src/renderer/components/CustomScrollbars.tsx
index 3dacd26626..139c4381de 100644
--- a/gui/src/renderer/components/CustomScrollbars.tsx
+++ b/gui/src/renderer/components/CustomScrollbars.tsx
@@ -1,6 +1,8 @@
import * as React from 'react';
import styled from 'styled-components';
+import { MacOsScrollbarVisibility } from '../../shared/ipc-schema';
import { Scheduler } from '../../shared/scheduler';
+import { useSelector } from '../redux/store';
const ScrollableContent = styled.div({
display: 'flex',
@@ -12,8 +14,8 @@ const ScrollableContent = styled.div({
const AUTOHIDE_TIMEOUT = 1000;
interface IProps {
- autoHide: boolean;
- trackPadding: { x: number; y: number };
+ autoHide?: boolean;
+ trackPadding?: { x: number; y: number };
onScroll?: (value: IScrollEvent) => void;
className?: string;
fillContainer?: boolean;
@@ -44,10 +46,26 @@ interface IScrollbarUpdateContext {
position: boolean;
}
-export default class CustomScrollbars extends React.Component<IProps, IState> {
- public static defaultProps: IProps = {
- // auto-hide on macOS by default
- autoHide: window.env.platform === 'darwin',
+export default React.forwardRef(function CustomScrollbarsContainer(
+ props: IProps,
+ forwardRef: React.Ref<CustomScrollbars>,
+) {
+ const macOsScrollbarVisibility = useSelector(
+ (state) => state.userInterface.macOsScrollbarVisibility,
+ );
+ const autoHide =
+ props.autoHide ??
+ (window.env.platform === 'darwin' &&
+ (macOsScrollbarVisibility === undefined ||
+ macOsScrollbarVisibility === MacOsScrollbarVisibility.whenScrolling));
+
+ return <CustomScrollbars {...props} autoHide={autoHide} ref={forwardRef} />;
+});
+
+export type CustomScrollbarsRef = CustomScrollbars;
+
+class CustomScrollbars extends React.Component<IProps, IState> {
+ public static defaultProps: Partial<IProps> = {
trackPadding: { x: 2, y: 2 },
};
@@ -161,8 +179,8 @@ export default class CustomScrollbars extends React.Component<IProps, IState> {
return (
prevProps.children !== nextProps.children ||
prevProps.autoHide !== nextProps.autoHide ||
- prevProps.trackPadding.x !== nextProps.trackPadding.x ||
- prevProps.trackPadding.y !== nextProps.trackPadding.y ||
+ prevProps.trackPadding?.x !== nextProps.trackPadding?.x ||
+ prevProps.trackPadding?.y !== nextProps.trackPadding?.y ||
prevState.canScroll !== nextState.canScroll ||
prevState.showScrollIndicators !== nextState.showScrollIndicators ||
prevState.showTrack !== nextState.showTrack ||
@@ -350,7 +368,7 @@ export default class CustomScrollbars extends React.Component<IProps, IState> {
// a thumb at the lowest point matches the bottom of scrollable view
const thumbBoundary = this.computeTrackLength(scrollable) - thumb.clientHeight;
const thumbTop =
- pointInScrollContainer.y - this.state.dragStart.y - this.props.trackPadding.y;
+ pointInScrollContainer.y - this.state.dragStart.y - (this.props.trackPadding?.y ?? 0);
const newScrollTop = (thumbTop / thumbBoundary) * maxScrollTop;
scrollable.scrollTop = newScrollTop;
@@ -410,7 +428,7 @@ export default class CustomScrollbars extends React.Component<IProps, IState> {
}
private computeTrackLength(scrollable: HTMLElement) {
- return scrollable.offsetHeight - this.props.trackPadding.y * 2;
+ return scrollable.offsetHeight - (this.props.trackPadding?.y ?? 0) * 2;
}
// Computes the position of child element within scrollable container
@@ -471,10 +489,10 @@ export default class CustomScrollbars extends React.Component<IProps, IState> {
// calculate thumb position based on scroll progress and thumb boundary
// adding vertical inset to adjust the thumb's appearance
- const thumbPosition = thumbBoundary * scrollPosition + this.props.trackPadding.y;
+ const thumbPosition = thumbBoundary * scrollPosition + (this.props.trackPadding?.y ?? 0);
return {
- x: -this.props.trackPadding.x,
+ x: -(this.props.trackPadding?.x ?? 0),
y: thumbPosition,
};
}
diff --git a/gui/src/renderer/components/MacOsScrollbarDetection.tsx b/gui/src/renderer/components/MacOsScrollbarDetection.tsx
new file mode 100644
index 0000000000..69de63cc6a
--- /dev/null
+++ b/gui/src/renderer/components/MacOsScrollbarDetection.tsx
@@ -0,0 +1,41 @@
+import React, { useEffect, useRef } from 'react';
+import styled from 'styled-components';
+import useActions from '../lib/actionsHook';
+import userInterface from '../redux/userinterface/actions';
+import { useSelector } from '../redux/store';
+import { MacOsScrollbarVisibility } from '../../shared/ipc-schema';
+
+const StyledContainer = styled.div({
+ position: 'absolute',
+ visibility: 'hidden',
+ overflowY: 'scroll',
+ overflowX: 'hidden',
+ width: '1px',
+ height: '0px',
+});
+
+// This component is used to determine wheter scrollbars should be always visible or only visible
+// while scrolling when the system setting for this is set to "Automatic". This is detected by
+// testing if any space is taken by a scrollbar.
+export default function MacOsScrollbarDetection() {
+ const visibility = useSelector((state) => state.userInterface.macOsScrollbarVisibility);
+ const { setMacOsScrollbarVisibility } = useActions(userInterface);
+ const ref = useRef() as React.RefObject<HTMLDivElement>;
+
+ useEffect(() => {
+ if (visibility === MacOsScrollbarVisibility.automatic) {
+ // If the width is 0 then the 1 px width of the parent has been used by the scrollbar.
+ const newVisibility =
+ ref.current?.offsetWidth === 0
+ ? MacOsScrollbarVisibility.always
+ : MacOsScrollbarVisibility.whenScrolling;
+ setMacOsScrollbarVisibility(newVisibility);
+ }
+ }, [visibility]);
+
+ return (
+ <StyledContainer>
+ <div ref={ref} />
+ </StyledContainer>
+ );
+}
diff --git a/gui/src/renderer/components/NavigationBar.tsx b/gui/src/renderer/components/NavigationBar.tsx
index 9af5a4b615..e02ae99387 100644
--- a/gui/src/renderer/components/NavigationBar.tsx
+++ b/gui/src/renderer/components/NavigationBar.tsx
@@ -6,7 +6,7 @@ import { useHistory } from '../lib/history';
import { useCombinedRefs } from '../lib/utilityHooks';
import { useSelector } from '../redux/store';
import userInterface from '../redux/userinterface/actions';
-import CustomScrollbars, { IScrollEvent } from './CustomScrollbars';
+import CustomScrollbars, { CustomScrollbarsRef, IScrollEvent } from './CustomScrollbars';
import {
StyledBackBarItemButton,
StyledBackBarItemIcon,
@@ -112,12 +112,12 @@ interface INavigationScrollbarsProps {
export const NavigationScrollbars = React.forwardRef(function NavigationScrollbarsT(
props: INavigationScrollbarsProps,
- forwardedRef?: React.Ref<CustomScrollbars>,
+ forwardedRef?: React.Ref<CustomScrollbarsRef>,
) {
const history = useHistory();
const { onScroll } = useContext(NavigationScrollContext);
- const ref = useRef<CustomScrollbars>();
+ const ref = useRef<CustomScrollbarsRef>();
const combinedRefs = useCombinedRefs(forwardedRef, ref);
const { addScrollPosition, removeScrollPosition } = useActions(userInterface);
diff --git a/gui/src/renderer/components/SelectLanguage.tsx b/gui/src/renderer/components/SelectLanguage.tsx
index 7c67927182..38d20b09e3 100644
--- a/gui/src/renderer/components/SelectLanguage.tsx
+++ b/gui/src/renderer/components/SelectLanguage.tsx
@@ -3,7 +3,7 @@ import styled from 'styled-components';
import { colors } from '../../config.json';
import { messages } from '../../shared/gettext';
import { AriaInputGroup } from './AriaGroup';
-import CustomScrollbars from './CustomScrollbars';
+import { CustomScrollbarsRef } from './CustomScrollbars';
import { Container, Layout } from './Layout';
import {
BackBarItem,
@@ -40,7 +40,7 @@ const StyledSelector = (styled(Selector)({
}) as unknown) as new <T>() => Selector<T>;
export default class SelectLanguage extends React.Component<IProps, IState> {
- private scrollView = React.createRef<CustomScrollbars>();
+ private scrollView = React.createRef<CustomScrollbarsRef>();
private selectedCellRef = React.createRef<HTMLButtonElement>();
constructor(props: IProps) {
diff --git a/gui/src/renderer/components/SelectLocation.tsx b/gui/src/renderer/components/SelectLocation.tsx
index 2867ddaf45..528e8129ef 100644
--- a/gui/src/renderer/components/SelectLocation.tsx
+++ b/gui/src/renderer/components/SelectLocation.tsx
@@ -6,7 +6,7 @@ import { messages } from '../../shared/gettext';
import { IRelayLocationRedux } from '../redux/settings/reducers';
import { LocationScope } from '../redux/userinterface/reducers';
import BridgeLocations, { SpecialBridgeLocationType } from './BridgeLocations';
-import CustomScrollbars from './CustomScrollbars';
+import { CustomScrollbarsRef } from './CustomScrollbars';
import ExitLocations from './ExitLocations';
import ImageView from './ImageView';
import { Layout } from './Layout';
@@ -66,7 +66,7 @@ interface ISelectLocationSnapshot {
export default class SelectLocation extends React.Component<IProps, IState> {
public state = { showFilterMenu: false, headingHeight: 0 };
- private scrollView = React.createRef<CustomScrollbars>();
+ private scrollView = React.createRef<CustomScrollbarsRef>();
private spacePreAllocationViewRef = React.createRef<SpacePreAllocationView>();
private selectedExitLocationRef = React.createRef<React.ReactInstance>();
private selectedBridgeLocationRef = React.createRef<React.ReactInstance>();
diff --git a/gui/src/renderer/components/SplitTunnelingSettings.tsx b/gui/src/renderer/components/SplitTunnelingSettings.tsx
index 64faf178f2..97a4e5189c 100644
--- a/gui/src/renderer/components/SplitTunnelingSettings.tsx
+++ b/gui/src/renderer/components/SplitTunnelingSettings.tsx
@@ -11,7 +11,7 @@ import { IReduxState } from '../redux/store';
import Accordion from './Accordion';
import * as AppButton from './AppButton';
import * as Cell from './cell';
-import CustomScrollbars from './CustomScrollbars';
+import { CustomScrollbarsRef } from './CustomScrollbars';
import ImageView from './ImageView';
import { Layout } from './Layout';
import { ModalContainer, ModalAlert, ModalAlertType } from './Modal';
@@ -50,7 +50,7 @@ import {
export default function SplitTunneling() {
const { pop } = useHistory();
const [browsing, setBrowsing] = useState(false);
- const scrollbarsRef = useRef() as React.RefObject<CustomScrollbars>;
+ const scrollbarsRef = useRef() as React.RefObject<CustomScrollbarsRef>;
const scrollToTop = useCallback(() => scrollbarsRef.current?.scrollToTop(true), [scrollbarsRef]);