diff --git a/CHANGELOG.md b/CHANGELOG.md index ef49bb2..fee931d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). effects to set children. - `spring()` returns a second value, a setter to set position, velocity and impulse. +- `show()` now receives a source to its callback returning the current value + of the condition. ### Changed diff --git a/src/show.luau b/src/show.luau index b3c3fef..e33393f 100644 --- a/src/show.luau +++ b/src/show.luau @@ -1,16 +1,28 @@ -local switch = require "./switch" +local derive = require "./derive" +local untrack = require "./untrack" -local function show(source: () -> any, component: () -> T, fallback: (() -> T)?): () -> T? - local function truthy() +local function show(source: () -> T?, component: (() -> T) -> U, fallback: (() -> U)?): () -> U? + local truthy = derive(function() return not not source() - end + end) - return switch(truthy) { - [true] = component, - [false] = fallback, - } + -- seemingly redundant derivation to extend the reactive graph so that + -- the propogation of the source's update is delayed, giving time for + -- the show() scope to be destroyed before potential effects registered + -- by the component can run when they should not run + -- todo: are there cases this method does not cover? + local derived = derive(function() + return source() + end) + + return derive(function() + return + if truthy() then untrack(function() return component(derived :: () -> T) end) + elseif fallback then untrack(fallback) + else nil + end) end -return show :: - ((source: () -> any, component: () -> T) -> () -> T?) & - ((source: () -> any, component: () -> T, fallback: () -> U) -> () -> (T | U)?) +return show :: ( + ( (source: () -> T?, component: (() -> T) -> U) -> () -> U? ) +) diff --git a/test/tests.luau b/test/tests.luau index d4c95ae..d79e966 100644 --- a/test/tests.luau +++ b/test/tests.luau @@ -1073,20 +1073,73 @@ TEST("show()", wrap_root(function() local show = vide.show local root = vide.root - do CASE "main" - -- uses switch() internally, more extensive testing of scoping not needed - local value = source("truey" :: unknown) + do CASE "show component" + local input = source(true) + local function one() return 1 end + + local output = show(input, one) + + CHECK(output() == 1) + input(false) + CHECK(output() == nil) + end + + do CASE "fallback component" + local input = source(true) local function one() return 1 end local function two() return 2 end - local output = show(value, one, two) + local output = show(input, one, two) CHECK(output() == 1) - value(nil) + input(false) CHECK(output() == 2) end - do CASE "alt" + do CASE "updating truth to truthy does not rerun" + local input = source(1) + local count = 0 + + local function component() + count += 1 + return 1 + end + + local output = show(input, component) + + CHECK(count == 1) + CHECK(output() == 1) + input(2) + CHECK(count == 1) + end + + do CASE "updating source passed to component" + local input = source(1 :: number?) + local count = 0 + + show(input :: () -> number?, function(value: () -> number) + vide.cleanup(function() print "destroyed" end) + effect(function() + local v = value() + + count += 1 + + CHECK(v == count) + if v ~= count then error(count) end + end) + + return true + end) + + input(2) + CHECK(count == 2) + input(3) + CHECK(count == 3) + input(nil) + CHECK(count == 3) + end + + do CASE "alt" -- todo: move test local visible = vide.source(true) local count = vide.source(0)