Compare commits

..

28 Commits

Author SHA1 Message Date
Tom Moor d4c594423f More email styling 2021-07-08 22:38:42 -04:00
Tom Moor 2bf237d54b Merge branch 'main' into email-diff 2021-07-08 21:20:18 -04:00
Tom Moor 9a1c8f07d1 feat: Add documentId filter to events.list (#2287) 2021-07-08 10:12:06 -07:00
Tom Moor 241cb11493 chore: Automate running yarn-deduplicate, see comment:
https://github.com/outline/outline/pull/2283\#discussion_r665301770
2021-07-07 22:26:56 -04:00
Saumya Pandey 8195791bb2 fix: Make search query string user friendly (#2283)
* Upgrade query-string package and skip empty string

* Run yarn-deduplicate command
2021-07-07 18:45:40 -07:00
Saumya Pandey b037ae5dc1 fix: Improve isChildDocument performance (#2284) 2021-07-07 04:53:40 -07:00
Tom Moor aeba8ce4eb fix: Empty context menu when user does not have permission to update collection 2021-07-06 22:02:31 -04:00
Tom Moor 3565e68725 Merge branch 'main' into email-diff 2021-07-06 20:43:51 -04:00
Tom Moor 429c5fba85 0.57.0 2021-07-06 09:12:54 -04:00
Tom Moor 9495ddba25 fix: Restore previous WSS CORS behavior 2021-07-05 23:01:25 -04:00
dependabot[bot] 486a60e97c chore(deps): bump socket.io from 2.3.0 to 2.4.0 (#1831)
Bumps [socket.io](https://github.com/socketio/socket.io) from 2.3.0 to 2.4.0.
- [Release notes](https://github.com/socketio/socket.io/releases)
- [Changelog](https://github.com/socketio/socket.io/blob/2.4.0/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io/compare/2.3.0...2.4.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-05 17:11:40 -07:00
Tom Moor c687745263 fix: E-mail signin on incorrect subdomain should allow the process to continue instead of error
closes #2276
2021-07-05 19:25:21 -04:00
Translate-O-Tron 1b92993b90 fix: New Portuguese, Brazilian translations from Crowdin (#2271) 2021-07-05 13:37:09 -07:00
Tom Moor 181a20a268 fix: More context menu fixes 2021-07-05 16:35:46 -04:00
Tom Moor f8ffa4e25a tweak sidebar item background 2021-07-04 18:44:09 -04:00
Farzad 7e139ca8f7 fix: nested link positions for RTL titles in sidebar (#2272) 2021-07-04 12:08:05 -07:00
Tom Moor 61039e9d0d Allow images in email diff 2021-06-25 09:41:34 -07:00
Tom Moor 6d09122d56 test: Deletion 2021-06-24 20:10:42 -07:00
Tom Moor 5fb6097153 Improved diff 2021-06-23 23:58:32 -07:00
Tom Moor ec17874568 Remove test harness 2021-06-22 07:35:38 -07:00
Tom Moor 40c3e9e85f test 2021-06-22 07:27:55 -07:00
Tom Moor 9f739f3788 Merge main 2021-06-22 07:26:45 -07:00
Tom Moor f6837b4742 wip 2021-06-20 23:15:04 -07:00
Tom Moor 1560e3c9f7 refactor 2021-06-20 12:49:15 -07:00
Tom Moor ca74908dc5 test 2021-06-20 00:20:37 -07:00
Tom Moor de7ec1119b Integrate into mailer, basic styling 2021-06-19 23:50:36 -07:00
Tom Moor 2093b4297f Merge main 2021-06-19 17:05:19 -07:00
Nan Yu 3df82c500b wip 2021-02-21 11:52:00 -08:00
526 changed files with 42968 additions and 5141 deletions
-1
View File
@@ -2,7 +2,6 @@
"presets": [
"@babel/preset-react",
"@babel/preset-flow",
"@babel/preset-typescript",
[
"@babel/preset-env",
{
+21 -11
View File
@@ -4,11 +4,12 @@
"react-app",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript",
"plugin:flowtype/recommended",
"plugin:react-hooks/recommended"
],
"plugins": [
"prettier"
"prettier",
"flowtype"
],
"rules": {
"eqeqeq": 2,
@@ -54,6 +55,21 @@
]
}
],
"flowtype/require-valid-file-annotation": [
2,
"always",
{
"annotationStyle": "line"
}
],
"flowtype/space-after-type-colon": [
2,
"always"
],
"flowtype/space-before-type-colon": [
2,
"never"
],
"prettier/prettier": [
"error",
{
@@ -68,22 +84,16 @@
"pragma": "React",
"version": "detect"
},
"import/parsers": {
"@typescript-eslint/parser": [
".ts",
".tsx"
]
},
"import/resolver": {
"typescript": {
"alwaysTryTypes": true
},
"node": {
"paths": [
"app",
"."
]
}
},
"flowtype": {
"onlyFilesWithFlowAnnotation": false
}
},
"env": {
+2 -2
View File
@@ -1,7 +1,7 @@
{
"javascript.validate.enable": false,
"javascript.format.enable": false,
"typescript.validate.enable": true,
"typescript.format.enable": true,
"typescript.validate.enable": false,
"typescript.format.enable": false,
"editor.formatOnSave": true,
}
@@ -1,3 +1,4 @@
// @flow
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import Flex from "components/Flex";
@@ -1,9 +1,10 @@
// @flow
/* global ga */
import * as React from "react";
import env from "env";
type Props = {
children?: React.ReactNode;
children?: React.Node,
};
export default class Analytics extends React.Component<Props> {
@@ -1,3 +1,4 @@
// @flow
import * as React from "react";
export default function Arrow() {
@@ -1,9 +1,10 @@
// @flow
import * as React from "react";
type Props = {
size?: number;
fill?: string;
className?: string;
size?: number,
fill?: string,
className?: string,
};
function GoogleLogo({ size = 34, fill = "#FFF", className }: Props) {
@@ -1,9 +1,10 @@
// @flow
import * as React from "react";
type Props = {
size?: number;
fill?: string;
className?: string;
size?: number,
fill?: string,
className?: string,
};
function MicrosoftLogo({ size = 34, fill = "#FFF", className }: Props) {
@@ -1,9 +1,10 @@
// @flow
import * as React from "react";
type Props = {
size?: number;
fill?: string;
className?: string;
size?: number,
fill?: string,
className?: string,
};
function SlackLogo({ size = 34, fill = "#FFF", className }: Props) {
@@ -1,13 +1,14 @@
// @flow
import * as React from "react";
import styled from "styled-components";
import GoogleLogo from "./GoogleLogo";
import MicrosoftLogo from "./MicrosoftLogo";
import SlackLogo from "./SlackLogo";
type Props = {
providerName: string;
size?: number;
};
type Props = {|
providerName: string,
size?: number,
|};
function AuthLogo({ providerName, size = 16 }: Props) {
switch (providerName) {
@@ -1,14 +1,15 @@
// @flow
import { observer } from "mobx-react";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Redirect } from "react-router-dom";
import { isCustomSubdomain } from "shared/utils/domains";
import LoadingIndicator from "components/LoadingIndicator";
import useStores from "../hooks/useStores";
import env from "env";
import useStores from "hooks/useStores";
type Props = {
children: JSX.Element;
children: React.Node,
};
const Authenticated = ({ children }: Props) => {
@@ -1,3 +1,4 @@
// @flow
import { observable } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
@@ -5,19 +6,18 @@ import styled from "styled-components";
import User from "models/User";
import placeholder from "./placeholder.png";
type Props = {
src: string;
size: number;
icon?: React.ReactNode;
user?: User;
onClick?: () => void;
className?: string;
};
type Props = {|
src: string,
size: number,
icon?: React.Node,
user?: User,
onClick?: () => void,
className?: string,
|};
@observer
class Avatar extends React.Component<Props> {
@observable
error: boolean;
@observable error: boolean;
static defaultProps = {
size: 24,
@@ -1,9 +1,9 @@
// @flow
import { observable } from "mobx";
import { observer } from "mobx-react";
import { EditIcon } from "outline-icons";
import * as React from "react";
import { withTranslation } from "react-i18next";
import { TFunction } from "react-i18next";
import { withTranslation, type TFunction } from "react-i18next";
import styled from "styled-components";
import User from "models/User";
import UserProfile from "scenes/UserProfile";
@@ -11,18 +11,17 @@ import Avatar from "components/Avatar";
import Tooltip from "components/Tooltip";
type Props = {
user: User;
isPresent: boolean;
isEditing: boolean;
isCurrentUser: boolean;
profileOnClick: boolean;
t: TFunction;
user: User,
isPresent: boolean,
isEditing: boolean,
isCurrentUser: boolean,
profileOnClick: boolean,
t: TFunction,
};
@observer
class AvatarWithPresence extends React.Component<Props> {
@observable
isOpen: boolean = false;
@observable isOpen: boolean = false;
handleOpenProfile = () => {
this.isOpen = true;
@@ -1,3 +1,4 @@
// @flow
import Avatar from "./Avatar";
import AvatarWithPresence from "./AvatarWithPresence";
@@ -1,3 +1,4 @@
// @flow
import styled from "styled-components";
const Badge = styled.span`
@@ -1,10 +1,11 @@
// @flow
import * as React from "react";
import styled from "styled-components";
import OutlineLogo from "./OutlineLogo";
import env from "env";
type Props = {
href?: string;
href?: string,
};
function Branding({ href = env.URL }: Props) {
@@ -1,3 +1,4 @@
// @flow
import { GoToIcon } from "outline-icons";
import * as React from "react";
import { Link } from "react-router-dom";
@@ -5,18 +6,18 @@ import styled from "styled-components";
import Flex from "components/Flex";
import BreadcrumbMenu from "menus/BreadcrumbMenu";
type MenuItem = {
icon?: React.ReactNode;
title: React.ReactNode;
to?: string;
};
type MenuItem = {|
icon?: React.Node,
title: React.Node,
to?: string,
|};
type Props = {
items: MenuItem[];
max?: number;
children?: React.ReactNode;
highlightFirstItem?: boolean;
};
type Props = {|
items: MenuItem[],
max?: number,
children?: React.Node,
highlightFirstItem?: boolean,
|};
function Breadcrumb({ items, highlightFirstItem, children, max = 2 }: Props) {
const totalItems = items.length;
@@ -1,10 +1,11 @@
// @flow
import * as React from "react";
import styled from "styled-components";
import { bounceIn } from "shared/styles/animations";
type Props = {
count: number;
};
type Props = {|
count: number,
|};
const Bubble = ({ count }: Props) => {
if (!count) {
@@ -1,7 +1,7 @@
// @flow
import { ExpandedIcon } from "outline-icons";
import { darken } from "polished";
import * as React from "react";
import { SyntheticEvent } from "react";
import styled from "styled-components";
const RealButton = styled.button`
@@ -108,35 +108,36 @@ export const Inner = styled.span`
${(props) => props.hasIcon && !props.hasText && "padding: 0 4px;"};
`;
export type Props = {
type?: "button" | "submit";
value?: string;
icon?: React.ReactNode;
iconColor?: string;
className?: string;
children?: React.ReactNode;
innerRef?: React.RefObject<any>;
disclosure?: boolean;
neutral?: boolean;
danger?: boolean;
primary?: boolean;
disabled?: boolean;
fullwidth?: boolean;
autoFocus?: boolean;
style?: any;
as?: React.ComponentType<any>;
to?: string;
onClick?: (event: SyntheticEvent) => unknown;
borderOnHover?: boolean;
"data-on"?: string;
"data-event-category"?: string;
"data-event-action"?: string;
};
export type Props = {|
type?: "button" | "submit",
value?: string,
icon?: React.Node,
iconColor?: string,
className?: string,
children?: React.Node,
innerRef?: React.ElementRef<any>,
disclosure?: boolean,
neutral?: boolean,
danger?: boolean,
primary?: boolean,
disabled?: boolean,
fullwidth?: boolean,
autoFocus?: boolean,
style?: Object,
as?: React.ComponentType<any>,
to?: string,
onClick?: (event: SyntheticEvent<>) => mixed,
borderOnHover?: boolean,
const Button = React.forwardRef<HTMLButtonElement>(
"data-on"?: string,
"data-event-category"?: string,
"data-event-action"?: string,
|};
const Button = React.forwardRef<Props, HTMLButtonElement>(
(
{
type = "button",
type = "text",
icon,
children,
value,
@@ -1,3 +1,4 @@
// @flow
import styled from "styled-components";
import Button, { Inner } from "./Button";
@@ -1,13 +1,17 @@
// @flow
import * as React from "react";
import { SyntheticEvent } from "react";
import styled from "styled-components";
type Props = {
onClick: (ev: SyntheticEvent) => void;
children: React.ReactNode;
onClick: (ev: SyntheticEvent<>) => void,
children: React.Node,
};
const ButtonLink = styled.button<Props>`
export default function ButtonLink(props: Props) {
return <Button {...props} />;
}
const Button = styled.button`
margin: 0;
padding: 0;
border: 0;
@@ -17,5 +21,3 @@ const ButtonLink = styled.button<Props>`
text-decoration: none;
cursor: pointer;
`;
export default ButtonLink;
@@ -1,13 +1,14 @@
// @flow
import * as React from "react";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
type Props = {
children?: React.ReactNode;
withStickyHeader?: boolean;
};
type Props = {|
children?: React.Node,
withStickyHeader?: boolean,
|};
const Container = styled.div<Props>`
const Container = styled.div`
width: 100%;
max-width: 100vw;
padding: ${(props) => (props.withStickyHeader ? "4px 12px" : "60px 12px")};
@@ -1,33 +1,29 @@
// @flow
import * as React from "react";
import { ChangeEvent } from "react";
import { VisuallyHidden } from "reakit/VisuallyHidden";
import styled from "styled-components";
import HelpText from "components/HelpText";
export type Props = {
checked?: boolean;
label?: React.ReactNode;
labelHidden?: boolean;
className?: string;
name?: string;
disabled?: boolean;
onChange: (event: ChangeEvent<HTMLInputElement>) => unknown;
note?: string;
short?: boolean;
small?: boolean;
};
export type Props = {|
checked?: boolean,
label?: React.Node,
labelHidden?: boolean,
className?: string,
name?: string,
disabled?: boolean,
onChange: (event: SyntheticInputEvent<HTMLInputElement>) => mixed,
note?: string,
short?: boolean,
small?: boolean,
|};
type LabelTextProps = { small: boolean };
const LabelText = styled.span<LabelTextProps>`
const LabelText = styled.span`
font-weight: 500;
margin-left: ${(props) => (props.small ? "6px" : "10px")};
${(props) => (props.small ? `color: ${props.theme.textSecondary}` : "")};
`;
type WrapperProps = { small: boolean };
const Wrapper = styled.div<WrapperProps>`
const Wrapper = styled.div`
padding-bottom: 8px;
${(props) => (props.small ? "font-size: 14px" : "")};
width: 100%;
@@ -1,10 +1,7 @@
// @flow
import styled from "styled-components";
type Props = {
grow: boolean;
};
const ClickablePadding = styled.div<Props>`
const ClickablePadding = styled.div`
min-height: 10em;
cursor: ${({ onClick }) => (onClick ? "text" : "default")};
${({ grow }) => grow && `flex-grow: 100;`};
@@ -1,3 +1,4 @@
// @flow
import { sortBy, filter, uniq } from "lodash";
import { observer } from "mobx-react";
import * as React from "react";
@@ -13,10 +14,10 @@ import NudeButton from "components/NudeButton";
import Popover from "components/Popover";
import useStores from "hooks/useStores";
type Props = {
document: Document;
currentUserId: string;
};
type Props = {|
document: Document,
currentUserId: string,
|};
function Collaborators(props: Props) {
const { t } = useTranslation();
@@ -1,3 +1,4 @@
// @flow
import { observer } from "mobx-react";
import { transparentize } from "polished";
import * as React from "react";
@@ -12,9 +13,9 @@ import NudeButton from "components/NudeButton";
import useDebouncedCallback from "hooks/useDebouncedCallback";
import useStores from "hooks/useStores";
type Props = {
collection: Collection;
};
type Props = {|
collection: Collection,
|};
function CollectionDescription({ collection }: Props) {
const { collections, ui, policies } = useStores();
@@ -1,3 +1,4 @@
// @flow
import { observer } from "mobx-react";
import { CollectionIcon } from "outline-icons";
import { getLuminance } from "polished";
@@ -7,9 +8,9 @@ import { icons } from "components/IconPicker";
import useStores from "hooks/useStores";
type Props = {
collection: Collection;
expanded?: boolean;
size?: number;
collection: Collection,
expanded?: boolean,
size?: number,
};
function ResolvedCollectionIcon({ collection, expanded, size }: Props) {
@@ -1,3 +1,4 @@
// @flow
import styled from "styled-components";
const Header = styled.h3`
@@ -1,22 +1,21 @@
// @flow
import { CheckmarkIcon } from "outline-icons";
import * as React from "react";
import { MenuItem as BaseMenuItem } from "reakit/Menu";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import { SyntheticEvent } from "react";
type Props = {
onClick?: (a: SyntheticEvent) => void | Promise<void>;
children?: React.ReactNode;
selected?: boolean;
disabled?: boolean;
to?: string;
href?: string;
target?: "_blank";
as?: string | React.ComponentType<any>;
hide?: () => void;
};
type Props = {|
onClick?: (SyntheticEvent<>) => void | Promise<void>,
children?: React.Node,
selected?: boolean,
disabled?: boolean,
to?: string,
href?: string,
target?: "_blank",
as?: string | React.ComponentType<*>,
hide?: () => void,
|};
const MenuItem = ({
onClick,
@@ -27,20 +26,29 @@ const MenuItem = ({
hide,
...rest
}: Props) => {
// We bind to mousedown instead of onClick here as otherwise menu items do not
// work in Firefox which triggers the hideOnClickOutside handler first via
// mousedown.
const handleMouseDown = React.useCallback(
const handleClick = React.useCallback(
(ev) => {
if (onClick) {
ev.preventDefault();
ev.stopPropagation();
onClick(ev);
}
if (hide) {
hide();
}
},
[onClick]
[onClick, hide]
);
// Preventing default mousedown otherwise menu items do not work in Firefox,
// which triggers the hideOnClickOutside handler first via mousedown hiding
// and un-rendering the menu contents.
const handleMouseDown = React.useCallback((ev) => {
ev.preventDefault();
ev.stopPropagation();
}, []);
return (
<BaseMenuItem
onClick={disabled ? undefined : onClick}
@@ -53,8 +61,8 @@ const MenuItem = ({
{...props}
$toggleable={selected !== undefined}
as={onClick ? "button" : as}
onClick={handleClick}
onMouseDown={handleMouseDown}
onClick={hide}
>
{selected !== undefined && (
<>
@@ -1,3 +1,4 @@
// @flow
import { MoreIcon } from "outline-icons";
import * as React from "react";
import { MenuButton } from "reakit/Menu";
@@ -1,3 +1,4 @@
// @flow
import * as React from "react";
import { MenuSeparator } from "reakit/Menu";
import styled from "styled-components";
@@ -1,3 +1,4 @@
// @flow
import { ExpandedIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
@@ -12,51 +13,49 @@ import MenuItem, { MenuAnchor } from "./MenuItem";
import Separator from "./Separator";
import ContextMenu from ".";
import { SyntheticEvent } from "react";
type TMenuItem =
| {
title: React.ReactNode;
to: string;
visible?: boolean;
selected?: boolean;
disabled?: boolean;
}
| {
title: React.ReactNode;
onClick: (event: SyntheticEvent) => void | Promise<void>;
visible?: boolean;
selected?: boolean;
disabled?: boolean;
}
| {
title: React.ReactNode;
href: string;
visible?: boolean;
selected?: boolean;
disabled?: boolean;
}
| {
title: React.ReactNode;
visible?: boolean;
disabled?: boolean;
style?: any;
hover?: boolean;
items: TMenuItem[];
}
| {
type: "separator";
visible?: boolean;
}
| {
type: "heading";
visible?: boolean;
title: React.ReactNode;
};
| {|
title: React.Node,
to: string,
visible?: boolean,
selected?: boolean,
disabled?: boolean,
|}
| {|
title: React.Node,
onClick: (event: SyntheticEvent<>) => void | Promise<void>,
visible?: boolean,
selected?: boolean,
disabled?: boolean,
|}
| {|
title: React.Node,
href: string,
visible?: boolean,
selected?: boolean,
disabled?: boolean,
|}
| {|
title: React.Node,
visible?: boolean,
disabled?: boolean,
style?: Object,
hover?: boolean,
items: TMenuItem[],
|}
| {|
type: "separator",
visible?: boolean,
|}
| {|
type: "heading",
visible?: boolean,
title: React.Node,
|};
type Props = {
items: TMenuItem[];
};
type Props = {|
items: TMenuItem[],
|};
const Disclosure = styled(ExpandedIcon)`
transform: rotate(270deg);
@@ -84,7 +83,7 @@ const Submenu = React.forwardRef(({ templateItems, title, ...rest }, ref) => {
);
});
function Template({ items, ...menu }: Props): React.ReactNode {
export function filterTemplateItems(items: TMenuItem[]): TMenuItem[] {
let filtered = items.filter((item) => item.visible !== false);
// this block literally just trims unneccessary separators
@@ -102,7 +101,11 @@ function Template({ items, ...menu }: Props): React.ReactNode {
return [...acc, item];
}, []);
return filtered.map((item, index) => {
return filtered;
}
function Template({ items, ...menu }: Props): React.Node {
return filterTemplateItems(items).map((item, index) => {
if (item.to) {
return (
<MenuItem
@@ -1,3 +1,4 @@
// @flow
import * as React from "react";
import { Portal } from "react-portal";
import { Menu } from "reakit/Menu";
@@ -10,14 +11,14 @@ import {
} from "shared/styles/animations";
import usePrevious from "hooks/usePrevious";
type Props = {
"aria-label": string;
visible?: boolean;
animating?: boolean;
children: React.ReactNode;
onOpen?: () => void;
onClose?: () => void;
};
type Props = {|
"aria-label": string,
visible?: boolean,
animating?: boolean,
children: React.Node,
onOpen?: () => void,
onClose?: () => void,
|};
export default function ContextMenu({
children,
@@ -1,17 +1,16 @@
// @flow
import copy from "copy-to-clipboard";
import * as React from "react";
import { SyntheticEvent } from "react";
type Props = {
text: string;
children?: React.ReactNode;
onClick?: () => void;
onCopy: () => void;
text: string,
children?: React.Node,
onClick?: () => void,
onCopy: () => void,
};
class CopyToClipboard extends React.PureComponent<Props> {
onClick = (ev: SyntheticEvent) => {
onClick = (ev: SyntheticEvent<>) => {
const { text, onCopy, children } = this.props;
const elem = React.Children.only(children);
copy(text, {
@@ -1,8 +1,9 @@
// @flow
import * as React from "react";
type Props = {
delay?: number;
children: JSX.Element;
delay?: number,
children: React.Node,
};
export default function DelayedMount({ delay = 250, children }: Props) {
@@ -1,3 +1,4 @@
// @flow
import styled from "styled-components";
const Divider = styled.hr`
@@ -1,3 +1,4 @@
// @flow
import { observer } from "mobx-react";
import {
ArchiveIcon,
@@ -15,11 +16,11 @@ import CollectionIcon from "components/CollectionIcon";
import useStores from "hooks/useStores";
import { collectionUrl } from "utils/routeHelpers";
type Props = {
document: Document;
children?: React.ReactNode;
onlyText: boolean;
};
type Props = {|
document: Document,
children?: React.Node,
onlyText: boolean,
|};
function useCategory(document) {
const { t } = useTranslation();
@@ -1,10 +1,10 @@
// @flow
import ArrowKeyNavigation from "boundless-arrow-key-navigation";
import { action, observable } from "mobx";
import { inject, observer } from "mobx-react";
import { CloseIcon } from "outline-icons";
import * as React from "react";
import { Redirect } from "react-router-dom";
import { Match, RouterHistory } from "react-router-dom";
import { type Match, Redirect, type RouterHistory } from "react-router-dom";
import { Waypoint } from "react-waypoint";
import styled from "styled-components";
@@ -20,24 +20,19 @@ import Revision from "./components/Revision";
import { documentHistoryUrl, documentUrl } from "utils/routeHelpers";
type Props = {
match: Match;
documents: DocumentsStore;
revisions: RevisionsStore;
history: RouterHistory;
match: Match,
documents: DocumentsStore,
revisions: RevisionsStore,
history: RouterHistory,
};
@observer
class DocumentHistory extends React.Component<Props> {
@observable
isLoaded: boolean = false;
@observable
isFetching: boolean = false;
@observable
offset: number = 0;
@observable
allowLoadMore: boolean = true;
@observable
redirectTo: string | undefined | null;
@observable isLoaded: boolean = false;
@observable isFetching: boolean = false;
@observable offset: number = 0;
@observable allowLoadMore: boolean = true;
@observable redirectTo: ?string;
async componentDidMount() {
await this.loadMoreResults();
@@ -1,3 +1,4 @@
// @flow
import { format } from "date-fns";
import * as React from "react";
import { NavLink } from "react-router-dom";
@@ -9,16 +10,16 @@ import Avatar from "components/Avatar";
import Flex from "components/Flex";
import Time from "components/Time";
import RevisionMenu from "menus/RevisionMenu";
import { Theme } from "types";
import { type Theme } from "types";
import { documentHistoryUrl } from "utils/routeHelpers";
type Props = {
theme: Theme;
showMenu: boolean;
selected: boolean;
document: Document;
revision: Revision;
theme: Theme,
showMenu: boolean,
selected: boolean,
document: Document,
revision: Revision,
};
class RevisionListItem extends React.Component<Props> {
@@ -1,2 +1,3 @@
// @flow
import DocumentHistory from "./DocumentHistory";
export default DocumentHistory;
@@ -1,17 +1,18 @@
// @flow
import ArrowKeyNavigation from "boundless-arrow-key-navigation";
import * as React from "react";
import Document from "models/Document";
import DocumentListItem from "components/DocumentListItem";
type Props = {
documents: Document[];
limit?: number;
showCollection?: boolean;
showPublished?: boolean;
showPin?: boolean;
showDraft?: boolean;
showTemplate?: boolean;
};
type Props = {|
documents: Document[],
limit?: number,
showCollection?: boolean,
showPublished?: boolean,
showPin?: boolean,
showDraft?: boolean,
showTemplate?: boolean,
|};
export default function DocumentList({ limit, documents, ...rest }: Props) {
const items = limit ? documents.splice(0, limit) : documents;
@@ -1,3 +1,4 @@
// @flow
import { observer } from "mobx-react";
import { PlusIcon } from "outline-icons";
import * as React from "react";
@@ -20,17 +21,17 @@ import useStores from "hooks/useStores";
import DocumentMenu from "menus/DocumentMenu";
import { newDocumentUrl } from "utils/routeHelpers";
type Props = {
document: Document;
highlight?: string | null;
context?: string | null;
showNestedDocuments?: boolean;
showCollection?: boolean;
showPublished?: boolean;
showPin?: boolean;
showDraft?: boolean;
showTemplate?: boolean;
};
type Props = {|
document: Document,
highlight?: ?string,
context?: ?string,
showNestedDocuments?: boolean,
showCollection?: boolean,
showPublished?: boolean,
showPin?: boolean,
showDraft?: boolean,
showTemplate?: boolean,
|};
const SEARCH_RESULT_REGEX = /<b\b[^>]*>(.*?)<\/b>/gi;
@@ -1,3 +1,4 @@
// @flow
import { observer } from "mobx-react";
import * as React from "react";
import { useTranslation } from "react-i18next";
@@ -28,15 +29,15 @@ const Modified = styled.span`
font-weight: ${(props) => (props.highlight ? "600" : "400")};
`;
type Props = {
showCollection?: boolean;
showPublished?: boolean;
showLastViewed?: boolean;
showNestedDocuments?: boolean;
document: Document;
children: React.ReactNode;
to?: string;
};
type Props = {|
showCollection?: boolean,
showPublished?: boolean,
showLastViewed?: boolean,
showNestedDocuments?: boolean,
document: Document,
children: React.Node,
to?: string,
|};
function DocumentMeta({
showPublished,
@@ -1,3 +1,4 @@
// @flow
import { useObserver } from "mobx-react";
import * as React from "react";
import { useTranslation } from "react-i18next";
@@ -7,14 +8,14 @@ import Document from "models/Document";
import DocumentMeta from "components/DocumentMeta";
import DocumentViews from "components/DocumentViews";
import Popover from "components/Popover";
import useStores from "hooks/useStores";
import useStores from "../hooks/useStores";
type Props = {
document: Document;
isDraft: boolean;
to?: string;
rtl?: boolean;
};
type Props = {|
document: Document,
isDraft: boolean,
to?: string,
rtl?: boolean,
|};
function DocumentMetaWithViews({ to, isDraft, document, ...rest }: Props) {
const { views } = useStores();
@@ -61,7 +62,7 @@ function DocumentMetaWithViews({ to, isDraft, document, ...rest }: Props) {
);
}
const Meta = styled(DocumentMeta)<{ rtl: boolean }>`
const Meta = styled(DocumentMeta)`
justify-content: ${(props) => (props.rtl ? "flex-end" : "flex-start")};
margin: -12px 0 2em 0;
font-size: 14px;
@@ -1,3 +1,4 @@
// @flow
import { formatDistanceToNow } from "date-fns";
import { sortBy } from "lodash";
import { observer } from "mobx-react";
@@ -9,10 +10,10 @@ import ListItem from "components/List/Item";
import PaginatedList from "components/PaginatedList";
import useStores from "hooks/useStores";
type Props = {
document: Document;
isOpen?: boolean;
};
type Props = {|
document: Document,
isOpen?: boolean,
|};
function DocumentViews({ document, isOpen }: Props) {
const { t } = useTranslation();
@@ -35,10 +36,9 @@ function DocumentViews({ document, isOpen }: Props) {
(view) => !presentIds.includes(view.user.id)
);
const users = React.useMemo(
() => sortedViews.map((v) => v.user),
[sortedViews]
);
const users = React.useMemo(() => sortedViews.map((v) => v.user), [
sortedViews,
]);
return (
<>
@@ -1,9 +1,8 @@
// @flow
import { lighten } from "polished";
import * as React from "react";
import { SyntheticEvent } from "react";
import { useTranslation } from "react-i18next";
import { withRouter, RouterHistory } from "react-router-dom";
import { withRouter, type RouterHistory } from "react-router-dom";
import styled, { withTheme } from "styled-components";
import { light } from "shared/styles/theme";
import UiStore from "stores/UiStore";
@@ -11,58 +10,51 @@ import ErrorBoundary from "components/ErrorBoundary";
import Tooltip from "components/Tooltip";
import embeds from "../embeds";
import useMediaQuery from "hooks/useMediaQuery";
import { Theme } from "types";
import { type Theme } from "types";
import { isModKey } from "utils/keyboard";
import { uploadFile } from "utils/uploadFile";
import { isInternalUrl } from "utils/urls";
const RichMarkdownEditor = React.lazy(
() =>
import(
/* webpackChunkName: "rich-markdown-editor" */ "rich-markdown-editor"
)
const RichMarkdownEditor = React.lazy(() =>
import(/* webpackChunkName: "rich-markdown-editor" */ "rich-markdown-editor")
);
const EMPTY_ARRAY = [];
export type Props = {
id?: string;
value?: string;
defaultValue?: string;
readOnly?: boolean;
grow?: boolean;
disableEmbeds?: boolean;
ui?: UiStore;
shareId?: string | null;
autoFocus?: boolean;
template?: boolean;
placeholder?: string;
maxLength?: number;
scrollTo?: string;
theme?: Theme;
handleDOMEvents?: any;
readOnlyWriteCheckboxes?: boolean;
onBlur?: (event: SyntheticEvent) => any;
onFocus?: (event: SyntheticEvent) => any;
onPublish?: (event: SyntheticEvent) => any;
onSave?: (a: {
done?: boolean;
autosave?: boolean;
publish?: boolean;
}) => any;
onCancel?: () => any;
onDoubleClick?: () => any;
onChange?: (getValue: () => string) => any;
onSearchLink?: (title: string) => any;
onHoverLink?: (event: MouseEvent) => any;
onCreateLink?: (title: string) => Promise<string>;
onImageUploadStart?: () => any;
onImageUploadStop?: () => any;
};
export type Props = {|
id?: string,
value?: string,
defaultValue?: string,
readOnly?: boolean,
grow?: boolean,
disableEmbeds?: boolean,
ui?: UiStore,
shareId?: ?string,
autoFocus?: boolean,
template?: boolean,
placeholder?: string,
maxLength?: number,
scrollTo?: string,
theme?: Theme,
handleDOMEvents?: Object,
readOnlyWriteCheckboxes?: boolean,
onBlur?: (event: SyntheticEvent<>) => any,
onFocus?: (event: SyntheticEvent<>) => any,
onPublish?: (event: SyntheticEvent<>) => any,
onSave?: ({ done?: boolean, autosave?: boolean, publish?: boolean }) => any,
onCancel?: () => any,
onDoubleClick?: () => any,
onChange?: (getValue: () => string) => any,
onSearchLink?: (title: string) => any,
onHoverLink?: (event: MouseEvent) => any,
onCreateLink?: (title: string) => Promise<string>,
onImageUploadStart?: () => any,
onImageUploadStop?: () => any,
|};
type PropsWithRef = Props & {
forwardedRef: React.Ref<any>;
history: RouterHistory;
forwardedRef: React.Ref<any>,
history: RouterHistory,
};
function Editor(props: PropsWithRef) {
@@ -267,6 +259,6 @@ const Span = styled.span`
const EditorWithRouterAndTheme = withRouter(withTheme(Editor));
export default React.forwardRef<typeof Editor>((props, ref) => (
export default React.forwardRef<Props, typeof Editor>((props, ref) => (
<EditorWithRouterAndTheme {...props} forwardedRef={ref} />
));
@@ -1,3 +1,4 @@
// @flow
import styled from "styled-components";
const Empty = styled.p`
@@ -1,3 +1,4 @@
// @flow
import * as Sentry from "@sentry/react";
import { observable } from "mobx";
import { observer } from "mobx-react";
@@ -11,18 +12,16 @@ import { githubIssuesUrl } from "../../shared/utils/routeHelpers";
import env from "env";
type Props = {
children: React.ReactNode;
reloadOnChunkMissing?: boolean;
children: React.Node,
reloadOnChunkMissing?: boolean,
};
@observer
class ErrorBoundary extends React.Component<Props> {
@observable
error: Error | undefined | null;
@observable
showDetails: boolean = false;
@observable error: ?Error;
@observable showDetails: boolean = false;
componentDidCatch(error: Error, info: any) {
componentDidCatch(error: Error, info: Object) {
this.error = error;
console.error(error);
@@ -1,14 +1,14 @@
// @flow
import * as React from "react";
import { SyntheticEvent } from "react";
type Props = {
children: React.ReactNode;
className?: string;
children: React.Node,
className?: string,
};
export default function EventBoundary({ children, className }: Props) {
const handleClick = React.useCallback((event: SyntheticEvent) => {
const handleClick = React.useCallback((event: SyntheticEvent<>) => {
event.preventDefault();
event.stopPropagation();
}, []);
@@ -1,3 +1,4 @@
// @flow
import { observer } from "mobx-react";
import * as React from "react";
import styled from "styled-components";
@@ -5,15 +6,13 @@ import User from "models/User";
import Avatar from "components/Avatar";
import Flex from "components/Flex";
import { SyntheticEvent } from "react";
type Props = {
users: User[];
size?: number;
overflow: number;
onClick?: (event: SyntheticEvent) => unknown;
renderAvatar?: (user: User) => React.ReactNode;
};
type Props = {|
users: User[],
size?: number,
overflow: number,
onClick?: (event: SyntheticEvent<>) => mixed,
renderAvatar?: (user: User) => React.Node,
|};
function Facepile({
users,
@@ -1,3 +1,4 @@
// @flow
import styled from "styled-components";
import { fadeIn } from "shared/styles/animations";
@@ -1,3 +1,4 @@
// @flow
import { find } from "lodash";
import * as React from "react";
import { useMenuState, MenuButton } from "reakit/Menu";
@@ -7,20 +8,20 @@ import ContextMenu from "components/ContextMenu";
import MenuItem from "components/ContextMenu/MenuItem";
import HelpText from "components/HelpText";
type TFilterOption = {
key: string;
label: string;
note?: string;
};
type TFilterOption = {|
key: string,
label: string,
note?: string,
|};
type Props = {
options: TFilterOption[];
activeKey: string | undefined | null;
defaultLabel?: string;
selectedPrefix?: string;
className?: string;
onSelect: (key?: string | null) => void;
};
type Props = {|
options: TFilterOption[],
activeKey: ?string,
defaultLabel?: string,
selectedPrefix?: string,
className?: string,
onSelect: (key: ?string) => void,
|};
const FilterOptions = ({
options,
@@ -1,3 +1,4 @@
// @flow
import * as React from "react";
import styled from "styled-components";
@@ -7,6 +8,7 @@ type JustifyValues =
| "space-between"
| "flex-start"
| "flex-end";
type AlignValues =
| "stretch"
| "center"
@@ -14,19 +16,29 @@ type AlignValues =
| "flex-start"
| "flex-end";
type Props = {
column?: boolean | null;
shrink?: boolean | null;
align?: AlignValues;
justify?: JustifyValues;
auto?: boolean | null;
className?: string;
children?: React.ReactNode;
role?: string;
gap?: number;
};
type Props = {|
column?: ?boolean,
shrink?: ?boolean,
align?: AlignValues,
justify?: JustifyValues,
auto?: ?boolean,
className?: string,
children?: React.Node,
role?: string,
gap?: number,
|};
const Flex = styled.div<Props>`
const Flex = React.forwardRef<Props, HTMLDivElement>((props: Props, ref) => {
const { children, ...restProps } = props;
return (
<Container ref={ref} {...restProps}>
{children}
</Container>
);
});
const Container = styled.div`
display: flex;
flex: ${({ auto }) => (auto ? "1 1 auto" : "initial")};
flex-direction: ${({ column }) => (column ? "column" : "row")};
@@ -1,3 +1,4 @@
// @flow
import * as React from "react";
import styled from "styled-components";
import Empty from "components/Empty";
@@ -1,9 +1,10 @@
// @flow
import * as React from "react";
type Props = {
size?: number;
fill?: string;
className?: string;
size?: number,
fill?: string,
className?: string,
};
function GithubLogo({ size = 34, fill = "#FFF", className }: Props) {
@@ -1,3 +1,4 @@
// @flow
import { observable } from "mobx";
import { observer, inject } from "mobx-react";
import { GroupIcon } from "outline-icons";
@@ -14,18 +15,17 @@ import ListItem from "components/List/Item";
import Modal from "components/Modal";
type Props = {
group: Group;
groupMemberships: GroupMembershipsStore;
membership?: CollectionGroupMembership;
showFacepile?: boolean;
showAvatar?: boolean;
renderActions: (a: { openMembersModal: () => void }) => React.ReactNode;
group: Group,
groupMemberships: GroupMembershipsStore,
membership?: CollectionGroupMembership,
showFacepile?: boolean,
showAvatar?: boolean,
renderActions: ({ openMembersModal: () => void }) => React.Node,
};
@observer
class GroupListItem extends React.Component<Props> {
@observable
membersModalOpen: boolean = false;
@observable membersModalOpen: boolean = false;
handleMembersModalOpen = () => {
this.membersModalOpen = true;
@@ -1,3 +1,4 @@
// @flow
import { observer } from "mobx-react";
import * as React from "react";
import { Dialog, DialogBackdrop, useDialogState } from "reakit/Dialog";
@@ -5,12 +6,12 @@ import styled from "styled-components";
import Scrollable from "components/Scrollable";
import usePrevious from "hooks/usePrevious";
type Props = {
children?: React.ReactNode;
isOpen: boolean;
title?: string;
onRequestClose: () => void;
};
type Props = {|
children?: React.Node,
isOpen: boolean,
title?: string,
onRequestClose: () => void,
|};
const Guide = ({
children,
@@ -1,3 +1,4 @@
// @flow
import { throttle } from "lodash";
import { observer } from "mobx-react";
import { transparentize } from "polished";
@@ -7,11 +8,11 @@ import breakpoint from "styled-components-breakpoint";
import Fade from "components/Fade";
import Flex from "components/Flex";
type Props = {
breadcrumb?: React.ReactNode;
title: React.ReactNode;
actions?: React.ReactNode;
};
type Props = {|
breadcrumb?: React.Node,
title: React.Node,
actions?: React.Node,
|};
function Header({ breadcrumb, title, actions }: Props) {
const [isScrolled, setScrolled] = React.useState(false);
@@ -1,3 +1,4 @@
// @flow
import styled from "styled-components";
const Heading = styled.h1`
@@ -1,10 +1,7 @@
// @flow
import styled from "styled-components";
type Props = {
small?: boolean;
};
const HelpText = styled.p<Props>`
const HelpText = styled.p`
margin-top: 0;
color: ${(props) => props.theme.textSecondary};
font-size: ${(props) => (props.small ? "13px" : "inherit")};
@@ -1,12 +1,13 @@
// @flow
import * as React from "react";
import replace from "string-replace-to-array";
import styled from "styled-components";
type Props = {
highlight: string | undefined | null | RegExp;
processResult?: (tag: string) => string;
text: string;
caseSensitive?: boolean;
highlight: ?string | RegExp,
processResult?: (tag: string) => string,
text: string,
caseSensitive?: boolean,
};
function Highlight({
@@ -1,3 +1,4 @@
// @flow
import { inject } from "mobx-react";
import { transparentize } from "polished";
import * as React from "react";
@@ -13,10 +14,10 @@ const DELAY_OPEN = 300;
const DELAY_CLOSE = 300;
type Props = {
node: HTMLAnchorElement;
event: MouseEvent;
documents: DocumentsStore;
onClose: () => void;
node: HTMLAnchorElement,
event: MouseEvent,
documents: DocumentsStore,
onClose: () => void,
};
function HoverPreviewInternal({ node, documents, onClose, event }: Props) {
@@ -25,7 +26,7 @@ function HoverPreviewInternal({ node, documents, onClose, event }: Props) {
const [isVisible, setVisible] = React.useState(false);
const timerClose = React.useRef();
const timerOpen = React.useRef();
const cardRef = React.useRef<HTMLDivElement | undefined | null>();
const cardRef = React.useRef<?HTMLDivElement>();
const startCloseTimer = () => {
stopOpenTimer();
@@ -1,3 +1,4 @@
// @flow
import { observer } from "mobx-react";
import * as React from "react";
import { Link } from "react-router-dom";
@@ -8,8 +9,8 @@ import Editor from "components/Editor";
import useStores from "hooks/useStores";
type Props = {
url: string;
children: (a: React.ReactNode) => React.ReactNode;
url: string,
children: (React.Node) => React.Node,
};
function HoverPreviewDocument({ url, children }: Props) {
@@ -1,3 +1,4 @@
// @flow
import {
CollectionIcon,
CoinsIcon,
@@ -125,12 +126,12 @@ const colors = [
"#2F362F",
];
type Props = {
onOpen?: () => void;
onChange: (color: string, icon: string) => void;
icon: string;
color: string;
};
type Props = {|
onOpen?: () => void,
onChange: (color: string, icon: string) => void,
icon: string,
color: string,
|};
function IconPicker({ onOpen, icon, color, onChange }: Props) {
const { t } = useTranslation();
@@ -1,13 +1,14 @@
// @flow
import * as React from "react";
import { cdnPath } from "utils/urls";
type Props = {
alt: string;
src: string;
title?: string;
width?: number;
height?: number;
};
type Props = {|
alt: string,
src: string,
title?: string,
width?: number,
height?: number,
|};
export default function Image({ src, alt, ...rest }: Props) {
return <img src={cdnPath(src)} alt={alt} {...rest} />;
@@ -1,3 +1,4 @@
// @flow
import { observable } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
@@ -6,8 +7,6 @@ import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import Flex from "components/Flex";
import { SyntheticEvent, ChangeEvent, KeyboardEvent } from "react";
const RealTextarea = styled.textarea`
border: 0;
flex: 1;
@@ -85,47 +84,46 @@ export const LabelText = styled.div`
display: inline-block;
`;
export type Props = {
type?: "text" | "email" | "checkbox" | "search" | "textarea";
value?: string;
label?: string;
className?: string;
labelHidden?: boolean;
flex?: boolean;
short?: boolean;
margin?: string | number;
icon?: React.ReactNode;
name?: string;
minLength?: number;
maxLength?: number;
autoFocus?: boolean;
autoComplete?: boolean | string;
readOnly?: boolean;
required?: boolean;
disabled?: boolean;
placeholder?: string;
export type Props = {|
type?: "text" | "email" | "checkbox" | "search" | "textarea",
value?: string,
label?: string,
className?: string,
labelHidden?: boolean,
flex?: boolean,
short?: boolean,
margin?: string | number,
icon?: React.Node,
name?: string,
minLength?: number,
maxLength?: number,
autoFocus?: boolean,
autoComplete?: boolean | string,
readOnly?: boolean,
required?: boolean,
disabled?: boolean,
placeholder?: string,
onChange?: (
ev: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => unknown;
onKeyDown?: (ev: KeyboardEvent<HTMLInputElement>) => unknown;
onFocus?: (ev: SyntheticEvent) => unknown;
onBlur?: (ev: SyntheticEvent) => unknown;
};
ev: SyntheticInputEvent<HTMLInputElement | HTMLTextAreaElement>
) => mixed,
onKeyDown?: (ev: SyntheticKeyboardEvent<HTMLInputElement>) => mixed,
onFocus?: (ev: SyntheticEvent<>) => mixed,
onBlur?: (ev: SyntheticEvent<>) => mixed,
|};
@observer
class Input extends React.Component<Props> {
input: HTMLInputElement | undefined | null;
@observable
focused: boolean = false;
input: ?HTMLInputElement;
@observable focused: boolean = false;
handleBlur = (ev: SyntheticEvent) => {
handleBlur = (ev: SyntheticEvent<>) => {
this.focused = false;
if (this.props.onBlur) {
this.props.onBlur(ev);
}
};
handleFocus = (ev: SyntheticEvent) => {
handleFocus = (ev: SyntheticEvent<>) => {
this.focused = true;
if (this.props.onFocus) {
this.props.onFocus(ev);
@@ -1,3 +1,4 @@
// @flow
import styled from "styled-components";
import Input from "./Input";
@@ -1,3 +1,4 @@
// @flow
import { observable } from "mobx";
import { observer, inject } from "mobx-react";
import * as React from "react";
@@ -7,20 +8,18 @@ import Editor from "components/Editor";
import HelpText from "components/HelpText";
import { LabelText, Outline } from "components/Input";
type Props = {
label: string;
minHeight?: number;
maxHeight?: number;
readOnly?: boolean;
ui: UiStore;
};
type Props = {|
label: string,
minHeight?: number,
maxHeight?: number,
readOnly?: boolean,
ui: UiStore,
|};
@observer
class InputRich extends React.Component<Props> {
@observable
editorComponent: React.ComponentType<any>;
@observable
focused: boolean = false;
@observable editorComponent: React.ComponentType<any>;
@observable focused: boolean = false;
handleBlur = () => {
this.focused = false;
@@ -1,19 +1,17 @@
// @flow
import { SearchIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { useTheme } from "styled-components";
import Input from "./Input";
import Input, { type Props as InputProps } from "./Input";
import { Props as InputProps } from "./Input";
import { ChangeEvent, KeyboardEvent } from "react";
type Props = {
placeholder?: string;
value?: string;
onChange: (event: ChangeEvent) => unknown;
onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => unknown;
} & InputProps;
type Props = {|
...InputProps,
placeholder?: string,
value?: string,
onChange: (event: SyntheticInputEvent<>) => mixed,
onKeyDown?: (event: SyntheticKeyboardEvent<HTMLInputElement>) => mixed,
|};
export default function InputSearch(props: Props) {
const { t } = useTranslation();
@@ -1,38 +1,38 @@
// @flow
import { observable } from "mobx";
import { observer } from "mobx-react";
import { SearchIcon } from "outline-icons";
import * as React from "react";
import { SyntheticEvent, ChangeEvent, KeyboardEvent } from "react";
import { withTranslation, TFunction } from "react-i18next";
import { withTranslation, type TFunction } from "react-i18next";
import keydown from "react-keydown";
import { withRouter, RouteComponentProps } from "react-router-dom";
import { withRouter, type RouterHistory } from "react-router-dom";
import styled, { withTheme } from "styled-components";
import Input from "./Input";
import { Theme } from "types";
import { type Theme } from "types";
import { meta } from "utils/keyboard";
import { searchUrl } from "utils/routeHelpers";
type Props = RouteComponentProps & {
theme: Theme;
source: string;
placeholder?: string;
label?: string;
labelHidden?: boolean;
collectionId?: string;
value: string;
onChange: (event: ChangeEvent) => unknown;
onKeyDown: (event: KeyboardEvent<HTMLInputElement>) => unknown;
t: TFunction;
type Props = {
history: RouterHistory,
theme: Theme,
source: string,
placeholder?: string,
label?: string,
labelHidden?: boolean,
collectionId?: string,
value: string,
onChange: (event: SyntheticInputEvent<>) => mixed,
onKeyDown: (event: SyntheticKeyboardEvent<HTMLInputElement>) => mixed,
t: TFunction,
};
@observer
class InputSearchPage extends React.Component<Props> {
input: Input | undefined | null;
@observable
focused: boolean = false;
input: ?Input;
@observable focused: boolean = false;
@keydown(`${meta}+f`)
focus(ev: SyntheticEvent) {
focus(ev: SyntheticEvent<>) {
ev.preventDefault();
if (this.input) {
@@ -40,7 +40,7 @@ class InputSearchPage extends React.Component<Props> {
}
}
handleSearchInput = (ev: ChangeEvent) => {
handleSearchInput = (ev: SyntheticInputEvent<>) => {
ev.preventDefault();
this.props.history.push(
searchUrl(ev.target.value, {
@@ -1,3 +1,4 @@
// @flow
import { observable } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
@@ -30,26 +31,22 @@ const Wrapper = styled.label`
max-width: ${(props) => (props.short ? "350px" : "100%")};
`;
export type Option = {
label: string;
value: string;
};
export type Option = { label: string, value: string };
export type Props = {
value?: string;
label?: string;
short?: boolean;
className?: string;
labelHidden?: boolean;
options: Option[];
onBlur?: () => void;
onFocus?: () => void;
value?: string,
label?: string,
short?: boolean,
className?: string,
labelHidden?: boolean,
options: Option[],
onBlur?: () => void,
onFocus?: () => void,
};
@observer
class InputSelect extends React.Component<Props> {
@observable
focused: boolean = false;
@observable focused: boolean = false;
handleBlur = () => {
this.focused = false;
@@ -60,8 +57,14 @@ class InputSelect extends React.Component<Props> {
};
render() {
const { label, className, labelHidden, options, short, ...rest } =
this.props;
const {
label,
className,
labelHidden,
options,
short,
...rest
} = this.props;
const wrappedLabel = <LabelText>{label}</LabelText>;
@@ -1,10 +1,11 @@
// @flow
import * as React from "react";
import { useTranslation } from "react-i18next";
import InputSelect from "./InputSelect";
import InputSelect, { type Props, type Option } from "./InputSelect";
import { Props, Option } from "./InputSelect";
export default function InputSelectPermission(props: Omit<Props, "options">) {
export default function InputSelectPermission(
props: $Rest<Props, { options: Array<Option> }>
) {
const { t } = useTranslation();
return (
@@ -1,3 +1,4 @@
// @flow
import styled from "styled-components";
const Key = styled.kbd`
@@ -1,12 +1,13 @@
// @flow
import { observer } from "mobx-react";
import * as React from "react";
import styled from "styled-components";
import Flex from "components/Flex";
type Props = {
label: React.ReactNode | string;
children: React.ReactNode;
};
type Props = {|
label: React.Node | string,
children: React.Node,
|};
const Labeled = ({ label, children, ...props }: Props) => (
<Flex column {...props}>
@@ -1,3 +1,4 @@
// @flow
import { find } from "lodash";
import * as React from "react";
import { Trans, useTranslation } from "react-i18next";
@@ -1,20 +1,18 @@
// @flow
import { observable } from "mobx";
import { observer, inject } from "mobx-react";
import { MenuIcon } from "outline-icons";
import * as React from "react";
import { SyntheticEvent } from "react";
import { Helmet } from "react-helmet";
import { withTranslation, TFunction } from "react-i18next";
import { withTranslation, type TFunction } from "react-i18next";
import keydown from "react-keydown";
import {
Switch,
Route,
Redirect,
withRouter,
RouterHistory,
type RouterHistory,
} from "react-router-dom";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import AuthStore from "stores/AuthStore";
@@ -41,26 +39,24 @@ import {
} from "utils/routeHelpers";
type Props = {
documents: DocumentsStore;
children?: React.ReactNode | null;
actions?: React.ReactNode | null;
title?: React.ReactNode | null;
auth: AuthStore;
ui: UiStore;
history: RouterHistory;
policies: PoliciesStore;
notifications?: React.ReactNode;
i18n: any;
t: TFunction;
documents: DocumentsStore,
children?: ?React.Node,
actions?: ?React.Node,
title?: ?React.Node,
auth: AuthStore,
ui: UiStore,
history: RouterHistory,
policies: PoliciesStore,
notifications?: React.Node,
i18n: Object,
t: TFunction,
};
@observer
class Layout extends React.Component<Props> {
scrollable: HTMLDivElement | undefined | null;
@observable
redirectTo: string | undefined | null;
@observable
keyboardShortcutsOpen: boolean = false;
scrollable: ?HTMLDivElement;
@observable redirectTo: ?string;
@observable keyboardShortcutsOpen: boolean = false;
componentDidUpdate() {
if (this.redirectTo) {
@@ -83,7 +79,7 @@ class Layout extends React.Component<Props> {
};
@keydown(["t", "/", `${meta}+k`])
goToSearch(ev: SyntheticEvent) {
goToSearch(ev: SyntheticEvent<>) {
ev.preventDefault();
ev.stopPropagation();
this.redirectTo = searchUrl();
@@ -1,14 +1,15 @@
// @flow
import * as React from "react";
import styled from "styled-components";
import Flex from "components/Flex";
type Props = {
image?: React.ReactNode;
title: React.ReactNode;
subtitle?: React.ReactNode;
actions?: React.ReactNode;
border?: boolean;
small?: boolean;
image?: React.Node,
title: React.Node,
subtitle?: React.Node,
actions?: React.Node,
border?: boolean,
small?: boolean,
};
const ListItem = ({
@@ -1,3 +1,4 @@
// @flow
import styled from "styled-components";
const List = styled.ol`
@@ -1,3 +1,4 @@
// @flow
import { times } from "lodash";
import * as React from "react";
import styled from "styled-components";
@@ -6,7 +7,7 @@ import Flex from "components/Flex";
import Mask from "components/Mask";
type Props = {
count?: number;
count?: number,
};
const Placeholder = ({ count }: Props) => {
@@ -1,2 +1,3 @@
// @flow
import List from "./List";
export default List;
@@ -0,0 +1,25 @@
// @flow
import { inject, observer } from "mobx-react";
import * as React from "react";
import UiStore from "stores/UiStore";
type Props = {
ui: UiStore,
};
@observer
class LoadingIndicator extends React.Component<Props> {
componentDidMount() {
this.props.ui.enableProgressBar();
}
componentWillUnmount() {
this.props.ui.disableProgressBar();
}
render() {
return null;
}
}
export default inject("ui")(LoadingIndicator);
@@ -1,16 +0,0 @@
import { observer } from "mobx-react";
import * as React from "react";
import useStores from "hooks/useStores";
function LoadingIndicator() {
const { ui } = useStores();
React.useEffect(() => {
ui.enableProgressBar();
return () => ui.disableProgressBar();
}, [ui]);
return null;
}
export default observer(LoadingIndicator);
@@ -1,3 +1,4 @@
// @flow
import * as React from "react";
import styled, { keyframes } from "styled-components";
@@ -1,3 +1,4 @@
// @flow
import LoadingIndicator from "./LoadingIndicator";
import LoadingIndicatorBar from "./LoadingIndicatorBar";
export default LoadingIndicator;
@@ -1,3 +1,4 @@
// @flow
import { times } from "lodash";
import * as React from "react";
import styled from "styled-components";
@@ -6,7 +7,7 @@ import Flex from "components/Flex";
import Mask from "components/Mask";
type Props = {
count?: number;
count?: number,
};
const ListPlaceHolder = ({ count }: Props) => {
@@ -1,3 +1,4 @@
// @flow
import * as React from "react";
import styled from "styled-components";
import DelayedMount from "components/DelayedMount";
@@ -5,7 +6,7 @@ import Fade from "components/Fade";
import Flex from "components/Flex";
import Mask from "components/Mask";
export default function LoadingPlaceholder(props: any) {
export default function LoadingPlaceholder(props: Object) {
return (
<DelayedMount>
<Wrapper>
@@ -1,3 +1,4 @@
// @flow
import ListPlaceholder from "./ListPlaceholder";
import LoadingPlaceholder from "./LoadingPlaceholder";
@@ -1,6 +1,6 @@
// @flow
import { format, formatDistanceToNow } from "date-fns";
import {
format,
formatDistanceToNow,
enUS,
de,
fr,
@@ -12,8 +12,7 @@ import {
zhCN,
zhTW,
ru,
} from "date-fns";
} from "date-fns/locale";
import * as React from "react";
import Tooltip from "components/Tooltip";
import useUserLocale from "hooks/useUserLocale";
@@ -49,11 +48,11 @@ function eachMinute(fn) {
}
type Props = {
dateTime: string;
children?: React.ReactNode;
tooltipDelay?: number;
addSuffix?: boolean;
shorten?: boolean;
dateTime: string,
children?: React.Node,
tooltipDelay?: number,
addSuffix?: boolean,
shorten?: boolean,
};
function LocaleTime({
@@ -65,7 +64,7 @@ function LocaleTime({
}: Props) {
const userLocale = useUserLocale();
const [_, setMinutesMounted] = React.useState(0); // eslint-disable-line no-unused-vars
const callback = React.useRef<Function>();
const callback = React.useRef();
React.useEffect(() => {
callback.current = eachMinute(() => {
@@ -1,15 +1,16 @@
// @flow
import * as React from "react";
import styled from "styled-components";
import { randomInteger } from "shared/random";
import { pulsate } from "shared/styles/animations";
import Flex from "components/Flex";
type Props = {
header?: boolean;
height?: number;
minWidth?: number;
maxWidth?: number;
};
type Props = {|
header?: boolean,
height?: number,
minWidth?: number,
maxWidth?: number,
|};
class Mask extends React.Component<Props> {
width: number;
@@ -19,7 +20,7 @@ class Mask extends React.Component<Props> {
}
constructor(props: Props) {
super(props);
super();
this.width = randomInteger(props.minWidth || 75, props.maxWidth || 100);
}
@@ -1,3 +1,4 @@
// @flow
import { observer } from "mobx-react";
import { CloseIcon, BackIcon } from "outline-icons";
import { transparentize } from "polished";
@@ -15,12 +16,12 @@ import useUnmount from "hooks/useUnmount";
let openModals = 0;
type Props = {
children?: React.ReactNode;
isOpen: boolean;
title?: string;
onRequestClose: () => void;
};
type Props = {|
children?: React.Node,
isOpen: boolean,
title?: string,
onRequestClose: () => void,
|};
const Modal = ({
children,
@@ -1,3 +1,4 @@
// @flow
import styled from "styled-components";
const Notice = styled.p`
@@ -1,11 +1,8 @@
// @flow
import * as React from "react";
import Notice from "components/Notice";
export default function AlertNotice({
children,
}: {
children: React.ReactNode;
}) {
export default function AlertNotice({ children }: { children: React.Node }) {
return (
<Notice muted>
<svg
@@ -1,3 +1,4 @@
// @flow
import styled from "styled-components";
const Notice = styled.p`
@@ -1,3 +1,4 @@
// @flow
import * as React from "react";
import styled from "styled-components";
@@ -13,6 +14,6 @@ const Button = styled.button`
user-select: none;
`;
export default React.forwardRef<typeof Button>(
export default React.forwardRef<any, typeof Button>(
({ size = 24, ...props }, ref) => <Button size={size} {...props} ref={ref} />
);
@@ -1,9 +1,10 @@
// @flow
import * as React from "react";
type Props = {
size?: number;
fill?: string;
className?: string;
size?: number,
fill?: string,
className?: string,
};
function OutlineLogo({ size = 32, fill = "#333", className }: Props) {
@@ -1,3 +1,4 @@
// @flow
import * as React from "react";
import { useTheme } from "styled-components";
import useStores from "hooks/useStores";
@@ -1,13 +1,14 @@
// @flow
import { observer } from "mobx-react";
import * as React from "react";
import { Helmet } from "react-helmet";
import useStores from "hooks/useStores";
import { cdnPath } from "utils/urls";
type Props = {
title: string;
favicon?: string;
};
type Props = {|
title: string,
favicon?: string,
|};
const PageTitle = ({ title, favicon }: Props) => {
const { auth } = useStores();
@@ -1,22 +1,23 @@
// @flow
import { observer } from "mobx-react";
import * as React from "react";
import Document from "models/Document";
import DocumentListItem from "components/DocumentListItem";
import PaginatedList from "components/PaginatedList";
type Props = {
documents: Document[];
fetch: (options?: any | null) => Promise<void>;
options?: any;
heading?: React.ReactNode;
empty?: React.ReactNode;
showNestedDocuments?: boolean;
showCollection?: boolean;
showPublished?: boolean;
showPin?: boolean;
showDraft?: boolean;
showTemplate?: boolean;
};
type Props = {|
documents: Document[],
fetch: (options: ?Object) => Promise<void>,
options?: Object,
heading?: React.Node,
empty?: React.Node,
showNestedDocuments?: boolean,
showCollection?: boolean,
showPublished?: boolean,
showPin?: boolean,
showDraft?: boolean,
showTemplate?: boolean,
|};
@observer
class PaginatedDocumentList extends React.Component<Props> {
@@ -1,3 +1,4 @@
// @flow
import ArrowKeyNavigation from "boundless-arrow-key-navigation";
import { isEqual } from "lodash";
import { observable, action } from "mobx";
@@ -9,29 +10,23 @@ import DelayedMount from "components/DelayedMount";
import { ListPlaceholder } from "components/LoadingPlaceholder";
type Props = {
fetch?: (options?: any | null) => Promise<void>;
options?: any;
heading?: React.ReactNode;
empty?: React.ReactNode;
items: any[];
renderItem: (a: any) => React.ReactNode;
fetch?: (options: ?Object) => Promise<void>,
options?: Object,
heading?: React.Node,
empty?: React.Node,
items: any[],
renderItem: (any) => React.Node,
};
@observer
class PaginatedList extends React.Component<Props> {
isInitiallyLoaded: boolean = false;
@observable
isLoaded: boolean = false;
@observable
isFetchingMore: boolean = false;
@observable
isFetching: boolean = false;
@observable
renderCount: number = DEFAULT_PAGINATION_LIMIT;
@observable
offset: number = 0;
@observable
allowLoadMore: boolean = true;
@observable isLoaded: boolean = false;
@observable isFetchingMore: boolean = false;
@observable isFetching: boolean = false;
@observable renderCount: number = DEFAULT_PAGINATION_LIMIT;
@observable offset: number = 0;
@observable allowLoadMore: boolean = true;
constructor(props: Props) {
super(props);
@@ -1,3 +1,4 @@
// @flow
import "../stores";
import { shallow } from "enzyme";
import * as React from "react";

Some files were not shown because too many files have changed in this diff Show More