Skip to content

Commit

Permalink
Reimplement asset selection swapping on “Save as a new asset”
Browse files Browse the repository at this point in the history
Resolves #8974
  • Loading branch information
brandonkelly committed Mar 18, 2022
1 parent 6126e81 commit 40cc20e
Show file tree
Hide file tree
Showing 16 changed files with 129 additions and 56 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

### Changed
- When an image is saved as a new asset from the Image Editor via an Assets field, the Assets field will now automatically replace the selected asset with the new one. ([#8974](https://github.com/craftcms/cms/discussions/8974))

### Deprecated
- Deprecated `craft\base\ApplicationTrait::getInstalledSchemaVersion()`.

Expand Down Expand Up @@ -438,7 +441,6 @@
- Filtering users by `active`, `pending`, and `locked` statuses no longer excludes suspended users.
- `credentialed` and `inactive` are now reserved user group handles.
- Assets fields that are restricted to a single location can now be configured to allow selection within subfolders of that location. ([#9070](https://github.com/craftcms/cms/discussions/9070))
- When an image is saved as a new asset from the Image Editor via an Assets field, the Assets field will now automatically replace the selected asset with the new one. ([#8974](https://github.com/craftcms/cms/discussions/8974))
- `alt` is now a reserved field handle for volume field layouts.
- Volumes no longer have “types”, and their file operations are now delegated to a filesystem selected by an “Asset Filesystem” setting on the volume.
- Volumes now have a “Transform Filesystem” setting, which can be used to choose which filesystem image transforms should be stored in. (The volume’s Asset Filesystem will be used by default.)
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/AssetsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,7 @@ public function actionSaveImage(): Response
// Don't validate required custom fields
Craft::$app->getElements()->saveElement($newAsset);

$output['elementId'] = $newAsset->id;
$output['newAssetId'] = $newAsset->id;
}
} catch (Throwable $exception) {
return $this->asFailure($exception->getMessage());
Expand Down
12 changes: 11 additions & 1 deletion src/elements/Asset.php
Original file line number Diff line number Diff line change
Expand Up @@ -2068,7 +2068,17 @@ public function getPreviewHtml(): string
$('#$editBtnId').on('click', () => {
new Craft.AssetImageEditor($this->id, {
allowDegreeFractions: Craft.isImagick,
onSave: () => {
onSave: data => {
if (data.newAssetId) {
// If this is within an Assets field’s editor slideout, replace the selected asset
const slideout = $('#$editBtnId').closest('[data-slideout]').data('slideout');
if (slideout && slideout.settings.elementSelectInput) {
slideout.settings.elementSelectInput.replaceElement(slideout.\$element.data('id'), data.newAssetId)
.catch(() => {});
}
return;
}
$updatePreviewThumbJs
},
});
Expand Down
19 changes: 14 additions & 5 deletions src/templates/_components/fieldtypes/Assets/input.twig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
{% set condition = condition ?? null -%}
{% set criteria = criteria ?? null -%}
{% set storageKey = storageKey ?? null -%}
{% set disabled = (disabled ?? false) ? true : false %}

<div id="{{ id }}" class="elementselect">
<div class="elements">
Expand All @@ -19,19 +20,27 @@
{% endfor %}
</div>

{% if sources %}
<div class="flex{% if limit and elements|length >= limit %} hidden{% endif %}">
<div class="flex flex-nowrap">
{% if sources %}
{{ tag('button', {
type: 'button',
text: selectionLabel,
class: ['btn', 'add', 'icon', 'dashed'],
class: [
'btn',
'add',
'icon',
'dashed',
disabled ? 'disabled',
limit and elements|length >= limit ? 'hidden',
],
aria: {
label: selectionLabel,
describedby: describedBy ?? false,
},
}) }}
</div>
{% endif %}
{% endif %}
<div class="spinner hidden"></div>
</div>
</div>

{% do view.registerAssetBundle("craft\\web\\assets\\prismjs\\PrismJsAsset") %}
Expand Down
3 changes: 2 additions & 1 deletion src/templates/_components/fieldtypes/Categories/input.twig
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
{% endnav %}
</ul>

<div class="flex">
<div class="flex flex-nowrap">
<button type="button" class="btn add icon dashed" aria-label="{{ selectionLabel }}">{{ selectionLabel }}</button>
<div class="spinner"></div>
</div>
</div>

Expand Down
12 changes: 10 additions & 2 deletions src/templates/_includes/forms/elementSelect.twig
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,25 @@
{% endfor %}
</div>

<div class="flex{% if limit and elements|length >= limit %} hidden{% endif %}">
<div class="flex flex-nowrap">
{{ tag('button', {
type: 'button',
class: ['btn', 'add', 'icon', 'dashed', disabled ? 'disabled']|filter,
class: [
'btn',
'add',
'icon',
'dashed',
disabled ? 'disabled',
limit and elements|length >= limit ? 'hidden',
]|filter,
text: selectionLabel ?? 'Choose'|t('app'),
disabled: disabled,
aria: {
label: selectionLabel ?? 'Choose'|t('app'),
describedby: describedBy ?? false,
}
}) }}
<div class="spinner hidden"></div>
</div>
{% endtag %}

Expand Down
2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/css/cp.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/css/cp.css.map

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/web/assets/cp/src/css/_main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2926,12 +2926,11 @@ $checkboxPadding: $checkboxSize + 4;
min-height: 37px;
margin-top: -7px;

&:after {
.elements:after {
@include clearafter;
}

.element,
.btn {
.element {
@include floatleft;
@include margin(7px, 7px, 0, 0);
}
Expand Down Expand Up @@ -2963,7 +2962,8 @@ $checkboxPadding: $checkboxSize + 4;
@include floatleft;
}

.flex .btn {
.flex {
margin-top: 7px;
float: none !important;
}
}
Expand Down
31 changes: 15 additions & 16 deletions src/web/assets/cp/src/js/AssetSelectInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Craft.AssetSelectInput = Craft.BaseElementSelectInput.extend({
this._attachUploader();
}

this.updateAddElementsBtn();

this.addListener(this.$elementsContainer, 'keydown', this._onKeyDown.bind(this));
this.elementSelect.on('focusItem', this._onElementFocus.bind(this));
},
Expand Down Expand Up @@ -152,23 +154,20 @@ Craft.AssetSelectInput = Craft.BaseElementSelectInput.extend({
}
},

replaceElement: function(elementId, replaceWithId) {
var data = {
elementId: replaceWithId,
siteId: this.settings.criteria.siteId,
thumbSize: this.settings.viewMode
};
enableAddElementsBtn: function() {
this.base();

Craft.sendActionRequest('POST', 'elements/get-element-html', {data})
.then((response) => {
var $existing = this.$elements.filter('[data-id="' + elementId + '"]');
this.removeElement($existing);
let elementInfo = Craft.getElementInfo(response.data.html);
this.selectElements([elementInfo]);
})
.catch(({response}) => {
alert(response.data.message);
});
if (this.$uploadBtn) {
this.$uploadBtn.removeClass('hidden');
}
},

disableAddElementsBtn: function() {
this.base();

if (this.$uploadBtn) {
this.$uploadBtn.addClass('hidden');
}
},

refreshThumbnail: function(elementId) {
Expand Down
82 changes: 61 additions & 21 deletions src/web/assets/cp/src/js/BaseElementSelectInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Craft.BaseElementSelectInput = Garnish.Base.extend({
$elementsContainer: null,
$elements: null,
$addElementBtn: null,
$addElementBtnContainer: null,
$spinner: null,

_initialized: false,

Expand Down Expand Up @@ -62,14 +62,8 @@ Craft.BaseElementSelectInput = Garnish.Base.extend({
this.$container.data('elementSelect', this);

this.$elementsContainer = this.getElementsContainer();

this.$addElementBtn = this.getAddElementsBtn();
if (this.$addElementBtn) {
this.$addElementBtnContainer = this.$addElementBtn.parent('.flex');
if (!this.$addElementBtnContainer.length) {
this.$addElementBtnContainer = null;
}
}
this.$spinner = this.getSpinner();

this.thumbLoader = new Craft.ElementThumbLoader();

Expand Down Expand Up @@ -113,6 +107,10 @@ Craft.BaseElementSelectInput = Garnish.Base.extend({
return this.$container.find('.btn.add:first');
},

getSpinner: function() {
return this.$container.find('.spinner');
},

initElementSelect: function() {
if (this.settings.selectable) {
this.elementSelect = new Garnish.Select({
Expand Down Expand Up @@ -162,18 +160,20 @@ Craft.BaseElementSelectInput = Garnish.Base.extend({
}
},

enableAddElementsBtn: function() {
this.$addElementBtn.removeClass('hidden');
},

disableAddElementsBtn: function() {
let $btn = this.$addElementBtnContainer || this.$addElementBtn;
if ($btn) {
$btn.addClass('hidden');
}
this.$addElementBtn.addClass('hidden');
},

enableAddElementsBtn: function() {
let $btn = this.$addElementBtnContainer || this.$addElementBtn;
if ($btn) {
$btn.removeClass('hidden');
}
showSpinner: function() {
this.$spinner.removeClass('hidden');
},

hideSpinner: function() {
this.$spinner.addClass('hidden');
},

focusNextLogicalElement: function() {
Expand Down Expand Up @@ -248,13 +248,53 @@ Craft.BaseElementSelectInput = Garnish.Base.extend({
},

createElementEditor: function($element, settings) {
if (!settings) {
settings = {};
}
settings.prevalidate = this.settings.prevalidate;
settings = Object.assign({
elementSelectInput: this,
prevalidate: this.settings.prevalidate,
}, settings);

return Craft.createElementEditor(this.settings.elementType, $element, settings);
},

replaceElement: function(elementId, replacementId) {
return new Promise((resolve, reject) => {
const $existing = this.$elements.filter(`[data-id="${elementId}"]`);

if (!$existing.length) {
reject(`No element selected with an ID of ${elementId}.`);
return;
}

this.showSpinner();

const data = {
elementId: replacementId,
siteId: this.settings.criteria.siteId,
thumbSize: this.settings.viewMode
};

Craft.sendActionRequest('POST', 'elements/get-element-html', {data})
.then((response) => {
this.removeElement($existing);
const elementInfo = Craft.getElementInfo(response.data.html);
this.selectElements([elementInfo]);
resolve();
})
.catch(({response}) => {
if (response && response.data && response.data.message) {
alert(response.data.message);
} else {
Craft.cp.displayError();
}

reject(response.data.message);
})
.finally(() => {
this.hideSpinner();
});
});
},

removeElements: function($elements) {
if (this.settings.selectable) {
this.elementSelect.removeItems($elements);
Expand Down
2 changes: 2 additions & 0 deletions src/web/assets/cp/src/js/ElementEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ Craft.ElementEditor = Garnish.Base.extend({
console.warn('Double-instantiating an element editor on an element.');
this.$container.data('elementEditor').destroy();
}

this.$container.data('elementEditor', this);
this.$container.attr('data-element-editor', '');

this.setSettings(settings, Craft.ElementEditor.defaults);

Expand Down
1 change: 1 addition & 0 deletions src/web/assets/cp/src/js/ElementEditorSlideout.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,6 @@ Craft.ElementEditorSlideout = Craft.CpScreenSlideout.extend({
elementIndex: null,
onSaveElement: null,
validators: [],
elementSelectInput: null,
},
});
1 change: 1 addition & 0 deletions src/web/assets/cp/src/js/Slideout.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

this.$outerContainer = $('<div/>', {class: 'slideout-container hidden'});
this.$container = $(`<${this.settings.containerElement}/>`, this.settings.containerAttributes)
.attr('data-slideout', '')
.addClass('slideout')
.append(contents)
.data('slideout', this)
Expand Down

0 comments on commit 40cc20e

Please sign in to comment.