Skip to content

Commit 769c4bd

Browse files
authored
Merge pull request #81 from prestaconcept/issue-79
Add bootstrap 5 support
2 parents 7e92d96 + 0827d93 commit 769c4bd

File tree

3 files changed

+139
-17
lines changed

3 files changed

+139
-17
lines changed

README.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ composer require presta/image-bundle
2121

2222
### Configure the bundle
2323

24-
You must use the `bootstrap_4.html.twig` form theme into `config/packages/twig.yaml`.
24+
You must use the `bootstrap_5.html.twig` form theme into `config/packages/twig.yaml`.
2525

2626
```yaml
2727
twig:
2828
form_themes:
29-
- "@PrestaImage/form/bootstrap_4.html.twig"
29+
- "@PrestaImage/form/bootstrap_5.html.twig"
3030
```
3131
32-
> Note: you can also use the `bootstrap_3.html.twig` form theme instead.
32+
> Note: you can also use the `bootstrap_4.html.twig` or the `bootstrap_3.html.twig` form theme instead.
3333

3434
You must include the routing into `config/routes.yaml`:
3535

@@ -66,13 +66,21 @@ Note that [jQuery][4] and [Bootstrap][5] are required and must be included.
6666
6767
$(function() {
6868
$('.cropper').each(function() {
69-
new Cropper($(this));
69+
new Cropper($(this), true);
7070
});
7171
});
7272
7373
})(window, jQuery);
7474
```
7575

76+
Note that you must skip the second parameter (or set it to `false`) if you are using a version of bootstrap < 5.
77+
78+
```javascript
79+
// ...
80+
new Cropper($(this));
81+
// ...
82+
```
83+
7684
### Use the form type
7785

7886
```php
@@ -135,9 +143,9 @@ Thanks to
135143
Released under the MIT License
136144

137145
[1]: https://github.com/fengyuanchen/jquery-cropper
138-
[2]: https://github.com/dustin10/VichUploaderBundle/blob/master/Resources/doc/usage.md
146+
[2]: https://github.com/dustin10/VichUploaderBundle/blob/master/docs/usage.md
139147
[3]: https://github.com/fengyuanchen/jquery-cropper#installation
140148
[4]: https://jquery.com/download/
141-
[5]: https://getbootstrap.com/docs/4.4/getting-started/download/
149+
[5]: https://getbootstrap.com/docs/5.1/getting-started/download/
142150
[6]: https://github.com/prestaconcept/PrestaImageBundle/blob/master/Resources/doc/webpack.md
143151
[7]: https://github.com/fengyuanchen/cropperjs#options

Resources/public/js/cropper.js

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
1+
const bootstrap = require('bootstrap');
12
const CropperJS = require('cropperjs');
23

34
(function(w, $) {
45

56
'use strict';
67

7-
const Cropper = function($el) {
8+
const Cropper = function($el, modalV5 = false) {
89
this.$el = $el;
910
this.options = $.extend({}, $el.data('cropper-options'));
1011

1112
this
12-
.initElements()
13+
.initElements(modalV5)
1314
.initLocalEvents()
1415
.initRemoteEvents()
1516
.initCroppingEvents()
1617
;
1718
};
1819

19-
Cropper.prototype.initElements = function() {
20+
Cropper.prototype.initElements = function(modalV5) {
2021
this.$modal = this.$el.find('.modal');
2122
this.$aspectRatio = this.$modal.find('input[name="cropperAspectRatio"]');
2223
this.$rotator = this.$modal.find('.rotate');
@@ -43,6 +44,7 @@ const CropperJS = require('cropperjs');
4344
});
4445

4546
this.cropper = null;
47+
this.modal = modalV5 ? new bootstrap.Modal(this.$modal) : undefined;
4648

4749
return this;
4850
};
@@ -151,18 +153,29 @@ const CropperJS = require('cropperjs');
151153
}
152154
});
153155

154-
this.$modal
155-
.one('shown.bs.modal', function() {
156-
// (re)build croppable image once the modal is shown (required to get proper image width)
156+
this.$modal.each((index, element) => {
157+
const rebuildCroppableImage = () => {
157158
$('<img>')
158159
.attr('src', base64)
159160
.on('load', function() {
160161
self.cropper = new CropperJS(this, self.options)
161162
})
162-
.appendTo(self.$container.$preview);
163-
})
164-
.modal('show')
165-
;
163+
.appendTo(self.$container.$preview)
164+
;
165+
}
166+
167+
// support for bootstrap < 5
168+
$(element).one('shown.bs.modal', rebuildCroppableImage);
169+
170+
// support for bootstrap >= 5
171+
element.addEventListener('shown.bs.modal', rebuildCroppableImage, {once: true})
172+
})
173+
174+
if (this.modal) {
175+
this.modal.show();
176+
} else {
177+
this.$modal.modal('show');
178+
}
166179
};
167180

168181
/**
@@ -198,7 +211,11 @@ const CropperJS = require('cropperjs');
198211
this.$input.val(image_canvas.toDataURL(this.$el.data('mimetype'), this.$el.data('quality')));
199212

200213
// hide the modal
201-
this.$modal.modal('hide');
214+
if (this.modal) {
215+
this.modal.hide();
216+
} else {
217+
this.$modal.modal('hide');
218+
}
202219
};
203220

204221
if (typeof module !== 'undefined' && 'exports' in module) {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
{% trans_default_domain 'PrestaImageBundle' %}
2+
3+
{% block image_widget %}
4+
{% apply spaceless %}
5+
<div class="cropper" data-cropper-options="{{ form.vars.cropper_options }}" data-max-width="{{ max_width }}" data-max-height="{{ max_height }}" data-mimetype="{{ upload_mimetype }}" data-quality="{{ upload_quality }}">
6+
<div class="row">
7+
{% if enable_locale %}
8+
<div class="col-4 cropper-local">
9+
<input type="file" name="file" class="d-none" />
10+
<button type="button" class="{{ upload_button_class }}">
11+
<span class="{{ upload_button_icon }}"></span>
12+
{{ 'btn_import_image_local'|trans }}
13+
</button>
14+
</div>
15+
{% endif %}
16+
{% if enable_remote %}
17+
<div class="col-8 cropper-remote">
18+
<div class="input-group">
19+
<input type="url" class="image-url-input form-control form-control-sm" placeholder="{{ 'image_dist_placeholder'|trans }}" />
20+
<div class="input-group-append">
21+
<button type="button" class="btn btn-sm btn-primary btn-upload-dist" disabled="disabled" data-url="{{ path('presta_image_url_to_base64') }}">
22+
<span class="fa fa-upload"></span>
23+
{{ 'btn_import_image_remote'|trans }}
24+
</button>
25+
<div class="remote-loader spinner d-none">
26+
<div class="rect1"></div>
27+
<div class="rect2"></div>
28+
<div class="rect3"></div>
29+
<div class="rect4"></div>
30+
</div>
31+
</div>
32+
</div>
33+
</div>
34+
{% endif %}
35+
{% if form.delete is defined %}
36+
<div class="col-12">
37+
{{ form_row(form.delete) }}
38+
</div>
39+
{% endif %}
40+
</div>
41+
42+
<div class="cropper-canvas-container mt-2{% if form.delete is defined %} cropper-canvas-has-delete{% endif %}" data-preview-width="{{ preview_width }}" data-preview-height="{{ preview_height }}">
43+
{% if form.vars.download_uri is defined and form.vars.download_uri %}
44+
<img id="pula" src="{{ asset(form.vars.download_uri) }}" style="max-width: {{ preview_width }}; max-height: {{ preview_height }};">
45+
{% endif %}
46+
</div>
47+
{{ form_row(form.base64) }}
48+
49+
{% set show_aspect_ratios = aspect_ratios|length > 1 %}
50+
<div class="modal fade">
51+
<div class="modal-dialog modal-lg">
52+
<div class="modal-content">
53+
<div class="modal-header">
54+
<h5 class="modal-title">{{ 'resize_image'|trans }}</h5>
55+
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
56+
</div>
57+
<div class="modal-body">
58+
<div class="row">
59+
<div class="{% if show_aspect_ratios %}col-10{% else %}col-12{% endif %}">
60+
<div class="cropper-preview"></div>
61+
</div>
62+
{% if show_aspect_ratios %}
63+
<div class="col-2">
64+
<div class="btn-group-vertical float-right">
65+
{% for aspect_ratio in aspect_ratios %}
66+
<label class="btn btn-primary mb-0{% if aspect_ratio.checked %} active{% endif %}">
67+
<input type="radio" name="cropperAspectRatio" class="d-none" value="{{ aspect_ratio.value }}"{% if aspect_ratio.checked %} checked="checked"{% endif %}>
68+
{{ aspect_ratio.label|trans }}
69+
</label>
70+
{% endfor %}
71+
</div>
72+
</div>
73+
{% else %}
74+
{% for aspect_ratio in aspect_ratios %}
75+
<input type="hidden" name="cropperAspectRatio" value="{{ aspect_ratio.value }}"{% if aspect_ratio.checked %} checked="checked"{% endif %}>
76+
{% endfor %}
77+
{% endif %}
78+
</div>
79+
{% if enable_rotation %}
80+
<div class="row">
81+
<div class="toolbar {% if show_aspect_ratios %}col-10{% else %}col-12{% endif %}">
82+
<button class="btn btn-default rotate" data-rotate="90"></button>
83+
<button class="btn btn-default rotate anti-rotate" data-rotate="-90"></button>
84+
</div>
85+
</div>
86+
{% endif %}
87+
</div>
88+
<div class="modal-footer">
89+
<button type="button" class="{{ cancel_button_class }}" data-bs-dismiss="modal">{{ 'btn_cancel'|trans }}</button>
90+
<button type="button" class="{{ save_button_class }}" data-method="getCroppedCanvas">{{ 'btn_validate'|trans }}</button>
91+
</div>
92+
</div>
93+
</div>
94+
</div>
95+
</div>
96+
{% endapply %}
97+
{% endblock %}

0 commit comments

Comments
 (0)