@awesomephant/reactcake 1.0.0 → 1.1.1

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/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
- [![release](https://github.com/awesomephant/reactcake/actions/workflows/release.yml/badge.svg)](https://github.com/awesomephant/reactcake/actions/workflows/release.yml)
5
+ ![NPM Version](https://img.shields.io/npm/v/%40awesomephant%2Freactcake) [![release](https://github.com/awesomephant/reactcake/actions/workflows/release.yml/badge.svg)](https://github.com/awesomephant/reactcake/actions/workflows/release.yml)
6
6
 
7
7
  ## Contributing
8
8
 
@@ -1,21 +1,24 @@
1
1
  import { type ReactNode } from "react";
2
+ import { useReactCake } from "../ReactCake";
3
+ import { LayoutProps } from "../types";
2
4
 
3
- interface HtmlProps {
5
+ interface HtmlProps extends LayoutProps {
4
6
  children?: ReactNode;
5
7
  }
6
8
 
7
- const Html = ({ children }: HtmlProps) => {
9
+ const Html = ({ overflow = "visible", children }: HtmlProps) => {
10
+ const { padding } = useReactCake();
11
+
8
12
  return (
9
13
  <div
10
14
  className="reactcake-html"
11
15
  style={{
12
16
  position: "absolute",
13
- height: "100%",
14
- width: "100%",
15
- top: 0,
16
- left: 0,
17
- right: 0,
18
- bottom: 0,
17
+ top: `${padding.top}px`,
18
+ left: `${padding.left}px`,
19
+ right: `${padding.right}px`,
20
+ bottom: `${padding.bottom}px`,
21
+ overflow,
19
22
  }}
20
23
  >
21
24
  {children}
@@ -1,8 +1,8 @@
1
1
  import { type ReactNode } from "react";
2
2
  import { useReactCake } from "../ReactCake";
3
+ import { LayoutProps } from "../types";
3
4
 
4
- interface SvgProps {
5
- overflow?: "visible" | "hidden";
5
+ interface SvgProps extends LayoutProps {
6
6
  children?: ReactNode;
7
7
  }
8
8
 
package/lib/Svg/index.tsx CHANGED
@@ -1,8 +1,8 @@
1
1
  import { type ReactNode } from "react";
2
2
  import { useReactCake } from "../ReactCake";
3
+ import { LayoutProps } from "../types";
3
4
 
4
- interface SvgProps {
5
- overflow?: "visible" | "hidden";
5
+ interface SvgProps extends LayoutProps {
6
6
  children?: ReactNode;
7
7
  }
8
8
 
package/lib/types.ts CHANGED
@@ -37,3 +37,7 @@ export interface ContextValue {
37
37
  zGet?: (d: unknown) => number | string;
38
38
  padding: Padding;
39
39
  }
40
+
41
+ export interface LayoutProps {
42
+ overflow?: "hidden" | "visible"
43
+ }
package/notes.md CHANGED
@@ -1 +1,11 @@
1
1
  - Anything you draw past the edges shows up in the padding, the way it does on the other layouts. (https://layercake.graphics/guide#canvas)
2
+
3
+ from: https://layercake.graphics/guide
4
+
5
+ - it reverses the default range from [0, height] to [height, 0] in order to match the DOM coordinate system and make drawing easier out of the box.
6
+ - The default ranges for each dimension are:
7
+
8
+ x: [0, width]
9
+ y: [height, 0], unless using a scaleBand or scalePoint and it is [0, height]
10
+ r: [1, 25]. The min is 1 and not zero because this is a scaleSqrt by default and 0 is not allowed in that type of chart.
11
+ z: [0, width]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awesomephant/reactcake",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "A headless chart framework for React inspired by Layercake",
5
5
  "keywords": [
6
6
  "react",
@@ -1,5 +1,6 @@
1
1
  // @ts-check
2
2
  import { defineConfig } from 'astro/config';
3
+ import {transformerRenderIndentGuides, transformerRenderLineNumber} from "@shikijs/transformers"
3
4
 
4
5
  import mdx from '@astrojs/mdx';
5
6
 
@@ -7,9 +8,14 @@ import react from '@astrojs/react';
7
8
 
8
9
  export default defineConfig({
9
10
  markdown: {
10
- shikiConfig: {
11
- theme: 'github-light-high-contrast',
12
- }
11
+ shikiConfig: {
12
+ theme: 'css-variables',
13
+ defaultColor: false,
14
+ transformers:[transformerRenderLineNumber(), transformerRenderIndentGuides()]
15
+ },
16
+ syntaxHighlight: {
17
+ type: 'shiki',
18
+ },
13
19
  },
14
20
 
15
21
  integrations: [mdx(), react()]
package/site/package.json CHANGED
@@ -21,6 +21,7 @@
21
21
  "react-dom": "^19.3.1"
22
22
  },
23
23
  "devDependencies": {
24
+ "@shikijs/transformers": "^4.6.0",
24
25
  "@types/d3-scale": "^4.0.9",
25
26
  "@types/react": "^19.3.0",
26
27
  "@types/react-dom": "^19.3.0"
@@ -11,6 +11,7 @@ const {height, width = "100%"} = Astro.props
11
11
  <style>
12
12
  .container {
13
13
  margin: 1rem 0;
14
- border: 1px solid #2b598a;
14
+ border: 1px solid var(--blue-50);
15
+ font-family: sans-serif;
15
16
  }
16
17
  </style>
@@ -0,0 +1,106 @@
1
+ import { Fragment } from "react";
2
+ import { useReactCake } from "../../../../lib";
3
+ import styles from "./styles.module.css";
4
+
5
+ interface AxisProps {
6
+ position?: "top" | "bottom";
7
+ count?: number;
8
+ format?: (d: unknown) => string;
9
+ label?: string;
10
+ ticksValues?: number | DatumValue[] | ((ticks: unknown[]) => unknown[]);
11
+ showBaseline?: boolean;
12
+ grid?: boolean;
13
+ snapLabels?: boolean;
14
+ tickLength?: number;
15
+ }
16
+
17
+ const AxisX = ({
18
+ ticks,
19
+ showBaseline = true,
20
+ position = "bottom",
21
+ grid = false,
22
+ format = (d) => String(d),
23
+ tickLength = 8,
24
+ }: AxisProps) => {
25
+ const { xScale } = useReactCake();
26
+
27
+ if (!xScale) return <></>;
28
+
29
+ const isBandwidth = typeof xScale.bandwidth === "function";
30
+
31
+ const tickVals = Array.isArray(ticks)
32
+ ? ticks
33
+ : isBandwidth
34
+ ? xScale.domain()
35
+ : typeof ticks === "function"
36
+ ? ticks(xScale.ticks())
37
+ : xScale.ticks(ticks);
38
+
39
+ const halfBand = isBandwidth ? xScale.bandwidth() / 2 : 0;
40
+
41
+ const tickEls = tickVals.map((v) => {
42
+ const x = xScale(v);
43
+
44
+ return (
45
+ <Fragment key={`tick-${v.toString()}`}>
46
+ {grid && (
47
+ <div
48
+ className={styles.gridline}
49
+ style={{
50
+ top: 0,
51
+ bottom: 0,
52
+ left: `${x + halfBand}%`,
53
+ }}
54
+ />
55
+ )}
56
+ <div
57
+ className={styles.tick}
58
+ style={{
59
+ left: `${x + halfBand}%`,
60
+ bottom: position === "bottom" ? -tickLength : "auto",
61
+ top: position === "top" ? -tickLength + 1 : "auto",
62
+ height: `${tickLength}px`,
63
+ }}
64
+ >
65
+ <span style={{ height: `${tickLength}px` }} className={styles.tick__mark}></span>
66
+ <span
67
+ className={styles.tick__text}
68
+ style={{
69
+ top: position === "bottom" ? `${tickLength}px` : "auto",
70
+ left: 0,
71
+ bottom: position === "top" ? `${tickLength + 2}px` : "auto",
72
+ transform: `translateX(-50%) ${position === "top" ? "translateY(-50%)" : ""}`,
73
+ }}
74
+ >
75
+ {format(v)}
76
+ </span>
77
+ </div>
78
+ </Fragment>
79
+ );
80
+ });
81
+
82
+ return (
83
+ <div
84
+ className={styles.axis}
85
+ style={{
86
+ bottom: position === "bottom" ? 0 : "auto",
87
+ top: position === "top" ? 0 : "auto",
88
+ }}
89
+ >
90
+ {tickEls}
91
+ {showBaseline && (
92
+ <div
93
+ className={styles.baseline}
94
+ style={{
95
+ bottom: position === "bottom" ? 0 : "auto",
96
+ top: position === "top" ? 0 : "auto",
97
+ left: 0,
98
+ right: 0,
99
+ }}
100
+ ></div>
101
+ )}
102
+ </div>
103
+ );
104
+ };
105
+
106
+ export default AxisX;
@@ -0,0 +1,36 @@
1
+ .axis {
2
+ width: 100%;
3
+ height: 100%;
4
+ position: absolute;
5
+ }
6
+
7
+ .tick {
8
+ position: absolute;
9
+ }
10
+
11
+ .tick__mark {
12
+ border-left: 1px solid var(--blue-50);
13
+ position: absolute;
14
+ transform: translateX(-50%);
15
+ }
16
+
17
+ .gridline {
18
+ bottom: 0;
19
+ position: absolute;
20
+ border-left: 1px dotted var(--blue-50);
21
+ }
22
+
23
+
24
+ .baseline {
25
+ position: absolute;
26
+ border-bottom: 1px solid var(--blue-50);
27
+ width: 100%;
28
+ }
29
+
30
+ .tick__text {
31
+ display: block;
32
+ font-size: var(--ms--1);
33
+ color: var(--blue-50);
34
+ position: relative;
35
+ white-space: nowrap;
36
+ }
@@ -0,0 +1,76 @@
1
+ import { Fragment } from "react";
2
+ import { useReactCake } from "../../../../lib";
3
+ import styles from "./styles.module.css";
4
+
5
+ interface AxisYProps {
6
+ position?: "left" | "right";
7
+ format?: (d: unknown) => string;
8
+ label?: string;
9
+ ticks?: number | DatumValue[] | ((ticks: unknown[]) => unknown[]);
10
+ showBaseline?: boolean;
11
+ grid?: boolean;
12
+ showTicks?: boolean;
13
+ snapLabels?: boolean;
14
+ tickLength?: number;
15
+ }
16
+
17
+ const AxisY = ({
18
+ ticks,
19
+ showBaseline = true,
20
+ grid = true,
21
+ showTicks = true,
22
+ tickLength = 8,
23
+ tickGutter = 3,
24
+ format = (d) => String(d),
25
+ }: AxisYProps) => {
26
+ const { yScale, padding } = useReactCake();
27
+ const isBandwidth = typeof yScale.bandwidth === "function";
28
+
29
+ const tickVals = Array.isArray(ticks)
30
+ ? ticks
31
+ : isBandwidth
32
+ ? yScale.domain()
33
+ : typeof ticks === "function"
34
+ ? ticks(yScale.ticks())
35
+ : yScale.ticks(ticks);
36
+
37
+ const halfBand = isBandwidth ? yScale.bandwidth() / 2 : 0;
38
+
39
+ const tickEls = tickVals.map((v, i) => {
40
+ const y = yScale(v);
41
+ return (
42
+ <Fragment key={`tick-${i}`}>
43
+ {grid && y + halfBand > 0 && (
44
+ <div className={styles.gridline} style={{ bottom: `${y + halfBand}%`, left: 0 }}></div>
45
+ )}
46
+ {format(v) !== "" && (
47
+ <div
48
+ className={styles.tick}
49
+ style={{
50
+ width: `${tickLength}px`,
51
+ bottom: `${y + halfBand}%`,
52
+ left: -tickLength,
53
+ }}
54
+ >
55
+ {showTicks && <span className={styles.mark}></span>}
56
+ <span
57
+ className={styles.text}
58
+ style={{ position: "absolute", right: `calc(100% + ${tickGutter}px)` }}
59
+ >
60
+ {format(v)}
61
+ </span>
62
+ </div>
63
+ )}
64
+ </Fragment>
65
+ );
66
+ });
67
+
68
+ return (
69
+ <div className={styles.axis} style={{ bottom: 0, top: 0 }}>
70
+ {tickEls}
71
+ {showBaseline && <div className={styles.baseline} style={{ left: 0 }}></div>}
72
+ </div>
73
+ );
74
+ };
75
+
76
+ export default AxisY;
@@ -0,0 +1,33 @@
1
+ .axis {
2
+ width: 100%;
3
+ height: 100%;
4
+ position: absolute;
5
+ }
6
+
7
+ .tick {
8
+ position: absolute;
9
+ border-top: 1px solid var(--blue-50);
10
+ }
11
+
12
+
13
+ .gridline {
14
+ left: 0;
15
+ right: 0;
16
+ position: absolute;
17
+ border-top: 1px dotted var(--blue-50);
18
+ }
19
+
20
+
21
+ .baseline {
22
+ position: absolute;
23
+ border-left: 1px solid var(--blue-50);
24
+ height: 100%;
25
+ }
26
+
27
+ .text {
28
+ display: block;
29
+ font-size: var(--ms--1);
30
+ color: var(--blue-50);
31
+ white-space: nowrap;
32
+ transform: translateY(-50%);
33
+ }
@@ -1,26 +1,43 @@
1
1
  import { ReactCake, ScaledSvg, Html } from "../../../lib";
2
+ import AxisX from "./AxisX/AxisX";
3
+ import AxisY from "./AxisY";
2
4
  import Columns from "./Columns";
3
- import { scaleLinear, scaleBand } from "d3-scale"
5
+ import { scaleLinear, scaleBand } from "d3-scale";
6
+ import { max } from "d3-array";
7
+ import DebugPadding from "./DebugPadding";
4
8
 
5
9
  const data = [
6
- {year: 2022, value: 12},
7
- {year: 2023, value: 22},
8
- {year: 2024, value: 32},
9
- {year: 2025, value: 15},
10
- {year: 2026, value: 52}
11
- ]
10
+ { year: 2021, value: 18 },
11
+ { year: 2022, value: 12 },
12
+ { year: 2023, value: 22 },
13
+ { year: 2024, value: 32 },
14
+ { year: 2025, value: 15 },
15
+ { year: 2026, value: 52 },
16
+ ];
12
17
 
13
- const xScale = scaleBand().paddingInner(.1)
18
+ const xScale = scaleBand().paddingInner(0.1).paddingOuter(0.2);
19
+ const yKey = "value";
14
20
 
15
21
  export default function ColumnChart() {
16
22
  return (
17
- <ReactCake data={data} x="year" y="value" yScale={scaleLinear()} xScale={xScale} percentRange={true} yDomain={[0, null]} padding={{ top: 10, left: 10, right: 10, bottom: 10 }}>
18
- <ScaledSvg>
19
- <Columns/>
20
- </ScaledSvg>
21
- <Html>
22
-
23
- </Html>
24
- </ReactCake>
25
- )
23
+ <ReactCake
24
+ data={data}
25
+ x="year"
26
+ y={yKey}
27
+ yScale={scaleLinear()}
28
+ xScale={xScale}
29
+ percentRange={true}
30
+ yDomain={[0, max(data, (d) => d[yKey]) + 5]}
31
+ padding={{ top: 10, left: 35, right: 10, bottom: 30 }}
32
+ >
33
+ <Html overflow="visible">
34
+ <DebugPadding />
35
+ <AxisX grid ticks />
36
+ <AxisY grid ticks={3} />
37
+ </Html>
38
+ <ScaledSvg overflow="hidden">
39
+ <Columns />
40
+ </ScaledSvg>
41
+ </ReactCake>
42
+ );
26
43
  }
@@ -1,30 +1,28 @@
1
- import { useReactCake } from '../../../lib';
1
+ import { useReactCake } from "../../../lib";
2
2
 
3
3
  export default function Columns() {
4
4
  const { data, x, xGet, yGet, xScale } = useReactCake();
5
5
 
6
- console.log(data)
7
-
8
6
  return (
9
7
  <g className="column-group">
10
8
  {data?.map((d) => {
9
+ const valueY = yGet(d);
10
+ const xGot = xGet(d);
11
+ const xPos = Array.isArray(xGot) ? xGot[0] : xGot;
12
+ const colWidth = xScale.bandwidth ? xScale.bandwidth() : 20;
11
13
 
12
- const valueY = yGet(d)
13
- const xGot = xGet(d)
14
- const xPos = Array.isArray(xGot) ? xGot[0] : xGot
15
- const colWidth = xScale.bandwidth ? xScale.bandwidth() : 20
16
-
17
- return <rect
18
- key={x(d)}
19
- x={xPos}
20
- y={100 - valueY}
21
- width={colWidth}
22
- height={valueY}
23
- stroke="#196c9e"
24
- fill="none"
25
- />
14
+ return (
15
+ <rect
16
+ key={x(d)}
17
+ x={xPos}
18
+ y={100 - valueY}
19
+ width={colWidth}
20
+ height={valueY}
21
+ stroke="var(--blue-50)"
22
+ fill="none"
23
+ />
24
+ );
26
25
  })}
27
26
  </g>
28
-
29
- )
27
+ );
30
28
  }
@@ -0,0 +1,17 @@
1
+ import { useReactCake } from "../../../lib";
2
+
3
+ export default function DebugPadding() {
4
+ const { padding } = useReactCake();
5
+ return (
6
+ <div
7
+ style={{
8
+ position: "absolute",
9
+ left: 0,
10
+ right: 0,
11
+ top: 0,
12
+ bottom: 0,
13
+ boxShadow: `${padding.right}px 0 var(--blue-20), 0 ${padding.bottom}px var(--blue-20), 0 ${-padding.top}px var(--blue-20), -${padding.left}px 0 var(--blue-20)`,
14
+ }}
15
+ ></div>
16
+ );
17
+ }
@@ -0,0 +1,39 @@
1
+ import { useContext } from "react";
2
+ import { Context } from "../ReactCake/";
3
+ import { type ContextValue } from "../ReactCake/types";
4
+
5
+ export default function GanttChart({ currentStep = null, highlight, maxDate, maxLegalDate }) {
6
+ const { x2, xGet, yGet, data, xScale, yScale, zGet } = useContext<ContextValue>(Context);
7
+
8
+ const scaledData = data.map((d, i) => {
9
+ const _maxDate =
10
+ d.kind === "construction" && maxLegalDate ? Math.min(maxLegalDate, maxDate) : maxDate;
11
+
12
+ const _x2 = x2(d) > _maxDate ? _maxDate : x2(d);
13
+
14
+ const fromStep = d.from_step || i;
15
+ const isActive = currentStep >= fromStep;
16
+
17
+ return {
18
+ ...d,
19
+ width: isActive ? xScale(_x2) - xGet(d) : 0,
20
+ };
21
+ });
22
+
23
+ return scaledData.map((d, i) => {
24
+ const x1 = xGet(d);
25
+ const y = yGet(d);
26
+
27
+ return (
28
+ <rect
29
+ key={`gantt-${i}`}
30
+ x={x1}
31
+ y={y}
32
+ width={d.width}
33
+ height={yScale.bandwidth()}
34
+ fill={zGet(d) || "lightgray"}
35
+ className={`gantt-block ${currentStep > -1 && highlight[currentStep].includes(i) ? "current" : ""}`}
36
+ ></rect>
37
+ );
38
+ });
39
+ }
@@ -0,0 +1,55 @@
1
+ import { useContext } from "react";
2
+ import { Context } from "../ReactCake/";
3
+ import { type ContextValue } from "../ReactCake/types";
4
+
5
+ interface OrdinalKeyProps {
6
+ shape?: "square" | "circle" | "line";
7
+ capitalise?: boolean;
8
+ format?: ((s: string | number) => string) | Record<string | number, string>;
9
+ align?: "start" | "center" | "end";
10
+ sort?: string[];
11
+ }
12
+
13
+ function cap(s: string) {
14
+ return String(s).replace(/^\w/, (d) => d.toUpperCase());
15
+ }
16
+
17
+ export default function OrdinalKey({
18
+ shape = "square",
19
+ align = "start",
20
+ sort = [],
21
+ format,
22
+ capitalise = true,
23
+ }: OrdinalKeyProps) {
24
+ const { zDomain, zScale } = useContext<ContextValue>(Context);
25
+
26
+ function displayName(val: string | number): string {
27
+ if (format) {
28
+ return typeof format === "function" ? format(val) : format[val] || val;
29
+ }
30
+ return capitalise === true ? cap(String(val)) : val;
31
+ }
32
+
33
+ return (
34
+ <div className="ordinal-key" style={{ justifyContent: align === "end" ? "flex-end" : align }}>
35
+ {zDomain
36
+ .sort((a, b) => sort.indexOf(b.kind) - sort.indexOf(a.kind))
37
+ .map((d) => {
38
+ return (
39
+ <div className="key-item" key={d}>
40
+ <div
41
+ className={`chip chip__${shape}`}
42
+ style={{
43
+ background:
44
+ shape === `line`
45
+ ? `linear-gradient(-45deg, var(--color-pageFill) 40%, ${zScale(d)} 41%, ${zScale(d)} 59%, var(--color-pageFill) 60%)`
46
+ : zScale(d),
47
+ }}
48
+ ></div>
49
+ <p className="name">{displayName(d)}</p>
50
+ </div>
51
+ );
52
+ })}
53
+ </div>
54
+ );
55
+ }
@@ -0,0 +1,47 @@
1
+ .ordinal-key {
2
+ display: flex;
3
+ flex-flow: column;
4
+ color: var(--o3-color-palette-black-80);
5
+ font-family: var(--o3-font-family-metric);
6
+ gap: 0.5em;
7
+ background: var(--o3-color-palette-paper);
8
+ box-shadow: 0 0 10px 10px var(--o3-color-palette-paper);
9
+
10
+ * {
11
+ margin: 0;
12
+ padding: 0;
13
+ }
14
+
15
+ .key-item {
16
+ display: flex;
17
+ gap: 0.1em;
18
+ align-items: center;
19
+ }
20
+
21
+ .chip {
22
+ display: inline-flex;
23
+ position: relative;
24
+ width: 12px;
25
+ height: 12px;
26
+ }
27
+
28
+ .chip__circle {
29
+ border-radius: 50%;
30
+ }
31
+
32
+ .chip__line:after {
33
+ content: "";
34
+ position: absolute;
35
+ border-width: 3px;
36
+ width: 14px;
37
+ transform: rotate(-45deg);
38
+ transform-origin: 14px 5px;
39
+ }
40
+
41
+ .name {
42
+ display: inline-block;
43
+ font-size: var(--o3-font-size-negative-2);
44
+ margin-left: 0.2em;
45
+ line-height: 1;
46
+ }
47
+ }
@@ -1,3 +1,7 @@
1
+ ---
2
+ import "./code.css"
3
+ ---
4
+
1
5
  <!doctype html>
2
6
  <html lang="en">
3
7
  <head>
@@ -24,22 +28,38 @@
24
28
  body {
25
29
  margin: 0;
26
30
  width: 100%;
27
- padding: 2rem 0;
28
- font-family: sans-serif;
31
+ padding: .5rem;
32
+ font-family: Georgia, 'Times New Roman', Times, serif;
33
+ --blue-70: hsl(220, 0%, 20%);
34
+ --blue-50: hsl(220, 0%, 36%);
35
+ --blue-30: hsl(220, 0%, 76%);
36
+ --blue-20: hsl(220, 0%, 92%);
37
+ --blue-10: hsl(220, 0%, 97%);
38
+ --ms--1: .8rem
39
+ }
40
+
41
+ a, a:visited {
42
+ color: var(--blue-70);
29
43
  }
30
44
 
31
45
  .content {
46
+ border: 1px solid var(--blue-70);
32
47
  max-width: 40rem;
33
48
  line-height: 1.4;
34
- margin: 0 auto;
49
+ padding: 1rem;
50
+ border-right: 1px solid var(--blue-70);
35
51
  }
36
52
 
37
53
  p, li {
38
54
  letter-spacing: .002em;
39
55
  }
40
56
 
57
+ h1 {
58
+ font-weight: 900;
59
+ }
60
+
41
61
  h2 {
42
- margin-top: 2rem;
62
+ margin-top: 1rem;
43
63
  margin-bottom: .25rem;
44
64
  }
45
65
  h3 {
@@ -50,20 +70,13 @@
50
70
 
51
71
  p, ul, ol {
52
72
  margin-bottom: 1rem;
73
+ &:last-child {
74
+ margin-bottom: 0;
75
+ }
53
76
  }
54
77
 
55
78
  ul, ol {
56
79
  margin-left: 1em;
57
80
  }
58
81
 
59
-
60
- .astro-code {
61
- border-top: 1px solid lightgray;
62
- border-bottom: 1px solid lightgray;
63
- margin: 1rem 0;
64
- padding: 1em 0;
65
- line-height: 1.25;
66
- font-family: monospace;
67
- }
68
-
69
82
  </style>
@@ -0,0 +1,37 @@
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
+ }
@@ -17,13 +17,13 @@ A headless chart framework for React inspired by [Layercake](https://layercake.g
17
17
  ## Installation
18
18
 
19
19
  ```bash
20
- npm install --save reactcake
20
+ npm install --save @awesomephant/reactcake
21
21
  ```
22
22
 
23
23
  ## Usage
24
24
 
25
25
  ```tsx
26
- import { ReactCake, Html, Svg } from "reactcake";
26
+ import { ReactCake, Html, Svg } from "@awesomephant/reactcake";
27
27
  import { scaleLinear, scaleBand } from "d3-scale"
28
28
 
29
29
  // Chart components live in your project
@@ -35,11 +35,11 @@ const data = [
35
35
  {year: 2026, value: 52}
36
36
  ];
37
37
 
38
- const xKey = "date";
39
- const yKey = "value";
38
+ // Use any d3 scale
40
39
  const xScale = scaleTime();
41
40
  const xScale = scaleBand();
42
41
 
42
+ // Build charts declaratively
43
43
  return (
44
44
  <ReactCake data={data} x="year" y="value" xScale={xScale} yScale={yScale}>
45
45
  <Html>
@@ -63,7 +63,7 @@ ReactCake is built on [D3](https://d3js.org/what-is-d3).
63
63
 
64
64
  ### Relationship to Layercake
65
65
 
66
- This project is an attempt to 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.
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
67
 
68
68
 
69
69
  ### Prior work
@@ -11,4 +11,4 @@
11
11
  "jsx": "react-jsx",
12
12
  "jsxImportSource": "react"
13
13
  }
14
- }
14
+ }
@@ -1 +0,0 @@
1
- // TKTK