Skip to content

Commit 6c9c036

Browse files
committed
✨(frontend) interlinking custom inline content
We want to be able to interlink documents in the editor. We created a custom inline content that allows users to interlink documents.
1 parent 5942fd1 commit 6c9c036

File tree

14 files changed

+364
-10
lines changed

14 files changed

+364
-10
lines changed

src/frontend/apps/impress/src/components/Box.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,12 @@ export interface BoxProps {
4444
export type BoxType = ComponentPropsWithRef<typeof Box>;
4545

4646
export const Box = styled('div')<BoxProps>`
47-
display: flex;
48-
flex-direction: column;
4947
${({ $align }) => $align && `align-items: ${$align};`}
5048
${({ $background }) => $background && `background: ${$background};`}
5149
${({ $color }) => $color && `color: ${$color};`}
52-
${({ $direction }) => $direction && `flex-direction: ${$direction};`}
53-
${({ $display }) => $display && `display: ${$display};`}
50+
${({ $direction }) => `flex-direction: ${$direction || 'column'};`}
51+
${({ $display, as }) =>
52+
`display: ${$display || as?.match('span|input') ? 'inline-flex' : 'flex'};`}
5453
${({ $flex }) => $flex && `flex: ${$flex};`}
5554
${({ $gap }) => $gap && `gap: ${$gap};`}
5655
${({ $height }) => $height && `height: ${$height};`}

src/frontend/apps/impress/src/components/DropdownMenu.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export type DropdownMenuProps = {
2323
arrowCss?: BoxProps['$css'];
2424
buttonCss?: BoxProps['$css'];
2525
disabled?: boolean;
26+
opened?: boolean;
2627
topMessage?: string;
2728
selectedValues?: string[];
2829
afterOpenChange?: (isOpen: boolean) => void;
@@ -36,12 +37,13 @@ export const DropdownMenu = ({
3637
arrowCss,
3738
buttonCss,
3839
label,
40+
opened,
3941
topMessage,
4042
afterOpenChange,
4143
selectedValues,
4244
}: PropsWithChildren<DropdownMenuProps>) => {
4345
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
44-
const [isOpen, setIsOpen] = useState(false);
46+
const [isOpen, setIsOpen] = useState(opened ?? false);
4547
const blockButtonRef = useRef<HTMLDivElement>(null);
4648

4749
const onOpenChange = (isOpen: boolean) => {

src/frontend/apps/impress/src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export * from './BoxButton';
33
export * from './Card';
44
export * from './DropButton';
55
export * from './DropdownMenu';
6+
export * from './quick-search';
67
export * from './Icon';
78
export * from './InfiniteScroll';
89
export * from './Link';
Lines changed: 14 additions & 0 deletions
Loading
Lines changed: 6 additions & 0 deletions
Loading

src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
BlockNoteSchema,
33
Dictionary,
44
defaultBlockSpecs,
5+
defaultInlineContentSpecs,
56
locales,
67
withPageBreak,
78
} from '@blocknote/core';
@@ -28,6 +29,10 @@ import { randomColor } from '../utils';
2829
import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu';
2930
import { BlockNoteToolbar } from './BlockNoteToolBar/BlockNoteToolbar';
3031
import { DividerBlock, QuoteBlock } from './custom-blocks';
32+
import {
33+
InterlinkingLinkInlineContent,
34+
InterlinkingSearchInlineContent,
35+
} from './custom-inline-content';
3136

3237
export const blockNoteSchema = withPageBreak(
3338
BlockNoteSchema.create({
@@ -36,6 +41,11 @@ export const blockNoteSchema = withPageBreak(
3641
divider: DividerBlock,
3742
quote: QuoteBlock,
3843
},
44+
inlineContentSpecs: {
45+
...defaultInlineContentSpecs,
46+
interlinkingSearchInline: InterlinkingSearchInlineContent,
47+
interlinkingLinkInline: InterlinkingLinkInlineContent,
48+
},
3949
}),
4050
);
4151

src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export const BlockNoteSuggestionMenu = () => {
3737
const index = defaultMenu.findIndex((item) => item.title === 'Code Block');
3838
const newSlashMenuItems = [
3939
...defaultMenu.slice(0, index + 1),
40-
...getInterlinkingMenuItems(t),
40+
...getInterlinkingMenuItems(editor, t),
4141
...defaultMenu.slice(index + 1),
4242
];
4343

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/* eslint-disable react-hooks/rules-of-hooks */
2+
import { createReactInlineContentSpec } from '@blocknote/react';
3+
import { css } from 'styled-components';
4+
5+
import { StyledLink, Text } from '@/components';
6+
import SelectedPageIcon from '@/docs/doc-editor/assets/doc-selected.svg';
7+
8+
export const InterlinkingLinkInlineContent = createReactInlineContentSpec(
9+
{
10+
type: 'interlinkingLinkInline',
11+
propSchema: {
12+
idPage: {
13+
default: '',
14+
},
15+
titlePage: {
16+
default: '',
17+
},
18+
},
19+
content: 'none',
20+
},
21+
{
22+
render: (props) => {
23+
return <LinkSelected {...props.inlineContent.props} />;
24+
},
25+
},
26+
);
27+
28+
interface LinkSelectedProps {
29+
idPage: string;
30+
titlePage: string;
31+
}
32+
const LinkSelected = ({ idPage, titlePage }: LinkSelectedProps) => {
33+
return (
34+
<StyledLink
35+
href={`/docs/${idPage}`}
36+
$css={css`
37+
display: inline;
38+
& svg {
39+
position: relative;
40+
top: 2px;
41+
margin-right: 0.2rem;
42+
}
43+
`}
44+
>
45+
<SelectedPageIcon width={11.5} />
46+
<Text $weight="500" spellCheck="false" $size="16px" $display="inline">
47+
{titlePage}
48+
</Text>
49+
</StyledLink>
50+
);
51+
};

src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-inline-content/Interlinking/InterlinkingSearchInlineContent.tsx

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,62 @@
1+
/* eslint-disable react-hooks/rules-of-hooks */
2+
import { createReactInlineContentSpec } from '@blocknote/react';
13
import { useTreeContext } from '@gouvfr-lasuite/ui-kit';
24
import { TFunction } from 'i18next';
35
import { useRouter } from 'next/navigation';
46
import { useCallback } from 'react';
57

8+
import { DocsBlockNoteEditor } from '@/docs/doc-editor';
9+
import LinkPageIcon from '@/docs/doc-editor/assets/doc-link.svg';
610
import AddPageIcon from '@/docs/doc-editor/assets/doc-plus.svg';
711
import { Doc, useCreateChildDoc, useDocStore } from '@/docs/doc-management';
812

13+
import { SearchPage } from './SearchPage';
14+
15+
export const InterlinkingSearchInlineContent = createReactInlineContentSpec(
16+
{
17+
type: 'interlinkingSearchInline',
18+
propSchema: {
19+
disabled: {
20+
default: false,
21+
values: [true, false],
22+
},
23+
},
24+
content: 'styled',
25+
},
26+
{
27+
render: (props) => {
28+
if (props.inlineContent.props.disabled) {
29+
return null;
30+
}
31+
32+
return <SearchPage {...props} />;
33+
},
34+
},
35+
);
36+
937
export const getInterlinkingMenuItems = (
38+
editor: DocsBlockNoteEditor,
1039
t: TFunction<'translation', undefined>,
1140
group: string,
1241
createPage: () => void,
1342
) => [
43+
{
44+
title: t('Link to a page'),
45+
onItemClick: () => {
46+
editor.insertInlineContent([
47+
{
48+
type: 'interlinkingSearchInline',
49+
props: {
50+
disabled: false,
51+
},
52+
},
53+
]);
54+
},
55+
aliases: ['interlinking', 'link', 'anchor', 'a'],
56+
group,
57+
icon: <LinkPageIcon />,
58+
subtext: t('Link to a page'),
59+
},
1460
{
1561
title: t('New page'),
1662
onItemClick: createPage,
@@ -42,8 +88,9 @@ export const useGetInterlinkingMenuItems = () => {
4288
});
4389

4490
return useCallback(
45-
(t: TFunction<'translation', undefined>) =>
91+
(editor: DocsBlockNoteEditor, t: TFunction<'translation', undefined>) =>
4692
getInterlinkingMenuItems(
93+
editor,
4794
t,
4895
t('Links'),
4996
() =>

0 commit comments

Comments
 (0)