@observablehq/stdlib

repository·main·Indexed 21 days ago

https://github.com/observablehq/stdlib

A comprehensive standard library for JavaScript providing essential utilities for DOM manipulation, file handling, and data processing. Optimized for Observable notebooks and web environments, it includes modules for creating DOM elements, managing file attachments and zip archives, handling generators for resource cleanup and data observation, and providing timing utilities via Promises.

Tokens
15.9K
Snippets
78
Records
97
Agent score
74%

What's inside @observablehq/stdlib

  1. Overview of @observablehq/stdlib

    main
    The @observablehq/stdlib is the Observable standard library, providing a collection of utilities and tools designed for use within the Observable ecosystem and general JavaScript environments. It includes modules for DOM manipulation, file handling, and various other data processing utilities.
  2. Handle resource cleanup with invalidation

    main

    The invalidation promise resolves when the current cell is re-evaluated (e.g., when code changes, a referenced input changes, or the cell is re-run). Use this to abort ongoing tasks like fetch requests to prevent resource leaks.

    Note: invalidation is provided by the runtime, not the stdlib, because it resolves to a new promise for every evaluation.

    {
      const controller = new AbortController;
      invalidation.then(() => controller.abort());
      const response = await fetch(url, {signal: controller.signal});
      return response.json();
    }
  3. Observe data sources without loss with Generators.queue

    main

    Generators.queue(initialize) is similar to Generators.observe(initialize), but it is non-lossy.

    If change is called multiple times before the next promise is pulled, the values are queued in order. The generator will return resolved promises sequentially until the queue is empty. This may result in yielding "stale" values if the consumer is slower than the producer, but no data points are skipped.

    {
      const generator = Generators.queue(…);
      const values = [];
      for (const value of generator) {
        if (values.push(await value) >= 4) {
          return values;
        }
      }
    }
  4. Observe data sources with Generators.observe

    main

    Generators.observe(initialize) adapts a push-based data source (like an Observable, EventEmitter, or EventTarget) into a pull-based generator.

    Usage: Pass an initialize function that receives a change function. Calling change(value) triggers the resolution of the current promise. The initialize function can optionally return a dispose function to clean up resources (e.g., removing event listeners) when the generator is disposed.

    Note: This generator is lossy. If change is called multiple times before the next promise is pulled, intermediate values may be skipped. Use Generators.queue if you need to ensure no values are lost.

    Generators.observe(change => {
      // An event listener to yield the element’s new value.
      const inputted = () => change(element.value);
    
      // Attach the event listener.
      element.addEventListener("input", inputted);
    
      // Yield the element’s initial value.
      change(element.value);
    
      // Detach the event listener when the generator is disposed.
      return () => element.removeEventListener("input", inputted);
    })
  5. Create HTML from Markdown with md`string`

    main

    A tagged template literal that returns an HTML element represented by the specified Markdown string (implemented via Marked).

    • Embedded DOM elements are embedded in the generated HTML.
    • Embedded arrays can contain strings (interpreted as Markdown) or DOM elements.
    md`# Hello, world!`
    
    // Example with array of data
    md`
    | Name      | Symbol      | Atomic number |
    |-----------|-------------|---------------|
    ${elements.map(e => `
    | ${e.name} | ${e.symbol} | ${e.number}   |`)}
    `
  6. Load npm modules with require(names...)

    main

    Returns a promise of an Asynchronous Module Definition (AMD) with the specified names, loaded from npm.

    • Supports package names and scoped packages.
    • Supports semver ranges using the @ symbol (e.g., package@1.1).
    • Multiple names can be passed to merge them into a single object.
    d3 = require("d3-array")
    
    // Load multiple and merge
    d3 = require("d3-array", "d3-color")
    
    // Load specific version
    d3 = require("d3-array@1.1")
  7. Create arithmetic progressions with Generators.range

    main

    Generators.range([start, ]stop[, step]) returns a generator yielding an arithmetic progression, similar to Python's range.

    • start: Defaults to 0.
    • stop: The exclusive end value.
    • step: Defaults to 1.

    Behavior:

    • If step is positive, the last element is the largest start + i * step less than stop.
    • If step is negative, the last element is the smallest start + i * step greater than stop.
    • If the range would be infinite, an empty range is returned.

    Floating Point Warning: Due to IEEE 754 precision, results with non-integer steps may have unexpected rounding (e.g., Generators.range(0, 1, 0.2) might yield 0.6000000000000001). For predictable array lengths, map over an integer range instead.

    i = {
      for (const i of Generators.range(0, 100, 1)) {
        yield i;
      }
    }
    
    // Or more simply:
    i = Generators.range(100)
  8. Define custom FileAttachment implementations

    main

    The FileAttachments(resolve) function allows you to define custom file attachment implementations when working directly with the Observable runtime.

    The resolve function you provide should take a name and return:

    • An object {url, mimeType} if the file exists.
    • null if the file does not exist.

    The url field can be a string or a Promise (useful for files currently being uploaded). The mimeType must be a string or undefined.

    // Example of a custom resolver
    const myFileAttachments = FileAttachments((name) => {
      if (name === "data.csv") {
        return { url: "https://example.com/data.csv", mimeType: "text/csv" };
      }
      return null;
    });
  9. Use Generators.input to observe HTML input elements

    main

    Generators.input(input) returns a new generator that yields promises to the current value of the specified input element. Each promise resolves when the element emits an event.

    Event Mapping:

    • button, submit, checkbox: click events.
    • file: change events.
    • All others: input events.

    Value Mapping:

    • range, number: input.valueAsNumber.
    • date: input.valueAsDate.
    • checkbox: input.checked.
    • Single-file: input.files[0].
    • Multi-file: input.files.
    • Others: input.value.

    Note: This generator is lossy. If multiple events occur before the next promise is pulled (e.g., more than once per animation frame), intermediate values may be skipped in favor of the latest value. For a non-debouncing version, use Generators.queue.

    {
      const values = [];
      for (const value of Generators.input(element)) {
        if (values.push(await value) >= 4) {
          return values;
        }
      }
    }