@contentful/field-editor-shared 1.6.0 → 1.7.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/dist/cjs/LocalePublishingEntityStatusBadge/Banner.js +44 -0
- package/dist/cjs/LocalePublishingEntityStatusBadge/LocalePublishingPopover.js +311 -0
- package/dist/cjs/LocalePublishingEntityStatusBadge/LocalePublishingStatus.js +50 -0
- package/dist/cjs/LocalePublishingEntityStatusBadge/LocalePublishingStatusList.js +85 -0
- package/dist/cjs/LocalePublishingEntityStatusBadge/ScheduledBanner.js +38 -0
- package/dist/cjs/LocalePublishingEntityStatusBadge/index.js +18 -0
- package/dist/cjs/index.js +14 -0
- package/dist/esm/LocalePublishingEntityStatusBadge/Banner.js +29 -0
- package/dist/esm/LocalePublishingEntityStatusBadge/LocalePublishingPopover.js +255 -0
- package/dist/esm/LocalePublishingEntityStatusBadge/LocalePublishingStatus.js +35 -0
- package/dist/esm/LocalePublishingEntityStatusBadge/LocalePublishingStatusList.js +70 -0
- package/dist/esm/LocalePublishingEntityStatusBadge/ScheduledBanner.js +23 -0
- package/dist/esm/LocalePublishingEntityStatusBadge/index.js +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/types/CharCounter.d.ts +2 -2
- package/dist/types/CharValidation.d.ts +2 -2
- package/dist/types/LocalePublishingEntityStatusBadge/Banner.d.ts +5 -0
- package/dist/types/LocalePublishingEntityStatusBadge/LocalePublishingPopover.d.ts +16 -0
- package/dist/types/LocalePublishingEntityStatusBadge/LocalePublishingStatus.d.ts +9 -0
- package/dist/types/LocalePublishingEntityStatusBadge/LocalePublishingStatusList.d.ts +10 -0
- package/dist/types/LocalePublishingEntityStatusBadge/ScheduledBanner.d.ts +8 -0
- package/dist/types/LocalePublishingEntityStatusBadge/index.d.ts +1 -0
- package/dist/types/ModalDialogLauncher.d.ts +1 -1
- package/dist/types/PredefinedValuesError.d.ts +2 -2
- package/dist/types/index.d.ts +1 -0
- package/package.json +7 -5
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "Banner", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return Banner;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _react = _interop_require_default(require("react"));
|
|
12
|
+
const _f36components = require("@contentful/f36-components");
|
|
13
|
+
const _f36tokens = _interop_require_default(require("@contentful/f36-tokens"));
|
|
14
|
+
const _emotion = require("emotion");
|
|
15
|
+
function _interop_require_default(obj) {
|
|
16
|
+
return obj && obj.__esModule ? obj : {
|
|
17
|
+
default: obj
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const styles = {
|
|
21
|
+
content: (0, _emotion.css)({
|
|
22
|
+
display: 'block'
|
|
23
|
+
}),
|
|
24
|
+
banner: (0, _emotion.css)({
|
|
25
|
+
background: _f36tokens.default.gray100,
|
|
26
|
+
padding: _f36tokens.default.spacingXs,
|
|
27
|
+
margin: `${_f36tokens.default.spacingXs} ${_f36tokens.default.spacingS}`,
|
|
28
|
+
borderRadius: _f36tokens.default.borderRadiusSmall
|
|
29
|
+
})
|
|
30
|
+
};
|
|
31
|
+
function Banner({ content, highlight }) {
|
|
32
|
+
return _react.default.createElement("div", {
|
|
33
|
+
className: styles.banner
|
|
34
|
+
}, _react.default.createElement(_f36components.Text, {
|
|
35
|
+
fontSize: "fontSizeS",
|
|
36
|
+
fontColor: "gray700",
|
|
37
|
+
className: styles.content
|
|
38
|
+
}, content), highlight && _react.default.createElement(_f36components.Text, {
|
|
39
|
+
fontSize: "fontSizeS",
|
|
40
|
+
fontColor: "gray700",
|
|
41
|
+
as: "strong",
|
|
42
|
+
fontWeight: "fontWeightDemiBold"
|
|
43
|
+
}, highlight));
|
|
44
|
+
}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "LocalePublishingPopover", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return LocalePublishingPopover;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _react = _interop_require_wildcard(require("react"));
|
|
12
|
+
const _f36components = require("@contentful/f36-components");
|
|
13
|
+
const _f36tokens = _interop_require_default(require("@contentful/f36-tokens"));
|
|
14
|
+
const _emotion = require("emotion");
|
|
15
|
+
const _entityHelpers = _interop_require_wildcard(require("../utils/entityHelpers"));
|
|
16
|
+
const _LocalePublishingStatusList = require("./LocalePublishingStatusList");
|
|
17
|
+
const _ScheduledBanner = require("./ScheduledBanner");
|
|
18
|
+
function _interop_require_default(obj) {
|
|
19
|
+
return obj && obj.__esModule ? obj : {
|
|
20
|
+
default: obj
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
24
|
+
if (typeof WeakMap !== "function") return null;
|
|
25
|
+
var cacheBabelInterop = new WeakMap();
|
|
26
|
+
var cacheNodeInterop = new WeakMap();
|
|
27
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
28
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
29
|
+
})(nodeInterop);
|
|
30
|
+
}
|
|
31
|
+
function _interop_require_wildcard(obj, nodeInterop) {
|
|
32
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
33
|
+
return obj;
|
|
34
|
+
}
|
|
35
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
36
|
+
return {
|
|
37
|
+
default: obj
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
41
|
+
if (cache && cache.has(obj)) {
|
|
42
|
+
return cache.get(obj);
|
|
43
|
+
}
|
|
44
|
+
var newObj = {
|
|
45
|
+
__proto__: null
|
|
46
|
+
};
|
|
47
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
48
|
+
for(var key in obj){
|
|
49
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
50
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
51
|
+
if (desc && (desc.get || desc.set)) {
|
|
52
|
+
Object.defineProperty(newObj, key, desc);
|
|
53
|
+
} else {
|
|
54
|
+
newObj[key] = obj[key];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
newObj.default = obj;
|
|
59
|
+
if (cache) {
|
|
60
|
+
cache.set(obj, newObj);
|
|
61
|
+
}
|
|
62
|
+
return newObj;
|
|
63
|
+
}
|
|
64
|
+
const colors = {
|
|
65
|
+
published: {
|
|
66
|
+
default: _f36tokens.default.green300,
|
|
67
|
+
hover: _f36tokens.default.green400,
|
|
68
|
+
icon: _f36tokens.default.green400
|
|
69
|
+
},
|
|
70
|
+
draft: {
|
|
71
|
+
default: _f36tokens.default.orange300,
|
|
72
|
+
hover: _f36tokens.default.orange400,
|
|
73
|
+
icon: _f36tokens.default.orange400
|
|
74
|
+
},
|
|
75
|
+
changed: {
|
|
76
|
+
default: _f36tokens.default.blue300,
|
|
77
|
+
hover: _f36tokens.default.blue400,
|
|
78
|
+
icon: _f36tokens.default.blue400
|
|
79
|
+
},
|
|
80
|
+
archived: {
|
|
81
|
+
default: _f36tokens.default.gray300,
|
|
82
|
+
hover: _f36tokens.default.gray400,
|
|
83
|
+
icon: _f36tokens.default.gray400
|
|
84
|
+
},
|
|
85
|
+
deleted: {
|
|
86
|
+
default: _f36tokens.default.red300,
|
|
87
|
+
hover: _f36tokens.default.red400,
|
|
88
|
+
icon: _f36tokens.default.red400
|
|
89
|
+
},
|
|
90
|
+
new: {
|
|
91
|
+
default: _f36tokens.default.blue600,
|
|
92
|
+
hover: _f36tokens.default.blue700,
|
|
93
|
+
icon: _f36tokens.default.blue700
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const getColor = ({ secondary, tertiary, isHover })=>{
|
|
97
|
+
const status = secondary || tertiary;
|
|
98
|
+
return isHover ? colors[status]?.hover : colors[status]?.default;
|
|
99
|
+
};
|
|
100
|
+
const generateDynamicStyles = (status)=>{
|
|
101
|
+
const wrapperClass = (0, _emotion.css)({
|
|
102
|
+
'& svg[data-status="secondary"]': {
|
|
103
|
+
fill: getColor({
|
|
104
|
+
secondary: status?.secondary,
|
|
105
|
+
isHover: false
|
|
106
|
+
})
|
|
107
|
+
},
|
|
108
|
+
'& svg[data-status="tertiary"]': {
|
|
109
|
+
fill: getColor({
|
|
110
|
+
tertiary: status?.tertiary,
|
|
111
|
+
isHover: false
|
|
112
|
+
})
|
|
113
|
+
},
|
|
114
|
+
'&:hover svg[data-status="secondary"]': {
|
|
115
|
+
fill: getColor({
|
|
116
|
+
secondary: status?.secondary,
|
|
117
|
+
isHover: true
|
|
118
|
+
})
|
|
119
|
+
},
|
|
120
|
+
'&:hover svg[data-status="tertiary"]': {
|
|
121
|
+
fill: getColor({
|
|
122
|
+
tertiary: status?.tertiary,
|
|
123
|
+
isHover: true
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
return wrapperClass;
|
|
128
|
+
};
|
|
129
|
+
const getIconColor = (status)=>colors[status].icon;
|
|
130
|
+
const styles = {
|
|
131
|
+
badge: (0, _emotion.css)({
|
|
132
|
+
'&:focus': {
|
|
133
|
+
outline: 'none',
|
|
134
|
+
boxShadow: `inset ${_f36tokens.default.glowPrimary}`
|
|
135
|
+
}
|
|
136
|
+
}),
|
|
137
|
+
wrapper: (0, _emotion.css)({
|
|
138
|
+
'& svg': {
|
|
139
|
+
transition: 'fill 0.2s ease-in-out'
|
|
140
|
+
},
|
|
141
|
+
'& svg[data-status="tertiary"]': {
|
|
142
|
+
marginLeft: '-1px'
|
|
143
|
+
}
|
|
144
|
+
}),
|
|
145
|
+
popoverContent: (0, _emotion.css)({
|
|
146
|
+
maxWidth: '336px',
|
|
147
|
+
maxHeight: '368px',
|
|
148
|
+
overflowY: 'auto',
|
|
149
|
+
padding: `${_f36tokens.default.spacing2Xs} 0`
|
|
150
|
+
})
|
|
151
|
+
};
|
|
152
|
+
const ArrowDownIcon = (0, _f36components.generateIcon)({
|
|
153
|
+
name: 'ArrowDownIcon',
|
|
154
|
+
viewBox: '0 0 12 20',
|
|
155
|
+
path: _react.default.createElement("path", {
|
|
156
|
+
d: "M3.03076 8C2.20109 8 1.73228 8.95209 2.23814 9.60971L5.20727 13.4696C5.60757 13.99 6.39223 13.99 6.79252 13.4696L9.76166 9.60971C10.2675 8.95209 9.79871 8 8.96904 8L3.03076 8Z",
|
|
157
|
+
fill: "currentColor"
|
|
158
|
+
})
|
|
159
|
+
});
|
|
160
|
+
const determineBadgeStatus = (localesStatusMap, activeLocales)=>{
|
|
161
|
+
if (!localesStatusMap) return;
|
|
162
|
+
if (localesStatusMap.size === 1) return {
|
|
163
|
+
primary: [
|
|
164
|
+
...localesStatusMap.values()
|
|
165
|
+
][0].status
|
|
166
|
+
};
|
|
167
|
+
let draftCount = 0, publishedCount = 0, changedCount = 0;
|
|
168
|
+
for (const localeStatus of localesStatusMap.values()){
|
|
169
|
+
if (!activeLocales || activeLocales.find((selectedLocale)=>selectedLocale.code === localeStatus.locale.code)) {
|
|
170
|
+
switch(localeStatus.status){
|
|
171
|
+
case 'changed':
|
|
172
|
+
changedCount += 1;
|
|
173
|
+
break;
|
|
174
|
+
case 'draft':
|
|
175
|
+
draftCount += 1;
|
|
176
|
+
break;
|
|
177
|
+
case 'published':
|
|
178
|
+
publishedCount += 1;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const hasChanged = changedCount > 0;
|
|
184
|
+
const hasPublished = publishedCount > 0;
|
|
185
|
+
const hasDraft = draftCount > 0;
|
|
186
|
+
switch(true){
|
|
187
|
+
case hasChanged && hasPublished && hasDraft:
|
|
188
|
+
return {
|
|
189
|
+
primary: 'changed',
|
|
190
|
+
secondary: 'published',
|
|
191
|
+
tertiary: 'draft'
|
|
192
|
+
};
|
|
193
|
+
case hasChanged && hasPublished:
|
|
194
|
+
return {
|
|
195
|
+
primary: 'changed',
|
|
196
|
+
secondary: 'published'
|
|
197
|
+
};
|
|
198
|
+
case hasChanged && hasDraft:
|
|
199
|
+
return {
|
|
200
|
+
primary: 'changed',
|
|
201
|
+
secondary: 'draft'
|
|
202
|
+
};
|
|
203
|
+
case hasPublished && hasDraft:
|
|
204
|
+
return {
|
|
205
|
+
primary: 'published',
|
|
206
|
+
secondary: 'draft'
|
|
207
|
+
};
|
|
208
|
+
case hasChanged:
|
|
209
|
+
return {
|
|
210
|
+
primary: 'changed',
|
|
211
|
+
secondary: 'changed'
|
|
212
|
+
};
|
|
213
|
+
case hasPublished:
|
|
214
|
+
return {
|
|
215
|
+
primary: 'published',
|
|
216
|
+
secondary: 'published'
|
|
217
|
+
};
|
|
218
|
+
default:
|
|
219
|
+
return {
|
|
220
|
+
primary: 'draft',
|
|
221
|
+
secondary: 'draft'
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
function LocalePublishingPopover({ entity, jobs, isScheduled, localesStatusMap, activeLocales }) {
|
|
226
|
+
const [isOpen, setIsOpen] = (0, _react.useState)(false);
|
|
227
|
+
const timeoutRef = (0, _react.useRef)();
|
|
228
|
+
const onMouseEnter = (0, _react.useCallback)(()=>{
|
|
229
|
+
clearTimeout(timeoutRef.current);
|
|
230
|
+
timeoutRef.current = setTimeout(()=>{
|
|
231
|
+
setIsOpen(true);
|
|
232
|
+
}, 300);
|
|
233
|
+
}, []);
|
|
234
|
+
const onMouseLeave = (0, _react.useCallback)(()=>{
|
|
235
|
+
clearTimeout(timeoutRef.current);
|
|
236
|
+
timeoutRef.current = setTimeout(()=>{
|
|
237
|
+
setIsOpen(false);
|
|
238
|
+
}, 300);
|
|
239
|
+
}, []);
|
|
240
|
+
const entityStatus = _entityHelpers.getEntityStatus(entity.sys, activeLocales?.map((locale)=>locale.code));
|
|
241
|
+
if ([
|
|
242
|
+
'archived',
|
|
243
|
+
'deleted'
|
|
244
|
+
].includes(entityStatus)) {
|
|
245
|
+
return _react.default.createElement(_f36components.EntityStatusBadge, {
|
|
246
|
+
className: styles.badge,
|
|
247
|
+
testId: "entity-state",
|
|
248
|
+
entityStatus: entityStatus,
|
|
249
|
+
tabIndex: 0,
|
|
250
|
+
isScheduled: isScheduled
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
const status = determineBadgeStatus(localesStatusMap, activeLocales);
|
|
254
|
+
const ariaLabel = status && status.secondary ? 'Multiple statuses' : status?.primary;
|
|
255
|
+
const wrapperClass = generateDynamicStyles(status);
|
|
256
|
+
return _react.default.createElement(_f36components.Popover, {
|
|
257
|
+
isOpen: localesStatusMap && isOpen,
|
|
258
|
+
onClose: ()=>setIsOpen(false),
|
|
259
|
+
autoFocus: false,
|
|
260
|
+
placement: "bottom-end"
|
|
261
|
+
}, _react.default.createElement(_f36components.Popover.Trigger, null, _react.default.createElement(_f36components.Flex, {
|
|
262
|
+
"aria-label": ariaLabel,
|
|
263
|
+
alignItems: "center",
|
|
264
|
+
className: (0, _emotion.cx)(styles.wrapper, wrapperClass)
|
|
265
|
+
}, _react.default.createElement(_f36components.EntityStatusBadge, {
|
|
266
|
+
className: styles.badge,
|
|
267
|
+
testId: "entity-state",
|
|
268
|
+
entityStatus: entityStatus,
|
|
269
|
+
tabIndex: 0,
|
|
270
|
+
onFocus: ()=>setIsOpen(true),
|
|
271
|
+
onBlur: ()=>setIsOpen(false),
|
|
272
|
+
endIcon: _react.default.createElement(ArrowDownIcon, {
|
|
273
|
+
color: getIconColor(entityStatus)
|
|
274
|
+
}),
|
|
275
|
+
onMouseOver: onMouseEnter,
|
|
276
|
+
isScheduled: isScheduled,
|
|
277
|
+
onMouseEnter: onMouseEnter,
|
|
278
|
+
onMouseLeave: onMouseLeave
|
|
279
|
+
}), status?.secondary && _react.default.createElement("svg", {
|
|
280
|
+
"data-status": "secondary",
|
|
281
|
+
"aria-hidden": "true",
|
|
282
|
+
width: "4",
|
|
283
|
+
height: "18",
|
|
284
|
+
viewBox: "0 0 4 18"
|
|
285
|
+
}, _react.default.createElement("path", {
|
|
286
|
+
fillRule: "evenodd",
|
|
287
|
+
clipRule: "evenodd",
|
|
288
|
+
d: "M1.45942e-06 18H0.00296171C2.2121 18 4.00296 16.2091 4.00296 14V4C4.00296 1.79086 2.2121 0 0.00295914 0H0C0.830421 0.732944 1.35418 1.80531 1.35418 3V15C1.35418 16.1947 0.830422 17.2671 1.45942e-06 18Z"
|
|
289
|
+
})), status?.tertiary && _react.default.createElement("svg", {
|
|
290
|
+
"data-status": "tertiary",
|
|
291
|
+
"aria-hidden": "true",
|
|
292
|
+
width: "5",
|
|
293
|
+
height: "16",
|
|
294
|
+
viewBox: "0 0 5 16"
|
|
295
|
+
}, _react.default.createElement("path", {
|
|
296
|
+
fillRule: "evenodd",
|
|
297
|
+
clipRule: "evenodd",
|
|
298
|
+
d: "M0.673828 16.0055H0.675391C2.88453 16.0055 4.67539 14.2146 4.67539 12.0055V3.9955C4.67539 1.88216 3.03648 0.151608 0.960312 0.00549316C1.78382 0.738158 2.30257 1.80597 2.30257 2.99493V12.7838C2.30257 14.1053 1.66172 15.2771 0.673828 16.0055Z"
|
|
299
|
+
})))), _react.default.createElement(_f36components.Popover.Content, {
|
|
300
|
+
className: styles.popoverContent,
|
|
301
|
+
onMouseEnter: onMouseEnter,
|
|
302
|
+
onMouseLeave: onMouseLeave
|
|
303
|
+
}, !!localesStatusMap && _react.default.createElement(_react.default.Fragment, null, _react.default.createElement(_ScheduledBanner.ScheduledBanner, {
|
|
304
|
+
entityId: entity.sys.id,
|
|
305
|
+
jobs: jobs
|
|
306
|
+
}), _react.default.createElement(_LocalePublishingStatusList.LocalePublishingStatusList, {
|
|
307
|
+
isScheduled: isScheduled,
|
|
308
|
+
statusMap: localesStatusMap,
|
|
309
|
+
activeLocales: activeLocales
|
|
310
|
+
}))));
|
|
311
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "LocalePublishingStatus", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return LocalePublishingStatus;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _react = _interop_require_default(require("react"));
|
|
12
|
+
const _f36components = require("@contentful/f36-components");
|
|
13
|
+
const _f36tokens = _interop_require_default(require("@contentful/f36-tokens"));
|
|
14
|
+
const _emotion = require("emotion");
|
|
15
|
+
function _interop_require_default(obj) {
|
|
16
|
+
return obj && obj.__esModule ? obj : {
|
|
17
|
+
default: obj
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const styles = {
|
|
21
|
+
locale: (0, _emotion.css)({
|
|
22
|
+
textOverflow: 'ellipsis',
|
|
23
|
+
overflow: 'hidden',
|
|
24
|
+
whiteSpace: 'nowrap'
|
|
25
|
+
}),
|
|
26
|
+
status: (0, _emotion.css)({
|
|
27
|
+
flexShrink: 0
|
|
28
|
+
}),
|
|
29
|
+
localePublishStatus: (0, _emotion.css)({
|
|
30
|
+
display: 'flex',
|
|
31
|
+
gap: _f36tokens.default.spacingXs,
|
|
32
|
+
justifyContent: 'space-between',
|
|
33
|
+
padding: `${_f36tokens.default.spacingXs} ${_f36tokens.default.spacingS}`
|
|
34
|
+
})
|
|
35
|
+
};
|
|
36
|
+
function LocalePublishingStatus({ locale, status, isScheduled }) {
|
|
37
|
+
return _react.default.createElement("div", {
|
|
38
|
+
className: styles.localePublishStatus,
|
|
39
|
+
"data-test-id": "locale-publishing-status"
|
|
40
|
+
}, _react.default.createElement(_f36components.Text, {
|
|
41
|
+
className: styles.locale,
|
|
42
|
+
fontColor: "gray700"
|
|
43
|
+
}, locale.name, ' ', _react.default.createElement(_f36components.Text, {
|
|
44
|
+
fontColor: "gray500"
|
|
45
|
+
}, "(", locale.code, ")", locale.default && ' | default')), _react.default.createElement(_f36components.EntityStatusBadge, {
|
|
46
|
+
entityStatus: status,
|
|
47
|
+
isScheduled: isScheduled,
|
|
48
|
+
className: styles.status
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "LocalePublishingStatusList", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return LocalePublishingStatusList;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _react = _interop_require_default(require("react"));
|
|
12
|
+
const _f36components = require("@contentful/f36-components");
|
|
13
|
+
const _lodash = require("lodash");
|
|
14
|
+
const _Banner = require("./Banner");
|
|
15
|
+
const _LocalePublishingStatus = require("./LocalePublishingStatus");
|
|
16
|
+
function _interop_require_default(obj) {
|
|
17
|
+
return obj && obj.__esModule ? obj : {
|
|
18
|
+
default: obj
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function groupAndSortLocales(entries, activeLocales) {
|
|
22
|
+
const { selected, nonSelected } = entries.reduce((prev, [localeCode, localeStatusType])=>{
|
|
23
|
+
return activeLocales?.some((sl)=>sl.code === localeCode) ? {
|
|
24
|
+
...prev,
|
|
25
|
+
selected: [
|
|
26
|
+
...prev.selected,
|
|
27
|
+
localeStatusType
|
|
28
|
+
]
|
|
29
|
+
} : {
|
|
30
|
+
...prev,
|
|
31
|
+
nonSelected: [
|
|
32
|
+
...prev.nonSelected,
|
|
33
|
+
localeStatusType
|
|
34
|
+
]
|
|
35
|
+
};
|
|
36
|
+
}, {
|
|
37
|
+
selected: [],
|
|
38
|
+
nonSelected: []
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
selected: (0, _lodash.sortBy)(selected, 'locale.name'),
|
|
42
|
+
nonSelected: nonSelected.sort((a, b)=>{
|
|
43
|
+
if (a.status === b.status) {
|
|
44
|
+
return a.locale.name.localeCompare(b.locale.name);
|
|
45
|
+
}
|
|
46
|
+
if (a.status === 'changed' || a.status === 'published' && b.status === 'draft') {
|
|
47
|
+
return -1;
|
|
48
|
+
}
|
|
49
|
+
return 1;
|
|
50
|
+
})
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function LocalePublishingStatusList({ isScheduled, statusMap, activeLocales }) {
|
|
54
|
+
const entries = [
|
|
55
|
+
...statusMap.entries()
|
|
56
|
+
];
|
|
57
|
+
const counters = entries.reduce((prev, [, { status }])=>({
|
|
58
|
+
changed: prev.changed + (status === 'changed' ? 1 : 0),
|
|
59
|
+
published: prev.published + (status === 'published' ? 1 : 0),
|
|
60
|
+
draft: prev.draft + (status === 'draft' ? 1 : 0)
|
|
61
|
+
}), {
|
|
62
|
+
published: 0,
|
|
63
|
+
changed: 0,
|
|
64
|
+
draft: 0
|
|
65
|
+
});
|
|
66
|
+
const { selected, nonSelected } = groupAndSortLocales(entries, activeLocales);
|
|
67
|
+
return _react.default.createElement(_react.default.Fragment, null, _react.default.createElement(_Banner.Banner, {
|
|
68
|
+
content: "The entry has locales with the following statuses:",
|
|
69
|
+
highlight: `${counters.changed} changed, ${counters.published} published, ${counters.draft} draft`
|
|
70
|
+
}), _react.default.createElement("div", {
|
|
71
|
+
"data-test-id": "locale-publishing-selected"
|
|
72
|
+
}, _react.default.createElement(_f36components.MenuSectionTitle, null, "Locales in the entry editor:"), selected.map(({ locale, status })=>_react.default.createElement(_LocalePublishingStatus.LocalePublishingStatus, {
|
|
73
|
+
key: `selected-${locale.code}`,
|
|
74
|
+
status: status,
|
|
75
|
+
locale: locale,
|
|
76
|
+
isScheduled: isScheduled
|
|
77
|
+
}))), nonSelected.length > 0 && _react.default.createElement("div", {
|
|
78
|
+
"data-test-id": "locale-publishing-others"
|
|
79
|
+
}, _react.default.createElement(_f36components.MenuSectionTitle, null, "Other locales:"), nonSelected.map(({ locale, status })=>_react.default.createElement(_LocalePublishingStatus.LocalePublishingStatus, {
|
|
80
|
+
key: `others-${locale.code}`,
|
|
81
|
+
status: status,
|
|
82
|
+
locale: locale,
|
|
83
|
+
isScheduled: isScheduled
|
|
84
|
+
}))));
|
|
85
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "ScheduledBanner", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return ScheduledBanner;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _react = _interop_require_default(require("react"));
|
|
12
|
+
const _f36components = require("@contentful/f36-components");
|
|
13
|
+
const _lodash = require("lodash");
|
|
14
|
+
const _Banner = require("./Banner");
|
|
15
|
+
function _interop_require_default(obj) {
|
|
16
|
+
return obj && obj.__esModule ? obj : {
|
|
17
|
+
default: obj
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function ScheduledBanner({ entityId, jobs }) {
|
|
21
|
+
const scheduledJobs = jobs.filter((job)=>job.entity.sys.id === entityId);
|
|
22
|
+
const sortedScheduledJobs = (0, _lodash.orderBy)(scheduledJobs, [
|
|
23
|
+
'scheduledFor.datetime'
|
|
24
|
+
], [
|
|
25
|
+
'asc'
|
|
26
|
+
]);
|
|
27
|
+
const scheduledAction = sortedScheduledJobs[0];
|
|
28
|
+
if (!scheduledAction) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const pendingJobsCount = scheduledJobs.length - 1;
|
|
32
|
+
const lowerCaseAction = scheduledAction.action.toLowerCase();
|
|
33
|
+
const action = lowerCaseAction === ScheduledActionTypes['patch+publish'] ? `${lowerCaseAction} (via Schedule Series)` : lowerCaseAction;
|
|
34
|
+
return _react.default.createElement(_Banner.Banner, {
|
|
35
|
+
content: `All locales are scheduled to ${action} on:`,
|
|
36
|
+
highlight: `${(0, _f36components.formatDateAndTime)(scheduledAction.scheduledFor.datetime)}${pendingJobsCount > 0 ? ` [and +${pendingJobsCount} more]` : ''}`
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
_export_star(require("./LocalePublishingPopover"), exports);
|
|
6
|
+
function _export_star(from, to) {
|
|
7
|
+
Object.keys(from).forEach(function(k) {
|
|
8
|
+
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
9
|
+
Object.defineProperty(to, k, {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get: function() {
|
|
12
|
+
return from[k];
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
return from;
|
|
18
|
+
}
|
package/dist/cjs/index.js
CHANGED
|
@@ -111,9 +111,23 @@ const _PredefinedValuesError = require("./PredefinedValuesError");
|
|
|
111
111
|
const _typesEntity = require("./typesEntity");
|
|
112
112
|
const _isValidImage = require("./utils/isValidImage");
|
|
113
113
|
const _shortenStorageUnit = require("./utils/shortenStorageUnit");
|
|
114
|
+
_export_star(require("./LocalePublishingEntityStatusBadge"), exports);
|
|
114
115
|
const _ModalDialogLauncher = _interop_require_wildcard(require("./ModalDialogLauncher"));
|
|
115
116
|
const _constraints = _interop_require_wildcard(require("./utils/constraints"));
|
|
116
117
|
const _entityHelpers = _interop_require_wildcard(require("./utils/entityHelpers"));
|
|
118
|
+
function _export_star(from, to) {
|
|
119
|
+
Object.keys(from).forEach(function(k) {
|
|
120
|
+
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
121
|
+
Object.defineProperty(to, k, {
|
|
122
|
+
enumerable: true,
|
|
123
|
+
get: function() {
|
|
124
|
+
return from[k];
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
return from;
|
|
130
|
+
}
|
|
117
131
|
function _getRequireWildcardCache(nodeInterop) {
|
|
118
132
|
if (typeof WeakMap !== "function") return null;
|
|
119
133
|
var cacheBabelInterop = new WeakMap();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text } from '@contentful/f36-components';
|
|
3
|
+
import tokens from '@contentful/f36-tokens';
|
|
4
|
+
import { css } from 'emotion';
|
|
5
|
+
const styles = {
|
|
6
|
+
content: css({
|
|
7
|
+
display: 'block'
|
|
8
|
+
}),
|
|
9
|
+
banner: css({
|
|
10
|
+
background: tokens.gray100,
|
|
11
|
+
padding: tokens.spacingXs,
|
|
12
|
+
margin: `${tokens.spacingXs} ${tokens.spacingS}`,
|
|
13
|
+
borderRadius: tokens.borderRadiusSmall
|
|
14
|
+
})
|
|
15
|
+
};
|
|
16
|
+
export function Banner({ content, highlight }) {
|
|
17
|
+
return React.createElement("div", {
|
|
18
|
+
className: styles.banner
|
|
19
|
+
}, React.createElement(Text, {
|
|
20
|
+
fontSize: "fontSizeS",
|
|
21
|
+
fontColor: "gray700",
|
|
22
|
+
className: styles.content
|
|
23
|
+
}, content), highlight && React.createElement(Text, {
|
|
24
|
+
fontSize: "fontSizeS",
|
|
25
|
+
fontColor: "gray700",
|
|
26
|
+
as: "strong",
|
|
27
|
+
fontWeight: "fontWeightDemiBold"
|
|
28
|
+
}, highlight));
|
|
29
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import React, { useCallback, useRef, useState } from 'react';
|
|
2
|
+
import { EntityStatusBadge, Flex, Popover, generateIcon } from '@contentful/f36-components';
|
|
3
|
+
import tokens from '@contentful/f36-tokens';
|
|
4
|
+
import { cx, css } from 'emotion';
|
|
5
|
+
import * as entityHelpers from '../utils/entityHelpers';
|
|
6
|
+
import { LocalePublishingStatusList } from './LocalePublishingStatusList';
|
|
7
|
+
import { ScheduledBanner } from './ScheduledBanner';
|
|
8
|
+
const colors = {
|
|
9
|
+
published: {
|
|
10
|
+
default: tokens.green300,
|
|
11
|
+
hover: tokens.green400,
|
|
12
|
+
icon: tokens.green400
|
|
13
|
+
},
|
|
14
|
+
draft: {
|
|
15
|
+
default: tokens.orange300,
|
|
16
|
+
hover: tokens.orange400,
|
|
17
|
+
icon: tokens.orange400
|
|
18
|
+
},
|
|
19
|
+
changed: {
|
|
20
|
+
default: tokens.blue300,
|
|
21
|
+
hover: tokens.blue400,
|
|
22
|
+
icon: tokens.blue400
|
|
23
|
+
},
|
|
24
|
+
archived: {
|
|
25
|
+
default: tokens.gray300,
|
|
26
|
+
hover: tokens.gray400,
|
|
27
|
+
icon: tokens.gray400
|
|
28
|
+
},
|
|
29
|
+
deleted: {
|
|
30
|
+
default: tokens.red300,
|
|
31
|
+
hover: tokens.red400,
|
|
32
|
+
icon: tokens.red400
|
|
33
|
+
},
|
|
34
|
+
new: {
|
|
35
|
+
default: tokens.blue600,
|
|
36
|
+
hover: tokens.blue700,
|
|
37
|
+
icon: tokens.blue700
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const getColor = ({ secondary, tertiary, isHover })=>{
|
|
41
|
+
const status = secondary || tertiary;
|
|
42
|
+
return isHover ? colors[status]?.hover : colors[status]?.default;
|
|
43
|
+
};
|
|
44
|
+
const generateDynamicStyles = (status)=>{
|
|
45
|
+
const wrapperClass = css({
|
|
46
|
+
'& svg[data-status="secondary"]': {
|
|
47
|
+
fill: getColor({
|
|
48
|
+
secondary: status?.secondary,
|
|
49
|
+
isHover: false
|
|
50
|
+
})
|
|
51
|
+
},
|
|
52
|
+
'& svg[data-status="tertiary"]': {
|
|
53
|
+
fill: getColor({
|
|
54
|
+
tertiary: status?.tertiary,
|
|
55
|
+
isHover: false
|
|
56
|
+
})
|
|
57
|
+
},
|
|
58
|
+
'&:hover svg[data-status="secondary"]': {
|
|
59
|
+
fill: getColor({
|
|
60
|
+
secondary: status?.secondary,
|
|
61
|
+
isHover: true
|
|
62
|
+
})
|
|
63
|
+
},
|
|
64
|
+
'&:hover svg[data-status="tertiary"]': {
|
|
65
|
+
fill: getColor({
|
|
66
|
+
tertiary: status?.tertiary,
|
|
67
|
+
isHover: true
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return wrapperClass;
|
|
72
|
+
};
|
|
73
|
+
const getIconColor = (status)=>colors[status].icon;
|
|
74
|
+
const styles = {
|
|
75
|
+
badge: css({
|
|
76
|
+
'&:focus': {
|
|
77
|
+
outline: 'none',
|
|
78
|
+
boxShadow: `inset ${tokens.glowPrimary}`
|
|
79
|
+
}
|
|
80
|
+
}),
|
|
81
|
+
wrapper: css({
|
|
82
|
+
'& svg': {
|
|
83
|
+
transition: 'fill 0.2s ease-in-out'
|
|
84
|
+
},
|
|
85
|
+
'& svg[data-status="tertiary"]': {
|
|
86
|
+
marginLeft: '-1px'
|
|
87
|
+
}
|
|
88
|
+
}),
|
|
89
|
+
popoverContent: css({
|
|
90
|
+
maxWidth: '336px',
|
|
91
|
+
maxHeight: '368px',
|
|
92
|
+
overflowY: 'auto',
|
|
93
|
+
padding: `${tokens.spacing2Xs} 0`
|
|
94
|
+
})
|
|
95
|
+
};
|
|
96
|
+
const ArrowDownIcon = generateIcon({
|
|
97
|
+
name: 'ArrowDownIcon',
|
|
98
|
+
viewBox: '0 0 12 20',
|
|
99
|
+
path: React.createElement("path", {
|
|
100
|
+
d: "M3.03076 8C2.20109 8 1.73228 8.95209 2.23814 9.60971L5.20727 13.4696C5.60757 13.99 6.39223 13.99 6.79252 13.4696L9.76166 9.60971C10.2675 8.95209 9.79871 8 8.96904 8L3.03076 8Z",
|
|
101
|
+
fill: "currentColor"
|
|
102
|
+
})
|
|
103
|
+
});
|
|
104
|
+
const determineBadgeStatus = (localesStatusMap, activeLocales)=>{
|
|
105
|
+
if (!localesStatusMap) return;
|
|
106
|
+
if (localesStatusMap.size === 1) return {
|
|
107
|
+
primary: [
|
|
108
|
+
...localesStatusMap.values()
|
|
109
|
+
][0].status
|
|
110
|
+
};
|
|
111
|
+
let draftCount = 0, publishedCount = 0, changedCount = 0;
|
|
112
|
+
for (const localeStatus of localesStatusMap.values()){
|
|
113
|
+
if (!activeLocales || activeLocales.find((selectedLocale)=>selectedLocale.code === localeStatus.locale.code)) {
|
|
114
|
+
switch(localeStatus.status){
|
|
115
|
+
case 'changed':
|
|
116
|
+
changedCount += 1;
|
|
117
|
+
break;
|
|
118
|
+
case 'draft':
|
|
119
|
+
draftCount += 1;
|
|
120
|
+
break;
|
|
121
|
+
case 'published':
|
|
122
|
+
publishedCount += 1;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const hasChanged = changedCount > 0;
|
|
128
|
+
const hasPublished = publishedCount > 0;
|
|
129
|
+
const hasDraft = draftCount > 0;
|
|
130
|
+
switch(true){
|
|
131
|
+
case hasChanged && hasPublished && hasDraft:
|
|
132
|
+
return {
|
|
133
|
+
primary: 'changed',
|
|
134
|
+
secondary: 'published',
|
|
135
|
+
tertiary: 'draft'
|
|
136
|
+
};
|
|
137
|
+
case hasChanged && hasPublished:
|
|
138
|
+
return {
|
|
139
|
+
primary: 'changed',
|
|
140
|
+
secondary: 'published'
|
|
141
|
+
};
|
|
142
|
+
case hasChanged && hasDraft:
|
|
143
|
+
return {
|
|
144
|
+
primary: 'changed',
|
|
145
|
+
secondary: 'draft'
|
|
146
|
+
};
|
|
147
|
+
case hasPublished && hasDraft:
|
|
148
|
+
return {
|
|
149
|
+
primary: 'published',
|
|
150
|
+
secondary: 'draft'
|
|
151
|
+
};
|
|
152
|
+
case hasChanged:
|
|
153
|
+
return {
|
|
154
|
+
primary: 'changed',
|
|
155
|
+
secondary: 'changed'
|
|
156
|
+
};
|
|
157
|
+
case hasPublished:
|
|
158
|
+
return {
|
|
159
|
+
primary: 'published',
|
|
160
|
+
secondary: 'published'
|
|
161
|
+
};
|
|
162
|
+
default:
|
|
163
|
+
return {
|
|
164
|
+
primary: 'draft',
|
|
165
|
+
secondary: 'draft'
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
export function LocalePublishingPopover({ entity, jobs, isScheduled, localesStatusMap, activeLocales }) {
|
|
170
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
171
|
+
const timeoutRef = useRef();
|
|
172
|
+
const onMouseEnter = useCallback(()=>{
|
|
173
|
+
clearTimeout(timeoutRef.current);
|
|
174
|
+
timeoutRef.current = setTimeout(()=>{
|
|
175
|
+
setIsOpen(true);
|
|
176
|
+
}, 300);
|
|
177
|
+
}, []);
|
|
178
|
+
const onMouseLeave = useCallback(()=>{
|
|
179
|
+
clearTimeout(timeoutRef.current);
|
|
180
|
+
timeoutRef.current = setTimeout(()=>{
|
|
181
|
+
setIsOpen(false);
|
|
182
|
+
}, 300);
|
|
183
|
+
}, []);
|
|
184
|
+
const entityStatus = entityHelpers.getEntityStatus(entity.sys, activeLocales?.map((locale)=>locale.code));
|
|
185
|
+
if ([
|
|
186
|
+
'archived',
|
|
187
|
+
'deleted'
|
|
188
|
+
].includes(entityStatus)) {
|
|
189
|
+
return React.createElement(EntityStatusBadge, {
|
|
190
|
+
className: styles.badge,
|
|
191
|
+
testId: "entity-state",
|
|
192
|
+
entityStatus: entityStatus,
|
|
193
|
+
tabIndex: 0,
|
|
194
|
+
isScheduled: isScheduled
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
const status = determineBadgeStatus(localesStatusMap, activeLocales);
|
|
198
|
+
const ariaLabel = status && status.secondary ? 'Multiple statuses' : status?.primary;
|
|
199
|
+
const wrapperClass = generateDynamicStyles(status);
|
|
200
|
+
return React.createElement(Popover, {
|
|
201
|
+
isOpen: localesStatusMap && isOpen,
|
|
202
|
+
onClose: ()=>setIsOpen(false),
|
|
203
|
+
autoFocus: false,
|
|
204
|
+
placement: "bottom-end"
|
|
205
|
+
}, React.createElement(Popover.Trigger, null, React.createElement(Flex, {
|
|
206
|
+
"aria-label": ariaLabel,
|
|
207
|
+
alignItems: "center",
|
|
208
|
+
className: cx(styles.wrapper, wrapperClass)
|
|
209
|
+
}, React.createElement(EntityStatusBadge, {
|
|
210
|
+
className: styles.badge,
|
|
211
|
+
testId: "entity-state",
|
|
212
|
+
entityStatus: entityStatus,
|
|
213
|
+
tabIndex: 0,
|
|
214
|
+
onFocus: ()=>setIsOpen(true),
|
|
215
|
+
onBlur: ()=>setIsOpen(false),
|
|
216
|
+
endIcon: React.createElement(ArrowDownIcon, {
|
|
217
|
+
color: getIconColor(entityStatus)
|
|
218
|
+
}),
|
|
219
|
+
onMouseOver: onMouseEnter,
|
|
220
|
+
isScheduled: isScheduled,
|
|
221
|
+
onMouseEnter: onMouseEnter,
|
|
222
|
+
onMouseLeave: onMouseLeave
|
|
223
|
+
}), status?.secondary && React.createElement("svg", {
|
|
224
|
+
"data-status": "secondary",
|
|
225
|
+
"aria-hidden": "true",
|
|
226
|
+
width: "4",
|
|
227
|
+
height: "18",
|
|
228
|
+
viewBox: "0 0 4 18"
|
|
229
|
+
}, React.createElement("path", {
|
|
230
|
+
fillRule: "evenodd",
|
|
231
|
+
clipRule: "evenodd",
|
|
232
|
+
d: "M1.45942e-06 18H0.00296171C2.2121 18 4.00296 16.2091 4.00296 14V4C4.00296 1.79086 2.2121 0 0.00295914 0H0C0.830421 0.732944 1.35418 1.80531 1.35418 3V15C1.35418 16.1947 0.830422 17.2671 1.45942e-06 18Z"
|
|
233
|
+
})), status?.tertiary && React.createElement("svg", {
|
|
234
|
+
"data-status": "tertiary",
|
|
235
|
+
"aria-hidden": "true",
|
|
236
|
+
width: "5",
|
|
237
|
+
height: "16",
|
|
238
|
+
viewBox: "0 0 5 16"
|
|
239
|
+
}, React.createElement("path", {
|
|
240
|
+
fillRule: "evenodd",
|
|
241
|
+
clipRule: "evenodd",
|
|
242
|
+
d: "M0.673828 16.0055H0.675391C2.88453 16.0055 4.67539 14.2146 4.67539 12.0055V3.9955C4.67539 1.88216 3.03648 0.151608 0.960312 0.00549316C1.78382 0.738158 2.30257 1.80597 2.30257 2.99493V12.7838C2.30257 14.1053 1.66172 15.2771 0.673828 16.0055Z"
|
|
243
|
+
})))), React.createElement(Popover.Content, {
|
|
244
|
+
className: styles.popoverContent,
|
|
245
|
+
onMouseEnter: onMouseEnter,
|
|
246
|
+
onMouseLeave: onMouseLeave
|
|
247
|
+
}, !!localesStatusMap && React.createElement(React.Fragment, null, React.createElement(ScheduledBanner, {
|
|
248
|
+
entityId: entity.sys.id,
|
|
249
|
+
jobs: jobs
|
|
250
|
+
}), React.createElement(LocalePublishingStatusList, {
|
|
251
|
+
isScheduled: isScheduled,
|
|
252
|
+
statusMap: localesStatusMap,
|
|
253
|
+
activeLocales: activeLocales
|
|
254
|
+
}))));
|
|
255
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { EntityStatusBadge, Text } from '@contentful/f36-components';
|
|
3
|
+
import tokens from '@contentful/f36-tokens';
|
|
4
|
+
import { css } from 'emotion';
|
|
5
|
+
const styles = {
|
|
6
|
+
locale: css({
|
|
7
|
+
textOverflow: 'ellipsis',
|
|
8
|
+
overflow: 'hidden',
|
|
9
|
+
whiteSpace: 'nowrap'
|
|
10
|
+
}),
|
|
11
|
+
status: css({
|
|
12
|
+
flexShrink: 0
|
|
13
|
+
}),
|
|
14
|
+
localePublishStatus: css({
|
|
15
|
+
display: 'flex',
|
|
16
|
+
gap: tokens.spacingXs,
|
|
17
|
+
justifyContent: 'space-between',
|
|
18
|
+
padding: `${tokens.spacingXs} ${tokens.spacingS}`
|
|
19
|
+
})
|
|
20
|
+
};
|
|
21
|
+
export function LocalePublishingStatus({ locale, status, isScheduled }) {
|
|
22
|
+
return React.createElement("div", {
|
|
23
|
+
className: styles.localePublishStatus,
|
|
24
|
+
"data-test-id": "locale-publishing-status"
|
|
25
|
+
}, React.createElement(Text, {
|
|
26
|
+
className: styles.locale,
|
|
27
|
+
fontColor: "gray700"
|
|
28
|
+
}, locale.name, ' ', React.createElement(Text, {
|
|
29
|
+
fontColor: "gray500"
|
|
30
|
+
}, "(", locale.code, ")", locale.default && ' | default')), React.createElement(EntityStatusBadge, {
|
|
31
|
+
entityStatus: status,
|
|
32
|
+
isScheduled: isScheduled,
|
|
33
|
+
className: styles.status
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { MenuSectionTitle } from '@contentful/f36-components';
|
|
3
|
+
import { sortBy } from 'lodash';
|
|
4
|
+
import { Banner } from './Banner';
|
|
5
|
+
import { LocalePublishingStatus } from './LocalePublishingStatus';
|
|
6
|
+
function groupAndSortLocales(entries, activeLocales) {
|
|
7
|
+
const { selected, nonSelected } = entries.reduce((prev, [localeCode, localeStatusType])=>{
|
|
8
|
+
return activeLocales?.some((sl)=>sl.code === localeCode) ? {
|
|
9
|
+
...prev,
|
|
10
|
+
selected: [
|
|
11
|
+
...prev.selected,
|
|
12
|
+
localeStatusType
|
|
13
|
+
]
|
|
14
|
+
} : {
|
|
15
|
+
...prev,
|
|
16
|
+
nonSelected: [
|
|
17
|
+
...prev.nonSelected,
|
|
18
|
+
localeStatusType
|
|
19
|
+
]
|
|
20
|
+
};
|
|
21
|
+
}, {
|
|
22
|
+
selected: [],
|
|
23
|
+
nonSelected: []
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
selected: sortBy(selected, 'locale.name'),
|
|
27
|
+
nonSelected: nonSelected.sort((a, b)=>{
|
|
28
|
+
if (a.status === b.status) {
|
|
29
|
+
return a.locale.name.localeCompare(b.locale.name);
|
|
30
|
+
}
|
|
31
|
+
if (a.status === 'changed' || a.status === 'published' && b.status === 'draft') {
|
|
32
|
+
return -1;
|
|
33
|
+
}
|
|
34
|
+
return 1;
|
|
35
|
+
})
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function LocalePublishingStatusList({ isScheduled, statusMap, activeLocales }) {
|
|
39
|
+
const entries = [
|
|
40
|
+
...statusMap.entries()
|
|
41
|
+
];
|
|
42
|
+
const counters = entries.reduce((prev, [, { status }])=>({
|
|
43
|
+
changed: prev.changed + (status === 'changed' ? 1 : 0),
|
|
44
|
+
published: prev.published + (status === 'published' ? 1 : 0),
|
|
45
|
+
draft: prev.draft + (status === 'draft' ? 1 : 0)
|
|
46
|
+
}), {
|
|
47
|
+
published: 0,
|
|
48
|
+
changed: 0,
|
|
49
|
+
draft: 0
|
|
50
|
+
});
|
|
51
|
+
const { selected, nonSelected } = groupAndSortLocales(entries, activeLocales);
|
|
52
|
+
return React.createElement(React.Fragment, null, React.createElement(Banner, {
|
|
53
|
+
content: "The entry has locales with the following statuses:",
|
|
54
|
+
highlight: `${counters.changed} changed, ${counters.published} published, ${counters.draft} draft`
|
|
55
|
+
}), React.createElement("div", {
|
|
56
|
+
"data-test-id": "locale-publishing-selected"
|
|
57
|
+
}, React.createElement(MenuSectionTitle, null, "Locales in the entry editor:"), selected.map(({ locale, status })=>React.createElement(LocalePublishingStatus, {
|
|
58
|
+
key: `selected-${locale.code}`,
|
|
59
|
+
status: status,
|
|
60
|
+
locale: locale,
|
|
61
|
+
isScheduled: isScheduled
|
|
62
|
+
}))), nonSelected.length > 0 && React.createElement("div", {
|
|
63
|
+
"data-test-id": "locale-publishing-others"
|
|
64
|
+
}, React.createElement(MenuSectionTitle, null, "Other locales:"), nonSelected.map(({ locale, status })=>React.createElement(LocalePublishingStatus, {
|
|
65
|
+
key: `others-${locale.code}`,
|
|
66
|
+
status: status,
|
|
67
|
+
locale: locale,
|
|
68
|
+
isScheduled: isScheduled
|
|
69
|
+
}))));
|
|
70
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { formatDateAndTime } from '@contentful/f36-components';
|
|
3
|
+
import { orderBy } from 'lodash';
|
|
4
|
+
import { Banner } from './Banner';
|
|
5
|
+
export function ScheduledBanner({ entityId, jobs }) {
|
|
6
|
+
const scheduledJobs = jobs.filter((job)=>job.entity.sys.id === entityId);
|
|
7
|
+
const sortedScheduledJobs = orderBy(scheduledJobs, [
|
|
8
|
+
'scheduledFor.datetime'
|
|
9
|
+
], [
|
|
10
|
+
'asc'
|
|
11
|
+
]);
|
|
12
|
+
const scheduledAction = sortedScheduledJobs[0];
|
|
13
|
+
if (!scheduledAction) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const pendingJobsCount = scheduledJobs.length - 1;
|
|
17
|
+
const lowerCaseAction = scheduledAction.action.toLowerCase();
|
|
18
|
+
const action = lowerCaseAction === ScheduledActionTypes['patch+publish'] ? `${lowerCaseAction} (via Schedule Series)` : lowerCaseAction;
|
|
19
|
+
return React.createElement(Banner, {
|
|
20
|
+
content: `All locales are scheduled to ${action} on:`,
|
|
21
|
+
highlight: `${formatDateAndTime(scheduledAction.scheduledFor.datetime)}${pendingJobsCount > 0 ? ` [and +${pendingJobsCount} more]` : ''}`
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './LocalePublishingPopover';
|
package/dist/esm/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export { shortenStorageUnit, toLocaleString } from './utils/shortenStorageUnit';
|
|
|
9
9
|
export { ModalDialogLauncher };
|
|
10
10
|
export { entityHelpers };
|
|
11
11
|
export { ConstraintsUtils };
|
|
12
|
+
export * from './LocalePublishingEntityStatusBadge';
|
|
12
13
|
import * as ModalDialogLauncher from './ModalDialogLauncher';
|
|
13
14
|
import * as ConstraintsUtils from './utils/constraints';
|
|
14
15
|
import * as entityHelpers from './utils/entityHelpers';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import * as React from 'react';
|
|
2
2
|
interface CharCounterProps {
|
|
3
3
|
value?: string;
|
|
4
4
|
checkConstraint: (n: number) => boolean;
|
|
5
5
|
}
|
|
6
|
-
export declare function CharCounter(props: CharCounterProps): JSX.Element;
|
|
6
|
+
export declare function CharCounter(props: CharCounterProps): React.JSX.Element;
|
|
7
7
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import * as React from 'react';
|
|
2
2
|
import { ValidationType } from './types';
|
|
3
3
|
interface CharValidationProps {
|
|
4
4
|
constraints: ValidationType;
|
|
5
5
|
}
|
|
6
|
-
export declare function CharValidation(props: CharValidationProps): JSX.Element;
|
|
6
|
+
export declare function CharValidation(props: CharValidationProps): React.JSX.Element;
|
|
7
7
|
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { LocaleProps, EntryProps, AssetProps, ScheduledActionProps } from 'contentful-management';
|
|
3
|
+
export type LocalePublishStatus = {
|
|
4
|
+
status: 'draft' | 'published' | 'changed';
|
|
5
|
+
locale: LocaleProps;
|
|
6
|
+
};
|
|
7
|
+
export type LocalePublishStatusMap = Map<string, LocalePublishStatus>;
|
|
8
|
+
type LocalePublishingPopoverProps = {
|
|
9
|
+
entity: EntryProps | AssetProps;
|
|
10
|
+
jobs: ScheduledActionProps[];
|
|
11
|
+
isScheduled: boolean;
|
|
12
|
+
activeLocales: LocaleProps[] | null;
|
|
13
|
+
localesStatusMap?: LocalePublishStatusMap;
|
|
14
|
+
};
|
|
15
|
+
export declare function LocalePublishingPopover({ entity, jobs, isScheduled, localesStatusMap, activeLocales, }: LocalePublishingPopoverProps): React.JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { LocaleProps } from 'contentful-management';
|
|
3
|
+
type LocalePublishingStatusProps = {
|
|
4
|
+
locale: LocaleProps;
|
|
5
|
+
status: 'draft' | 'published' | 'changed';
|
|
6
|
+
isScheduled: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare function LocalePublishingStatus({ locale, status, isScheduled, }: LocalePublishingStatusProps): React.JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { LocaleProps } from 'contentful-management';
|
|
3
|
+
import type { LocalePublishStatusMap } from './LocalePublishingPopover';
|
|
4
|
+
type LocalePublishingStatusListProps = {
|
|
5
|
+
isScheduled: boolean;
|
|
6
|
+
statusMap: LocalePublishStatusMap;
|
|
7
|
+
activeLocales: LocaleProps[] | null;
|
|
8
|
+
};
|
|
9
|
+
export declare function LocalePublishingStatusList({ isScheduled, statusMap, activeLocales, }: LocalePublishingStatusListProps): React.JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ScheduledActionProps } from 'contentful-management';
|
|
3
|
+
type ScheduledBannerProps = {
|
|
4
|
+
entityId: string;
|
|
5
|
+
jobs: ScheduledActionProps[];
|
|
6
|
+
};
|
|
7
|
+
export declare function ScheduledBanner({ entityId, jobs }: ScheduledBannerProps): React.JSX.Element | null;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './LocalePublishingPopover';
|
|
@@ -4,7 +4,7 @@ export declare function open(componentRenderer: (params: {
|
|
|
4
4
|
onClose: Function;
|
|
5
5
|
isShown: boolean;
|
|
6
6
|
}) => any): Promise<unknown>;
|
|
7
|
-
export declare function openDialog<T>(options: OpenCustomWidgetOptions, Component: React.
|
|
7
|
+
export declare function openDialog<T>(options: OpenCustomWidgetOptions, Component: React.FC<{
|
|
8
8
|
onClose: (result: T) => void;
|
|
9
9
|
}>): Promise<unknown>;
|
|
10
10
|
declare const _default: {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export declare function PredefinedValuesError(): JSX.Element;
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export declare function PredefinedValuesError(): React.JSX.Element;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export { shortenStorageUnit, toLocaleString } from './utils/shortenStorageUnit';
|
|
|
10
10
|
export { ModalDialogLauncher };
|
|
11
11
|
export { entityHelpers };
|
|
12
12
|
export { ConstraintsUtils };
|
|
13
|
+
export * from './LocalePublishingEntityStatusBadge';
|
|
13
14
|
import * as ModalDialogLauncher from './ModalDialogLauncher';
|
|
14
15
|
import * as ConstraintsUtils from './utils/constraints';
|
|
15
16
|
import * as entityHelpers from './utils/entityHelpers';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/field-editor-shared",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"main": "dist/cjs/index.js",
|
|
5
5
|
"module": "dist/esm/index.js",
|
|
6
6
|
"types": "dist/types/index.d.ts",
|
|
@@ -36,11 +36,13 @@
|
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@contentful/app-sdk": "^4.29.0",
|
|
39
|
-
"@contentful/field-editor-test-utils": "^1.5.
|
|
39
|
+
"@contentful/field-editor-test-utils": "^1.5.2"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@contentful/f36-
|
|
43
|
-
"@contentful/f36-
|
|
42
|
+
"@contentful/f36-components": "^4.70.0",
|
|
43
|
+
"@contentful/f36-note": "^4.70.0",
|
|
44
|
+
"@contentful/f36-tokens": "^4.0.5",
|
|
45
|
+
"contentful-management": "^11.35.1",
|
|
44
46
|
"emotion": "^10.0.17",
|
|
45
47
|
"fast-deep-equal": "^3.1.3",
|
|
46
48
|
"lodash": "^4.17.15"
|
|
@@ -52,5 +54,5 @@
|
|
|
52
54
|
"publishConfig": {
|
|
53
55
|
"registry": "https://npm.pkg.github.com/"
|
|
54
56
|
},
|
|
55
|
-
"gitHead": "
|
|
57
|
+
"gitHead": "d734036ff89da141dbb91ade0342fb7f82cfe167"
|
|
56
58
|
}
|