wwwision/neos-mediabrowserdialog

JavaScripts to provide asset selection via Media Browser in Neos Backend Modules

Fund package maintenance!
bwaidelich
Paypal

Installs: 3 522

Dependents: 0

Suggesters: 0

Security: 0

Stars: 7

Watchers: 3

Forks: 2

Open Issues: 0

Language:JavaScript

Type:neos-package

2.4.0 2023-11-27 14:22 UTC

This package is auto-updated.

Last update: 2024-11-27 16:25:22 UTC


README

JavaScripts to provide asset selection via Media Browser in Neos Backend Modules

Installation

Install via composer:

composer require wwwision/neos-mediabrowserdialog

Usage

In the configuration of a Neos backend module, include the provided dist.js file:

Neos:
  Neos:
    modules:
      'administration':
        submodules:
          'some-module':
            # ...
            additionalResources:
              javaScripts:
                - 'resource://Wwwision.Neos.MediaBrowserDialog/Public/dist.js'
              styleSheets:
                - 'resource://Wwwision.Neos.MediaBrowserDialog/Public/dist.css'

With that in place, the following data attributes can be added to HTML elements in the module in order to include the Neos Media Browser and/or asset preview:

Example

Simple asset selection

To render just the input field and a button to open the Media Browser:

<div data-asset>
  <input type="text" name="assetId" data-asset-field/>
  <a href="#" data-asset-browse>
</div>

Asset preview

To render a preview image and the label of a previously selected asset:

<div data-asset="{someAssetId}">
  <template data-asset-preview-template>
    <figure data-asset-hide-if-missing>
      <img style="width: 100px" data-asset-preview-image/>
      <figcaption data-asset-preview-label></figcaption>
    </figure>
    <span data-asset-hide-if-set>-</span>
  </template>
  <div data-asset-preview-container data-asset-hide-if-missing></div>
</div>

Elements with the data-asset-hide-if-missing attribute will be hidden when no asset is selected. Elements with the data-asset-hide-if-set attribute will only be visible if no asset is selected – this allows for rendering fallbacks.

Example 01 (Single image upload, Fluid)

The following example renders a hidden field and browse button. If an asset is selected, the browse button is replaced with the label of the asset and a button to replace the asset. If the label is clicked, the Media Browser is opened allowing to inspect or replace the selected asset.

Furthermore, the Media Browser will be limited to only show documents and images of the neos and someAssetSource Asset sources (see Asset Constraints):

<div data-asset="{someAssetId}" data-asset-constraints-media-types="application/*,image/*" data-asset-constraints-asset-sources="neos,someAssetSource">
  <template data-asset-preview-template>
    <a href="#" data-asset-browse>
      <i class="fas fa-camera icon-white"></i> <span data-asset-preview-label></span>
    </a>
    <a data-asset-replace href="#" class="neos-button" title="Replace asset">
      <i class="fas fa-trash icon-white"></i>
    </a>
    <f:form.hidden property="someAsset" value="{someAssetId}" data="{asset-field: true}" additionalAttributes="{disabled: true}" />
  </template>
  <div data-asset-preview-container data-asset-hide-if-missing></div>
  <div data-asset-hide-if-set>
    <a class="neos-button neos-button-icon" data-asset-browse>
      <i class="fas fa-camera icon-white"></i> Browse assets
    </a>
  </div>
</div>

Example 02 (Multiple images upload, Fluid)

The following example, renders a list of {images} as preview image. Next to every image, move buttons are shown and a delete button is shown that removes the specific asset from the list. Underneath the list, a Browse button allows to select additional images and finally the whole list can be emptied with an additional button:

<div data-asset="<f:for each='{images}' as='image'>{image.identifier},</f:for>"
     data-asset-constraints-media-types="image/*"
     data-asset-constraints-asset-sources="neos"
     data-asset-multiple>
  <template data-asset-preview-template>
    <div>
      <h2 data-asset-preview-label></h2>
      <img data-asset-preview-image />
      <f:form.hidden name="images[]" data="{asset-field: true}" additionalAttributes="{disabled: true}" />
      <a data-asset-move-up href="#" class="neos-button" title="Move this image one position up">
        <i class="fas fa-arrow-up icon-white"></i>
      </a>
      <a data-asset-move-down href="#" class="neos-button" title="Move this image one position down">
        <i class="fas fa-arrow-down icon-white"></i>
      </a>
      <a data-asset-replace href="#" class="neos-button neos-button-danger" title="Remove this image">
        <i class="fas fa-eraser icon-white"></i>
      </a>
    </div>
  </template>
  <div data-asset-preview-container data-asset-hide-if-missing></div>
  <div data-asset-hide-if-set>
    No images have been selected yet
  </div>
  <div class="neos-button-group">
    <div data-asset-hide-if-missing>
      <a href="#" class="neos-button" data-asset-browse>
        <i class="fas fa-camera icon-white"></i> Add image(s)
      </a>
      <a data-asset-replace href="#" class="neos-button" title="Remove all">
        <i class="fas fa-eraser icon-white"></i>
      </a>
    </div>
    <div data-asset-hide-if-set>
      <a class="neos-button neos-button-icon" data-asset-browse>
        <i class="fas fa-camera icon-white"></i> Add image(s)
      </a>
    </div>
  </div>
</div>

Note: For multiple assets to be mapped correctly, you might need to explicitly allow them in the MVC controller:

class SomeController extends ActionController {

    // ...

    protected function initializeUpdateAction(): void
    {
        $this->arguments->getArgument('images')->getPropertyMappingConfiguration()->allowAllProperties();
    }

    /**
     * @param array<ImageInterface> $images
     * @Flow\IgnoreValidation("$images")
     */
    public function updateAction(array $images = []): void
    {
        // ...
    }
    
}

Events

The following events are dispatched on the container element (with the data-asset attribute):

Example

document.querySelectorAll('[data-asset]').forEach(container => {
  container.addEventListener('assetChosen', e => console.log('Asset selected: ' + e.detail));
  container.addEventListener('assetRemoved', e => console.log('Asset removed'));
});

Acknowledgements

The idea for this package and parts of the implementation are inspired by Alois Rietzler from brandung GmbH, thank you!

Contribution

Contributions in the form of issues or pull requests are highly appreciated.

License

See LICENSE