@yak/react 0.0.0-stage → 10.0.0-rc.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 +102 -2
- package/dist/context/baseContext.cjs +9 -0
- package/dist/context/baseContext.cjs.map +1 -0
- package/dist/context/baseContext.d.cts +6 -0
- package/dist/context/baseContext.d.ts +6 -0
- package/dist/context/baseContext.js +8 -0
- package/dist/context/baseContext.js.map +1 -0
- package/dist/context/index.cjs +45 -0
- package/dist/context/index.cjs.map +1 -0
- package/dist/context/index.d.cts +21 -0
- package/dist/context/index.d.ts +21 -0
- package/dist/context/index.js +15 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/index.server.cjs +47 -0
- package/dist/context/index.server.cjs.map +1 -0
- package/dist/context/index.server.d.cts +10 -0
- package/dist/context/index.server.d.ts +10 -0
- package/dist/context/index.server.js +17 -0
- package/dist/context/index.server.js.map +1 -0
- package/dist/cross-file-resolver/index.d.ts +199 -0
- package/dist/cross-file-resolver/index.js +535 -0
- package/dist/cross-file-resolver/index.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +320 -0
- package/dist/index.d.ts +320 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.cjs +544 -0
- package/dist/internal.cjs.map +1 -0
- package/dist/internal.d.cts +452 -0
- package/dist/internal.d.ts +452 -0
- package/dist/internal.js +363 -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 +317 -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/jsx-dev-runtime.cjs +2 -0
- package/dist/jsx-dev-runtime.cjs.map +1 -0
- package/dist/jsx-dev-runtime.d.cts +7 -0
- package/dist/jsx-dev-runtime.d.ts +7 -0
- package/dist/jsx-dev-runtime.js +2 -0
- package/dist/jsx-dev-runtime.js.map +1 -0
- package/dist/jsx-runtime.cjs +2 -0
- package/dist/jsx-runtime.cjs.map +1 -0
- package/dist/jsx-runtime.d.cts +38 -0
- package/dist/jsx-runtime.d.ts +38 -0
- package/dist/jsx-runtime.js +2 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/loaders/turbo-loader.cjs +803 -0
- package/dist/loaders/turbo-loader.cjs.map +1 -0
- package/dist/loaders/turbo-loader.d.cts +15 -0
- package/dist/loaders/vite-plugin.d.ts +111 -0
- package/dist/loaders/vite-plugin.js +878 -0
- package/dist/loaders/vite-plugin.js.map +1 -0
- package/dist/loaders/webpack-loader.cjs +697 -0
- package/dist/loaders/webpack-loader.cjs.map +1 -0
- package/dist/loaders/webpack-loader.d.cts +12 -0
- package/dist/rsbuild/index.d.ts +29 -0
- package/dist/rsbuild/index.js +52 -0
- package/dist/rsbuild/index.js.map +1 -0
- package/dist/static/chunk-DK3Fl9T5.js +1 -0
- package/dist/static/index.cjs +2 -0
- package/dist/static/index.cjs.map +1 -0
- package/dist/static/index.d.cts +28 -0
- package/dist/static/index.d.ts +28 -0
- package/dist/static/index.js +2 -0
- package/dist/static/index.js.map +1 -0
- package/dist/withYak/index.cjs +208 -0
- package/dist/withYak/index.cjs.map +1 -0
- package/dist/withYak/index.d.cts +121 -0
- package/dist/withYak/index.d.ts +121 -0
- package/dist/withYak/index.js +177 -0
- package/dist/withYak/index.js.map +1 -0
- package/loaders/lib/resolveCrossFileSelectors.ts +162 -0
- package/loaders/turbo-evaluator.ts +134 -0
- package/loaders/turbo-loader.ts +180 -0
- package/loaders/vite-plugin.ts +28 -0
- package/loaders/webpack-loader.ts +44 -0
- package/package.json +214 -3
- package/rsbuild/index.ts +92 -0
- package/runtime/__tests__/atoms.test.tsx +48 -0
- package/runtime/__tests__/attrs.test.tsx +490 -0
- package/runtime/__tests__/cssLiteral.test.tsx +503 -0
- package/runtime/__tests__/cssProp.test.tsx +392 -0
- package/runtime/__tests__/cssPropTest.tsx +227 -0
- package/runtime/__tests__/foldedJsxParity.test.tsx +117 -0
- package/runtime/__tests__/styled.test.tsx +653 -0
- package/runtime/__tests__/tsconfig.json +18 -0
- package/runtime/__tests__/typeTest.tsx +374 -0
- package/runtime/atoms.tsx +36 -0
- package/runtime/context/README.md +84 -0
- package/runtime/context/baseContext.tsx +5 -0
- package/runtime/context/index.server.tsx +22 -0
- package/runtime/context/index.tsx +36 -0
- package/runtime/cssLiteral.tsx +214 -0
- package/runtime/globalStyle.tsx +54 -0
- package/runtime/index.ts +37 -0
- package/runtime/internal.ts +42 -0
- package/runtime/internals/mergeClassNames.ts +23 -0
- package/runtime/internals/mergeCssProp.ts +69 -0
- package/runtime/internals/propMarkers.ts +20 -0
- package/runtime/internals/unitPostFix.ts +34 -0
- package/runtime/jsx-dev-runtime.ts +6 -0
- package/runtime/jsx-runtime.ts +48 -0
- package/runtime/keyframes.tsx +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 +159 -0
- package/runtime/styled.tsx +372 -0
- package/runtime/styledDom.tsx +139 -0
- package/withYak/allow-global-css.ts +94 -0
- package/withYak/index.ts +282 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { css, styled, keyframes, GenericYakComponentOf } from "@yak/react";
|
|
2
|
+
import React, { ReactElement } from "react";
|
|
3
|
+
import { useRef } from "react";
|
|
4
|
+
|
|
5
|
+
declare module "@yak/react" {
|
|
6
|
+
export interface YakTheme {
|
|
7
|
+
mood: "happy" | "sad";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const keyframe = keyframes`
|
|
12
|
+
from {
|
|
13
|
+
opacity: 0;
|
|
14
|
+
}
|
|
15
|
+
to {
|
|
16
|
+
opacity: 1;
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
const mixin = css`
|
|
21
|
+
animation: ${keyframe} 1s;
|
|
22
|
+
color: ${({ theme }) => {
|
|
23
|
+
// verify that theme is typed correctly in styled.tsx
|
|
24
|
+
const mood: "happy" | "sad" = theme.mood;
|
|
25
|
+
return mood === "happy" ? "green" : "red";
|
|
26
|
+
}};
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const Button = styled.button`
|
|
30
|
+
color: ${({ theme }) => {
|
|
31
|
+
// verify that theme is typed correctly in styled.tsx
|
|
32
|
+
const mood: "happy" | "sad" = theme.mood;
|
|
33
|
+
return mood === "happy" ? "green" : "red";
|
|
34
|
+
}};
|
|
35
|
+
${mixin}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
// Verify that a HTMLButtonElement Ref can be
|
|
39
|
+
// used for a styled.button component
|
|
40
|
+
const RefTest1 = () => {
|
|
41
|
+
const ref = useRef<HTMLButtonElement>(null);
|
|
42
|
+
return <Button ref={ref}>Hello World</Button>;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Verify that a HTMLButtonElement Ref function can be
|
|
46
|
+
// used for a styled.button component
|
|
47
|
+
const RefTest2 = () => {
|
|
48
|
+
return (
|
|
49
|
+
<Button
|
|
50
|
+
ref={(element) => {
|
|
51
|
+
const button: HTMLButtonElement = element!;
|
|
52
|
+
console.log(button);
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
Hello World
|
|
56
|
+
</Button>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Verify that a ref is inferred correctly
|
|
61
|
+
const Button2 = styled(Button)``;
|
|
62
|
+
const RefTest3 = () => {
|
|
63
|
+
const ref = useRef<HTMLButtonElement>(null);
|
|
64
|
+
return <Button2 ref={ref}>Hello World</Button2>;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Verify that a ref function is inferred correctly
|
|
68
|
+
const RefTest4 = () => {
|
|
69
|
+
return (
|
|
70
|
+
<Button2
|
|
71
|
+
ref={(element) => {
|
|
72
|
+
const button: HTMLButtonElement = element!;
|
|
73
|
+
console.log(button);
|
|
74
|
+
}}
|
|
75
|
+
>
|
|
76
|
+
Hello World
|
|
77
|
+
</Button2>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Verify that props are merged correctly
|
|
82
|
+
const Button3 = styled.button<{ $primary?: boolean }>`
|
|
83
|
+
${({ $primary }) =>
|
|
84
|
+
$primary &&
|
|
85
|
+
css`
|
|
86
|
+
color: green;
|
|
87
|
+
`}
|
|
88
|
+
`;
|
|
89
|
+
const Button4 = styled(Button3)<{ $secondary?: boolean }>`
|
|
90
|
+
${({ $secondary }) =>
|
|
91
|
+
$secondary &&
|
|
92
|
+
css`
|
|
93
|
+
color: blue;
|
|
94
|
+
`}
|
|
95
|
+
`;
|
|
96
|
+
|
|
97
|
+
const MergeTest1 = () => {
|
|
98
|
+
return (
|
|
99
|
+
<Button4 type="button" $primary $secondary>
|
|
100
|
+
Click me
|
|
101
|
+
</Button4>
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const MergeTestErrors = () => {
|
|
106
|
+
switch (Math.random().toString(64)) {
|
|
107
|
+
case "$primary":
|
|
108
|
+
// @ts-expect-error - primary must only allow booleans
|
|
109
|
+
return <Button4 $primary="true" />;
|
|
110
|
+
case "$secondary":
|
|
111
|
+
// @ts-expect-error - secondary must only allow booleans
|
|
112
|
+
return <Button4 $secondary="true" />;
|
|
113
|
+
case "html prop":
|
|
114
|
+
// @ts-expect-error - only valid html attribute values are allowed
|
|
115
|
+
return <Button4 type="submit submit" />;
|
|
116
|
+
case "html prop2":
|
|
117
|
+
return <Button4 type="submit" />;
|
|
118
|
+
case "ref":
|
|
119
|
+
// @ts-expect-error - only valid html elements
|
|
120
|
+
return <Button4 ref={(element: HTMLDivElement | null) => {}} />;
|
|
121
|
+
case "theme":
|
|
122
|
+
// @ts-expect-error - theme is not part of the props
|
|
123
|
+
return <Button4 theme={{ mood: "happy" }} />;
|
|
124
|
+
default:
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Verify that a ref is not added if the component does not accept it
|
|
130
|
+
const Button5 = (props: { className: string }) => <button {...props}>Hello World</button>;
|
|
131
|
+
const Button6 = styled(Button5)``;
|
|
132
|
+
<Button6
|
|
133
|
+
// @ts-expect-error - should not allow ref on a component that does not accept it
|
|
134
|
+
ref={console.log}
|
|
135
|
+
/>;
|
|
136
|
+
|
|
137
|
+
const CompositionOverridingAndMergingTest = () => {
|
|
138
|
+
const case1 = () => {
|
|
139
|
+
const Child = styled.div<{ $child: boolean }>`
|
|
140
|
+
${({ $child }) =>
|
|
141
|
+
$child &&
|
|
142
|
+
css`
|
|
143
|
+
color: red;
|
|
144
|
+
`}
|
|
145
|
+
`;
|
|
146
|
+
|
|
147
|
+
const Parent = styled.div`
|
|
148
|
+
${Child} {
|
|
149
|
+
color: blue;
|
|
150
|
+
}
|
|
151
|
+
`;
|
|
152
|
+
|
|
153
|
+
// should not inherit component props of Child
|
|
154
|
+
const _ = (
|
|
155
|
+
<Parent>
|
|
156
|
+
<Child $child />
|
|
157
|
+
</Parent>
|
|
158
|
+
);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const case2 = () => {
|
|
162
|
+
const Child = styled.div<{ $child: boolean }>`
|
|
163
|
+
${({ $child }) =>
|
|
164
|
+
$child &&
|
|
165
|
+
css`
|
|
166
|
+
color: red;
|
|
167
|
+
`}
|
|
168
|
+
`;
|
|
169
|
+
|
|
170
|
+
const Mixin = css<{ $mixin: boolean }>`
|
|
171
|
+
color: ${({ $mixin }) => ($mixin ? `blue` : `red`)};
|
|
172
|
+
`;
|
|
173
|
+
|
|
174
|
+
const Parent = styled.div<{ $mixin: boolean }>`
|
|
175
|
+
${Child} {
|
|
176
|
+
color: blue;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
${Mixin};
|
|
180
|
+
`;
|
|
181
|
+
|
|
182
|
+
// Overriding with Child and Mixin should work
|
|
183
|
+
const _ = (
|
|
184
|
+
<Parent $mixin>
|
|
185
|
+
<Child $child />
|
|
186
|
+
</Parent>
|
|
187
|
+
);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const case3 = () => {
|
|
191
|
+
const Child = styled.div<{ $child: boolean }>`
|
|
192
|
+
${({ $child }) =>
|
|
193
|
+
$child &&
|
|
194
|
+
css`
|
|
195
|
+
color: red;
|
|
196
|
+
`}
|
|
197
|
+
`;
|
|
198
|
+
|
|
199
|
+
const Parent = styled.div`
|
|
200
|
+
${Child} {
|
|
201
|
+
color: blue;
|
|
202
|
+
}
|
|
203
|
+
color: ${
|
|
204
|
+
// @ts-expect-error - should not allow props which have not been defined (like styled-components)
|
|
205
|
+
({ $colorMe }) => ($colorMe ? `blue` : `red`)
|
|
206
|
+
};
|
|
207
|
+
`;
|
|
208
|
+
|
|
209
|
+
// Overriding with Child and Mixin should work
|
|
210
|
+
const _ = (
|
|
211
|
+
// @ts-expect-error - should not allow props which have not been defined (like styled-components)
|
|
212
|
+
<Parent $colorMe>
|
|
213
|
+
<Child $child />
|
|
214
|
+
</Parent>
|
|
215
|
+
);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const case4 = () => {
|
|
219
|
+
const Wrap = styled.button``;
|
|
220
|
+
const Icon = styled.svg<{ $active: boolean }>``;
|
|
221
|
+
const Button = styled(Wrap)`
|
|
222
|
+
${Icon} {
|
|
223
|
+
color: red;
|
|
224
|
+
}
|
|
225
|
+
`;
|
|
226
|
+
const Button2 = styled.button`
|
|
227
|
+
${Icon} {
|
|
228
|
+
color: red;
|
|
229
|
+
}
|
|
230
|
+
`;
|
|
231
|
+
|
|
232
|
+
// @ts-expect-error $active should not be required
|
|
233
|
+
<Button $active={true}>Click me</Button>;
|
|
234
|
+
|
|
235
|
+
// @ts-expect-error $active should not be required
|
|
236
|
+
<Button2 $active={true}>Click me</Button2>;
|
|
237
|
+
|
|
238
|
+
// should be callable without any properties
|
|
239
|
+
<Button>Click me</Button>;
|
|
240
|
+
<Button2>Click me</Button2>;
|
|
241
|
+
|
|
242
|
+
// the types should be equal
|
|
243
|
+
type Equal = typeof Button extends typeof Button2 ? true : false;
|
|
244
|
+
const _: Equal = true;
|
|
245
|
+
};
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const GenericComponentWorks = () => {
|
|
249
|
+
interface MyGenericProps<T> {
|
|
250
|
+
input: T;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
type MyGenericComponent<T> = React.ElementType<MyGenericProps<T>> & {
|
|
254
|
+
<TOther>(props: MyGenericProps<TOther>): React.ReactNode;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
type MyDefaultType = {
|
|
258
|
+
[x: string]: any;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const GenericComponent: MyGenericComponent<MyDefaultType> = () => {
|
|
262
|
+
return null;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const ConcreteStyledComponent = GenericComponent<"test">;
|
|
266
|
+
|
|
267
|
+
const MyComponent = styled(ConcreteStyledComponent)``;
|
|
268
|
+
|
|
269
|
+
const Test = () => {
|
|
270
|
+
return (
|
|
271
|
+
<div>
|
|
272
|
+
<MyComponent input="test" />
|
|
273
|
+
</div>
|
|
274
|
+
);
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const KeyframesWithConstants = () => {
|
|
279
|
+
const constString = "rotate(360deg)";
|
|
280
|
+
const constNumber = 360;
|
|
281
|
+
|
|
282
|
+
const rotate = keyframes`
|
|
283
|
+
from {
|
|
284
|
+
transform: rotate(${constNumber}deg);
|
|
285
|
+
}
|
|
286
|
+
to {
|
|
287
|
+
transform: ${constString};
|
|
288
|
+
}
|
|
289
|
+
`;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const SelectorMixinsShouldNotAlterType = () => {
|
|
293
|
+
const Button = styled.button<{ $primary?: boolean }>``;
|
|
294
|
+
|
|
295
|
+
const Mixin = css`
|
|
296
|
+
${Button} {
|
|
297
|
+
color: red;
|
|
298
|
+
}
|
|
299
|
+
`;
|
|
300
|
+
|
|
301
|
+
const X = styled.div`
|
|
302
|
+
${Button} {
|
|
303
|
+
color: blue;
|
|
304
|
+
}
|
|
305
|
+
${Mixin};
|
|
306
|
+
`;
|
|
307
|
+
|
|
308
|
+
<X />;
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const InferenceShouldWorkWithComplexTypes = () => {
|
|
312
|
+
const Title = styled<React.ComponentPropsWithRef<"strong" | "h1">>({} as any)<{
|
|
313
|
+
$primary: boolean;
|
|
314
|
+
}>`
|
|
315
|
+
${({ $primary }) => {
|
|
316
|
+
if ($primary) {
|
|
317
|
+
return css`
|
|
318
|
+
color: red;
|
|
319
|
+
`;
|
|
320
|
+
}
|
|
321
|
+
}}
|
|
322
|
+
`;
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const WebComponentsShouldWork = () => {
|
|
326
|
+
const MyWebComponent = styled("my-web-component")<{ $primary?: boolean }>`
|
|
327
|
+
color: red;
|
|
328
|
+
`;
|
|
329
|
+
|
|
330
|
+
// @ts-expect-error - web component props should match the defined props
|
|
331
|
+
<MyWebComponent $primary={42} />;
|
|
332
|
+
|
|
333
|
+
// Should allow all valid HTML attributes
|
|
334
|
+
<MyWebComponent onClick={(e) => console.log("clicked", e.target)} />;
|
|
335
|
+
|
|
336
|
+
// Should allow refs
|
|
337
|
+
<MyWebComponent
|
|
338
|
+
$primary
|
|
339
|
+
ref={(el) => {
|
|
340
|
+
if (el) {
|
|
341
|
+
console.log(el.classList.toggle("active"));
|
|
342
|
+
}
|
|
343
|
+
}}
|
|
344
|
+
/>;
|
|
345
|
+
|
|
346
|
+
<MyWebComponent $primary />;
|
|
347
|
+
|
|
348
|
+
return <MyWebComponent />;
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
const GenericYakComponentShouldWork = () => {
|
|
352
|
+
const Component: <T extends object>(props: T) => ReactElement<T> = (props) => (
|
|
353
|
+
<div {...props}>hello</div>
|
|
354
|
+
);
|
|
355
|
+
<Component<{ TEST: "ME" }> TEST="ME" />;
|
|
356
|
+
|
|
357
|
+
const StyledComponent = styled(Component)`` as GenericYakComponentOf<typeof Component>;
|
|
358
|
+
const StyledComponentWithAdditionalProps = styled(Component)`` as GenericYakComponentOf<
|
|
359
|
+
typeof Component,
|
|
360
|
+
{
|
|
361
|
+
$primary?: boolean;
|
|
362
|
+
}
|
|
363
|
+
>;
|
|
364
|
+
<StyledComponent<{ TEST: "ME" }> TEST="ME" />;
|
|
365
|
+
<StyledComponentWithAdditionalProps<{ TEST: "ME" }> TEST="ME" $primary />;
|
|
366
|
+
|
|
367
|
+
const shouldBeSelectable = styled.div`
|
|
368
|
+
${StyledComponent} {
|
|
369
|
+
&:hover {
|
|
370
|
+
color: blue;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
`;
|
|
374
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ClassNames, ComponentStyles, css } from "./cssLiteral.js";
|
|
2
|
+
import { RuntimeStyleProcessor } from "./publicStyledApi.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Allows to use atomic CSS classes in a styled or css block
|
|
6
|
+
*
|
|
7
|
+
* @usage
|
|
8
|
+
*
|
|
9
|
+
* ```tsx
|
|
10
|
+
* import { styled, atoms } from "@yak/react";
|
|
11
|
+
*
|
|
12
|
+
* const Button = styled.button<{ $primary?: boolean }>`
|
|
13
|
+
* ${atoms("text-teal-600", "text-base", "rounded-md")}
|
|
14
|
+
* ${props => props.$primary && atoms("shadow-md")}
|
|
15
|
+
* `;
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export const atoms = <T,>(
|
|
19
|
+
...atoms: (string | RuntimeStyleProcessor<T> | false)[]
|
|
20
|
+
): ComponentStyles<T> => {
|
|
21
|
+
const staticClasses = new ClassNames();
|
|
22
|
+
const dynamicFunctions: RuntimeStyleProcessor<T>[] = [];
|
|
23
|
+
|
|
24
|
+
for (const atom of atoms) {
|
|
25
|
+
if (typeof atom === "string") {
|
|
26
|
+
staticClasses.add(atom);
|
|
27
|
+
} else if (typeof atom === "function") {
|
|
28
|
+
dynamicFunctions.push(atom);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// the collected classes are passed as css()'s static class name,
|
|
33
|
+
// only the dynamic atoms stay functions
|
|
34
|
+
// @ts-expect-error the internal implementation of css is not typed
|
|
35
|
+
return css(staticClasses.value, ...dynamicFunctions);
|
|
36
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Yak Theme Context
|
|
2
|
+
|
|
3
|
+
The yak theme context allows users to configure the theme of their yak application.
|
|
4
|
+
|
|
5
|
+
It works for react-server components and for normal react components.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
The context will be available in every `styled` and `css` literal:
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
const Headline = styled.h1`
|
|
13
|
+
color: ${(props) => props.theme.colors.primary};
|
|
14
|
+
`;
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## React Server Components
|
|
18
|
+
|
|
19
|
+
To use the yak theme context in a react server component, you need to place a `yak.context.tsx` in
|
|
20
|
+
the same directory as your Next.js config.
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
// yak.context.tsx
|
|
24
|
+
export async function getYakThemeContext() {
|
|
25
|
+
return {
|
|
26
|
+
colors: {
|
|
27
|
+
primary: "red",
|
|
28
|
+
secondary: "blue",
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
declare module "@yak/react/context" {
|
|
34
|
+
export interface YakTheme extends Awaited<ReturnType<typeof getYakThemeContext>> {}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
> **Note:** The `getYakThemeContext` function can be async, which is useful when you need to access
|
|
39
|
+
> server-side APIs like `cookies()` or `headers()` from Next.js 15+. @yak/react automatically handles
|
|
40
|
+
> the promise resolution using React's `use()` hook internally.
|
|
41
|
+
|
|
42
|
+
In your root layout component you have to forward the entire response of `getYakThemeContext`
|
|
43
|
+
context to the client:
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
// layout.tsx
|
|
47
|
+
import { YakThemeProvider } from "@yak/react";
|
|
48
|
+
|
|
49
|
+
export function Layout({ children }) {
|
|
50
|
+
return <YakThemeProvider>{children}</YakThemeProvider>;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Internally this works because of an alias set by `withYak` inside the NextJs config.
|
|
55
|
+
|
|
56
|
+
## React Components
|
|
57
|
+
|
|
58
|
+
If you are using server components you can **skip this section**.
|
|
59
|
+
|
|
60
|
+
To use the yak theme context in a normal react component, you can just use the `YakThemeProvider`:
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
// _app.tsx
|
|
64
|
+
import { YakThemeProvider } from "@yak/react/context";
|
|
65
|
+
|
|
66
|
+
const theme = {
|
|
67
|
+
colors: {
|
|
68
|
+
primary: "red",
|
|
69
|
+
secondary: "blue",
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
declare module "@yak/react/context" {
|
|
74
|
+
export interface YakTheme extends typeof theme { }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function App({ children }) {
|
|
78
|
+
return (
|
|
79
|
+
<YakThemeProvider value={theme}>
|
|
80
|
+
{children}
|
|
81
|
+
</YakThemeProvider>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//
|
|
2
|
+
// This file is the react-server component version of index.tsx
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// @ts-ignore - in the current @types/react "cache" is not typed
|
|
6
|
+
import React, { ReactNode, cache, use } from "react";
|
|
7
|
+
import { YakTheme, YakThemeProvider as YakThemeClientProvider } from "./index.js";
|
|
8
|
+
|
|
9
|
+
// the following import might be changed by
|
|
10
|
+
// the user config in withYak to point to their own
|
|
11
|
+
// context
|
|
12
|
+
import { getYakThemeContext } from "@yak/react/context/baseContext";
|
|
13
|
+
|
|
14
|
+
/** Request based RSC YAK Context */
|
|
15
|
+
const getYakContext = cache(() => getYakThemeContext());
|
|
16
|
+
export const useTheme = (): YakTheme | undefined => {
|
|
17
|
+
const theme: YakTheme | undefined | Promise<YakTheme> = getYakContext();
|
|
18
|
+
return theme instanceof Promise ? use(theme) : theme;
|
|
19
|
+
};
|
|
20
|
+
export const YakThemeProvider = ({ children }: { children: ReactNode }) => {
|
|
21
|
+
return <YakThemeClientProvider theme={getYakContext()}>{children}</YakThemeClientProvider>;
|
|
22
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
//
|
|
3
|
+
// This file is the client component (browser & ssr) version of index.server.tsx
|
|
4
|
+
//
|
|
5
|
+
import React, { ReactNode, createContext, use } from "react";
|
|
6
|
+
|
|
7
|
+
export interface YakTheme {}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The yak theme context
|
|
11
|
+
* @see https://github.com/DigitecGalaxus/next-yak/blob/main/packages/yak-react/runtime/context/README.md
|
|
12
|
+
*/
|
|
13
|
+
const YakContext = createContext<YakTheme>({});
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns the current yak theme context
|
|
17
|
+
*
|
|
18
|
+
* @see https://github.com/DigitecGalaxus/next-yak/blob/main/packages/yak-react/runtime/context/README.md
|
|
19
|
+
*/
|
|
20
|
+
export const useTheme = (): YakTheme => {
|
|
21
|
+
const context = use(YakContext);
|
|
22
|
+
return context instanceof Promise ? use(context) : context;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Yak theme context provider
|
|
27
|
+
*
|
|
28
|
+
* @see https://github.com/DigitecGalaxus/next-yak/blob/main/packages/yak-react/runtime/context/README.md
|
|
29
|
+
*/
|
|
30
|
+
export const YakThemeProvider = ({
|
|
31
|
+
children,
|
|
32
|
+
theme = {},
|
|
33
|
+
}: {
|
|
34
|
+
children: ReactNode;
|
|
35
|
+
theme?: YakTheme;
|
|
36
|
+
}) => <YakContext.Provider value={theme}>{children}</YakContext.Provider>;
|