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

@formancy/builder-angular

v0.13.0

Published

Keyboard-first Angular builder UI for formancy form documents.

Readme

@formancy/builder-angular

The form builder's structure editor, for Angular. Drives @formancy/builder-core, which holds the document, the undo stack, the rules about what edits are legal, and — since this package exists — everything the two builders read off a session.

import { Component } from '@angular/core'
import { createBuilderSession } from '@formancy/builder-core'
import { FormancyBuilder } from '@formancy/builder-angular'

@Component({
  imports: [FormancyBuilder],
  template: `<formancy-builder [session]="session" (selected)="chosen.set($event)" />`,
})
export class Editor {
  protected readonly session = createBuilderSession(schema)
}

Angular 22, and zoneless: it never touches zone.js, never calls ChangeDetectorRef, and never re-renders because something elsewhere changed. The session is read through one signal that changes exactly once per accepted command, so a keystroke the session refuses costs no render at all.

It is the same builder, not a second one

The parts that decide anything live in @formancy/builder-core and are shared with @formancy/builder-react: which destinations a field may move to and how each is described in words, which types the document's spec version allows, what a structured condition compiles to in CEL, where a drop lands. Two builders offering different destinations for one document would be two products, and nobody using only one of them could see the difference.

What is per framework is the components. This package's job is to turn a session into signals and a signal into markup.

It is a keyboard interface first

Both builders were built that way and the order is the point: WCAG 2.2 SC 2.5.7 requires every dragging movement to have a non-dragging alternative that does the same job, and built the other way round the drag ships while the keyboard path becomes a follow-up competing with features.

| Key | What it does | |---|---| | ↑ ↓ | Move between fields, in the order a person reading the form meets them | | Home End | First and last field | | a | Add a field — asks what, then where | | m | Move the focused field — opens a list of destinations | | p | Add a page, making the form a wizard | | u | Take a container away and keep what is inside | | Delete | Remove it | | Ctrl+Z / Ctrl+Y | Undo / redo |

The tree is one tab stop, not one per field: a hundred-field form should not cost a hundred tabs to get past, so a roving tabindex gives exactly one item the tab order and the arrows do the rest. Every destination is a sentence — "inside Billing address, after Street" — rather than an index, because a palette reading "0, 1, 2" is a keyboard route only in the sense that it exists.

Every command reports through one polite live region. role="status" already implies aria-live="polite"; setting both is the classic way to hear it twice.

Two trees, as in the React builder

FormancyLayoutPane is the arrangement: rows, columns and sections, which is how two fields end up side by side.

template: `<formancy-layout-pane [session]="session" layout="web" />`

| Key | What it does | |---|---| | a | Add a row, column or section | | m | Move the focused item | | u | Unwrap a row or column, keeping what is in it | | w | Put it and another item side by side in a row | | Delete | Take it out of the arrangement — the form still collects the field |

That last distinction is the pane's reason to exist: the model answers what does this form collect and the arrangement answers where does it appear. A field can be in one without being in the other, which is why a field no arrangement places is listed under its own heading rather than silently left out — it is collected and invisible to everyone filling the form in.

Editing what a field is

FormancyPropertyPanel is generated from the spec's own JSON Schema rather than written out per field type, so a property the format grows appears with its own title and description and nobody has to remember it exists.

template: `<formancy-property-panel [session]="session" [keyPath]="chosen()" />`

FormancyLayoutPropertyPanel is the same thing for a node in the arrangement, and FormancyOptionsEditor handles the one shape generation cannot: a select's value/label pairs, where value is stable identity that orphans answers if it changes and label is safe to reword.

Both keep a draft of the text being typed, because the document refuses invalid states and a person passes through them. span is anyOf: [integer, const "all"], so typing the word offers "a", then "al", then "all" — the first two are refused, and a box bound straight to the document would re-render empty and swallow the next keystroke.

Writing rules, and translating

FormancyLogicPanel writes the rules — conditions composed as comparisons and compiled to CEL, a check named for the deployment to answer, a calculation written as an expression. FormancyTranslationsPane extracts, translates, takes a catalogue out and brings it back, and previews the form in the language being worked on without editing the document to look at it. Given [ask], it asks a model for the messages a language is missing, and FormancyTranslationReview holds the answer for review message by message before it lands; a translation somebody made is never replaced (0161).

Which kinds exist and what each is written with comes from @formancy/builder-core, so the two builders cannot offer different ones.

Dragging

Both trees take a drag, and it is a second route: every command works from the keyboard first, which is what WCAG 2.2 SC 2.5.7 asks for and the order these were built in. Only a drop the session will accept shows an indicator, and every drop is announced through the same live region the keyboard uses.

Arranging on the form itself

FormancyArrangeSurface wraps a rendered form and makes it a drop target, as the React builder's does. It is off unless a caller turns it on: a preview somebody is typing into should not be picking up drags.

Where a drop lands is arrangeDrop in @formancy/builder-core, shared with the React surface, so the two cannot disagree about which part of a field is a side zone (0094). What is here is the Angular half: reading a pointer, marking elements draggable, drawing the indicator, and announcing the outcome through a live region. Every move goes through the same session command the arrangement tree and the keyboard use. The surface also reads which way the form reads, as the browser computed it, so read right to left the side a field is aimed at is still the side it lands on (0123); if you bundle workbench.css, set your CSS target as the themes README says.

The renderer knows nothing about it. @formancy/angular emits data-formancy-layout-path and data-formancy-field-path on what it renders, both inert, and this reads them from the outside.

What it does not have yet

Nothing the React builder has. It can be seen working: the playground offers it beside the React builder, over the same session, and apps/angular-starter mounts it beside an Angular Material form.

Licence

Apache-2.0.