payload 1.0.32 → 1.0.35
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/.eslintrc.js +1 -0
- package/CHANGELOG.md +33 -1
- package/components/index.js +2 -2
- package/dist/admin/components/elements/DeleteDocument/index.js +6 -6
- package/dist/admin/components/elements/DuplicateDocument/index.js +4 -4
- package/dist/admin/components/elements/GenerateConfirmation/index.js +7 -5
- package/dist/admin/components/elements/Popup/index.js +14 -13
- package/dist/admin/components/elements/Status/index.js +14 -9
- package/dist/admin/components/forms/Error/index.scss +2 -1
- package/dist/admin/components/forms/Label/index.scss +3 -1
- package/dist/admin/components/forms/field-types/Relationship/index.js +12 -1
- package/dist/admin/components/forms/field-types/Relationship/optionsReducer.js +1 -1
- package/dist/admin/components/forms/field-types/Relationship/types.d.ts +0 -1
- package/dist/admin/components/forms/field-types/RichText/RichText.d.ts +2 -2
- package/dist/admin/components/forms/field-types/RichText/RichText.js +4 -7
- package/dist/admin/components/forms/field-types/RichText/elements/indent/index.js +0 -3
- package/dist/admin/components/forms/field-types/RichText/elements/index.d.ts +6 -3
- package/dist/admin/components/forms/field-types/RichText/elements/link/Button.d.ts +4 -0
- package/dist/admin/components/forms/field-types/RichText/elements/link/Button.js +105 -0
- package/dist/admin/components/forms/field-types/RichText/elements/link/Element.d.ts +9 -0
- package/dist/admin/components/forms/field-types/RichText/elements/link/Element.js +120 -0
- package/dist/admin/components/forms/field-types/RichText/elements/link/Modal/baseFields.d.ts +3 -0
- package/dist/admin/components/forms/field-types/RichText/elements/link/Modal/baseFields.js +56 -0
- package/dist/admin/components/forms/field-types/RichText/elements/link/Modal/index.d.ts +4 -0
- package/dist/admin/components/forms/field-types/RichText/elements/link/Modal/index.js +22 -0
- package/dist/admin/components/forms/field-types/RichText/elements/link/Modal/index.scss +29 -0
- package/dist/admin/components/forms/field-types/RichText/elements/link/Modal/types.d.ts +9 -0
- package/dist/admin/components/forms/field-types/RichText/elements/link/Modal/types.js +1 -0
- package/dist/admin/components/forms/field-types/RichText/elements/link/index.d.ts +6 -5
- package/dist/admin/components/forms/field-types/RichText/elements/link/index.js +4 -67
- package/dist/admin/components/forms/field-types/RichText/elements/link/index.scss +36 -63
- package/dist/admin/components/forms/field-types/RichText/elements/link/shared.d.ts +1 -0
- package/dist/admin/components/forms/field-types/RichText/elements/link/shared.js +1 -0
- package/dist/admin/components/forms/field-types/RichText/elements/link/utilities.d.ts +1 -1
- package/dist/admin/components/forms/field-types/RichText/elements/link/utilities.js +13 -23
- package/dist/admin/components/forms/field-types/RichText/elements/relationship/Button/index.js +6 -10
- package/dist/admin/components/forms/field-types/RichText/elements/toggle.js +0 -3
- package/dist/admin/components/forms/field-types/RichText/elements/upload/Button/index.js +8 -12
- package/dist/admin/components/forms/field-types/RichText/elements/upload/Element/EditModal/index.js +2 -3
- package/dist/admin/components/forms/field-types/RichText/elements/upload/Element/index.js +6 -6
- package/dist/admin/components/forms/field-types/RichText/types.d.ts +0 -4
- package/dist/admin/components/forms/field-types/Text/Input.d.ts +2 -0
- package/dist/admin/components/forms/field-types/Text/Input.js +2 -3
- package/dist/admin/components/forms/field-types/Text/index.js +2 -2
- package/dist/admin/components/forms/field-types/Text/types.d.ts +3 -0
- package/dist/admin/components/forms/field-types/Upload/Add/index.js +4 -4
- package/dist/admin/components/forms/field-types/Upload/Input.js +3 -3
- package/dist/admin/components/forms/field-types/Upload/SelectExisting/index.js +6 -6
- package/dist/admin/components/modals/StayLoggedIn/index.js +4 -3
- package/dist/admin/components/utilities/Auth/index.js +1 -1
- package/dist/admin/components/views/Version/Restore/index.js +3 -3
- package/dist/admin/hooks/useThrottledEffect.js +15 -10
- package/dist/admin/index.js +1 -1
- package/dist/admin/scss/vars.scss +2 -2
- package/dist/auth/types.d.ts +1 -1
- package/dist/fields/config/schema.js +6 -3
- package/dist/fields/config/types.d.ts +3 -0
- package/dist/fields/config/types.js +1 -1
- package/dist/fields/hooks/afterRead/promise.js +3 -3
- package/dist/fields/richText/recurseNestedFields.js +3 -3
- package/dist/fields/richText/{relationshipPromise.d.ts → richTextRelationshipPromise.d.ts} +0 -0
- package/dist/fields/richText/richTextRelationshipPromise.js +104 -0
- package/dist/fields/validations.js +2 -2
- package/dist/graphql/schema/buildObjectType.js +3 -3
- package/dist/graphql/utilities/formatName.js +4 -1
- package/dist/graphql/utilities/formatName.spec.d.ts +1 -0
- package/dist/graphql/utilities/formatName.spec.js +23 -0
- package/dist/mongoose/sanitizeFormattedValue.js +1 -1
- package/dist/uploads/getBaseFields.js +3 -3
- package/dist/utilities/wordBoundariesRegex.js +5 -2
- package/docs/configuration/collections.mdx +1 -1
- package/docs/configuration/overview.mdx +1 -1
- package/docs/fields/rich-text.mdx +7 -0
- package/docs/getting-started/concepts.mdx +1 -1
- package/package.json +2 -2
- package/dist/fields/richText/relationshipPromise.js +0 -71
|
@@ -20,7 +20,7 @@ const sortOptions = (options) => options.sort((a, b) => {
|
|
|
20
20
|
const optionsReducer = (state, action) => {
|
|
21
21
|
switch (action.type) {
|
|
22
22
|
case 'CLEAR': {
|
|
23
|
-
return
|
|
23
|
+
return [];
|
|
24
24
|
}
|
|
25
25
|
case 'ADD': {
|
|
26
26
|
const { hasMultipleRelations, collection, relation, data, sort, ids = [] } = action;
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { BaseEditor } from 'slate';
|
|
3
3
|
import { ReactEditor } from 'slate-react';
|
|
4
4
|
import { HistoryEditor } from 'slate-history';
|
|
5
|
-
import { Props
|
|
5
|
+
import { Props } from './types';
|
|
6
6
|
import './index.scss';
|
|
7
7
|
declare type CustomText = {
|
|
8
8
|
text: string;
|
|
@@ -14,7 +14,7 @@ declare type CustomElement = {
|
|
|
14
14
|
};
|
|
15
15
|
declare module 'slate' {
|
|
16
16
|
interface CustomTypes {
|
|
17
|
-
Editor: BaseEditor & ReactEditor & HistoryEditor
|
|
17
|
+
Editor: BaseEditor & ReactEditor & HistoryEditor;
|
|
18
18
|
Element: CustomElement;
|
|
19
19
|
Text: CustomText;
|
|
20
20
|
}
|
|
@@ -69,14 +69,11 @@ const RichText = (props) => {
|
|
|
69
69
|
].filter(Boolean).join(' ');
|
|
70
70
|
const editor = useMemo(() => {
|
|
71
71
|
let CreatedEditor = withEnterBreakOut(withHistory(withReact(createEditor())));
|
|
72
|
+
CreatedEditor = withHTML(CreatedEditor);
|
|
72
73
|
CreatedEditor = enablePlugins(CreatedEditor, elements);
|
|
73
74
|
CreatedEditor = enablePlugins(CreatedEditor, leaves);
|
|
74
|
-
CreatedEditor = withHTML(CreatedEditor);
|
|
75
75
|
return CreatedEditor;
|
|
76
76
|
}, [elements, leaves]);
|
|
77
|
-
const onBlur = useCallback(() => {
|
|
78
|
-
editor.blurSelection = editor.selection;
|
|
79
|
-
}, [editor]);
|
|
80
77
|
useEffect(() => {
|
|
81
78
|
if (!loaded) {
|
|
82
79
|
const mergedElements = mergeCustomFunctions(elements, elementTypes);
|
|
@@ -128,7 +125,7 @@ const RichText = (props) => {
|
|
|
128
125
|
const elementType = enabledElements[elementName];
|
|
129
126
|
const Button = elementType === null || elementType === void 0 ? void 0 : elementType.Button;
|
|
130
127
|
if (Button) {
|
|
131
|
-
return (React.createElement(Button, { key: i, path: path }));
|
|
128
|
+
return (React.createElement(Button, { fieldProps: props, key: i, path: path }));
|
|
132
129
|
}
|
|
133
130
|
return null;
|
|
134
131
|
}),
|
|
@@ -141,12 +138,12 @@ const RichText = (props) => {
|
|
|
141
138
|
const leafType = enabledLeaves[leafName];
|
|
142
139
|
const Button = leafType === null || leafType === void 0 ? void 0 : leafType.Button;
|
|
143
140
|
if (Button) {
|
|
144
|
-
return (React.createElement(Button, { key: i, path: path }));
|
|
141
|
+
return (React.createElement(Button, { fieldProps: props, key: i, path: path }));
|
|
145
142
|
}
|
|
146
143
|
return null;
|
|
147
144
|
}))),
|
|
148
145
|
React.createElement("div", { className: `${baseClass}__editor`, ref: editorRef },
|
|
149
|
-
React.createElement(Editable, { id: `field-${path.replace(/\./gi, '__')}`, className: `${baseClass}__input`, renderElement: renderElement, renderLeaf: renderLeaf, placeholder: placeholder, spellCheck: true, readOnly: readOnly,
|
|
146
|
+
React.createElement(Editable, { id: `field-${path.replace(/\./gi, '__')}`, className: `${baseClass}__input`, renderElement: renderElement, renderLeaf: renderLeaf, placeholder: placeholder, spellCheck: true, readOnly: readOnly, onKeyDown: (event) => {
|
|
150
147
|
if (event.key === 'Enter') {
|
|
151
148
|
if (event.shiftKey) {
|
|
152
149
|
event.preventDefault();
|
|
@@ -13,9 +13,6 @@ const indent = {
|
|
|
13
13
|
const editor = useSlate();
|
|
14
14
|
const handleIndent = useCallback((e, dir) => {
|
|
15
15
|
e.preventDefault();
|
|
16
|
-
if (editor.blurSelection) {
|
|
17
|
-
Transforms.select(editor, editor.blurSelection);
|
|
18
|
-
}
|
|
19
16
|
if (dir === 'left') {
|
|
20
17
|
Transforms.unwrapNodes(editor, {
|
|
21
18
|
match: (n) => Element.isElement(n) && [indentType, ...listTypes].includes(n.type),
|
|
@@ -43,14 +43,17 @@ declare const elements: {
|
|
|
43
43
|
}) => JSX.Element;
|
|
44
44
|
};
|
|
45
45
|
link: {
|
|
46
|
-
Button: (
|
|
47
|
-
|
|
46
|
+
Button: ({ fieldProps }: {
|
|
47
|
+
fieldProps: any;
|
|
48
|
+
}) => JSX.Element;
|
|
49
|
+
Element: ({ attributes, children, element, editorRef, fieldProps }: {
|
|
48
50
|
attributes: any;
|
|
49
51
|
children: any;
|
|
50
52
|
element: any;
|
|
51
53
|
editorRef: any;
|
|
54
|
+
fieldProps: any;
|
|
52
55
|
}) => JSX.Element;
|
|
53
|
-
plugins: ((incomingEditor: import("slate").BaseEditor & import("slate-react").ReactEditor & import("slate-history").HistoryEditor
|
|
56
|
+
plugins: ((incomingEditor: import("slate").BaseEditor & import("slate-react").ReactEditor & import("slate-history").HistoryEditor) => import("slate").BaseEditor & import("slate-react").ReactEditor & import("slate-history").HistoryEditor)[];
|
|
54
57
|
};
|
|
55
58
|
ol: {
|
|
56
59
|
Button: () => JSX.Element;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import React, { Fragment, useState } from 'react';
|
|
2
|
+
import { ReactEditor, useSlate } from 'slate-react';
|
|
3
|
+
import { Transforms, Editor, Range } from 'slate';
|
|
4
|
+
import { useModal } from '@faceless-ui/modal';
|
|
5
|
+
import ElementButton from '../Button';
|
|
6
|
+
import { unwrapLink } from './utilities';
|
|
7
|
+
import LinkIcon from '../../../../../icons/Link';
|
|
8
|
+
import { EditModal } from './Modal';
|
|
9
|
+
import { modalSlug as baseModalSlug } from './shared';
|
|
10
|
+
import isElementActive from '../isActive';
|
|
11
|
+
import buildStateFromSchema from '../../../../Form/buildStateFromSchema';
|
|
12
|
+
import { useAuth } from '../../../../../utilities/Auth';
|
|
13
|
+
import { useLocale } from '../../../../../utilities/Locale';
|
|
14
|
+
import { useConfig } from '../../../../../utilities/Config';
|
|
15
|
+
import { getBaseFields } from './Modal/baseFields';
|
|
16
|
+
import reduceFieldsToValues from '../../../../Form/reduceFieldsToValues';
|
|
17
|
+
export const LinkButton = ({ fieldProps }) => {
|
|
18
|
+
var _a, _b;
|
|
19
|
+
const customFieldSchema = (_b = (_a = fieldProps === null || fieldProps === void 0 ? void 0 : fieldProps.admin) === null || _a === void 0 ? void 0 : _a.link) === null || _b === void 0 ? void 0 : _b.fields;
|
|
20
|
+
const modalSlug = `${baseModalSlug}-${fieldProps.path}`;
|
|
21
|
+
const config = useConfig();
|
|
22
|
+
const editor = useSlate();
|
|
23
|
+
const { user } = useAuth();
|
|
24
|
+
const locale = useLocale();
|
|
25
|
+
const { toggleModal } = useModal();
|
|
26
|
+
const [renderModal, setRenderModal] = useState(false);
|
|
27
|
+
const [initialState, setInitialState] = useState({});
|
|
28
|
+
const [fieldSchema] = useState(() => {
|
|
29
|
+
const fields = [
|
|
30
|
+
...getBaseFields(config),
|
|
31
|
+
];
|
|
32
|
+
if (customFieldSchema) {
|
|
33
|
+
fields.push({
|
|
34
|
+
name: 'fields',
|
|
35
|
+
type: 'group',
|
|
36
|
+
admin: {
|
|
37
|
+
style: {
|
|
38
|
+
margin: 0,
|
|
39
|
+
padding: 0,
|
|
40
|
+
borderTop: 0,
|
|
41
|
+
borderBottom: 0,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
fields: customFieldSchema,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return fields;
|
|
48
|
+
});
|
|
49
|
+
return (React.createElement(Fragment, null,
|
|
50
|
+
React.createElement(ElementButton, { format: "link", onClick: async () => {
|
|
51
|
+
if (isElementActive(editor, 'link')) {
|
|
52
|
+
unwrapLink(editor);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
toggleModal(modalSlug);
|
|
56
|
+
setRenderModal(true);
|
|
57
|
+
const isCollapsed = editor.selection && Range.isCollapsed(editor.selection);
|
|
58
|
+
if (!isCollapsed) {
|
|
59
|
+
const data = {
|
|
60
|
+
text: editor.selection ? Editor.string(editor, editor.selection) : '',
|
|
61
|
+
};
|
|
62
|
+
const state = await buildStateFromSchema({ fieldSchema, data, user, operation: 'create', locale });
|
|
63
|
+
setInitialState(state);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} },
|
|
67
|
+
React.createElement(LinkIcon, null)),
|
|
68
|
+
renderModal && (React.createElement(EditModal, { modalSlug: modalSlug, fieldSchema: fieldSchema, initialState: initialState, close: () => {
|
|
69
|
+
toggleModal(modalSlug);
|
|
70
|
+
setRenderModal(false);
|
|
71
|
+
}, handleModalSubmit: (fields) => {
|
|
72
|
+
const isCollapsed = editor.selection && Range.isCollapsed(editor.selection);
|
|
73
|
+
const data = reduceFieldsToValues(fields, true);
|
|
74
|
+
const newLink = {
|
|
75
|
+
type: 'link',
|
|
76
|
+
linkType: data.linkType,
|
|
77
|
+
url: data.url,
|
|
78
|
+
doc: data.doc,
|
|
79
|
+
newTab: data.newTab,
|
|
80
|
+
fields: data.fields,
|
|
81
|
+
children: [],
|
|
82
|
+
};
|
|
83
|
+
if (isCollapsed || !editor.selection) {
|
|
84
|
+
// If selection anchor and focus are the same,
|
|
85
|
+
// Just inject a new node with children already set
|
|
86
|
+
Transforms.insertNodes(editor, {
|
|
87
|
+
...newLink,
|
|
88
|
+
children: [{ text: String(data.text) }],
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
else if (editor.selection) {
|
|
92
|
+
// Otherwise we need to wrap the selected node in a link,
|
|
93
|
+
// Delete its old text,
|
|
94
|
+
// Move the selection one position forward into the link,
|
|
95
|
+
// And insert the text back into the new link
|
|
96
|
+
Transforms.wrapNodes(editor, newLink, { split: true });
|
|
97
|
+
Transforms.delete(editor, { at: editor.selection.focus.path, unit: 'word' });
|
|
98
|
+
Transforms.move(editor, { distance: 1, unit: 'offset' });
|
|
99
|
+
Transforms.insertText(editor, String(data.text), { at: editor.selection.focus.path });
|
|
100
|
+
}
|
|
101
|
+
toggleModal(modalSlug);
|
|
102
|
+
setRenderModal(false);
|
|
103
|
+
ReactEditor.focus(editor);
|
|
104
|
+
} }))));
|
|
105
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import React, { Fragment, useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { ReactEditor, useSlate } from 'slate-react';
|
|
3
|
+
import { Transforms, Node, Editor } from 'slate';
|
|
4
|
+
import { useModal } from '@faceless-ui/modal';
|
|
5
|
+
import { unwrapLink } from './utilities';
|
|
6
|
+
import Popup from '../../../../../elements/Popup';
|
|
7
|
+
import { EditModal } from './Modal';
|
|
8
|
+
import { modalSlug as baseModalSlug } from './shared';
|
|
9
|
+
import buildStateFromSchema from '../../../../Form/buildStateFromSchema';
|
|
10
|
+
import { useAuth } from '../../../../../utilities/Auth';
|
|
11
|
+
import { useLocale } from '../../../../../utilities/Locale';
|
|
12
|
+
import { useConfig } from '../../../../../utilities/Config';
|
|
13
|
+
import { getBaseFields } from './Modal/baseFields';
|
|
14
|
+
import reduceFieldsToValues from '../../../../Form/reduceFieldsToValues';
|
|
15
|
+
import deepCopyObject from '../../../../../../../utilities/deepCopyObject';
|
|
16
|
+
import Button from '../../../../../elements/Button';
|
|
17
|
+
import './index.scss';
|
|
18
|
+
const baseClass = 'rich-text-link';
|
|
19
|
+
// TODO: Multiple modal windows stacked go boom (rip). Edit Upload in fields -> rich text
|
|
20
|
+
export const LinkElement = ({ attributes, children, element, editorRef, fieldProps }) => {
|
|
21
|
+
var _a, _b;
|
|
22
|
+
const customFieldSchema = (_b = (_a = fieldProps === null || fieldProps === void 0 ? void 0 : fieldProps.admin) === null || _a === void 0 ? void 0 : _a.link) === null || _b === void 0 ? void 0 : _b.fields;
|
|
23
|
+
const editor = useSlate();
|
|
24
|
+
const config = useConfig();
|
|
25
|
+
const { user } = useAuth();
|
|
26
|
+
const locale = useLocale();
|
|
27
|
+
const { openModal, toggleModal } = useModal();
|
|
28
|
+
const [renderModal, setRenderModal] = useState(false);
|
|
29
|
+
const [renderPopup, setRenderPopup] = useState(false);
|
|
30
|
+
const [initialState, setInitialState] = useState({});
|
|
31
|
+
const [fieldSchema] = useState(() => {
|
|
32
|
+
const fields = [
|
|
33
|
+
...getBaseFields(config),
|
|
34
|
+
];
|
|
35
|
+
if (customFieldSchema) {
|
|
36
|
+
fields.push({
|
|
37
|
+
name: 'fields',
|
|
38
|
+
type: 'group',
|
|
39
|
+
admin: {
|
|
40
|
+
style: {
|
|
41
|
+
margin: 0,
|
|
42
|
+
padding: 0,
|
|
43
|
+
borderTop: 0,
|
|
44
|
+
borderBottom: 0,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
fields: customFieldSchema,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return fields;
|
|
51
|
+
});
|
|
52
|
+
const modalSlug = `${baseModalSlug}-${fieldProps.path}`;
|
|
53
|
+
const handleTogglePopup = useCallback((render) => {
|
|
54
|
+
if (!render) {
|
|
55
|
+
setRenderPopup(render);
|
|
56
|
+
}
|
|
57
|
+
}, []);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const awaitInitialState = async () => {
|
|
60
|
+
const data = {
|
|
61
|
+
text: Node.string(element),
|
|
62
|
+
linkType: element.linkType,
|
|
63
|
+
url: element.url,
|
|
64
|
+
doc: element.doc,
|
|
65
|
+
newTab: element.newTab,
|
|
66
|
+
fields: deepCopyObject(element.fields),
|
|
67
|
+
};
|
|
68
|
+
const state = await buildStateFromSchema({ fieldSchema, data, user, operation: 'update', locale });
|
|
69
|
+
setInitialState(state);
|
|
70
|
+
};
|
|
71
|
+
awaitInitialState();
|
|
72
|
+
}, [renderModal, element, fieldSchema, user, locale]);
|
|
73
|
+
return (React.createElement("span", { className: baseClass, ...attributes },
|
|
74
|
+
React.createElement("span", { style: { userSelect: 'none' }, contentEditable: false },
|
|
75
|
+
renderModal && (React.createElement(EditModal, { modalSlug: modalSlug, fieldSchema: fieldSchema, close: () => {
|
|
76
|
+
toggleModal(modalSlug);
|
|
77
|
+
setRenderModal(false);
|
|
78
|
+
}, handleModalSubmit: (fields) => {
|
|
79
|
+
toggleModal(modalSlug);
|
|
80
|
+
setRenderModal(false);
|
|
81
|
+
const data = reduceFieldsToValues(fields, true);
|
|
82
|
+
const [, parentPath] = Editor.above(editor);
|
|
83
|
+
const newNode = {
|
|
84
|
+
newTab: data.newTab,
|
|
85
|
+
url: data.url,
|
|
86
|
+
linkType: data.linkType,
|
|
87
|
+
doc: data.doc,
|
|
88
|
+
};
|
|
89
|
+
if (customFieldSchema) {
|
|
90
|
+
newNode.fields = data.fields;
|
|
91
|
+
}
|
|
92
|
+
Transforms.setNodes(editor, newNode, { at: parentPath });
|
|
93
|
+
Transforms.delete(editor, { at: editor.selection.focus.path, unit: 'block' });
|
|
94
|
+
Transforms.move(editor, { distance: 1, unit: 'offset' });
|
|
95
|
+
Transforms.insertText(editor, String(data.text), { at: editor.selection.focus.path });
|
|
96
|
+
ReactEditor.focus(editor);
|
|
97
|
+
}, initialState: initialState })),
|
|
98
|
+
React.createElement(Popup, { buttonType: "none", size: "small", forceOpen: renderPopup, onToggleOpen: handleTogglePopup, horizontalAlign: "left", verticalAlign: "bottom", boundingRef: editorRef, render: () => {
|
|
99
|
+
var _a, _b, _c, _d;
|
|
100
|
+
return (React.createElement("div", { className: `${baseClass}__popup` },
|
|
101
|
+
element.linkType === 'internal' && ((_a = element.doc) === null || _a === void 0 ? void 0 : _a.relationTo) && ((_b = element.doc) === null || _b === void 0 ? void 0 : _b.value) && (React.createElement(Fragment, null,
|
|
102
|
+
"Linked to\u00A0",
|
|
103
|
+
React.createElement("a", { className: `${baseClass}__link-label`, href: `${config.routes.admin}/collections/${element.doc.relationTo}/${element.doc.value}`, target: "_blank", rel: "noreferrer" }, (_d = (_c = config.collections.find(({ slug }) => slug === element.doc.relationTo)) === null || _c === void 0 ? void 0 : _c.labels) === null || _d === void 0 ? void 0 : _d.singular))),
|
|
104
|
+
(element.linkType === 'custom' || !element.linkType) && (React.createElement("a", { className: `${baseClass}__link-label`, href: element.url, target: "_blank", rel: "noreferrer" }, element.url)),
|
|
105
|
+
React.createElement(Button, { className: `${baseClass}__link-edit`, icon: "edit", round: true, buttonStyle: "icon-label", onClick: (e) => {
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
setRenderPopup(false);
|
|
108
|
+
openModal(modalSlug);
|
|
109
|
+
setRenderModal(true);
|
|
110
|
+
}, tooltip: "Edit" }),
|
|
111
|
+
React.createElement(Button, { className: `${baseClass}__link-close`, icon: "x", round: true, buttonStyle: "icon-label", onClick: (e) => {
|
|
112
|
+
e.preventDefault();
|
|
113
|
+
unwrapLink(editor);
|
|
114
|
+
}, tooltip: "Remove" })));
|
|
115
|
+
} })),
|
|
116
|
+
React.createElement("span", { tabIndex: 0, role: "button", className: [
|
|
117
|
+
`${baseClass}__button`,
|
|
118
|
+
].filter(Boolean).join(' '), onKeyDown: (e) => { if (e.key === 'Enter')
|
|
119
|
+
setRenderPopup(true); }, onClick: () => setRenderPopup(true) }, children)));
|
|
120
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export const getBaseFields = (config) => [
|
|
2
|
+
{
|
|
3
|
+
name: 'text',
|
|
4
|
+
label: 'Text to display',
|
|
5
|
+
type: 'text',
|
|
6
|
+
required: true,
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
name: 'linkType',
|
|
10
|
+
label: 'Link Type',
|
|
11
|
+
type: 'radio',
|
|
12
|
+
required: true,
|
|
13
|
+
admin: {
|
|
14
|
+
description: 'Choose between entering a custom text URL or linking to another document.',
|
|
15
|
+
},
|
|
16
|
+
defaultValue: 'custom',
|
|
17
|
+
options: [
|
|
18
|
+
{
|
|
19
|
+
label: 'Custom URL',
|
|
20
|
+
value: 'custom',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
label: 'Internal Link',
|
|
24
|
+
value: 'internal',
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'url',
|
|
30
|
+
label: 'Enter a URL',
|
|
31
|
+
type: 'text',
|
|
32
|
+
required: true,
|
|
33
|
+
admin: {
|
|
34
|
+
condition: ({ linkType, url }) => {
|
|
35
|
+
return (typeof linkType === 'undefined' && url) || linkType === 'custom';
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'doc',
|
|
41
|
+
label: 'Choose a document to link to',
|
|
42
|
+
type: 'relationship',
|
|
43
|
+
required: true,
|
|
44
|
+
relationTo: config.collections.map(({ slug }) => slug),
|
|
45
|
+
admin: {
|
|
46
|
+
condition: ({ linkType }) => {
|
|
47
|
+
return linkType === 'internal';
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'newTab',
|
|
53
|
+
label: 'Open in new tab',
|
|
54
|
+
type: 'checkbox',
|
|
55
|
+
},
|
|
56
|
+
];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Modal } from '@faceless-ui/modal';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { MinimalTemplate } from '../../../../../..';
|
|
4
|
+
import Button from '../../../../../../elements/Button';
|
|
5
|
+
import X from '../../../../../../icons/X';
|
|
6
|
+
import Form from '../../../../../Form';
|
|
7
|
+
import FormSubmit from '../../../../../Submit';
|
|
8
|
+
import fieldTypes from '../../../..';
|
|
9
|
+
import RenderFields from '../../../../../RenderFields';
|
|
10
|
+
import './index.scss';
|
|
11
|
+
const baseClass = 'rich-text-link-edit-modal';
|
|
12
|
+
export const EditModal = ({ close, handleModalSubmit, initialState, fieldSchema, modalSlug, }) => {
|
|
13
|
+
return (React.createElement(Modal, { slug: modalSlug, className: baseClass },
|
|
14
|
+
React.createElement(MinimalTemplate, { className: `${baseClass}__template` },
|
|
15
|
+
React.createElement("header", { className: `${baseClass}__header` },
|
|
16
|
+
React.createElement("h3", null, "Edit Link"),
|
|
17
|
+
React.createElement(Button, { buttonStyle: "none", onClick: close },
|
|
18
|
+
React.createElement(X, null))),
|
|
19
|
+
React.createElement(Form, { onSubmit: handleModalSubmit, initialState: initialState },
|
|
20
|
+
React.createElement(RenderFields, { fieldTypes: fieldTypes, readOnly: false, fieldSchema: fieldSchema, forceRender: true }),
|
|
21
|
+
React.createElement(FormSubmit, null, "Confirm")))));
|
|
22
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
@import '../../../../../../../scss/styles.scss';
|
|
2
|
+
|
|
3
|
+
.rich-text-link-edit-modal {
|
|
4
|
+
@include blur-bg;
|
|
5
|
+
display: flex;
|
|
6
|
+
align-items: center;
|
|
7
|
+
height: 100%;
|
|
8
|
+
|
|
9
|
+
&__template {
|
|
10
|
+
position: relative;
|
|
11
|
+
z-index: 1;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
&__header {
|
|
15
|
+
width: 100%;
|
|
16
|
+
margin-bottom: $baseline;
|
|
17
|
+
display: flex;
|
|
18
|
+
justify-content: space-between;
|
|
19
|
+
|
|
20
|
+
h3 {
|
|
21
|
+
margin: 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
svg {
|
|
25
|
+
width: base(1.5);
|
|
26
|
+
height: base(1.5);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Field } from '../../../../../../../../fields/config/types';
|
|
2
|
+
import { Fields } from '../../../../../Form/types';
|
|
3
|
+
export declare type Props = {
|
|
4
|
+
modalSlug: string;
|
|
5
|
+
close: () => void;
|
|
6
|
+
handleModalSubmit: (fields: Fields, data: Record<string, unknown>) => void;
|
|
7
|
+
initialState?: Fields;
|
|
8
|
+
fieldSchema: Field[];
|
|
9
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import { ReactEditor } from 'slate-react';
|
|
3
|
-
import './index.scss';
|
|
4
2
|
declare const link: {
|
|
5
|
-
Button: (
|
|
6
|
-
|
|
3
|
+
Button: ({ fieldProps }: {
|
|
4
|
+
fieldProps: any;
|
|
5
|
+
}) => JSX.Element;
|
|
6
|
+
Element: ({ attributes, children, element, editorRef, fieldProps }: {
|
|
7
7
|
attributes: any;
|
|
8
8
|
children: any;
|
|
9
9
|
element: any;
|
|
10
10
|
editorRef: any;
|
|
11
|
+
fieldProps: any;
|
|
11
12
|
}) => JSX.Element;
|
|
12
|
-
plugins: ((incomingEditor: import("slate").BaseEditor &
|
|
13
|
+
plugins: ((incomingEditor: import("slate").BaseEditor & import("slate-react").ReactEditor & import("slate-history").HistoryEditor) => import("slate").BaseEditor & import("slate-react").ReactEditor & import("slate-history").HistoryEditor)[];
|
|
13
14
|
};
|
|
14
15
|
export default link;
|
|
@@ -1,72 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import ElementButton from '../Button';
|
|
5
|
-
import { withLinks, wrapLink } from './utilities';
|
|
6
|
-
import LinkIcon from '../../../../../icons/Link';
|
|
7
|
-
import Popup from '../../../../../elements/Popup';
|
|
8
|
-
import Button from '../../../../../elements/Button';
|
|
9
|
-
import Check from '../../../../../icons/Check';
|
|
10
|
-
import Error from '../../../../Error';
|
|
11
|
-
import './index.scss';
|
|
12
|
-
const baseClass = 'rich-text-link';
|
|
13
|
-
const Link = ({ attributes, children, element, editorRef }) => {
|
|
14
|
-
const editor = useSlate();
|
|
15
|
-
const [error, setError] = useState(false);
|
|
16
|
-
const [open, setOpen] = useState(element.url === undefined);
|
|
17
|
-
const handleToggleOpen = useCallback((newOpen) => {
|
|
18
|
-
setOpen(newOpen);
|
|
19
|
-
if (element.url === undefined && !newOpen) {
|
|
20
|
-
const path = ReactEditor.findPath(editor, element);
|
|
21
|
-
Transforms.setNodes(editor, { url: '' }, { at: path });
|
|
22
|
-
}
|
|
23
|
-
}, [editor, element]);
|
|
24
|
-
return (React.createElement("span", { className: baseClass, ...attributes },
|
|
25
|
-
React.createElement("span", { style: { userSelect: 'none' }, contentEditable: false },
|
|
26
|
-
React.createElement(Popup, { initActive: element.url === undefined, buttonType: "none", size: "small", horizontalAlign: "center", forceOpen: open, onToggleOpen: handleToggleOpen, boundingRef: editorRef, render: ({ close }) => (React.createElement(Fragment, null,
|
|
27
|
-
React.createElement("div", { className: `${baseClass}__url-wrap` },
|
|
28
|
-
React.createElement("input", { value: element.url || '', className: `${baseClass}__url`, placeholder: "Enter a URL", onChange: (e) => {
|
|
29
|
-
const { value } = e.target;
|
|
30
|
-
if (value && error) {
|
|
31
|
-
setError(false);
|
|
32
|
-
}
|
|
33
|
-
const path = ReactEditor.findPath(editor, element);
|
|
34
|
-
Transforms.setNodes(editor, { url: value }, { at: path });
|
|
35
|
-
}, onKeyDown: (e) => {
|
|
36
|
-
if (e.key === 'Enter') {
|
|
37
|
-
e.preventDefault();
|
|
38
|
-
close();
|
|
39
|
-
}
|
|
40
|
-
} }),
|
|
41
|
-
React.createElement(Button, { className: `${baseClass}__confirm`, buttonStyle: "none", icon: "chevron", onClick: (e) => {
|
|
42
|
-
e.preventDefault();
|
|
43
|
-
if (element.url) {
|
|
44
|
-
close();
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
setError(true);
|
|
48
|
-
}
|
|
49
|
-
} }),
|
|
50
|
-
error && (React.createElement(Error, { showError: error, message: "Please enter a valid URL." }))),
|
|
51
|
-
React.createElement(Button, { className: [`${baseClass}__new-tab`, element.newTab && `${baseClass}__new-tab--checked`].filter(Boolean).join(' '), buttonStyle: "none", onClick: () => {
|
|
52
|
-
const path = ReactEditor.findPath(editor, element);
|
|
53
|
-
Transforms.setNodes(editor, { newTab: !element.newTab }, { at: path });
|
|
54
|
-
} },
|
|
55
|
-
React.createElement(Check, null),
|
|
56
|
-
"Open link in new tab"))) })),
|
|
57
|
-
React.createElement("button", { className: [
|
|
58
|
-
`${baseClass}__button`,
|
|
59
|
-
open && `${baseClass}__button--open`,
|
|
60
|
-
].filter(Boolean).join(' '), type: "button", onClick: () => setOpen(true) }, children)));
|
|
61
|
-
};
|
|
62
|
-
const LinkButton = () => {
|
|
63
|
-
const editor = useSlate();
|
|
64
|
-
return (React.createElement(ElementButton, { format: "link", onClick: () => wrapLink(editor) },
|
|
65
|
-
React.createElement(LinkIcon, null)));
|
|
66
|
-
};
|
|
1
|
+
import { withLinks } from './utilities';
|
|
2
|
+
import { LinkButton } from './Button';
|
|
3
|
+
import { LinkElement } from './Element';
|
|
67
4
|
const link = {
|
|
68
5
|
Button: LinkButton,
|
|
69
|
-
Element:
|
|
6
|
+
Element: LinkElement,
|
|
70
7
|
plugins: [
|
|
71
8
|
withLinks,
|
|
72
9
|
],
|