Skip to content

e280/lettuce

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

99 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ₯¬ lettuce

flexible layout ui for web apps

πŸ₯— splitty-panelly tabby draggy-droppy leafy layout ui

  • πŸ‘‰ https://lettuce.e280.org/ πŸ‘ˆ try it, nerd!
  • pane splitting, resizing, vertical, horizontal β€” you get it
  • dude, it's web components β€” universal compatibility
  • you can drag-and-drop tabs between panes
    • done efficiently with slots, tab doesn't remount to move
    • that's actually legit neato if you have heavy-weight stuff in your tabs
  • using

Important

lettuce is just an early prototype.
more work is yet to be done in terms of features, extensibility, and customizability.



πŸ₯¬ make a quick layout salad

how to setup lettuce in your app

πŸ₯— lettuce installation, html, and css

  1. install
    npm install @e280/lettuce lit
  2. html
    <lettuce-desk></lettuce-desk>
  3. css
    lettuce-desk {
        color: #fff8;
        background: #111;
    
        --scale: 1.5em;
        --highlight: yellow;
        --special: aqua;
        --dropcover: 10%;
        --warn: red;
        --warntext: white;
        --dock: #181818;
        --taskbar: #181818;
        --tab: transparent;
        --gutter: #000;
        --focal: transparent;
        --pointerlock: yellow;
    }

πŸ₯— lettuce typescript

  1. imports
    import {html} from "lit"
    import * as lettuce from "@e280/lettuce"
  2. setup your panels β€” these panels are available for the user to open
    const panels = lettuce.asPanels({
      alpha: {
        label: "Alpha",
        icon: () => html`πŸ‡¦`,
        render: () => html`alpha content`,
      },
      bravo: {
        label: "Bravo",
        icon: () => html`πŸ‡§`,
        render: () => html`bravo content`,
      },
      charlie: {
        label: "Charlie",
        icon: () => html`πŸ‡¨`,
        render: () => html`charlie content`,
      },
    })
  3. setup your layout
    const layout = new lettuce.Layout({
      stock: lettuce.Builder.fn<keyof typeof panels>()(b => ({
        default: () => b.cell(b.tabs("alpha", "bravo", "charlie")),
        empty: () => b.blank(),
      })),
    })
    • panels are referenced by their string keys.
    • Layout is a facility for reading and manipulating.
    • Builder.fn helps you build a tree of layout nodes with less verbosity (note the spooky-typing double-invocation).
    • stock.empty defines the fallback state for when a user closes everything.
    • stock.default defines the initial state for a first-time user.
  4. enable localstorage persistence (optional)
    const persistence = new lettuce.Persistence({
      layout,
      key: "lettuceLayoutBlueprint",
      kv: lettuce.Persistence.localStorageKv(),
    })
    
    await persistence.load()
    persistence.setupAutoSave()
    persistence.setupLoadOnStorageEvent()
    • see @e280/kv to learn how to control where the data is saved
  5. setup a studio for displaying the layout in browser
    const studio = new lettuce.Studio({panels, layout})
  6. register the web components to the dom
    studio.ui.registerComponents()



πŸ₯¬ layout

layout engine with serializable state

πŸ₯— layout package export path

  • import directly to avoid browser concerns (for running under node etc)
    import * as lettuce from "@e280/lettuce/layout"

πŸ₯— layout concepts explained

  • Blueprint
    • serializable layout data.
    • contains a version number and a root cell.
  • LayoutNode
    • any cell, dock, or surface.
    • all nodes have a unique string id.
    • all nodes have a kind string that is "cell", "dock", or "surface".
  • Cell
    • a cell is a group that arranges its children either vertically or horizontally.
    • this is where splits are expressed.
    • a cell's children can be docks or more cells.
  • Dock
    • a dock contains the ui with the little tab buttons, splitting buttons, x button, etc.
    • a dock's children must be surfaces.
  • Surface
    • a surface is the rendering target location of where a panel will be rendered.
    • it uses a <slot> to magically render your panel into the location of this surface.

πŸ₯— layout explorer.ts β€” read and query immutable state

  • read the source code for the real details
  • the state that explorer returns is all immutable and readonly, if you try to mutate it, an error will be thrown
  • layout.explorer.root
  • layout.explorer.walk()
  • layout.explorer.all β€” is a "scout"
  • layout.explorer.cells β€” is a "scout"
  • layout.explorer.docks β€” is a "scout"
  • layout.explorer.surfaces β€” is a "scout"
  • all scouts have:
    • .getReport(id)
    • .requireReport(id)
    • .get(id)
    • .require(id)
    • .parent(id)
    • .reports
    • .nodes
    • .count

πŸ₯— layout actions.ts β€” mutate state

  • read the source code for the real details
  • these actions are the only way you can mutate or modify the state
  • layout.actions.mutate()
  • layout.actions.reset(cell?)
  • layout.actions.addSurface(dockId, panel)
  • layout.actions.activateSurface(surfaceId)
  • layout.actions.setDockActiveSurface(dockId, activeSurfaceIndex)
  • layout.actions.resize(id, size)
  • layout.actions.deleteSurface(id)
  • layout.actions.deleteDock(id)
  • layout.actions.splitDock(id, vertical)
  • layout.actions.moveSurface(id, dockId, destinationIndex)

πŸ₯— layout state management, using strata

  • get/set the data
    const blueprint = layout.getBlueprint()
    layout.setBlueprint(blueprint)
  • you can manually subscribe to changes like this
    layout.on(blueprint => {
      console.log("layout changed", blueprint)
    })
  • any strata-compatible ui (like sly) will magically auto-rerender
    import {view} from "@e280/sly"
    
    view(use => () => html`
      <p>node count: ${layout.explorer.all.count}</p>
    `)
  • you can use strata effects to magically respond to changes
    import {effect} from "@e280/strata"
    
    effect(() => {
      console.log("node count changed", layout.explorer.all.count)
    })



πŸ₯¬ studio

in-browser layout user-experience

πŸ₯— studio ui.ts β€” control how the ui is deployed

  • read the source code for the real details
  • studio.ui.registerComponents() β€” shortcut to register the components with their default names
  • studio.ui.views β€” access to ui in the form of sly views
    import {html} from "lit"
    
    html`
      <div>
        ${studio.ui.views.Desk()}
      </div>
    `
  • studio.ui.components β€” access to ui in the form of web components
    import {dom} from "@e280/sly"
    
    // manually registering the web components to the dom
    dom.register({
    
      // renaming the web component as an example
      LolDesk: studio.ui.components.LettuceDesk,
    })
    <lol-desk></lol-desk>



πŸ₯¬ i made this open sourcedly just for you

pay your respects, gimmie a github star.

About

πŸ₯¬ incredible layout salad

Resources

License

Stars

Watchers

Forks