@vonaffenfels/contentful-teasermanager 1.0.31 → 1.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/components/Contentful/EntryEditor.js +3 -6
- package/src/components/Teasermanager/Timeline.js +7 -7
- package/src/components/Teasermanager/Timeline.module.css +3 -0
- package/src/components/Teasermanager.js +58 -37
- package/src/components/Teasermanager.module.css +44 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vonaffenfels/contentful-teasermanager",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.32",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"prepublish": "yarn run build",
|
|
6
6
|
"dev": "yarn run start",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"@vonaffenfels/slate-editor": "^1.0.31",
|
|
100
100
|
"webpack": "5.88.2"
|
|
101
101
|
},
|
|
102
|
-
"gitHead": "
|
|
102
|
+
"gitHead": "0a84becd51cafa0c69f4c3e867a036fc684b0f0c",
|
|
103
103
|
"publishConfig": {
|
|
104
104
|
"access": "public"
|
|
105
105
|
}
|
|
@@ -8,7 +8,6 @@ const Entry = ({sdk}) => {
|
|
|
8
8
|
const [locale, setLocale] = useState(sdk.locales.default);
|
|
9
9
|
const [portal, setPortal] = useState();
|
|
10
10
|
const portalField = sdk?.entry?.fields?.portal;
|
|
11
|
-
const [reloadTrigger, setReloadTrigger] = useState(0);
|
|
12
11
|
|
|
13
12
|
portalField?.onValueChanged(() => {
|
|
14
13
|
if (portalField.getValue() !== portal) {
|
|
@@ -27,7 +26,7 @@ const Entry = ({sdk}) => {
|
|
|
27
26
|
}, []);
|
|
28
27
|
|
|
29
28
|
const onSlotClick = (slotId, currentDate) => {
|
|
30
|
-
sdk.dialogs.selectSingleEntry({contentTypes: ["article"]}).then(async (entry) => {
|
|
29
|
+
return sdk.dialogs.selectSingleEntry({contentTypes: ["article"]}).then(async (entry) => {
|
|
31
30
|
if (!entry) {
|
|
32
31
|
return;
|
|
33
32
|
}
|
|
@@ -46,7 +45,7 @@ const Entry = ({sdk}) => {
|
|
|
46
45
|
"date": currentDate,
|
|
47
46
|
}),
|
|
48
47
|
});
|
|
49
|
-
|
|
48
|
+
return entry;
|
|
50
49
|
} catch (e) {
|
|
51
50
|
console.error(e);
|
|
52
51
|
alert("Fehler beim speichern!");
|
|
@@ -69,7 +68,6 @@ const Entry = ({sdk}) => {
|
|
|
69
68
|
"date": currentDate,
|
|
70
69
|
}),
|
|
71
70
|
});
|
|
72
|
-
setReloadTrigger((current) => current + 1);
|
|
73
71
|
} catch (e) {
|
|
74
72
|
console.error(e);
|
|
75
73
|
alert("Fehler beim speichern!");
|
|
@@ -115,8 +113,7 @@ const Entry = ({sdk}) => {
|
|
|
115
113
|
loadSlotStateForPage={loadSlotStateForPage}
|
|
116
114
|
removeSlotData={removeSlotData}
|
|
117
115
|
loadTimelineStateForPage={loadTimelineStateForPage}
|
|
118
|
-
onSlotClick={onSlotClick}
|
|
119
|
-
reloadTrigger={reloadTrigger}/>;
|
|
116
|
+
onSlotClick={onSlotClick}/>;
|
|
120
117
|
};
|
|
121
118
|
|
|
122
119
|
export default Entry;
|
|
@@ -93,9 +93,9 @@ export const Timeline = ({
|
|
|
93
93
|
<div className="w-full h-auto text-center py-2">
|
|
94
94
|
{!!currentDate && (
|
|
95
95
|
<>
|
|
96
|
-
<input
|
|
97
|
-
type="date"
|
|
98
|
-
value={format(currentDate, "yyyy-MM-dd")}
|
|
96
|
+
<input
|
|
97
|
+
type="date"
|
|
98
|
+
value={format(currentDate, "yyyy-MM-dd")}
|
|
99
99
|
onChange={handleDateChange}
|
|
100
100
|
className="mr-2 w-auto"/>
|
|
101
101
|
<TimeSelect className="inline w-auto" currentDate={currentDate} onChange={handleTimeChange}/>
|
|
@@ -140,8 +140,8 @@ const TimeSelect = ({
|
|
|
140
140
|
let options = [];
|
|
141
141
|
|
|
142
142
|
for (let i = 1; i <= 24; i++) {
|
|
143
|
-
options.push(<option
|
|
144
|
-
key={`hour-option-${i}`}
|
|
143
|
+
options.push(<option
|
|
144
|
+
key={`hour-option-${i}`}
|
|
145
145
|
value={i}
|
|
146
146
|
style={{padding: "8px"}}>{i < 10 ? `0${i}` : i}</option>);
|
|
147
147
|
}
|
|
@@ -153,8 +153,8 @@ const TimeSelect = ({
|
|
|
153
153
|
let options = [];
|
|
154
154
|
|
|
155
155
|
for (let i = 0; i <= 60; i += 15) {
|
|
156
|
-
options.push(<option
|
|
157
|
-
key={`minute-option-${i}`}
|
|
156
|
+
options.push(<option
|
|
157
|
+
key={`minute-option-${i}`}
|
|
158
158
|
value={i}
|
|
159
159
|
style={{padding: "8px"}}>{i < 10 ? `0${i}` : i}</option>);
|
|
160
160
|
}
|
|
@@ -8,16 +8,16 @@ import componentLoader from "@vonaffenfels/slate-editor/componentLoader";
|
|
|
8
8
|
import {Timeline} from "./Teasermanager/Timeline";
|
|
9
9
|
import styles from "./Teasermanager.module.css";
|
|
10
10
|
import {runLoaders} from "../lib/runLoaders";
|
|
11
|
+
import classNames from "classnames";
|
|
11
12
|
|
|
12
13
|
export const Teasermanager = ({
|
|
13
14
|
entryId,
|
|
14
15
|
loadSlotStateForPage = () => console.error("missing loadSlotStateForPage"),
|
|
15
16
|
loadTimelineStateForPage = () => console.error("missing loadTimelineStateForPage"),
|
|
16
|
-
onSlotClick = () => console.error("missing onSlotClick"),
|
|
17
|
-
removeSlotData = () => console.error("missing removeSlotData"),
|
|
17
|
+
onSlotClick = async () => console.error("missing onSlotClick"),
|
|
18
|
+
removeSlotData = async () => console.error("missing removeSlotData"),
|
|
18
19
|
contentFieldName = "content",
|
|
19
20
|
locale = "de",
|
|
20
|
-
reloadTrigger = 0,
|
|
21
21
|
}) => {
|
|
22
22
|
const contentfulClient = getContentfulClient();
|
|
23
23
|
const [entry, setEntry] = useState(null);
|
|
@@ -26,6 +26,7 @@ export const Teasermanager = ({
|
|
|
26
26
|
const [loadedContent, setLoadedContent] = useState(null);
|
|
27
27
|
const [currentDate, _setCurrentDate] = useState(null);
|
|
28
28
|
const [currentSlotState, setCurrentSlotState] = useState(null);
|
|
29
|
+
const currentLoadingOperation = useRef(0);
|
|
29
30
|
|
|
30
31
|
const setCurrentDate = (date) => {
|
|
31
32
|
if (date) {
|
|
@@ -35,6 +36,38 @@ export const Teasermanager = ({
|
|
|
35
36
|
_setCurrentDate(date);
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
const reloadAllData = async () => {
|
|
40
|
+
let loadingId = ++currentLoadingOperation.current;
|
|
41
|
+
if (!currentDate) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const contentValue = entry?.fields?.[contentFieldName]?.[locale];
|
|
46
|
+
const portalValue = entry?.fields?.portal?.[locale];
|
|
47
|
+
|
|
48
|
+
if (!contentValue) {
|
|
49
|
+
setLoading("initial");
|
|
50
|
+
setLoadedContent(null);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
console.log(`Teasermanager :: LOADING DATA`);
|
|
55
|
+
if (loading !== "initial") {
|
|
56
|
+
setLoading(true);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const content = await runLoaders(contentValue, portalValue, entry?.sys?.id, currentDate);
|
|
60
|
+
|
|
61
|
+
// only apply the LAST update
|
|
62
|
+
if (currentLoadingOperation.current === loadingId) {
|
|
63
|
+
updateSlotStateData();
|
|
64
|
+
|
|
65
|
+
console.log(`Teasermanager :: LOADED DATA`);
|
|
66
|
+
setLoadedContent(content);
|
|
67
|
+
setLoading(false);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
38
71
|
const initSlot = (node) => {
|
|
39
72
|
const existingManagementNode = node.querySelector(`.__teasermanager_management_node`);
|
|
40
73
|
if (existingManagementNode) {
|
|
@@ -67,7 +100,13 @@ export const Teasermanager = ({
|
|
|
67
100
|
</svg>
|
|
68
101
|
`;
|
|
69
102
|
managementRemoveButtonNode.addEventListener("click", (e) => {
|
|
70
|
-
removeSlotData(slotId, currentDate)
|
|
103
|
+
removeSlotData(slotId, currentDate).then(() => {
|
|
104
|
+
managementNode.classList.add(styles.loading);
|
|
105
|
+
reloadAllData().then(() => {
|
|
106
|
+
// DONT remove, in case multiple loading operations are running we want the state to stay loading :)
|
|
107
|
+
//managementNode.classList.remove(styles.loading);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
71
110
|
e.preventDefault();
|
|
72
111
|
e.stopPropagation();
|
|
73
112
|
return false;
|
|
@@ -83,9 +122,17 @@ export const Teasermanager = ({
|
|
|
83
122
|
}
|
|
84
123
|
|
|
85
124
|
managementNode.appendChild(managementLabelNode);
|
|
86
|
-
|
|
87
125
|
managementNode.addEventListener("click", (e) => {
|
|
88
|
-
onSlotClick(slotId, currentDate)
|
|
126
|
+
onSlotClick(slotId, currentDate).then((entry) => {
|
|
127
|
+
if (!entry) {
|
|
128
|
+
return; // nothing changed, no need to reload!
|
|
129
|
+
}
|
|
130
|
+
managementNode.classList.add(styles.loading);
|
|
131
|
+
reloadAllData().then(() => {
|
|
132
|
+
// DONT remove, in case multiple loading operations are running we want the state to stay loading :)
|
|
133
|
+
//managementNode.classList.remove(styles.loading);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
89
136
|
e.preventDefault();
|
|
90
137
|
e.stopPropagation();
|
|
91
138
|
return false;
|
|
@@ -101,6 +148,7 @@ export const Teasermanager = ({
|
|
|
101
148
|
// update all slots
|
|
102
149
|
const slots = wrapperRef.current.querySelectorAll(`*[data-teasermanager-slot]`);
|
|
103
150
|
slots.forEach(initSlot);
|
|
151
|
+
console.log(`Teasermanager :: UPDATE ALL SLOTS`);
|
|
104
152
|
} else {
|
|
105
153
|
// update only if the change was not a management node
|
|
106
154
|
const isValidUpdate = mutationList?.find(mutation => {
|
|
@@ -149,7 +197,7 @@ export const Teasermanager = ({
|
|
|
149
197
|
return () => {
|
|
150
198
|
observer.disconnect();
|
|
151
199
|
};
|
|
152
|
-
}, [wrapperRef, currentDate, currentSlotState]);
|
|
200
|
+
}, [wrapperRef, currentDate, currentSlotState, onSlotClick, removeSlotData, reloadAllData]);
|
|
153
201
|
|
|
154
202
|
const updateSlotStateData = async () => {
|
|
155
203
|
try {
|
|
@@ -162,37 +210,13 @@ export const Teasermanager = ({
|
|
|
162
210
|
}
|
|
163
211
|
}
|
|
164
212
|
|
|
165
|
-
const reloadAllData = async () => {
|
|
166
|
-
if (loading === true || !currentDate) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const contentValue = entry?.fields?.[contentFieldName]?.[locale];
|
|
171
|
-
const portalValue = entry?.fields?.portal?.[locale];
|
|
172
|
-
|
|
173
|
-
if (!contentValue) {
|
|
174
|
-
setLoading(false);
|
|
175
|
-
setLoadedContent(null);
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
console.log(`Teasermanager :: LOADING DATA`);
|
|
180
|
-
setLoading(true);
|
|
181
|
-
runLoaders(contentValue, portalValue, entry?.sys?.id, currentDate).then(content => {
|
|
182
|
-
setLoadedContent(content);
|
|
183
|
-
setLoading(false);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
updateSlotStateData();
|
|
187
|
-
}
|
|
188
|
-
|
|
189
213
|
useEffect(() => {
|
|
190
214
|
updateSlots();
|
|
191
215
|
}, [currentSlotState]);
|
|
192
216
|
|
|
193
217
|
useEffect(() => {
|
|
194
218
|
reloadAllData();
|
|
195
|
-
}, [contentFieldName, locale, entry,
|
|
219
|
+
}, [contentFieldName, locale, entry, currentDate]);
|
|
196
220
|
|
|
197
221
|
return <div className="flex w-full flex-col">
|
|
198
222
|
<div className="w-full">
|
|
@@ -201,11 +225,8 @@ export const Teasermanager = ({
|
|
|
201
225
|
setCurrentDate={setCurrentDate}
|
|
202
226
|
loadTimelineStateForPage={loadTimelineStateForPage}/>
|
|
203
227
|
</div>
|
|
204
|
-
<div className={styles.wrapper} ref={wrapperRef}>
|
|
205
|
-
{loading &&
|
|
206
|
-
<div className="dark:text-white w-full font-base text-2xl p-8 h-full text-center align-middle">Lade
|
|
207
|
-
Inhalte...</div>}
|
|
208
|
-
{!!loadedContent && !loading &&
|
|
228
|
+
<div className={classNames(styles.wrapper, {[styles.loading]: loading === "initial"})} ref={wrapperRef}>
|
|
229
|
+
{!!loadedContent && loading !== "initial" &&
|
|
209
230
|
<Renderer
|
|
210
231
|
value={loadedContent}
|
|
211
232
|
storybookComponentLoader={componentLoader}
|
|
@@ -32,13 +32,53 @@
|
|
|
32
32
|
cursor: pointer;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
.loading {
|
|
36
|
+
animation: pulse 3s ease-in-out infinite;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.loading:after {
|
|
40
|
+
content: '';
|
|
41
|
+
position: absolute;
|
|
42
|
+
top: 50%;
|
|
43
|
+
left: 50%;
|
|
44
|
+
margin-top: -50px;
|
|
45
|
+
margin-left: -50px;
|
|
46
|
+
width: 100px;
|
|
47
|
+
height: 100px;
|
|
48
|
+
border-radius: 999999px;
|
|
49
|
+
border: 10px dotted white;
|
|
50
|
+
filter: drop-shadow(2px 4px 6px black);
|
|
51
|
+
animation: loading 3s linear infinite;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@keyframes loading {
|
|
55
|
+
0% {
|
|
56
|
+
transform: rotate(0deg);
|
|
57
|
+
}
|
|
58
|
+
100% {
|
|
59
|
+
transform: rotate(360deg);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@keyframes pulse {
|
|
64
|
+
from {
|
|
65
|
+
opacity: 1;
|
|
66
|
+
}
|
|
67
|
+
50% {
|
|
68
|
+
opacity: 0.3;
|
|
69
|
+
}
|
|
70
|
+
to {
|
|
71
|
+
opacity: 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
35
75
|
@media (prefers-color-scheme: dark) {
|
|
36
76
|
.management {
|
|
37
77
|
outline: 2px dashed white;
|
|
38
78
|
}
|
|
39
79
|
}
|
|
40
80
|
|
|
41
|
-
.management:hover
|
|
81
|
+
.management:hover {
|
|
42
82
|
background-color: rgba(255, 255, 255, 0.9);
|
|
43
83
|
}
|
|
44
84
|
|
|
@@ -54,10 +94,11 @@
|
|
|
54
94
|
}
|
|
55
95
|
|
|
56
96
|
.changed {
|
|
57
|
-
background-color: rgba(172, 255, 156, 0.
|
|
97
|
+
background-color: rgba(172, 255, 156, 0.4);
|
|
58
98
|
}
|
|
99
|
+
|
|
59
100
|
.changed:hover {
|
|
60
|
-
background-color: rgba(97, 255, 68, 0.
|
|
101
|
+
background-color: rgba(97, 255, 68, 0.6);
|
|
61
102
|
}
|
|
62
103
|
|
|
63
104
|
.changed-border {
|