import { observer } from "mobx-react"; import * as React from "react"; import { useTranslation } from "react-i18next"; import styled from "styled-components"; import breakpoint from "styled-components-breakpoint"; import { MAX_TITLE_LENGTH } from "@shared/constants"; import { light } from "@shared/theme"; import parseTitle from "@shared/utils/parseTitle"; import Document from "~/models/Document"; import ContentEditable from "~/components/ContentEditable"; import Star, { AnimatedStar } from "~/components/Star"; import useStores from "~/hooks/useStores"; import { isModKey } from "~/utils/keyboard"; type Props = { value: string; document: Document; readOnly?: boolean; onChange: (text: string) => void; onGoToNextInput: (insertParagraph?: boolean) => void; onSave?: (options: { publish?: boolean; done?: boolean }) => void; }; function EditableTitle({ value, document, readOnly, onChange, onSave, onGoToNextInput, }: Props) { const { policies } = useStores(); const { t } = useTranslation(); const can = policies.abilities(document.id); const { emoji } = parseTitle(value); const startsWithEmojiAndSpace = !!(emoji && value.startsWith(`${emoji} `)); const normalizedTitle = !value && readOnly ? document.titleWithDefault : value; const handleKeyDown = React.useCallback( (event: React.KeyboardEvent) => { if (event.key === "Enter") { event.preventDefault(); if (isModKey(event)) { onSave?.({ done: true, }); return; } onGoToNextInput(true); return; } if (event.key === "Tab" || event.key === "ArrowDown") { event.preventDefault(); onGoToNextInput(); return; } if (event.key === "p" && isModKey(event) && event.shiftKey) { event.preventDefault(); onSave?.({ publish: true, done: true, }); return; } if (event.key === "s" && isModKey(event)) { event.preventDefault(); onSave?.({}); return; } }, [onGoToNextInput, onSave] ); return ( {(can.star || can.unstar) && <StarButton document={document} size={32} />} ); } const StarButton = styled(Star)` position: relative; top: 4px; left: 4px; `; const Title = styled(ContentEditable)<{ $startsWithEmojiAndSpace: boolean; $isStarred: boolean; }>` line-height: 1.25; margin-top: 1em; margin-bottom: 0.5em; background: ${(props) => props.theme.background}; transition: ${(props) => props.theme.backgroundTransition}; color: ${(props) => props.theme.text}; -webkit-text-fill-color: ${(props) => props.theme.text}; font-size: 2.25em; font-weight: 500; outline: none; border: 0; padding: 0; resize: none; > span { outline: none; } &::placeholder { color: ${(props) => props.theme.placeholder}; -webkit-text-fill-color: ${(props) => props.theme.placeholder}; } ${breakpoint("tablet")` margin-left: ${(props: any) => props.$startsWithEmojiAndSpace ? "-1.2em" : 0}; `}; ${AnimatedStar} { opacity: ${(props) => (props.$isStarred ? "1 !important" : 0)}; } &:hover { ${AnimatedStar} { opacity: 0.5; &:hover { opacity: 1; } } } @media print { color: ${(props) => light.text}; -webkit-text-fill-color: ${(props) => light.text}; background: none; } `; export default observer(EditableTitle);