Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI edits and changes from VS #18

Merged
merged 3 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions lib/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
useState,
} from 'react';

import { KEY } from '../common/keys';
import { isEscape, KEY } from '../common/keys';
import { BooleanLike, classes } from '../common/react';
import styles from '../styles/components/Button.module.scss';
import { Box, BoxProps, computeBoxClassName, computeBoxProps } from './Box';
Expand Down Expand Up @@ -56,6 +56,8 @@ type Props = Partial<{
iconPosition: string;
/** Icon rotation */
iconRotation: number;
/** Icon size */
iconSize: number
/** Makes the icon spin */
iconSpin: BooleanLike;
/** Called when element is clicked */
Expand Down Expand Up @@ -89,6 +91,7 @@ export function Button(props: Props) {
iconColor,
iconPosition,
iconRotation,
iconSize,
iconSpin,
onClick,
selected,
Expand Down Expand Up @@ -140,7 +143,7 @@ export function Button(props: Props) {
}

// Refocus layout on pressing escape.
if (event.key === KEY.Escape) {
if (isEscape(event.key)) {
event.preventDefault();
}
}}
Expand All @@ -153,6 +156,7 @@ export function Button(props: Props) {
name={icon}
color={iconColor}
rotation={iconRotation}
size={iconSize}
spin={iconSpin}
/>
)}
Expand All @@ -171,6 +175,7 @@ export function Button(props: Props) {
name={icon}
color={iconColor}
rotation={iconRotation}
size={iconSize}
spin={iconSpin}
/>
)}
Expand Down Expand Up @@ -353,7 +358,7 @@ function ButtonInput(props: InputProps) {
commitResult(event);
return;
}
if (event.key === KEY.Escape) {
if (isEscape(event.key)) {
setInInput(false);
}
}}
Expand Down
14 changes: 12 additions & 2 deletions lib/components/Collapsible.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { Button } from './Button';
type Props = Partial<{
/** Buttons or other content to render inline with the button */
buttons: ReactNode;
/** Top margin of the child nodes, defaulted to 1 */
child_mt: number;
/** Icon to display with the collapsible */
icon: string;
/** Whether the collapsible is open */
Expand All @@ -16,7 +18,15 @@ type Props = Partial<{
BoxProps;

export function Collapsible(props: Props) {
const { children, color, title, buttons, icon, ...rest } = props;
const {
children,
child_mt = 1,
color,
title,
buttons,
icon,
...rest
} = props;
const [open, setOpen] = useState(props.open);

return (
Expand All @@ -37,7 +47,7 @@ export function Collapsible(props: Props) {
<div className="Table__cell Table__cell--collapsing">{buttons}</div>
)}
</div>
{open && <Box mt={1}>{children}</Box>}
{open && <Box mt={child_mt}>{children}</Box>}
</Box>
);
}
17 changes: 15 additions & 2 deletions lib/components/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { KeyboardEvent, SyntheticEvent, useEffect, useRef } from 'react';

import { KEY } from '../common/keys';
import { isEscape, KEY } from '../common/keys';
import { classes } from '../common/react';
import { debounce } from '../common/timer';
import styles from '../styles/components/Input.module.scss';
Expand Down Expand Up @@ -122,7 +122,7 @@ export function Input(props: Props) {
return;
}

if (event.key === KEY.Escape) {
if (isEscape(event.key)) {
onEscape?.(event);

event.currentTarget.value = toInputValue(value);
Expand Down Expand Up @@ -150,6 +150,19 @@ export function Input(props: Props) {
}, 1);
}, []);

useEffect(() => {
const input = inputRef.current;
if (!input) return;

if (document.activeElement === input) {
return;
}

const newValue = toInputValue(value);

if (input.value !== newValue) input.value = newValue;
});

return (
<Box
className={classes([
Expand Down
9 changes: 7 additions & 2 deletions lib/components/NoticeBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Box, BoxProps } from './Box';
type Props = ExclusiveProps & BoxProps;

/** You MUST use only one or none */
type NoticeType = 'info' | 'success' | 'danger';
type NoticeType = 'info' | 'success' | 'warning' | 'danger';

type None = {
[K in NoticeType]?: undefined;
Expand All @@ -21,13 +21,17 @@ type ExclusiveProps =
/** Green notice */
success: boolean;
})
| (Omit<None, 'warning'> & {
/** Orange notice */
warning: boolean;
})
| (Omit<None, 'danger'> & {
/** Red notice */
danger: boolean;
});

export function NoticeBox(props: Props) {
const { className, color, info, success, danger, ...rest } = props;
const { className, color, info, success, warning, danger, ...rest } = props;

return (
<Box
Expand All @@ -36,6 +40,7 @@ export function NoticeBox(props: Props) {
color && styles['color__' + color],
info && styles.info,
success && styles.success,
warning && styles.warning,
danger && styles.danger,
className,
])}
Expand Down
4 changes: 2 additions & 2 deletions lib/components/NumberInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
RefObject,
} from 'react';

import { KEY } from '../common/keys';
import { isEscape, KEY } from '../common/keys';
import { clamp } from '../common/math';
import { BooleanLike, classes } from '../common/react';
import styles from '../styles/components/NumberInput.module.scss';
Expand Down Expand Up @@ -239,7 +239,7 @@ export class NumberInput extends Component<Props, State> {
onChange?.(targetValue);
onDrag?.(targetValue);
}
} else if (event.key === KEY.Escape) {
} else if (isEscape(event.key)) {
this.setState({
editing: false,
});
Expand Down
135 changes: 75 additions & 60 deletions lib/components/Section.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { forwardRef, ReactNode, RefObject, useEffect } from 'react';
import { ReactNode, useEffect, useRef } from 'react';

import { addScrollableNode, removeScrollableNode } from '../common/events';
import { canRender, classes } from '../common/react';
Expand All @@ -14,12 +14,18 @@ type Props = Partial<{
fill: boolean;
/** If true, removes all section padding. */
fitted: boolean;
/** If true, fills the area without forcing height to 100% */
flexGrow: boolean;
/** If true, removes the section top padding */
noTopPadding: boolean;
/** @member Callback function for the `scroll` event */
onScroll: ((this: GlobalEventHandlers, ev: Event) => any) | null;
/** Shows or hides the scrollbar. */
scrollable: boolean;
/** Shows or hides the horizontal scrollbar. */
scrollableHorizontal: boolean;
/** If true, filly the area except for -3rem */
stretchContents: boolean;
/** Title of the section. */
title: ReactNode;
}> &
Expand Down Expand Up @@ -52,69 +58,78 @@ type Props = Partial<{
* </Section>
* ```
*/
export const Section = forwardRef(
(props: Props, forwardedRef: RefObject<HTMLDivElement>) => {
const {
buttons,
children,
className,
fill,
fitted,
onScroll,
scrollable,
scrollableHorizontal,
title,
container_id,
...rest
} = props;
export const Section = (props: Props) => {
const {
buttons,
children,
className,
fill,
fitted,
flexGrow,
noTopPadding,
onScroll,
scrollable,
scrollableHorizontal,
stretchContents,
title,
container_id,
...rest
} = props;

const hasTitle = canRender(title) || canRender(buttons);
const node = useRef(null);

/** We want to be able to scroll on hover, but using focus will steal it from inputs */
useEffect(() => {
if (!forwardedRef?.current) return;
if (!scrollable && !scrollableHorizontal) return;
const hasTitle = canRender(title) || canRender(buttons);

addScrollableNode(forwardedRef.current);
/** We want to be able to scroll on hover, but using focus will steal it from inputs */
useEffect(() => {
if (!node?.current) return;
if (!scrollable && !scrollableHorizontal) return;
const self = node.current;

return () => {
if (!forwardedRef?.current) return;
removeScrollableNode(forwardedRef.current!);
};
}, []);
addScrollableNode(self);

return (
<div
id={container_id || ''}
className={classes([
styles.section,
fill && styles.fill,
fitted && styles.fitted,
scrollable && styles.scrollable,
scrollableHorizontal && styles.scrollableHorizontal,
className,
computeBoxClassName(rest),
])}
{...computeBoxProps(rest)}
>
{hasTitle && (
<div className={styles.title}>
<span className={styles.titleText}>{title}</span>
<div className={styles.buttons}>{buttons}</div>
</div>
)}
<div className={styles.rest}>
<div
className={styles.content}
onScroll={onScroll}
// For posterity: the forwarded ref needs to be here specifically
// to actually let things interact with the scrolling.
ref={forwardedRef}
>
{children}
</div>
return () => {
if (!self) return;
removeScrollableNode(self!);
};
}, []);

return (
<div
id={container_id || ''}
className={classes([
styles.section,
fill && styles.fill,
fitted && styles.fitted,
scrollable && styles.scrollable,
scrollableHorizontal && styles.scrollableHorizontal,
flexGrow && styles.sectionFlex,
className,
computeBoxClassName(rest),
])}
{...computeBoxProps(rest)}
>
{hasTitle && (
<div className={styles.title}>
<span className={styles.titleText}>{title}</span>
<div className={styles.buttons}>{buttons}</div>
</div>
)}
<div className={styles.rest}>
<div
className={classes([
styles.content,
!!stretchContents && styles.stretchContents,
!!noTopPadding && styles.noTopPadding,
])}
onScroll={onScroll}
// For posterity: the forwarded ref needs to be here specifically
// to actually let things interact with the scrolling.
ref={node}
>
{children}
</div>
</div>
);
},
);
</div>
);
};
13 changes: 12 additions & 1 deletion lib/components/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type TabProps = Partial<{
className: string;
color: string;
icon: string;
iconSpin: boolean;
leftSlot: ReactNode;
onClick: (e?) => void;
rightSlot: ReactNode;
Expand Down Expand Up @@ -52,12 +53,21 @@ function Tab(props: TabProps) {
selected,
color,
icon,
iconSpin,
leftSlot,
rightSlot,
children,
onClick,
...rest
} = props;

const handleClick = (e) => {
if (onClick) {
onClick(e);
e.target.blur();
}
};

return (
<div
className={classes([
Expand All @@ -68,14 +78,15 @@ function Tab(props: TabProps) {
className,
computeBoxClassName(rest),
])}
onClick={handleClick}
{...computeBoxProps(rest)}
>
{(canRender(leftSlot) && (
<div className={styles.tab__left}>{leftSlot}</div>
)) ||
(!!icon && (
<div className={styles.tab__left}>
<Icon name={icon} />
<Icon name={icon} spin={iconSpin} />
</div>
))}
<div className={styles.tab__text}>{children}</div>
Expand Down
Loading