mirror of
https://github.com/outline/outline.git
synced 2025-04-13 01:29:39 +00:00
feat: Add option to 'Create new child doc' from link editor
This commit is contained in:
@ -35,7 +35,7 @@ type Props = {
|
||||
to: number;
|
||||
dictionary: Dictionary;
|
||||
onRemoveLink?: () => void;
|
||||
onCreateLink?: (title: string) => Promise<void>;
|
||||
onCreateLink?: (title: string, nested?: boolean) => Promise<void>;
|
||||
onSearchLink?: (term: string) => Promise<SearchResult[]>;
|
||||
onSelectLink: (options: {
|
||||
href: string;
|
||||
@ -186,7 +186,7 @@ class LinkEditor extends React.Component<Props, State> {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const { selectedIndex } = this.state;
|
||||
const total = results.length;
|
||||
const total = results.length + 1;
|
||||
const nextIndex = selectedIndex + 1;
|
||||
|
||||
this.setState({
|
||||
@ -243,17 +243,17 @@ class LinkEditor extends React.Component<Props, State> {
|
||||
}
|
||||
};
|
||||
|
||||
handleCreateLink = async (value: string) => {
|
||||
handleCreateLink = async (title: string, nested?: boolean) => {
|
||||
this.discardInputValue = true;
|
||||
const { onCreateLink } = this.props;
|
||||
|
||||
value = value.trim();
|
||||
if (value.length === 0) {
|
||||
title = title.trim();
|
||||
if (title.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (onCreateLink) {
|
||||
return onCreateLink(value);
|
||||
return onCreateLink(title, nested);
|
||||
}
|
||||
};
|
||||
|
||||
@ -368,22 +368,42 @@ class LinkEditor extends React.Component<Props, State> {
|
||||
))}
|
||||
|
||||
{showCreateLink && (
|
||||
<LinkSearchResult
|
||||
key="create"
|
||||
containerRef={this.resultsRef}
|
||||
title={suggestedLinkTitle}
|
||||
subtitle={dictionary.createNewDoc}
|
||||
icon={<PlusIcon />}
|
||||
onPointerMove={() => this.handleFocusLink(results.length)}
|
||||
onClick={async () => {
|
||||
await this.handleCreateLink(suggestedLinkTitle);
|
||||
<>
|
||||
<LinkSearchResult
|
||||
key="create"
|
||||
containerRef={this.resultsRef}
|
||||
title={suggestedLinkTitle}
|
||||
subtitle={dictionary.createNewDoc}
|
||||
icon={<PlusIcon />}
|
||||
onPointerMove={() => this.handleFocusLink(results.length)}
|
||||
onClick={async () => {
|
||||
await this.handleCreateLink(suggestedLinkTitle);
|
||||
|
||||
if (this.initialSelectionLength) {
|
||||
this.moveSelectionToEnd();
|
||||
if (this.initialSelectionLength) {
|
||||
this.moveSelectionToEnd();
|
||||
}
|
||||
}}
|
||||
selected={results.length === selectedIndex}
|
||||
/>
|
||||
<LinkSearchResult
|
||||
key="create-nested"
|
||||
containerRef={this.resultsRef}
|
||||
title={suggestedLinkTitle}
|
||||
subtitle={dictionary.createNewChildDoc}
|
||||
icon={<PlusIcon />}
|
||||
onPointerMove={() =>
|
||||
this.handleFocusLink(results.length + 1)
|
||||
}
|
||||
}}
|
||||
selected={results.length === selectedIndex}
|
||||
/>
|
||||
onClick={async () => {
|
||||
await this.handleCreateLink(suggestedLinkTitle, true);
|
||||
|
||||
if (this.initialSelectionLength) {
|
||||
this.moveSelectionToEnd();
|
||||
}
|
||||
}}
|
||||
selected={results.length + 1 === selectedIndex}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
@ -52,7 +52,7 @@ export default function LinkToolbar({
|
||||
});
|
||||
|
||||
const handleOnCreateLink = React.useCallback(
|
||||
async (title: string) => {
|
||||
async (title: string, nested?: boolean) => {
|
||||
onClose();
|
||||
view.focus();
|
||||
|
||||
@ -81,6 +81,7 @@ export default function LinkToolbar({
|
||||
);
|
||||
|
||||
return createAndInsertLink(view, title, href, {
|
||||
nested,
|
||||
onCreateLink,
|
||||
dictionary,
|
||||
});
|
||||
|
@ -148,7 +148,10 @@ export default function SelectionToolbar(props: Props) {
|
||||
};
|
||||
}, [isActive, previousIsActive, readOnly, view]);
|
||||
|
||||
const handleOnCreateLink = async (title: string): Promise<void> => {
|
||||
const handleOnCreateLink = async (
|
||||
title: string,
|
||||
nested?: boolean
|
||||
): Promise<void> => {
|
||||
const { onCreateLink } = props;
|
||||
|
||||
if (!onCreateLink) {
|
||||
@ -173,6 +176,7 @@ export default function SelectionToolbar(props: Props) {
|
||||
);
|
||||
|
||||
return createAndInsertLink(view, title, href, {
|
||||
nested,
|
||||
onCreateLink,
|
||||
dictionary,
|
||||
});
|
||||
|
@ -24,6 +24,7 @@ export default function useDictionary() {
|
||||
createLink: t("Create link"),
|
||||
createLinkError: t("Sorry, an error occurred creating the link"),
|
||||
createNewDoc: t("Create a new doc"),
|
||||
createNewChildDoc: t("Create a new child doc"),
|
||||
deleteColumn: t("Delete column"),
|
||||
deleteRow: t("Delete row"),
|
||||
deleteTable: t("Delete table"),
|
||||
|
@ -43,7 +43,7 @@ type Children = (options: {
|
||||
revision: Revision | undefined;
|
||||
abilities: Record<string, boolean>;
|
||||
readOnly: boolean;
|
||||
onCreateLink: (title: string) => Promise<string>;
|
||||
onCreateLink: (title: string, nested?: boolean) => Promise<string>;
|
||||
sharedTree: NavigationNode | undefined;
|
||||
}) => React.ReactNode;
|
||||
|
||||
@ -152,14 +152,14 @@ function DataLoader({ match, children }: Props) {
|
||||
}, [document?.id, document?.isDeleted, revisionId, views]);
|
||||
|
||||
const onCreateLink = React.useCallback(
|
||||
async (title: string) => {
|
||||
async (title: string, nested?: boolean) => {
|
||||
if (!document) {
|
||||
throw new Error("Document not loaded yet");
|
||||
}
|
||||
|
||||
const newDocument = await documents.create({
|
||||
collectionId: document.collectionId,
|
||||
parentDocumentId: document.parentDocumentId,
|
||||
parentDocumentId: nested ? document.id : document.parentDocumentId,
|
||||
title,
|
||||
text: "",
|
||||
});
|
||||
|
@ -78,7 +78,7 @@ type Props = WithTranslation &
|
||||
revision?: Revision;
|
||||
readOnly: boolean;
|
||||
shareId?: string;
|
||||
onCreateLink?: (title: string) => Promise<string>;
|
||||
onCreateLink?: (title: string, nested?: boolean) => Promise<string>;
|
||||
onSearchLink?: (term: string) => any;
|
||||
};
|
||||
|
||||
|
@ -38,14 +38,15 @@ const createAndInsertLink = async function (
|
||||
href: string,
|
||||
options: {
|
||||
dictionary: any;
|
||||
onCreateLink: (title: string) => Promise<string>;
|
||||
nested?: boolean;
|
||||
onCreateLink: (title: string, nested?: boolean) => Promise<string>;
|
||||
}
|
||||
) {
|
||||
const { dispatch, state } = view;
|
||||
const { onCreateLink } = options;
|
||||
|
||||
try {
|
||||
const url = await onCreateLink(title);
|
||||
const url = await onCreateLink(title, options.nested);
|
||||
const result = findPlaceholderLink(view.state.doc, href);
|
||||
|
||||
if (!result) {
|
||||
|
@ -302,6 +302,7 @@
|
||||
"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",
|
||||
"Create a new child doc": "Create a new child doc",
|
||||
"Delete column": "Delete column",
|
||||
"Delete row": "Delete row",
|
||||
"Delete table": "Delete table",
|
||||
|
Reference in New Issue
Block a user