@octane-xplat/charts 0.0.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/macos/Chart.macos.tsrx.js +351 -0
- package/dist/macos/core/axes.js +146 -0
- package/dist/macos/core/hit.js +131 -0
- package/dist/macos/core/marks.js +262 -0
- package/dist/macos/core/scales.js +99 -0
- package/dist/macos/core/scene.js +120 -0
- package/dist/macos/core/svg.js +40 -0
- package/dist/macos/index.js +2 -0
- package/dist/macos/octane-client-build.json +8 -0
- package/dist/macos/svg-image.macos.js +14 -0
- package/dist/native/Chart.tsrx.js +340 -0
- package/dist/native/core/axes.js +146 -0
- package/dist/native/core/hit.js +131 -0
- package/dist/native/core/marks.js +262 -0
- package/dist/native/core/scales.js +99 -0
- package/dist/native/core/scene.js +120 -0
- package/dist/native/core/svg.js +40 -0
- package/dist/native/index.js +2 -0
- package/dist/native/octane-client-build.json +8 -0
- package/dist/web/Chart.web.tsrx.js +488 -0
- package/dist/web/core/axes.js +146 -0
- package/dist/web/core/hit.js +131 -0
- package/dist/web/core/marks.js +262 -0
- package/dist/web/core/scales.js +99 -0
- package/dist/web/core/scene.js +120 -0
- package/dist/web/core/svg.js +40 -0
- package/dist/web/index.js +2 -0
- package/dist/web/octane-client-build.json +8 -0
- package/package.json +86 -3
- package/src/Chart.macos.tsrx +134 -0
- package/src/Chart.tsrx +174 -0
- package/src/Chart.web.tsrx +215 -0
- package/src/core/axes.ts +211 -0
- package/src/core/hit.ts +201 -0
- package/src/core/marks.ts +380 -0
- package/src/core/scales.ts +168 -0
- package/src/core/scene.ts +145 -0
- package/src/core/svg.ts +84 -0
- package/src/index.macos.ts +12 -0
- package/src/index.ts +12 -0
- package/src/index.web.ts +12 -0
- package/src/props.ts +90 -0
- package/src/svg-image.macos.ts +34 -0
- package/types/index.d.ts +17 -0
- package/types/props.d.ts +83 -0
- package/README.md +0 -4
package/src/core/svg.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { Mark } from './marks'
|
|
2
|
+
import type { PlotBox } from './scales'
|
|
3
|
+
|
|
4
|
+
/** Mark[] → one `<svg>` markup string. Emits only the androidsvg ∩ SVGKit
|
|
5
|
+
* subset: `<svg>`/`<g>` structure, path/rect/circle/line shapes, solid fills
|
|
6
|
+
* and strokes, `translate` transforms, opacity attributes. Never `<text>`,
|
|
7
|
+
* filters, masks, patterns, or gradients — labels are real elements in the
|
|
8
|
+
* leaf overlay (docs/notes/primitive-notes.md). */
|
|
9
|
+
|
|
10
|
+
function num(v: number): string {
|
|
11
|
+
if (!Number.isFinite(v)) {
|
|
12
|
+
return '0'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return Number.isInteger(v) ? String(v) : String(+v.toFixed(2))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function esc(v: string): string {
|
|
19
|
+
return v
|
|
20
|
+
.replaceAll('&', '&')
|
|
21
|
+
.replaceAll('"', '"')
|
|
22
|
+
.replaceAll('<', '<')
|
|
23
|
+
.replaceAll('>', '>')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function paintAttrs(m: Mark): string {
|
|
27
|
+
let out = ''
|
|
28
|
+
if (m.fill !== undefined) {
|
|
29
|
+
out += ` fill="${esc(m.fill)}"`
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (m.fillOpacity !== undefined) {
|
|
33
|
+
out += ` fill-opacity="${num(m.fillOpacity)}"`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (m.stroke !== undefined) {
|
|
37
|
+
out += ` stroke="${esc(m.stroke)}"`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (m.strokeOpacity !== undefined) {
|
|
41
|
+
out += ` stroke-opacity="${num(m.strokeOpacity)}"`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (m.strokeWidth !== undefined) {
|
|
45
|
+
out += ` stroke-width="${num(m.strokeWidth)}"`
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (m.strokeLinecap !== undefined) {
|
|
49
|
+
out += ` stroke-linecap="${m.strokeLinecap}"`
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (m.transform !== undefined) {
|
|
53
|
+
out += ` transform="${esc(m.transform)}"`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return out
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function markXml(m: Mark): string {
|
|
60
|
+
switch (m.kind) {
|
|
61
|
+
case 'path':
|
|
62
|
+
return `<path d="${esc(m.d)}"${paintAttrs(m)}/>`
|
|
63
|
+
case 'rect':
|
|
64
|
+
return `<rect x="${num(m.x)}" y="${num(m.y)}" width="${num(m.width)}" height="${num(m.height)}"${paintAttrs(m)}/>`
|
|
65
|
+
case 'circle':
|
|
66
|
+
return `<circle cx="${num(m.cx)}" cy="${num(m.cy)}" r="${num(m.r)}"${paintAttrs(m)}/>`
|
|
67
|
+
case 'line':
|
|
68
|
+
return `<line x1="${num(m.x1)}" y1="${num(m.y1)}" x2="${num(m.x2)}" y2="${num(m.y2)}"${paintAttrs(m)}/>`
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Chart marks are plot-space; axis rules are chart-space. Paint order:
|
|
73
|
+
* gridlines under the translated marks group, frame (spine + stubs) over. */
|
|
74
|
+
export function svgDocument(
|
|
75
|
+
marks: Mark[],
|
|
76
|
+
grid: Mark[],
|
|
77
|
+
frame: Mark[],
|
|
78
|
+
plot: PlotBox,
|
|
79
|
+
width: number,
|
|
80
|
+
height: number,
|
|
81
|
+
): string {
|
|
82
|
+
const body = `${grid.map(markXml).join('')}<g transform="translate(${num(plot.x)} ${num(plot.y)})">${marks.map(markXml).join('')}</g>${frame.map(markXml).join('')}`
|
|
83
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${num(width)} ${num(height)}" width="${num(width)}" height="${num(height)}">${body}</svg>`
|
|
84
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Platform barrels — explicit .tsrx leaf specifiers (moduleSuffixes don't
|
|
2
|
+
// reach .tsrx; same convention as @octane-xplat/ui index.*.ts).
|
|
3
|
+
export { Chart } from './Chart.macos.tsrx'
|
|
4
|
+
export type {
|
|
5
|
+
AxisSpec,
|
|
6
|
+
ChartDatum,
|
|
7
|
+
ChartHit,
|
|
8
|
+
ChartMargin,
|
|
9
|
+
ChartProps,
|
|
10
|
+
ChartType,
|
|
11
|
+
SeriesSpec,
|
|
12
|
+
} from './props'
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Platform barrels — explicit .tsrx leaf specifiers (moduleSuffixes don't
|
|
2
|
+
// reach .tsrx; same convention as @octane-xplat/ui index.*.ts).
|
|
3
|
+
export { Chart } from './Chart.tsrx'
|
|
4
|
+
export type {
|
|
5
|
+
AxisSpec,
|
|
6
|
+
ChartDatum,
|
|
7
|
+
ChartHit,
|
|
8
|
+
ChartMargin,
|
|
9
|
+
ChartProps,
|
|
10
|
+
ChartType,
|
|
11
|
+
SeriesSpec,
|
|
12
|
+
} from './props'
|
package/src/index.web.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Platform barrels — explicit .tsrx leaf specifiers (moduleSuffixes don't
|
|
2
|
+
// reach .tsrx; same convention as @octane-xplat/ui index.*.ts).
|
|
3
|
+
export { Chart } from './Chart.web.tsrx'
|
|
4
|
+
export type {
|
|
5
|
+
AxisSpec,
|
|
6
|
+
ChartDatum,
|
|
7
|
+
ChartHit,
|
|
8
|
+
ChartMargin,
|
|
9
|
+
ChartProps,
|
|
10
|
+
ChartType,
|
|
11
|
+
SeriesSpec,
|
|
12
|
+
} from './props'
|
package/src/props.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/** Shared prop/type contract for @octane-xplat/charts — the single source for
|
|
2
|
+
* both platform leaves and the published .d.ts. No imports: the shared
|
|
3
|
+
* contract stays dependency-free and platform-agnostic. */
|
|
4
|
+
|
|
5
|
+
export type ChartType = 'line' | 'area' | 'bar' | 'pie' | 'scatter'
|
|
6
|
+
|
|
7
|
+
export interface ChartDatum {
|
|
8
|
+
/** Bar/pie: the category label. Line/area/scatter: number or Date. */
|
|
9
|
+
x: number | string | Date
|
|
10
|
+
y: number
|
|
11
|
+
/** Per-datum color override — wins over the series color (pie slices,
|
|
12
|
+
* scatter points, individual bars). */
|
|
13
|
+
color?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SeriesSpec {
|
|
17
|
+
name: string
|
|
18
|
+
/** Series color — stroke for line/scatter, fill for area/bar. Falls back
|
|
19
|
+
* to the built-in palette by series index. */
|
|
20
|
+
color?: string
|
|
21
|
+
values: ChartDatum[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ChartMargin {
|
|
25
|
+
top?: number
|
|
26
|
+
right?: number
|
|
27
|
+
bottom?: number
|
|
28
|
+
left?: number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Axis decomposition — gridline, tick stub, and label toggle independently
|
|
32
|
+
* (Swift Charts `AxisMarks` shape). Labels render as real positioned
|
|
33
|
+
* elements in the overlay, never `<text>` in markup. */
|
|
34
|
+
export interface AxisSpec {
|
|
35
|
+
/** Tick labels in the margin. Default true. */
|
|
36
|
+
labels?: boolean
|
|
37
|
+
/** Tick stubs on the axis spine. Default true. */
|
|
38
|
+
ticks?: boolean
|
|
39
|
+
/** Gridlines across the plot area. Default false. */
|
|
40
|
+
grid?: boolean
|
|
41
|
+
/** Desired tick count hint — a hint, not a guarantee. */
|
|
42
|
+
tickCount?: number
|
|
43
|
+
/** Tick label formatter; receives the domain value. */
|
|
44
|
+
format?: (value: number | string | Date) => string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** A resolved datum reference — `x`/`y` are the datum's values, not pixels. */
|
|
48
|
+
export interface ChartHit {
|
|
49
|
+
series: number
|
|
50
|
+
index: number
|
|
51
|
+
x: number | string | Date
|
|
52
|
+
y: number
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface ChartProps {
|
|
56
|
+
type: ChartType
|
|
57
|
+
data: SeriesSpec[]
|
|
58
|
+
/** Explicit size in px (web) / dips (native). Without `height`, the leaf
|
|
59
|
+
* fills its parent or derives height from `aspectRatio`. */
|
|
60
|
+
width?: number
|
|
61
|
+
height?: number
|
|
62
|
+
/** width/height ratio used when `height` is unset. */
|
|
63
|
+
aspectRatio?: number
|
|
64
|
+
/** Space reserved inside the chart box for axis labels — labels live in
|
|
65
|
+
* the margin band, the plot in the remainder. Defaults reserve room for
|
|
66
|
+
* x labels (bottom) and y labels (left) when those axes show labels. */
|
|
67
|
+
margin?: ChartMargin
|
|
68
|
+
/** Bar only: stack series instead of grouping side by side. */
|
|
69
|
+
stacked?: boolean
|
|
70
|
+
/** Pie only: 0–<1 inner-radius fraction — any positive value renders a
|
|
71
|
+
* donut. */
|
|
72
|
+
innerRadiusRatio?: number
|
|
73
|
+
xAxis?: AxisSpec
|
|
74
|
+
yAxis?: AxisSpec
|
|
75
|
+
/** Legend row of series swatches. `true` = 'bottom'. Pie legends list
|
|
76
|
+
* slices, not series. */
|
|
77
|
+
legend?: boolean | 'top' | 'bottom'
|
|
78
|
+
/** Show a value callout at the scrub/hover cursor. */
|
|
79
|
+
tooltip?: boolean
|
|
80
|
+
/** Show a snap-to-x cursor rule while scrubbing. */
|
|
81
|
+
crosshair?: boolean
|
|
82
|
+
/** Tap/click a datum. */
|
|
83
|
+
onPress?: (hit: ChartHit) => void
|
|
84
|
+
/** Scrub position changes — `null` when the gesture ends or leaves. */
|
|
85
|
+
onScrub?: (hit: ChartHit | null) => void
|
|
86
|
+
accessibilityLabel?: string
|
|
87
|
+
className?: any
|
|
88
|
+
style?: any
|
|
89
|
+
id?: string
|
|
90
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Kept local to avoid coupling the charts leaf to private UI helpers.
|
|
2
|
+
// Foundation's public ObjC API is available through the AppKit metadata bridge.
|
|
3
|
+
declare const NSString: {
|
|
4
|
+
stringWithString(value: string): {
|
|
5
|
+
dataUsingEncoding(encoding: number): {
|
|
6
|
+
base64EncodedStringWithOptions(options: number): string
|
|
7
|
+
} | null
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function svgDataUri(markup: string): string {
|
|
12
|
+
const data = NSString.stringWithString(markup).dataUsingEncoding(4) // NSUTF8StringEncoding
|
|
13
|
+
if (!data) {
|
|
14
|
+
throw new Error('Unable to encode SVG as UTF-8')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return `data:image/svg+xml;base64,${data.base64EncodedStringWithOptions(0)}`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Normalize bundled markup and SVG data URIs for AppKit's image host. */
|
|
21
|
+
export function appKitImageSource(src: string): string {
|
|
22
|
+
if (src.trimStart().startsWith('<svg') || src.trimStart().startsWith('<?xml')) {
|
|
23
|
+
return svgDataUri(src)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (
|
|
27
|
+
/^data:image\/svg\+xml[;,]/i.test(src) &&
|
|
28
|
+
!src.slice(0, src.indexOf(',')).endsWith(';base64')
|
|
29
|
+
) {
|
|
30
|
+
return svgDataUri(decodeURIComponent(src.slice(src.indexOf(',') + 1)))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return src
|
|
34
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Boundary types for @octane-xplat/charts — the mechanical wrapper over
|
|
2
|
+
// props.ts (tsrx-tsc can't emit .tsrx declarations; see ui/types/index.d.ts
|
|
3
|
+
// for the same pattern).
|
|
4
|
+
import type { UniversalComponent } from 'octane/universal'
|
|
5
|
+
import type { ChartProps } from './props.js'
|
|
6
|
+
|
|
7
|
+
export declare const Chart: UniversalComponent<ChartProps>
|
|
8
|
+
|
|
9
|
+
export type {
|
|
10
|
+
AxisSpec,
|
|
11
|
+
ChartDatum,
|
|
12
|
+
ChartHit,
|
|
13
|
+
ChartMargin,
|
|
14
|
+
ChartProps,
|
|
15
|
+
ChartType,
|
|
16
|
+
SeriesSpec,
|
|
17
|
+
} from './props.js'
|
package/types/props.d.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/** Shared prop/type contract for @octane-xplat/charts — the single source for
|
|
2
|
+
* both platform leaves and the published .d.ts. No imports: the shared
|
|
3
|
+
* contract stays dependency-free and platform-agnostic. */
|
|
4
|
+
export type ChartType = 'line' | 'area' | 'bar' | 'pie' | 'scatter';
|
|
5
|
+
export interface ChartDatum {
|
|
6
|
+
/** Bar/pie: the category label. Line/area/scatter: number or Date. */
|
|
7
|
+
x: number | string | Date;
|
|
8
|
+
y: number;
|
|
9
|
+
/** Per-datum color override — wins over the series color (pie slices,
|
|
10
|
+
* scatter points, individual bars). */
|
|
11
|
+
color?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface SeriesSpec {
|
|
14
|
+
name: string;
|
|
15
|
+
/** Series color — stroke for line/scatter, fill for area/bar. Falls back
|
|
16
|
+
* to the built-in palette by series index. */
|
|
17
|
+
color?: string;
|
|
18
|
+
values: ChartDatum[];
|
|
19
|
+
}
|
|
20
|
+
export interface ChartMargin {
|
|
21
|
+
top?: number;
|
|
22
|
+
right?: number;
|
|
23
|
+
bottom?: number;
|
|
24
|
+
left?: number;
|
|
25
|
+
}
|
|
26
|
+
/** Axis decomposition — gridline, tick stub, and label toggle independently
|
|
27
|
+
* (Swift Charts `AxisMarks` shape). Labels render as real positioned
|
|
28
|
+
* elements in the overlay, never `<text>` in markup. */
|
|
29
|
+
export interface AxisSpec {
|
|
30
|
+
/** Tick labels in the margin. Default true. */
|
|
31
|
+
labels?: boolean;
|
|
32
|
+
/** Tick stubs on the axis spine. Default true. */
|
|
33
|
+
ticks?: boolean;
|
|
34
|
+
/** Gridlines across the plot area. Default false. */
|
|
35
|
+
grid?: boolean;
|
|
36
|
+
/** Desired tick count hint — a hint, not a guarantee. */
|
|
37
|
+
tickCount?: number;
|
|
38
|
+
/** Tick label formatter; receives the domain value. */
|
|
39
|
+
format?: (value: number | string | Date) => string;
|
|
40
|
+
}
|
|
41
|
+
/** A resolved datum reference — `x`/`y` are the datum's values, not pixels. */
|
|
42
|
+
export interface ChartHit {
|
|
43
|
+
series: number;
|
|
44
|
+
index: number;
|
|
45
|
+
x: number | string | Date;
|
|
46
|
+
y: number;
|
|
47
|
+
}
|
|
48
|
+
export interface ChartProps {
|
|
49
|
+
type: ChartType;
|
|
50
|
+
data: SeriesSpec[];
|
|
51
|
+
/** Explicit size in px (web) / dips (native). Without `height`, the leaf
|
|
52
|
+
* fills its parent or derives height from `aspectRatio`. */
|
|
53
|
+
width?: number;
|
|
54
|
+
height?: number;
|
|
55
|
+
/** width/height ratio used when `height` is unset. */
|
|
56
|
+
aspectRatio?: number;
|
|
57
|
+
/** Space reserved inside the chart box for axis labels — labels live in
|
|
58
|
+
* the margin band, the plot in the remainder. Defaults reserve room for
|
|
59
|
+
* x labels (bottom) and y labels (left) when those axes show labels. */
|
|
60
|
+
margin?: ChartMargin;
|
|
61
|
+
/** Bar only: stack series instead of grouping side by side. */
|
|
62
|
+
stacked?: boolean;
|
|
63
|
+
/** Pie only: 0–<1 inner-radius fraction — any positive value renders a
|
|
64
|
+
* donut. */
|
|
65
|
+
innerRadiusRatio?: number;
|
|
66
|
+
xAxis?: AxisSpec;
|
|
67
|
+
yAxis?: AxisSpec;
|
|
68
|
+
/** Legend row of series swatches. `true` = 'bottom'. Pie legends list
|
|
69
|
+
* slices, not series. */
|
|
70
|
+
legend?: boolean | 'top' | 'bottom';
|
|
71
|
+
/** Show a value callout at the scrub/hover cursor. */
|
|
72
|
+
tooltip?: boolean;
|
|
73
|
+
/** Show a snap-to-x cursor rule while scrubbing. */
|
|
74
|
+
crosshair?: boolean;
|
|
75
|
+
/** Tap/click a datum. */
|
|
76
|
+
onPress?: (hit: ChartHit) => void;
|
|
77
|
+
/** Scrub position changes — `null` when the gesture ends or leaves. */
|
|
78
|
+
onScrub?: (hit: ChartHit | null) => void;
|
|
79
|
+
accessibilityLabel?: string;
|
|
80
|
+
className?: any;
|
|
81
|
+
style?: any;
|
|
82
|
+
id?: string;
|
|
83
|
+
}
|
package/README.md
DELETED