RTL document support (#2263)
* Basic RTL support in documents * fix: DocumentListItem and ReferenceListItem for RTL content
This commit is contained in:
@ -46,7 +46,7 @@ export default function ContextMenu({
|
|||||||
<Menu hideOnClickOutside preventBodyScroll {...rest}>
|
<Menu hideOnClickOutside preventBodyScroll {...rest}>
|
||||||
{(props) => (
|
{(props) => (
|
||||||
<Position {...props}>
|
<Position {...props}>
|
||||||
<Background>
|
<Background dir="auto">
|
||||||
{rest.visible || rest.animating ? children : null}
|
{rest.visible || rest.animating ? children : null}
|
||||||
</Background>
|
</Background>
|
||||||
</Position>
|
</Position>
|
||||||
|
@ -69,6 +69,7 @@ function DocumentListItem(props: Props, ref) {
|
|||||||
return (
|
return (
|
||||||
<DocumentLink
|
<DocumentLink
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
dir={document.dir}
|
||||||
$isStarred={document.isStarred}
|
$isStarred={document.isStarred}
|
||||||
$menuOpen={menuOpen}
|
$menuOpen={menuOpen}
|
||||||
to={{
|
to={{
|
||||||
@ -77,8 +78,12 @@ function DocumentListItem(props: Props, ref) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Content>
|
<Content>
|
||||||
<Heading>
|
<Heading dir={document.dir}>
|
||||||
<Title text={document.titleWithDefault} highlight={highlight} />
|
<Title
|
||||||
|
text={document.titleWithDefault}
|
||||||
|
highlight={highlight}
|
||||||
|
dir={document.dir}
|
||||||
|
/>
|
||||||
{document.isNew && document.createdBy.id !== currentUser.id && (
|
{document.isNew && document.createdBy.id !== currentUser.id && (
|
||||||
<Badge yellow>{t("New")}</Badge>
|
<Badge yellow>{t("New")}</Badge>
|
||||||
)}
|
)}
|
||||||
@ -222,6 +227,7 @@ const DocumentLink = styled(Link)`
|
|||||||
|
|
||||||
const Heading = styled.h3`
|
const Heading = styled.h3`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: ${(props) => (props.rtl ? "flex-end" : "flex-start")};
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
@ -11,6 +11,7 @@ import Time from "components/Time";
|
|||||||
import useStores from "hooks/useStores";
|
import useStores from "hooks/useStores";
|
||||||
|
|
||||||
const Container = styled(Flex)`
|
const Container = styled(Flex)`
|
||||||
|
justify-content: ${(props) => (props.rtl ? "flex-end" : "flex-start")};
|
||||||
color: ${(props) => props.theme.textTertiary};
|
color: ${(props) => props.theme.textTertiary};
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@ -135,7 +136,7 @@ function DocumentMeta({
|
|||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container align="center" {...rest}>
|
<Container align="center" rtl={document.dir === "rtl"} {...rest} dir="ltr">
|
||||||
{updatedByMe ? t("You") : updatedBy.name}
|
{updatedByMe ? t("You") : updatedBy.name}
|
||||||
{to ? <Link to={to}>{content}</Link> : content}
|
{to ? <Link to={to}>{content}</Link> : content}
|
||||||
{showCollection && collection && (
|
{showCollection && collection && (
|
||||||
|
@ -14,6 +14,7 @@ type Props = {|
|
|||||||
document: Document,
|
document: Document,
|
||||||
isDraft: boolean,
|
isDraft: boolean,
|
||||||
to?: string,
|
to?: string,
|
||||||
|
rtl?: boolean,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
function DocumentMetaWithViews({ to, isDraft, document, ...rest }: Props) {
|
function DocumentMetaWithViews({ to, isDraft, document, ...rest }: Props) {
|
||||||
@ -62,6 +63,7 @@ function DocumentMetaWithViews({ to, isDraft, document, ...rest }: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Meta = styled(DocumentMeta)`
|
const Meta = styled(DocumentMeta)`
|
||||||
|
justify-content: ${(props) => (props.rtl ? "flex-end" : "flex-start")};
|
||||||
margin: -12px 0 2em 0;
|
margin: -12px 0 2em 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -84,7 +84,7 @@ function SidebarLink(
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
{icon && <IconWrapper>{icon}</IconWrapper>}
|
{icon && <IconWrapper>{icon}</IconWrapper>}
|
||||||
<Label>{label}</Label>
|
<Label dir="auto">{label}</Label>
|
||||||
</Link>
|
</Link>
|
||||||
{menu && <Actions showActions={showActions}>{menu}</Actions>}
|
{menu && <Actions showActions={showActions}>{menu}</Actions>}
|
||||||
</>
|
</>
|
||||||
|
@ -58,6 +58,26 @@ export default class Document extends BaseModel {
|
|||||||
return emoji;
|
return emoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Best-guess the text direction of the document based on the language the
|
||||||
|
* title is written in. Note: wrapping as a computed getter means that it will
|
||||||
|
* only be called directly when the title changes.
|
||||||
|
*/
|
||||||
|
@computed
|
||||||
|
get dir(): "rtl" | "ltr" {
|
||||||
|
const element = document.createElement("p");
|
||||||
|
element.innerHTML = this.title;
|
||||||
|
element.style.visibility = "hidden";
|
||||||
|
element.dir = "auto";
|
||||||
|
|
||||||
|
// element must appear in body for direction to be computed
|
||||||
|
document.body?.appendChild(element);
|
||||||
|
|
||||||
|
const direction = window.getComputedStyle(element).direction;
|
||||||
|
document.body?.removeChild(element);
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
get noun(): string {
|
get noun(): string {
|
||||||
return this.template ? "template" : "document";
|
return this.template ? "template" : "document";
|
||||||
|
@ -33,6 +33,7 @@ type Props = {|
|
|||||||
@observer
|
@observer
|
||||||
class DocumentEditor extends React.Component<Props> {
|
class DocumentEditor extends React.Component<Props> {
|
||||||
@observable activeLinkEvent: ?MouseEvent;
|
@observable activeLinkEvent: ?MouseEvent;
|
||||||
|
@observable ref = React.createRef<HTMLDivElement | HTMLInputElement>();
|
||||||
|
|
||||||
focusAtStart = () => {
|
focusAtStart = () => {
|
||||||
if (this.props.innerRef.current) {
|
if (this.props.innerRef.current) {
|
||||||
@ -109,13 +110,17 @@ class DocumentEditor extends React.Component<Props> {
|
|||||||
const normalizedTitle =
|
const normalizedTitle =
|
||||||
!title && readOnly ? document.titleWithDefault : title;
|
!title && readOnly ? document.titleWithDefault : title;
|
||||||
|
|
||||||
|
console.log(this.ref.current);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex auto column>
|
<Flex auto column>
|
||||||
{readOnly ? (
|
{readOnly ? (
|
||||||
<Title
|
<Title
|
||||||
as="div"
|
as="div"
|
||||||
|
ref={this.ref}
|
||||||
$startsWithEmojiAndSpace={startsWithEmojiAndSpace}
|
$startsWithEmojiAndSpace={startsWithEmojiAndSpace}
|
||||||
$isStarred={document.isStarred}
|
$isStarred={document.isStarred}
|
||||||
|
dir="auto"
|
||||||
>
|
>
|
||||||
<span>{normalizedTitle}</span>{" "}
|
<span>{normalizedTitle}</span>{" "}
|
||||||
{!shareId && <StarButton document={document} size={32} />}
|
{!shareId && <StarButton document={document} size={32} />}
|
||||||
@ -123,6 +128,7 @@ class DocumentEditor extends React.Component<Props> {
|
|||||||
) : (
|
) : (
|
||||||
<Title
|
<Title
|
||||||
type="text"
|
type="text"
|
||||||
|
ref={this.ref}
|
||||||
onChange={onChangeTitle}
|
onChange={onChangeTitle}
|
||||||
onKeyDown={this.handleTitleKeyDown}
|
onKeyDown={this.handleTitleKeyDown}
|
||||||
placeholder={document.placeholder}
|
placeholder={document.placeholder}
|
||||||
@ -130,6 +136,7 @@ class DocumentEditor extends React.Component<Props> {
|
|||||||
$startsWithEmojiAndSpace={startsWithEmojiAndSpace}
|
$startsWithEmojiAndSpace={startsWithEmojiAndSpace}
|
||||||
autoFocus={!title}
|
autoFocus={!title}
|
||||||
maxLength={MAX_TITLE_LENGTH}
|
maxLength={MAX_TITLE_LENGTH}
|
||||||
|
dir="auto"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!shareId && (
|
{!shareId && (
|
||||||
@ -137,6 +144,11 @@ class DocumentEditor extends React.Component<Props> {
|
|||||||
isDraft={isDraft}
|
isDraft={isDraft}
|
||||||
document={document}
|
document={document}
|
||||||
to={documentHistoryUrl(document)}
|
to={documentHistoryUrl(document)}
|
||||||
|
rtl={
|
||||||
|
this.ref.current
|
||||||
|
? window.getComputedStyle(this.ref.current).direction === "rtl"
|
||||||
|
: false
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Editor
|
<Editor
|
||||||
|
@ -35,7 +35,6 @@ const DocumentLink = styled(Link)`
|
|||||||
const Title = styled.h3`
|
const Title = styled.h3`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
max-width: 90%;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@ -78,7 +77,7 @@ function ReferenceListItem({
|
|||||||
}}
|
}}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<Title>
|
<Title dir="auto">
|
||||||
{document.emoji ? (
|
{document.emoji ? (
|
||||||
<Emoji>{document.emoji}</Emoji>
|
<Emoji>{document.emoji}</Emoji>
|
||||||
) : (
|
) : (
|
||||||
|
@ -123,7 +123,7 @@
|
|||||||
"randomstring": "1.1.5",
|
"randomstring": "1.1.5",
|
||||||
"raw-loader": "^0.5.1",
|
"raw-loader": "^0.5.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-autosize-textarea": "^6.0.0",
|
"react-autosize-textarea": "^7.1.0",
|
||||||
"react-avatar-editor": "^11.1.0",
|
"react-avatar-editor": "^11.1.0",
|
||||||
"react-color": "^2.17.3",
|
"react-color": "^2.17.3",
|
||||||
"react-dnd": "^14.0.1",
|
"react-dnd": "^14.0.1",
|
||||||
@ -142,7 +142,7 @@
|
|||||||
"react-window": "^1.8.6",
|
"react-window": "^1.8.6",
|
||||||
"reakit": "^1.3.6",
|
"reakit": "^1.3.6",
|
||||||
"regenerator-runtime": "^0.13.7",
|
"regenerator-runtime": "^0.13.7",
|
||||||
"rich-markdown-editor": "^11.12.0",
|
"rich-markdown-editor": "^11.13.0",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.2",
|
||||||
"sequelize": "^6.3.4",
|
"sequelize": "^6.3.4",
|
||||||
"sequelize-cli": "^6.2.0",
|
"sequelize-cli": "^6.2.0",
|
||||||
|
@ -11606,10 +11606,10 @@ retry-as-promised@^3.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
any-promise "^1.3.0"
|
any-promise "^1.3.0"
|
||||||
|
|
||||||
rich-markdown-editor@^11.12.0:
|
rich-markdown-editor@^11.13.0:
|
||||||
version "11.12.0"
|
version "11.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/rich-markdown-editor/-/rich-markdown-editor-11.12.0.tgz#7fd9bd80b0afcb06d2bbcb8a2d38ac2279bc3110"
|
resolved "https://registry.yarnpkg.com/rich-markdown-editor/-/rich-markdown-editor-11.13.0.tgz#7009bddbd667c2fdcf6a7d4210913a1c040acdde"
|
||||||
integrity sha512-mf5i/JJZktNwQnEjjQIYGzIoBvh3LAmCZoRC8EcbsACO3ISgOcNdccmdd0utZMs4QHASUgE+eBjtM+awvhIPqw==
|
integrity sha512-WZLL7z0sE7kogYjpPWPbLnbWjYwCn5RLoju6Vg6WfVDVWPSRRfWo+IHSKpBAK8kP+h9pKxZItwHrHN4ShXapjA==
|
||||||
dependencies:
|
dependencies:
|
||||||
copy-to-clipboard "^3.0.8"
|
copy-to-clipboard "^3.0.8"
|
||||||
lodash "^4.17.11"
|
lodash "^4.17.11"
|
||||||
|
Reference in New Issue
Block a user