mirror of
https://github.com/zoriya/yoshiki.git
synced 2025-12-05 22:56:14 +00:00
Add a generic no-state child
This commit is contained in:
@@ -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": [
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user