payload 1.0.34 → 1.1.0
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/components/elements.ts +1 -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/ListControls/index.js +3 -2
- package/dist/admin/components/elements/Nav/index.js +43 -24
- package/dist/admin/components/elements/Nav/index.scss +22 -26
- package/dist/admin/components/elements/NavGroup/index.d.ts +8 -0
- package/dist/admin/components/elements/NavGroup/index.js +52 -0
- package/dist/admin/components/elements/NavGroup/index.scss +53 -0
- package/dist/admin/components/elements/Popup/index.js +14 -13
- package/dist/admin/components/elements/SearchFilter/index.js +45 -16
- package/dist/admin/components/elements/SearchFilter/types.d.ts +2 -0
- package/dist/admin/components/elements/Status/index.js +14 -9
- package/dist/admin/components/elements/WhereBuilder/index.js +14 -1
- package/dist/admin/components/forms/Error/index.scss +2 -1
- package/dist/admin/components/forms/Form/buildStateFromSchema/addFieldStatePromise.js +3 -3
- package/dist/admin/components/forms/Label/index.scss +3 -1
- package/dist/admin/components/forms/field-types/Array/index.js +14 -9
- package/dist/admin/components/forms/field-types/Blocks/index.js +12 -7
- package/dist/admin/components/forms/field-types/Collapsible/index.js +7 -7
- package/dist/admin/components/forms/field-types/RichText/RichText.d.ts +2 -2
- package/dist/admin/components/forms/field-types/RichText/RichText.js +3 -6
- 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/Tabs/index.js +4 -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/forms/field-types/rowReducer.d.ts +1 -0
- package/dist/admin/components/forms/field-types/rowReducer.js +2 -2
- package/dist/admin/components/graphics/Account/Gravatar.d.ts +3 -0
- package/dist/admin/components/graphics/Account/Gravatar.js +15 -0
- package/dist/admin/components/graphics/Account/index.d.ts +2 -2
- package/dist/admin/components/graphics/Account/index.js +24 -1
- 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/Account/index.js +1 -1
- package/dist/admin/components/views/Dashboard/Default.js +44 -12
- package/dist/admin/components/views/Dashboard/index.scss +1 -1
- package/dist/admin/components/views/Version/Restore/index.js +3 -3
- package/dist/admin/components/views/collections/List/RelationshipProvider/index.js +1 -1
- package/dist/admin/components/views/collections/List/getInitialColumns.js +2 -2
- 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/admin/utilities/groupNavItems.d.ts +19 -0
- package/dist/admin/utilities/groupNavItems.js +36 -0
- package/dist/auth/types.d.ts +1 -1
- package/dist/bin/generateTypes.js +20 -4
- package/dist/collections/config/schema.js +18 -2
- package/dist/collections/config/types.d.ts +9 -0
- package/dist/collections/operations/create.js +3 -1
- package/dist/collections/operations/local/create.d.ts +2 -2
- package/dist/collections/operations/local/create.js +3 -3
- package/dist/collections/operations/local/update.js +2 -2
- package/dist/collections/operations/restoreVersion.js +11 -1
- package/dist/collections/operations/update.js +3 -1
- package/dist/config/defaults.js +2 -1
- package/dist/config/schema.js +3 -1
- package/dist/config/types.d.ts +1 -0
- package/dist/fields/config/schema.js +14 -6
- package/dist/fields/config/types.d.ts +45 -9
- package/dist/fields/config/types.js +10 -2
- package/dist/fields/hooks/afterChange/index.d.ts +2 -1
- package/dist/fields/hooks/afterChange/index.js +4 -2
- package/dist/fields/hooks/afterChange/promise.d.ts +5 -3
- package/dist/fields/hooks/afterChange/promise.js +49 -18
- package/dist/fields/hooks/afterChange/traverseFields.d.ts +5 -3
- package/dist/fields/hooks/afterChange/traverseFields.js +4 -2
- package/dist/fields/hooks/afterRead/promise.d.ts +2 -2
- package/dist/fields/hooks/afterRead/promise.js +50 -22
- package/dist/fields/hooks/afterRead/traverseFields.d.ts +2 -2
- package/dist/fields/hooks/beforeChange/promise.d.ts +2 -2
- package/dist/fields/hooks/beforeChange/promise.js +51 -20
- package/dist/fields/hooks/beforeChange/traverseFields.d.ts +2 -2
- package/dist/fields/hooks/beforeValidate/promise.d.ts +2 -2
- package/dist/fields/hooks/beforeValidate/promise.js +35 -15
- package/dist/fields/hooks/beforeValidate/traverseFields.d.ts +2 -2
- 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/globals/config/schema.js +2 -1
- package/dist/globals/config/types.d.ts +2 -0
- package/dist/globals/operations/restoreVersion.js +10 -1
- package/dist/globals/operations/update.js +4 -1
- package/dist/graphql/schema/buildMutationInputType.js +27 -13
- package/dist/graphql/schema/buildObjectType.js +18 -3
- package/dist/graphql/schema/withNullableType.d.ts +2 -2
- package/dist/graphql/schema/withNullableType.js +1 -1
- package/dist/mongoose/buildQuery.js +4 -2
- package/dist/mongoose/buildSchema.js +25 -14
- package/dist/types/index.d.ts +1 -1
- package/dist/uploads/getBaseFields.js +3 -3
- package/dist/uploads/getFileByPath.d.ts +1 -1
- package/dist/uploads/getFileByPath.js +5 -5
- package/dist/uploads/getSafeFilename.js +3 -4
- package/dist/uploads/imageResizer.d.ts +1 -9
- package/dist/uploads/imageResizer.js +18 -18
- package/dist/uploads/types.d.ts +18 -3
- package/dist/uploads/uploadFile.js +29 -8
- package/dist/utilities/flattenTopLevelFields.js +2 -2
- package/dist/utilities/groupOrTabHasRequiredSubfield.d.ts +2 -0
- package/dist/utilities/groupOrTabHasRequiredSubfield.js +17 -0
- package/dist/utilities/logger.js +2 -3
- package/dist/utilities/wordBoundariesRegex.js +2 -2
- package/docs/admin/overview.mdx +2 -1
- package/docs/configuration/collections.mdx +14 -1
- package/docs/fields/array.mdx +10 -1
- package/docs/fields/blocks.mdx +9 -1
- package/docs/fields/collapsible.mdx +13 -5
- package/docs/fields/rich-text.mdx +7 -0
- package/docs/fields/tabs.mdx +4 -2
- package/docs/hooks/collections.mdx +3 -2
- package/docs/hooks/fields.mdx +11 -9
- package/docs/hooks/globals.mdx +1 -0
- package/docs/upload/overview.mdx +5 -3
- package/package.json +4 -4
- package/CHANGELOG.md +0 -1870
- package/dist/fields/richText/relationshipPromise.js +0 -71
|
@@ -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
|
],
|
|
@@ -10,14 +10,44 @@
|
|
|
10
10
|
right: 0;
|
|
11
11
|
bottom: 0;
|
|
12
12
|
left: 0;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
.popup__scroll,
|
|
16
|
+
.popup__wrap {
|
|
17
|
+
overflow: visible;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.popup__scroll {
|
|
21
|
+
padding-right: base(.5);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.icon--x line {
|
|
26
|
+
stroke-width: 2px;
|
|
13
27
|
}
|
|
14
|
-
}
|
|
15
28
|
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
&__popup {
|
|
30
|
+
@extend %body;
|
|
31
|
+
font-family: var(--font-body);
|
|
32
|
+
display: flex;
|
|
18
33
|
|
|
19
|
-
|
|
20
|
-
|
|
34
|
+
button {
|
|
35
|
+
@extend %btn-reset;
|
|
36
|
+
font-weight: 600;
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
margin: 0 0 0 base(.25);
|
|
39
|
+
|
|
40
|
+
&:hover {
|
|
41
|
+
text-decoration: underline;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&__link-label {
|
|
47
|
+
max-width: base(8);
|
|
48
|
+
overflow: hidden;
|
|
49
|
+
text-overflow: ellipsis;
|
|
50
|
+
margin-right: base(.25);
|
|
21
51
|
}
|
|
22
52
|
}
|
|
23
53
|
|
|
@@ -36,61 +66,4 @@
|
|
|
36
66
|
&--open {
|
|
37
67
|
z-index: var(--z-popup);
|
|
38
68
|
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.rich-text-link__url-wrap {
|
|
42
|
-
position: relative;
|
|
43
|
-
width: 100%;
|
|
44
|
-
margin-bottom: base(.5);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.rich-text-link__confirm {
|
|
48
|
-
position: absolute;
|
|
49
|
-
right: base(.5);
|
|
50
|
-
top: 50%;
|
|
51
|
-
transform: translateY(-50%);
|
|
52
|
-
|
|
53
|
-
svg {
|
|
54
|
-
@include color-svg(var(--theme-elevation-0));
|
|
55
|
-
transform: rotate(-90deg);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.rich-text-link__url {
|
|
60
|
-
@include formInput;
|
|
61
|
-
padding-right: base(1.75);
|
|
62
|
-
min-width: base(12);
|
|
63
|
-
width: 100%;
|
|
64
|
-
background: var(--theme-input-bg);
|
|
65
|
-
color: var(--theme-elevation-1000);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.rich-text-link__new-tab {
|
|
69
|
-
svg {
|
|
70
|
-
@include color-svg(var(--theme-elevation-900));
|
|
71
|
-
background: var(--theme-elevation-100);
|
|
72
|
-
margin-right: base(.5);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
path {
|
|
76
|
-
opacity: 0;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
&:hover {
|
|
80
|
-
path {
|
|
81
|
-
opacity: .2;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
&--checked {
|
|
86
|
-
path {
|
|
87
|
-
opacity: 1;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
&:hover {
|
|
91
|
-
path {
|
|
92
|
-
opacity: .8;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const modalSlug = "rich-text-link-modal";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const modalSlug = 'rich-text-link-modal';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Editor } from 'slate';
|
|
2
2
|
export declare const unwrapLink: (editor: Editor) => void;
|
|
3
|
-
export declare const wrapLink: (editor: Editor
|
|
3
|
+
export declare const wrapLink: (editor: Editor) => void;
|
|
4
4
|
export declare const withLinks: (incomingEditor: Editor) => Editor;
|
|
@@ -1,32 +1,22 @@
|
|
|
1
1
|
import { Transforms, Range, Element } from 'slate';
|
|
2
|
-
import isElementActive from '../isActive';
|
|
3
2
|
export const unwrapLink = (editor) => {
|
|
4
3
|
Transforms.unwrapNodes(editor, { match: (n) => Element.isElement(n) && n.type === 'link' });
|
|
5
4
|
};
|
|
6
|
-
export const wrapLink = (editor
|
|
7
|
-
const { selection
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
export const wrapLink = (editor) => {
|
|
6
|
+
const { selection } = editor;
|
|
7
|
+
const isCollapsed = selection && Range.isCollapsed(selection);
|
|
8
|
+
const link = {
|
|
9
|
+
type: 'link',
|
|
10
|
+
url: undefined,
|
|
11
|
+
newTab: false,
|
|
12
|
+
children: isCollapsed ? [{ text: '' }] : [],
|
|
13
|
+
};
|
|
14
|
+
if (isCollapsed) {
|
|
15
|
+
Transforms.insertNodes(editor, link);
|
|
13
16
|
}
|
|
14
17
|
else {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const link = {
|
|
18
|
-
type: 'link',
|
|
19
|
-
url,
|
|
20
|
-
newTab,
|
|
21
|
-
children: isCollapsed ? [{ text: url }] : [],
|
|
22
|
-
};
|
|
23
|
-
if (isCollapsed) {
|
|
24
|
-
Transforms.insertNodes(editor, link);
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
Transforms.wrapNodes(editor, link, { split: true });
|
|
28
|
-
Transforms.collapse(editor, { edge: 'end' });
|
|
29
|
-
}
|
|
18
|
+
Transforms.wrapNodes(editor, link, { split: true });
|
|
19
|
+
Transforms.collapse(editor, { edge: 'end' });
|
|
30
20
|
}
|
|
31
21
|
};
|
|
32
22
|
export const withLinks = (incomingEditor) => {
|