Draggable LocalFile fileInput

https://observablehq.com/@tomlarkworthy/fileinput

A combination of @fil/fileuploader and @mbostock/localfile for a draggable file input that has all the convenience methods of a FileAttachment (e.g. .json()) and has a hover effect. Quite a good example of how much nicer using the view literal is.

I have not added many options, but Suggestions are welcome.


function fileInput({ prompt = 'Drag a file here', accept = null } = {}) {
  const output = Inputs.input();
  const input = htl.html`<input type="file">`;
  input.oninput = () => {
    output.value = new LocalFile(input.files[0]);
    output.dispatchEvent(new Event("input", { bubbles: true }));
  };

  input.ondragenter = evt => {
    input.classList.add("hover");
  };

  input.ondragleave = evt => {
    input.classList.remove("hover");
  };

  input.ondrop = evt => {
    input.classList.remove("hover");
  };

  return viewUI`<div class="dropfileContainer">
    <style>
      .dropfileContainer > input[type=file] {
        width:100%;
        height:80px;
        background:#fefef2;
        border:solid 2px black;
        border-radius: 10px;
        padding-top: 10px;
      }
      
      .dropfileContainer > input[type=file].hover {
        background: #deded2;
      }
    </style>

    <p style="position: absolute;
              margin-top: 30px;
              margin-left: auto;
              margin-right: auto;
              left: 0;
              right: 0;
              text-align: center;
              pointer-events: none;">
      ${prompt}
    </p>
    ${input}
    <!-- ${['...', output]} -->
  </div>`;
}
const example_file = view(fileInput())
example_file.text()
//import { LocalFile } from '@mbostock/localfile'
//import { LocalFile } from '/components/localfile.js';

// --- Use the real attachment as the "AbstractFile" base and as a fallback value ---
const emptyAttachment = FileAttachment("empty@1"); // <-- must be a real SQLite DB

// Derive AbstractFile from a real attachment (same as in notebooks)
const AbstractFile = emptyAttachment.constructor.__proto__;


class LocalFile extends AbstractFile {
  constructor(file) {
    super(file.name);
    Object.defineProperty(this, "_", {value: file});
    Object.defineProperty(this, "_url", {writable: true});
  }
  async url() {
    return this._url || (this._url = URL.createObjectURL(this._));
  }
  async blob() {
    return this._;
  }
  async stream() {
    return this._.stream();
  }
};
display(LocalFile)
//import { view } from '@tomlarkworthy/view'
import { viewUI } from '/components/view.js';
display(viewUI)
//import { footer } from "@tomlarkworthy/footer"
//footer