@cfasim-ui/charts 0.1.3 → 0.1.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfasim-ui/charts",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "description": "Chart visualization components for cfasim-ui",
6
6
  "license": "Apache-2.0",
@@ -9,6 +9,9 @@
9
9
  "url": "https://github.com/CDCgov/cfa-simulator.git",
10
10
  "directory": "cfasim-ui/charts"
11
11
  },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
12
15
  "files": [
13
16
  "src"
14
17
  ],
@@ -22,7 +25,7 @@
22
25
  "reka-ui": "^2.9.2",
23
26
  "topojson-client": "^3.1.0",
24
27
  "us-atlas": "^3.0.1",
25
- "@cfasim-ui/shared": "0.1.3"
28
+ "@cfasim-ui/shared": "0.1.5"
26
29
  },
27
30
  "peerDependencies": {
28
31
  "vue": "^3.5.0"
@@ -35,7 +38,7 @@
35
38
  "@types/topojson-specification": "^1.0.5",
36
39
  "@vitejs/plugin-vue": "^6.0.5",
37
40
  "@vue/test-utils": "^2.4.6",
38
- "happy-dom": "^20.8.4",
41
+ "happy-dom": "^20.8.8",
39
42
  "vitest": "^4.1.0"
40
43
  }
41
44
  }
@@ -12,16 +12,26 @@ export interface Series {
12
12
  opacity?: number;
13
13
  }
14
14
 
15
+ export interface Area {
16
+ upper: number[];
17
+ lower: number[];
18
+ color?: string;
19
+ opacity?: number;
20
+ }
21
+
15
22
  const props = withDefaults(
16
23
  defineProps<{
17
24
  data?: number[];
18
25
  series?: Series[];
26
+ areas?: Area[];
19
27
  width?: number;
20
28
  height?: number;
21
29
  lineOpacity?: number;
22
30
  title?: string;
23
31
  xLabel?: string;
24
32
  yLabel?: string;
33
+ yMin?: number;
34
+ xMin?: number;
25
35
  debounce?: number;
26
36
  menu?: boolean | string;
27
37
  }>(),
@@ -81,11 +91,17 @@ const allSeries = computed<Series[]>(() => {
81
91
  return [];
82
92
  });
83
93
 
94
+ const allAreas = computed<Area[]>(() => props.areas ?? []);
95
+
84
96
  const maxLen = computed(() => {
85
97
  let m = 0;
86
98
  for (const s of allSeries.value) {
87
99
  if (s.data.length > m) m = s.data.length;
88
100
  }
101
+ for (const a of allAreas.value) {
102
+ if (a.upper.length > m) m = a.upper.length;
103
+ if (a.lower.length > m) m = a.lower.length;
104
+ }
89
105
  return m;
90
106
  });
91
107
 
@@ -98,7 +114,18 @@ const extent = computed(() => {
98
114
  if (v > max) max = v;
99
115
  }
100
116
  }
117
+ for (const a of allAreas.value) {
118
+ for (const v of a.upper) {
119
+ if (v < min) min = v;
120
+ if (v > max) max = v;
121
+ }
122
+ for (const v of a.lower) {
123
+ if (v < min) min = v;
124
+ if (v > max) max = v;
125
+ }
126
+ }
101
127
  if (!isFinite(min)) return { min: 0, max: 0, range: 1 };
128
+ if (props.yMin != null && props.yMin < min) min = props.yMin;
102
129
  return { min, max, range: max - min || 1 };
103
130
  });
104
131
 
@@ -116,6 +143,22 @@ function toPath(data: number[]): string {
116
143
  return d;
117
144
  }
118
145
 
146
+ function toAreaPath(upper: number[], lower: number[]): string {
147
+ const len = Math.min(upper.length, lower.length);
148
+ if (len === 0) return "";
149
+ const { min, range } = extent.value;
150
+ const ml = maxLen.value;
151
+ const xScale = innerW.value / (ml - 1 || 1);
152
+ const yScale = innerH.value / range;
153
+ const py = padding.value.top + innerH.value;
154
+ const x = (i: number) => padding.value.left + i * xScale;
155
+ const y = (v: number) => py - (v - min) * yScale;
156
+ let d = `M${x(0)},${y(upper[0])}`;
157
+ for (let i = 1; i < len; i++) d += `L${x(i)},${y(upper[i])}`;
158
+ for (let i = len - 1; i >= 0; i--) d += `L${x(i)},${y(lower[i])}`;
159
+ return d + "Z";
160
+ }
161
+
119
162
  function niceStep(range: number, targetTicks: number): number {
120
163
  const rough = range / targetTicks;
121
164
  const mag = Math.pow(10, Math.floor(Math.log10(rough)));
@@ -164,13 +207,14 @@ const yTicks = computed(() => {
164
207
  const xTicks = computed(() => {
165
208
  const len = maxLen.value;
166
209
  if (len <= 1) return [];
210
+ const offset = props.xMin ?? 0;
167
211
  const targetTicks = Math.max(3, Math.floor(innerW.value / 80));
168
212
  const step = niceStep(len - 1, targetTicks);
169
213
  const ticks: { value: string; x: number }[] = [];
170
214
  for (let i = 0; i <= len - 1; i += step) {
171
215
  const idx = Math.round(i);
172
216
  ticks.push({
173
- value: idx.toString(),
217
+ value: formatTick(idx + offset),
174
218
  x: padding.value.left + (idx / (len - 1)) * innerW.value,
175
219
  });
176
220
  }
@@ -309,6 +353,15 @@ const menuItems = computed<ChartMenuItem[]>(() => {
309
353
  >
310
354
  {{ xLabel }}
311
355
  </text>
356
+ <!-- areas -->
357
+ <path
358
+ v-for="(a, i) in allAreas"
359
+ :key="'area' + i"
360
+ :d="toAreaPath(a.upper, a.lower)"
361
+ :fill="a.color ?? 'currentColor'"
362
+ :fill-opacity="a.opacity ?? 0.2"
363
+ stroke="none"
364
+ />
312
365
  <!-- data lines -->
313
366
  <path
314
367
  v-for="(s, i) in allSeries"
package/src/index.ts CHANGED
@@ -1,4 +1,8 @@
1
- export { default as LineChart, type Series } from "./LineChart/LineChart.vue";
1
+ export {
2
+ default as LineChart,
3
+ type Series,
4
+ type Area,
5
+ } from "./LineChart/LineChart.vue";
2
6
  export {
3
7
  default as ChoroplethMap,
4
8
  type GeoType,