Add a generic no-state child

This commit is contained in:
Zoe Roux
2023-01-10 22:22:26 +09:00
parent 33d3c3a6e0
commit e0c24f6554
5 changed files with 89 additions and 59 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "yoshiki",
"version": "1.0.8",
"version": "1.1.0",
"author": "Zoe Roux <zoe.roux@sdg.moe> (https://github.com/AnonymusRaccoon)",
"license": "MIT",
"keywords": [

View File

@@ -21,7 +21,7 @@ import {
} from "../type";
import { isBreakpoints } from "../utils";
import { shorthandsFn } from "../shorthands";
import { StyleFunc, NativeCssFunc } from "./type";
import { StyleFunc, NativeCssFunc, EnhancedStyle } from "./type";
import { useReducer, useRef } from "react";
const useBreakpoint = (): number => {
@@ -64,15 +64,17 @@ const useForceRerender = () => {
return useReducer((x) => x + 1, 0)[1];
};
type State = EnhancedStyle<ViewStyle | TextStyle | ImageStyle> | undefined;
export const useYoshiki = () => {
const breakpoint = useBreakpoint();
const theme = useTheme();
const rerender = useForceRerender();
const childStyles = useRef<Record<string, ViewStyle | TextStyle | ImageStyle | undefined>>({});
const childStyles = useRef<Record<string, State>>({});
const css: NativeCssFunc = (cssList, leftOvers) => {
// The as any is because we can't be sure the style type is right one.
const css = processStyleListWithChild(cssList, childStyles.current as any);
const { child, ...css } = processStyleListWithChild(cssList, childStyles.current as any);
const processStyle = (styleList: Record<string, YoshikiStyle<unknown>>) => {
const ret = Object.fromEntries(
@@ -83,18 +85,20 @@ export const useYoshiki = () => {
return ret;
};
if (hasState<Record<string, ViewStyle>>(css)) {
if (hasState<State>(css)) {
const { hover, focus, press, ...inline } = css;
const { onPressIn, onPressOut, onHoverIn, onHoverOut, onFocus, onBlur } =
leftOvers as PressableProps;
const ret: StyleFunc<unknown> = ({ hovered, focused, pressed }) => {
childStyles.current = {};
assignChilds(childStyles.current, child);
if (hovered) assignChilds(childStyles.current, hover);
if (focused) assignChilds(childStyles.current, focus);
if (pressed) assignChilds(childStyles.current, press);
return [
processStyle(inline),
processStyle(child?.self ?? {}),
hovered && processStyle(hover?.self ?? {}),
focused && processStyle(focus?.self ?? {}),
pressed && processStyle(press?.self ?? {}),
@@ -138,7 +142,7 @@ export const useYoshiki = () => {
} else {
return {
...leftOvers,
style: [processStyle(css), leftOvers?.style],
style: [processStyle(css), processStyle(child?.self ?? {}), leftOvers?.style],
} as any;
}
};

View File

@@ -3,7 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for details.
//
import { WithState, YoshikiStyle, StyleList } from "../type";
import { WithState, YoshikiStyle, StyleList, WithChild } from "../type";
import { shorthandsFn } from "../shorthands";
import { ImageStyle, PressableProps, StyleProp, TextStyle, ViewStyle } from "react-native";
import { Theme } from "../theme";
@@ -43,37 +43,45 @@ export type StyleFunc<Style> = (state: {
type AddLO<T, LO> = [LO] extends [never] ? T : Omit<LO, "style"> & T;
type NativeStyle = ViewStyle | TextStyle | ImageStyle;
type CssList<Style> = StyleList<
(EnhancedStyle<Style> & Partial<WithChild<EnhancedStyle<NativeStyle>>>) | string
>;
type CssListWithState<Style> = StyleList<
| (EnhancedStyle<Style> &
Partial<WithChild<EnhancedStyle<NativeStyle>> & WithState<EnhancedStyle<NativeStyle>>>)
| string
>;
declare function nativeCss<Leftover = never>(
cssList: StyleList<EnhancedStyle<ViewStyle> | string>,
cssList: CssList<ViewStyle>,
leftOvers?: Leftover & { style?: StyleProp<ViewStyle> | null },
): AddLO<{ style?: ViewStyle }, Leftover>;
declare function nativeCss<Leftover = never>(
cssList: StyleList<
(EnhancedStyle<ViewStyle> & Partial<WithState<EnhancedStyle<NativeStyle>>>) | string
>,
cssList: CssListWithState<ViewStyle>,
leftOvers?: Leftover & { style?: StyleProp<ViewStyle> | StyleFunc<StyleProp<ViewStyle>> | null },
): AddLO<PressableProps, Leftover>;
declare function nativeCss<Leftover = never>(
cssList: StyleList<EnhancedStyle<TextStyle> | string>,
cssList: CssList<TextStyle>,
leftOvers?: Leftover & { style?: StyleProp<TextStyle> | null },
): AddLO<{ style?: TextStyle }, Leftover>;
declare function nativeCss<Leftover = never>(
cssList: StyleList<
(EnhancedStyle<TextStyle> & Partial<WithState<EnhancedStyle<NativeStyle>>>) | string
>,
cssList: CssListWithState<TextStyle>,
leftOvers?: Leftover & { style?: StyleProp<TextStyle> | StyleFunc<StyleProp<TextStyle>> | null },
): AddLO<PressableProps, Leftover>;
declare function nativeCss<Leftover = never>(
cssList: StyleList<EnhancedStyle<ImageStyle> | string>,
cssList: CssList<ImageStyle>,
leftOvers?: Leftover & { style?: StyleProp<ImageStyle> | null },
): AddLO<{ style?: ImageStyle }, Leftover>;
declare function nativeCss<Leftover = never>(
cssList: StyleList<
(EnhancedStyle<ImageStyle> & Partial<WithState<EnhancedStyle<NativeStyle>>>) | string
>,
leftOvers?: Leftover & { style?: StyleProp<ImageStyle> | StyleFunc<StyleProp<ImageStyle>> | null },
): AddLO<PressableProps & { style?: StyleProp<ImageStyle> | StyleFunc<StyleProp<ImageStyle>> }, Leftover>;
cssList: CssListWithState<ImageStyle>,
leftOvers?: Leftover & {
style?: StyleProp<ImageStyle> | StyleFunc<StyleProp<ImageStyle>> | null;
},
): AddLO<
PressableProps & { style?: StyleProp<ImageStyle> | StyleFunc<StyleProp<ImageStyle>> },
Leftover
>;
export type NativeCssFunc = typeof nativeCss;

View File

@@ -19,9 +19,13 @@ export type WithState<Style> = {
focus: { self?: Style; [key: string]: Style | undefined };
press: { self?: Style; [key: string]: Style | undefined };
};
export type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U];
export type WithChild<Style> = {
child: { self?: Style; [key: string]: Style | undefined };
};
export const hasState = <Style = Record<string, unknown>>(obj: unknown): obj is WithState<Style> => {
export const hasState = <Style = Record<string, unknown>>(
obj: unknown,
): obj is WithState<Style> => {
if (!obj || typeof obj !== "object") return false;
return "hover" in obj || "focus" in obj || "press" in obj;
};

View File

@@ -7,7 +7,13 @@ import { useId, useInsertionEffect } from "react";
import { prefix } from "inline-style-prefixer";
import { Properties as CssProperties } from "csstype";
import { Theme, breakpoints, useTheme } from "../theme";
import { WithState, YoshikiStyle, StyleList, processStyleListWithoutChild } from "../type";
import {
WithState,
YoshikiStyle,
StyleList,
processStyleListWithoutChild,
WithChild,
} from "../type";
import { forceBreakpoint, isBreakpoints } from "../utils";
import { StyleRegistry, useStyleRegistry } from "./registry";
import { shorthandsFn } from "../shorthands";
@@ -148,12 +154,13 @@ const dedupProperties = (...classList: (string[] | undefined)[]) => {
atomicMap.set(key, name);
}
}
rest.push(...atomicMap.values())
rest.push(...atomicMap.values());
return rest.join(" ");
};
export const yoshikiCssToClassNames = (
css: Record<string, unknown> & Partial<WithState<Record<string, unknown>>>,
css: Record<string, unknown> &
Partial<WithState<Record<string, unknown>> & WithChild<Record<string, unknown>>>,
classNames: string[] | undefined,
{
registry,
@@ -169,7 +176,7 @@ export const yoshikiCssToClassNames = (
preprocessBlock?: PreprocessBlockFunction;
},
) => {
const { hover, focus, press, ...inline } = css;
const { child, hover, focus, press, ...inline } = css;
const processStyles = (
inlineStyle?: Record<string, unknown>,
@@ -189,6 +196,7 @@ export const yoshikiCssToClassNames = (
return dedupProperties(
processStyles(inline),
processStyles(child?.self),
processStyles(hover?.self, "hover"),
processStyles(focus?.self, "focus"),
processStyles(press?.self, "press"),
@@ -199,44 +207,18 @@ export const yoshikiCssToClassNames = (
);
};
export const useYoshiki = () => {
const theme = useTheme();
const registry = useStyleRegistry();
const [parentPrefix, childPrefix] = useClassId();
useInsertionEffect(() => {
registry.flushToBrowser();
}, [registry]);
return {
css: <Leftover>(
cssList: StyleList<CssObject | string>,
leftOverProps?: Leftover & { className?: string },
): { className: string } & Omit<Leftover, "className"> => {
const [css, parentKeys] = processStyleListWithoutChild(cssList);
const { className, ...leftOver } = leftOverProps ?? {};
generateChildCss(css, { parentPrefix, childPrefix, registry, theme });
return {
className: yoshikiCssToClassNames(
css,
[...parentKeys.map((x) => `${childPrefix}${x}`), ...(className?.split(" ") ?? [])],
{ registry, theme, parentPrefix },
),
...leftOver,
} as any;
},
theme,
};
};
export const useClassId = () => {
const id = useId().replaceAll(":", "-");
return ["ysp" + id, "ysc" + id] as const;
};
export const generateChildCss = (
{ hover, focus, press }: Partial<WithState<Record<string, unknown>>>,
{
hover,
focus,
press,
child,
}: Partial<WithState<Record<string, unknown>> & WithChild<Record<string, unknown>>>,
{
parentPrefix,
childPrefix,
@@ -305,7 +287,39 @@ export const generateChildCss = (
}
};
processStyles(child, "normal");
processStyles(hover, "hover");
processStyles(focus, "focus");
processStyles(press, "press");
};
export const useYoshiki = () => {
const theme = useTheme();
const registry = useStyleRegistry();
const [parentPrefix, childPrefix] = useClassId();
useInsertionEffect(() => {
registry.flushToBrowser();
}, [registry]);
return {
css: <Leftover>(
cssList: StyleList<CssObject | string> & Partial<WithState<CssObject>> & WithChild<CssObject>,
leftOverProps?: Leftover & { className?: string },
): { className: string } & Omit<Leftover, "className"> => {
const [css, parentKeys] = processStyleListWithoutChild(cssList);
const { className, ...leftOver } = leftOverProps ?? {};
generateChildCss(css, { parentPrefix, childPrefix, registry, theme });
return {
className: yoshikiCssToClassNames(
css,
[...parentKeys.map((x) => `${childPrefix}${x}`), ...(className?.split(" ") ?? [])],
{ registry, theme, parentPrefix },
),
...leftOver,
} as any;
},
theme,
};
};