Skip to content

Commit 2523485

Browse files
authored
Merge pull request #191 from jacquesbh/copy-paste-ui-element
Copy/Paste Ui Element!
2 parents 0017901 + b564fc3 commit 2523485

File tree

5 files changed

+141
-30
lines changed

5 files changed

+141
-30
lines changed

assets/js/app.js

Lines changed: 104 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ global.MonsieurBizRichEditorConfig = class {
6060
uielements,
6161
wysiwyg,
6262
containerHtml,
63-
buttonAddHtml,
63+
actionsHtml,
6464
elementHtml,
6565
elementCardHtml,
6666
deletionConfirmation,
@@ -69,13 +69,14 @@ global.MonsieurBizRichEditorConfig = class {
6969
renderElementsUrl,
7070
defaultUiElement,
7171
defaultUIElementDataField,
72-
errorMessage
72+
errorMessage,
73+
unallowedUiElementMessage
7374
) {
7475
this.input = input;
7576
this.uielements = uielements;
7677
this.wysiwyg = wysiwyg;
7778
this.containerHtml = containerHtml;
78-
this.buttonAddHtml = buttonAddHtml;
79+
this.actionsHtml = actionsHtml;
7980
this.elementHtml = elementHtml;
8081
this.elementCardHtml = elementCardHtml;
8182
this.deletionConfirmation = deletionConfirmation;
@@ -85,6 +86,7 @@ global.MonsieurBizRichEditorConfig = class {
8586
this.defaultUiElement = defaultUiElement;
8687
this.defaultUIElementDataField = defaultUIElementDataField;
8788
this.errorMessage = errorMessage;
89+
this.unallowedUiElementMessage = unallowedUiElementMessage;
8890
}
8991

9092
findUiElementByCode(code) {
@@ -138,6 +140,10 @@ global.MonsieurBizRichEditorUiElement = class {
138140
this.manager.editUiElement(this);
139141
}
140142

143+
copy(callback) {
144+
this.manager.saveUiElementToClipboard(this, callback);
145+
}
146+
141147
up() {
142148
this.manager.moveUp(this);
143149
}
@@ -225,6 +231,11 @@ global.MonsieurBizRichEditorManager = class {
225231
document.dispatchEvent(new CustomEvent('mbiz:rich-editor:init-interface-complete', {
226232
'detail': {'editorManager': this}
227233
}));
234+
document.addEventListener('mbiz:rich-editor:uielement:copied', function (e) {
235+
this.container.querySelectorAll('.js-uie-paste').forEach(function (action) {
236+
action.classList.remove('disabled');
237+
}.bind(this));
238+
}.bind(this));
228239
}
229240

230241
initUiPanelsInterface() {
@@ -259,24 +270,41 @@ global.MonsieurBizRichEditorManager = class {
259270
let elementsContainer = this.container.querySelector('.js-uie-container');
260271
elementsContainer.innerHTML = '';
261272
this.uiElements.forEach(function (element, position) {
262-
elementsContainer.append(this.getNewButton(position));
273+
elementsContainer.append(this.getActions(position));
263274
elementsContainer.append(this.getUiElement(element, position));
264275
}.bind(this));
265-
elementsContainer.append(this.getNewButton(this.uiElements.length));
266-
}
267-
268-
getNewButton(position) {
269-
let buttonWrapper = document.createElement('div');
270-
buttonWrapper.innerHTML = Mustache.render(this.config.buttonAddHtml, {'position': position});
271-
let button = buttonWrapper.firstElementChild;
272-
button.querySelector('.js-uie-add').position = position;
273-
button.querySelector('.js-uie-add').manager = this;
274-
button.querySelector('.js-uie-add').addEventListener('click', function (e) {
275-
button.querySelector('.js-uie-add').manager.openSelectionPanel(
276-
button.querySelector('.js-uie-add').position
276+
elementsContainer.append(this.getActions(this.uiElements.length));
277+
}
278+
279+
getActions(position) {
280+
let actionsWrapper = document.createElement('div');
281+
actionsWrapper.innerHTML = Mustache.render(this.config.actionsHtml, {'position': position});
282+
283+
let actions = actionsWrapper.firstElementChild;
284+
285+
// Add button
286+
actions.querySelector('.js-uie-add').position = position;
287+
actions.querySelector('.js-uie-add').manager = this;
288+
actions.querySelector('.js-uie-add').addEventListener('click', function (e) {
289+
actions.querySelector('.js-uie-add').manager.openSelectionPanel(
290+
actions.querySelector('.js-uie-add').position
277291
);
278292
});
279-
return button;
293+
294+
// Paste clipboard button
295+
actions.querySelector('.js-uie-paste').position = position;
296+
actions.querySelector('.js-uie-paste').manager = this;
297+
actions.querySelector('.js-uie-paste').addEventListener('click', function (e) {
298+
actions.querySelector('.js-uie-paste').manager.pasteUiElementFromClipboard(
299+
actions.querySelector('.js-uie-paste').position
300+
);
301+
});
302+
// Disabled?
303+
if (!this.isClipboardEmpty()) {
304+
actions.querySelector('.js-uie-paste').classList.remove('disabled');
305+
}
306+
307+
return actions;
280308
}
281309

282310
getUiElement(element, position) {
@@ -312,6 +340,16 @@ global.MonsieurBizRichEditorManager = class {
312340
uiElement.querySelector('.js-uie-edit').addEventListener('click', function () {
313341
this.closest('.js-uie-element').element.edit();
314342
});
343+
uiElement.querySelector('.js-uie-copy').addEventListener('click', function (e) {
344+
this.closest('.js-uie-element').element.copy(function () {
345+
const button = e.currentTarget;
346+
const originalText = button.dataset.tooltip;
347+
button.dataset.tooltip = button.dataset.alternateText;
348+
window.setTimeout(function () {
349+
button.dataset.tooltip = originalText;
350+
}, 1000);
351+
});
352+
});
315353
return uiElement;
316354
}
317355

@@ -577,6 +615,55 @@ global.MonsieurBizRichEditorManager = class {
577615
req.send(data);
578616
}
579617

618+
isClipboardEmpty() {
619+
const clipboard = window.sessionStorage.getItem('monsieurBizRichEditorClipboard');
620+
return null === clipboard || '' === clipboard;
621+
}
622+
623+
saveUiElementToClipboard(uiElement, callback) {
624+
window.sessionStorage.setItem('monsieurBizRichEditorClipboard', JSON.stringify(uiElement));
625+
callback();
626+
document.dispatchEvent(new CustomEvent('mbiz:rich-editor:uielement:copied', {}));
627+
}
628+
629+
pasteUiElementFromClipboard(futurePosition) {
630+
const clipboard = window.sessionStorage.getItem('monsieurBizRichEditorClipboard');
631+
if (null !== clipboard) {
632+
const pastedUiElement = JSON.parse(clipboard);
633+
const manager = this;
634+
manager.requestUiElementsHtml([pastedUiElement], function () {
635+
if (this.status === 200) {
636+
let renderedElements = JSON.parse(this.responseText);
637+
const elementHtml = renderedElements.shift();
638+
if (pastedUiElement.code === undefined && pastedUiElement.type !== undefined) {
639+
pastedUiElement.code = pastedUiElement.type;
640+
pastedUiElement.data = pastedUiElement.fields;
641+
delete pastedUiElement.type;
642+
delete pastedUiElement.fields;
643+
}
644+
let uiElement = manager.config.findUiElementByCode(pastedUiElement.code);
645+
if (null !== uiElement) {
646+
if (manager.tags.length > 0) {
647+
let copy = false;
648+
for (let tagIndex in manager.tags) {
649+
if (0 <= manager.config.uielements[uiElement.code].tags.indexOf(manager.tags[tagIndex])) {
650+
copy = true;
651+
}
652+
}
653+
if (copy) {
654+
manager.create(uiElement.code, pastedUiElement.data, elementHtml, futurePosition);
655+
} else {
656+
alert(manager.config.unallowedUiElementMessage);
657+
}
658+
} else {
659+
manager.create(uiElement.code, pastedUiElement.data, elementHtml, futurePosition);
660+
}
661+
}
662+
}
663+
});
664+
}
665+
}
666+
580667
dispatchInitFormEvent(form) {
581668
document.dispatchEvent(new CustomEvent('monsieurBizRichEditorInitForm', {
582669
'detail': {'form': form}

src/Resources/public/js/rich-editor-js.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Resources/translations/messages.en.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ monsieurbiz_richeditor_plugin:
8989
form:
9090
add: 'Add an element'
9191
move: 'Move'
92+
move_up: 'Move Up'
93+
move_down: 'Move Down'
9294
edit: 'Edit'
9395
delete: 'Delete'
9496
close: 'Close'
@@ -112,6 +114,10 @@ monsieurbiz_richeditor_plugin:
112114
center: 'Center'
113115
right: 'Right'
114116
justify: 'Justify'
117+
clipboard: 'Clipboard'
118+
paste_from_clipboard: 'Paste from clipboard'
119+
copied: 'Copied!'
120+
copy: 'Copy'
115121
action:
116122
new: 'Toggle available widgets'
117123
preview: 'Preview'
@@ -120,5 +126,6 @@ monsieurbiz_richeditor_plugin:
120126
cannot_find_type: 'Cannot find type for some UI Elements'
121127
cannot_upload_image: 'Cannot upload the image. The file may be too large or have incorrect format.'
122128
ajax_error: 'There is a technical problem. Please contact your administrator.'
129+
unallowed_uielement: 'Element not allowed here'
123130
ui:
124131
disabled: 'Disabled'

src/Resources/translations/messages.fr.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ monsieurbiz_richeditor_plugin:
8989
form:
9090
add: 'Ajouter un élément'
9191
move: 'Déplacer'
92+
move_up: 'Déplacer vers le haut'
93+
move_down: 'Déplacer vers le bas'
9294
edit: 'Éditer'
9395
delete: 'Supprimer'
9496
close: 'Fermer'
@@ -112,6 +114,10 @@ monsieurbiz_richeditor_plugin:
112114
center: 'Centré'
113115
right: 'Droite'
114116
justify: 'Justifié'
117+
clipboard: 'Presse-papier'
118+
paste_from_clipboard: 'Coller depuis le presse-papier'
119+
copied: 'Copié !'
120+
copy: 'Copier'
115121
action:
116122
new: 'Afficher les widgets disponibles'
117123
preview: 'Prévisualiser'
@@ -120,5 +126,6 @@ monsieurbiz_richeditor_plugin:
120126
cannot_find_type: 'Impossible de trouver le type pour certains éléments'
121127
cannot_upload_file: 'Impossible de charger le fichier. Il est possible qu''il soit trop lourd ou son format incorrect.'
122128
ajax_error: 'Un problème technique est survenu. Merci de contacter votre administrateur.'
129+
unallowed_uielement: 'Élément non autorisé ici'
123130
ui:
124131
disabled: 'Désactivé'

src/Resources/views/Admin/app.html.twig

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
{% include '@SyliusUi/_javascripts.html.twig' with {'path': 'bundles/monsieurbizsyliusricheditorplugin/js/rich-editor-js.js'} %}
22
{% include '@SyliusUi/_stylesheets.html.twig' with {'path': 'bundles/monsieurbizsyliusricheditorplugin/css/rich-editor-css.css'} %}
33

4-
<script id="monsieurbiz-rich-editor-button-add" type="x-tmpl-mustache">
4+
<script id="monsieurbiz-rich-editor-actions" type="x-tmpl-mustache">
55
{% verbatim %}
66
<div class="ui horizontal divider">
7-
<button data-position="{{position}}" class="tiny ui basic button uie-m-0 js-uie-add" type="button">
8-
<i class="plus square icon"></i>
9-
{% endverbatim %}{{ "monsieurbiz_richeditor_plugin.form.add"|trans }}{% verbatim %}
10-
</button>
7+
<div class="ui compact tiny menu">
8+
<a data-position="{{position}}" class="item js-uie-add" type="button">
9+
<i class="plus square icon"></i>
10+
{% endverbatim %}{{ "monsieurbiz_richeditor_plugin.form.add"|trans }}{% verbatim %}
11+
</a>
12+
<a data-position="{{position}}" class="item js-uie-paste disabled" type="button">
13+
<i class="paste icon"></i>
14+
{% endverbatim %}{{ "monsieurbiz_richeditor_plugin.form.paste_from_clipboard"|trans }}{% verbatim %}
15+
</a>
16+
</div>
1117
</div>
1218
{% endverbatim %}
1319
</script>
@@ -23,18 +29,21 @@
2329
{{#disabled}}<span class="ui label">{% endverbatim %}{{ 'monsieurbiz_richeditor_plugin.ui.disabled'|trans }}{% verbatim %}</span>{{/disabled}}
2430
</h3>
2531
<div class="ui small basic icon buttons sm:uie-mt-xs">
26-
<button class="ui button js-uie-delete" type="button">
32+
<button class="ui button js-uie-delete" type="button" data-tooltip="{% endverbatim %}{{ 'monsieurbiz_richeditor_plugin.form.delete'|trans }}{% verbatim %}" data-inverted>
2733
<i class="trash alternate outline icon"></i>
2834
</button>
29-
<button class="ui button js-uie-edit" type="button">
35+
<button class="ui button js-uie-edit" type="button" data-tooltip="{% endverbatim %}{{ 'monsieurbiz_richeditor_plugin.form.edit'|trans }}{% verbatim %}" data-inverted>
3036
<i class="pencil alternate icon"></i>
3137
</button>
32-
<button class="ui button js-uie-up" type="button">
38+
<button class="ui button js-uie-up" type="button" data-tooltip="{% endverbatim %}{{ 'monsieurbiz_richeditor_plugin.form.move_up'|trans }}{% verbatim %}" data-inverted>
3339
<i class="arrow up icon"></i>
3440
</button>
35-
<button class="ui button js-uie-down" type="button">
41+
<button class="ui button js-uie-down" type="button" data-tooltip="{% endverbatim %}{{ 'monsieurbiz_richeditor_plugin.form.move_down'|trans }}{% verbatim %}" data-inverted>
3642
<i class="arrow down icon"></i>
3743
</button>
44+
<button class="ui button js-uie-copy" type="button" data-alternate-text="{% endverbatim %}{{ 'monsieurbiz_richeditor_plugin.form.copied'|trans }}{% verbatim %}" data-tooltip="{% endverbatim %}{{ 'monsieurbiz_richeditor_plugin.form.copy'|trans }}{% verbatim %}" data-inverted>
45+
<i class="copy icon"></i>
46+
</button>
3847
</div>
3948
</div>
4049
<div class="ui fluid js-uie-preview">{{{preview}}}</div>
@@ -138,7 +147,7 @@
138147
uielements,
139148
wysiwyg,
140149
document.getElementById('monsieurbiz-rich-editor-ui-elements-container').innerHTML,
141-
document.getElementById('monsieurbiz-rich-editor-button-add').innerHTML,
150+
document.getElementById('monsieurbiz-rich-editor-actions').innerHTML,
142151
document.getElementById('monsieurbiz-rich-editor-ui-element').innerHTML,
143152
document.getElementById('monsieurbiz-rich-editor-ui-element-card').innerHTML,
144153
'{{ "monsieurbiz_richeditor_plugin.form.confirm_delete" |trans|e('js') }}',
@@ -147,7 +156,8 @@
147156
'{{ url("monsieurbiz_richeditor_admin_form_render_elements")|e('js') }}',
148157
'{{ monsieurbiz_richeditor_get_default_element() }}',
149158
'{{ monsieurbiz_richeditor_get_default_element_data_field() }}',
150-
'{{ 'monsieurbiz_richeditor_plugin.error.ajax_error'|trans|e('js') }}'
159+
'{{ 'monsieurbiz_richeditor_plugin.error.ajax_error'|trans|e('js') }}',
160+
'{{ 'monsieurbiz_richeditor_plugin.error.unallowed_uielement'|trans|e('js') }}'
151161
);
152162
editor.manager = new MonsieurBizRichEditorManager(config);
153163
});

0 commit comments

Comments
 (0)