From 034014945792474100e600e63e1b1f00191ec7f5 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sun, 12 Dec 2021 18:44:32 -0800 Subject: [PATCH] chore: More type improvements --- app/components/Editor.tsx | 161 ++++---------------- app/components/InputRich.tsx | 10 +- app/components/Tooltip.tsx | 8 +- app/hooks/useDictionary.ts | 74 +++++++++ app/scenes/Document/components/Document.tsx | 1 - app/scenes/Document/components/Editor.tsx | 2 +- shared/embeds/index.tsx | 10 +- shared/i18n/locales/en_US/translation.json | 119 ++++++++------- 8 files changed, 176 insertions(+), 209 deletions(-) create mode 100644 app/hooks/useDictionary.ts diff --git a/app/components/Editor.tsx b/app/components/Editor.tsx index cd5413717..c2b2cb769 100644 --- a/app/components/Editor.tsx +++ b/app/components/Editor.tsx @@ -1,13 +1,14 @@ import { lighten } from "polished"; import * as React from "react"; -import { useTranslation } from "react-i18next"; -import { Extension } from "rich-markdown-editor"; -import styled, { DefaultTheme, withTheme } from "styled-components"; +import { Props as EditorProps } from "rich-markdown-editor"; +import { EmbedDescriptor } from "rich-markdown-editor/dist/types"; +import styled, { useTheme } from "styled-components"; +import { Optional } from "utility-types"; import embeds from "@shared/embeds"; import { light } from "@shared/theme"; -import UiStore from "~/stores/UiStore"; import ErrorBoundary from "~/components/ErrorBoundary"; import Tooltip from "~/components/Tooltip"; +import useDictionary from "~/hooks/useDictionary"; import useMediaQuery from "~/hooks/useMediaQuery"; import useToasts from "~/hooks/useToasts"; import history from "~/utils/history"; @@ -23,56 +24,25 @@ const RichMarkdownEditor = React.lazy( ) ); -// @ts-expect-error ts-migrate(7034) FIXME: Variable 'EMPTY_ARRAY' implicitly has type 'any[]'... Remove this comment to see the full error message -const EMPTY_ARRAY = []; +const EMPTY_ARRAY: EmbedDescriptor[] = []; -export type Props = { - id?: string; - value?: string; - defaultValue?: string; - readOnly?: boolean; - grow?: boolean; +export type Props = Optional< + EditorProps, + "placeholder" | "defaultValue" | "tooltip" | "onClickLink" | "embeds" +> & { + shareId?: string | undefined; disableEmbeds?: boolean; - ui?: UiStore; - style?: React.CSSProperties; - extensions?: Extension[]; - shareId?: string | null | undefined; - autoFocus?: boolean; - template?: boolean; - placeholder?: string; - maxLength?: number; - scrollTo?: string; - theme?: DefaultTheme; - className?: string; - readOnlyWriteCheckboxes?: boolean; - onBlur?: () => void; - onFocus?: () => void; - onPublish?: (event: React.MouseEvent) => any; - onSave?: (arg0: { - done?: boolean; - autosave?: boolean; - publish?: boolean; - }) => any; + grow?: boolean; onSynced?: () => Promise; - onCancel?: () => any; - onDoubleClick?: () => any; - onChange?: (getValue: () => string) => any; - onSearchLink?: (title: string) => any; - onHoverLink?: (event: MouseEvent) => any; - onCreateLink?: (title: string) => Promise; - onImageUploadStart?: () => any; - onImageUploadStop?: () => any; + onPublish?: (event: React.MouseEvent) => any; }; -type PropsWithRef = Props & { - forwardedRef: React.Ref; -}; - -function Editor(props: PropsWithRef) { +function Editor(props: Props, ref: React.Ref) { const { id, shareId } = props; - const { t } = useTranslation(); + const theme = useTheme(); const { showToast } = useToasts(); const isPrinting = useMediaQuery("print"); + const dictionary = useDictionary(); const onUploadImage = React.useCallback( async (file: File) => { @@ -115,7 +85,7 @@ function Editor(props: PropsWithRef) { window.open(href, "_blank"); } }, - [history, shareId] + [shareId] ); const onShowToast = React.useCallback( @@ -125,88 +95,20 @@ function Editor(props: PropsWithRef) { [showToast] ); - const dictionary = React.useMemo(() => { - return { - addColumnAfter: t("Insert column after"), - addColumnBefore: t("Insert column before"), - addRowAfter: t("Insert row after"), - addRowBefore: t("Insert row before"), - alignCenter: t("Align center"), - alignLeft: t("Align left"), - alignRight: t("Align right"), - bulletList: t("Bulleted list"), - checkboxList: t("Todo list"), - codeBlock: t("Code block"), - codeCopied: t("Copied to clipboard"), - codeInline: t("Code"), - createLink: t("Create link"), - createLinkError: t("Sorry, an error occurred creating the link"), - createNewDoc: t("Create a new doc"), - deleteColumn: t("Delete column"), - deleteRow: t("Delete row"), - deleteTable: t("Delete table"), - deleteImage: t("Delete image"), - downloadImage: t("Download image"), - alignImageLeft: t("Float left"), - alignImageRight: t("Float right"), - alignImageDefault: t("Center large"), - em: t("Italic"), - embedInvalidLink: t("Sorry, that link won’t work for this embed type"), - findOrCreateDoc: `${t("Find or create a doc")}…`, - h1: t("Big heading"), - h2: t("Medium heading"), - h3: t("Small heading"), - heading: t("Heading"), - hr: t("Divider"), - image: t("Image"), - imageUploadError: t("Sorry, an error occurred uploading the image"), - imageCaptionPlaceholder: t("Write a caption"), - info: t("Info"), - infoNotice: t("Info notice"), - link: t("Link"), - linkCopied: t("Link copied to clipboard"), - mark: t("Highlight"), - newLineEmpty: `${t("Type '/' to insert")}…`, - newLineWithSlash: `${t("Keep typing to filter")}…`, - noResults: t("No results"), - openLink: t("Open link"), - orderedList: t("Ordered list"), - pageBreak: t("Page break"), - pasteLink: `${t("Paste a link")}…`, - pasteLinkWithTitle: (service: string) => - t("Paste a {{service}} link…", { - service, - }), - placeholder: t("Placeholder"), - quote: t("Quote"), - removeLink: t("Remove link"), - searchOrPasteLink: `${t("Search or paste a link")}…`, - strikethrough: t("Strikethrough"), - strong: t("Bold"), - subheading: t("Subheading"), - table: t("Table"), - tip: t("Tip"), - tipNotice: t("Tip notice"), - warning: t("Warning"), - warningNotice: t("Warning notice"), - }; - }, [t]); - return ( ); @@ -320,20 +222,19 @@ const StyledEditor = styled(RichMarkdownEditor)<{ grow?: boolean }>` } `; -// @ts-expect-error ts-migrate(7031) FIXME: Binding element 'children' implicitly has an 'any'... Remove this comment to see the full error message -const EditorTooltip = ({ children, ...props }) => ( - // @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call. - - {children} +type TooltipProps = { + children: React.ReactNode; + tooltip: string; +}; + +const EditorTooltip = ({ children, tooltip, ...props }: TooltipProps) => ( + + {children} ); -const Span = styled.span` +const TooltipContent = styled.span` outline: none; `; -const EditorWithTheme = withTheme(Editor); - -export default React.forwardRef((props, ref) => ( - -)); +export default React.forwardRef(Editor); diff --git a/app/components/InputRich.tsx b/app/components/InputRich.tsx index bc0457ee7..97fcbcf23 100644 --- a/app/components/InputRich.tsx +++ b/app/components/InputRich.tsx @@ -5,7 +5,6 @@ import styled from "styled-components"; import Editor from "~/components/Editor"; import HelpText from "~/components/HelpText"; import { LabelText, Outline } from "~/components/Input"; -import useStores from "~/hooks/useStores"; type Props = { label: string; @@ -16,7 +15,6 @@ type Props = { function InputRich({ label, minHeight, maxHeight, ...rest }: Props) { const [focused, setFocused] = React.useState(false); - const { ui } = useStores(); const handleBlur = React.useCallback(() => { setFocused(false); }, []); @@ -39,13 +37,7 @@ function InputRich({ label, minHeight, maxHeight, ...rest }: Props) { } > - + diff --git a/app/components/Tooltip.tsx b/app/components/Tooltip.tsx index 5223115d1..00f4704e1 100644 --- a/app/components/Tooltip.tsx +++ b/app/components/Tooltip.tsx @@ -1,15 +1,11 @@ -import Tippy from "@tippy.js/react"; +import Tippy, { TippyProps } from "@tippy.js/react"; import { TFunctionResult } from "i18next"; import * as React from "react"; import styled from "styled-components"; -type Props = { +type Props = Omit & { tooltip: React.ReactChild | React.ReactChild[] | TFunctionResult; shortcut?: React.ReactNode; - placement?: "top" | "bottom" | "left" | "right"; - children: React.ReactElement; - delay?: number; - className?: string; }; function Tooltip({ shortcut, tooltip, delay = 50, ...rest }: Props) { diff --git a/app/hooks/useDictionary.ts b/app/hooks/useDictionary.ts new file mode 100644 index 000000000..b18015f17 --- /dev/null +++ b/app/hooks/useDictionary.ts @@ -0,0 +1,74 @@ +import * as React from "react"; +import { useTranslation } from "react-i18next"; + +export default function useDictionary() { + const { t } = useTranslation(); + + return React.useMemo(() => { + return { + addColumnAfter: t("Insert column after"), + addColumnBefore: t("Insert column before"), + addRowAfter: t("Insert row after"), + addRowBefore: t("Insert row before"), + alignCenter: t("Align center"), + alignLeft: t("Align left"), + alignRight: t("Align right"), + bulletList: t("Bulleted list"), + checkboxList: t("Todo list"), + codeBlock: t("Code block"), + codeCopied: t("Copied to clipboard"), + codeInline: t("Code"), + createLink: t("Create link"), + createLinkError: t("Sorry, an error occurred creating the link"), + createNewDoc: t("Create a new doc"), + deleteColumn: t("Delete column"), + deleteRow: t("Delete row"), + deleteTable: t("Delete table"), + deleteImage: t("Delete image"), + downloadImage: t("Download image"), + replaceImage: t("Replace image"), + alignImageLeft: t("Float left"), + alignImageRight: t("Float right"), + alignImageDefault: t("Center large"), + em: t("Italic"), + embedInvalidLink: t("Sorry, that link won’t work for this embed type"), + findOrCreateDoc: `${t("Find or create a doc")}…`, + h1: t("Big heading"), + h2: t("Medium heading"), + h3: t("Small heading"), + heading: t("Heading"), + hr: t("Divider"), + image: t("Image"), + imageUploadError: t("Sorry, an error occurred uploading the image"), + imageCaptionPlaceholder: t("Write a caption"), + info: t("Info"), + infoNotice: t("Info notice"), + link: t("Link"), + linkCopied: t("Link copied to clipboard"), + mark: t("Highlight"), + newLineEmpty: `${t("Type '/' to insert")}…`, + newLineWithSlash: `${t("Keep typing to filter")}…`, + noResults: t("No results"), + openLink: t("Open link"), + orderedList: t("Ordered list"), + pageBreak: t("Page break"), + pasteLink: `${t("Paste a link")}…`, + pasteLinkWithTitle: (service: string) => + t("Paste a {{service}} link…", { + service, + }), + placeholder: t("Placeholder"), + quote: t("Quote"), + removeLink: t("Remove link"), + searchOrPasteLink: `${t("Search or paste a link")}…`, + strikethrough: t("Strikethrough"), + strong: t("Bold"), + subheading: t("Subheading"), + table: t("Table"), + tip: t("Tip"), + tipNotice: t("Tip notice"), + warning: t("Warning"), + warningNotice: t("Warning notice"), + }; + }, [t]); +} diff --git a/app/scenes/Document/components/Document.tsx b/app/scenes/Document/components/Document.tsx index 6c10c41b7..7288856a2 100644 --- a/app/scenes/Document/components/Document.tsx +++ b/app/scenes/Document/components/Document.tsx @@ -568,7 +568,6 @@ class DocumentScene extends React.Component { onCancel={this.goBack} readOnly={readOnly} readOnlyWriteCheckboxes={readOnly && abilities.update} - ui={this.props.ui} > {shareId && ( diff --git a/app/scenes/Document/components/Editor.tsx b/app/scenes/Document/components/Editor.tsx index dc1a04d8c..0d522fe68 100644 --- a/app/scenes/Document/components/Editor.tsx +++ b/app/scenes/Document/components/Editor.tsx @@ -18,7 +18,6 @@ type Props = EditorProps & title: string; document: Document; isDraft: boolean; - shareId: string | null | undefined; multiplayer?: boolean; onSave: (arg0: { done?: boolean; @@ -60,6 +59,7 @@ class DocumentEditor extends React.Component { handleLinkActive = (event: MouseEvent) => { this.activeLinkEvent = event; + return false; }; handleLinkInactive = () => { diff --git a/shared/embeds/index.tsx b/shared/embeds/index.tsx index 88eb0d29a..f03836cb1 100644 --- a/shared/embeds/index.tsx +++ b/shared/embeds/index.tsx @@ -1,4 +1,5 @@ import * as React from "react"; +import { EmbedDescriptor } from "rich-markdown-editor/dist/types"; import styled from "styled-components"; import Abstract from "./Abstract"; import Airtable from "./Airtable"; @@ -43,7 +44,7 @@ export type EmbedProps = { }; function matcher(Component: React.ComponentType) { - return (url: string) => { + return (url: string): boolean | [] | RegExpMatchArray => { // @ts-expect-error not aware of static const regexes = Component.ENABLED; @@ -55,7 +56,7 @@ function matcher(Component: React.ComponentType) { } } - return undefined; + return false; }; } @@ -65,7 +66,7 @@ const Img = styled(Image)` height: 18px; `; -export default [ +const embeds: EmbedDescriptor[] = [ { title: "Abstract", keywords: "design", @@ -162,6 +163,7 @@ export default [ }, { title: "Google Docs", + keywords: "documents word", icon: () => Google Docs, component: GoogleDocs, matcher: matcher(GoogleDocs), @@ -298,3 +300,5 @@ export default [ matcher: matcher(YouTube), }, ]; + +export default embeds; diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json index 40c578d02..1907ee82b 100644 --- a/shared/i18n/locales/en_US/translation.json +++ b/shared/i18n/locales/en_US/translation.json @@ -88,65 +88,6 @@ "Currently editing": "Currently editing", "Currently viewing": "Currently viewing", "Viewed {{ timeAgo }} ago": "Viewed {{ timeAgo }} ago", - "Insert column after": "Insert column after", - "Insert column before": "Insert column before", - "Insert row after": "Insert row after", - "Insert row before": "Insert row before", - "Align center": "Align center", - "Align left": "Align left", - "Align right": "Align right", - "Bulleted list": "Bulleted list", - "Todo list": "Task list", - "Code block": "Code block", - "Copied to clipboard": "Copied to clipboard", - "Code": "Code", - "Create link": "Create link", - "Sorry, an error occurred creating the link": "Sorry, an error occurred creating the link", - "Create a new doc": "Create a new doc", - "Delete column": "Delete column", - "Delete row": "Delete row", - "Delete table": "Delete table", - "Delete image": "Delete image", - "Download image": "Download image", - "Float left": "Float left", - "Float right": "Float right", - "Center large": "Center large", - "Italic": "Italic", - "Sorry, that link won’t work for this embed type": "Sorry, that link won’t work for this embed type", - "Find or create a doc": "Find or create a doc", - "Big heading": "Big heading", - "Medium heading": "Medium heading", - "Small heading": "Small heading", - "Heading": "Heading", - "Divider": "Divider", - "Image": "Image", - "Sorry, an error occurred uploading the image": "Sorry, an error occurred uploading the image", - "Write a caption": "Write a caption", - "Info": "Info", - "Info notice": "Info notice", - "Link": "Link", - "Link copied to clipboard": "Link copied to clipboard", - "Highlight": "Highlight", - "Type '/' to insert": "Type '/' to insert", - "Keep typing to filter": "Keep typing to filter", - "No results": "No results", - "Open link": "Open link", - "Ordered list": "Ordered list", - "Page break": "Page break", - "Paste a link": "Paste a link", - "Paste a {{service}} link…": "Paste a {{service}} link…", - "Placeholder": "Placeholder", - "Quote": "Quote", - "Remove link": "Remove link", - "Search or paste a link": "Search or paste a link", - "Strikethrough": "Strikethrough", - "Bold": "Bold", - "Subheading": "Subheading", - "Table": "Table", - "Tip": "Tip", - "Tip notice": "Tip notice", - "Warning": "Warning", - "Warning notice": "Warning notice", "Module failed to load": "Module failed to load", "Loading Failed": "Loading Failed", "Sorry, part of the application failed to load. This may be because it was updated since you opened the tab or because of a failed network request. Please try reloading.": "Sorry, part of the application failed to load. This may be because it was updated since you opened the tab or because of a failed network request. Please try reloading.", @@ -209,8 +150,68 @@ "Export": "Export", "Integrations": "Integrations", "Installation": "Installation", + "No results": "No results", "Previous page": "Previous page", "Next page": "Next page", + "Insert column after": "Insert column after", + "Insert column before": "Insert column before", + "Insert row after": "Insert row after", + "Insert row before": "Insert row before", + "Align center": "Align center", + "Align left": "Align left", + "Align right": "Align right", + "Bulleted list": "Bulleted list", + "Todo list": "Task list", + "Code block": "Code block", + "Copied to clipboard": "Copied to clipboard", + "Code": "Code", + "Create link": "Create link", + "Sorry, an error occurred creating the link": "Sorry, an error occurred creating the link", + "Create a new doc": "Create a new doc", + "Delete column": "Delete column", + "Delete row": "Delete row", + "Delete table": "Delete table", + "Delete image": "Delete image", + "Download image": "Download image", + "Replace image": "Replace image", + "Float left": "Float left", + "Float right": "Float right", + "Center large": "Center large", + "Italic": "Italic", + "Sorry, that link won’t work for this embed type": "Sorry, that link won’t work for this embed type", + "Find or create a doc": "Find or create a doc", + "Big heading": "Big heading", + "Medium heading": "Medium heading", + "Small heading": "Small heading", + "Heading": "Heading", + "Divider": "Divider", + "Image": "Image", + "Sorry, an error occurred uploading the image": "Sorry, an error occurred uploading the image", + "Write a caption": "Write a caption", + "Info": "Info", + "Info notice": "Info notice", + "Link": "Link", + "Link copied to clipboard": "Link copied to clipboard", + "Highlight": "Highlight", + "Type '/' to insert": "Type '/' to insert", + "Keep typing to filter": "Keep typing to filter", + "Open link": "Open link", + "Ordered list": "Ordered list", + "Page break": "Page break", + "Paste a link": "Paste a link", + "Paste a {{service}} link…": "Paste a {{service}} link…", + "Placeholder": "Placeholder", + "Quote": "Quote", + "Remove link": "Remove link", + "Search or paste a link": "Search or paste a link", + "Strikethrough": "Strikethrough", + "Bold": "Bold", + "Subheading": "Subheading", + "Table": "Table", + "Tip": "Tip", + "Tip notice": "Tip notice", + "Warning": "Warning", + "Warning notice": "Warning notice", "Could not import file": "Could not import file", "Switch team": "Switch team", "Show path to document": "Show path to document",