diff --git a/packages/nanoviews/.size-limit.json b/packages/nanoviews/.size-limit.json index 2275901e..1f5e310c 100644 --- a/packages/nanoviews/.size-limit.json +++ b/packages/nanoviews/.size-limit.json @@ -10,7 +10,7 @@ "name": "All publics (Brotli)", "path": "dist/index.js", "import": "*", - "limit": "6.6 kB" + "limit": "6.65 kB" }, { "name": "Average usage (Gzip)", diff --git a/packages/nanoviews/src/flow/for.spec.ts b/packages/nanoviews/src/flow/for.spec.ts index bb1a9d6e..ee63fb5d 100644 --- a/packages/nanoviews/src/flow/for.spec.ts +++ b/packages/nanoviews/src/flow/for.spec.ts @@ -25,6 +25,7 @@ import { import { fragment } from '../elements/fragment.js' import { trackById, + as_, for_ } from './for.js' import * as Stories from './for.stories.js' @@ -1032,6 +1033,71 @@ describe('nanoviews', () => { expect(container.innerHTML).toBe('
') }) + it('should hand the row through a transform with `as_`', () => { + const items = signal([ + { + id: 1, + name: 'Yatoro' + }, + { + id: 2, + name: 'Larl' + } + ]) + const { container } = render(() => ul()( + for_(items, trackById)( + as_(record, ($player, $index) => li()($player.$name, ':', $index)) + ) + )) + + expect(container.innerHTML).toBe('
') + + // the transformed row is still the row: a write reaches the array + untracked(items)[0].name = 'x' + items([ + { + id: 1, + name: 'Collapse' + }, + { + id: 2, + name: 'Larl' + } + ]) + + expect(container.innerHTML).toBe('
') + }) + + it('should keep a row written through `as_` writable', () => { + const items = signal([ + { + id: 1, + name: ' Larl ' + } + ]) + + render(() => ul()( + for_(items, trackById)( + as_(record, ($player) => { + const name = untracked($player.$name) + + if (name !== name.trim()) { + $player.$name(name.trim()) + } + + return li()($player.$name) + }) + ) + )) + + expect(untracked(items)).toEqual([ + { + id: 1, + name: 'Larl' + } + ]) + }) + describe('fuzz', () => { // A named test pins a shape someone thought of; these walk sequences // nobody did. Both invariants are checked after every step: the rows diff --git a/packages/nanoviews/src/flow/for.ts b/packages/nanoviews/src/flow/for.ts index 168301fb..8474f2d6 100644 --- a/packages/nanoviews/src/flow/for.ts +++ b/packages/nanoviews/src/flow/for.ts @@ -30,6 +30,20 @@ export function trackById(item: { id: unknown }) { return item.id } +/** + * Hand the row through a transform before rendering it + * @param as - Function that transforms the row, eg `record` + * @param each_ - Function that renders the transformed row + * @returns Function to pass to `for_` + */ +/* @__NO_SIDE_EFFECTS__ */ +export function as_( + as: (item: Item) => Value, + each_: (value: Value, index: Index) => Child +) { + return (item: Item, index: Index) => each_(as(item), index) +} + type AnyEach = ( item: Accessor, index: ReadableSignal diff --git a/todo.txt b/todo.txt index 31e9e3cc..b3b46b8f 100644 --- a/todo.txt +++ b/todo.txt @@ -47,17 +47,8 @@ - for_ duplicate track keys: unsupported by design, must keep failing loudly - the crash lands a step away from the cause, so it needs a clear throw at the point of detection - rework return slot$ to look like element/component? fn.prop is faster than {f,p} - util to transform static props to signal props? -- additional arg for record in for$? as$ - static index for untracked loop? -for_($items)( - as_(record, ($item) => li()($item.$name)) -) - -for_($items)( - asRecord(($item) => li()($item.$name)) -) - ## Perf - !== undefined is faster