@awesomephant/reactcake 1.1.1 → 1.2.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 +13 -2
- package/lib/ReactCake/ReactCake.test.tsx +0 -0
- package/lib/ReactCake/index.tsx +26 -9
- package/lib/ScaledSvg/index.tsx +17 -3
- package/lib/Svg/index.tsx +5 -2
- package/lib/index.ts +15 -5
- package/lib/package.json +1 -1
- package/lib/types.ts +20 -4
- package/package.json +18 -9
- package/site/astro.config.mjs +13 -2
- package/site/package.json +14 -8
- package/site/src/assets/ABCArealVariable.woff2 +0 -0
- package/site/src/components/Example.astro +1 -2
- package/site/src/components/PropList.astro +55 -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 +35 -19
- package/site/src/layouts/{code.css → code.scss} +8 -5
- package/site/src/pages/index.astro +108 -0
- package/tsconfig.json +1 -1
- 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
|
@@ -6,12 +6,22 @@ interface HtmlProps extends LayoutProps {
|
|
|
6
6
|
children?: ReactNode;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
const Html = ({
|
|
9
|
+
const Html = ({
|
|
10
|
+
overflow = "visible",
|
|
11
|
+
zIndex,
|
|
12
|
+
label,
|
|
13
|
+
labelledBy,
|
|
14
|
+
describedBy,
|
|
15
|
+
children,
|
|
16
|
+
}: HtmlProps) => {
|
|
10
17
|
const { padding } = useReactCake();
|
|
11
18
|
|
|
12
19
|
return (
|
|
13
20
|
<div
|
|
14
21
|
className="reactcake-html"
|
|
22
|
+
aria-label={labelledBy}
|
|
23
|
+
aria-describedby={describedBy}
|
|
24
|
+
role={label ? "figure" : "none"}
|
|
15
25
|
style={{
|
|
16
26
|
position: "absolute",
|
|
17
27
|
top: `${padding.top}px`,
|
|
@@ -19,6 +29,7 @@ const Html = ({ overflow = "visible", children }: HtmlProps) => {
|
|
|
19
29
|
right: `${padding.right}px`,
|
|
20
30
|
bottom: `${padding.bottom}px`,
|
|
21
31
|
overflow,
|
|
32
|
+
zIndex,
|
|
22
33
|
}}
|
|
23
34
|
>
|
|
24
35
|
{children}
|
|
@@ -26,4 +37,4 @@ const Html = ({ overflow = "visible", children }: HtmlProps) => {
|
|
|
26
37
|
);
|
|
27
38
|
};
|
|
28
39
|
|
|
29
|
-
export
|
|
40
|
+
export { Html, type HtmlProps };
|
|
File without changes
|
package/lib/ReactCake/index.tsx
CHANGED
|
@@ -2,26 +2,43 @@ 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 { Padding, 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
|
-
|
|
12
|
+
data: object[];
|
|
13
|
+
/**
|
|
14
|
+
* x accs comment
|
|
15
|
+
*/
|
|
16
|
+
x?: Accessor;
|
|
17
|
+
x2?: Accessor;
|
|
18
|
+
y?: Accessor;
|
|
19
|
+
z?: Accessor;
|
|
20
|
+
flatData?: object[];
|
|
15
21
|
padding?: Padding;
|
|
16
22
|
xScale?: ScaleLike;
|
|
17
|
-
|
|
23
|
+
xRange?: RangeLike;
|
|
24
|
+
xDomain?: DomainLike;
|
|
25
|
+
x2Scale?: ScaleLike;
|
|
18
26
|
yScale?: ScaleLike;
|
|
27
|
+
yRange?: RangeLike;
|
|
28
|
+
yDomain?: DomainLike;
|
|
19
29
|
zScale?: ScaleLike;
|
|
30
|
+
zRange?: RangeLike;
|
|
31
|
+
zDomain?: DomainLike;
|
|
20
32
|
style?: CSSProperties;
|
|
21
33
|
percentRange?: boolean;
|
|
22
34
|
children?: ReactNode;
|
|
23
35
|
}
|
|
24
36
|
|
|
37
|
+
/**
|
|
38
|
+
* This is a comment
|
|
39
|
+
* @param param0
|
|
40
|
+
* @returns
|
|
41
|
+
*/
|
|
25
42
|
const ReactCake = ({
|
|
26
43
|
data = [],
|
|
27
44
|
flatData,
|
|
@@ -50,7 +67,7 @@ const ReactCake = ({
|
|
|
50
67
|
const _x = typeof x === "string" ? (d) => d[x] : x;
|
|
51
68
|
const _x2 = typeof x2 === "string" ? (d) => d[x2] : x2;
|
|
52
69
|
const _y = typeof y === "string" ? (d) => d[y] : y;
|
|
53
|
-
const _z = typeof
|
|
70
|
+
const _z = typeof z === "string" ? (d) => d[z] : z;
|
|
54
71
|
|
|
55
72
|
const xIsBandscale = xScale && typeof xScale.bandwidth === "function";
|
|
56
73
|
const yIsBandscale = yScale && typeof yScale.bandwidth === "function";
|
|
@@ -123,4 +140,4 @@ function useReactCake() {
|
|
|
123
140
|
return context;
|
|
124
141
|
}
|
|
125
142
|
|
|
126
|
-
export { ReactCake, useReactCake };
|
|
143
|
+
export { ReactCake, useReactCake, type ReactCakeProps };
|
package/lib/ScaledSvg/index.tsx
CHANGED
|
@@ -2,17 +2,29 @@ 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
|
+
title?: string;
|
|
6
7
|
children?: ReactNode;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
const ScaledSvg = ({
|
|
10
|
+
const ScaledSvg = ({
|
|
11
|
+
children,
|
|
12
|
+
label,
|
|
13
|
+
labelledBy,
|
|
14
|
+
describedBy,
|
|
15
|
+
title,
|
|
16
|
+
overflow = "visible",
|
|
17
|
+
zIndex,
|
|
18
|
+
}: ScaledSvgProps) => {
|
|
10
19
|
const { padding } = useReactCake();
|
|
11
20
|
return (
|
|
12
21
|
<>
|
|
13
22
|
<style>{`svg * { vector-effect: non-scaling-stroke;}`}</style>
|
|
14
23
|
<svg
|
|
15
24
|
className="reactcake-svg"
|
|
25
|
+
aria-label={labelledBy}
|
|
26
|
+
aria-describedby={describedBy}
|
|
27
|
+
role={label ? "figure" : "none"}
|
|
16
28
|
preserveAspectRatio="none"
|
|
17
29
|
height={`calc(100% - ${padding.top}px - ${padding.bottom}px`}
|
|
18
30
|
width={`calc(100% - ${padding.left}px - ${padding.right}px`}
|
|
@@ -22,12 +34,14 @@ const ScaledSvg = ({ children, overflow = "visible" }: SvgProps) => {
|
|
|
22
34
|
top: `${padding.top}px`,
|
|
23
35
|
left: `${padding.left}px`,
|
|
24
36
|
overflow: overflow,
|
|
37
|
+
zIndex,
|
|
25
38
|
}}
|
|
26
39
|
>
|
|
40
|
+
{title && <title>{title}</title>}
|
|
27
41
|
{children}
|
|
28
42
|
</svg>
|
|
29
43
|
</>
|
|
30
44
|
);
|
|
31
45
|
};
|
|
32
46
|
|
|
33
|
-
export
|
|
47
|
+
export { ScaledSvg, type ScaledSvgProps };
|
package/lib/Svg/index.tsx
CHANGED
|
@@ -3,10 +3,11 @@ import { useReactCake } from "../ReactCake";
|
|
|
3
3
|
import { LayoutProps } from "../types";
|
|
4
4
|
|
|
5
5
|
interface SvgProps extends LayoutProps {
|
|
6
|
+
title?: string;
|
|
6
7
|
children?: ReactNode;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
const Svg = ({ children, overflow = "hidden" }: SvgProps) => {
|
|
10
|
+
const Svg = ({ children, title, overflow = "hidden", zIndex }: SvgProps) => {
|
|
10
11
|
const { padding } = useReactCake();
|
|
11
12
|
return (
|
|
12
13
|
<svg
|
|
@@ -22,11 +23,13 @@ const Svg = ({ children, overflow = "hidden" }: SvgProps) => {
|
|
|
22
23
|
bottom: 0,
|
|
23
24
|
right: 0,
|
|
24
25
|
overflow: overflow,
|
|
26
|
+
zIndex,
|
|
25
27
|
}}
|
|
26
28
|
>
|
|
29
|
+
{title && <title>{title}</title>}
|
|
27
30
|
<g transform={`translate(${padding.left} ${padding.top})`}>{children}</g>
|
|
28
31
|
</svg>
|
|
29
32
|
);
|
|
30
33
|
};
|
|
31
34
|
|
|
32
|
-
export
|
|
35
|
+
export { Svg, type SvgProps };
|
package/lib/index.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
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
5
|
|
|
6
|
-
export {
|
|
6
|
+
export {
|
|
7
|
+
Html,
|
|
8
|
+
ReactCake,
|
|
9
|
+
useReactCake,
|
|
10
|
+
Svg,
|
|
11
|
+
ScaledSvg,
|
|
12
|
+
type HtmlProps,
|
|
13
|
+
type ScaledSvgProps,
|
|
14
|
+
type SvgProps,
|
|
15
|
+
type ReactCakeProps,
|
|
16
|
+
};
|
package/lib/package.json
CHANGED
package/lib/types.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
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 DomainLike =
|
|
7
|
+
| [min: number | null, max: number | null]
|
|
8
|
+
| Array<string | number | null>
|
|
9
|
+
| ((domain: Array<any>) => Array<any>)
|
|
10
|
+
| undefined;
|
|
11
|
+
|
|
6
12
|
export type Padding = {
|
|
7
13
|
top?: number;
|
|
8
14
|
left?: number;
|
|
@@ -12,7 +18,7 @@ export type Padding = {
|
|
|
12
18
|
|
|
13
19
|
export interface ScaleLike<T = DatumValue> {
|
|
14
20
|
(value: unknown): number;
|
|
15
|
-
domain: () => T
|
|
21
|
+
domain: (domain: DomainLike) => ScaleLike<T>;
|
|
16
22
|
range: (range: [number, number]) => ScaleLike<T>;
|
|
17
23
|
ticks?: (count?: number) => T[];
|
|
18
24
|
tickFormat?: (count?: number) => TickFormatter<T>;
|
|
@@ -20,7 +26,7 @@ export interface ScaleLike<T = DatumValue> {
|
|
|
20
26
|
invert?: (d: number) => ScaleLike;
|
|
21
27
|
}
|
|
22
28
|
|
|
23
|
-
type Accessor = (
|
|
29
|
+
export type Accessor = string | ((d: unknown, i?: number) => unknown);
|
|
24
30
|
|
|
25
31
|
export interface ContextValue {
|
|
26
32
|
width: number;
|
|
@@ -39,5 +45,15 @@ export interface ContextValue {
|
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
export interface LayoutProps {
|
|
42
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Sets the CSS overflow property
|
|
50
|
+
*/
|
|
51
|
+
overflow?: "hidden" | "visible";
|
|
52
|
+
label?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Sets the aria-labelledby attribute
|
|
55
|
+
*/
|
|
56
|
+
labelledBy?: string;
|
|
57
|
+
describedBy?: string;
|
|
58
|
+
zIndex?: number;
|
|
43
59
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awesomephant/reactcake",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.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,5 +1,5 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
import { defineConfig } from 'astro/config';
|
|
2
|
+
import { defineConfig, fontProviders } from 'astro/config';
|
|
3
3
|
import {transformerRenderIndentGuides, transformerRenderLineNumber} from "@shikijs/transformers"
|
|
4
4
|
|
|
5
5
|
import mdx from '@astrojs/mdx';
|
|
@@ -7,11 +7,22 @@ import mdx from '@astrojs/mdx';
|
|
|
7
7
|
import react from '@astrojs/react';
|
|
8
8
|
|
|
9
9
|
export default defineConfig({
|
|
10
|
+
fonts: [{
|
|
11
|
+
provider: fontProviders.local(),
|
|
12
|
+
name: "Areal",
|
|
13
|
+
cssVariable: "--font-areal",
|
|
14
|
+
options: {
|
|
15
|
+
variants: [{
|
|
16
|
+
weight: "100 900",
|
|
17
|
+
style: "normal",
|
|
18
|
+
src:["./src/assets/ABCArealVariable.woff2"]
|
|
19
|
+
}]
|
|
20
|
+
}
|
|
21
|
+
}],
|
|
10
22
|
markdown: {
|
|
11
23
|
shikiConfig: {
|
|
12
24
|
theme: 'css-variables',
|
|
13
25
|
defaultColor: false,
|
|
14
|
-
transformers:[transformerRenderLineNumber(), transformerRenderIndentGuides()]
|
|
15
26
|
},
|
|
16
27
|
syntaxHighlight: {
|
|
17
28
|
type: 'shiki',
|
package/site/package.json
CHANGED
|
@@ -7,23 +7,29 @@
|
|
|
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
|
+
"sass-embedded": "^1.108.2",
|
|
33
|
+
"typedoc": "^0.28.20"
|
|
28
34
|
}
|
|
29
35
|
}
|
|
Binary file
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getCollection } from "astro:content";
|
|
3
|
+
|
|
4
|
+
const {data} = Astro.props;
|
|
5
|
+
const typedoc = await getCollection('typedoc');
|
|
6
|
+
|
|
7
|
+
const expandReferenceType = (name: string, data) => {
|
|
8
|
+
const ref = data.find(d => d.data.name === name)
|
|
9
|
+
return ref ? ref.data.children : null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const expanded = data[0].parameters ? data[0].parameters.map(d => {
|
|
13
|
+
return d.type.type === "reference"? expandReferenceType(d.type.name, typedoc): d.type
|
|
14
|
+
}).flat(): []
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<ul>
|
|
19
|
+
{expanded.map(d =>
|
|
20
|
+
<>
|
|
21
|
+
<li><span>{d?.name}{!d?.flags.isOptional && <strong class="required">required</strong>}</span><span><code>{d?.type.type === "union"? `${d.type.types.map(t => `"${t.value}"`).join(" | ")}` : d?.type.type ==="array"?<code>{d.type.elementType.name}[]</code> : d?.type?.name}</code></span>{d.comment && <span>{d.comment.summary[0].text}</span>}</li>
|
|
22
|
+
</>
|
|
23
|
+
)}
|
|
24
|
+
</ul>
|
|
25
|
+
|
|
26
|
+
<style lang="scss">
|
|
27
|
+
ul {
|
|
28
|
+
display: grid;
|
|
29
|
+
grid-template-columns: repeat(2, max-content) 1fr;
|
|
30
|
+
margin: 0;
|
|
31
|
+
column-gap: 2rem;
|
|
32
|
+
margin-bottom: 1rem;
|
|
33
|
+
width: 100%;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
li {
|
|
37
|
+
display: grid;
|
|
38
|
+
grid-template-columns: subgrid;
|
|
39
|
+
grid-column: 1 / 4;
|
|
40
|
+
border-bottom: 1px dotted currentColor;
|
|
41
|
+
&:last-child {
|
|
42
|
+
border-bottom: 0;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
.required {
|
|
46
|
+
margin-left: .5ch;
|
|
47
|
+
background: #f2d9d9;
|
|
48
|
+
font-family: monospace;
|
|
49
|
+
padding: .1em .3em;
|
|
50
|
+
padding-bottom: 0;
|
|
51
|
+
font-size: .9em;
|
|
52
|
+
font-weight: normal;
|
|
53
|
+
text-transform: uppercase;
|
|
54
|
+
}
|
|
55
|
+
</style>
|
|
@@ -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,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
import "
|
|
2
|
+
import { Font } from "astro:assets";
|
|
3
|
+
import "./code.scss"
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
<!doctype html>
|
|
@@ -10,11 +11,10 @@ import "./code.css"
|
|
|
10
11
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
11
12
|
<meta name="generator" content={Astro.generator} />
|
|
12
13
|
<title>ReactCake</title>
|
|
14
|
+
<Font cssVariable="--font-areal" />
|
|
13
15
|
</head>
|
|
14
16
|
<body>
|
|
15
|
-
<main class="content">
|
|
16
17
|
<slot />
|
|
17
|
-
</main>
|
|
18
18
|
</body>
|
|
19
19
|
</html>
|
|
20
20
|
|
|
@@ -23,32 +23,33 @@ import "./code.css"
|
|
|
23
23
|
box-sizing: border-box;
|
|
24
24
|
margin: 0;
|
|
25
25
|
padding: 0;
|
|
26
|
+
font-size: inherit;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
body {
|
|
29
30
|
margin: 0;
|
|
30
31
|
width: 100%;
|
|
31
32
|
padding: .5rem;
|
|
32
|
-
font-family:
|
|
33
|
-
--blue-70: hsl(
|
|
34
|
-
--blue-50: hsl(
|
|
35
|
-
--blue-30: hsl(
|
|
36
|
-
--blue-20: hsl(
|
|
37
|
-
--blue-10: hsl(
|
|
33
|
+
font-family: var(--font-areal), sans-serif;
|
|
34
|
+
--blue-70: hsl(20, 80%, 20%);
|
|
35
|
+
--blue-50: hsl(20, 80%, 26%);
|
|
36
|
+
--blue-30: hsl(20, 80%, 76%);
|
|
37
|
+
--blue-20: hsl(20, 80%, 92%);
|
|
38
|
+
--blue-10: hsl(20, 80%, 97%);
|
|
38
39
|
--ms--1: .8rem
|
|
40
|
+
padding: 1rem;
|
|
41
|
+
background: #d8d2f6;
|
|
42
|
+
line-height: 1.25;
|
|
43
|
+
color: var(--blue-50);
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
a, a:visited {
|
|
42
|
-
color: var(--blue-
|
|
47
|
+
color: var(--blue-50);
|
|
48
|
+
background: #f0f0f0;
|
|
49
|
+
text-decoration: none;
|
|
50
|
+
border-bottom: 1px dotted currentColor;
|
|
43
51
|
}
|
|
44
52
|
|
|
45
|
-
.content {
|
|
46
|
-
border: 1px solid var(--blue-70);
|
|
47
|
-
max-width: 40rem;
|
|
48
|
-
line-height: 1.4;
|
|
49
|
-
padding: 1rem;
|
|
50
|
-
border-right: 1px solid var(--blue-70);
|
|
51
|
-
}
|
|
52
53
|
|
|
53
54
|
p, li {
|
|
54
55
|
letter-spacing: .002em;
|
|
@@ -56,20 +57,35 @@ import "./code.css"
|
|
|
56
57
|
|
|
57
58
|
h1 {
|
|
58
59
|
font-weight: 900;
|
|
60
|
+
font-size: 2.75em;
|
|
61
|
+
transform: scaleX(.8);
|
|
62
|
+
transform-origin: left;
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
h2 {
|
|
66
|
+
transform: scaleX(.8);
|
|
67
|
+
transform-origin: left;
|
|
68
|
+
font-size: 1.75rem;
|
|
62
69
|
margin-top: 1rem;
|
|
63
70
|
margin-bottom: .25rem;
|
|
71
|
+
&:first-child {
|
|
72
|
+
margin-top: 0;
|
|
73
|
+
}
|
|
64
74
|
}
|
|
65
75
|
h3 {
|
|
66
|
-
margin-top: 1.25rem;
|
|
67
76
|
margin-bottom: 0.1rem;
|
|
68
77
|
font-weight: 600;
|
|
78
|
+
font-size: 1.2em;
|
|
79
|
+
}
|
|
80
|
+
h4 {
|
|
81
|
+
font-weight: 400;
|
|
82
|
+
font-size: 1rem;
|
|
83
|
+
opacity: .7;
|
|
84
|
+
margin-bottom: 0.1rem;
|
|
69
85
|
}
|
|
70
86
|
|
|
71
87
|
p, ul, ol {
|
|
72
|
-
margin-bottom:
|
|
88
|
+
margin-bottom: .75em;
|
|
73
89
|
&:last-child {
|
|
74
90
|
margin-bottom: 0;
|
|
75
91
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
.astro-code {
|
|
2
|
-
--astro-code-background:
|
|
2
|
+
--astro-code-background: white;
|
|
3
3
|
--astro-code-token-comment: #666;
|
|
4
|
+
border: 1px solid currentColor;
|
|
4
5
|
margin: 1em 0;
|
|
5
6
|
margin-top: .5em;
|
|
6
7
|
padding: .5em;
|
|
7
8
|
font-size: 12px;
|
|
8
|
-
line-height: 1.
|
|
9
|
+
line-height: 1.1;
|
|
9
10
|
font-family: monospace;
|
|
11
|
+
&:last-child {
|
|
12
|
+
margin-bottom: 0;
|
|
13
|
+
}
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
|
|
@@ -27,11 +31,10 @@ pre.astro-code .indent::before {
|
|
|
27
31
|
opacity: 0.5;
|
|
28
32
|
width: 1px;
|
|
29
33
|
height: 100%;
|
|
30
|
-
background-color: red;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
pre.astro-code .line-number {
|
|
34
37
|
user-select: none;
|
|
35
|
-
color:
|
|
36
|
-
margin-right:
|
|
38
|
+
color: rgba(black, .5);
|
|
39
|
+
margin-right: 3ch;
|
|
37
40
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
---
|
|
14
|
+
|
|
15
|
+
<Base>
|
|
16
|
+
|
|
17
|
+
<article class="home">
|
|
18
|
+
|
|
19
|
+
<header>
|
|
20
|
+
<div>
|
|
21
|
+
<h1>ReactCake</h1>
|
|
22
|
+
<p>A headless chart framework for React inspired by <a href="https://layercake.graphics/">Layercake</a></p>
|
|
23
|
+
<ul>
|
|
24
|
+
<li><a href="https://github.com/awesomephant/reactcake">Github</a></li>
|
|
25
|
+
<li><a href="https://www.npmjs.com/package/@awesomephant/reactcake">npm</a></li>
|
|
26
|
+
</ul>
|
|
27
|
+
</div>
|
|
28
|
+
<Example height="12rem" >
|
|
29
|
+
<ColumnChart/>
|
|
30
|
+
</Example>
|
|
31
|
+
</header>
|
|
32
|
+
|
|
33
|
+
<section class="installation">
|
|
34
|
+
<h2>Quickstart</h2>
|
|
35
|
+
<Installation/>
|
|
36
|
+
</section>
|
|
37
|
+
|
|
38
|
+
<section class="docs">
|
|
39
|
+
<h2>Docs</h2>
|
|
40
|
+
<h3>Components</h3>
|
|
41
|
+
{typedoc.filter(d => d.data.kind === 64 && !d.data.name.startsWith("use")).map(d => {
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<h3><code>{`<`}{d.data.name}{`/>`}</code></h3>
|
|
45
|
+
{d.data.signatures[0].comment && <p>{d.data.signatures[0].comment.summary[0].text}</p>}
|
|
46
|
+
<PropList data={d.data.signatures}/>
|
|
47
|
+
</>)
|
|
48
|
+
})}
|
|
49
|
+
<h3>Hooks</h3>
|
|
50
|
+
{typedoc.filter(d => d.data.kind === 64 && d.data.name.startsWith("use")).map(d => {
|
|
51
|
+
return (
|
|
52
|
+
<>
|
|
53
|
+
<h3><code>{d.data.name}{`()`}</code></h3>
|
|
54
|
+
{d.data.signatures[0].comment && <p>{d.data.signatures[0].comment.summary[0].text}</p>}
|
|
55
|
+
<PropList data={d.data.signatures}/>
|
|
56
|
+
</>)
|
|
57
|
+
})}
|
|
58
|
+
<h3>Utils</h3>
|
|
59
|
+
|
|
60
|
+
</section>
|
|
61
|
+
|
|
62
|
+
<section class="background">
|
|
63
|
+
<Background/>
|
|
64
|
+
</section>
|
|
65
|
+
</article>
|
|
66
|
+
|
|
67
|
+
</Base>
|
|
68
|
+
|
|
69
|
+
<style lang="scss">
|
|
70
|
+
.home {
|
|
71
|
+
display: grid;
|
|
72
|
+
grid-template-columns: repeat(12, 1fr);
|
|
73
|
+
hyphens: auto;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
header, section {
|
|
77
|
+
padding: 1rem;
|
|
78
|
+
border: 1px solid var(--blue-50);
|
|
79
|
+
background-color: white;
|
|
80
|
+
margin-bottom: -1px;
|
|
81
|
+
margin-right: -1px;
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
header {
|
|
85
|
+
border-top: 1px solid var(--blue-50);
|
|
86
|
+
border-left: 1px solid var(--blue-50);
|
|
87
|
+
grid-column: span 2;
|
|
88
|
+
display: flex;
|
|
89
|
+
flex-flow: column;
|
|
90
|
+
justify-content: space-between;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.installation {
|
|
94
|
+
border-top: 1px solid var(--blue-50);
|
|
95
|
+
grid-column: 3 / 8;
|
|
96
|
+
}
|
|
97
|
+
.docs {
|
|
98
|
+
border-top: 1px solid var(--blue-50);
|
|
99
|
+
grid-column: 8 / 13;
|
|
100
|
+
grid-row: span 3;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.background {
|
|
104
|
+
grid-column: 3 / 8;
|
|
105
|
+
border-left: 1px solid var(--blue-50);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
</style>
|
package/tsconfig.json
CHANGED
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)
|