Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | 1x 1x 1x 5x 5x 5x 5x 5x 5x 8x 8x 3x 3x 8x 8x 8x 7x 7x 7x 3x 3x 3x 7x 7x 8x 1x 1x 8x 7x 7x 5x 5x 5x | import { createEffect } from '../primitives/effect';
import { createRoot, onCleanup } from '../lifecycle/lifecycle';
import type { Disposer, SignalGetter } from '../types';
/**
* Props for the Show component that conditionally renders content.
*/
type ShowProps<T> = {
/** Signal that determines whether to show children or fallback */
when: SignalGetter<T>;
/** Optional function that returns content to show when condition is falsy */
fallback?: () => Node;
/** Function that returns content to show when condition is truthy */
children: () => Node;
};
/**
* Conditionally renders content based on a reactive condition.
*
* The Show component efficiently switches between showing children or fallback content
* based on the truthiness of the `when` signal. Each conditional branch gets its own
* reactive scope for proper cleanup when switching.
*
* @param props Configuration object with when, children, and optional fallback
* @returns A DocumentFragment that conditionally contains the rendered content
*
* @example
* ```typescript
* const [isLoggedIn, setLoggedIn] = createSignal(false);
*
* const content = Show({
* when: isLoggedIn,
* fallback: () => h.p('Please log in'),
* children: () => h.p('Welcome back!')
* });
*
* // Shows "Please log in" initially, switches to "Welcome back!" when logged in
* ```
*/
export function Show<T>(props: ShowProps<T>): Node {
const { when, fallback, children } = props;
const container = document.createDocumentFragment();
// End marker provides stable insertion point for conditional content
const endMarker = document.createTextNode('');
container.appendChild(endMarker);
let currentDisposer: Disposer | null = null;
createEffect(() => {
const condition = !!when();
// Always clean up the previous state before rendering the new one
if (currentDisposer) {
currentDisposer();
// Removed: container.textContent = ''; (invalid on DocumentFragment)
}
const renderer = condition ? children : fallback;
let branchElement: Node | null = null; // Local to this effect run
if (renderer) {
// Create a new root for the branch, so it gets its own lifecycle
currentDisposer = createRoot(() => {
branchElement = renderer();
// Added: Explicit cleanup to remove the node from DOM on dispose
onCleanup(() => {
if (branchElement && branchElement.parentNode) {
branchElement.parentNode.removeChild(branchElement);
}
});
});
} else {
currentDisposer = null;
}
// Added: Insert the new branch before the endMarker (handles fragment initial / DOM updates)
if (branchElement) {
endMarker.parentNode!.insertBefore(branchElement, endMarker);
}
});
return container;
}
|