Skip to content

Commit 01df1db

Browse files
committed
feat: finalize the features
1 parent 1752730 commit 01df1db

File tree

12 files changed

+149
-255
lines changed

12 files changed

+149
-255
lines changed

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ on:
44
workflow_dispatch:
55
push:
66
branches:
7-
- master
7+
- main
88
pull_request:
99
branches:
10-
- master
10+
- main
1111
- develop
1212

1313
jobs:
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import React, { useEffect, useState } from 'react';
2+
// eslint-disable-next-line import/no-unresolved
3+
import { renderToStaticMarkup } from 'react-dom/server';
4+
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
5+
import { Duplicate } from '@strapi/icons';
6+
7+
const Button = () => (
8+
<button
9+
aria-disabled="false"
10+
type="button"
11+
className="sc-aXZVg sc-gEvEer sc-cwHptR bzWqhm bYXTJs ksKyfS sc-cfxfcM bNDrnU sc-cPrPEB gsfWfo duplicator-button"
12+
tabIndex="0"
13+
aria-labelledby=":r1n:"
14+
style={{ display: 'inline-block', width: '2rem', height: '2rem', padding: '8px' }}
15+
>
16+
<Duplicate width={12} fill="#666687" />
17+
</button>
18+
);
19+
20+
const insertDuplicateButton = (node) => {
21+
const deleteButtons = node.querySelectorAll('button');
22+
deleteButtons.forEach((button) => {
23+
const span = button.querySelector('span');
24+
if (span && span.textContent.trim() === 'Delete') {
25+
26+
const buttonContainer = button.parentElement.parentElement;
27+
28+
// Check if there already is a duplicate button in the container.
29+
// If so we should not attempt to add a new one.
30+
if (buttonContainer && !buttonContainer.querySelector('.duplicator-span')) {
31+
32+
const duplicatorSpan = document.createElement('span');
33+
duplicatorSpan.classList.add('duplicator-span');
34+
duplicatorSpan.style.display = 'inline-block';
35+
36+
const duplicatorButtonHTML = renderToStaticMarkup(<Button />);
37+
duplicatorSpan.innerHTML = duplicatorButtonHTML;
38+
39+
buttonContainer.insertBefore(duplicatorSpan, button.parentElement.nextSibling);
40+
}
41+
}
42+
});
43+
};
44+
45+
46+
const DuplicateButton = () => {
47+
const { modifiedData, onChange, allLayoutData } = useCMEditViewDataManager();
48+
const [count, setCount] = useState(0);
49+
const visibleFields = allLayoutData.contentType.layouts.edit;
50+
const repeatableComponentFields = visibleFields.filter((field) => field[0].fieldSchema.type === 'component' && field[0].fieldSchema.repeatable);
51+
52+
// Initially insert the duplicate buttons for all existing repeatable components.
53+
useEffect(() => {
54+
repeatableComponentFields.forEach((field) => {
55+
const { label } = field[0].metadatas;
56+
57+
for (const labelElement of document.querySelectorAll('label')) {
58+
if (labelElement.textContent?.startsWith(`${label}`)) {
59+
const wrapperElement = labelElement.parentElement.parentElement.parentElement;
60+
insertDuplicateButton(wrapperElement);
61+
}
62+
}
63+
});
64+
}, [allLayoutData]);
65+
66+
// Insert the duplicate button for any new repeatable components that are added to the DOM.
67+
// This happens when somebody adds a new component to the list, or drags it to a new position.
68+
useEffect(() => {
69+
const targetNode = document.body;
70+
const config = { childList: true, subtree: true };
71+
72+
const callback = (mutationsList, observer) => {
73+
mutationsList.forEach((mutation) => {
74+
if (mutation.type === 'childList') {
75+
mutation.addedNodes.forEach((node) => {
76+
if (node.nodeType === Node.ELEMENT_NODE) {
77+
insertDuplicateButton(node);
78+
setCount((prevCount) => prevCount + 1);
79+
}
80+
});
81+
}
82+
});
83+
};
84+
85+
const observer = new MutationObserver(callback);
86+
observer.observe(targetNode, config);
87+
}, []);
88+
89+
// Add a click event listener to all the duplicate buttons.
90+
useEffect(() => {
91+
const duplicatorButtons = document.querySelectorAll('.duplicator-button');
92+
93+
if (!duplicatorButtons || duplicatorButtons.length === 0) {
94+
return () => {};
95+
}
96+
97+
const clickFunction = (e) => {
98+
const button = e.target.nodeName === 'BUTTON' ? e.target : e.target.closest('button');
99+
const listItem = button.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement;
100+
const siblings = Array.from(listItem.parentElement.children);
101+
const index = siblings.indexOf(listItem);
102+
103+
const wrapper = listItem.parentElement.parentElement.parentElement.parentElement.parentElement;
104+
const componentLabel = wrapper.querySelector('label').textContent;
105+
const componentInfo = repeatableComponentFields.filter((field) => componentLabel?.startsWith(`${field[0].metadatas.label}`));
106+
const componentName = componentInfo[0][0].name;
107+
108+
const componentData = modifiedData[componentName][index];
109+
110+
const currentComponents = [...modifiedData[componentName]] || [];
111+
const newComponent = { ...componentData, __temp_key__: Date.now() };
112+
delete newComponent.id;
113+
114+
currentComponents.splice(index + 1, 0, newComponent);
115+
116+
onChange({ target: { name: componentName, value: currentComponents } });
117+
};
118+
119+
duplicatorButtons.forEach((button) => {
120+
button.addEventListener('click', clickFunction);
121+
});
122+
123+
return () => {
124+
duplicatorButtons.forEach((button) => {
125+
button.removeEventListener('click', clickFunction);
126+
});
127+
};
128+
}, [modifiedData, count]);
129+
};
130+
131+
export default DuplicateButton;

admin/src/components/Duplicator/index.jsx

Lines changed: 0 additions & 30 deletions
This file was deleted.

admin/src/domManipulator.js

Lines changed: 0 additions & 187 deletions
This file was deleted.

admin/src/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import pluginPkg from '../../package.json';
44
import pluginId from './helpers/pluginId';
55
import Duplicator from './components/Duplicator';
66
import pluginPermissions from './permissions';
7-
import { setupDOMManipulator } from './domManipulator';
7+
import DuplicateButton from './components/DuplicateButton';
88
// import getTrad from './helpers/getTrad';
99

1010
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
@@ -31,7 +31,7 @@ export default {
3131
// Inject CMEditViewExclude
3232
app.injectContentManagerComponent('editView', 'informations', {
3333
name: 'component-duplicator-exclude-filter-edit-view',
34-
Component: setupDOMManipulator,
34+
Component: DuplicateButton,
3535
});
3636

3737
// Inject Duplicator - tijdelijk uitgeschakeld

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
"immutable": "^3.8.2",
4040
"redux-immutable": "^4.0.0",
4141
"redux-thunk": "^2.3.0",
42-
"component-duplicator": "^7.1.0",
4342
"xml2js": "^0.5.0"
4443
},
4544
"author": {

0 commit comments

Comments
 (0)