npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@scaleflex/folder-picker

v0.1.0

Published

Standalone folder picker for Scaleflex DAM — lazy folder tree with search, inline folder creation, single/multi selection, and per-folder disable rules. Lit web component built on @scaleflex/dam-core; embeddable inline or as a modal, in any framework via

Readme

@scaleflex/folder-picker

A folder picker web component for Scaleflex DAM — a lazy folder tree with search, inline folder creation, single or multi selection, and per-folder disable rules.

Built as a Lit 3 custom element on @scaleflex/dam-core + @scaleflex/dam-ui, so it drops into React, Vue, Angular, or plain HTML. Ships to npm only — there is no CDN bundle.

Part of the scaleflex-dam-tools monorepo (packages/folder-picker/).

Install

pnpm add @scaleflex/folder-picker

Import surfaces

| Import | Use | | --- | --- | | @scaleflex/folder-picker | Element class + types (no auto-register) | | @scaleflex/folder-picker/define | Registers <sfx-folder-picker> | | @scaleflex/folder-picker/react | <FolderPicker> React wrapper |

Web component

<sfx-folder-picker id="picker"></sfx-folder-picker>
<script type="module">
  import '@scaleflex/folder-picker/define';

  const picker = document.getElementById('picker');
  picker.config = { auth: { mode: 'sass-key', container: 'my-container', sassKey: '…' } };
  picker.addEventListener('sfx-folder-select', (e) => console.log(e.detail.selection.path));
</script>

React

import { FolderPicker } from '@scaleflex/folder-picker/react';

<FolderPicker
  config={{ auth: { mode: 'sass-key', container, sassKey } }}
  presentation="modal"
  open={open}
  features={{ createFolder: true }}
  onConfirm={({ selection }) => save(selection?.path)}
  onCancel={() => setOpen(false)}
/>;

Next.js: this module registers a custom element at import time, so pull it in with dynamic(() => import('...'), { ssr: false }).

Configuration

interface FolderPickerConfig {
  auth?: AnyAuthConfig;  // canonical or the asset-picker shape — both accepted
  apiDomain?: string;
  client?: DamClient;    // reuse a client you already built
  locale?: string;       // BCP-47
}

| Property | Type | Default | | | --- | --- | --- | --- | | mode | 'single' \| 'multi' | 'single' | Multi adds checkboxes and removable chips | | presentation | 'inline' \| 'modal' | 'inline' | Modal wraps the panel in sfx-dialog | | open | boolean | false | Modal only | | value | string[] | — | Selected paths. Setting it makes the picker controlled | | rootPath / rootLabel | string | '/' / "Root" | Scope the tree | | revealPath | string | — | Expand down to this path | | disabled | DisabledRule | {} | See below | | permissions | PermissionCheck | — | Checked on select only | | source | FolderSource | — | Bring your own data layer | | sort | SortSpec \| comparator | name asc | Presentation order; re-orders without refetching | | filter | (folder) => boolean | — | Hides folders outright (disabled only greys them out) | | query | string | '' | Search text — set it to drive the tree from your own field | | variant | 'hub' \| 'portals' | 'hub' | Which shipped look to use — see Theming | | loading / errorMessage | boolean / string | — | Host-driven overlay |

Features

picker.features = {
  search: true,        // debounced search across the subtree
  createFolder: false, // opt-in — only you know the user's permissions
  root: true,          // show a selectable Root row
  confirm: true,       // two-step select; defaults to true for modal, false for inline
  chips: true,         // removable pills in multi mode
};

Disable rules

picker.disabled = {
  pathPrefixes: ['/Campaigns'], // the folder and everything under it — the move-into-self guard
  uuids: ['…'],
  paths: ['/Reports'],          // exact match only
  predicate: (folder) => folder.name.startsWith('.'),
  hideSubtree: false,           // greyed out by default; true hides descendants entirely
};

Events

All events bubble and are composed.

| Event | detail | | --- | --- | | sfx-folder-select | { mode, selection, selected } — fires on every click/toggle, including deselect | | sfx-folder-confirm | { selection, selected } — only when features.confirm | | sfx-folder-cancel | {} | | sfx-folder-open-change | { open } | | sfx-folder-expand | { path, expanded } | | sfx-folder-create | { folder, parentPath } | | sfx-folder-search | { query, resultCount, truncated } | | sfx-folder-error | { code, message, path? }code is load \| search \| create \| permission |

A FolderSelection is { path, uuid?, name, folder? }. path is the identityuuid is absent for the synthetic Root row and is not stable across a move.

The picker never reaches into the host's UI. It emits; you decide what to enable.

Composing your own chrome

The panel is search + tree. Anything around it — a title, a sort control, a "collapse all" button — is yours, through two slots and a couple of hooks:

<sfx-folder-picker id="picker" features='{"search":false}'>
  <div slot="header">Folders · your sort dropdown · your collapse button</div>
  <div slot="footer">…</div>
</sfx-folder-picker>
picker.query = q                          // drive search from a field you rendered elsewhere
picker.sort = { by: 'modified', order: 'desc' }
picker.collapseAll()                       // fold the tree; loaded children stay cached

sort also takes a comparator ((a, b) => number) when a named field is not enough. Both sort and filter apply at render time over the cache, so switching either is instant and issues no requests.

Bringing your own data

Hosts that already hold a folder tree (Redux, react-query, …) should implement FolderSource instead of letting the element fetch, so nothing is requested twice:

picker.source = {
  async listChildren(path, { offset, limit }) {
    const folders = await myCache.children(path, offset, limit);
    return { folders, hasMore: folders.length === limit };
  },
  async search(query, { rootPath, limit }) { … },
  async create(leafName, parentPath) { … },
  async checkPermissions(uuid, required) { … },
};

createStaticFolderSource(folders) wraps a flat list — handy for tests and demos.

Folders are paged at 200 with up to 10 pages fetched automatically; beyond that the node is marked partial and a Show more row appears. (The API returns no total, so "more exist" is inferred from a full page.)

Theming

Pick a shape, then wire your palette

Two shapes ship with the package, so a host does not have to reconstruct one from a pile of metrics:

| variant | Reproduces | Rows | | --- | --- | --- | | hub (default) | Hub's Airbox picker | chevron only, 20px indent, 11px chevron, 220px list | | portals | Portals' folders-selector | folder icon per row, 16px indent, 20px chevron that swaps glyph on expand, 320px list |

<FolderPicker variant='portals' … />

Colours are not part of a variant — every product has its own palette — so map those with custom properties:

sfx-folder-picker {
  --sfx-fp-fg: var(--foreground);
  --sfx-fp-accent: var(--accent-foreground);
  --sfx-fp-accent-soft: var(--accent);
  --sfx-fp-border: var(--border);
  --sfx-fp-hover: var(--muted);
}

Overriding one value

Any --sfx-fp-* property wins over the variant, so a one-off tweak does not mean leaving the shape behind:

sfx-folder-picker { --sfx-fp-body-max-height: 420px; }

Metrics available: --sfx-fp-indent, --sfx-fp-row-padding-{left,right,y}, --sfx-fp-row-{gap,radius,font-size,line-height}, --sfx-fp-chevron-{box,size}, --sfx-fp-icon-chevron[-open], --sfx-fp-folder-icon-display, --sfx-fp-list-padding, --sfx-fp-body-max-height, --sfx-fp-search-{height,font-size,padding-x,gap,bg,border,color,shadow}, --sfx-fp-placeholder, --sfx-fp-focus-ring{,-width,-offset,-offset-color}.

The chevron is a CSS mask, so --sfx-fp-icon-chevron also accepts a different glyph as a data: URI.

Embedding inside a Radix / headless overlay

Overlay libraries treat a click inside a shadow-DOM element as an outside click and close themselves. Guard with the exported helper:

import { isInsideFolderPicker } from '@scaleflex/folder-picker';

<DialogContent
  onInteractOutside={(e) => isInsideFolderPicker(e.target) && e.preventDefault()}
  onEscapeKeyDown={(e) => isInsideFolderPicker(e.target) && e.preventDefault()}
/>;

Accessibility

role="tree" with a single tab stop (roving tabindex). Arrow keys move and expand/collapse, Home/End jump, Enter/Space select, Escape cancels.

Development

pnpm --filter @scaleflex/folder-picker dev        # dev harness, offline scenarios included
pnpm --filter @scaleflex/folder-picker test
pnpm --filter @scaleflex/folder-picker typecheck
pnpm --filter @scaleflex/folder-picker build

The harness ships offline scenarios (small tree, 2500 children, stalled, failing, scoped) so it works without credentials. For live data, paste a container + sass key into the bar.