@yak/solid 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +126 -0
- package/dist/context/baseContext.d.ts +7 -0
- package/dist/context/baseContext.js +8 -0
- package/dist/context/baseContext.js.map +1 -0
- package/dist/context/index.d.ts +29 -0
- package/dist/context/index.js +17 -0
- package/dist/context/index.js.map +1 -0
- package/dist/index.d.ts +352 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +473 -0
- package/dist/internal.js +382 -0
- package/dist/internal.js.map +1 -0
- package/dist/isolated-source-eval/index.d.ts +30 -0
- package/dist/isolated-source-eval/index.js +315 -0
- package/dist/isolated-source-eval/index.js.map +1 -0
- package/dist/isolated-source-eval/worker.js +59 -0
- package/dist/isolated-source-eval/worker.js.map +1 -0
- package/dist/loaders/vite-plugin.d.ts +116 -0
- package/dist/loaders/vite-plugin.js +862 -0
- package/dist/loaders/vite-plugin.js.map +1 -0
- package/loaders/vite-plugin.ts +29 -0
- package/package.json +108 -0
- package/runtime/__tests__/mergeClasses.test.ts +22 -0
- package/runtime/__tests__/reactivity.test.tsx +157 -0
- package/runtime/__tests__/styled.test.tsx +270 -0
- package/runtime/__tests__/testUtils.tsx +25 -0
- package/runtime/__tests__/tsconfig.json +9 -0
- package/runtime/__tests__/typeTest.tsx +62 -0
- package/runtime/atoms.ts +44 -0
- package/runtime/context/baseContext.ts +5 -0
- package/runtime/context/index.ts +37 -0
- package/runtime/cssLiteral.ts +215 -0
- package/runtime/cssProp.ts +26 -0
- package/runtime/globalStyle.ts +54 -0
- package/runtime/index.ts +39 -0
- package/runtime/internal.ts +41 -0
- package/runtime/internals/mergeClasses.ts +36 -0
- package/runtime/internals/mergeCssProp.ts +54 -0
- package/runtime/internals/unitPostFix.ts +34 -0
- package/runtime/keyframes.ts +30 -0
- package/runtime/mocks/cssLiteral.ts +74 -0
- package/runtime/mocks/globalStyle.ts +14 -0
- package/runtime/mocks/keyframes.ts +29 -0
- package/runtime/mocks/styled.ts +8 -0
- package/runtime/publicStyledApi.ts +177 -0
- package/runtime/styled.ts +393 -0
- package/runtime/styledDom.ts +139 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adds the yak `css` prop to Solid's JSX types.
|
|
3
|
+
*
|
|
4
|
+
* Solid compiles JSX natively (via babel-preset-solid), so unlike React
|
|
5
|
+
* there is no jsx-runtime module to wrap. Importing "@yak/solid" is enough
|
|
6
|
+
* to activate this augmentation.
|
|
7
|
+
*
|
|
8
|
+
* The css prop is compiled away by the yak SWC plugin into a
|
|
9
|
+
* `__yak_mergeCssProp` spread; on custom components it requires the
|
|
10
|
+
* component to accept `class` and `style` props.
|
|
11
|
+
*/
|
|
12
|
+
import type { ComponentStyles } from "./cssLiteral.ts";
|
|
13
|
+
|
|
14
|
+
declare module "@solidjs/web" {
|
|
15
|
+
namespace JSX {
|
|
16
|
+
// ElementAttributes is the shared base of HTMLAttributes, SVGAttributes
|
|
17
|
+
// and MathMLAttributes in Solid 2. Declaration merging requires the type
|
|
18
|
+
// parameter to keep Solid's original name.
|
|
19
|
+
// eslint-disable-next-line no-unused-vars
|
|
20
|
+
interface ElementAttributes<T> {
|
|
21
|
+
css?: ComponentStyles<{}>;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ComponentStyles } from "./cssLiteral.js";
|
|
2
|
+
import type { YakComponent } from "./publicStyledApi.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Values that may be interpolated into a `globalStyle` template.
|
|
6
|
+
*
|
|
7
|
+
* Only build-time values are allowed: constants, `keyframes` animation names,
|
|
8
|
+
* static `css` mixins and styled-component selectors. Runtime functions
|
|
9
|
+
* (`${(props) => ...}`) are intentionally excluded: a global rule has no element
|
|
10
|
+
* to attach a CSS variable to. Declare a CSS custom property instead and toggle
|
|
11
|
+
* it via an attribute/class on the root element.
|
|
12
|
+
*/
|
|
13
|
+
export type GlobalStyleInterpolation = string | number | ComponentStyles<{}> | YakComponent<any>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Declares global, unscoped styles that ride the same zero-runtime extraction
|
|
17
|
+
* pipeline as `styled` and `keyframes`.
|
|
18
|
+
*
|
|
19
|
+
* Global styles are **part of the stylesheet, not part of the render tree**.
|
|
20
|
+
* They apply exactly when the module declaring them is included in the bundle,
|
|
21
|
+
* typically by importing it from a layout or entry point. They cannot be
|
|
22
|
+
* conditionally mounted; express conditions in CSS (`@media`, `@supports`,
|
|
23
|
+
* `:root[data-theme]`, `:has()`) or through CSS custom properties.
|
|
24
|
+
*
|
|
25
|
+
* @usage
|
|
26
|
+
*
|
|
27
|
+
* ```tsx
|
|
28
|
+
* import { globalStyle, keyframes } from "@yak/solid";
|
|
29
|
+
*
|
|
30
|
+
* const fadeIn = keyframes`
|
|
31
|
+
* from { opacity: 0; }
|
|
32
|
+
* `;
|
|
33
|
+
*
|
|
34
|
+
* globalStyle`
|
|
35
|
+
* :root {
|
|
36
|
+
* --spacing: 4px;
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
* body {
|
|
40
|
+
* margin: 0;
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* ::view-transition-new(root) {
|
|
44
|
+
* animation: ${fadeIn} 200ms ease;
|
|
45
|
+
* }
|
|
46
|
+
* `;
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export const globalStyle = (
|
|
50
|
+
_styles: TemplateStringsArray,
|
|
51
|
+
..._values: Array<GlobalStyleInterpolation>
|
|
52
|
+
): void => {
|
|
53
|
+
return undefined;
|
|
54
|
+
};
|
package/runtime/index.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file contains the typings for the public API for @yak/solid and testing mocks
|
|
3
|
+
*
|
|
4
|
+
* IMPORTANT: In production builds, imports to this file should be replaced by the SWC plugin.
|
|
5
|
+
* If you're seeing this code in a production environment, your build process may not be configured correctly.
|
|
6
|
+
*
|
|
7
|
+
* Purpose:
|
|
8
|
+
* 1. Provide a test-friendly version of the @yak/solid API
|
|
9
|
+
* 2. Offer type definitions for the public API
|
|
10
|
+
*
|
|
11
|
+
* Usage in tests:
|
|
12
|
+
* - Import from "@yak/solid" as usual in your test files
|
|
13
|
+
* - These mock implementations will be used instead of the actual runtime
|
|
14
|
+
*
|
|
15
|
+
* Warning for production:
|
|
16
|
+
* - If these exports are used in a production build, styles will not be applied correctly
|
|
17
|
+
* - Ensure your build process is configured to use the yak-swc SWC plugin
|
|
18
|
+
* (e.g. via `yak` from "@yak/solid/vite")
|
|
19
|
+
*
|
|
20
|
+
* For maintainers:
|
|
21
|
+
* - Keep this API surface in sync with the actual implementation in @yak/solid/internal
|
|
22
|
+
* - Ensure mock implementations here are suitable for testing purposes
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import "./cssProp.js";
|
|
26
|
+
|
|
27
|
+
// the context is a package-level export so the vite plugin can alias
|
|
28
|
+
// "@yak/solid/context/baseContext" to the user's theme context file
|
|
29
|
+
export { useTheme, YakThemeProvider, YakThemeContext } from "@yak/solid/context";
|
|
30
|
+
export type { YakTheme } from "./context/index.ts";
|
|
31
|
+
|
|
32
|
+
export type { GenericYakComponentOf, YakComponent } from "./publicStyledApi.ts";
|
|
33
|
+
|
|
34
|
+
export { atoms } from "./atoms.js";
|
|
35
|
+
export { css } from "./mocks/cssLiteral.js";
|
|
36
|
+
export { keyframes } from "./mocks/keyframes.js";
|
|
37
|
+
export { styled } from "./mocks/styled.js";
|
|
38
|
+
export { globalStyle } from "./mocks/globalStyle.js";
|
|
39
|
+
export type { GlobalStyleInterpolation } from "./mocks/globalStyle.js";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IMPORTANT: This file contains the internal implementation of @yak/solid's core APIs.
|
|
3
|
+
*
|
|
4
|
+
* Purpose:
|
|
5
|
+
* - Provides the actual runtime implementations for styled, css, keyframes, etc.
|
|
6
|
+
* - Referenced only by the compiled code "@yak/solid/internal"
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* - DO NOT import from this file directly in your application code.
|
|
10
|
+
* - Always use `import { ... } from "@yak/solid"` in your source files.
|
|
11
|
+
* - The SWC plugin will automatically transform those imports to use this internal module.
|
|
12
|
+
*
|
|
13
|
+
* Why this exists:
|
|
14
|
+
* 1. Allows for cleaner separation between the public API and internal implementation
|
|
15
|
+
* 2. Enables better typing for both pre-compilation (user code) and post-compilation scenarios
|
|
16
|
+
* 3. Easier testing and snapshot comparisons without hashes (in index.ts)
|
|
17
|
+
* 4. Makes @yak/solid work out-of-the-box with testing frameworks like Jest and Vitest
|
|
18
|
+
*
|
|
19
|
+
* Note for maintainers:
|
|
20
|
+
* - Ensure that types from this file are not published to avoid exposing internal APIs.
|
|
21
|
+
*
|
|
22
|
+
* @internal This module is not intended for direct usage and may change without notice.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
export { css } from "./cssLiteral.js";
|
|
26
|
+
export { styled } from "./styled.js";
|
|
27
|
+
export { atoms } from "./atoms.js";
|
|
28
|
+
export { keyframes } from "./keyframes.js";
|
|
29
|
+
export { globalStyle } from "./globalStyle.js";
|
|
30
|
+
|
|
31
|
+
// the context is a package-level export so the vite plugin can alias
|
|
32
|
+
// "@yak/solid/context/baseContext" to the user's theme context file
|
|
33
|
+
export { useTheme, YakThemeProvider, YakThemeContext } from "@yak/solid/context";
|
|
34
|
+
|
|
35
|
+
// runtime internals (helpers which get injected by the compiler)
|
|
36
|
+
export { unitPostFix as __yak_unitPostFix } from "./internals/unitPostFix.js";
|
|
37
|
+
export { mergeCssProp as __yak_mergeCssProp } from "./internals/mergeCssProp.js";
|
|
38
|
+
export { mergeClasses as __yak_mergeClassNames } from "./internals/mergeClasses.js";
|
|
39
|
+
|
|
40
|
+
// export shorthand for DOM styled components (e.g. for styled.div)
|
|
41
|
+
export * from "./styledDom.js";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize Solid `class` values for string merging.
|
|
3
|
+
* Solid 2 allows strings, arrays and object syntax on the class attribute.
|
|
4
|
+
*/
|
|
5
|
+
export const normalizeClass = (value: unknown): string => {
|
|
6
|
+
if (!value) return "";
|
|
7
|
+
if (typeof value === "string") return value;
|
|
8
|
+
if (Array.isArray(value)) return value.map(normalizeClass).filter(Boolean).join(" ");
|
|
9
|
+
if (typeof value === "object") {
|
|
10
|
+
return Object.keys(value)
|
|
11
|
+
.filter((key) => (value as Record<string, unknown>)[key])
|
|
12
|
+
.join(" ");
|
|
13
|
+
}
|
|
14
|
+
return String(value);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Merges the folded yak class name with a user `class` value.
|
|
19
|
+
*
|
|
20
|
+
* Injected by the compiler (as `__yak_mergeClassNames`) when it replaces a JSX
|
|
21
|
+
* usage of a fully static styled component with a plain element:
|
|
22
|
+
* ```tsx
|
|
23
|
+
* const Card = styled.div`color: red;`;
|
|
24
|
+
* <Card class={active() && "active"} />
|
|
25
|
+
* ```
|
|
26
|
+
* becomes
|
|
27
|
+
* ```tsx
|
|
28
|
+
* <div class={__yak_mergeClassNames("yX", active() && "active")} />
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export const mergeClasses = (yakClass: string, userClass: unknown): string | undefined => {
|
|
32
|
+
const user = normalizeClass(userClass);
|
|
33
|
+
if (!yakClass) return user || undefined;
|
|
34
|
+
if (!user) return yakClass;
|
|
35
|
+
return yakClass + " " + user;
|
|
36
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Classes } from "../cssLiteral.js";
|
|
2
|
+
import { RuntimeStyleProcessor } from "../publicStyledApi.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This is an internal helper function to merge relevant props of a native element with a css prop.
|
|
6
|
+
* It's automatically added when using the `css` prop in a JSX element.
|
|
7
|
+
* e.g.:
|
|
8
|
+
* ```tsx
|
|
9
|
+
* <p
|
|
10
|
+
* class="foo"
|
|
11
|
+
* css={css`
|
|
12
|
+
* color: green;
|
|
13
|
+
* `}
|
|
14
|
+
* {...{ style: { padding: "30px" }}}
|
|
15
|
+
* />
|
|
16
|
+
*/
|
|
17
|
+
export const mergeCssProp = (
|
|
18
|
+
relevantProps: {
|
|
19
|
+
class?: string;
|
|
20
|
+
style?: Record<string, string>;
|
|
21
|
+
} & Record<string, unknown>,
|
|
22
|
+
cssProp: RuntimeStyleProcessor<unknown> | false | null | undefined,
|
|
23
|
+
) => {
|
|
24
|
+
const classes = new Classes(relevantProps.class);
|
|
25
|
+
|
|
26
|
+
const existingStyle = relevantProps.style;
|
|
27
|
+
const style = existingStyle ? { ...existingStyle } : {};
|
|
28
|
+
|
|
29
|
+
// a falsy css prop applies no styles, e.g. `css={on && css`...`}` with `on` false
|
|
30
|
+
if (cssProp) {
|
|
31
|
+
cssProp({}, classes, style);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Forward all other props (onClick, aria-*, id, …) untouched and only
|
|
35
|
+
// override class/style with the merged result — the transform already
|
|
36
|
+
// built `relevantProps` in JSX attribute order, so this preserves overrides.
|
|
37
|
+
const result: Record<string, unknown> & {
|
|
38
|
+
class?: string;
|
|
39
|
+
style?: Record<string, string>;
|
|
40
|
+
} = { ...relevantProps };
|
|
41
|
+
|
|
42
|
+
if (Object.keys(style).length > 0) {
|
|
43
|
+
result.style = style;
|
|
44
|
+
} else {
|
|
45
|
+
delete result.style;
|
|
46
|
+
}
|
|
47
|
+
if (classes.value) {
|
|
48
|
+
result.class = classes.value;
|
|
49
|
+
} else {
|
|
50
|
+
delete result.class;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal helper called by transformed code. Do not use directly.
|
|
3
|
+
*
|
|
4
|
+
* Takes a function and a css unit and returns the result of the function concatenated with the unit
|
|
5
|
+
*
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { styled } from "@yak/solid";
|
|
8
|
+
*
|
|
9
|
+
* const Button = styled.button<{ $width?: boolean }>`
|
|
10
|
+
* width: ${({ $width }) => $width}px;
|
|
11
|
+
* `;
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Which will be transformed to:
|
|
15
|
+
* ```tsx
|
|
16
|
+
* import { styled } from "@yak/solid/internal";
|
|
17
|
+
*
|
|
18
|
+
* const Button = styled.button<{ $width?: boolean }>(
|
|
19
|
+
* "button", {
|
|
20
|
+
* width: unitPostFix({ $width }) => $width, "px")
|
|
21
|
+
* });
|
|
22
|
+
*/
|
|
23
|
+
export const unitPostFix = (arg: unknown, unit: string) => {
|
|
24
|
+
switch (typeof arg) {
|
|
25
|
+
case "function":
|
|
26
|
+
return (props: any) => unitPostFix(arg(props), unit);
|
|
27
|
+
case "number":
|
|
28
|
+
case "string":
|
|
29
|
+
return `${arg}${unit}`;
|
|
30
|
+
// Ignore falsy values
|
|
31
|
+
default:
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Allows to use CSS keyframe animations in a styled or css block
|
|
3
|
+
*
|
|
4
|
+
* @usage
|
|
5
|
+
*
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { styled, keyframes } from "@yak/solid";
|
|
8
|
+
*
|
|
9
|
+
* const rotate = keyframes`
|
|
10
|
+
* from {
|
|
11
|
+
* transform: rotate(0deg);
|
|
12
|
+
* }
|
|
13
|
+
* to {
|
|
14
|
+
* transform: rotate(360deg);
|
|
15
|
+
* }
|
|
16
|
+
* `;
|
|
17
|
+
*
|
|
18
|
+
* const Spinner = styled.div`
|
|
19
|
+
* animation: ${rotate} 1s linear infinite;
|
|
20
|
+
* `;
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export const keyframes = <T extends (string | number | bigint)[] = never>(
|
|
24
|
+
styles: TemplateStringsArray,
|
|
25
|
+
..._dynamic: T
|
|
26
|
+
): string => {
|
|
27
|
+
// during compilation all args of keyframe are compiled
|
|
28
|
+
// to a string which references the animation name
|
|
29
|
+
return styles as any as string;
|
|
30
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { css as cssInternal, NestedRuntimeStyleProcessor } from "../cssLiteral.js";
|
|
2
|
+
|
|
3
|
+
export type { ComponentStyles, CSSInterpolation } from "../cssLiteral.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Allows to use CSS styles in a styled or css block
|
|
7
|
+
*
|
|
8
|
+
* e.g.
|
|
9
|
+
*
|
|
10
|
+
* ```tsx
|
|
11
|
+
* const Component = styled.div`
|
|
12
|
+
* color: black;
|
|
13
|
+
* ${({$active}) => $active && css`color: red;`}
|
|
14
|
+
* `;
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export const css: typeof cssInternal = (styles: TemplateStringsArray, ...args: unknown[]) => {
|
|
18
|
+
// When called in yak files as a template tag (without SWC transformation),
|
|
19
|
+
// return { __yak: rawCss } so the cross-file resolver can
|
|
20
|
+
// extract the mixin value from evaluated .yak files.
|
|
21
|
+
if (Array.isArray(styles) && "raw" in styles) {
|
|
22
|
+
let rawCss = styles[0];
|
|
23
|
+
for (let i = 0; i < args.length; i++) {
|
|
24
|
+
const interpolation = args[i];
|
|
25
|
+
rawCss +=
|
|
26
|
+
interpolation && typeof interpolation === "object" && "__yak" in interpolation
|
|
27
|
+
? (interpolation as { __yak: string }).__yak
|
|
28
|
+
: String(interpolation);
|
|
29
|
+
rawCss += styles[i + 1];
|
|
30
|
+
}
|
|
31
|
+
return { __yak: rawCss } as any;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const dynamicCssFunctions: NestedRuntimeStyleProcessor[] = [];
|
|
35
|
+
for (const arg of args as Array<string | Function | object>) {
|
|
36
|
+
// Dynamic CSS e.g.
|
|
37
|
+
// css`${props => props.active && css`color: red;`}`
|
|
38
|
+
// compiled -> css((props: { active: boolean }) => props.active && css("yak31e4"))
|
|
39
|
+
if (typeof arg === "function") {
|
|
40
|
+
dynamicCssFunctions.push(arg as unknown as NestedRuntimeStyleProcessor);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (dynamicCssFunctions.length === 0) {
|
|
44
|
+
return {
|
|
45
|
+
class: "",
|
|
46
|
+
style: undefined,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return ((props: unknown) => {
|
|
50
|
+
for (let i = 0; i < dynamicCssFunctions.length; i++) {
|
|
51
|
+
// run the dynamic expressions and ignore the return value
|
|
52
|
+
// the execution is important to ensure that the user code is executed
|
|
53
|
+
// the same way as in the real runtime
|
|
54
|
+
executeDynamicExpressionRecursively(props, dynamicCssFunctions[i]);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
class: "",
|
|
58
|
+
style: undefined,
|
|
59
|
+
};
|
|
60
|
+
}) as any;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
function executeDynamicExpressionRecursively(
|
|
64
|
+
props: unknown,
|
|
65
|
+
expression: NestedRuntimeStyleProcessor,
|
|
66
|
+
) {
|
|
67
|
+
const classes = new Set<string>();
|
|
68
|
+
const style = {};
|
|
69
|
+
let result = expression(props, classes, style);
|
|
70
|
+
while (typeof result === "function") {
|
|
71
|
+
result = result(props, classes, style);
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { globalStyle as globalStyleInternal } from "../globalStyle.js";
|
|
2
|
+
|
|
3
|
+
export type { GlobalStyleInterpolation } from "../globalStyle.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Test-friendly mock of `globalStyle`.
|
|
7
|
+
*
|
|
8
|
+
* `globalStyle` has no runtime behaviour: the SWC plugin extracts its CSS at
|
|
9
|
+
* build time and replaces the call with a no-op. The mock mirrors that: it does
|
|
10
|
+
* nothing so yak files can be imported in Jest/Vitest without the compiler.
|
|
11
|
+
*/
|
|
12
|
+
export const globalStyle: typeof globalStyleInternal = (_styles, ..._values) => {
|
|
13
|
+
// no-op in the mock
|
|
14
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { keyframes as keyframesInternal } from "../keyframes.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Allows to use CSS keyframe animations in a styled or css block
|
|
5
|
+
*
|
|
6
|
+
* @usage
|
|
7
|
+
*
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { styled, keyframes } from "@yak/solid";
|
|
10
|
+
*
|
|
11
|
+
* const rotate = keyframes`
|
|
12
|
+
* from {
|
|
13
|
+
* transform: rotate(0deg);
|
|
14
|
+
* }
|
|
15
|
+
* to {
|
|
16
|
+
* transform: rotate(360deg);
|
|
17
|
+
* }
|
|
18
|
+
* `;
|
|
19
|
+
*
|
|
20
|
+
* const Spinner = styled.div`
|
|
21
|
+
* animation: ${rotate} 1s linear infinite;
|
|
22
|
+
* `;
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export const keyframes: typeof keyframesInternal = (_styles, ..._dynamic) => {
|
|
26
|
+
// the keyframes function is a no-op in the mock
|
|
27
|
+
// as it has no dynamic runtime behavior but only css
|
|
28
|
+
return "";
|
|
29
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { JSX } from "@solidjs/web";
|
|
2
|
+
import { styled as StyledFactory } from "../styled.js";
|
|
3
|
+
|
|
4
|
+
export const styled = /* @__PURE__ */ new Proxy(StyledFactory, {
|
|
5
|
+
get(target, TagName: keyof JSX.IntrinsicElements) {
|
|
6
|
+
return target(TagName);
|
|
7
|
+
},
|
|
8
|
+
}) as typeof StyledFactory;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// This is the public facing API for the styled object.
|
|
2
|
+
import type { JSX } from "@solidjs/web";
|
|
3
|
+
import type { YakTheme } from "./context/index.js";
|
|
4
|
+
import type { Accessor } from "solid-js";
|
|
5
|
+
import { CSSInterpolation, yakComponentSymbol } from "./cssLiteral.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Component signature accepted and produced by `styled`.
|
|
9
|
+
*
|
|
10
|
+
* Intentionally NOT Solid 2's `Component` type: that alias constrains props
|
|
11
|
+
* to `Record<string, any>` which does not compose with the `object`-based
|
|
12
|
+
* generics of the styled API (kept in sync with the React runtime).
|
|
13
|
+
*/
|
|
14
|
+
export type AnyComponent<T> = (props: T) => JSX.Element;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Style object the runtime writes CSS custom properties into.
|
|
18
|
+
* Solid also allows string styles on elements; the runtime normalizes
|
|
19
|
+
* those to objects before processing (see `unwrapStyle` in styled.ts).
|
|
20
|
+
*/
|
|
21
|
+
export type StyleObject = JSX.CSSProperties & Record<`--${string}`, string | number | undefined>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Main styled interface that combines HTML tag mappings with the styled function.
|
|
25
|
+
* This is the primary entry point for creating styled components.
|
|
26
|
+
*/
|
|
27
|
+
export interface Styled extends MappedHtmlTags, StyledFn {}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Function interface for creating styled components from any component or HTML tag.
|
|
31
|
+
* Supports Solid components, HTML tags, and custom web components.
|
|
32
|
+
*/
|
|
33
|
+
export interface StyledFn {
|
|
34
|
+
<TProps extends object = JSX.ElementAttributes<Element>>(
|
|
35
|
+
Component: HtmlTags | AnyComponent<TProps> | CustomWebComponentTag,
|
|
36
|
+
): LiteralWithAttrs<TProps>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A yak component with a special symbol that allows component targeting
|
|
41
|
+
* and proper attrs function handling.
|
|
42
|
+
* @example styled.svg`${Button}:hover & { fill: red; }` or styled(Button)`color: red;`
|
|
43
|
+
*/
|
|
44
|
+
export interface YakComponent<T> extends AnyComponent<T> {
|
|
45
|
+
// This is intentionally typed to hide the internal implementation details.
|
|
46
|
+
[yakComponentSymbol]: [unknown, unknown, unknown, unknown];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Styled component with attrs method for adding default props.
|
|
51
|
+
* Extends StyledLiteral with the ability to specify default attributes.
|
|
52
|
+
*/
|
|
53
|
+
export interface LiteralWithAttrs<T extends object> extends StyledLiteral<T> {
|
|
54
|
+
attrs: <
|
|
55
|
+
TAttrsIn extends object = {},
|
|
56
|
+
TAttrsOut extends AttrsMerged<T, TAttrsIn> = AttrsMerged<T, TAttrsIn>,
|
|
57
|
+
>(
|
|
58
|
+
attrs: Attrs<T, TAttrsIn, TAttrsOut>,
|
|
59
|
+
) => StyledLiteral<Substitute<T, TAttrsIn>>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Template literal function for defining CSS styles with interpolation support.
|
|
64
|
+
* Accepts CSS template strings and interpolated values with proper typing.
|
|
65
|
+
*/
|
|
66
|
+
export interface StyledLiteral<T> {
|
|
67
|
+
<TCSSProps>(
|
|
68
|
+
styles: TemplateStringsArray,
|
|
69
|
+
...values: Array<
|
|
70
|
+
CSSInterpolation<
|
|
71
|
+
T &
|
|
72
|
+
// Prevent TypeScript from inferring types from template literal usage
|
|
73
|
+
// This ensures proper typing and enables destructuring hints
|
|
74
|
+
NoInfer<TCSSProps> & { theme: Accessor<YakTheme> }
|
|
75
|
+
>
|
|
76
|
+
>
|
|
77
|
+
): YakComponent<TCSSProps & T>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Function variant of attrs that receives current props and returns additional props.
|
|
82
|
+
* Allows for dynamic prop generation based on component state.
|
|
83
|
+
*/
|
|
84
|
+
export interface AttrsFunction<
|
|
85
|
+
TBaseProps,
|
|
86
|
+
TIn extends object = {},
|
|
87
|
+
TOut extends AttrsMerged<TBaseProps, TIn> = AttrsMerged<TBaseProps, TIn>,
|
|
88
|
+
> {
|
|
89
|
+
(p: Substitute<TBaseProps & { theme: Accessor<YakTheme> }, TIn>): Partial<TOut>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Merges provided props with initial props, making specified props optional.
|
|
94
|
+
* Includes theme support for styled components.
|
|
95
|
+
*/
|
|
96
|
+
export type AttrsMerged<TBaseProps, TIn extends object = {}> = Substitute<
|
|
97
|
+
TBaseProps & { theme?: Accessor<YakTheme> },
|
|
98
|
+
TIn
|
|
99
|
+
>;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Maps all HTML tag names to their corresponding styled component types with attributes support.
|
|
103
|
+
* Provides typed access to all standard HTML elements through the styled interface.
|
|
104
|
+
*/
|
|
105
|
+
export type MappedHtmlTags = {
|
|
106
|
+
[Tag in HtmlTags]: LiteralWithAttrs<JSX.IntrinsicElements[Tag]>;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The attrs function allows adding additional props to a styled component.
|
|
111
|
+
* Props can be specified as an object or as a function that receives current props.
|
|
112
|
+
*/
|
|
113
|
+
export type Attrs<
|
|
114
|
+
TBaseProps,
|
|
115
|
+
TIn extends object = {},
|
|
116
|
+
TOut extends AttrsMerged<TBaseProps, TIn> = AttrsMerged<TBaseProps, TIn>,
|
|
117
|
+
> = Partial<TOut> | AttrsFunction<TBaseProps, TIn, TOut>;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Utility type to merge two object types, with properties from B taking precedence.
|
|
121
|
+
* If a property exists in both A and B, the property from B is used.
|
|
122
|
+
*/
|
|
123
|
+
export type Substitute<A extends object, B extends object> = FastOmit<A, keyof B> & B;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Union type of all valid HTML element tag names.
|
|
127
|
+
* Derived from Solid's JSX intrinsic elements.
|
|
128
|
+
*/
|
|
129
|
+
export type HtmlTags = keyof JSX.IntrinsicElements;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Custom web component tag pattern that must contain at least one hyphen.
|
|
133
|
+
* Follows the web component naming convention.
|
|
134
|
+
*/
|
|
135
|
+
export type CustomWebComponentTag = `${string}-${string}`;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Utility type to efficiently remove properties from an object type.
|
|
139
|
+
* More performant than the built-in Omit type for large object types.
|
|
140
|
+
*/
|
|
141
|
+
export type FastOmit<T extends object, U extends string | number | symbol> = {
|
|
142
|
+
[K in keyof T as K extends U ? never : K]: T[K];
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Set-like collector for class names.
|
|
147
|
+
*
|
|
148
|
+
* Implemented as a string builder in the runtime (a Set<string>
|
|
149
|
+
* split → Set → Array.from → join round-trip dominates render cost);
|
|
150
|
+
* a real Set<string> also satisfies this interface.
|
|
151
|
+
*/
|
|
152
|
+
export type ClassCollector = {
|
|
153
|
+
add(name: string): void;
|
|
154
|
+
has(name: string): boolean;
|
|
155
|
+
delete(name: string): void;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Type of all functions that can be passed to manipulate styles
|
|
160
|
+
*
|
|
161
|
+
* `$dynamic` marks processors that execute user functions or write style
|
|
162
|
+
* values at render time; purely static processors (class names only) can
|
|
163
|
+
* skip theme lookup and style-object allocation.
|
|
164
|
+
*/
|
|
165
|
+
export type RuntimeStyleProcessor<T> = ((
|
|
166
|
+
props: T,
|
|
167
|
+
classes: ClassCollector,
|
|
168
|
+
style: StyleObject,
|
|
169
|
+
) => void) & { $dynamic?: boolean };
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Utility type to keep the generic API of a component while still being able to use it in a selector
|
|
173
|
+
*/
|
|
174
|
+
export type GenericYakComponentOf<T, P = {}> = T &
|
|
175
|
+
YakComponent<P> & {
|
|
176
|
+
<G = {}>(props: P & G): JSX.Element;
|
|
177
|
+
};
|