Skip to content

Commit 35a5ed0

Browse files
committed
Cropper Image Upload and ImageCarousel
1 parent 19ca9ec commit 35a5ed0

4 files changed

Lines changed: 133 additions & 11 deletions

File tree

css/bootstrap.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,4 @@ input.plain-checkbox {
212212
margin-left: 15px;
213213
}
214214

215+

js/cropper/cropper-ui.js

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,8 @@ class CropperUI {
218218
scaleX: 1,
219219
scaleY: 1,
220220
};
221-
cropperUI.createCropper(this, options);
221+
var cropperOptions = { autoCrop: false };
222+
cropperUI.createCropper(this, options, cropperOptions);
222223
});
223224
jQuery('[data-toggle="tooltip"]').tooltip();
224225
jQuery('.cropper-nav a.cropper-nav-link').each(function(index) {
@@ -240,12 +241,12 @@ class CropperUI {
240241
}
241242
}
242243

243-
createCropper(domElement, options) {
244+
createCropper(domElement, options, cropperOptions) {
244245
var elem = jQuery(domElement);
245246
options.destroyed = false;
246-
options.cropper = new Cropper(domElement, {
247-
aspectRatio: 1,
248-
});
247+
if (!cropperOptions) cropperOptions = {};
248+
cropperOptions.aspectRatio= 1;
249+
options.cropper = new Cropper(domElement, cropperOptions);
249250
elem.data('upload', options);
250251
this.registerEvents(domElement);
251252
}
@@ -332,12 +333,12 @@ class CropperUI {
332333
return rc;
333334
}
334335

335-
selectImage(domElement, checkChange) {
336+
selectImage(domElement, checkChange, ignoreIdChange) {
336337
// ask for confirmation when image changed
337338
var cropper = this.getCropper(domElement);
338339
var options = this.getOptions(domElement);
339340
var imgId = jQuery(domElement).data('imgid');
340-
if (!cropper || options.originalImageId != imgId) {
341+
if (!cropper || ignoreIdChange || (options.originalImageId != imgId)) {
341342
if (checkChange) {
342343
if (cropper && options.changed) {
343344
var modal = new SelectImageModal(domElement);
@@ -358,7 +359,10 @@ class CropperUI {
358359
scaleX: 1,
359360
scaleY: 1,
360361
};
361-
this.createCropper(image[0], options);
362+
var cropperOptions = {
363+
autoCrop: false,
364+
};
365+
this.createCropper(image[0], options, cropperOptions);
362366
}
363367
}
364368

@@ -463,7 +467,7 @@ class CropperUI {
463467
});
464468
}
465469
// Send data options for modifications
466-
webApp.POST(document.location, data, new SaveImageAjaxController(options));
470+
webApp.POST(document.location, data, new SaveImageAjaxController(domElement));
467471
}
468472
}
469473

@@ -576,15 +580,43 @@ class DeleteImageModal extends ChangeConfirmModal {
576580

577581
class SaveImageAjaxController extends WebAppDefaultAjaxController {
578582

579-
constructor(options) {
583+
constructor(domElement) {
580584
super();
581-
this.options = options;
585+
this.domElement = domElement;
582586
}
583587

584588
done(ajaxParams, data, textStatus, jqXHR) {
585589
super.done(ajaxParams, data, textStatus, jqXHR);
586590
if (data.success) {
591+
var options = cropperUI.getOptions(this.domElement);
587592
// Change nav and editor URL (replace/reset?), set changed to FALSE
593+
var nav = cropperUI.getNav(this.domElement);
594+
if (nav) {
595+
var link = nav.find('a[data-imgid="'+options.originalImageId+'"]');
596+
var d = new Date();
597+
link.find('img').attr('src', data.data.path+'?'+d.getTime());
598+
link.data('imgid', data.data.id);
599+
link.attr('data-imgid', data.data.id);
600+
options.changed = false;
601+
// Select image
602+
cropperUI.selectImage(link[0], false, true);
603+
} else {
604+
// No nav to help here
605+
options = {
606+
replacedImageId: null,
607+
originalImageId: data.data.id,
608+
originalImageURL: data.data.path,
609+
uploadedImageType: 'image/jpeg',
610+
uploadedImageName: 'upload.jpeg',
611+
uploadedImageURL: null,
612+
scaleX: 1,
613+
scaleY: 1,
614+
file: null,
615+
};
616+
cropperUI.destroy(this.domElement);
617+
var image = cropperUI.getImage(this.domElement);
618+
cropperUI.createCropper(image[0], options);
619+
}
588620
} else {
589621
this.showError();
590622
}
@@ -623,6 +655,7 @@ class DeleteImageAjaxController extends WebAppDefaultAjaxController {
623655
}
624656

625657
deleteImage() {
658+
var options = cropperUI.getOptions(this.domElement);
626659
cropperUI.destroy(this.domElement);
627660
// Remove from navigation
628661
var nav = cropperUI.getNav(this.domElement);
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
namespace WebApp\BootstrapTheme;
4+
5+
use TgI18n\I18N;
6+
7+
class ImageCarouselRenderer extends \WebApp\DefaultTheme\DivRenderer {
8+
9+
public function __construct($theme, $component) {
10+
parent::__construct($theme, $component);
11+
$this->addClass('image-carousel');
12+
}
13+
14+
public function renderChildren() {
15+
$rc = '';
16+
$images = $this->component->getImages();
17+
if (count($images) == 0) {
18+
// Placeholder image or nothing?
19+
} else if (count($images) == 1) {
20+
// No navigation - just a simple image
21+
$rc = $this->theme->renderComponent($images[0]->image);
22+
} else if (count($images) > 1) {
23+
$id = $this->component->getId().'-carousel';
24+
25+
// Navigation and carousel
26+
$nav = '<ol class="carousel-indicators">';
27+
$inner = '<div class="carousel-inner">';
28+
$idx = 0;
29+
foreach ($images AS $image) {
30+
$active = $idx == 0 ? 'active' : '';
31+
$nav .= '<li data-target="#'.$id.'" data-slide-to="'.$idx.'" class="'.$active.'"></li>';
32+
$title = htmlentities($image->image->getTitle());
33+
$inner .= '<div class="carousel-item '.$active.'">'.
34+
'<img src="'.$image->image->getUrl().'" class="d-block w-100" alt="'.$title.'" title="'.$title.'">';
35+
if ($image->title || $image->description) {
36+
$inner .= '<div class="carousel-caption d-none d-md-block">';
37+
if ($image->title) $inner .= '<h5>'.$image->title.'</h5>';
38+
if ($image->description) $inner .= '<p>'.$image->description.'</p>';
39+
$inner .= '</div>';
40+
}
41+
$inner .= '</div>';
42+
$idx++;
43+
}
44+
$inner .= '</div>';
45+
$nav .= '</ol>';
46+
47+
// Composite
48+
$rc = '<div id="'.$id.'" class="carousel slide" data-ride="carousel">'.$nav.$inner.
49+
'<a class="carousel-control-prev" href="#'.$id.'" role="button" data-slide="prev">'.
50+
'<span class="carousel-control-prev-icon" aria-hidden="true"></span>'.
51+
'<span class="sr-only">'.I18N::_('previous').'</span>'.
52+
'</a>'.
53+
'<a class="carousel-control-next" href="#'.$id.'" role="button" data-slide="next">'.
54+
'<span class="carousel-control-next-icon" aria-hidden="true"></span>'.
55+
'<span class="sr-only">'.I18N::_('next').'</span>'.
56+
'</a>'.
57+
'</div>';
58+
}
59+
return $rc;
60+
}
61+
}
62+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace WebApp\Component;
4+
5+
class ImageCarousel extends Div {
6+
7+
protected $images;
8+
9+
public function __construct($parent) {
10+
parent::__construct($parent);
11+
$this->images = array();
12+
}
13+
14+
public function addImage($image, $title = NULL, $description = NULL) {
15+
$img = new \stdClass;
16+
$img->image = is_string($image) ? new Image($this, $image, $title) : $image;
17+
$img->title = $title;
18+
$img->descripton = $description;
19+
$this->images[] = $img;
20+
}
21+
22+
public function getImages() {
23+
return $this->images;
24+
}
25+
}
26+

0 commit comments

Comments
 (0)