@awesomephant/reactcake 1.1.2 → 1.3.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/CHANGELOG.md +6 -0
- package/Readme.md +1 -1
- package/lib/Html/index.tsx +18 -4
- package/lib/ReactCake/ReactCake.test.tsx +0 -0
- package/lib/ReactCake/index.tsx +43 -12
- package/lib/ScaledSvg/index.tsx +8 -3
- package/lib/Svg/index.tsx +6 -2
- package/lib/index.ts +17 -5
- package/lib/package.json +1 -1
- package/lib/types.ts +37 -11
- package/package.json +18 -9
- package/site/astro.config.mjs +41 -9
- package/site/package.json +16 -8
- package/site/src/assets/ABCArealMonoVariable.woff2 +0 -0
- package/site/src/assets/ABCArealVariable.woff2 +0 -0
- package/site/src/components/Example.astro +1 -2
- package/site/src/components/PropList.astro +97 -0
- package/site/src/components/TSDComment.astro +32 -0
- package/site/src/content/background.md +18 -0
- package/site/src/content/installation.md +38 -0
- package/site/src/content.config.ts +8 -0
- package/site/src/examples/AxisX/styles.module.css +3 -4
- package/site/src/examples/AxisY/index.tsx +2 -1
- package/site/src/examples/AxisY/styles.module.css +3 -5
- package/site/src/examples/ColumnChart.tsx +0 -2
- package/site/src/examples/Columns.tsx +1 -1
- package/site/src/examples/DebugPadding.tsx +2 -1
- package/site/src/layouts/Base.astro +87 -60
- package/site/src/layouts/code.scss +32 -0
- package/site/src/pages/index.astro +137 -0
- package/tsconfig.json +1 -1
- package/site/src/layouts/code.css +0 -37
- package/site/src/pages/index.mdx +0 -73
package/CHANGELOG.md
ADDED
package/Readme.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A headless chart framework for React inspired by [Layercake](https://layercake.graphics/). See [documentation](https://reactcake.maxkohler.com/) for usage and motivation.
|
|
4
4
|
|
|
5
|
-
 [](https://github.com/awesomephant/reactcake/actions/workflows/release.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/@awesomephant/reactcake) [](https://github.com/awesomephant/reactcake/actions/workflows/release.yml)
|
|
6
6
|
|
|
7
7
|
## Contributing
|
|
8
8
|
|
package/lib/Html/index.tsx
CHANGED
|
@@ -3,18 +3,31 @@ import { useReactCake } from "../ReactCake";
|
|
|
3
3
|
import { LayoutProps } from "../types";
|
|
4
4
|
|
|
5
5
|
interface HtmlProps extends LayoutProps {
|
|
6
|
+
/**
|
|
7
|
+
* Sets the HTML `role` attribute
|
|
8
|
+
*/
|
|
9
|
+
role?: string;
|
|
6
10
|
children?: ReactNode;
|
|
7
11
|
}
|
|
8
12
|
|
|
9
|
-
const Html = ({
|
|
13
|
+
const Html = ({
|
|
14
|
+
overflow = "visible",
|
|
15
|
+
zIndex,
|
|
16
|
+
label,
|
|
17
|
+
labelledBy,
|
|
18
|
+
describedBy,
|
|
19
|
+
children,
|
|
20
|
+
role = "figure",
|
|
21
|
+
}: HtmlProps) => {
|
|
10
22
|
const { padding } = useReactCake();
|
|
11
23
|
|
|
12
24
|
return (
|
|
13
25
|
<div
|
|
14
26
|
className="reactcake-html"
|
|
15
|
-
aria-label={
|
|
27
|
+
aria-label={label}
|
|
28
|
+
aria-labelledby={labelledBy}
|
|
16
29
|
aria-describedby={describedBy}
|
|
17
|
-
role={
|
|
30
|
+
role={role}
|
|
18
31
|
style={{
|
|
19
32
|
position: "absolute",
|
|
20
33
|
top: `${padding.top}px`,
|
|
@@ -22,6 +35,7 @@ const Html = ({ overflow = "visible", label, labelledBy, describedBy, children }
|
|
|
22
35
|
right: `${padding.right}px`,
|
|
23
36
|
bottom: `${padding.bottom}px`,
|
|
24
37
|
overflow,
|
|
38
|
+
zIndex,
|
|
25
39
|
}}
|
|
26
40
|
>
|
|
27
41
|
{children}
|
|
@@ -29,4 +43,4 @@ const Html = ({ overflow = "visible", label, labelledBy, describedBy, children }
|
|
|
29
43
|
);
|
|
30
44
|
};
|
|
31
45
|
|
|
32
|
-
export
|
|
46
|
+
export { Html, type HtmlProps };
|
|
File without changes
|
package/lib/ReactCake/index.tsx
CHANGED
|
@@ -2,26 +2,57 @@ import { createContext, useContext, type CSSProperties, type ReactNode } from "r
|
|
|
2
2
|
import { extent } from "d3-array";
|
|
3
3
|
|
|
4
4
|
import { useDimensions } from "../hooks/useDimensions";
|
|
5
|
-
import type {
|
|
5
|
+
import type { ScaleLike, ContextValue, DomainLike, Accessor } from "../types";
|
|
6
6
|
|
|
7
7
|
const ReactCakeContext = createContext<ContextValue>(null);
|
|
8
8
|
|
|
9
|
+
type RangeLike = [number, number];
|
|
10
|
+
|
|
9
11
|
interface ReactCakeProps {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
data: object[];
|
|
13
|
+
/**
|
|
14
|
+
* The key in each row of data that corresponds to the x-field
|
|
15
|
+
*/
|
|
16
|
+
x?: Accessor;
|
|
17
|
+
x2?: Accessor;
|
|
18
|
+
y?: Accessor;
|
|
19
|
+
z?: Accessor;
|
|
20
|
+
flatData?: object[];
|
|
21
|
+
/**
|
|
22
|
+
* Sets the chart area padding in pixels following the {@link https://gist.github.com/mbostock/3019563 | D3 margin convention}. All values are optional.
|
|
23
|
+
*/
|
|
24
|
+
padding?: {
|
|
25
|
+
top?: number;
|
|
26
|
+
left?: number;
|
|
27
|
+
right?: number;
|
|
28
|
+
bottom?: number;
|
|
29
|
+
};
|
|
16
30
|
xScale?: ScaleLike;
|
|
17
|
-
|
|
31
|
+
xRange?: RangeLike;
|
|
32
|
+
x2Range?: RangeLike;
|
|
33
|
+
xDomain?: DomainLike;
|
|
34
|
+
x2Domain?: DomainLike;
|
|
35
|
+
x2Scale?: ScaleLike;
|
|
18
36
|
yScale?: ScaleLike;
|
|
37
|
+
yRange?: RangeLike;
|
|
38
|
+
yDomain?: DomainLike;
|
|
19
39
|
zScale?: ScaleLike;
|
|
40
|
+
zRange?: RangeLike;
|
|
41
|
+
zDomain?: DomainLike;
|
|
42
|
+
/**
|
|
43
|
+
* CSS property/value pairs applied to the container element.
|
|
44
|
+
*/
|
|
20
45
|
style?: CSSProperties;
|
|
46
|
+
/**
|
|
47
|
+
* Sets ranges to `[0, 100]` for SSR
|
|
48
|
+
*/
|
|
21
49
|
percentRange?: boolean;
|
|
22
50
|
children?: ReactNode;
|
|
23
51
|
}
|
|
24
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Top-level container element for your chart.
|
|
55
|
+
*/
|
|
25
56
|
const ReactCake = ({
|
|
26
57
|
data = [],
|
|
27
58
|
flatData,
|
|
@@ -50,7 +81,7 @@ const ReactCake = ({
|
|
|
50
81
|
const _x = typeof x === "string" ? (d) => d[x] : x;
|
|
51
82
|
const _x2 = typeof x2 === "string" ? (d) => d[x2] : x2;
|
|
52
83
|
const _y = typeof y === "string" ? (d) => d[y] : y;
|
|
53
|
-
const _z = typeof
|
|
84
|
+
const _z = typeof z === "string" ? (d) => d[z] : z;
|
|
54
85
|
|
|
55
86
|
const xIsBandscale = xScale && typeof xScale.bandwidth === "function";
|
|
56
87
|
const yIsBandscale = yScale && typeof yScale.bandwidth === "function";
|
|
@@ -71,10 +102,10 @@ const ReactCake = ({
|
|
|
71
102
|
? Array.from(new Set(data.map((d) => _z(d))))
|
|
72
103
|
: extent(flatData || data, _z);
|
|
73
104
|
|
|
74
|
-
const _xScale = xScale?.range([0, width]).domain(xDomain || extentX);
|
|
105
|
+
const _xScale = xScale?.range(xRange || [0, width]).domain(xDomain || extentX);
|
|
75
106
|
|
|
76
107
|
const _yScale = yScale
|
|
77
|
-
?.range([0, height])
|
|
108
|
+
?.range(yRange || [0, height])
|
|
78
109
|
.domain([
|
|
79
110
|
yDomain[0] !== null ? yDomain[0] : extentY[0],
|
|
80
111
|
yDomain[1] !== null ? yDomain[1] : extentY[1],
|
|
@@ -123,4 +154,4 @@ function useReactCake() {
|
|
|
123
154
|
return context;
|
|
124
155
|
}
|
|
125
156
|
|
|
126
|
-
export { ReactCake, useReactCake };
|
|
157
|
+
export { ReactCake, useReactCake, type ReactCakeProps };
|
package/lib/ScaledSvg/index.tsx
CHANGED
|
@@ -2,7 +2,10 @@ import { type ReactNode } from "react";
|
|
|
2
2
|
import { useReactCake } from "../ReactCake";
|
|
3
3
|
import { LayoutProps } from "../types";
|
|
4
4
|
|
|
5
|
-
interface
|
|
5
|
+
interface ScaledSvgProps extends LayoutProps {
|
|
6
|
+
/**
|
|
7
|
+
* Sets the contents of the <title> tag
|
|
8
|
+
*/
|
|
6
9
|
title?: string;
|
|
7
10
|
children?: ReactNode;
|
|
8
11
|
}
|
|
@@ -14,7 +17,8 @@ const ScaledSvg = ({
|
|
|
14
17
|
describedBy,
|
|
15
18
|
title,
|
|
16
19
|
overflow = "visible",
|
|
17
|
-
|
|
20
|
+
zIndex,
|
|
21
|
+
}: ScaledSvgProps) => {
|
|
18
22
|
const { padding } = useReactCake();
|
|
19
23
|
return (
|
|
20
24
|
<>
|
|
@@ -33,6 +37,7 @@ const ScaledSvg = ({
|
|
|
33
37
|
top: `${padding.top}px`,
|
|
34
38
|
left: `${padding.left}px`,
|
|
35
39
|
overflow: overflow,
|
|
40
|
+
zIndex,
|
|
36
41
|
}}
|
|
37
42
|
>
|
|
38
43
|
{title && <title>{title}</title>}
|
|
@@ -42,4 +47,4 @@ const ScaledSvg = ({
|
|
|
42
47
|
);
|
|
43
48
|
};
|
|
44
49
|
|
|
45
|
-
export
|
|
50
|
+
export { ScaledSvg, type ScaledSvgProps };
|
package/lib/Svg/index.tsx
CHANGED
|
@@ -3,11 +3,14 @@ import { useReactCake } from "../ReactCake";
|
|
|
3
3
|
import { LayoutProps } from "../types";
|
|
4
4
|
|
|
5
5
|
interface SvgProps extends LayoutProps {
|
|
6
|
+
/**
|
|
7
|
+
* Sets the contents of the `<title>` tag
|
|
8
|
+
*/
|
|
6
9
|
title?: string;
|
|
7
10
|
children?: ReactNode;
|
|
8
11
|
}
|
|
9
12
|
|
|
10
|
-
const Svg = ({ children, title, overflow = "hidden" }: SvgProps) => {
|
|
13
|
+
const Svg = ({ children, title, overflow = "hidden", zIndex }: SvgProps) => {
|
|
11
14
|
const { padding } = useReactCake();
|
|
12
15
|
return (
|
|
13
16
|
<svg
|
|
@@ -23,6 +26,7 @@ const Svg = ({ children, title, overflow = "hidden" }: SvgProps) => {
|
|
|
23
26
|
bottom: 0,
|
|
24
27
|
right: 0,
|
|
25
28
|
overflow: overflow,
|
|
29
|
+
zIndex,
|
|
26
30
|
}}
|
|
27
31
|
>
|
|
28
32
|
{title && <title>{title}</title>}
|
|
@@ -31,4 +35,4 @@ const Svg = ({ children, title, overflow = "hidden" }: SvgProps) => {
|
|
|
31
35
|
);
|
|
32
36
|
};
|
|
33
37
|
|
|
34
|
-
export
|
|
38
|
+
export { Svg, type SvgProps };
|
package/lib/index.ts
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
import Html from "./Html/index.tsx";
|
|
2
|
-
import Svg from "./Svg";
|
|
3
|
-
import ScaledSvg from "./ScaledSvg"
|
|
4
|
-
import { ReactCake ,
|
|
1
|
+
import { Html, type HtmlProps } from "./Html/index.tsx";
|
|
2
|
+
import { Svg, type SvgProps } from "./Svg";
|
|
3
|
+
import { ScaledSvg, type ScaledSvgProps } from "./ScaledSvg";
|
|
4
|
+
import { ReactCake, useReactCake, type ReactCakeProps } from "./ReactCake/index.tsx";
|
|
5
|
+
import { type Accessor } from "./types.ts";
|
|
5
6
|
|
|
6
|
-
export {
|
|
7
|
+
export {
|
|
8
|
+
Html,
|
|
9
|
+
ReactCake,
|
|
10
|
+
useReactCake,
|
|
11
|
+
Svg,
|
|
12
|
+
ScaledSvg,
|
|
13
|
+
type HtmlProps,
|
|
14
|
+
type ScaledSvgProps,
|
|
15
|
+
type SvgProps,
|
|
16
|
+
type ReactCakeProps,
|
|
17
|
+
type Accessor,
|
|
18
|
+
};
|
package/lib/package.json
CHANGED
package/lib/types.ts
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
|
|
3
|
-
export type DatumValue = number |
|
|
3
|
+
export type DatumValue = number | string | Date;
|
|
4
4
|
export type TickFormatter<TValue = DatumValue> = (value: TValue) => ReactNode;
|
|
5
5
|
|
|
6
|
-
export type
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
};
|
|
6
|
+
export type DomainLike =
|
|
7
|
+
| [min: number | null, max: number | null]
|
|
8
|
+
| Array<string | number | null>
|
|
9
|
+
| ((domain: Array<any>) => Array<any>)
|
|
10
|
+
| undefined;
|
|
12
11
|
|
|
13
12
|
export interface ScaleLike<T = DatumValue> {
|
|
14
13
|
(value: unknown): number;
|
|
15
|
-
domain: () => T
|
|
14
|
+
domain: (domain: DomainLike) => ScaleLike<T>;
|
|
16
15
|
range: (range: [number, number]) => ScaleLike<T>;
|
|
17
16
|
ticks?: (count?: number) => T[];
|
|
18
17
|
tickFormat?: (count?: number) => TickFormatter<T>;
|
|
@@ -20,7 +19,7 @@ export interface ScaleLike<T = DatumValue> {
|
|
|
20
19
|
invert?: (d: number) => ScaleLike;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
type Accessor = (
|
|
22
|
+
export type Accessor = string | ((d: unknown, i?: number) => unknown);
|
|
24
23
|
|
|
25
24
|
export interface ContextValue {
|
|
26
25
|
width: number;
|
|
@@ -35,12 +34,39 @@ export interface ContextValue {
|
|
|
35
34
|
xGet?: (d: unknown) => number;
|
|
36
35
|
yGet?: (d: unknown) => number;
|
|
37
36
|
zGet?: (d: unknown) => number | string;
|
|
38
|
-
padding:
|
|
37
|
+
padding: {
|
|
38
|
+
top?: number;
|
|
39
|
+
left?: number;
|
|
40
|
+
right?: number;
|
|
41
|
+
bottom?: number;
|
|
42
|
+
};
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
export interface LayoutProps {
|
|
42
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Sets the CSS `overflow` property.
|
|
48
|
+
* @defaultValue "visible"
|
|
49
|
+
*/
|
|
50
|
+
overflow?: "hidden" | "visible";
|
|
51
|
+
/**
|
|
52
|
+
* Sets the `aria-label` attribute
|
|
53
|
+
*/
|
|
43
54
|
label?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Sets the `aria-labelledby` attribute
|
|
57
|
+
*/
|
|
44
58
|
labelledBy?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Sets the `aria-describedby` attribute
|
|
61
|
+
*/
|
|
45
62
|
describedBy?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Sets the CSS `z-index` property
|
|
65
|
+
*/
|
|
66
|
+
zIndex?: number;
|
|
67
|
+
/**
|
|
68
|
+
* Sets the CSS `pointer-events` property.
|
|
69
|
+
* @defaultValue true
|
|
70
|
+
*/
|
|
71
|
+
pointerEvents?: boolean;
|
|
46
72
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awesomephant/reactcake",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "A headless chart framework for React inspired by Layercake",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -27,21 +27,30 @@
|
|
|
27
27
|
"build:ts": "esbuild lib/index.ts --outdir=./dist --bundle",
|
|
28
28
|
"build:min": "esbuild lib/index.ts --outdir=./dist --entry-names=[dir]/[name].min --bundle --minify",
|
|
29
29
|
"build:types": "tsc",
|
|
30
|
-
"build:lib": "run-p 'build:ts' 'build:min'"
|
|
31
|
-
"build:demo": "esbuild src/index.ts src/index.html --loader:.html=copy --outdir=./_site --minify"
|
|
30
|
+
"build:lib": "run-p 'build:ts' 'build:min'"
|
|
32
31
|
},
|
|
33
32
|
"devDependencies": {
|
|
34
|
-
"esbuild": "^0.28.
|
|
33
|
+
"esbuild": "^0.28.2",
|
|
35
34
|
"npm-run-all": "^4.1.5",
|
|
36
|
-
"tsx": "^4.
|
|
37
|
-
"typescript": "^
|
|
35
|
+
"tsx": "^4.23.15",
|
|
36
|
+
"typescript": "^6.0.0"
|
|
38
37
|
},
|
|
39
38
|
"workspaces": [
|
|
40
|
-
"
|
|
41
|
-
"
|
|
39
|
+
"lib",
|
|
40
|
+
"site"
|
|
42
41
|
],
|
|
43
42
|
"allowScripts": {
|
|
44
43
|
"esbuild@0.28.2": true,
|
|
45
|
-
"fsevents@2.3.3": true
|
|
44
|
+
"fsevents@2.3.3": true,
|
|
45
|
+
"@parcel/watcher@2.6.0": true
|
|
46
|
+
},
|
|
47
|
+
"release": {
|
|
48
|
+
"plugins": [
|
|
49
|
+
"@semantic-release/commit-analyzer",
|
|
50
|
+
"@semantic-release/release-notes-generator",
|
|
51
|
+
"@semantic-release/changelog",
|
|
52
|
+
"@semantic-release/npm",
|
|
53
|
+
"@semantic-release/github"
|
|
54
|
+
]
|
|
46
55
|
}
|
|
47
56
|
}
|
package/site/astro.config.mjs
CHANGED
|
@@ -1,22 +1,54 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
import { defineConfig } from
|
|
3
|
-
import {
|
|
2
|
+
import { defineConfig, fontProviders } from "astro/config";
|
|
3
|
+
import {
|
|
4
|
+
transformerRenderIndentGuides,
|
|
5
|
+
transformerRenderLineNumber,
|
|
6
|
+
} from "@shikijs/transformers";
|
|
4
7
|
|
|
5
|
-
import mdx from
|
|
8
|
+
import mdx from "@astrojs/mdx";
|
|
6
9
|
|
|
7
|
-
import react from
|
|
10
|
+
import react from "@astrojs/react";
|
|
8
11
|
|
|
9
12
|
export default defineConfig({
|
|
13
|
+
fonts: [
|
|
14
|
+
{
|
|
15
|
+
provider: fontProviders.local(),
|
|
16
|
+
name: "Areal",
|
|
17
|
+
cssVariable: "--font-areal",
|
|
18
|
+
options: {
|
|
19
|
+
variants: [
|
|
20
|
+
{
|
|
21
|
+
weight: "100 900",
|
|
22
|
+
style: "normal",
|
|
23
|
+
src: ["./src/assets/ABCArealVariable.woff2"],
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
provider: fontProviders.local(),
|
|
30
|
+
name: "Areal Mono",
|
|
31
|
+
cssVariable: "--font-areal-mono",
|
|
32
|
+
options: {
|
|
33
|
+
variants: [
|
|
34
|
+
{
|
|
35
|
+
weight: "100 900",
|
|
36
|
+
style: "normal",
|
|
37
|
+
src: ["./src/assets/ABCArealMonoVariable.woff2"],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
],
|
|
10
43
|
markdown: {
|
|
11
44
|
shikiConfig: {
|
|
12
|
-
theme:
|
|
45
|
+
theme: "css-variables",
|
|
13
46
|
defaultColor: false,
|
|
14
|
-
transformers:[transformerRenderLineNumber(), transformerRenderIndentGuides()]
|
|
15
47
|
},
|
|
16
48
|
syntaxHighlight: {
|
|
17
|
-
type:
|
|
18
|
-
|
|
49
|
+
type: "shiki",
|
|
50
|
+
},
|
|
19
51
|
},
|
|
20
52
|
|
|
21
|
-
integrations: [mdx(), react()]
|
|
53
|
+
integrations: [mdx(), react()],
|
|
22
54
|
});
|
package/site/package.json
CHANGED
|
@@ -7,23 +7,31 @@
|
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
9
|
"start": "astro dev",
|
|
10
|
-
"build": "
|
|
10
|
+
"build:typedoc": "typedoc ../lib/index.ts --tsconfig ../tsconfig.json --json ./src/content/typedoc.json",
|
|
11
|
+
"build:astro": "astro build",
|
|
11
12
|
"preview": "astro preview",
|
|
12
|
-
"astro": "astro"
|
|
13
|
+
"astro": "astro",
|
|
14
|
+
"build": "run-s 'build:*'"
|
|
13
15
|
},
|
|
14
16
|
"dependencies": {
|
|
15
|
-
"@astrojs/markdown-satteri": "^0.4.
|
|
16
|
-
"@astrojs/mdx": "^8.1.
|
|
17
|
-
"@astrojs/react": "^7.1.
|
|
18
|
-
"astro": "^7.4.
|
|
19
|
-
"d3-scale": "^4.0.
|
|
17
|
+
"@astrojs/markdown-satteri": "^0.4.7",
|
|
18
|
+
"@astrojs/mdx": "^8.1.3",
|
|
19
|
+
"@astrojs/react": "^7.1.4",
|
|
20
|
+
"astro": "^7.4.7",
|
|
21
|
+
"d3-scale": "^4.0.3",
|
|
20
22
|
"react": "^19.3.1",
|
|
21
23
|
"react-dom": "^19.3.1"
|
|
22
24
|
},
|
|
23
25
|
"devDependencies": {
|
|
26
|
+
"@semantic-release/changelog": "^7.0.0",
|
|
24
27
|
"@shikijs/transformers": "^4.6.0",
|
|
25
28
|
"@types/d3-scale": "^4.0.9",
|
|
26
29
|
"@types/react": "^19.3.0",
|
|
27
|
-
"@types/react-dom": "^19.3.0"
|
|
30
|
+
"@types/react-dom": "^19.3.0",
|
|
31
|
+
"npm-run-all": "^4.1.5",
|
|
32
|
+
"prettier": "^3.10.1",
|
|
33
|
+
"prettier-plugin-astro": "^1.1.2",
|
|
34
|
+
"sass-embedded": "^1.108.2",
|
|
35
|
+
"typedoc": "^0.28.20"
|
|
28
36
|
}
|
|
29
37
|
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getCollection } from "astro:content";
|
|
3
|
+
import TsdComment from "./TSDComment.astro";
|
|
4
|
+
|
|
5
|
+
const { data } = Astro.props;
|
|
6
|
+
const typedoc = await getCollection("typedoc");
|
|
7
|
+
|
|
8
|
+
const expandReferenceType = (name: string, data) => {
|
|
9
|
+
const ref = data.find((d) => d.data.name === name);
|
|
10
|
+
return ref ? ref.data.children : null;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const expanded = data[0].parameters
|
|
14
|
+
? data[0].parameters
|
|
15
|
+
.map((d) => {
|
|
16
|
+
return d.type.type === "reference"
|
|
17
|
+
? expandReferenceType(d.type.name, typedoc)
|
|
18
|
+
: d.type;
|
|
19
|
+
})
|
|
20
|
+
.flat()
|
|
21
|
+
: [];
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
<ul>
|
|
25
|
+
{expanded.map((d) => (
|
|
26
|
+
<>
|
|
27
|
+
<li>
|
|
28
|
+
<span>
|
|
29
|
+
{d?.name}
|
|
30
|
+
{!d?.flags.isOptional && <strong class="required">required</strong>}
|
|
31
|
+
</span>
|
|
32
|
+
<span>
|
|
33
|
+
<code class="value">
|
|
34
|
+
{d?.type.type === "union" ? (
|
|
35
|
+
`${d.type.types.map((t) => `"${t.value}"`).join(" | ")}`
|
|
36
|
+
) : d?.type.type === "array" ? (
|
|
37
|
+
<>{d.type.elementType.name}[]</>
|
|
38
|
+
) : d?.type.type === "reflection" ? (
|
|
39
|
+
<pre>{JSON.stringify(
|
|
40
|
+
Object.fromEntries(
|
|
41
|
+
d.type.declaration.children.map((c) => [
|
|
42
|
+
c.name,
|
|
43
|
+
c.type.name,
|
|
44
|
+
]),
|
|
45
|
+
),
|
|
46
|
+
null,
|
|
47
|
+
" ",
|
|
48
|
+
).replaceAll('"', "")}</pre>
|
|
49
|
+
) : (
|
|
50
|
+
<>{d?.type?.name}</>
|
|
51
|
+
)}
|
|
52
|
+
</code>
|
|
53
|
+
</span>
|
|
54
|
+
{d.comment && <TsdComment comment={d.comment} />}
|
|
55
|
+
</li>
|
|
56
|
+
</>
|
|
57
|
+
))}
|
|
58
|
+
</ul>
|
|
59
|
+
|
|
60
|
+
<style lang="scss">
|
|
61
|
+
ul {
|
|
62
|
+
display: grid;
|
|
63
|
+
grid-template-columns: repeat(2, max-content) 1fr;
|
|
64
|
+
margin: 0;
|
|
65
|
+
column-gap: 2rem;
|
|
66
|
+
margin-bottom: 1rem;
|
|
67
|
+
width: 100%;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
li {
|
|
71
|
+
display: grid;
|
|
72
|
+
grid-template-columns: subgrid;
|
|
73
|
+
border-bottom: 1px dotted currentColor;
|
|
74
|
+
grid-column: 1 / 4;
|
|
75
|
+
align-items: baseline;
|
|
76
|
+
&:last-child {
|
|
77
|
+
border-bottom: 0;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
.required {
|
|
81
|
+
margin-left: 0.5ch;
|
|
82
|
+
background: #f2d9d9;
|
|
83
|
+
font-family: monospace;
|
|
84
|
+
padding: 0.1em 0.3em;
|
|
85
|
+
padding-bottom: 0;
|
|
86
|
+
font-size: 0.9em;
|
|
87
|
+
font-weight: normal;
|
|
88
|
+
text-transform: uppercase;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.value {
|
|
92
|
+
display: block;
|
|
93
|
+
hyphens: none;
|
|
94
|
+
max-width: 32ch;
|
|
95
|
+
overflow-wrap: break-word;
|
|
96
|
+
}
|
|
97
|
+
</style>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
const { comment } = Astro.props;
|
|
3
|
+
|
|
4
|
+
const defaultValue = comment.blockTags?.find((c) => c.tag === "@defaultValue");
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<span>
|
|
8
|
+
{comment.summary.map((part) =>
|
|
9
|
+
part.kind === "text" ? (
|
|
10
|
+
<>{part.text}</>
|
|
11
|
+
) : part.kind === "code" ? (
|
|
12
|
+
<code>{part.text.replaceAll("`", "")}</code>
|
|
13
|
+
) : part.tag === "@link" ? (
|
|
14
|
+
<a href={part.target}>{part.text}</a>
|
|
15
|
+
) : (
|
|
16
|
+
<></>
|
|
17
|
+
),
|
|
18
|
+
)}
|
|
19
|
+
{defaultValue && (
|
|
20
|
+
<>
|
|
21
|
+
{" "}
|
|
22
|
+
Defaults to{" "}
|
|
23
|
+
<code>
|
|
24
|
+
{defaultValue.content[0].text
|
|
25
|
+
.replaceAll("```", "")
|
|
26
|
+
.replace("ts", "")
|
|
27
|
+
.trim()}
|
|
28
|
+
</code>
|
|
29
|
+
.
|
|
30
|
+
</>
|
|
31
|
+
)}
|
|
32
|
+
</span>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
## Background
|
|
2
|
+
|
|
3
|
+
ReactCake provides useful context for drawing data-driven charts, like a shared coordinate system, data extents and margins. Components that draw actual chart elements, like bars and lines, live in your project or your own component library; ReactCake is completely agnostic to what you render with it.
|
|
4
|
+
|
|
5
|
+
It provides sensible defaults and infers as much information as possible from your data, but also exposes fine-grained props to configure things yourself. Its primary use case are custom news graphics.
|
|
6
|
+
|
|
7
|
+
ReactCake is built on [D3](https://d3js.org/what-is-d3).
|
|
8
|
+
|
|
9
|
+
### Relationship to Layercake
|
|
10
|
+
|
|
11
|
+
This project is an attempt to carefully translate the overall approach and development ergonomics of the Layercake framework from Svelte to React. It does not aim for feature parity or identical API surfaces.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Prior work
|
|
15
|
+
|
|
16
|
+
- Rich Harris (2020): *[A new technique for making responsive, JavaScript-free charts](https://dev.to/richharris/a-new-technique-for-making-responsive-javascript-free-charts-gmp)*
|
|
17
|
+
- Michael Keller + contributors: *[Layercake](https://layercake.graphics/)*
|
|
18
|
+
- Rich Harris (2020): *[pancake](https://github.com/Rich-Harris/pancake)*
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
### Installation
|
|
2
|
+
|
|
3
|
+
```bash
|
|
4
|
+
npm install --save @awesomephant/reactcake
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
### Usage
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { ReactCake, Html, Svg } from "@awesomephant/reactcake";
|
|
11
|
+
import { scaleLinear, scaleBand } from "d3-scale";
|
|
12
|
+
|
|
13
|
+
// Chart components live in your project
|
|
14
|
+
import { xScale, yScale, Bars } from "./lib";
|
|
15
|
+
|
|
16
|
+
const data = [
|
|
17
|
+
{ year: 2024, value: 32 },
|
|
18
|
+
{ year: 2025, value: 15 },
|
|
19
|
+
{ year: 2026, value: 52 },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
// Use any d3 scale
|
|
23
|
+
const xScale = scaleBand();
|
|
24
|
+
const yScale = scaleLinear();
|
|
25
|
+
|
|
26
|
+
// Build charts declaratively
|
|
27
|
+
return (
|
|
28
|
+
<ReactCake data={data} x="year" y="value" xScale={xScale} yScale={yScale}>
|
|
29
|
+
<Html>
|
|
30
|
+
<AxisX />
|
|
31
|
+
<AxisY />
|
|
32
|
+
</Html>
|
|
33
|
+
<Svg>
|
|
34
|
+
<Bars />
|
|
35
|
+
</Svg>
|
|
36
|
+
</ReactCake>
|
|
37
|
+
);
|
|
38
|
+
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { defineCollection } from "astro:content";
|
|
2
|
+
import { file } from "astro/loaders";
|
|
3
|
+
|
|
4
|
+
const typedoc = defineCollection({
|
|
5
|
+
loader: file("./src/content/typedoc.json", { parser: (s) => JSON.parse(s).children }),
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export const collections = { typedoc };
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
.tick__mark {
|
|
12
|
-
border-left: 1px solid
|
|
12
|
+
border-left: 1px solid currentColor;
|
|
13
13
|
position: absolute;
|
|
14
14
|
transform: translateX(-50%);
|
|
15
15
|
}
|
|
@@ -17,20 +17,19 @@
|
|
|
17
17
|
.gridline {
|
|
18
18
|
bottom: 0;
|
|
19
19
|
position: absolute;
|
|
20
|
-
border-left: 1px dotted
|
|
20
|
+
border-left: 1px dotted currentColor;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
.baseline {
|
|
25
25
|
position: absolute;
|
|
26
|
-
border-bottom: 1px solid
|
|
26
|
+
border-bottom: 1px solid currentColor;
|
|
27
27
|
width: 100%;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
.tick__text {
|
|
31
31
|
display: block;
|
|
32
32
|
font-size: var(--ms--1);
|
|
33
|
-
color: var(--blue-50);
|
|
34
33
|
position: relative;
|
|
35
34
|
white-space: nowrap;
|
|
36
35
|
}
|
|
@@ -11,6 +11,7 @@ interface AxisYProps {
|
|
|
11
11
|
grid?: boolean;
|
|
12
12
|
showTicks?: boolean;
|
|
13
13
|
snapLabels?: boolean;
|
|
14
|
+
tickGutter: number;
|
|
14
15
|
tickLength?: number;
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -23,7 +24,7 @@ const AxisY = ({
|
|
|
23
24
|
tickGutter = 3,
|
|
24
25
|
format = (d) => String(d),
|
|
25
26
|
}: AxisYProps) => {
|
|
26
|
-
const { yScale
|
|
27
|
+
const { yScale } = useReactCake();
|
|
27
28
|
const isBandwidth = typeof yScale.bandwidth === "function";
|
|
28
29
|
|
|
29
30
|
const tickVals = Array.isArray(ticks)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
.tick {
|
|
8
8
|
position: absolute;
|
|
9
|
-
border-top: 1px solid
|
|
9
|
+
border-top: 1px solid currentColor;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
|
|
@@ -14,20 +14,18 @@
|
|
|
14
14
|
left: 0;
|
|
15
15
|
right: 0;
|
|
16
16
|
position: absolute;
|
|
17
|
-
border-top: 1px dotted
|
|
17
|
+
border-top: 1px dotted currentColor;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
21
20
|
.baseline {
|
|
22
21
|
position: absolute;
|
|
23
|
-
border-left: 1px solid
|
|
22
|
+
border-left: 1px solid currentColor;
|
|
24
23
|
height: 100%;
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
.text {
|
|
28
27
|
display: block;
|
|
29
28
|
font-size: var(--ms--1);
|
|
30
|
-
color: var(--blue-50);
|
|
31
29
|
white-space: nowrap;
|
|
32
30
|
transform: translateY(-50%);
|
|
33
31
|
}
|
|
@@ -2,6 +2,7 @@ import { useReactCake } from "../../../lib";
|
|
|
2
2
|
|
|
3
3
|
export default function DebugPadding() {
|
|
4
4
|
const { padding } = useReactCake();
|
|
5
|
+
const fill = "rgba(125,125,105,.1)";
|
|
5
6
|
return (
|
|
6
7
|
<div
|
|
7
8
|
style={{
|
|
@@ -10,7 +11,7 @@ export default function DebugPadding() {
|
|
|
10
11
|
right: 0,
|
|
11
12
|
top: 0,
|
|
12
13
|
bottom: 0,
|
|
13
|
-
boxShadow: `${padding.right}px 0
|
|
14
|
+
boxShadow: `${padding.right}px 0 ${fill}, 0 ${padding.bottom}px ${fill}, 0 ${-padding.top}px ${fill}, -${padding.left}px 0 ${fill}`,
|
|
14
15
|
}}
|
|
15
16
|
></div>
|
|
16
17
|
);
|
|
@@ -1,82 +1,109 @@
|
|
|
1
1
|
---
|
|
2
|
-
import "
|
|
2
|
+
import { Font } from "astro:assets";
|
|
3
|
+
import "./code.scss";
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
<!doctype html>
|
|
6
7
|
<html lang="en">
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
<head>
|
|
9
|
+
<meta charset="utf-8" />
|
|
10
|
+
<meta name="viewport" content="width=device-width" />
|
|
11
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
12
|
+
<meta name="generator" content={Astro.generator} />
|
|
13
|
+
<title>ReactCake</title>
|
|
14
|
+
<Font cssVariable="--font-areal" />
|
|
15
|
+
<Font cssVariable="--font-areal-mono" />
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<slot />
|
|
19
|
+
</body>
|
|
19
20
|
</html>
|
|
20
21
|
|
|
21
22
|
<style is:global>
|
|
22
23
|
* {
|
|
23
24
|
box-sizing: border-box;
|
|
25
|
+
font-size: inherit;
|
|
24
26
|
margin: 0;
|
|
25
27
|
padding: 0;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
30
|
+
body {
|
|
31
|
+
margin: 0;
|
|
32
|
+
width: 100%;
|
|
33
|
+
padding: 0.5rem;
|
|
34
|
+
font-family: var(--font-areal), sans-serif;
|
|
35
|
+
--blue-70: hsl(20, 80%, 20%);
|
|
36
|
+
--blue-50: hsl(20, 80%, 26%);
|
|
37
|
+
--blue-30: hsl(20, 80%, 76%);
|
|
38
|
+
--blue-20: hsl(20, 80%, 92%);
|
|
39
|
+
--blue-10: hsl(20, 80%, 97%);
|
|
40
|
+
--ms--1: 0.8rem padding: 1rem;
|
|
41
|
+
background: #d8d2f6;
|
|
42
|
+
line-height: 1.25;
|
|
43
|
+
color: var(--blue-50);
|
|
44
|
+
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
a,
|
|
47
|
+
a:visited {
|
|
48
|
+
color: var(--blue-50);
|
|
49
|
+
background: #f0f0f0;
|
|
50
|
+
text-decoration: none;
|
|
51
|
+
border-bottom: 1px dotted currentColor;
|
|
52
|
+
}
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
code {
|
|
55
|
+
font-family: var(--font-areal-mono);
|
|
56
|
+
font-size: 0.825rem;
|
|
57
|
+
}
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
p,
|
|
60
|
+
li {
|
|
61
|
+
letter-spacing: 0.002em;
|
|
62
|
+
}
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
margin-bottom: 0.1rem;
|
|
68
|
-
font-weight: 600;
|
|
69
|
-
}
|
|
64
|
+
h1 {
|
|
65
|
+
font-weight: 900;
|
|
66
|
+
font-size: 2.75em;
|
|
67
|
+
transform: scaleX(0.8);
|
|
68
|
+
transform-origin: left;
|
|
69
|
+
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
h2 {
|
|
72
|
+
transform: scaleX(0.8) skew(-10deg);
|
|
73
|
+
transform-origin: left;
|
|
74
|
+
font-size: 1.85rem;
|
|
75
|
+
margin-top: 1rem;
|
|
76
|
+
margin-bottom: 0.25rem;
|
|
77
|
+
&:first-child {
|
|
78
|
+
margin-top: 0;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
h3 {
|
|
82
|
+
margin-bottom: 0.1rem;
|
|
83
|
+
font-weight: 600;
|
|
84
|
+
font-size: 1.2em;
|
|
85
|
+
}
|
|
86
|
+
h4 {
|
|
87
|
+
font-weight: 400;
|
|
88
|
+
font-size: 1.1rem;
|
|
89
|
+
margin-bottom: 0.25rem;
|
|
90
|
+
code {
|
|
91
|
+
font-size: 0.85em;
|
|
92
|
+
font-weight: 800;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
77
95
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
96
|
+
p,
|
|
97
|
+
ul,
|
|
98
|
+
ol {
|
|
99
|
+
margin-bottom: 0.75em;
|
|
100
|
+
&:last-child {
|
|
101
|
+
margin-bottom: 0;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
81
104
|
|
|
105
|
+
ul,
|
|
106
|
+
ol {
|
|
107
|
+
margin-left: 1em;
|
|
108
|
+
}
|
|
82
109
|
</style>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
.astro-code {
|
|
2
|
+
--astro-code-background: white;
|
|
3
|
+
--astro-code-token-comment: #666;
|
|
4
|
+
font-family: var(--font-areal-mono);
|
|
5
|
+
border: 1px solid currentColor;
|
|
6
|
+
margin: 1em 0;
|
|
7
|
+
margin-top: 0.5em;
|
|
8
|
+
padding: 0.5em;
|
|
9
|
+
line-height: 0.95;
|
|
10
|
+
&:last-child {
|
|
11
|
+
margin-bottom: 0;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
pre.astro-code .indent {
|
|
16
|
+
display: inline-block;
|
|
17
|
+
position: relative;
|
|
18
|
+
left: var(--indent-offset);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
pre.astro-code .indent:empty {
|
|
22
|
+
height: 1lh;
|
|
23
|
+
vertical-align: bottom;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
pre.astro-code .indent::before {
|
|
27
|
+
content: "";
|
|
28
|
+
position: absolute;
|
|
29
|
+
opacity: 0.5;
|
|
30
|
+
width: 1px;
|
|
31
|
+
height: 100%;
|
|
32
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getCollection } from "astro:content";
|
|
3
|
+
|
|
4
|
+
import Base from "../layouts/Base.astro";
|
|
5
|
+
import { Content as Installation } from "../content/installation.md";
|
|
6
|
+
import { Content as Background } from "../content/background.md";
|
|
7
|
+
import Example from "../components/Example.astro";
|
|
8
|
+
import PropList from "../components/PropList.astro";
|
|
9
|
+
import ColumnChart from "../examples/ColumnChart.tsx";
|
|
10
|
+
|
|
11
|
+
const typedoc = await getCollection("typedoc");
|
|
12
|
+
|
|
13
|
+
const components = ["Reactcake", "Html", "Svg", "ScaledSvg"];
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<Base>
|
|
17
|
+
<article class="home">
|
|
18
|
+
<header>
|
|
19
|
+
<div>
|
|
20
|
+
<h1>ReactCake</h1>
|
|
21
|
+
<p>
|
|
22
|
+
A headless chart framework for React inspired by{" "}
|
|
23
|
+
<a href="https://layercake.graphics/">Layercake</a>
|
|
24
|
+
</p>
|
|
25
|
+
<ul>
|
|
26
|
+
<li>
|
|
27
|
+
<a href="https://github.com/awesomephant/reactcake">Github</a>
|
|
28
|
+
</li>
|
|
29
|
+
<li>
|
|
30
|
+
<a href="https://www.npmjs.com/package/@awesomephant/reactcake">
|
|
31
|
+
npm
|
|
32
|
+
</a>
|
|
33
|
+
</li>
|
|
34
|
+
</ul>
|
|
35
|
+
</div>
|
|
36
|
+
<Example height="12rem">
|
|
37
|
+
<ColumnChart />
|
|
38
|
+
</Example>
|
|
39
|
+
</header>
|
|
40
|
+
|
|
41
|
+
<section class="installation">
|
|
42
|
+
<h2>Quickstart</h2>
|
|
43
|
+
<Installation />
|
|
44
|
+
</section>
|
|
45
|
+
|
|
46
|
+
<section class="docs">
|
|
47
|
+
<h2>Docs</h2>
|
|
48
|
+
<h3>Components</h3>
|
|
49
|
+
{typedoc
|
|
50
|
+
.filter((d) => d.data.kind === 64 && !d.data.name.startsWith("use"))
|
|
51
|
+
.sort(
|
|
52
|
+
(a, b) =>
|
|
53
|
+
components.indexOf(a.data.name) - components.indexOf(b.data.name),
|
|
54
|
+
)
|
|
55
|
+
.map((d) => {
|
|
56
|
+
return (
|
|
57
|
+
<>
|
|
58
|
+
<h4>
|
|
59
|
+
<code>
|
|
60
|
+
{`<`}
|
|
61
|
+
{d.data.name}
|
|
62
|
+
{`/>`}
|
|
63
|
+
</code>
|
|
64
|
+
</h4>
|
|
65
|
+
{d.data.signatures[0].comment && (
|
|
66
|
+
<p>{d.data.signatures[0].comment.summary[0].text}</p>
|
|
67
|
+
)}
|
|
68
|
+
<PropList data={d.data.signatures} />
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
})}
|
|
72
|
+
<h3>Hooks</h3>
|
|
73
|
+
{typedoc
|
|
74
|
+
.filter((d) => d.data.kind === 64 && d.data.name.startsWith("use"))
|
|
75
|
+
.map((d) => {
|
|
76
|
+
return (
|
|
77
|
+
<>
|
|
78
|
+
<h3>
|
|
79
|
+
<code>
|
|
80
|
+
{d.data.name}
|
|
81
|
+
{`()`}
|
|
82
|
+
</code>
|
|
83
|
+
</h3>
|
|
84
|
+
{d.data.signatures[0].comment && (
|
|
85
|
+
<p>{d.data.signatures[0].comment.summary[0].text}</p>
|
|
86
|
+
)}
|
|
87
|
+
<PropList data={d.data.signatures} />
|
|
88
|
+
</>
|
|
89
|
+
);
|
|
90
|
+
})}
|
|
91
|
+
<h3>Utils</h3>
|
|
92
|
+
</section>
|
|
93
|
+
|
|
94
|
+
<section class="background">
|
|
95
|
+
<Background />
|
|
96
|
+
</section>
|
|
97
|
+
</article>
|
|
98
|
+
</Base>
|
|
99
|
+
|
|
100
|
+
<style lang="scss">
|
|
101
|
+
.home {
|
|
102
|
+
display: grid;
|
|
103
|
+
grid-template-columns: repeat(12, 1fr);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
header,
|
|
107
|
+
section {
|
|
108
|
+
padding: 1rem;
|
|
109
|
+
border: 1px solid var(--blue-50);
|
|
110
|
+
background-color: white;
|
|
111
|
+
margin-bottom: -1px;
|
|
112
|
+
margin-right: -1px;
|
|
113
|
+
}
|
|
114
|
+
header {
|
|
115
|
+
border-top: 1px solid var(--blue-50);
|
|
116
|
+
border-left: 1px solid var(--blue-50);
|
|
117
|
+
grid-column: span 2;
|
|
118
|
+
display: flex;
|
|
119
|
+
flex-flow: column;
|
|
120
|
+
justify-content: space-between;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.installation {
|
|
124
|
+
border-top: 1px solid var(--blue-50);
|
|
125
|
+
grid-column: 3 / 8;
|
|
126
|
+
}
|
|
127
|
+
.docs {
|
|
128
|
+
border-top: 1px solid var(--blue-50);
|
|
129
|
+
grid-column: 8 / 13;
|
|
130
|
+
grid-row: span 3;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.background {
|
|
134
|
+
grid-column: 3 / 8;
|
|
135
|
+
border-left: 1px solid var(--blue-50);
|
|
136
|
+
}
|
|
137
|
+
</style>
|
package/tsconfig.json
CHANGED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
.astro-code {
|
|
2
|
-
--astro-code-background: hsl(51, 62.5%, 93.7%);
|
|
3
|
-
--astro-code-token-comment: #666;
|
|
4
|
-
margin: 1em 0;
|
|
5
|
-
margin-top: .5em;
|
|
6
|
-
padding: .5em;
|
|
7
|
-
font-size: 12px;
|
|
8
|
-
line-height: 1.25;
|
|
9
|
-
font-family: monospace;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
pre.astro-code .indent {
|
|
14
|
-
display: inline-block;
|
|
15
|
-
position: relative;
|
|
16
|
-
left: var(--indent-offset);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
pre.astro-code .indent:empty {
|
|
20
|
-
height: 1lh;
|
|
21
|
-
vertical-align: bottom;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
pre.astro-code .indent::before {
|
|
25
|
-
content: '';
|
|
26
|
-
position: absolute;
|
|
27
|
-
opacity: 0.5;
|
|
28
|
-
width: 1px;
|
|
29
|
-
height: 100%;
|
|
30
|
-
background-color: red;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
pre.astro-code .line-number {
|
|
34
|
-
user-select: none;
|
|
35
|
-
color: #666;
|
|
36
|
-
margin-right: 2ch;
|
|
37
|
-
}
|
package/site/src/pages/index.mdx
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
layout: "../layouts/Base.astro"
|
|
3
|
-
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
import Example from "../components/Example.astro"
|
|
7
|
-
import ColumnChart from "../examples/ColumnChart.tsx"
|
|
8
|
-
|
|
9
|
-
# ReactCake
|
|
10
|
-
|
|
11
|
-
A headless chart framework for React inspired by [Layercake](https://layercake.graphics/).
|
|
12
|
-
|
|
13
|
-
<Example height="12rem" width="20rem">
|
|
14
|
-
<ColumnChart></ColumnChart>
|
|
15
|
-
</Example>
|
|
16
|
-
|
|
17
|
-
## Installation
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm install --save @awesomephant/reactcake
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Usage
|
|
24
|
-
|
|
25
|
-
```tsx
|
|
26
|
-
import { ReactCake, Html, Svg } from "@awesomephant/reactcake";
|
|
27
|
-
import { scaleLinear, scaleBand } from "d3-scale"
|
|
28
|
-
|
|
29
|
-
// Chart components live in your project
|
|
30
|
-
import { xScale, yScale, Bars } from "./lib";
|
|
31
|
-
|
|
32
|
-
const data = [
|
|
33
|
-
{year: 2024, value: 32},
|
|
34
|
-
{year: 2025, value: 15},
|
|
35
|
-
{year: 2026, value: 52}
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
// Use any d3 scale
|
|
39
|
-
const xScale = scaleTime();
|
|
40
|
-
const xScale = scaleBand();
|
|
41
|
-
|
|
42
|
-
// Build charts declaratively
|
|
43
|
-
return (
|
|
44
|
-
<ReactCake data={data} x="year" y="value" xScale={xScale} yScale={yScale}>
|
|
45
|
-
<Html>
|
|
46
|
-
<AxisX />
|
|
47
|
-
<AxisY />
|
|
48
|
-
</Html>
|
|
49
|
-
<Svg>
|
|
50
|
-
<Bars/>
|
|
51
|
-
</Svg>
|
|
52
|
-
</ReactCake>
|
|
53
|
-
);
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Concepts
|
|
57
|
-
|
|
58
|
-
ReactCake provides useful context for drawing data-driven charts, like a coordinate system, data extents and shared margins. Components that draw actual chart elements, like bars and lines, live in your project (or maybe a local component library), ReactCake is completely agnostic to what you render with it.
|
|
59
|
-
|
|
60
|
-
It provides sensible defaults and infers as much information as possible from your data, but also exposes fine-grained props to configure things yourself.
|
|
61
|
-
|
|
62
|
-
ReactCake is built on [D3](https://d3js.org/what-is-d3).
|
|
63
|
-
|
|
64
|
-
### Relationship to Layercake
|
|
65
|
-
|
|
66
|
-
This project is an attempt to carefully translate the overall approach and development ergonomics of the Layercake framework from Svelte to React. It does not aim for feature parity or identical API surfaces.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
### Prior work
|
|
70
|
-
|
|
71
|
-
- [A new technique for making responsive, JavaScript-free charts](https://dev.to/richharris/a-new-technique-for-making-responsive-javascript-free-charts-gmp)
|
|
72
|
-
- [Layercake](https://layercake.graphics/)
|
|
73
|
-
- [pancake](https://github.com/Rich-Harris/pancake)
|