@contentful/field-editor-rich-text 3.12.0 → 3.12.1

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.
@@ -90,12 +90,16 @@ const Asset = ({ command , selectedItem })=>_react.createElement("button", {
90
90
  }, _react.createElement(_f36components.Flex, {
91
91
  alignItems: "center",
92
92
  gap: "spacingS"
93
- }, command.thumbnail && _react.createElement("img", {
93
+ }, command.thumbnail ? _react.createElement("img", {
94
94
  width: "30",
95
95
  height: "30",
96
96
  src: command.thumbnail,
97
97
  alt: "",
98
98
  className: _CommandListstyles.default.thumbnail
99
+ }) : _react.createElement(_f36components.AssetIcon, {
100
+ width: "30",
101
+ height: "30",
102
+ className: _CommandListstyles.default.thumbnail
99
103
  }), _react.createElement("span", null, command.label)));
100
104
  const Item = ({ command })=>_react.createElement("button", {
101
105
  key: command.id,
@@ -105,12 +109,15 @@ const Item = ({ command })=>_react.createElement("button", {
105
109
  const CommandListItems = ({ commandItems , selectedItem })=>{
106
110
  return _react.createElement(_react.Fragment, null, commandItems.map((command)=>{
107
111
  return 'group' in command ? _react.createElement(Group, {
112
+ key: command.group,
108
113
  commandGroup: command,
109
114
  selectedItem: selectedItem
110
115
  }) : command.callback ? _react.createElement(Asset, {
116
+ key: command.id,
111
117
  command: command,
112
118
  selectedItem: selectedItem
113
119
  }) : _react.createElement(Item, {
120
+ key: command.id,
114
121
  command: command
115
122
  });
116
123
  }));
@@ -9,8 +9,15 @@ Object.defineProperty(exports, "CommandPrompt", {
9
9
  }
10
10
  });
11
11
  const _react = _interop_require_wildcard(require("react"));
12
+ const _f36tokens = _interop_require_default(require("@contentful/f36-tokens"));
13
+ const _emotion = require("emotion");
12
14
  const _trimLeadingSlash = require("../utils/trimLeadingSlash");
13
15
  const _CommandList = require("./CommandList");
16
+ function _interop_require_default(obj) {
17
+ return obj && obj.__esModule ? obj : {
18
+ default: obj
19
+ };
20
+ }
14
21
  function _getRequireWildcardCache(nodeInterop) {
15
22
  if (typeof WeakMap !== "function") return null;
16
23
  var cacheBabelInterop = new WeakMap();
@@ -50,6 +57,11 @@ function _interop_require_wildcard(obj, nodeInterop) {
50
57
  }
51
58
  return newObj;
52
59
  }
60
+ const styles = {
61
+ commandPrompt: (0, _emotion.css)({
62
+ color: _f36tokens.default.blue400
63
+ })
64
+ };
53
65
  const CommandPrompt = (props)=>{
54
66
  const query = _react.useMemo(()=>(0, _trimLeadingSlash.trimLeadingSlash)(props.text.text), [
55
67
  props.text.text
@@ -57,6 +69,7 @@ const CommandPrompt = (props)=>{
57
69
  const editor = props.editor;
58
70
  const [textElement, setTextElement] = _react.useState();
59
71
  return _react.createElement("span", {
72
+ className: styles.commandPrompt,
60
73
  ref: (e)=>{
61
74
  setTextElement(e);
62
75
  },
@@ -64,9 +64,36 @@ function getCommandPermissions(sdk, editor) {
64
64
  assetsAllowed: assetsAllowed && canInsertBlocks
65
65
  };
66
66
  }
67
+ const getAllowedContentTypesFromValidation = (validations)=>{
68
+ const types = [
69
+ _richtexttypes.BLOCKS.EMBEDDED_ENTRY,
70
+ _richtexttypes.INLINES.EMBEDDED_ENTRY
71
+ ];
72
+ return validations.reduce((acc, validation)=>{
73
+ types.forEach((type)=>{
74
+ const linkContentTypes = validation.nodes?.[type]?.[0]?.linkContentType;
75
+ if (linkContentTypes) {
76
+ if (!acc[type]) {
77
+ acc[type] = {};
78
+ }
79
+ linkContentTypes.forEach((contentType)=>{
80
+ acc[type][contentType] = true;
81
+ });
82
+ }
83
+ });
84
+ return acc;
85
+ }, {});
86
+ };
67
87
  const useCommands = (sdk, query, editor)=>{
68
88
  const contentTypes = sdk.space.getCachedContentTypes();
69
89
  const { inlineAllowed , entriesAllowed , assetsAllowed } = getCommandPermissions(sdk, editor);
90
+ const allowedContentTypesFromValidation = getAllowedContentTypesFromValidation(sdk.field.validations);
91
+ const filteredBlockContentTypes = contentTypes.filter((contentType)=>allowedContentTypesFromValidation[_richtexttypes.BLOCKS.EMBEDDED_ENTRY]?.[contentType.sys.id]);
92
+ const filteredInlineContentTypes = contentTypes.filter((contentType)=>allowedContentTypesFromValidation[_richtexttypes.INLINES.EMBEDDED_ENTRY]?.[contentType.sys.id]);
93
+ const isBlockContentTypeFiltered = filteredBlockContentTypes.length > 0;
94
+ const isInlineContentTypeFiltered = filteredInlineContentTypes.length > 0;
95
+ const blockContentTypesToUse = isBlockContentTypeFiltered ? filteredBlockContentTypes : contentTypes;
96
+ const inlineContentTypesToUse = isInlineContentTypeFiltered ? filteredInlineContentTypes : contentTypes;
70
97
  const [commands, setCommands] = (0, _react.useState)(()=>{
71
98
  const getEmbedEntry = (contentType)=>{
72
99
  return {
@@ -83,16 +110,16 @@ const useCommands = (sdk, query, editor)=>{
83
110
  }
84
111
  ]);
85
112
  } else {
86
- setCommands(entries.map((entry)=>{
113
+ setCommands(entries.map((ct)=>{
87
114
  return {
88
- id: `${entry.id}-${entry.displayTitle.replace(/\W+/g, '-').toLowerCase()}`,
89
- label: entry.displayTitle,
115
+ id: ct.entry.sys.id,
116
+ label: ct.displayTitle,
90
117
  callback: ()=>{
91
118
  removeCommand(editor);
92
119
  if (editor.selection) {
93
120
  const selection = editor.selection;
94
121
  editor.insertSoftBreak();
95
- (0, _insertBlock.insertBlock)(editor, _richtexttypes.BLOCKS.EMBEDDED_ENTRY, entry.entry);
122
+ (0, _insertBlock.insertBlock)(editor, _richtexttypes.BLOCKS.EMBEDDED_ENTRY, ct.entry);
96
123
  (0, _transforms.select)(editor, selection);
97
124
  editor.tracking.onCommandPaletteAction('insert', {
98
125
  nodeType: _richtexttypes.BLOCKS.EMBEDDED_ENTRY
@@ -121,12 +148,12 @@ const useCommands = (sdk, query, editor)=>{
121
148
  }
122
149
  ]);
123
150
  } else {
124
- setCommands(entries.map((entry)=>{
151
+ setCommands(entries.map((ct)=>{
125
152
  return {
126
- id: `${entry.id}-${entry.displayTitle.replace(/\W+/g, '-').toLowerCase()}`,
127
- label: entry.displayTitle,
153
+ id: ct.entry.sys.id,
154
+ label: ct.displayTitle,
128
155
  callback: ()=>{
129
- const inlineNode = (0, _createInlineEntryNode.createInlineEntryNode)(entry.id);
156
+ const inlineNode = (0, _createInlineEntryNode.createInlineEntryNode)(ct.entry.sys.id);
130
157
  removeCommand(editor);
131
158
  (0, _transforms.insertNodes)(editor, inlineNode);
132
159
  editor.insertText('');
@@ -142,16 +169,18 @@ const useCommands = (sdk, query, editor)=>{
142
169
  };
143
170
  };
144
171
  const contentTypeCommands = entriesAllowed || inlineAllowed ? contentTypes.map((contentType)=>{
172
+ const blockEmbedAllowed = blockContentTypesToUse.some((ct)=>ct.sys.id === contentType.sys.id);
173
+ const inlineEmbedAllowed = inlineContentTypesToUse.some((ct)=>ct.sys.id === contentType.sys.id);
174
+ const commands = [];
175
+ if (entriesAllowed && blockEmbedAllowed) {
176
+ commands.push(getEmbedEntry(contentType));
177
+ }
178
+ if (inlineAllowed && inlineEmbedAllowed) {
179
+ commands.push(getEmbedInline(contentType));
180
+ }
145
181
  return {
146
182
  group: contentType.name,
147
- commands: entriesAllowed && inlineAllowed ? [
148
- getEmbedEntry(contentType),
149
- getEmbedInline(contentType)
150
- ] : entriesAllowed ? [
151
- getEmbedEntry(contentType)
152
- ] : [
153
- getEmbedInline(contentType)
154
- ]
183
+ commands: commands
155
184
  };
156
185
  }) : [];
157
186
  if (assetsAllowed) {
@@ -174,7 +203,7 @@ const useCommands = (sdk, query, editor)=>{
174
203
  } else {
175
204
  setCommands(assets.map((asset)=>{
176
205
  return {
177
- id: `${asset.id}-${asset.displayTitle.replace(/\W+/g, '-').toLowerCase()}`,
206
+ id: asset.entity.sys.id,
178
207
  label: asset.displayTitle,
179
208
  thumbnail: asset.thumbnail,
180
209
  callback: ()=>{
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { usePopper } from 'react-popper';
3
- import { Popover, Stack, SectionHeading, ScreenReaderOnly, Flex } from '@contentful/f36-components';
3
+ import { Popover, Stack, SectionHeading, ScreenReaderOnly, Flex, AssetIcon } from '@contentful/f36-components';
4
4
  import { Portal } from '@contentful/f36-utils';
5
5
  import { cx } from 'emotion';
6
6
  import { useSdkContext } from '../../../SdkProvider';
@@ -36,12 +36,16 @@ const Asset = ({ command , selectedItem })=>React.createElement("button", {
36
36
  }, React.createElement(Flex, {
37
37
  alignItems: "center",
38
38
  gap: "spacingS"
39
- }, command.thumbnail && React.createElement("img", {
39
+ }, command.thumbnail ? React.createElement("img", {
40
40
  width: "30",
41
41
  height: "30",
42
42
  src: command.thumbnail,
43
43
  alt: "",
44
44
  className: styles.thumbnail
45
+ }) : React.createElement(AssetIcon, {
46
+ width: "30",
47
+ height: "30",
48
+ className: styles.thumbnail
45
49
  }), React.createElement("span", null, command.label)));
46
50
  const Item = ({ command })=>React.createElement("button", {
47
51
  key: command.id,
@@ -51,12 +55,15 @@ const Item = ({ command })=>React.createElement("button", {
51
55
  const CommandListItems = ({ commandItems , selectedItem })=>{
52
56
  return React.createElement(React.Fragment, null, commandItems.map((command)=>{
53
57
  return 'group' in command ? React.createElement(Group, {
58
+ key: command.group,
54
59
  commandGroup: command,
55
60
  selectedItem: selectedItem
56
61
  }) : command.callback ? React.createElement(Asset, {
62
+ key: command.id,
57
63
  command: command,
58
64
  selectedItem: selectedItem
59
65
  }) : React.createElement(Item, {
66
+ key: command.id,
60
67
  command: command
61
68
  });
62
69
  }));
@@ -1,6 +1,13 @@
1
1
  import * as React from 'react';
2
+ import tokens from '@contentful/f36-tokens';
3
+ import { css } from 'emotion';
2
4
  import { trimLeadingSlash } from '../utils/trimLeadingSlash';
3
5
  import { CommandList } from './CommandList';
6
+ const styles = {
7
+ commandPrompt: css({
8
+ color: tokens.blue400
9
+ })
10
+ };
4
11
  export const CommandPrompt = (props)=>{
5
12
  const query = React.useMemo(()=>trimLeadingSlash(props.text.text), [
6
13
  props.text.text
@@ -8,6 +15,7 @@ export const CommandPrompt = (props)=>{
8
15
  const editor = props.editor;
9
16
  const [textElement, setTextElement] = React.useState();
10
17
  return React.createElement("span", {
18
+ className: styles.commandPrompt,
11
19
  ref: (e)=>{
12
20
  setTextElement(e);
13
21
  },
@@ -46,9 +46,36 @@ function getCommandPermissions(sdk, editor) {
46
46
  assetsAllowed: assetsAllowed && canInsertBlocks
47
47
  };
48
48
  }
49
+ const getAllowedContentTypesFromValidation = (validations)=>{
50
+ const types = [
51
+ BLOCKS.EMBEDDED_ENTRY,
52
+ INLINES.EMBEDDED_ENTRY
53
+ ];
54
+ return validations.reduce((acc, validation)=>{
55
+ types.forEach((type)=>{
56
+ const linkContentTypes = validation.nodes?.[type]?.[0]?.linkContentType;
57
+ if (linkContentTypes) {
58
+ if (!acc[type]) {
59
+ acc[type] = {};
60
+ }
61
+ linkContentTypes.forEach((contentType)=>{
62
+ acc[type][contentType] = true;
63
+ });
64
+ }
65
+ });
66
+ return acc;
67
+ }, {});
68
+ };
49
69
  export const useCommands = (sdk, query, editor)=>{
50
70
  const contentTypes = sdk.space.getCachedContentTypes();
51
71
  const { inlineAllowed , entriesAllowed , assetsAllowed } = getCommandPermissions(sdk, editor);
72
+ const allowedContentTypesFromValidation = getAllowedContentTypesFromValidation(sdk.field.validations);
73
+ const filteredBlockContentTypes = contentTypes.filter((contentType)=>allowedContentTypesFromValidation[BLOCKS.EMBEDDED_ENTRY]?.[contentType.sys.id]);
74
+ const filteredInlineContentTypes = contentTypes.filter((contentType)=>allowedContentTypesFromValidation[INLINES.EMBEDDED_ENTRY]?.[contentType.sys.id]);
75
+ const isBlockContentTypeFiltered = filteredBlockContentTypes.length > 0;
76
+ const isInlineContentTypeFiltered = filteredInlineContentTypes.length > 0;
77
+ const blockContentTypesToUse = isBlockContentTypeFiltered ? filteredBlockContentTypes : contentTypes;
78
+ const inlineContentTypesToUse = isInlineContentTypeFiltered ? filteredInlineContentTypes : contentTypes;
52
79
  const [commands, setCommands] = useState(()=>{
53
80
  const getEmbedEntry = (contentType)=>{
54
81
  return {
@@ -65,16 +92,16 @@ export const useCommands = (sdk, query, editor)=>{
65
92
  }
66
93
  ]);
67
94
  } else {
68
- setCommands(entries.map((entry)=>{
95
+ setCommands(entries.map((ct)=>{
69
96
  return {
70
- id: `${entry.id}-${entry.displayTitle.replace(/\W+/g, '-').toLowerCase()}`,
71
- label: entry.displayTitle,
97
+ id: ct.entry.sys.id,
98
+ label: ct.displayTitle,
72
99
  callback: ()=>{
73
100
  removeCommand(editor);
74
101
  if (editor.selection) {
75
102
  const selection = editor.selection;
76
103
  editor.insertSoftBreak();
77
- insertBlock(editor, BLOCKS.EMBEDDED_ENTRY, entry.entry);
104
+ insertBlock(editor, BLOCKS.EMBEDDED_ENTRY, ct.entry);
78
105
  select(editor, selection);
79
106
  editor.tracking.onCommandPaletteAction('insert', {
80
107
  nodeType: BLOCKS.EMBEDDED_ENTRY
@@ -103,12 +130,12 @@ export const useCommands = (sdk, query, editor)=>{
103
130
  }
104
131
  ]);
105
132
  } else {
106
- setCommands(entries.map((entry)=>{
133
+ setCommands(entries.map((ct)=>{
107
134
  return {
108
- id: `${entry.id}-${entry.displayTitle.replace(/\W+/g, '-').toLowerCase()}`,
109
- label: entry.displayTitle,
135
+ id: ct.entry.sys.id,
136
+ label: ct.displayTitle,
110
137
  callback: ()=>{
111
- const inlineNode = createInlineEntryNode(entry.id);
138
+ const inlineNode = createInlineEntryNode(ct.entry.sys.id);
112
139
  removeCommand(editor);
113
140
  insertNodes(editor, inlineNode);
114
141
  editor.insertText('');
@@ -124,16 +151,18 @@ export const useCommands = (sdk, query, editor)=>{
124
151
  };
125
152
  };
126
153
  const contentTypeCommands = entriesAllowed || inlineAllowed ? contentTypes.map((contentType)=>{
154
+ const blockEmbedAllowed = blockContentTypesToUse.some((ct)=>ct.sys.id === contentType.sys.id);
155
+ const inlineEmbedAllowed = inlineContentTypesToUse.some((ct)=>ct.sys.id === contentType.sys.id);
156
+ const commands = [];
157
+ if (entriesAllowed && blockEmbedAllowed) {
158
+ commands.push(getEmbedEntry(contentType));
159
+ }
160
+ if (inlineAllowed && inlineEmbedAllowed) {
161
+ commands.push(getEmbedInline(contentType));
162
+ }
127
163
  return {
128
164
  group: contentType.name,
129
- commands: entriesAllowed && inlineAllowed ? [
130
- getEmbedEntry(contentType),
131
- getEmbedInline(contentType)
132
- ] : entriesAllowed ? [
133
- getEmbedEntry(contentType)
134
- ] : [
135
- getEmbedInline(contentType)
136
- ]
165
+ commands: commands
137
166
  };
138
167
  }) : [];
139
168
  if (assetsAllowed) {
@@ -156,7 +185,7 @@ export const useCommands = (sdk, query, editor)=>{
156
185
  } else {
157
186
  setCommands(assets.map((asset)=>{
158
187
  return {
159
- id: `${asset.id}-${asset.displayTitle.replace(/\W+/g, '-').toLowerCase()}`,
188
+ id: asset.entity.sys.id,
160
189
  label: asset.displayTitle,
161
190
  thumbnail: asset.thumbnail,
162
191
  callback: ()=>{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/field-editor-rich-text",
3
- "version": "3.12.0",
3
+ "version": "3.12.1",
4
4
  "source": "./src/index.tsx",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -81,5 +81,5 @@
81
81
  "prism-react-renderer": "2.0.5",
82
82
  "react": ">=16.14.0"
83
83
  },
84
- "gitHead": "277d527c724264183021838773059048750f9266"
84
+ "gitHead": "b5c00e8e8a9bc89016de965cffd49f54bf7a6299"
85
85
  }