When to use

When users pick many images at once — a gallery, a slider, a set of files attached to a form — selecting them is only half of the job. Usually the order matters, and often every image needs its own alt text or caption.

The picked files line at the bottom of the Flmngr dialog supports reordering and removing items, but with a long list it turns into a long horizontal scroll.

For such cases Flmngr has the Manage panel: a vertical list of all picked files where the user reorders them with drag and drop, removes unneeded ones, and edits the metadata of every file on one screen. All changes are applied on Save and discarded on Cancel.

How it works

Open the file manager the same way as in the manage image gallery sample: set isMultiple to true and pass the URLs of previously selected images in the list parameter.

Then define the metadata fields your app needs with the metadataFields parameter — text fields, text areas and switches are supported. When it is set, every picked file gets a metadata editor, and the Manage tab appears above the picked files line: it opens a panel listing all picked files vertically to reorder them, remove some, and fill their metadata in one place.

You can also preset values with the metadata parameter — a map from a file URL to its metadata. Values edited by the user in the file manager override the preset ones.

When the user confirms the selection, onFinish receives the files in their final order, and each file carries its metadata object.

How to start using

Use this code in a custom application when you install the React file manager component or the JavaScript file manager package from NPM.

If your app doesn't use NPM at all, you can quickly install the file manager JS snippet instead.

These packages provide a powerful Flmngr API, which this and other code samples demonstrate.

Demo
Code
HTML
CSS
import {Flmngr} from "flmngr";

Flmngr.load({
  apiKey: "FLMN24RR1234123412341234",
  urlFileManager: 'https://flmngr.democdn.com/file-manager-default',
  urlFiles: 'https://flmngr.democdn.com/files/default'
}, {
  onFlmngrLoaded: () => {
    attachOnClickListenerToButton();
  }
});

function selectFiles() {

  // Collect URLs of images of existing gallery set
  // (from the URL line, `src` of images has a no-cache suffix)
  let elsExistingUrls = document.querySelectorAll(".image > p.image-url");
  let urls = [];
  for (let i=0; i<elsExistingUrls.length; i++)
    urls.push(elsExistingUrls.item(i).textContent);

  // Metadata can also be preset via API: here the first image gets an alt text
  // (a value edited in the file manager wins over it)
  let metadata = {};
  if (urls.length > 0)
    metadata[urls[0]] = {alt: "Alt passed via API"};

  Flmngr.open({
    list: urls,
    isMultiple: true,
    acceptExtensions: ["png", "jpeg", "jpg", "webp", "gif"],
    metadataFields: [
      {name: "alt", type: "textfield", title: "Alt text"},
      {name: "caption", type: "textarea", title: "Caption"},
      {name: "align", type: "switch", title: "Align", values: ["", "left", "right"]}
    ],
    metadata: metadata,
    onFinish: (files) => {
      showSelectedImages(files);
    }
  });
}

function showSelectedImages(files) {
  let elImages = document.getElementById("images");
  elImages.innerHTML = "";

  let elP = document.createElement("p");
  elP.textContent = files.length + " images selected";
  elImages.appendChild(elP);

  for (let file of files) {

    let el = document.createElement("div");
    el.className = "image";
    elImages.appendChild(el);

    let elImg = document.createElement("img");
    elImg.src = Flmngr.getNoCacheUrl(file.url);
    elImg.alt = (file.metadata && file.metadata.alt) || "Image selected in Flmngr";
    el.appendChild(elImg);

    let elUrl = document.createElement("p");
    elUrl.className = "image-url";
    elUrl.textContent = file.url;
    el.appendChild(elUrl);

    let elMeta = document.createElement("p");
    elMeta.className = "image-meta";
    elMeta.textContent = "metadata: " + JSON.stringify(file.metadata || {});
    el.appendChild(elMeta);

  }
}

function attachOnClickListenerToButton() {
  let elBtn = document.getElementById("btn");

  // Style button as ready to be pressed
  elBtn.style.opacity = 1;
  elBtn.style.cursor = "pointer";
  let elLoading = document.getElementById("loading");
  elLoading.parentElement.removeChild(elLoading);

  // Add a listener for selecting files
  elBtn.addEventListener("click", () => {
    selectFiles();
  });
}
<h2 class="mb-3">Flmngr: Manage Picked Files</h2>

<div id="btn" class="btn btn-primary btn-lg" style="opacity:0.2;cursor:default">Manage gallery</div>

<div id="loading" style="font-size:12px">Loading file manager...</div>

<p class="hint"><b>Hint</b>: after picking several images, press the <b>Manage</b> tab above the picked files line<br/> to reorder them, remove some, and edit their metadata in one place.</p>

<h4 class="mt-5">Image gallery</h4>

<div id="images">
  <p>0 images selected</p>
</div>
body {
  margin: 0;
  padding: 20px;
  background-color: #F4F4F4;
}

h2 {
  font-weight: bold;
}

#images {

  .image {
    border: 1px solid #DDD;
    box-shadow: 5px 5px 0 0 #DDD;
    background-color: white;
    padding: 10px;
    display: inline-flex;
    flex-direction: column;
    margin: 0 25px 25px 0;
    max-width: 300px;

    img {
      max-height: 250px;
      border: 2px solid #007FFF;
    }

    p {
      margin: 5px 0 0 0;
      font-size: 14px;
      color: #444;
    }

    p.image-meta {
      font-family: monospace;
      font-size: 12px;
      color: #007FFF;
      word-break: break-all;
    }
  }

}

.hint {
  color: #007FFF;
  font-size: 14px;
  margin-top: 10px;
  line-height: 16px;
}