@jpmorganchase/mosaic-content-editor-plugin 0.1.0-beta.72 → 0.1.0-beta.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -3
- package/.turbo/turbo-build.log +0 -4
- package/CHANGELOG.md +0 -687
- package/src/components/ActionMenu/ActionMenu.css.ts +0 -9
- package/src/components/ActionMenu/ActionMenu.tsx +0 -26
- package/src/components/BaseToolbar/BaseToolbar.css.ts +0 -21
- package/src/components/BaseToolbar/BaseToolbar.tsx +0 -13
- package/src/components/BaseTooltray/BaseTooltray.css.ts +0 -31
- package/src/components/BaseTooltray/BaseTooltray.tsx +0 -22
- package/src/components/ContentEditor.tsx +0 -14
- package/src/components/Dialog.css.ts +0 -7
- package/src/components/Dialog.tsx +0 -12
- package/src/components/Editor.css.ts +0 -64
- package/src/components/Editor.tsx +0 -145
- package/src/components/EditorControls.css.ts +0 -6
- package/src/components/EditorControls.tsx +0 -37
- package/src/components/LinkEditor/EditLinkButton.tsx +0 -9
- package/src/components/LinkEditor/LinkEditor.css.ts +0 -15
- package/src/components/LinkEditor/LinkEditor.tsx +0 -136
- package/src/components/LinkEditor/SaveAdornment.tsx +0 -17
- package/src/components/PersistEditDialog/PersistStatus.css.ts +0 -21
- package/src/components/PersistEditDialog/PersistStatus.tsx +0 -57
- package/src/components/PersistEditDialog/__tests__/index.test.tsx +0 -90
- package/src/components/PersistEditDialog/index.css.ts +0 -14
- package/src/components/PersistEditDialog/index.tsx +0 -152
- package/src/components/Popper/Popper.css.ts +0 -12
- package/src/components/Popper/Popper.tsx +0 -36
- package/src/components/SaveButton.tsx +0 -23
- package/src/components/ScrollableSection/ScrollableSection.css.ts +0 -10
- package/src/components/ScrollableSection/ScrollableSection.tsx +0 -20
- package/src/components/StatusBanner.tsx +0 -16
- package/src/components/Toolbar/InsertBlockDropdown.tsx +0 -134
- package/src/components/Toolbar/InsertHorizontalRule.tsx +0 -18
- package/src/components/Toolbar/InsertImage.css.ts +0 -7
- package/src/components/Toolbar/InsertImage.tsx +0 -138
- package/src/components/Toolbar/InsertLink.css.ts +0 -7
- package/src/components/Toolbar/InsertLink.tsx +0 -148
- package/src/components/Toolbar/InsertTable.css.ts +0 -52
- package/src/components/Toolbar/InsertTable.tsx +0 -196
- package/src/components/Toolbar/TextFormatTooltray.css.ts +0 -22
- package/src/components/Toolbar/TextFormatTooltray.tsx +0 -113
- package/src/components/Toolbar/Toolbar.tsx +0 -52
- package/src/components/Toolbar/ToolbarButton.css.ts +0 -11
- package/src/components/Toolbar/ToolbarButton.tsx +0 -41
- package/src/components/Toolbar/ToolbarSeparator.css.ts +0 -28
- package/src/components/Toolbar/ToolbarSepartor.tsx +0 -13
- package/src/components/__tests__/EditorControls.test.tsx +0 -69
- package/src/components/__tests__/StatusBanner.test.tsx +0 -33
- package/src/hooks/useEditHistory.ts +0 -49
- package/src/hooks/useWorkflowFeed.ts +0 -69
- package/src/index.ts +0 -5
- package/src/nodes/index.ts +0 -23
- package/src/plugins/FloatingToolbarPlugin.css.ts +0 -14
- package/src/plugins/FloatingToolbarPlugin.tsx +0 -101
- package/src/plugins/HorizontalRulePlugin.tsx +0 -39
- package/src/plugins/MarkdownImagePlugin.tsx +0 -58
- package/src/plugins/MarkdownLinkPlugin.tsx +0 -69
- package/src/plugins/PreviewPlugin.tsx +0 -71
- package/src/plugins/TableActionMenuPlugin.css.ts +0 -29
- package/src/plugins/TableActionMenuPlugin.tsx +0 -274
- package/src/store/__tests__/index.test.ts +0 -171
- package/src/store/index.ts +0 -84
- package/src/theme/index.css.ts +0 -39
- package/src/theme/index.ts +0 -22
- package/src/transformers/horizontalRule.ts +0 -25
- package/src/transformers/index.ts +0 -6
- package/src/transformers/link.ts +0 -3
- package/src/transformers/tableRule.ts +0 -193
- package/src/utils/getSelectedNode.ts +0 -16
- package/tsconfig.json +0 -8
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { MenuButton, MenuDescriptor } from '@salt-ds/lab';
|
|
3
|
-
import { Icon } from '@jpmorganchase/mosaic-components';
|
|
4
|
-
|
|
5
|
-
import type { CascadingMenuProps } from '@salt-ds/lab';
|
|
6
|
-
|
|
7
|
-
export type ActionMenuSource = MenuDescriptor;
|
|
8
|
-
|
|
9
|
-
interface ActionMenuProps {
|
|
10
|
-
initialSource: CascadingMenuProps['initialSource'];
|
|
11
|
-
onItemClick: CascadingMenuProps['onItemClick'];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function ActionMenu({ initialSource, onItemClick }: ActionMenuProps) {
|
|
15
|
-
return (
|
|
16
|
-
<MenuButton
|
|
17
|
-
hideCaret
|
|
18
|
-
CascadingMenuProps={{
|
|
19
|
-
initialSource,
|
|
20
|
-
onItemClick
|
|
21
|
-
}}
|
|
22
|
-
>
|
|
23
|
-
<Icon name="menu" />
|
|
24
|
-
</MenuButton>
|
|
25
|
-
);
|
|
26
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { globalStyle, style } from '@vanilla-extract/css';
|
|
2
|
-
import { backgroundColor, vars } from '@jpmorganchase/mosaic-theme';
|
|
3
|
-
|
|
4
|
-
const root = style([
|
|
5
|
-
backgroundColor({ variant: 'emphasis' }),
|
|
6
|
-
style({
|
|
7
|
-
overflow: 'hidden',
|
|
8
|
-
position: 'relative',
|
|
9
|
-
display: 'flex',
|
|
10
|
-
alignItems: 'center',
|
|
11
|
-
padding: vars.space.horizontal.x1,
|
|
12
|
-
height: '44px'
|
|
13
|
-
})
|
|
14
|
-
]);
|
|
15
|
-
export default {
|
|
16
|
-
root
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
globalStyle(`${root} > *:not(:first-child)`, {
|
|
20
|
-
marginLeft: vars.space.horizontal.x2
|
|
21
|
-
});
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import React, { type ReactNode } from 'react';
|
|
2
|
-
import classnames from 'clsx';
|
|
3
|
-
|
|
4
|
-
import styles from './BaseToolbar.css';
|
|
5
|
-
|
|
6
|
-
interface BaseToolbarProps {
|
|
7
|
-
className?: string;
|
|
8
|
-
children?: ReactNode;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const BaseToolbar = ({ className, ...rest }: BaseToolbarProps) => (
|
|
12
|
-
<div className={classnames(className, styles.root)} {...rest} />
|
|
13
|
-
);
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { globalStyle, style } from '@vanilla-extract/css';
|
|
2
|
-
import { vars } from '@jpmorganchase/mosaic-theme';
|
|
3
|
-
|
|
4
|
-
const root = style({
|
|
5
|
-
background: 'inherit',
|
|
6
|
-
display: 'flex',
|
|
7
|
-
justifyContent: 'center',
|
|
8
|
-
flexShrink: 0,
|
|
9
|
-
flexGrow: 0,
|
|
10
|
-
position: 'relative',
|
|
11
|
-
overflow: 'hidden',
|
|
12
|
-
padding: vars.space.horizontal.x1
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
export default {
|
|
16
|
-
alignLeft: style({
|
|
17
|
-
marginRight: 'auto'
|
|
18
|
-
}),
|
|
19
|
-
alignRight: style({
|
|
20
|
-
selectors: {
|
|
21
|
-
[`${root}&`]: {
|
|
22
|
-
marginLeft: 'auto!important'
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}),
|
|
26
|
-
root
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
globalStyle(`${root} > *:not(:first-child)`, {
|
|
30
|
-
marginLeft: vars.space.horizontal.x1
|
|
31
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import React, { type ReactNode } from 'react';
|
|
2
|
-
import classnames from 'clsx';
|
|
3
|
-
|
|
4
|
-
import styles from './BaseTooltray.css';
|
|
5
|
-
|
|
6
|
-
type Alignment = 'left' | 'right';
|
|
7
|
-
|
|
8
|
-
interface BaseTooltrayProps {
|
|
9
|
-
className?: string;
|
|
10
|
-
children?: ReactNode;
|
|
11
|
-
align?: Alignment;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const BaseTooltray = ({ className, align, ...rest }: BaseTooltrayProps) => (
|
|
15
|
-
<div
|
|
16
|
-
className={classnames(className, styles.root, {
|
|
17
|
-
[styles.alignLeft]: align === 'left',
|
|
18
|
-
[styles.alignRight]: align === 'right'
|
|
19
|
-
})}
|
|
20
|
-
{...rest}
|
|
21
|
-
/>
|
|
22
|
-
);
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
|
|
3
|
-
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
|
|
4
|
-
import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary';
|
|
5
|
-
|
|
6
|
-
const ContentEditor = () => (
|
|
7
|
-
<RichTextPlugin
|
|
8
|
-
contentEditable={<ContentEditable />}
|
|
9
|
-
placeholder={<div>Enter some text...</div>}
|
|
10
|
-
ErrorBoundary={LexicalErrorBoundary}
|
|
11
|
-
/>
|
|
12
|
-
);
|
|
13
|
-
|
|
14
|
-
export default ContentEditor;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Dialog as SaltDialog, type DialogProps as SaltDialogProps } from '@salt-ds/core';
|
|
3
|
-
import { themeClassName } from '@jpmorganchase/mosaic-theme';
|
|
4
|
-
import classnames from 'clsx';
|
|
5
|
-
|
|
6
|
-
import style from './Dialog.css';
|
|
7
|
-
|
|
8
|
-
interface DialogProps extends SaltDialogProps {}
|
|
9
|
-
|
|
10
|
-
export const Dialog = ({ className, ...props }: DialogProps) => (
|
|
11
|
-
<SaltDialog className={classnames(themeClassName, style.root, className)} {...props} />
|
|
12
|
-
);
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { globalStyle, style } from '@vanilla-extract/css';
|
|
2
|
-
import {
|
|
3
|
-
vars,
|
|
4
|
-
backgroundColor,
|
|
5
|
-
neutralBorder,
|
|
6
|
-
navigableBorder,
|
|
7
|
-
config
|
|
8
|
-
} from '@jpmorganchase/mosaic-theme';
|
|
9
|
-
|
|
10
|
-
globalStyle('div[contenteditable]', {
|
|
11
|
-
outline: 'none'
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
export default {
|
|
15
|
-
root: style({
|
|
16
|
-
display: 'grid',
|
|
17
|
-
gridTemplateColumns: '1fr',
|
|
18
|
-
gridTemplateRows: 'auto 1fr',
|
|
19
|
-
gridTemplateAreas: '"toolbar" "editor" '
|
|
20
|
-
}),
|
|
21
|
-
toolbarContainer: style([
|
|
22
|
-
style({
|
|
23
|
-
gridArea: 'toolbar',
|
|
24
|
-
position: 'sticky',
|
|
25
|
-
top: `${config.appHeader.height}px`,
|
|
26
|
-
zIndex: 2
|
|
27
|
-
}),
|
|
28
|
-
neutralBorder({
|
|
29
|
-
variant: 'low',
|
|
30
|
-
borderWidth: 'thin'
|
|
31
|
-
})
|
|
32
|
-
]),
|
|
33
|
-
editorRoot: style({
|
|
34
|
-
display: 'flex',
|
|
35
|
-
marginTop: vars.space.vertical.x3,
|
|
36
|
-
flexWrap: 'wrap',
|
|
37
|
-
gridArea: 'editor'
|
|
38
|
-
}),
|
|
39
|
-
splitter: style({
|
|
40
|
-
display: 'flex',
|
|
41
|
-
flexDirection: 'row'
|
|
42
|
-
}),
|
|
43
|
-
gutter: style([
|
|
44
|
-
{
|
|
45
|
-
backgroundRepeat: 'no-repeat',
|
|
46
|
-
backgroundPosition: '50%',
|
|
47
|
-
backgroundImage:
|
|
48
|
-
'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==")',
|
|
49
|
-
cursor: 'col-resize'
|
|
50
|
-
},
|
|
51
|
-
backgroundColor({ variant: 'emphasis' })
|
|
52
|
-
]),
|
|
53
|
-
unfocused: neutralBorder({
|
|
54
|
-
variant: 'low',
|
|
55
|
-
borderTopWidth: 'thin',
|
|
56
|
-
borderLeftWidth: 'thin',
|
|
57
|
-
borderRightWidth: 'thin',
|
|
58
|
-
borderBottomWidth: 'none'
|
|
59
|
-
}),
|
|
60
|
-
focused: navigableBorder({
|
|
61
|
-
variant: 'focusRing',
|
|
62
|
-
borderWidth: 'medium'
|
|
63
|
-
})
|
|
64
|
-
};
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import React, { ComponentType, FC, useRef, useState } from 'react';
|
|
2
|
-
import classnames from 'clsx';
|
|
3
|
-
import matter from 'gray-matter';
|
|
4
|
-
import { LexicalComposer } from '@lexical/react/LexicalComposer';
|
|
5
|
-
import { ListPlugin } from '@lexical/react/LexicalListPlugin';
|
|
6
|
-
import { TablePlugin } from '@lexical/react/LexicalTablePlugin';
|
|
7
|
-
import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
|
|
8
|
-
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
|
|
9
|
-
import { MarkdownShortcutPlugin } from '@lexical/react/LexicalMarkdownShortcutPlugin';
|
|
10
|
-
import Split from 'react-split';
|
|
11
|
-
import { $convertFromMarkdownString } from '@lexical/markdown';
|
|
12
|
-
|
|
13
|
-
import transformers from '../transformers';
|
|
14
|
-
import ContentEditor from './ContentEditor';
|
|
15
|
-
import { nodes } from '../nodes';
|
|
16
|
-
import { useEditorUser, usePreviewContent } from '../store';
|
|
17
|
-
import { PreviewPlugin } from '../plugins/PreviewPlugin';
|
|
18
|
-
import styles from './Editor.css';
|
|
19
|
-
import Toolbar from './Toolbar/Toolbar';
|
|
20
|
-
import theme from '../theme';
|
|
21
|
-
import { PersistDialog } from './PersistEditDialog';
|
|
22
|
-
import StatusBanner from './StatusBanner';
|
|
23
|
-
import { MarkdownImagePlugin } from '../plugins/MarkdownImagePlugin';
|
|
24
|
-
import { MarkdownLinkPlugin } from '../plugins/MarkdownLinkPlugin';
|
|
25
|
-
import { LinkEditor } from './LinkEditor/LinkEditor';
|
|
26
|
-
import { ScrollableSection } from './ScrollableSection/ScrollableSection';
|
|
27
|
-
import HorizontalRulePlugin from '../plugins/HorizontalRulePlugin';
|
|
28
|
-
import { FloatingToolbarPlugin } from '../plugins/FloatingToolbarPlugin';
|
|
29
|
-
import { TableActionMenuPlugin } from '../plugins/TableActionMenuPlugin';
|
|
30
|
-
|
|
31
|
-
function onError(error: Error) {
|
|
32
|
-
console.error(error);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const initialConfig = {
|
|
36
|
-
namespace: 'mosaic-content-editor',
|
|
37
|
-
onError,
|
|
38
|
-
nodes,
|
|
39
|
-
theme
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
interface PreviewComponentProps {
|
|
43
|
-
source: any;
|
|
44
|
-
meta?: any;
|
|
45
|
-
components: any;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
interface EditorProps extends PreviewComponentProps {
|
|
49
|
-
content: string;
|
|
50
|
-
PreviewComponent?: ComponentType<PreviewComponentProps>;
|
|
51
|
-
previewUrl?: string;
|
|
52
|
-
persistUrl?: string;
|
|
53
|
-
user?: any;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const gutter = () => {
|
|
57
|
-
const gutter = document.createElement('div');
|
|
58
|
-
gutter.className = styles.gutter;
|
|
59
|
-
return gutter;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const Editor: FC<EditorProps> = ({
|
|
63
|
-
components,
|
|
64
|
-
content,
|
|
65
|
-
persistUrl,
|
|
66
|
-
PreviewComponent,
|
|
67
|
-
previewUrl,
|
|
68
|
-
source,
|
|
69
|
-
user
|
|
70
|
-
}) => {
|
|
71
|
-
const previewContent = usePreviewContent() || source;
|
|
72
|
-
const { setUser } = useEditorUser();
|
|
73
|
-
const [focused, setFocused] = useState(false);
|
|
74
|
-
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
75
|
-
const { data: meta, content: markdown } = matter(content);
|
|
76
|
-
|
|
77
|
-
const handleEditorFocus = () => {
|
|
78
|
-
setFocused(true);
|
|
79
|
-
setUser(user);
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const handleEditorBlur = () => {
|
|
83
|
-
setFocused(false);
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
return (
|
|
87
|
-
<LexicalComposer
|
|
88
|
-
initialConfig={{
|
|
89
|
-
...initialConfig,
|
|
90
|
-
editorState: () => $convertFromMarkdownString(markdown, transformers)
|
|
91
|
-
}}
|
|
92
|
-
>
|
|
93
|
-
<div className={styles.root} onFocus={handleEditorFocus} onBlur={handleEditorBlur}>
|
|
94
|
-
<div className={styles.toolbarContainer}>
|
|
95
|
-
<Toolbar />
|
|
96
|
-
<PersistDialog meta={meta} persistUrl={persistUrl} />
|
|
97
|
-
<StatusBanner />
|
|
98
|
-
</div>
|
|
99
|
-
<div className={styles.editorRoot}>
|
|
100
|
-
<Split
|
|
101
|
-
className={styles.splitter}
|
|
102
|
-
cursor="col-resize"
|
|
103
|
-
direction="horizontal"
|
|
104
|
-
dragInterval={1}
|
|
105
|
-
expandToMin={false}
|
|
106
|
-
gutter={gutter}
|
|
107
|
-
gutterAlign="center"
|
|
108
|
-
minSize={100}
|
|
109
|
-
sizes={[50, 50]}
|
|
110
|
-
snapOffset={30}
|
|
111
|
-
>
|
|
112
|
-
<ScrollableSection
|
|
113
|
-
className={classnames({
|
|
114
|
-
[styles.focused]: focused,
|
|
115
|
-
[styles.unfocused]: !focused
|
|
116
|
-
})}
|
|
117
|
-
ref={containerRef}
|
|
118
|
-
>
|
|
119
|
-
<ContentEditor />
|
|
120
|
-
</ScrollableSection>
|
|
121
|
-
<ScrollableSection>
|
|
122
|
-
{PreviewComponent && (
|
|
123
|
-
<PreviewComponent source={previewContent} meta={meta} components={components} />
|
|
124
|
-
)}
|
|
125
|
-
</ScrollableSection>
|
|
126
|
-
</Split>
|
|
127
|
-
<HistoryPlugin />
|
|
128
|
-
<ListPlugin />
|
|
129
|
-
<TablePlugin />
|
|
130
|
-
<LinkPlugin />
|
|
131
|
-
<MarkdownShortcutPlugin transformers={transformers} />
|
|
132
|
-
{previewUrl ? <PreviewPlugin previewUrl={previewUrl} /> : null}
|
|
133
|
-
<MarkdownImagePlugin />
|
|
134
|
-
<MarkdownLinkPlugin />
|
|
135
|
-
<LinkEditor />
|
|
136
|
-
<HorizontalRulePlugin />
|
|
137
|
-
<FloatingToolbarPlugin />
|
|
138
|
-
<TableActionMenuPlugin />
|
|
139
|
-
</div>
|
|
140
|
-
</div>
|
|
141
|
-
</LexicalComposer>
|
|
142
|
-
);
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
export default Editor;
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Icon } from '@jpmorganchase/mosaic-components';
|
|
3
|
-
import { BaseToolbar as Toolbar } from './BaseToolbar/BaseToolbar';
|
|
4
|
-
import { BaseTooltray as Tooltray } from './BaseTooltray/BaseTooltray';
|
|
5
|
-
import { ToolbarButton } from './Toolbar/ToolbarButton';
|
|
6
|
-
|
|
7
|
-
import { default as useContentEditor } from '../store';
|
|
8
|
-
import styles from './EditorControls.css';
|
|
9
|
-
|
|
10
|
-
export interface EditorControlsProps {
|
|
11
|
-
enabled?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const EditorControls = ({ enabled = false }: EditorControlsProps) => {
|
|
15
|
-
const { pageState, startEditing, stopEditing } = useContentEditor();
|
|
16
|
-
const isEditing = pageState === 'EDIT';
|
|
17
|
-
|
|
18
|
-
const handleClick = () => (!isEditing ? startEditing() : stopEditing());
|
|
19
|
-
|
|
20
|
-
const enabledLabel = isEditing ? 'Cancel Editing' : 'Edit Page';
|
|
21
|
-
const overflowLabel = !enabled ? 'Login Required to Edit' : enabledLabel;
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<Toolbar aria-label="editor-controls" className={styles.root}>
|
|
25
|
-
<Tooltray aria-label="page editor controls tooltray">
|
|
26
|
-
<ToolbarButton
|
|
27
|
-
aria-label={isEditing ? 'cancel editing' : 'start editing'}
|
|
28
|
-
onClick={handleClick}
|
|
29
|
-
disabled={!enabled}
|
|
30
|
-
label={overflowLabel}
|
|
31
|
-
>
|
|
32
|
-
<Icon name={isEditing ? 'delete' : 'edit'} />
|
|
33
|
-
</ToolbarButton>
|
|
34
|
-
</Tooltray>
|
|
35
|
-
</Toolbar>
|
|
36
|
-
);
|
|
37
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Icon } from '@jpmorganchase/mosaic-components';
|
|
3
|
-
import { ToolbarButton, type ToolbarButtonProps } from '../Toolbar/ToolbarButton';
|
|
4
|
-
|
|
5
|
-
export const EditLinkButton = (props: ToolbarButtonProps) => (
|
|
6
|
-
<ToolbarButton {...props} aria-label="Edit link" disableTooltip variant="regular">
|
|
7
|
-
<Icon name="edit" />
|
|
8
|
-
</ToolbarButton>
|
|
9
|
-
);
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { style } from '@vanilla-extract/css';
|
|
2
|
-
import { vars, neutralBorder } from '@jpmorganchase/mosaic-theme';
|
|
3
|
-
|
|
4
|
-
export default {
|
|
5
|
-
popper: style([
|
|
6
|
-
style({
|
|
7
|
-
padding: vars.space.vertical.x1,
|
|
8
|
-
marginLeft: vars.space.horizontal.x2
|
|
9
|
-
}),
|
|
10
|
-
neutralBorder({
|
|
11
|
-
variant: 'low',
|
|
12
|
-
borderWidth: 'thin'
|
|
13
|
-
})
|
|
14
|
-
])
|
|
15
|
-
};
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { mergeRegister } from '@lexical/utils';
|
|
2
|
-
import {
|
|
3
|
-
$getSelection,
|
|
4
|
-
$isRangeSelection,
|
|
5
|
-
COMMAND_PRIORITY_LOW,
|
|
6
|
-
RangeSelection,
|
|
7
|
-
SELECTION_CHANGE_COMMAND
|
|
8
|
-
} from 'lexical';
|
|
9
|
-
import React, { ChangeEvent, useCallback, useEffect, useRef, useState } from 'react';
|
|
10
|
-
import { $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link';
|
|
11
|
-
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
12
|
-
import { useFloatingUI, Input } from '@salt-ds/core';
|
|
13
|
-
import { useDismiss, useInteractions } from '@floating-ui/react';
|
|
14
|
-
|
|
15
|
-
import styles from './LinkEditor.css';
|
|
16
|
-
import { SaveAdornment } from './SaveAdornment';
|
|
17
|
-
import { EditLinkButton } from './EditLinkButton';
|
|
18
|
-
import { Popper } from '../Popper/Popper';
|
|
19
|
-
import { getSelectedNode } from '../../utils/getSelectedNode';
|
|
20
|
-
|
|
21
|
-
export function LinkEditor() {
|
|
22
|
-
const [editor] = useLexicalComposerContext();
|
|
23
|
-
const inputRef = useRef<HTMLInputElement>(null);
|
|
24
|
-
const [linkUrl, setLinkUrl] = useState('');
|
|
25
|
-
const [isEdit, setIsEdit] = useState(false);
|
|
26
|
-
const [lastSelection, setLastSelection] = useState<RangeSelection | null>(null);
|
|
27
|
-
|
|
28
|
-
const reset = () => {
|
|
29
|
-
setLinkUrl('');
|
|
30
|
-
setIsEdit(false);
|
|
31
|
-
setLastSelection(null);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const { context, floating, reference, strategy, x, y } = useFloatingUI({
|
|
35
|
-
placement: 'right',
|
|
36
|
-
strategy: 'absolute',
|
|
37
|
-
open: linkUrl.length > 0,
|
|
38
|
-
onOpenChange: reset
|
|
39
|
-
});
|
|
40
|
-
const { getFloatingProps } = useInteractions([
|
|
41
|
-
useDismiss(context, {
|
|
42
|
-
ancestorScroll: true
|
|
43
|
-
})
|
|
44
|
-
]);
|
|
45
|
-
|
|
46
|
-
const updateLinkEditor = useCallback(() => {
|
|
47
|
-
const selection = $getSelection();
|
|
48
|
-
if ($isRangeSelection(selection)) {
|
|
49
|
-
const node = getSelectedNode(selection);
|
|
50
|
-
const parent = node.getParent();
|
|
51
|
-
if ($isLinkNode(parent)) {
|
|
52
|
-
reference(editor.getElementByKey(parent.getKey()));
|
|
53
|
-
setLinkUrl(parent.getURL());
|
|
54
|
-
setLastSelection(selection);
|
|
55
|
-
} else if ($isLinkNode(node)) {
|
|
56
|
-
reference(editor.getElementByKey(node.getKey()));
|
|
57
|
-
setLinkUrl(node.getURL());
|
|
58
|
-
setLastSelection(selection);
|
|
59
|
-
} else {
|
|
60
|
-
// no links to edit on this selection
|
|
61
|
-
reset();
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
setIsEdit(false);
|
|
65
|
-
return true;
|
|
66
|
-
}, [editor, reference]);
|
|
67
|
-
|
|
68
|
-
useEffect(
|
|
69
|
-
() =>
|
|
70
|
-
mergeRegister(
|
|
71
|
-
editor.registerUpdateListener(({ editorState }) => {
|
|
72
|
-
editorState.read(() => {
|
|
73
|
-
updateLinkEditor();
|
|
74
|
-
});
|
|
75
|
-
}),
|
|
76
|
-
|
|
77
|
-
editor.registerCommand(
|
|
78
|
-
SELECTION_CHANGE_COMMAND,
|
|
79
|
-
() => {
|
|
80
|
-
updateLinkEditor();
|
|
81
|
-
return true;
|
|
82
|
-
},
|
|
83
|
-
COMMAND_PRIORITY_LOW
|
|
84
|
-
)
|
|
85
|
-
),
|
|
86
|
-
[editor, updateLinkEditor]
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
useEffect(() => {
|
|
90
|
-
editor.getEditorState().read(() => {
|
|
91
|
-
updateLinkEditor();
|
|
92
|
-
});
|
|
93
|
-
}, [editor, updateLinkEditor]);
|
|
94
|
-
|
|
95
|
-
const handleSave = () => {
|
|
96
|
-
if (lastSelection !== null) {
|
|
97
|
-
if (linkUrl !== '') {
|
|
98
|
-
editor.dispatchCommand(TOGGLE_LINK_COMMAND, linkUrl);
|
|
99
|
-
setIsEdit(false);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const handleEdit = () => setIsEdit(true);
|
|
105
|
-
|
|
106
|
-
return (
|
|
107
|
-
<Popper
|
|
108
|
-
className={styles.popper}
|
|
109
|
-
ref={floating}
|
|
110
|
-
open={linkUrl.length > 0}
|
|
111
|
-
style={{ top: y ?? '', left: x ?? '', position: strategy }}
|
|
112
|
-
{...getFloatingProps({})}
|
|
113
|
-
>
|
|
114
|
-
{!isEdit ? (
|
|
115
|
-
<EditLinkButton onClick={handleEdit} />
|
|
116
|
-
) : (
|
|
117
|
-
<Input
|
|
118
|
-
endAdornment={<SaveAdornment onSave={handleSave} />}
|
|
119
|
-
ref={inputRef}
|
|
120
|
-
value={linkUrl}
|
|
121
|
-
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
|
122
|
-
setLinkUrl(event.target.value);
|
|
123
|
-
}}
|
|
124
|
-
onKeyDown={event => {
|
|
125
|
-
if (event.key === 'Enter') {
|
|
126
|
-
event.preventDefault();
|
|
127
|
-
handleSave();
|
|
128
|
-
} else if (event.key === 'Escape') {
|
|
129
|
-
event.preventDefault();
|
|
130
|
-
}
|
|
131
|
-
}}
|
|
132
|
-
/>
|
|
133
|
-
)}
|
|
134
|
-
</Popper>
|
|
135
|
-
);
|
|
136
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Icon } from '@jpmorganchase/mosaic-components';
|
|
3
|
-
import type { ButtonProps } from '@jpmorganchase/mosaic-components';
|
|
4
|
-
import { StaticInputAdornment } from '@salt-ds/lab';
|
|
5
|
-
import { ToolbarButton } from '../Toolbar/ToolbarButton';
|
|
6
|
-
|
|
7
|
-
interface SaveAdornmentProps {
|
|
8
|
-
onSave: ButtonProps['onClick'];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const SaveAdornment = ({ onSave }: SaveAdornmentProps) => (
|
|
12
|
-
<StaticInputAdornment>
|
|
13
|
-
<ToolbarButton onClick={onSave} aria-label="save link changes" disableTooltip variant="regular">
|
|
14
|
-
<Icon name="save" />
|
|
15
|
-
</ToolbarButton>
|
|
16
|
-
</StaticInputAdornment>
|
|
17
|
-
);
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { style } from '@vanilla-extract/css';
|
|
2
|
-
import { vars } from '@jpmorganchase/mosaic-theme';
|
|
3
|
-
|
|
4
|
-
export default {
|
|
5
|
-
step: style({
|
|
6
|
-
padding: vars.space.vertical.x2,
|
|
7
|
-
opacity: vars.opacity.light.disabled
|
|
8
|
-
}),
|
|
9
|
-
stepActive: style({
|
|
10
|
-
fontSize: vars.fontSize.s80,
|
|
11
|
-
fontStyle: vars.fontWeight.semibold,
|
|
12
|
-
opacity: 1
|
|
13
|
-
}),
|
|
14
|
-
stepComplete: style({
|
|
15
|
-
opacity: 1
|
|
16
|
-
}),
|
|
17
|
-
stepText: style({
|
|
18
|
-
marginLeft: vars.space.horizontal.x2,
|
|
19
|
-
marginRight: vars.space.horizontal.x2
|
|
20
|
-
})
|
|
21
|
-
};
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import React, { FC, ReactNode } from 'react';
|
|
2
|
-
import classnames from 'clsx';
|
|
3
|
-
import { P2, P6 } from '@jpmorganchase/mosaic-components';
|
|
4
|
-
|
|
5
|
-
import styles from './PersistStatus.css';
|
|
6
|
-
import { SourceWorkflowMessageEvent } from '@jpmorganchase/mosaic-types';
|
|
7
|
-
|
|
8
|
-
interface StepProps {
|
|
9
|
-
children?: ReactNode;
|
|
10
|
-
isComplete: boolean;
|
|
11
|
-
isCurrent: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const Step: FC<StepProps> = ({ children, isComplete, isCurrent }) => (
|
|
15
|
-
<div
|
|
16
|
-
className={classnames(styles.step, {
|
|
17
|
-
[styles.stepComplete]: isComplete,
|
|
18
|
-
[styles.stepActive]: isCurrent
|
|
19
|
-
})}
|
|
20
|
-
>
|
|
21
|
-
<span role="img" aria-label={isComplete ? 'emoji with party hat' : 'hourglass'}>
|
|
22
|
-
{isComplete ? '🥳' : '⌛'}
|
|
23
|
-
</span>
|
|
24
|
-
|
|
25
|
-
<span className={styles.stepText}>{children}</span>
|
|
26
|
-
</div>
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
interface StatusProps {
|
|
30
|
-
error: string | null;
|
|
31
|
-
progress: SourceWorkflowMessageEvent[];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export const PersistStatus = ({ error, progress }: StatusProps) => {
|
|
35
|
-
if (error) {
|
|
36
|
-
return (
|
|
37
|
-
<>
|
|
38
|
-
<P2>😢 Sorry something has went wrong. Please try again later.</P2>
|
|
39
|
-
<P6>Error - {error}</P6>
|
|
40
|
-
</>
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<>
|
|
46
|
-
{progress.map((message, index) => (
|
|
47
|
-
<Step
|
|
48
|
-
key={`step -${index}`}
|
|
49
|
-
isComplete={index < progress.length - 1}
|
|
50
|
-
isCurrent={progress.length - 1 === index}
|
|
51
|
-
>
|
|
52
|
-
{message.message}
|
|
53
|
-
</Step>
|
|
54
|
-
))}
|
|
55
|
-
</>
|
|
56
|
-
);
|
|
57
|
-
};
|