fix: Implement custom Select Input (#2571)
This commit is contained in:
parent
99381d10ff
commit
40e09dd829
|
@ -2,7 +2,7 @@
|
|||
import { CheckmarkIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { MenuItem as BaseMenuItem } from "reakit/Menu";
|
||||
import styled from "styled-components";
|
||||
import styled, { css } from "styled-components";
|
||||
import breakpoint from "styled-components-breakpoint";
|
||||
import MenuIconWrapper from "../MenuIconWrapper";
|
||||
|
||||
|
@ -88,12 +88,12 @@ const Spacer = styled.svg`
|
|||
flex-shrink: 0;
|
||||
`;
|
||||
|
||||
export const MenuAnchor = styled.a`
|
||||
export const MenuAnchorCSS = css`
|
||||
display: flex;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
padding: 12px;
|
||||
padding-left: ${(props) => 12 + props.level * 10}px;
|
||||
padding-left: ${(props) => 12 + (props.level || 0) * 10}px;
|
||||
width: 100%;
|
||||
min-height: 32px;
|
||||
background: none;
|
||||
|
@ -138,5 +138,8 @@ export const MenuAnchor = styled.a`
|
|||
font-size: 14px;
|
||||
`};
|
||||
`;
|
||||
export const MenuAnchor = styled.a`
|
||||
${MenuAnchorCSS}
|
||||
`;
|
||||
|
||||
export default MenuItem;
|
||||
|
|
|
@ -4,9 +4,8 @@ import { Portal } from "react-portal";
|
|||
import { Menu } from "reakit/Menu";
|
||||
import styled from "styled-components";
|
||||
import breakpoint from "styled-components-breakpoint";
|
||||
import useMobile from "hooks/useMobile";
|
||||
import useMenuHeight from "hooks/useMenuHeight";
|
||||
import usePrevious from "hooks/usePrevious";
|
||||
import useWindowSize from "hooks/useWindowSize";
|
||||
import {
|
||||
fadeIn,
|
||||
fadeAndSlideUp,
|
||||
|
@ -35,9 +34,7 @@ export default function ContextMenu({
|
|||
...rest
|
||||
}: Props) {
|
||||
const previousVisible = usePrevious(rest.visible);
|
||||
const [maxHeight, setMaxHeight] = React.useState(undefined);
|
||||
const isMobile = useMobile();
|
||||
const { height: windowHeight } = useWindowSize();
|
||||
const maxHeight = useMenuHeight(rest.visible, rest.unstable_disclosureRef);
|
||||
const backgroundRef = React.useRef();
|
||||
|
||||
React.useEffect(() => {
|
||||
|
@ -55,21 +52,6 @@ export default function ContextMenu({
|
|||
|
||||
// sets the menu height based on the available space between the disclosure/
|
||||
// trigger and the bottom of the window
|
||||
React.useLayoutEffect(() => {
|
||||
const padding = 8;
|
||||
|
||||
if (rest.visible && !isMobile) {
|
||||
setMaxHeight(
|
||||
rest.unstable_disclosureRef?.current
|
||||
? windowHeight -
|
||||
rest.unstable_disclosureRef.current.getBoundingClientRect()
|
||||
.bottom -
|
||||
padding
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
}, [rest.visible, rest.unstable_disclosureRef, windowHeight, isMobile]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Menu hideOnClickOutside preventBodyScroll {...rest}>
|
||||
|
@ -104,7 +86,7 @@ export default function ContextMenu({
|
|||
);
|
||||
}
|
||||
|
||||
const Backdrop = styled.div`
|
||||
export const Backdrop = styled.div`
|
||||
animation: ${fadeIn} 200ms ease-in-out;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
@ -119,7 +101,7 @@ const Backdrop = styled.div`
|
|||
`};
|
||||
`;
|
||||
|
||||
const Position = styled.div`
|
||||
export const Position = styled.div`
|
||||
position: absolute;
|
||||
z-index: ${(props) => props.theme.depths.menu};
|
||||
|
||||
|
@ -133,7 +115,7 @@ const Position = styled.div`
|
|||
`};
|
||||
`;
|
||||
|
||||
const Background = styled.div`
|
||||
export const Background = styled.div`
|
||||
animation: ${mobileContextMenu} 200ms ease;
|
||||
transform-origin: 50% 100%;
|
||||
max-width: 100%;
|
||||
|
@ -154,8 +136,7 @@ const Background = styled.div`
|
|||
${breakpoint("tablet")`
|
||||
animation: ${(props) =>
|
||||
props.topAnchor ? fadeAndSlideDown : fadeAndSlideUp} 200ms ease;
|
||||
transform-origin: ${(props) =>
|
||||
props.rightAnchor === "bottom-end" ? "75%" : "25%"} 0;
|
||||
transform-origin: ${(props) => (props.rightAnchor ? "75%" : "25%")} 0;
|
||||
max-width: 276px;
|
||||
background: ${(props) => props.theme.menuBackground};
|
||||
box-shadow: ${(props) => props.theme.menuShadow};
|
||||
|
|
|
@ -1,80 +1,91 @@
|
|||
// @flow
|
||||
import { observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import {
|
||||
Select,
|
||||
SelectOption,
|
||||
useSelectState,
|
||||
useSelectPopover,
|
||||
SelectPopover,
|
||||
} from "@renderlesskit/react";
|
||||
import { CheckmarkIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { VisuallyHidden } from "reakit/VisuallyHidden";
|
||||
import styled from "styled-components";
|
||||
import breakpoint from "styled-components-breakpoint";
|
||||
import { Outline, LabelText } from "./Input";
|
||||
|
||||
const Select = styled.select`
|
||||
border: 0;
|
||||
flex: 1;
|
||||
padding: 4px 0;
|
||||
margin: 0 12px;
|
||||
outline: none;
|
||||
background: none;
|
||||
color: ${(props) => props.theme.text};
|
||||
height: 30px;
|
||||
font-size: 14px;
|
||||
|
||||
option {
|
||||
background: ${(props) => props.theme.buttonNeutralBackground};
|
||||
}
|
||||
|
||||
&:disabled,
|
||||
&::placeholder {
|
||||
color: ${(props) => props.theme.placeholder};
|
||||
}
|
||||
|
||||
${breakpoint("mobile", "tablet")`
|
||||
font-size: 16px;
|
||||
`};
|
||||
`;
|
||||
|
||||
const Wrapper = styled.label`
|
||||
display: block;
|
||||
max-width: ${(props) => (props.short ? "350px" : "100%")};
|
||||
`;
|
||||
import styled, { css } from "styled-components";
|
||||
import Button, { Inner } from "components/Button";
|
||||
import { Position, Background, Backdrop } from "./ContextMenu";
|
||||
import { MenuAnchorCSS } from "./ContextMenu/MenuItem";
|
||||
import { LabelText } from "./Input";
|
||||
import useMenuHeight from "hooks/useMenuHeight";
|
||||
|
||||
export type Option = { label: string, value: string };
|
||||
|
||||
export type Props = {
|
||||
value?: string,
|
||||
label?: string,
|
||||
nude?: boolean,
|
||||
ariaLabel: string,
|
||||
short?: boolean,
|
||||
className?: string,
|
||||
labelHidden?: boolean,
|
||||
options: Option[],
|
||||
onBlur?: () => void,
|
||||
onFocus?: () => void,
|
||||
onChange: (string) => Promise<void> | void,
|
||||
};
|
||||
|
||||
@observer
|
||||
class InputSelect extends React.Component<Props> {
|
||||
@observable focused: boolean = false;
|
||||
const getOptionFromValue = (options: Option[], value) => {
|
||||
return options.find((option) => option.value === value) || {};
|
||||
};
|
||||
|
||||
handleBlur = () => {
|
||||
this.focused = false;
|
||||
};
|
||||
const InputSelect = (props: Props) => {
|
||||
const {
|
||||
value,
|
||||
label,
|
||||
className,
|
||||
labelHidden,
|
||||
options,
|
||||
short,
|
||||
ariaLabel,
|
||||
onChange,
|
||||
nude,
|
||||
} = props;
|
||||
|
||||
handleFocus = () => {
|
||||
this.focused = true;
|
||||
};
|
||||
const select = useSelectState({
|
||||
gutter: 0,
|
||||
modal: true,
|
||||
selectedValue: value,
|
||||
});
|
||||
|
||||
render() {
|
||||
const {
|
||||
label,
|
||||
className,
|
||||
labelHidden,
|
||||
options,
|
||||
short,
|
||||
...rest
|
||||
} = this.props;
|
||||
const popOver = useSelectPopover({
|
||||
...select,
|
||||
hideOnClickOutside: true,
|
||||
preventBodyScroll: true,
|
||||
});
|
||||
|
||||
const wrappedLabel = <LabelText>{label}</LabelText>;
|
||||
const previousValue = React.useRef(value);
|
||||
const buttonRef = React.useRef();
|
||||
const minWidth = buttonRef.current?.offsetWidth || 0;
|
||||
|
||||
return (
|
||||
const maxHeight = useMenuHeight(
|
||||
select.visible,
|
||||
select.unstable_disclosureRef
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (previousValue.current === select.selectedValue) return;
|
||||
|
||||
previousValue.current = select.selectedValue;
|
||||
async function load() {
|
||||
await onChange(select.selectedValue);
|
||||
}
|
||||
load();
|
||||
}, [onChange, select.selectedValue]);
|
||||
|
||||
const wrappedLabel = <LabelText>{label}</LabelText>;
|
||||
|
||||
const selectedValueIndex = options.findIndex(
|
||||
(option) => option.value === select.selectedValue
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Wrapper short={short}>
|
||||
{label &&
|
||||
(labelHidden ? (
|
||||
|
@ -82,18 +93,126 @@ class InputSelect extends React.Component<Props> {
|
|||
) : (
|
||||
wrappedLabel
|
||||
))}
|
||||
<Outline focused={this.focused} className={className}>
|
||||
<Select onBlur={this.handleBlur} onFocus={this.handleFocus} {...rest}>
|
||||
{options.map((option) => (
|
||||
<option value={option.value} key={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</Outline>
|
||||
<Select {...select} aria-label={ariaLabel} ref={buttonRef}>
|
||||
{(props) => (
|
||||
<StyledButton
|
||||
neutral
|
||||
disclosure
|
||||
className={className}
|
||||
nude={nude}
|
||||
{...props}
|
||||
>
|
||||
{getOptionFromValue(options, select.selectedValue).label ||
|
||||
`Select a ${ariaLabel}`}
|
||||
</StyledButton>
|
||||
)}
|
||||
</Select>
|
||||
<SelectPopover {...select} {...popOver}>
|
||||
{(props) => {
|
||||
const topAnchor = props.style.top === "0";
|
||||
const rightAnchor = props.placement === "bottom-end";
|
||||
|
||||
// offset top of select to place selected item under the cursor
|
||||
if (selectedValueIndex !== -1) {
|
||||
props.style.top = `-${(selectedValueIndex + 1) * 32}px`;
|
||||
}
|
||||
|
||||
return (
|
||||
<Positioner {...props}>
|
||||
<Background
|
||||
dir="auto"
|
||||
topAnchor={topAnchor}
|
||||
rightAnchor={rightAnchor}
|
||||
style={
|
||||
maxHeight && topAnchor
|
||||
? { maxHeight, minWidth }
|
||||
: { minWidth }
|
||||
}
|
||||
>
|
||||
{select.visible || select.animating
|
||||
? options.map((option) => (
|
||||
<StyledSelectOption
|
||||
{...select}
|
||||
value={option.value}
|
||||
key={option.value}
|
||||
>
|
||||
{select.selectedValue !== undefined && (
|
||||
<>
|
||||
{select.selectedValue === option.value ? (
|
||||
<CheckmarkIcon color="currentColor" />
|
||||
) : (
|
||||
<Spacer />
|
||||
)}
|
||||
|
||||
</>
|
||||
)}
|
||||
{option.label}
|
||||
</StyledSelectOption>
|
||||
))
|
||||
: null}
|
||||
</Background>
|
||||
</Positioner>
|
||||
);
|
||||
}}
|
||||
</SelectPopover>
|
||||
</Wrapper>
|
||||
);
|
||||
{(select.visible || select.animating) && <Backdrop />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Spacer = styled.svg`
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
flex-shrink: 0;
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
font-weight: normal;
|
||||
text-transform: none;
|
||||
margin-bottom: 16px;
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
${(props) =>
|
||||
props.nude &&
|
||||
css`
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
`}
|
||||
|
||||
${Inner} {
|
||||
line-height: 28px;
|
||||
padding-left: 16px;
|
||||
padding-right: 8px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const StyledSelectOption = styled(SelectOption)`
|
||||
${MenuAnchorCSS}
|
||||
`;
|
||||
|
||||
const Wrapper = styled.label`
|
||||
display: block;
|
||||
max-width: ${(props) => (props.short ? "350px" : "100%")};
|
||||
`;
|
||||
|
||||
const Positioner = styled(Position)`
|
||||
&.focus-visible {
|
||||
${StyledSelectOption} {
|
||||
&[aria-selected="true"] {
|
||||
color: ${(props) => props.theme.white};
|
||||
background: ${(props) => props.theme.primary};
|
||||
box-shadow: none;
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
fill: ${(props) => props.theme.white};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default InputSelect;
|
||||
|
|
|
@ -4,19 +4,33 @@ import { useTranslation } from "react-i18next";
|
|||
import InputSelect, { type Props, type Option } from "./InputSelect";
|
||||
|
||||
export default function InputSelectPermission(
|
||||
props: $Rest<Props, { options: Array<Option> }>
|
||||
props: $Rest<$Exact<Props>, {| options: Array<Option>, ariaLabel: string |}>
|
||||
) {
|
||||
const { value, onChange, ...rest } = props;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChange = React.useCallback(
|
||||
(value) => {
|
||||
if (value === "no_access") {
|
||||
value = "";
|
||||
}
|
||||
onChange(value);
|
||||
},
|
||||
[onChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<InputSelect
|
||||
label={t("Default access")}
|
||||
options={[
|
||||
{ label: t("View and edit"), value: "read_write" },
|
||||
{ label: t("View only"), value: "read" },
|
||||
{ label: t("No access"), value: "" },
|
||||
{ label: t("No access"), value: "no_access" },
|
||||
]}
|
||||
{...props}
|
||||
ariaLabel={t("Default access")}
|
||||
value={value || "no_access"}
|
||||
onChange={handleChange}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@ import * as React from "react";
|
|||
import { useTranslation } from "react-i18next";
|
||||
import InputSelect, { type Props, type Option } from "components/InputSelect";
|
||||
|
||||
const InputSelectRole = (props: $Rest<Props, { options: Array<Option> }>) => {
|
||||
const InputSelectRole = (
|
||||
props: $Rest<$Exact<Props>, {| options: Array<Option>, ariaLabel: string |}>
|
||||
) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
|
@ -14,6 +16,7 @@ const InputSelectRole = (props: $Rest<Props, { options: Array<Option> }>) => {
|
|||
{ label: t("Viewer"), value: "viewer" },
|
||||
{ label: t("Admin"), value: "admin" },
|
||||
]}
|
||||
ariaLabel={t("Role")}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -36,7 +36,11 @@ const ListItem = (
|
|||
</Subtitle>
|
||||
)}
|
||||
</Content>
|
||||
{actions && <Actions $selected={selected}>{actions}</Actions>}
|
||||
{actions && (
|
||||
<Actions $selected={selected} gap={4}>
|
||||
{actions}
|
||||
</Actions>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
|
@ -104,7 +108,6 @@ const Subtitle = styled.p`
|
|||
export const Actions = styled(Flex)`
|
||||
align-self: center;
|
||||
justify-content: center;
|
||||
margin-right: 4px;
|
||||
color: ${(props) =>
|
||||
props.$selected ? props.theme.white : props.theme.textSecondary};
|
||||
`;
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// @flow
|
||||
import * as React from "react";
|
||||
import { type ElementRef } from "reakit";
|
||||
import useMobile from "hooks/useMobile";
|
||||
import useWindowSize from "hooks/useWindowSize";
|
||||
|
||||
const useMenuHeight = (
|
||||
visible: void | boolean,
|
||||
unstable_disclosureRef: void | { current: null | ElementRef<"button"> }
|
||||
) => {
|
||||
const [maxHeight, setMaxHeight] = React.useState(undefined);
|
||||
const isMobile = useMobile();
|
||||
const { height: windowHeight } = useWindowSize();
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
const padding = 8;
|
||||
|
||||
if (visible && !isMobile) {
|
||||
setMaxHeight(
|
||||
unstable_disclosureRef?.current
|
||||
? windowHeight -
|
||||
unstable_disclosureRef.current.getBoundingClientRect().bottom -
|
||||
padding
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
}, [visible, unstable_disclosureRef, windowHeight, isMobile]);
|
||||
|
||||
return maxHeight;
|
||||
};
|
||||
|
||||
export default useMenuHeight;
|
|
@ -54,8 +54,8 @@ const CollectionEdit = ({ collection, onSubmit }: Props) => {
|
|||
[collection, color, icon, name, onSubmit, showToast, sort, t]
|
||||
);
|
||||
|
||||
const handleSortChange = (ev: SyntheticInputEvent<HTMLSelectElement>) => {
|
||||
const [field, direction] = ev.target.value.split(".");
|
||||
const handleSortChange = (value: string) => {
|
||||
const [field, direction] = value.split(".");
|
||||
|
||||
if (direction === "asc" || direction === "desc") {
|
||||
setSort({ field, direction });
|
||||
|
@ -101,6 +101,7 @@ const CollectionEdit = ({ collection, onSubmit }: Props) => {
|
|||
]}
|
||||
value={`${sort.field}.${sort.direction}`}
|
||||
onChange={handleSortChange}
|
||||
ariaLabel={t("Sort")}
|
||||
/>
|
||||
<Button type="submit" disabled={isSaving || !collection.name}>
|
||||
{isSaving ? `${t("Saving")}…` : t("Save")}
|
||||
|
|
|
@ -88,8 +88,8 @@ class CollectionNew extends React.Component<Props> {
|
|||
this.hasOpenedIconPicker = true;
|
||||
};
|
||||
|
||||
handlePermissionChange = (ev: SyntheticInputEvent<HTMLInputElement>) => {
|
||||
this.permission = ev.target.value;
|
||||
handlePermissionChange = (newPermission: string) => {
|
||||
this.permission = newPermission;
|
||||
};
|
||||
|
||||
handleSharingChange = (ev: SyntheticInputEvent<HTMLInputElement>) => {
|
||||
|
|
|
@ -5,7 +5,7 @@ import styled from "styled-components";
|
|||
import CollectionGroupMembership from "models/CollectionGroupMembership";
|
||||
import Group from "models/Group";
|
||||
import GroupListItem from "components/GroupListItem";
|
||||
import InputSelect from "components/InputSelect";
|
||||
import InputSelect, { type Props as SelectProps } from "components/InputSelect";
|
||||
import CollectionGroupMemberMenu from "menus/CollectionGroupMemberMenu";
|
||||
|
||||
type Props = {|
|
||||
|
@ -47,8 +47,10 @@ const CollectionGroupMemberListItem = ({
|
|||
? collectionGroupMembership.permission
|
||||
: undefined
|
||||
}
|
||||
onChange={(ev) => onUpdate(ev.target.value)}
|
||||
onChange={onUpdate}
|
||||
ariaLabel={t("Permissions")}
|
||||
labelHidden
|
||||
nude
|
||||
/>
|
||||
<Spacer />
|
||||
<CollectionGroupMemberMenu
|
||||
|
@ -65,7 +67,7 @@ const Spacer = styled.div`
|
|||
width: 8px;
|
||||
`;
|
||||
|
||||
const Select = styled(InputSelect)`
|
||||
const Select = (styled(InputSelect)`
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
border-color: transparent;
|
||||
|
@ -73,6 +75,6 @@ const Select = styled(InputSelect)`
|
|||
select {
|
||||
margin: 0;
|
||||
}
|
||||
`;
|
||||
`: React.ComponentType<SelectProps>);
|
||||
|
||||
export default CollectionGroupMemberListItem;
|
||||
|
|
|
@ -8,7 +8,7 @@ import Avatar from "components/Avatar";
|
|||
import Badge from "components/Badge";
|
||||
import Button from "components/Button";
|
||||
import Flex from "components/Flex";
|
||||
import InputSelect from "components/InputSelect";
|
||||
import InputSelect, { type Props as SelectProps } from "components/InputSelect";
|
||||
import ListItem from "components/List/Item";
|
||||
import Time from "components/Time";
|
||||
import MemberMenu from "menus/MemberMenu";
|
||||
|
@ -64,9 +64,11 @@ const MemberListItem = ({
|
|||
label={t("Permissions")}
|
||||
options={PERMISSIONS}
|
||||
value={membership ? membership.permission : undefined}
|
||||
onChange={(ev) => onUpdate(ev.target.value)}
|
||||
onChange={onUpdate}
|
||||
disabled={!canEdit}
|
||||
ariaLabel={t("Permissions")}
|
||||
labelHidden
|
||||
nude
|
||||
/>
|
||||
)}
|
||||
{canEdit && (
|
||||
|
@ -90,7 +92,7 @@ const Spacer = styled.div`
|
|||
width: 8px;
|
||||
`;
|
||||
|
||||
const Select = styled(InputSelect)`
|
||||
const Select = (styled(InputSelect)`
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
border-color: transparent;
|
||||
|
@ -98,6 +100,6 @@ const Select = styled(InputSelect)`
|
|||
select {
|
||||
margin: 0;
|
||||
}
|
||||
`;
|
||||
`: React.ComponentType<SelectProps>);
|
||||
|
||||
export default MemberListItem;
|
||||
|
|
|
@ -138,9 +138,9 @@ function CollectionPermissions({ collection }: Props) {
|
|||
);
|
||||
|
||||
const handleChangePermission = React.useCallback(
|
||||
async (ev) => {
|
||||
async (permission: string) => {
|
||||
try {
|
||||
await collection.save({ permission: ev.target.value });
|
||||
await collection.save({ permission });
|
||||
showToast(t("Default access permissions were updated"), {
|
||||
type: "success",
|
||||
});
|
||||
|
|
|
@ -112,10 +112,10 @@ function Invite({ onSubmit }: Props) {
|
|||
});
|
||||
}, [showToast, t]);
|
||||
|
||||
const handleRoleChange = React.useCallback((ev, index) => {
|
||||
const handleRoleChange = React.useCallback((role: Role, index: number) => {
|
||||
setInvites((prevInvites) => {
|
||||
const newInvites = [...prevInvites];
|
||||
newInvites[index]["role"] = ev.target.value;
|
||||
newInvites[index]["role"] = role;
|
||||
return newInvites;
|
||||
});
|
||||
}, []);
|
||||
|
@ -194,7 +194,7 @@ function Invite({ onSubmit }: Props) {
|
|||
required={!!invite.email}
|
||||
/>
|
||||
<InputSelectRole
|
||||
onChange={(ev) => handleRoleChange(ev, index)}
|
||||
onChange={(role: any) => handleRoleChange((role: Role), index)}
|
||||
value={invite.role}
|
||||
labelHidden={index !== 0}
|
||||
short
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
// @flow
|
||||
import { observable } from "mobx";
|
||||
import { observer, inject } from "mobx-react";
|
||||
import { observer } from "mobx-react";
|
||||
import { ProfileIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { Trans, withTranslation, type TFunction } from "react-i18next";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import { languageOptions } from "shared/i18n";
|
||||
import AuthStore from "stores/AuthStore";
|
||||
import ToastsStore from "stores/ToastsStore";
|
||||
import UserDelete from "scenes/UserDelete";
|
||||
import Button from "components/Button";
|
||||
import Flex from "components/Flex";
|
||||
|
@ -17,162 +14,137 @@ import Input, { LabelText } from "components/Input";
|
|||
import InputSelect from "components/InputSelect";
|
||||
import Scene from "components/Scene";
|
||||
import ImageUpload from "./components/ImageUpload";
|
||||
import useStores from "hooks/useStores";
|
||||
import useToasts from "hooks/useToasts";
|
||||
|
||||
type Props = {
|
||||
auth: AuthStore,
|
||||
toasts: ToastsStore,
|
||||
t: TFunction,
|
||||
};
|
||||
const Profile = () => {
|
||||
const { auth } = useStores();
|
||||
const form = React.useRef<?HTMLFormElement>();
|
||||
const [name, setName] = React.useState<string>(auth.user?.name || "");
|
||||
const [avatarUrl, setAvatarUrl] = React.useState<?string>();
|
||||
const [showDeleteModal, setShowDeleteModal] = React.useState(false);
|
||||
const [language, setLanguage] = React.useState(auth.user?.language);
|
||||
|
||||
@observer
|
||||
class Profile extends React.Component<Props> {
|
||||
timeout: TimeoutID;
|
||||
form: ?HTMLFormElement;
|
||||
const { showToast } = useToasts();
|
||||
const { t } = useTranslation();
|
||||
|
||||
@observable name: string;
|
||||
@observable avatarUrl: ?string;
|
||||
@observable showDeleteModal: boolean = false;
|
||||
@observable language: string;
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.auth.user) {
|
||||
this.name = this.props.auth.user.name;
|
||||
this.language = this.props.auth.user.language;
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
handleSubmit = async (ev: SyntheticEvent<>) => {
|
||||
const { t } = this.props;
|
||||
const handleSubmit = async (ev: SyntheticEvent<>) => {
|
||||
ev.preventDefault();
|
||||
|
||||
await this.props.auth.updateUser({
|
||||
name: this.name,
|
||||
avatarUrl: this.avatarUrl,
|
||||
language: this.language,
|
||||
await auth.updateUser({
|
||||
name,
|
||||
avatarUrl,
|
||||
language,
|
||||
});
|
||||
|
||||
this.props.toasts.showToast(t("Profile saved"), { type: "success" });
|
||||
showToast(t("Profile saved"), { type: "success" });
|
||||
};
|
||||
|
||||
handleNameChange = (ev: SyntheticInputEvent<*>) => {
|
||||
this.name = ev.target.value;
|
||||
const handleNameChange = (ev: SyntheticInputEvent<*>) => {
|
||||
setName(ev.target.value);
|
||||
};
|
||||
|
||||
handleAvatarUpload = async (avatarUrl: string) => {
|
||||
const { t } = this.props;
|
||||
this.avatarUrl = avatarUrl;
|
||||
const handleAvatarUpload = async (avatarUrl: string) => {
|
||||
setAvatarUrl(avatarUrl);
|
||||
|
||||
await this.props.auth.updateUser({
|
||||
avatarUrl: this.avatarUrl,
|
||||
await auth.updateUser({
|
||||
avatarUrl,
|
||||
});
|
||||
this.props.toasts.showToast(t("Profile picture updated"), {
|
||||
showToast(t("Profile picture updated"), {
|
||||
type: "success",
|
||||
});
|
||||
};
|
||||
|
||||
handleAvatarError = (error: ?string) => {
|
||||
const { t } = this.props;
|
||||
this.props.toasts.showToast(
|
||||
error || t("Unable to upload new profile picture"),
|
||||
{ type: "error" }
|
||||
);
|
||||
const handleAvatarError = (error: ?string) => {
|
||||
showToast(error || t("Unable to upload new profile picture"), {
|
||||
type: "error",
|
||||
});
|
||||
};
|
||||
|
||||
handleLanguageChange = (ev: SyntheticInputEvent<*>) => {
|
||||
this.language = ev.target.value;
|
||||
const handleLanguageChange = (value: string) => {
|
||||
setLanguage(value);
|
||||
};
|
||||
|
||||
toggleDeleteAccount = () => {
|
||||
this.showDeleteModal = !this.showDeleteModal;
|
||||
const toggleDeleteAccount = () => {
|
||||
setShowDeleteModal((prev) => !prev);
|
||||
};
|
||||
|
||||
get isValid() {
|
||||
return this.form && this.form.checkValidity();
|
||||
}
|
||||
const isValid = form.current && form.current.checkValidity();
|
||||
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
const { user, isSaving } = this.props.auth;
|
||||
if (!user) return null;
|
||||
const avatarUrl = this.avatarUrl || user.avatarUrl;
|
||||
const { user, isSaving } = auth;
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
<Scene title={t("Profile")} icon={<ProfileIcon color="currentColor" />}>
|
||||
<Heading>{t("Profile")}</Heading>
|
||||
<ProfilePicture column>
|
||||
<LabelText>{t("Photo")}</LabelText>
|
||||
<AvatarContainer>
|
||||
<ImageUpload
|
||||
onSuccess={this.handleAvatarUpload}
|
||||
onError={this.handleAvatarError}
|
||||
return (
|
||||
<Scene title={t("Profile")} icon={<ProfileIcon color="currentColor" />}>
|
||||
<Heading>{t("Profile")}</Heading>
|
||||
<ProfilePicture column>
|
||||
<LabelText>{t("Photo")}</LabelText>
|
||||
<AvatarContainer>
|
||||
<ImageUpload
|
||||
onSuccess={handleAvatarUpload}
|
||||
onError={handleAvatarError}
|
||||
>
|
||||
<Avatar src={avatarUrl || user?.avatarUrl} />
|
||||
<Flex auto align="center" justify="center">
|
||||
{t("Upload")}
|
||||
</Flex>
|
||||
</ImageUpload>
|
||||
</AvatarContainer>
|
||||
</ProfilePicture>
|
||||
<form onSubmit={handleSubmit} ref={form}>
|
||||
<Input
|
||||
label={t("Full name")}
|
||||
autoComplete="name"
|
||||
value={name}
|
||||
onChange={handleNameChange}
|
||||
required
|
||||
short
|
||||
/>
|
||||
<br />
|
||||
<InputSelect
|
||||
label={t("Language")}
|
||||
options={languageOptions}
|
||||
value={language}
|
||||
onChange={handleLanguageChange}
|
||||
ariaLabel={t("Language")}
|
||||
short
|
||||
/>
|
||||
<HelpText small>
|
||||
<Trans>
|
||||
Please note that translations are currently in early access.
|
||||
<br />
|
||||
Community contributions are accepted though our{" "}
|
||||
<a
|
||||
href="https://translate.getoutline.com"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<Avatar src={avatarUrl} />
|
||||
<Flex auto align="center" justify="center">
|
||||
{t("Upload")}
|
||||
</Flex>
|
||||
</ImageUpload>
|
||||
</AvatarContainer>
|
||||
</ProfilePicture>
|
||||
<form onSubmit={this.handleSubmit} ref={(ref) => (this.form = ref)}>
|
||||
<Input
|
||||
label={t("Full name")}
|
||||
autoComplete="name"
|
||||
value={this.name}
|
||||
onChange={this.handleNameChange}
|
||||
required
|
||||
short
|
||||
/>
|
||||
<br />
|
||||
<InputSelect
|
||||
label={t("Language")}
|
||||
options={languageOptions}
|
||||
value={this.language}
|
||||
onChange={this.handleLanguageChange}
|
||||
short
|
||||
/>
|
||||
<HelpText small>
|
||||
<Trans>
|
||||
Please note that translations are currently in early access.
|
||||
<br />
|
||||
Community contributions are accepted though our{" "}
|
||||
<a
|
||||
href="https://translate.getoutline.com"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
translation portal
|
||||
</a>
|
||||
</Trans>
|
||||
.
|
||||
</HelpText>
|
||||
<Button type="submit" disabled={isSaving || !this.isValid}>
|
||||
{isSaving ? `${t("Saving")}…` : t("Save")}
|
||||
</Button>
|
||||
</form>
|
||||
translation portal
|
||||
</a>
|
||||
</Trans>
|
||||
.
|
||||
</HelpText>
|
||||
<Button type="submit" disabled={isSaving || !isValid}>
|
||||
{isSaving ? `${t("Saving")}…` : t("Save")}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<DangerZone>
|
||||
<h2>{t("Delete Account")}</h2>
|
||||
<HelpText small>
|
||||
<Trans>
|
||||
You may delete your account at any time, note that this is
|
||||
unrecoverable
|
||||
</Trans>
|
||||
</HelpText>
|
||||
<Button onClick={this.toggleDeleteAccount} neutral>
|
||||
{t("Delete account")}…
|
||||
</Button>
|
||||
</DangerZone>
|
||||
{this.showDeleteModal && (
|
||||
<UserDelete onRequestClose={this.toggleDeleteAccount} />
|
||||
)}
|
||||
</Scene>
|
||||
);
|
||||
}
|
||||
}
|
||||
<DangerZone>
|
||||
<h2>{t("Delete Account")}</h2>
|
||||
<HelpText small>
|
||||
<Trans>
|
||||
You may delete your account at any time, note that this is
|
||||
unrecoverable
|
||||
</Trans>
|
||||
</HelpText>
|
||||
<Button onClick={toggleDeleteAccount} neutral>
|
||||
{t("Delete account")}…
|
||||
</Button>
|
||||
</DangerZone>
|
||||
{showDeleteModal && <UserDelete onRequestClose={toggleDeleteAccount} />}
|
||||
</Scene>
|
||||
);
|
||||
};
|
||||
|
||||
const DangerZone = styled.div`
|
||||
margin-top: 60px;
|
||||
|
@ -215,4 +187,4 @@ const Avatar = styled.img`
|
|||
${avatarStyles};
|
||||
`;
|
||||
|
||||
export default withTranslation()<Profile>(inject("auth", "toasts")(Profile));
|
||||
export default observer(Profile);
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
"@hocuspocus/server": "^1.0.0-alpha.73",
|
||||
"@outlinewiki/koa-passport": "^4.1.4",
|
||||
"@outlinewiki/passport-azure-ad-oauth2": "^0.1.0",
|
||||
"@renderlesskit/react": "^0.6.0",
|
||||
"@sentry/node": "^6.3.1",
|
||||
"@sentry/react": "^6.3.1",
|
||||
"@sentry/tracing": "^6.3.1",
|
||||
|
@ -224,4 +225,4 @@
|
|||
"js-yaml": "^3.13.1"
|
||||
},
|
||||
"version": "0.59.0"
|
||||
}
|
||||
}
|
||||
|
|
257
yarn.lock
257
yarn.lock
|
@ -978,10 +978,10 @@
|
|||
core-js-pure "^3.0.0"
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.3.1", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||
version "7.15.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.3.tgz#2e1c2880ca118e5b2f9988322bd8a7656a32502b"
|
||||
integrity sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.3.1", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||
version "7.15.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a"
|
||||
integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
|
@ -1062,6 +1062,41 @@
|
|||
serialize-query-params "^1.3.3"
|
||||
superstruct "^0.8.3"
|
||||
|
||||
"@chakra-ui/counter@^1.1.9":
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/counter/-/counter-1.1.9.tgz#954794624806ea6a00f0ebdd3d50c6838d0b41fc"
|
||||
integrity sha512-WHkYSHJynkFwVFD6wg6afDteBeAmDHV35/tPMwpyTcgagpF99xY/8mULnBoLkkCc/PMe+meHuZJEXuCaxy4ecg==
|
||||
dependencies:
|
||||
"@chakra-ui/hooks" "1.6.0"
|
||||
"@chakra-ui/utils" "1.8.2"
|
||||
|
||||
"@chakra-ui/hooks@1.6.0", "@chakra-ui/hooks@^1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/hooks/-/hooks-1.6.0.tgz#94f54540298b6a5a7ef68b15e451e76b0ee1fed4"
|
||||
integrity sha512-5QFICaE1omNCJyVQQX62sZvRvIpI4VansN2AvZpSdrMjRiWvmBNLZN2Khr7+8j6F7uDh5LSgTxiP02vWLp12hA==
|
||||
dependencies:
|
||||
"@chakra-ui/react-utils" "1.1.2"
|
||||
"@chakra-ui/utils" "1.8.2"
|
||||
compute-scroll-into-view "1.0.14"
|
||||
copy-to-clipboard "3.3.1"
|
||||
|
||||
"@chakra-ui/react-utils@1.1.2", "@chakra-ui/react-utils@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/react-utils/-/react-utils-1.1.2.tgz#7ea80b6ae25bd7b182095cc9ffaad23c464408b5"
|
||||
integrity sha512-S8jPVKGZH2qF7ZGxl/0DF/dXXI2AxDNGf4Ahi2LGHqajMvqBB7vtYIRRmIA7+jAnErhzO8WUi3i4Z7oScp6xSA==
|
||||
dependencies:
|
||||
"@chakra-ui/utils" "^1.7.0"
|
||||
|
||||
"@chakra-ui/utils@1.8.2", "@chakra-ui/utils@^1.7.0", "@chakra-ui/utils@^1.8.2":
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/utils/-/utils-1.8.2.tgz#5a9f1f67c5f2232769fe7d009fcf96eebf3c2b4e"
|
||||
integrity sha512-MnE4czCQCE87Ch1DfAdmZvObgRviw9wQ9Zti372P8VD1ILEdff/C5WBWHW6mgG3YcorPAxgnrNF3MmNE95jRkA==
|
||||
dependencies:
|
||||
"@types/lodash.mergewith" "4.6.6"
|
||||
css-box-model "1.2.1"
|
||||
framesync "5.3.0"
|
||||
lodash.mergewith "4.6.2"
|
||||
|
||||
"@cnakazawa/watch@^1.0.3":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a"
|
||||
|
@ -1117,6 +1152,45 @@
|
|||
minimatch "^3.0.4"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@formatjs/ecma402-abstract@1.9.8":
|
||||
version "1.9.8"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.9.8.tgz#f3dad447fbc7f063f88e2a148b7a353161740e74"
|
||||
integrity sha512-2U4n11bLmTij/k4ePCEFKJILPYwdMcJTdnKVBi+JMWBgu5O1N+XhCazlE6QXqVO1Agh2Doh0b/9Jf1mSmSVfhA==
|
||||
dependencies:
|
||||
"@formatjs/intl-localematcher" "0.2.20"
|
||||
tslib "^2.1.0"
|
||||
|
||||
"@formatjs/fast-memoize@1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-1.2.0.tgz#1123bfcc5d21d761f15d8b1c32d10e1b6530355d"
|
||||
integrity sha512-fObitP9Tlc31SKrPHgkPgQpGo4+4yXfQQITTCNH8AZdEqB7Mq4nPrjpUL/tNGN3lEeJcFxDbi0haX8HM7QvQ8w==
|
||||
dependencies:
|
||||
tslib "^2.1.0"
|
||||
|
||||
"@formatjs/icu-messageformat-parser@2.0.11":
|
||||
version "2.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.0.11.tgz#e4ba40b9a8aefc8bccfc96be5906d3bca305b4b3"
|
||||
integrity sha512-5mWb8U8aulYGwnDZWrr+vdgn5PilvtrqQYQ1pvpgzQes/osi85TwmL2GqTGLlKIvBKD2XNA61kAqXYY95w4LWg==
|
||||
dependencies:
|
||||
"@formatjs/ecma402-abstract" "1.9.8"
|
||||
"@formatjs/icu-skeleton-parser" "1.2.12"
|
||||
tslib "^2.1.0"
|
||||
|
||||
"@formatjs/icu-skeleton-parser@1.2.12":
|
||||
version "1.2.12"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.2.12.tgz#45426eb1448c0c08c931eb9f0672283c0e4d0062"
|
||||
integrity sha512-DTFxWmEA02ZNW6fsYjGYSADvtrqqjCYF7DSgCmMfaaE0gLP4pCdAgOPE+lkXXU+jP8iCw/YhMT2Seyk/C5lBWg==
|
||||
dependencies:
|
||||
"@formatjs/ecma402-abstract" "1.9.8"
|
||||
tslib "^2.1.0"
|
||||
|
||||
"@formatjs/intl-localematcher@0.2.20":
|
||||
version "0.2.20"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.2.20.tgz#782aef53d1c1b6112ee67468dc59f9b8d1ba7b17"
|
||||
integrity sha512-/Ro85goRZnCojzxOegANFYL0LaDIpdPjAukR7xMTjOtRx+3yyjR0ifGTOW3/Kjhmab3t6GnyHBYWZSudxEOxPA==
|
||||
dependencies:
|
||||
tslib "^2.1.0"
|
||||
|
||||
"@hapi/address@^2.1.2":
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
|
||||
|
@ -1184,6 +1258,21 @@
|
|||
resolved "https://registry.yarnpkg.com/@icons/material/-/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8"
|
||||
integrity sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==
|
||||
|
||||
"@internationalized/message@^3.0.2":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@internationalized/message/-/message-3.0.2.tgz#c3db2b6b7f75af815819f77da11f8424381416e3"
|
||||
integrity sha512-ZZ8FQDCsri3vUB2mfDD76Vbf97DH361AiZUXKHV4BqwCtYyaNYiZqIr8KXrcMCxJvrIYVQLSn8+jeIQRO3bvtw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.2"
|
||||
intl-messageformat "^9.6.12"
|
||||
|
||||
"@internationalized/number@^3.0.2":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@internationalized/number/-/number-3.0.3.tgz#d29003dffdff54ca6f2287ec0cb77ff3d045478f"
|
||||
integrity sha512-ewFoVvsxSyd9QZnknvOWPjirYqdMQhXTeDhJg3hM6C/FeZt0banpGH1nZ0SGMZXHz8NK9uAa2KVIq+jqAIOg4w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.2"
|
||||
|
||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
|
||||
|
@ -2168,6 +2257,76 @@
|
|||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@react-aria/i18n@^3.3.2":
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@react-aria/i18n/-/i18n-3.3.2.tgz#891902938333c6ab5491b7acb7581f8567045dbc"
|
||||
integrity sha512-a4AptbWLPVMJfjPdyW60TFtT4gvKAputx9YaUrIywoV/5p900AcOOc3uuL43+vuCKBzMkGUeTa1a4eL1HstDUA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.2"
|
||||
"@internationalized/message" "^3.0.2"
|
||||
"@internationalized/number" "^3.0.2"
|
||||
"@react-aria/ssr" "^3.0.3"
|
||||
"@react-aria/utils" "^3.8.2"
|
||||
"@react-types/shared" "^3.8.0"
|
||||
|
||||
"@react-aria/interactions@^3.5.1", "@react-aria/interactions@^3.6.0":
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-aria/interactions/-/interactions-3.6.0.tgz#63c16e6179e8ae38221e26256d9a7639d7f9b24e"
|
||||
integrity sha512-dMEGYIIhJ3uxDd19Z/rxuqQp9Rx9c46AInrfzAiOijQj/fTmb4ubCsuFOAQrc0sy1HCY1/ntnRZQuRgT/iS74w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.2"
|
||||
"@react-aria/utils" "^3.9.0"
|
||||
"@react-types/shared" "^3.9.0"
|
||||
|
||||
"@react-aria/live-announcer@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-aria/live-announcer/-/live-announcer-3.0.1.tgz#772888326808d180adc5bc9fa0b4b1416ec08811"
|
||||
integrity sha512-c63UZ4JhXxy29F6FO1LUkQLDRzv17W4g3QQ+sy6tmFw7R5I5r8uh8jR7RCbBX7bdGCLnQDwOQ055KsM/a9MT3A==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.2"
|
||||
"@react-aria/utils" "^3.8.2"
|
||||
"@react-aria/visually-hidden" "^3.2.3"
|
||||
|
||||
"@react-aria/spinbutton@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-aria/spinbutton/-/spinbutton-3.0.1.tgz#e0d5595e1c74518ca46acdeebf7bd19022ee5d50"
|
||||
integrity sha512-V2wUhSgJDxSqzo5HPbx7OgGpFeuvxq8/7nNO8mT3cEZfZASUGvjIdCRmAf243qyfo9Yby4zdx9E/BxNOGCZ9cQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.2"
|
||||
"@react-aria/i18n" "^3.3.2"
|
||||
"@react-aria/live-announcer" "^3.0.1"
|
||||
"@react-aria/utils" "^3.8.2"
|
||||
"@react-types/button" "^3.4.1"
|
||||
"@react-types/shared" "^3.8.0"
|
||||
|
||||
"@react-aria/ssr@^3.0.3", "@react-aria/ssr@^3.1.0":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.1.0.tgz#b7163e6224725c30121932a8d1422ef91d1fab22"
|
||||
integrity sha512-RxqQKmE8sO7TGdrcSlHTcVzMP450hqowtBSd2bBS9oPlcokVkaGq28c3Rwa8ty5ctw4EBCjXqjP7xdcKMGDzug==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.2"
|
||||
|
||||
"@react-aria/utils@^3.8.2", "@react-aria/utils@^3.9.0":
|
||||
version "3.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-aria/utils/-/utils-3.9.0.tgz#c5446f807091a744311d4d559fa8a42e7448824d"
|
||||
integrity sha512-P0dEOMHGHHJ5KC8iCpaMxAtgdUdeISAm4FZnmoD5fK3JxlKEC046hUxlad83RkNOBZkT2dDvF4HeDCUqdMWHKQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.2"
|
||||
"@react-aria/ssr" "^3.1.0"
|
||||
"@react-stately/utils" "^3.2.2"
|
||||
"@react-types/shared" "^3.9.0"
|
||||
clsx "^1.1.1"
|
||||
|
||||
"@react-aria/visually-hidden@^3.2.3":
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@react-aria/visually-hidden/-/visually-hidden-3.2.3.tgz#4779df0a468873550afb42a7f5fcb2411d82db8d"
|
||||
integrity sha512-iAe5EFI7obEOwTnIdAwWrKq+CrIJFGTw85v8fXnQ7CIVGRDblX85GOUww9bzQNPDLLRYWS4VF702ii8kV4+JCw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.2"
|
||||
"@react-aria/interactions" "^3.5.1"
|
||||
"@react-aria/utils" "^3.8.2"
|
||||
clsx "^1.1.1"
|
||||
|
||||
"@react-dnd/asap@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-4.0.0.tgz#b300eeed83e9801f51bd66b0337c9a6f04548651"
|
||||
|
@ -2183,6 +2342,25 @@
|
|||
resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz#a3031eb54129f2c66b2753f8404266ec7bf67f0a"
|
||||
integrity sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==
|
||||
|
||||
"@react-stately/utils@^3.2.2":
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@react-stately/utils/-/utils-3.2.2.tgz#468eafa60740c6b0b847a368215dfaa55e87f505"
|
||||
integrity sha512-7NCpRMAexDdgVqbrB9uDrkDpM4Tdw5BU6Gu6IKUXmKsoDYziE6mAjaGkCZBitsrln1Cezc6euI5YPa1JqxgpJg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.2"
|
||||
|
||||
"@react-types/button@^3.4.1":
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-types/button/-/button-3.4.1.tgz#715ac9d4997c79233be4d9020b58f85936b8252b"
|
||||
integrity sha512-B54M84LxdEppwjXNlkBEJyMfe9fd+bvFV7R6+NJvupGrZm/LuFNYjFcHk7yjMKWTdWm6DbpIuQz54n5qTW7Vlg==
|
||||
dependencies:
|
||||
"@react-types/shared" "^3.8.0"
|
||||
|
||||
"@react-types/shared@^3.8.0", "@react-types/shared@^3.9.0":
|
||||
version "3.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-types/shared/-/shared-3.9.0.tgz#d834f3e6e2c992089192f3c83fb7963e3a6f5207"
|
||||
integrity sha512-YYksINfR6q92P10AhPEGo47Hd7oz1hrnZ6Vx8Gsrq62IbqDdv1XOTzPBaj17Z1ymNY2pitLUSEXsLmozt4wxxQ==
|
||||
|
||||
"@relative-ci/agent@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@relative-ci/agent/-/agent-2.0.0.tgz#21f73fec146a0f7c94684ead0f6ecbf05ea5c4ee"
|
||||
|
@ -2206,6 +2384,24 @@
|
|||
execa "^4.0.0"
|
||||
java-properties "^1.0.0"
|
||||
|
||||
"@renderlesskit/react@^0.6.0":
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@renderlesskit/react/-/react-0.6.0.tgz#93ced709412f6e50538c132812e40ae6fe1f96ef"
|
||||
integrity sha512-v1FChZQj8te+XK9MhGT5XVxgynAMriY6CKcY4pCa7+YljM2vLuRgo7yaOhh5saHrd/t5dbtcdS4s/xo9lZFYEQ==
|
||||
dependencies:
|
||||
"@chakra-ui/counter" "^1.1.9"
|
||||
"@chakra-ui/hooks" "^1.6.0"
|
||||
"@chakra-ui/react-utils" "^1.1.2"
|
||||
"@chakra-ui/utils" "^1.8.2"
|
||||
"@react-aria/i18n" "^3.3.2"
|
||||
"@react-aria/interactions" "^3.6.0"
|
||||
"@react-aria/spinbutton" "^3.0.1"
|
||||
"@react-aria/utils" "^3.9.0"
|
||||
date-fns "^2.23.0"
|
||||
reakit-system "^0.15.2"
|
||||
reakit-utils "^0.15.2"
|
||||
reakit-warning "^0.6.2"
|
||||
|
||||
"@rollup/plugin-babel@^5.2.0":
|
||||
version "5.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.2.3.tgz#ee8fffbaa62a6c9ccd41b1bfca32e81f847700ee"
|
||||
|
@ -2593,6 +2789,18 @@
|
|||
"@types/koa-compose" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/lodash.mergewith@4.6.6":
|
||||
version "4.6.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash.mergewith/-/lodash.mergewith-4.6.6.tgz#c4698f5b214a433ff35cb2c75ee6ec7f99d79f10"
|
||||
integrity sha512-RY/8IaVENjG19rxTZu9Nukqh0W2UrYgmBj5sdns4hWRZaV8PqR7wIKHFKzvOTjo4zVRV7sVI+yFhAJql12Kfqg==
|
||||
dependencies:
|
||||
"@types/lodash" "*"
|
||||
|
||||
"@types/lodash@*":
|
||||
version "4.14.172"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.172.tgz#aad774c28e7bfd7a67de25408e03ee5a8c3d028a"
|
||||
integrity sha512-/BHF5HAx3em7/KkzVKm3LrsD6HZAXuXO1AJZQ3cRRBZj4oHZDviWPYu0aEplAqDFNHZPW6d3G7KN+ONcCCC7pw==
|
||||
|
||||
"@types/long@^4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"
|
||||
|
@ -4475,6 +4683,11 @@ cloneable-readable@^1.0.0:
|
|||
process-nextick-args "^2.0.0"
|
||||
readable-stream "^2.3.5"
|
||||
|
||||
clsx@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
|
||||
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
|
||||
|
||||
cluster-key-slot@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d"
|
||||
|
@ -4653,6 +4866,11 @@ compressorjs@^1.0.7:
|
|||
blueimp-canvas-to-blob "^3.28.0"
|
||||
is-blob "^2.1.0"
|
||||
|
||||
compute-scroll-into-view@1.0.14:
|
||||
version "1.0.14"
|
||||
resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.14.tgz#80e3ebb25d6aa89f42e533956cb4b16a04cfe759"
|
||||
integrity sha512-mKDjINe3tc6hGelUMNDzuhorIUZ7kS7BwyY0r2wQd2HOH2tRuJykiC06iSEX8y1TuhNzvz4GcJnK16mM2J1NMQ==
|
||||
|
||||
compute-scroll-into-view@^1.0.16:
|
||||
version "1.0.16"
|
||||
resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.16.tgz#5b7bf4f7127ea2c19b750353d7ce6776a90ee088"
|
||||
|
@ -4826,7 +5044,7 @@ copy-descriptor@^0.1.0:
|
|||
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
|
||||
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
|
||||
|
||||
copy-to-clipboard@^3.0.8, copy-to-clipboard@^3.3.1:
|
||||
copy-to-clipboard@3.3.1, copy-to-clipboard@^3.0.8, copy-to-clipboard@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae"
|
||||
integrity sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==
|
||||
|
@ -4986,6 +5204,13 @@ crypto-random-string@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
|
||||
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
|
||||
|
||||
css-box-model@1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1"
|
||||
integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==
|
||||
dependencies:
|
||||
tiny-invariant "^1.0.6"
|
||||
|
||||
css-color-keywords@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
|
||||
|
@ -5110,7 +5335,7 @@ date-fns@2.22.1:
|
|||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.22.1.tgz#1e5af959831ebb1d82992bf67b765052d8f0efc4"
|
||||
integrity sha512-yUFPQjrxEmIsMqlHhAhmxkuH769baF21Kk+nZwZGyrMoyLA+LugaQtC0+Tqf9CBUUULWwUJt6Q5ySI3LJDDCGg==
|
||||
|
||||
date-fns@^2.16.1:
|
||||
date-fns@^2.16.1, date-fns@^2.23.0:
|
||||
version "2.23.0"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.23.0.tgz#4e886c941659af0cf7b30fafdd1eaa37e88788a9"
|
||||
integrity sha512-5ycpauovVyAk0kXNZz6ZoB9AYMZB4DObse7P3BPWmyEjXNORTI8EJ6X0uaSAq4sCHzM1uajzrkr6HnsLQpxGXA==
|
||||
|
@ -7758,6 +7983,15 @@ interpret@^1.0.0, interpret@^1.4.0:
|
|||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
|
||||
integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
|
||||
|
||||
intl-messageformat@^9.6.12:
|
||||
version "9.9.1"
|
||||
resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-9.9.1.tgz#255d453b0656b4f7e741f31d2b4a95bf2adfe064"
|
||||
integrity sha512-cuzS/XKHn//hvKka77JKU2dseiVY2dofQjIOZv6ZFxFt4Z9sPXnZ7KQ9Ak2r+4XBCjI04MqJ1PhKs/3X22AkfA==
|
||||
dependencies:
|
||||
"@formatjs/fast-memoize" "1.2.0"
|
||||
"@formatjs/icu-messageformat-parser" "2.0.11"
|
||||
tslib "^2.1.0"
|
||||
|
||||
invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
|
||||
|
@ -9617,6 +9851,11 @@ lodash.keys@2.3.x, lodash.keys@~2.3.0:
|
|||
lodash._shimkeys "~2.3.0"
|
||||
lodash.isobject "~2.3.0"
|
||||
|
||||
lodash.mergewith@4.6.2:
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55"
|
||||
integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==
|
||||
|
||||
lodash.noop@~2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-2.3.0.tgz#3059d628d51bbf937cd2a0b6fc3a7f212a669c2c"
|
||||
|
@ -12092,19 +12331,19 @@ readdirp@~3.5.0:
|
|||
dependencies:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
reakit-system@^0.15.2:
|
||||
reakit-system@^0.15.1, reakit-system@^0.15.2:
|
||||
version "0.15.2"
|
||||
resolved "https://registry.yarnpkg.com/reakit-system/-/reakit-system-0.15.2.tgz#a485fab84b3942acbed6212c3b56a6ef8611c457"
|
||||
integrity sha512-TvRthEz0DmD0rcJkGamMYx+bATwnGNWJpe/lc8UV2Js8nnPvkaxrHk5fX9cVASFrWbaIyegZHCWUBfxr30bmmA==
|
||||
dependencies:
|
||||
reakit-utils "^0.15.2"
|
||||
|
||||
reakit-utils@^0.15.2:
|
||||
reakit-utils@^0.15.1, reakit-utils@^0.15.2:
|
||||
version "0.15.2"
|
||||
resolved "https://registry.yarnpkg.com/reakit-utils/-/reakit-utils-0.15.2.tgz#b4d5836e534576bfd175171541d43182ad97f2d2"
|
||||
integrity sha512-i/RYkq+W6hvfFmXw5QW7zvfJJT/K8a4qZ0hjA79T61JAFPGt23DsfxwyBbyK91GZrJ9HMrXFVXWMovsKBc1qEQ==
|
||||
|
||||
reakit-warning@^0.6.2:
|
||||
reakit-warning@^0.6.1, reakit-warning@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/reakit-warning/-/reakit-warning-0.6.2.tgz#9c346ae483eb1f284f2088653f90cabd26dbee56"
|
||||
integrity sha512-z/3fvuc46DJyD3nJAUOto6inz2EbSQTjvI/KBQDqxwB0y02HDyeP8IWOJxvkuAUGkWpeSx+H3QWQFSNiPcHtmw==
|
||||
|
|
Reference in New Issue