refactor(ui): centralize prop resolution in useDefinition

Extract prop-splitting and responsive resolution with default
fallbacks from `useDefinition` into a new `resolveDefinitionProps`
helper. This makes `propDefs.default` the single authoritative
source of truth for default values.

Previously, `useBgProvider` had a separate manual default lookup
(`props.bg ?? (definition.propDefs as any).bg?.default`) that
duplicated the main resolution loop.

Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
Johan Persson
2026-02-23 15:15:45 +01:00
parent 344242adcf
commit f2f6e3d47a
2 changed files with 52 additions and 30 deletions
+37 -1
View File
@@ -16,7 +16,7 @@
import { breakpoints } from '../useBreakpoint';
import { utilityClassMap } from '../../utils/utilityClassMap';
import type { UnwrapResponsive, UtilityStyle } from './types';
import type { ComponentConfig, UnwrapResponsive, UtilityStyle } from './types';
const namedBreakpoints = breakpoints.filter(b => b.id !== 'initial');
@@ -57,6 +57,42 @@ export function resolveResponsiveValue<T>(
return value as UnwrapResponsive<T>;
}
export function resolveDefinitionProps<D extends ComponentConfig<any, any>>(
definition: D,
props: Record<string, any>,
breakpoint: string,
): {
ownPropsResolved: Record<string, any>;
restProps: Record<string, any>;
} {
const ownPropKeys = new Set(Object.keys(definition.propDefs));
const utilityPropKeys = new Set(definition.utilityProps ?? []);
const ownPropsRaw: Record<string, any> = {};
const restProps: Record<string, any> = {};
for (const [key, value] of Object.entries(props)) {
if (ownPropKeys.has(key)) {
ownPropsRaw[key] = value;
} else if (!(utilityPropKeys as Set<string>).has(key)) {
restProps[key] = value;
}
}
const ownPropsResolved: Record<string, any> = {};
for (const [key, config] of Object.entries(definition.propDefs)) {
const rawValue = ownPropsRaw[key];
const resolvedValue = resolveResponsiveValue(rawValue, breakpoint);
const finalValue = resolvedValue ?? (config as any).default;
if (finalValue !== undefined) {
ownPropsResolved[key] = finalValue;
}
}
return { ownPropsResolved, restProps };
}
export function processUtilityProps<Keys extends string>(
props: Record<string, any>,
utilityPropKeys: readonly Keys[],
@@ -18,7 +18,7 @@ import { ReactNode } from 'react';
import clsx from 'clsx';
import { useBreakpoint } from '../useBreakpoint';
import { useBgProvider, useBgConsumer, BgProvider } from '../useBg';
import { resolveResponsiveValue, processUtilityProps } from './helpers';
import { resolveDefinitionProps, processUtilityProps } from './helpers';
import type {
ComponentConfig,
UseDefinitionOptions,
@@ -36,41 +36,19 @@ export function useDefinition<
): UseDefinitionResult<D, P> {
const { breakpoint } = useBreakpoint();
// Provider: resolve bg and provide context for children
const providerBg = useBgProvider(
definition.bg === 'provider'
? props.bg ?? (definition.propDefs as any).bg?.default
: undefined,
// Resolve all props centrally — applies responsive values and defaults
const { ownPropsResolved, restProps } = resolveDefinitionProps(
definition,
props,
breakpoint,
);
// Consumer: read parent context bg
const consumerBg = useBgConsumer();
const ownPropKeys = new Set(Object.keys(definition.propDefs));
const utilityPropKeys = new Set(definition.utilityProps ?? []);
const ownPropsRaw: Record<string, any> = {};
const restProps: Record<string, any> = {};
for (const [key, value] of Object.entries(props)) {
if (ownPropKeys.has(key)) {
ownPropsRaw[key] = value;
} else if (!(utilityPropKeys as Set<string>).has(key)) {
restProps[key] = value;
}
}
const ownPropsResolved: Record<string, any> = {};
const dataAttributes: Record<string, string | undefined> = {};
for (const [key, config] of Object.entries(definition.propDefs)) {
const rawValue = ownPropsRaw[key];
const resolvedValue = resolveResponsiveValue(rawValue, breakpoint);
const finalValue = resolvedValue ?? (config as any).default;
const finalValue = ownPropsResolved[key];
if (finalValue !== undefined) {
ownPropsResolved[key] = finalValue;
// Skip data-bg for bg prop when the provider path handles it
if (key === 'bg' && definition.bg === 'provider') continue;
@@ -81,6 +59,14 @@ export function useDefinition<
}
}
// Provider: resolve bg and provide context for children
const providerBg = useBgProvider(
definition.bg === 'provider' ? ownPropsResolved.bg : undefined,
);
// Consumer: read parent context bg
const consumerBg = useBgConsumer();
// Provider: set data-bg from the resolved provider bg
if (definition.bg === 'provider' && providerBg.bg !== undefined) {
dataAttributes['data-bg'] = String(providerBg.bg);