Skip to content

feat(flux): add createListDirective factory for zero-boilerplate dynamic lists#1840

Draft
alimd wants to merge 2 commits into
nextfrom
claude/awesome-lovelace-qyz1yo
Draft

feat(flux): add createListDirective factory for zero-boilerplate dynamic lists#1840
alimd wants to merge 2 commits into
nextfrom
claude/awesome-lovelace-qyz1yo

Conversation

@alimd

@alimd alimd commented Jun 22, 2026

Copy link
Copy Markdown
Member

Summary

Adds createListDirective to @alwatr/flux — a factory that collapses the repetitive "subscribe to a list signal and render its rows with lit-html repeat" boilerplate into a single declarative configuration object.

Motivation: rendering a dynamic list the manual way means writing a dedicated LitDirective subclass per list (@state accessor + init_() subscription + render_() calling repeat(...)). The only parts that ever change are the signal, the key, and the row template. This factory captures exactly those three and returns a ready-to-register lazy directive.

This is the chosen design (Path B) after evaluating the alternatives:

  • ❌ Bringing in Preact (second VDOM runtime, fights the ActionService/no-per-element-listener model).
  • ❌ Building a bind_repeat in @alwatr/bind (would reimplement keyed reconciliation that lit-html already provides; bind is intentionally for flat, surgical primitive updates).
  • ✅ A thin factory over lit-html's proven keyed repeat, living in flux (which already bundles lit-html + directive + signal).

Changes

  • pkg/flux/src/list-directive.ts (new) — createListDirective(config) + ListDirectiveConfig<T>, fully JSDoc-documented.
  • pkg/flux/src/main.ts — export the new module.
  • pkg/flux/README.md — Key Features section, a repeat entry in the Template section, and a full API reference for createListDirective.
  • pkg/nanolib/bind/README.md — a Scope & Boundaries note clarifying that dynamic lists belong to createListDirective, not bind.

Design notes

  • Lazy & side-effect freesource is a thunk (() => signal.instance) resolved inside init_(), preserving the lazy().instance guarantee; importing the module triggers nothing.
  • Keyed reconciliation — built on lit-html's repeat, so only changed rows are created/removed/moved (focus, input state, scroll, and transitions survive reorders).
  • Headless & UDF-pure — ships no markup or styling tokens; the App layer supplies the row template; intents fire up via on-<event> delegation; the directive never owns or mutates state.
  • Auto-cleanup — the subscription unsubscribes on directive destroy (e.g. bfcache teardown).

Verification

  • lerna run build:ts --scope @alwatr/flux --include-dependencies ✅ (19 packages, noEmitOnErrordist/list-directive.d.ts emitted cleanly).

Usage

export const registerShopListDirective = createListDirective({
  name: 'shop_list',
  source: () => state_shop_list.instance,
  key: (shop) => shop.meta.id,
  row: (shop) => html`<li on-click="ui_shop_clicked:${shop.meta.id}">${shop.content.title}</li>`,
  empty: () => html`<p class="muted">No shops to display.</p>`,
});
registerShopListDirective(true);
<ul shop_list></ul>

🤖 Generated with Claude Code


Generated by Claude Code

…mic lists

Introduce `createListDirective`, a factory that captures the repetitive
"subscribe to a list signal and render rows with lit-html repeat" pattern
into a single declarative config (name, source thunk, key, row, empty).

It returns a lazyDirective-compatible registration function, builds on
lit-html's keyed `repeat` for surgical reconciliation, resolves the source
signal lazily inside init_() to keep the module side-effect free, and stays
headless (the App layer supplies the row template).

- Add pkg/flux/src/list-directive.ts (full JSDoc) and export from main.
- Document the feature and API in the flux README.
- Add a Scope & Boundaries note to the bind README clarifying that dynamic
  lists belong to createListDirective, not bind.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016t1nbKp6daS49fbfN3NRCC
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new utility, createListDirective, to the @alwatr/flux package. Its primary purpose is to reduce boilerplate when rendering dynamic lists that depend on signal state. By providing a declarative factory, it allows developers to implement keyed list reconciliation efficiently without manually authoring repetitive LitDirective classes, while maintaining strict adherence to the project's lazy-loading and side-effect-free architecture.

Highlights

  • New Factory: createListDirective: Introduced a factory function in @alwatr/flux that simplifies dynamic list rendering by eliminating the need for custom LitDirective subclasses for every list.
  • Declarative Configuration: The new factory allows developers to define lists using a simple configuration object (name, source thunk, key function, and row template) while leveraging lit-html's keyed reconciliation.
  • Documentation Updates: Updated README files in @alwatr/flux and @alwatr/bind to provide clear guidance on when to use the new directive versus existing binding tools.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.


A list that grows and shifts its place, With keyed rows in a steady grace. No boilerplate to cloud the view, Just config objects, clean and new.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

این درخواست ادغام قابلیت جدیدی به نام createListDirective را به بسته @alwatr/flux اضافه می‌کند که یک کارخانه (factory) برای ایجاد دایرکتیوهای لیست پویا با حداقل کدنویسی تکراری است و از سیستم تطبیق کلیددار repeat در lit-html استفاده می‌کند. همچنین مستندات مربوطه به‌روزرسانی شده‌اند. در بازبینی کد، پیشنهاد شده است که برای افزایش امنیت در زمان اجرا و اطمینان از آرایه بودن داده‌ها، به جای عملگر ?? از بررسی Array.isArray استفاده شود تا از خطاهای احتمالی جلوگیری به عمل آید.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread pkg/flux/src/list-directive.ts Outdated
// The signal invokes the callback immediately with its current value, so the first render is
// scheduled right after `init_()` — no separate priming step needed.
this.subscribe_(source(), (items) => {
this.items_ = items ?? [];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

برای افزایش امنیت کد در برابر مقادیر غیرمنتظره در زمان اجرا (Runtime)، پیشنهاد می‌شود به جای استفاده از عملگر ?? از Array.isArray استفاده کنید. این کار تضمین می‌کند که this.items_ همیشه یک آرایه معتبر است و از خطاهای احتمالی هنگام خواندن length یا اجرای repeat جلوگیری می‌کند.

Suggested change
this.items_ = items ?? [];
this.items_ = Array.isArray(items) ? items : [];

Replace the nullish-only guard with an Array.isArray check so a hydrated or
serialized source (e.g. EmbeddedDataCollector JSON) that yields a non-array
value cannot break `length` access or `repeat`. Addresses PR review feedback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016t1nbKp6daS49fbfN3NRCC
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants