layerchart 2.0.0-next.7 → 2.0.0-next.8

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.
@@ -844,12 +844,12 @@
844
844
  if (verbose === true) {
845
845
  if (width <= 0 && isMounted === true) {
846
846
  console.warn(
847
- '[LayerChart] Target div has zero or negative width. Did you forget to set an explicit width in CSS on the container?'
847
+ `[LayerChart] Target div has zero or negative width (${width}). Did you forget to set an explicit width in CSS on the container?`
848
848
  );
849
849
  }
850
850
  if (height <= 0 && isMounted === true) {
851
851
  console.warn(
852
- '[LayerChart] Target div has zero or negative height. Did you forget to set an explicit height in CSS on the container?'
852
+ `[LayerChart] Target div has zero or negative height (${height}). Did you forget to set an explicit height in CSS on the container?`
853
853
  );
854
854
  }
855
855
  }
@@ -351,8 +351,8 @@
351
351
 
352
352
  $effect(() => {
353
353
  if (!draw) return;
354
- [tweenedState.current];
355
354
  // Anytime the path data changes, redraw
355
+ [pathData, data, ctx.data];
356
356
  key = Symbol();
357
357
  });
358
358
  </script>
@@ -84,7 +84,7 @@
84
84
 
85
85
  <script lang="ts" generics="TData">
86
86
  import { onMount, type ComponentProps } from 'svelte';
87
- import { scaleBand, scaleLinear } from 'd3-scale';
87
+ import { scaleBand, scaleLinear, scaleTime } from 'd3-scale';
88
88
  import { stack, stackOffsetDiverging, stackOffsetExpand, stackOffsetNone } from 'd3-shape';
89
89
  import { format } from '@layerstack/utils';
90
90
  import { cls } from '@layerstack/tailwind';
@@ -109,7 +109,7 @@
109
109
  import { asAny } from '../../utils/types.js';
110
110
  import type { Insets } from '../../utils/rect.svelte.js';
111
111
  import type { SeriesData, SimplifiedChartProps, SimplifiedChartPropsObject } from './types.js';
112
- import type { AnyScale } from '../../utils/scales.svelte.js';
112
+ import { isScaleTime, type AnyScale } from '../../utils/scales.svelte.js';
113
113
  import { createLegendProps, SeriesState } from './utils.svelte.js';
114
114
  import { setTooltipMetaContext } from '../tooltip/tooltipMetaContext.js';
115
115
  import DefaultTooltip from './DefaultTooltip.svelte';
@@ -171,15 +171,56 @@
171
171
  const isStackSeries = $derived(seriesLayout.startsWith('stack'));
172
172
  const isGroupSeries = $derived(seriesLayout === 'group');
173
173
 
174
+ const chartData: Array<TData & { stackData?: any }> = $derived.by(() => {
175
+ let _chartData = (
176
+ seriesState.allSeriesData.length ? seriesState.allSeriesData : chartDataArray(data)
177
+ ) as Array<TData & { stackData?: any }>;
178
+ if (isStackSeries) {
179
+ const seriesKeys = seriesState.visibleSeries.map((s) => s.key);
180
+
181
+ const offset =
182
+ seriesLayout === 'stackExpand'
183
+ ? stackOffsetExpand
184
+ : seriesLayout === 'stackDiverging'
185
+ ? stackOffsetDiverging
186
+ : stackOffsetNone;
187
+ const stackData = stack()
188
+ .keys(seriesKeys)
189
+ .value((d, key) => {
190
+ const s = series.find((d) => d.key === key)!;
191
+ return accessor(s.value ?? s.key)(d as any);
192
+ })
193
+ .offset(offset)(chartDataArray(data)) as any[];
194
+
195
+ _chartData = _chartData.map((d, i) => {
196
+ return {
197
+ ...d,
198
+ stackData: stackData.map((sd) => sd[i]),
199
+ };
200
+ });
201
+ }
202
+ return _chartData;
203
+ });
204
+
174
205
  const xScale = $derived(
175
- xScaleProp ?? (isVertical ? scaleBand().padding(bandPadding) : scaleLinear())
206
+ xScaleProp ??
207
+ (isVertical
208
+ ? scaleBand().padding(bandPadding)
209
+ : accessor(xProp)(chartData[0]) instanceof Date // TODO: also check for Array<Date> instances (ex. x={['start', 'end']})
210
+ ? scaleTime()
211
+ : scaleLinear())
176
212
  );
177
- const xBaseline = $derived(isVertical ? undefined : 0);
213
+ const xBaseline = $derived(isVertical || isScaleTime(xScale) ? undefined : 0);
178
214
 
179
215
  const yScale = $derived(
180
- yScaleProp ?? (isVertical ? scaleLinear() : scaleBand().padding(bandPadding))
216
+ yScaleProp ??
217
+ (isVertical
218
+ ? accessor(yProp)(chartData[0]) instanceof Date // TODO: also check for Array<Date> instances (ex. y={['start', 'end']})
219
+ ? scaleTime()
220
+ : scaleLinear()
221
+ : scaleBand().padding(bandPadding))
181
222
  );
182
- const yBaseline = $derived(isVertical ? 0 : undefined);
223
+ const yBaseline = $derived(isVertical || isScaleTime(yScale) ? 0 : undefined);
183
224
 
184
225
  const x1Scale = $derived(
185
226
  isGroupSeries && isVertical ? scaleBand().padding(groupPadding) : undefined
@@ -214,37 +255,6 @@
214
255
  return d && typeof d === 'object' && 'stackData' in d;
215
256
  }
216
257
 
217
- const chartData: Array<TData & { stackData?: any }> = $derived.by(() => {
218
- let _chartData = (
219
- seriesState.allSeriesData.length ? seriesState.allSeriesData : chartDataArray(data)
220
- ) as Array<TData & { stackData?: any }>;
221
- if (isStackSeries) {
222
- const seriesKeys = seriesState.visibleSeries.map((s) => s.key);
223
-
224
- const offset =
225
- seriesLayout === 'stackExpand'
226
- ? stackOffsetExpand
227
- : seriesLayout === 'stackDiverging'
228
- ? stackOffsetDiverging
229
- : stackOffsetNone;
230
- const stackData = stack()
231
- .keys(seriesKeys)
232
- .value((d, key) => {
233
- const s = series.find((d) => d.key === key)!;
234
- return accessor(s.value ?? s.key)(d as any);
235
- })
236
- .offset(offset)(chartDataArray(data)) as any[];
237
-
238
- _chartData = _chartData.map((d, i) => {
239
- return {
240
- ...d,
241
- stackData: stackData.map((sd) => sd[i]),
242
- };
243
- });
244
- }
245
- return _chartData;
246
- });
247
-
248
258
  function getBarsProps(s: SeriesData<TData, typeof Bars>, i: number): ComponentProps<typeof Bars> {
249
259
  const isFirst = i == 0;
250
260
  const isLast = i == seriesState.visibleSeries.length - 1;
@@ -278,7 +288,12 @@
278
288
  y: isVertical ? valueAccessor : undefined,
279
289
  x1: isVertical && isGroupSeries ? (d) => s.value ?? s.key : undefined,
280
290
  y1: !isVertical && isGroupSeries ? (d) => s.value ?? s.key : undefined,
281
- rounded: isStackLayout && i !== seriesState.visibleSeries.length - 1 ? 'none' : 'edge',
291
+ rounded:
292
+ isStackLayout && i !== seriesState.visibleSeries.length - 1
293
+ ? 'none'
294
+ : Array.isArray(xProp) || Array.isArray(yProp)
295
+ ? 'all'
296
+ : 'edge',
282
297
  radius: 4,
283
298
  strokeWidth: 1,
284
299
  insets: stackInsets,
@@ -14,14 +14,14 @@
14
14
  class: className,
15
15
  }: {
16
16
  source: string | null;
17
- language: string;
18
- highlightedSource: string;
19
- classes: {
17
+ language?: string;
18
+ highlightedSource?: string;
19
+ classes?: {
20
20
  root?: string;
21
21
  pre?: string;
22
22
  code?: string;
23
23
  };
24
- class: string;
24
+ class?: string;
25
25
  } = $props();
26
26
  </script>
27
27
 
@@ -1,14 +1,14 @@
1
1
  import 'prism-svelte';
2
2
  type $$ComponentProps = {
3
3
  source: string | null;
4
- language: string;
5
- highlightedSource: string;
6
- classes: {
4
+ language?: string;
5
+ highlightedSource?: string;
6
+ classes?: {
7
7
  root?: string;
8
8
  pre?: string;
9
9
  code?: string;
10
10
  };
11
- class: string;
11
+ class?: string;
12
12
  };
13
13
  declare const Code: import("svelte").Component<$$ComponentProps, {}, "">;
14
14
  type Code = ReturnType<typeof Code>;
@@ -1,8 +1,17 @@
1
1
  <script lang="ts">
2
+ import { type ComponentProps } from 'svelte';
2
3
  import JsonTree from 'svelte-json-tree';
3
4
  import { cls } from '@layerstack/tailwind';
4
5
 
5
- const { value, defaultExpandedPaths = ['$'], class: className } = $props();
6
+ const {
7
+ value,
8
+ defaultExpandedPaths = ['$'],
9
+ class: className,
10
+ }: {
11
+ value: ComponentProps<JsonTree>['value'];
12
+ defaultExpandedPaths?: ComponentProps<JsonTree>['defaultExpandedPaths'];
13
+ class?: string;
14
+ } = $props();
6
15
  </script>
7
16
 
8
17
  <div class={cls('overflow-auto px-4 py-2 bg-[#1e1e1e]', className)}>
@@ -1,7 +1,10 @@
1
- declare const Json: import("svelte").Component<{
2
- value: any;
3
- defaultExpandedPaths?: any[];
4
- class: any;
5
- }, {}, "">;
1
+ import { type ComponentProps } from 'svelte';
2
+ import JsonTree from 'svelte-json-tree';
3
+ type $$ComponentProps = {
4
+ value: ComponentProps<JsonTree>['value'];
5
+ defaultExpandedPaths?: ComponentProps<JsonTree>['defaultExpandedPaths'];
6
+ class?: string;
7
+ };
8
+ declare const Json: import("svelte").Component<$$ComponentProps, {}, "">;
6
9
  type Json = ReturnType<typeof Json>;
7
10
  export default Json;
@@ -1,26 +1,26 @@
1
1
  <script lang="ts">
2
2
  import { SelectField, Switch } from 'svelte-ux';
3
3
 
4
- export let doubleScale = devicePixelRatio > 1;
4
+ let { doubleScale = $bindable(devicePixelRatio > 1), serviceUrl = $bindable() } = $props();
5
5
 
6
6
  // TODO: Access via context, or possibly global state
7
7
  const ACCESS_TOKEN =
8
8
  'pk.eyJ1IjoidGVjaG5pcTM1IiwiYSI6ImNsZTR5cDd0ZjAyNm8zdnFvczhzdnFpcXkifQ.-LAr8sl5BZ3y-H0pDyD1qA';
9
9
 
10
10
  // https://docs.mapbox.com/api/maps/styles/
11
- $: mapboxv1 = (style: string) => (x: number, y: number, z: number) => {
11
+ const mapboxv1 = $derived((style: string) => (x: number, y: number, z: number) => {
12
12
  return `https://api.mapbox.com/styles/v1/mapbox/${style}/tiles/${z}/${x}/${y}${
13
13
  doubleScale ? '@2x' : ''
14
14
  }?access_token=${ACCESS_TOKEN}`;
15
- };
15
+ });
16
16
 
17
17
  // https://docs.mapbox.com/api/maps/raster-tiles/
18
18
  // https://docs.mapbox.com/data/tilesets/reference/mapbox-streets-v8/
19
- $: mapboxv4 = (tileset: string) => (x: number, y: number, z: number) => {
19
+ const mapboxv4 = $derived((tileset: string) => (x: number, y: number, z: number) => {
20
20
  return `https://${'abc'[Math.abs(x + y) % 3]}.tiles.mapbox.com/v4/${tileset}/${z}/${x}/${y}${
21
21
  doubleScale ? '@2x' : ''
22
22
  }.png?access_token=${ACCESS_TOKEN}`;
23
- };
23
+ });
24
24
 
25
25
  // https://apps.nationalmap.gov/services/
26
26
  const nationalmap = (tileset: string) => (x: number, y: number, z: number) => {
@@ -54,7 +54,7 @@
54
54
  return `https://${s}.tile.opentopomap.org/${z}/${x}/${y}.png`;
55
55
  };
56
56
 
57
- $: services = {
57
+ const services = $derived<Record<string, Record<string, Function>>>({
58
58
  'mapbox v1': {
59
59
  'streets-v11': mapboxv1('streets-v11'),
60
60
  'light-v10': mapboxv1('light-v10'),
@@ -100,24 +100,25 @@
100
100
  // 'ArcGIS Vector': {
101
101
  // 'Community Map', url: arcgisVector('World_Basemap_v2'),
102
102
  // }
103
- } as Record<string, Record<string, Function>>;
104
-
105
- $: serviceOptions = Object.entries(services).flatMap(([group, service]) => {
106
- return Object.entries(service).map(([label, value]) => {
107
- return { label, value: `${group}:${label}`, group, serviceUrl: value };
108
- });
109
103
  });
110
104
 
111
- $: defaultServiceUrl = services['mapbox v1']['streets-v11'];
112
- export let serviceUrl = defaultServiceUrl;
105
+ const serviceOptions = $derived(
106
+ Object.entries(services).flatMap(([group, service]) => {
107
+ return Object.entries(service).map(([label, value]) => {
108
+ return { label, value: `${group}:${label}`, group, serviceUrl: value };
109
+ });
110
+ })
111
+ );
113
112
 
114
- $: getServiceUrl = (option: string) => {
113
+ const getServiceUrl = $derived((option: string) => {
115
114
  const [selectedService, selectedTileset] = selected.split(':');
116
115
  return services[selectedService][selectedTileset];
117
- };
116
+ });
118
117
 
119
- let selected = 'mapbox v1:streets-v11';
120
- $: serviceUrl = getServiceUrl(selected);
118
+ let selected = $state('mapbox v1:streets-v11');
119
+ $effect(() => {
120
+ serviceUrl = getServiceUrl(selected);
121
+ });
121
122
  </script>
122
123
 
123
124
  <SelectField
@@ -128,7 +129,7 @@
128
129
  toggleIcon={null}
129
130
  stepper
130
131
  >
131
- <div slot="append" on:click|stopPropagation role="none">
132
+ <div slot="append" onclick={(e) => e.stopPropagation()} role="none">
132
133
  <div class="text-[10px] text-surface-content/50 text-center">2x</div>
133
134
  <Switch bind:checked={doubleScale} size="md" />
134
135
  </div>
@@ -1,23 +1,6 @@
1
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
- $$bindings?: Bindings;
4
- } & Exports;
5
- (internal: unknown, props: Props & {
6
- $$events?: Events;
7
- $$slots?: Slots;
8
- }): Exports & {
9
- $set?: any;
10
- $on?: any;
11
- };
12
- z_$$bindings?: Bindings;
13
- }
14
- declare const TilesetField: $$__sveltets_2_IsomorphicComponent<{
15
- doubleScale?: boolean;
16
- serviceUrl?: Function;
17
- }, {
18
- click: MouseEvent;
19
- } & {
20
- [evt: string]: CustomEvent<any>;
21
- }, {}, {}, string>;
22
- type TilesetField = InstanceType<typeof TilesetField>;
1
+ declare const TilesetField: import("svelte").Component<{
2
+ doubleScale?: any;
3
+ serviceUrl?: any;
4
+ }, {}, "doubleScale" | "serviceUrl">;
5
+ type TilesetField = ReturnType<typeof TilesetField>;
23
6
  export default TilesetField;
@@ -1,4 +1,4 @@
1
- import { type ScaleBand } from 'd3-scale';
1
+ import { type ScaleBand, type ScaleTime } from 'd3-scale';
2
2
  import { type MotionProp, type MotionOptions, type SpringOptions, type TweenOptions } from './motion.svelte.js';
3
3
  import type { Accessor } from './common.js';
4
4
  import type { OnlyObjects } from './types.js';
@@ -23,6 +23,8 @@ export type AnyScale<TInput extends SingleDomainType = any, TOutput extends Sing
23
23
  thresholds?: () => TInput[];
24
24
  quantiles?: () => TInput[];
25
25
  };
26
+ export declare function isScaleBand(scale: AnyScale<any, any>): scale is ScaleBand<any>;
27
+ export declare function isScaleTime(scale: AnyScale<any, any>): scale is ScaleTime<any, any>;
26
28
  export declare function getRange(scale: any): any[];
27
29
  export type SingleDomainType = number | string | Date | null | undefined;
28
30
  export type DomainType = (number | string | Date | null | undefined)[] | null | undefined;
@@ -45,7 +47,6 @@ export declare function createMotionScale<Domain, Range>(scale: AnyScale, motion
45
47
  * https://gist.github.com/LuisSevillano/d53a1dc529eef518780c6df99613e2fd
46
48
  */
47
49
  export declare function scaleBandInvert(scale: ScaleBand<any>): (value: number) => any;
48
- export declare function isScaleBand(scale: AnyScale<any, any>): scale is ScaleBand<any>;
49
50
  /**
50
51
  * Generic way to invert a scale value, handling scaleBand and continuous scales (linear, time, etc).
51
52
  * Useful to map mouse event location (x,y) to domain value
@@ -5,6 +5,13 @@ import { Spring, Tween } from 'svelte/motion';
5
5
  function isAnyScale(scale) {
6
6
  return typeof scale === 'function' && typeof scale.range === 'function';
7
7
  }
8
+ export function isScaleBand(scale) {
9
+ return typeof scale.bandwidth === 'function';
10
+ }
11
+ export function isScaleTime(scale) {
12
+ const domain = scale.domain();
13
+ return domain[0] instanceof Date || domain[1] instanceof Date;
14
+ }
8
15
  export function getRange(scale) {
9
16
  if (isAnyScale(scale)) {
10
17
  return scale.range();
@@ -54,9 +61,6 @@ export function scaleBandInvert(scale) {
54
61
  return domain[Math.max(0, Math.min(index, domain.length - 1))];
55
62
  };
56
63
  }
57
- export function isScaleBand(scale) {
58
- return typeof scale.bandwidth === 'function';
59
- }
60
64
  /**
61
65
  * Generic way to invert a scale value, handling scaleBand and continuous scales (linear, time, etc).
62
66
  * Useful to map mouse event location (x,y) to domain value
@@ -1,7 +1,7 @@
1
1
  import { timeYear, timeMonth, timeWeek, timeDay, timeHour, timeMinute, timeSecond, timeMillisecond, } from 'd3-time';
2
2
  import { format } from 'date-fns';
3
3
  import { formatDate, PeriodType, getDuration, fail } from '@layerstack/utils';
4
- import { isScaleBand } from './scales.svelte.js';
4
+ import { isScaleBand, isScaleTime } from './scales.svelte.js';
5
5
  // TODO: Use PeriodType along with Duration to format (and possibly select intervals)
6
6
  const majorTicks = [
7
7
  {
@@ -170,6 +170,12 @@ export function resolveTickVals(scale, ticks, placement) {
170
170
  ? scale.domain().filter((_, i) => i % ticks === 0)
171
171
  : scale.domain();
172
172
  }
173
+ // if (isScaleTime(scale)) {
174
+ // const interval = getMajorTicks(scale.domain()[0], scale.domain()[1])!;
175
+ // // const interval = getMinorTicks(scale.domain()[0], scale.domain()[1]);
176
+ // return scale.ticks(interval);
177
+ // }
178
+ // Ticks from scale
173
179
  if (scale.ticks && typeof scale.ticks === 'function') {
174
180
  if (placement) {
175
181
  return scale.ticks(ticks ?? (placement === 'left' || placement === 'right' ? 4 : undefined));
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "author": "Sean Lynch <techniq35@gmail.com>",
5
5
  "license": "MIT",
6
6
  "repository": "techniq/layerchart",
7
- "version": "2.0.0-next.7",
7
+ "version": "2.0.0-next.8",
8
8
  "devDependencies": {
9
9
  "@changesets/cli": "^2.29.4",
10
10
  "@iconify-json/lucide": "^1.2.42",