From 6c71a48ae53a5d3f9b2ce0dbe71a95507dff88cc Mon Sep 17 00:00:00 2001 From: madaxen86 <59310516+madaxen86@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:25:37 +0200 Subject: [PATCH 1/2] upgrade Show component - added prop checkObjectValues --- packages/solid/src/render/flow.ts | 91 ++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 15 deletions(-) diff --git a/packages/solid/src/render/flow.ts b/packages/solid/src/render/flow.ts index d48737b61..7e7c3aece 100644 --- a/packages/solid/src/render/flow.ts +++ b/packages/solid/src/render/flow.ts @@ -78,35 +78,95 @@ export function Index(props: { )) as unknown as JSX.Element; } +// Helper to prettify types in errors +type Prettify = { + [K in keyof T]: T[K]; +} & {}; + +// Helper to make all values non-nullable +type NonNullableValues = { + [K in keyof T]: NonNullable; +}; + +// Strict object type to exclude arrays, maps, sets, and functions +type StrictKeyValueObject = { + [key: string]: string | number | boolean | object | null | undefined; +}; + +// ConditionalRecord ensures that when `checkObjectValues` is true, `T` must be a StrictKeyValueObject (not an array or other types) +type NarrowedRecord< + T, + CheckObjectValues extends boolean | undefined +> = CheckObjectValues extends true + ? T extends StrictKeyValueObject + ? NonNullableValues // Ensure all values are NonNullable + : never // Throw an error if not a strict object + : T; + +// Enforce when to be a StrictKeyValueObject if checkObjectValues is true +type EnforceStrictKeyValueObject< + T, + CheckObjectValues extends boolean | undefined +> = CheckObjectValues extends true + ? T extends StrictKeyValueObject + ? T + : never // Enforce strict key-value object when checkObjectValues is true + : T; + +// RequiredParameter helper type RequiredParameter = T extends () => unknown ? never : T; -/** - * Conditionally render its children or an optional fallback component - * @description https://docs.solidjs.com/reference/components/show - */ + export function Show< T, - TRenderFunction extends (item: Accessor>) => JSX.Element + CheckObjectValues extends boolean | undefined, + TRenderFunction extends ( + item: Accessor>>> + ) => JSX.Element >(props: { - when: T | undefined | null | false; + when: EnforceStrictKeyValueObject; keyed?: false; + checkObjectValues?: CheckObjectValues; fallback?: JSX.Element; children: JSX.Element | RequiredParameter; }): JSX.Element; -export function Show) => JSX.Element>(props: { - when: T | undefined | null | false; + +export function Show< + T, + CheckObjectValues extends boolean | undefined, + TRenderFunction extends ( + item: Prettify>> + ) => JSX.Element +>(props: { + when: EnforceStrictKeyValueObject; keyed: true; + checkObjectValues?: CheckObjectValues; fallback?: JSX.Element; children: JSX.Element | RequiredParameter; }): JSX.Element; -export function Show(props: { - when: T | undefined | null | false; + +export function Show(props: { + when: EnforceStrictKeyValueObject; keyed?: boolean; + checkObjectValues?: CheckObjectValues; fallback?: JSX.Element; - children: JSX.Element | ((item: NonNullable | Accessor>) => JSX.Element); + children: + | JSX.Element + | (( + item: + | NonNullable> + | Accessor>> + ) => JSX.Element); }): JSX.Element { const keyed = props.keyed; - const condition = createMemo( - () => props.when, + + // Memoize the condition based on whether checkObjectValues is true or not + const condition = createMemo( + () => { + const when = props.when; + if (!props.checkObjectValues) return when; + // Check if all values in `when` are truthy + return Object.values(when as Record).every(value => !!value) ? when : false; + }, undefined, "_SOLID_DEV_" ? { @@ -115,6 +175,7 @@ export function Show(props: { } : { equals: (a, b) => (keyed ? a === b : !a === !b) } ); + return createMemo( () => { const c = condition(); @@ -127,8 +188,8 @@ export function Show(props: { keyed ? (c as T) : () => { - if (!untrack(condition)) throw narrowedError("Show"); - return props.when; + if (!untrack(condition)) throw new Error("Show"); + return condition() as T; } ) ) From 7f0aaa69008fdd53db06b3494620c53c08423063 Mon Sep 17 00:00:00 2001 From: madaxen86 <59310516+madaxen86@users.noreply.github.com> Date: Tue, 12 Nov 2024 20:36:21 +0100 Subject: [PATCH 2/2] fixed: keep granular updates - with store simplified types --- packages/solid/src/render/flow.ts | 114 +++++++++++++++++------------- 1 file changed, 63 insertions(+), 51 deletions(-) diff --git a/packages/solid/src/render/flow.ts b/packages/solid/src/render/flow.ts index 7e7c3aece..ed1237603 100644 --- a/packages/solid/src/render/flow.ts +++ b/packages/solid/src/render/flow.ts @@ -12,6 +12,7 @@ import { import { mapArray, indexArray } from "../reactive/array.js"; import { sharedConfig } from "./hydration.js"; import type { JSX } from "../jsx.js"; +import { createStore, reconcile } from "../../store/src/index.js"; const narrowedError = (name: string) => "_SOLID_DEV_" @@ -78,14 +79,15 @@ export function Index(props: { )) as unknown as JSX.Element; } -// Helper to prettify types in errors +// Helper to prettify types type Prettify = { [K in keyof T]: T[K]; } & {}; +type NonNullable = T extends null | undefined | false | 0 ? never : T; // Helper to make all values non-nullable type NonNullableValues = { - [K in keyof T]: NonNullable; + [K in keyof T]: NonNullable; // extends null | undefined | false ? never : T[K]; }; // Strict object type to exclude arrays, maps, sets, and functions @@ -93,89 +95,99 @@ type StrictKeyValueObject = { [key: string]: string | number | boolean | object | null | undefined; }; -// ConditionalRecord ensures that when `checkObjectValues` is true, `T` must be a StrictKeyValueObject (not an array or other types) -type NarrowedRecord< - T, - CheckObjectValues extends boolean | undefined -> = CheckObjectValues extends true - ? T extends StrictKeyValueObject - ? NonNullableValues // Ensure all values are NonNullable - : never // Throw an error if not a strict object - : T; - -// Enforce when to be a StrictKeyValueObject if checkObjectValues is true -type EnforceStrictKeyValueObject< - T, - CheckObjectValues extends boolean | undefined -> = CheckObjectValues extends true - ? T extends StrictKeyValueObject - ? T - : never // Enforce strict key-value object when checkObjectValues is true - : T; +type SwitchEval = MemoOptions["equals"]; -// RequiredParameter helper type RequiredParameter = T extends () => unknown ? never : T; - +/** + * Conditionally render its children or an optional fallback component + * @description https://docs.solidjs.com/reference/components/show + */ export function Show< T, - CheckObjectValues extends boolean | undefined, - TRenderFunction extends ( - item: Accessor>>> - ) => JSX.Element + TRenderFunction extends (item: Accessor>) => JSX.Element >(props: { - when: EnforceStrictKeyValueObject; + when: T | undefined | null | false; keyed?: false; - checkObjectValues?: CheckObjectValues; + checkObjectValues?: false; fallback?: JSX.Element; children: JSX.Element | RequiredParameter; }): JSX.Element; - export function Show< - T, - CheckObjectValues extends boolean | undefined, - TRenderFunction extends ( - item: Prettify>> - ) => JSX.Element + T extends StrictKeyValueObject, + TRenderFunction extends (item: Accessor>>) => JSX.Element >(props: { - when: EnforceStrictKeyValueObject; + when: T; + keyed?: false; + checkObjectValues: true; + fallback?: JSX.Element; + children: JSX.Element | RequiredParameter; +}): JSX.Element; +export function Show) => JSX.Element>(props: { + when: T | undefined | null | false; keyed: true; - checkObjectValues?: CheckObjectValues; + checkObjectValues?: false; fallback?: JSX.Element; children: JSX.Element | RequiredParameter; }): JSX.Element; - -export function Show(props: { - when: EnforceStrictKeyValueObject; +export function Show< + T extends StrictKeyValueObject, + TRenderFunction extends (item: Prettify>) => JSX.Element +>(props: { + when: T; + keyed?: boolean; + checkObjectValues: true; + fallback?: JSX.Element; + children: JSX.Element | RequiredParameter; +}): JSX.Element; +export function Show(props: { + when: T | undefined | null | false; keyed?: boolean; - checkObjectValues?: CheckObjectValues; + checkObjectValues?: boolean; fallback?: JSX.Element; children: | JSX.Element | (( item: - | NonNullable> - | Accessor>> + | NonNullable + | Accessor> + | Accessor>> + | Prettify> ) => JSX.Element); }): JSX.Element { - const keyed = props.keyed; + const equals: SwitchEval = (a, b) => { + if (!obj) return keyed ? a === b : !a === !b; + + const obj1 = a as StrictKeyValueObject; + const obj2 = b as StrictKeyValueObject; + const o1 = Object.entries(obj1); + const o2 = Object.entries(obj2); + if (o1.length !== o2.length) return false; + return o1.every(([key, value]) => obj2.hasOwnProperty(key) && obj2[key] === value); + }; - // Memoize the condition based on whether checkObjectValues is true or not + const keyed = props.keyed; + const obj = props.checkObjectValues; + const s = obj ? createStore(props.when as StrictKeyValueObject) : null; const condition = createMemo( () => { const when = props.when; - if (!props.checkObjectValues) return when; - // Check if all values in `when` are truthy - return Object.values(when as Record).every(value => !!value) ? when : false; + if (!obj) return when; + if (!Object.values(when as StrictKeyValueObject).every(v => !!v)) return false; + if (!s) throw new Error("Show"); + const [v, setV] = s; + setV(reconcile(when as StrictKeyValueObject)); + return v; }, undefined, "_SOLID_DEV_" ? { - equals: (a, b) => (keyed ? a === b : !a === !b), + equals, name: "condition" } - : { equals: (a, b) => (keyed ? a === b : !a === !b) } + : { + equals + } ); - return createMemo( () => { const c = condition();