diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cb45b1a..e50d447 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,10 +4,10 @@ on: workflow_dispatch: push: branches: - - master + - main pull_request: branches: - - master + - main - develop jobs: diff --git a/admin/src/components/DuplicateButton/index.jsx b/admin/src/components/DuplicateButton/index.jsx new file mode 100644 index 0000000..57e0919 --- /dev/null +++ b/admin/src/components/DuplicateButton/index.jsx @@ -0,0 +1,131 @@ +import React, { useEffect, useState } from 'react'; +// eslint-disable-next-line import/no-unresolved +import { renderToStaticMarkup } from 'react-dom/server'; +import { useCMEditViewDataManager } from '@strapi/helper-plugin'; +import { Duplicate } from '@strapi/icons'; + +const Button = () => ( + +); + +const insertDuplicateButton = (node) => { + const deleteButtons = node.querySelectorAll('button'); + deleteButtons.forEach((button) => { + const span = button.querySelector('span'); + if (span && span.textContent.trim() === 'Delete') { + + const buttonContainer = button.parentElement.parentElement; + + // Check if there already is a duplicate button in the container. + // If so we should not attempt to add a new one. + if (buttonContainer && !buttonContainer.querySelector('.duplicator-span')) { + + const duplicatorSpan = document.createElement('span'); + duplicatorSpan.classList.add('duplicator-span'); + duplicatorSpan.style.display = 'inline-block'; + + const duplicatorButtonHTML = renderToStaticMarkup( -); - -const insertDuplicateButton = (node) => { - // Zoek naar alle 'Delete' knoppen binnen het nieuwe node - const deleteButtons = node.querySelectorAll('button'); - deleteButtons.forEach((button) => { - const duplicatorButtons = document.querySelectorAll('.duplicator-button'); - const index = duplicatorButtons.length; - const span = button.querySelector('span'); - if (span && span.textContent.trim() === 'Delete') { - console.log('Gevonden Delete knop:', button); - - const buttonContainer = button.parentElement; // De element rond de 'Delete' knop - - if (buttonContainer) { - // Controleer of de duplicator knop al bestaat binnen dezelfde container - if (!buttonContainer.querySelector('.duplicator-span')) { // Check for duplicator-span - console.log('Voeg duplicator knop toe aan:', buttonContainer); - - // Creƫer een nieuwe element voor de duplicator knop - const duplicatorSpan = document.createElement('span'); - duplicatorSpan.classList.add('duplicator-span'); - duplicatorSpan.style.display = 'inline-block'; // Zorg ervoor dat de span naast bestaande spans staat - - // Render de DuplicatorButton component als HTML string en geef de variant index door - const duplicatorButtonHTML = renderToStaticMarkup(); - duplicatorSpan.innerHTML = duplicatorButtonHTML; - - // Voeg de duplicator span toe naast de bestaande span - buttonContainer.parentElement.insertBefore(duplicatorSpan, buttonContainer.nextSibling); - } - } else { - console.warn('buttonContainer niet gevonden voor Delete knop:', button); - } - } - }); -}; - - -/** - * Setup DOM Manipulator - * Initialiseert een MutationObserver die de DOM observeert op toevoeging van nieuwe nodes. - * Het zoekt specifiek naar 'Delete' knoppen en voegt daar de Duplicator-knop naast toe in een nieuwe . - * - * @param {object} app - Strapi app instance - */ -export const setupDOMManipulator = (app) => { - const [insertedButtons, setInsertedButtons] = useState(0); - const targetNode = document.body; // Observeer het gehele body-element - const config = { childList: true, subtree: true }; // Observeer kind-elementen en de gehele subtree - const { modifiedData, onChange, slug, allLayoutData } = useCMEditViewDataManager(); - - console.log(modifiedData); - - useEffect(() => { - const visibleFields = allLayoutData.contentType.layouts.edit; - const repeatableComponentFields = visibleFields.filter((field) => field[0].fieldSchema.type === 'component' && field[0].fieldSchema.repeatable); - - repeatableComponentFields.forEach((field) => { - const { label } = field[0].metadatas; - - for (const labelElement of document.querySelectorAll('label')) { - if (labelElement.textContent?.startsWith(`${label}`)) { - const wrapperElement = labelElement.parentElement.parentElement.parentElement; - insertDuplicateButton(wrapperElement); - setInsertedButtons(insertedButtons + 1); - } - } - }); - }, [allLayoutData]); - - useEffect(() => { - const callback = (mutationsList, observer) => { - mutationsList.forEach((mutation) => { - if (mutation.type === 'childList') { - mutation.addedNodes.forEach((node) => { - if (node.nodeType === Node.ELEMENT_NODE) { - insertDuplicateButton(node); - setInsertedButtons(insertedButtons + 1); - console.log('inserted button'); - } - }); - } - }); - }; - - // Initialiseer de MutationObserver met de callback en config - - // @todo: De mutiation observer zorgt er voor dat de duplicator knoppen - // alleen worden toegevoegd aan de DOM wanneer er nieuwe nodes worden toegevoegd. - // Echter betekend dat voor bestaande wielen, met al eerder aangemaakte varianten, - // de duplicator knoppen niet worden toegevoegd. Dit moet nog worden opgelost. - const observer = new MutationObserver(callback); - observer.observe(targetNode, config); - }, []); - - useEffect(() => { - // @todo: Hier moet een loop komen om alle duplicator-buttons te selecteren - // en event listeners toe te voegen. - - // Selecteer de duplicator-button om een event listener toe te voegen - const duplicatorButtons = document.querySelectorAll('.duplicator-button'); - - if (!duplicatorButtons || duplicatorButtons.length === 0) { - return () => {}; - } - - const clickFunction = (e) => { - console.log('Dupliceer knop geklikt via innerHTML', e); - - // Gebruik de variantIndex om de juiste variant te vinden - const index = parseInt(e.target.parentElement.parentElement.getAttribute('data-index'), 10); - console.log('Dupliceer knop indexx:', index); - - // Haal de variant data op via de index - // @todo: Hier staat .Variant hardcoded. Dit moet dynamisch worden gemaakt. - const componentData = modifiedData.Variant[index]; - if (!componentData) { - console.error('Variant data niet gevonden voor index:', index); - return; - } - - // Log de variant data - console.log('Variant data:', componentData); - - // Implementeer de duplicatie logica - // @todo: Hier staat .Variant hardcoded. Dit moet dynamisch worden gemaakt. - const currentVariants = modifiedData.Variant || []; - const newVariant = { ...componentData, __temp_key__: Date.now() }; // Voeg unieke ID toe indien nodig - delete newVariant.id; - - // Update de varianten lijst en voeg het nieuwe item toe - const updatedVariants = [...currentVariants, newVariant]; - - // Gebruik onChange om de nieuwe variant lijst in te stellen - // @todo: Hier staat Variant hardcoded. Dit moet dynamisch worden gemaakt. - onChange({ target: { name: 'Variant', value: updatedVariants } }); - - // // Gebruik notification in plaats van alert - // app.toggleNotification({ - // type: 'success', - // message: { id: getTrad('duplicator.success'), defaultMessage: 'Component gedupliceerd!' }, - // }); - }; - - duplicatorButtons.forEach((button) => { - button.addEventListener('click', clickFunction); - }); - - console.log('added click function'); - - return () => { - duplicatorButtons.forEach((button) => { - button.removeEventListener('click', clickFunction); - }); - }; - }, [modifiedData, insertedButtons]); -}; - -export default setupDOMManipulator; diff --git a/admin/src/index.js b/admin/src/index.js index 8ec3c1d..762c745 100644 --- a/admin/src/index.js +++ b/admin/src/index.js @@ -4,7 +4,7 @@ import pluginPkg from '../../package.json'; import pluginId from './helpers/pluginId'; import Duplicator from './components/Duplicator'; import pluginPermissions from './permissions'; -import { setupDOMManipulator } from './domManipulator'; +import DuplicateButton from './components/DuplicateButton'; // import getTrad from './helpers/getTrad'; const pluginDescription = pluginPkg.strapi.description || pluginPkg.description; @@ -31,7 +31,7 @@ export default { // Inject CMEditViewExclude app.injectContentManagerComponent('editView', 'informations', { name: 'component-duplicator-exclude-filter-edit-view', - Component: setupDOMManipulator, + Component: DuplicateButton, }); // Inject Duplicator - tijdelijk uitgeschakeld diff --git a/package.json b/package.json index 25e5347..c90a542 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "immutable": "^3.8.2", "redux-immutable": "^4.0.0", "redux-thunk": "^2.3.0", - "component-duplicator": "^7.1.0", "xml2js": "^0.5.0" }, "author": { diff --git a/playground/.strapi/client/app.js b/playground/.strapi/client/app.js index 36f5ecc..5328939 100644 --- a/playground/.strapi/client/app.js +++ b/playground/.strapi/client/app.js @@ -4,13 +4,13 @@ */ import i18N from "@strapi/plugin-i18n/strapi-admin"; import usersPermissions from "@strapi/plugin-users-permissions/strapi-admin"; -import component-duplicator from "../../src/plugins/component-duplicator/strapi-admin"; +import componentDuplicator from "../../src/plugins/component-duplicator/strapi-admin"; import { renderAdmin } from "@strapi/strapi/admin"; renderAdmin(document.getElementById("strapi"), { plugins: { i18n: i18N, "users-permissions": usersPermissions, - component-duplicator: component-duplicator, + "component-duplicator": componentDuplicator, }, }); diff --git a/playground/config/env/ci/plugins.ts b/playground/config/env/ci/plugins.ts index 5d0c320..b815ade 100644 --- a/playground/config/env/ci/plugins.ts +++ b/playground/config/env/ci/plugins.ts @@ -1,7 +1,7 @@ const path = require('path'); export default { - component-duplicator: { + 'component-duplicator': { enabled: true, resolve: path.resolve(__dirname, '../../../../node_modules/strapi-plugin-component-duplicator'), }, diff --git a/playground/config/env/test/plugins.ts b/playground/config/env/test/plugins.ts index 21fdbbc..c76636e 100644 --- a/playground/config/env/test/plugins.ts +++ b/playground/config/env/test/plugins.ts @@ -1,7 +1,7 @@ const path = require('path'); export default { - component-duplicator: { + 'component-duplicator': { enabled: true, resolve: path.resolve(__dirname, '../../../../src/plugins/component-duplicator'), }, diff --git a/playground/src/api/wielen/content-types/wielen/schema.json b/playground/src/api/wielen/content-types/wielen/schema.json index 2e430f3..5cde699 100644 --- a/playground/src/api/wielen/content-types/wielen/schema.json +++ b/playground/src/api/wielen/content-types/wielen/schema.json @@ -4,7 +4,8 @@ "info": { "singularName": "wielen", "pluralName": "wielens", - "displayName": "Wielen" + "displayName": "Wielen", + "description": "" }, "options": { "draftAndPublish": true @@ -19,6 +20,11 @@ "type": "component", "repeatable": true, "component": "variants.variant" + }, + "tralali": { + "type": "component", + "repeatable": true, + "component": "variants.variant" } } } diff --git a/playground/types/generated/contentTypes.d.ts b/playground/types/generated/contentTypes.d.ts index f267ce1..f3b8aa7 100644 --- a/playground/types/generated/contentTypes.d.ts +++ b/playground/types/generated/contentTypes.d.ts @@ -833,6 +833,7 @@ export interface ApiWielenWielen extends Schema.CollectionType { singularName: 'wielen'; pluralName: 'wielens'; displayName: 'Wielen'; + description: ''; }; options: { draftAndPublish: true; @@ -840,6 +841,7 @@ export interface ApiWielenWielen extends Schema.CollectionType { attributes: { modelnummer: Attribute.String; Variant: Attribute.Component<'variants.variant', true>; + tralali: Attribute.Component<'variants.variant', true>; createdAt: Attribute.DateTime; updatedAt: Attribute.DateTime; publishedAt: Attribute.DateTime; diff --git a/yarn.lock b/yarn.lock index 0f82b07..9cb2eea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2446,11 +2446,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.2.tgz#fa6a90f2600e052a03c18b8cb3fd83dd4e599898" integrity sha512-vOBLVQeCQfIcF/2Y7eKFTqrMnizK5lRNQ7ykML/5RuwVXVWxYkgwS7xbt4B6fKCUPgbSL5FSsjHQpaGQP/dQmw== -"@types/node@^17.0.5": - version "17.0.45" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" - integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== - "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -2492,13 +2487,6 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/sax@^1.2.1": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/sax/-/sax-1.2.4.tgz#8221affa7f4f3cb21abd22f244cfabfa63e6a69e" - integrity sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw== - dependencies: - "@types/node" "*" - "@types/scheduler@*": version "0.16.3" resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" @@ -2732,11 +2720,6 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" -arg@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" - integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== - argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -6279,7 +6262,7 @@ safer-buffer@^2.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sax@>=0.6.0, sax@^1.2.4: +sax@>=0.6.0: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -6355,16 +6338,6 @@ sisteransi@^1.0.5: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== -component-duplicator@^7.1.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/component-duplicator/-/component-duplicator-7.1.1.tgz#eeed9ad6d95499161a3eadc60f8c6dce4bea2bef" - integrity sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg== - dependencies: - "@types/node" "^17.0.5" - "@types/sax" "^1.2.1" - arg "^5.0.0" - sax "^1.2.4" - slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"