@communitiesuk/svelte-component-library 0.1.19-beta.2 → 0.1.19-beta.20

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.
Files changed (43) hide show
  1. package/README.md +7 -0
  2. package/dist/components/data-vis/Histogram.svelte +245 -0
  3. package/dist/components/data-vis/Histogram.svelte.d.ts +71 -0
  4. package/dist/components/data-vis/axis/Axis.svelte +140 -34
  5. package/dist/components/data-vis/axis/Axis.svelte.d.ts +34 -30
  6. package/dist/components/data-vis/axis/Ticks.svelte +155 -60
  7. package/dist/components/data-vis/axis/Ticks.svelte.d.ts +25 -30
  8. package/dist/components/data-vis/line-chart/LineChart.svelte +51 -21
  9. package/dist/components/data-vis/line-chart/LineChart.svelte.d.ts +14 -6
  10. package/dist/components/data-vis/position-chart/PositionChart.svelte +220 -112
  11. package/dist/components/data-vis/position-chart/PositionChart.svelte.d.ts +26 -4
  12. package/dist/components/data-vis/position-chart/PositionChartAxis.svelte +38 -34
  13. package/dist/components/data-vis/position-chart/PositionChartAxis.svelte.d.ts +6 -2
  14. package/dist/components/layout/Footer.svelte +9 -0
  15. package/dist/components/layout/Footer.svelte.d.ts +1 -0
  16. package/dist/components/layout/PhaseBanner.svelte +10 -1
  17. package/dist/components/layout/PhaseBanner.svelte.d.ts +1 -0
  18. package/dist/components/layout/ServiceNavigation.svelte +19 -1
  19. package/dist/components/layout/ServiceNavigation.svelte.d.ts +2 -0
  20. package/dist/components/ui/BasicMultiSelect.svelte +185 -0
  21. package/dist/components/ui/BasicMultiSelect.svelte.d.ts +8 -0
  22. package/dist/components/ui/Button.svelte +1 -0
  23. package/dist/components/ui/Card.svelte +48 -60
  24. package/dist/components/ui/Card.svelte.d.ts +26 -12
  25. package/dist/components/ui/CardHeader.svelte +46 -0
  26. package/dist/components/ui/CardHeader.svelte.d.ts +21 -0
  27. package/dist/components/ui/ChartExporter.svelte +142 -0
  28. package/dist/components/ui/ChartExporter.svelte.d.ts +16 -0
  29. package/dist/components/ui/Details.svelte +10 -2
  30. package/dist/components/ui/Details.svelte.d.ts +2 -0
  31. package/dist/components/ui/Masthead.svelte +36 -6
  32. package/dist/components/ui/Masthead.svelte.d.ts +4 -0
  33. package/dist/components/ui/PostcodeOrAreaSearch.svelte +12 -0
  34. package/dist/components/ui/PostcodeOrAreaSearch.svelte.d.ts +4 -0
  35. package/dist/components/ui/RelatedContent.svelte +4 -1
  36. package/dist/components/ui/RelatedContent.svelte.d.ts +1 -0
  37. package/dist/components/ui/SearchAutocomplete.svelte +185 -34
  38. package/dist/components/ui/SearchAutocomplete.svelte.d.ts +5 -0
  39. package/dist/components/ui/Tabs.svelte +190 -18
  40. package/dist/components/ui/Tabs.svelte.d.ts +1 -0
  41. package/dist/index.d.ts +4 -0
  42. package/dist/index.js +4 -0
  43. package/package.json +4 -1
@@ -1,112 +1,207 @@
1
- <script>
1
+ <script lang="ts">
2
2
  import Decimal from "decimal.js";
3
3
 
4
+ // Types
5
+ type Axis = "x" | "y";
6
+ type Position = "left" | "right" | "top" | "bottom";
7
+
8
+ interface Orientation {
9
+ axis: Axis;
10
+ position: Position;
11
+ }
12
+ type Polarity = "standard" | "reverse";
13
+
14
+ // Props with defaults (Svelte 5 runes)
4
15
  let {
5
- ticksArray = $bindable(),
6
- prefix,
7
- suffix,
16
+ ticksArray = $bindable<number[]>(),
8
17
  chartWidth,
9
18
  chartHeight,
10
19
  axisFunction,
11
- values,
20
+ min,
21
+ max,
12
22
  numberOfTicks,
13
23
  floor,
14
24
  ceiling,
15
25
  orientation,
16
- yearsInput,
26
+ labelFormatter,
27
+ fontSize = 19,
28
+ polarity = "standard",
29
+ showGridlines = false,
30
+ showTickMarks = false,
31
+
32
+ strokeWidth = 2,
33
+ }: {
34
+ ticksArray?: number[]; // bindable
35
+ chartWidth: number;
36
+ chartHeight: number;
37
+ axisFunction: any;
38
+ min: number;
39
+ max: number;
40
+ numberOfTicks?: number;
41
+ floor?: number;
42
+ ceiling?: number;
43
+ orientation: Orientation;
44
+ labelFormatter?: (tick: number, index: number) => string;
45
+ fontSize?: number;
46
+ polarity?: Polarity;
47
+ showGridlines?: Boolean;
48
+ showTickMarks?: Boolean;
49
+
50
+ strokeWidth?: number;
17
51
  } = $props();
52
+ function axisValue(fn: any, tick: number): number {
53
+ // Try single-call first: axisFunction(tick)
54
+ try {
55
+ const v = fn(tick);
56
+ if (typeof v === "number") return v;
57
+ } catch {
58
+ // ignore
59
+ }
18
60
 
19
- $inspect(ticksArray);
20
-
21
- function generateTicks(data, numTicks, floor, ceiling) {
22
- let minValueFromData = Decimal.min(...data);
23
-
24
- let minVal = floor
25
- ? Decimal.max(floor, minValueFromData)
26
- : minValueFromData;
27
-
28
- let maxValueFromData = Decimal.max(...data);
29
-
30
- let maxVal = ceiling
31
- ? Decimal.min(ceiling, maxValueFromData)
32
- : maxValueFromData;
33
-
34
- let rangeVal = maxVal.minus(minVal);
35
-
36
- let roughStep = rangeVal.div(numTicks - 1);
37
- let normalizedSteps = [1, 2, 5, 10];
61
+ // Fallback: axisFunction()(tick)
62
+ const inner = fn();
63
+ return inner(tick);
64
+ }
38
65
 
39
- let stepPower = Decimal.pow(
66
+ function generateTicks(
67
+ min: number,
68
+ max: number,
69
+ numTicks: number,
70
+ floorVal?: number,
71
+ ceilingVal?: number,
72
+ ): number[] {
73
+ let minVal =
74
+ floorVal !== undefined ? new Decimal(floorVal) : new Decimal(min);
75
+
76
+ let maxVal =
77
+ ceilingVal !== undefined ? new Decimal(ceilingVal) : new Decimal(max);
78
+
79
+ const rangeVal = maxVal.minus(minVal);
80
+ const roughStep = rangeVal.div(numTicks - 1);
81
+ const normalizedSteps = [
82
+ 1, 2, 2.5, 3, 4, 5, 6, 8, 10, 12, 15, 25, 30, 35, 40, 45,
83
+ ];
84
+ const stepPower = Decimal.pow(
40
85
  10,
41
86
  -Math.floor(Math.log10(roughStep.toNumber())),
42
87
  );
43
- let normalizedStep = roughStep.mul(stepPower);
44
- let optimalStep = new Decimal(
45
- normalizedSteps.find((step) => step >= normalizedStep.toNumber()),
46
- ).div(stepPower);
47
88
 
48
- let scaleMin = minVal.div(optimalStep).floor().mul(optimalStep);
49
- let scaleMax = maxVal.div(optimalStep).ceil().mul(optimalStep);
89
+ const normalizedStep = roughStep.mul(stepPower);
90
+ const chosen = normalizedSteps.find(
91
+ (step) => step >= normalizedStep.toNumber(),
92
+ );
93
+ const optimalStep = new Decimal(chosen ?? 10).div(stepPower);
94
+
95
+ const scaleMin = minVal.div(optimalStep).floor().mul(optimalStep);
96
+ const scaleMax = maxVal.div(optimalStep).ceil().mul(optimalStep);
50
97
 
51
- let ticks = [];
98
+ const ticks: number[] = [];
52
99
  for (let i = scaleMin; i.lte(scaleMax); i = i.plus(optimalStep)) {
53
100
  ticks.push(i.toNumber());
54
101
  }
55
102
  return ticks;
56
103
  }
57
104
 
58
- function tickCount(chartWidth, chartHeight) {
59
- let tickNum = orientation.axis === "y" ? chartHeight / 50 : chartWidth / 50;
105
+ // Default label when no labelFormatter is supplied
106
+ function defaultLabel(tick: number): string {
107
+ return String(tick);
108
+ }
109
+
110
+ function tickCount(w: number, h: number): number {
111
+ // Keep behavior aligned with your original code.
112
+ const tickNum = orientation.axis === "y" ? h / 50 : w / 50;
60
113
  return tickNum;
61
114
  }
115
+ function clampTickEnds(
116
+ ticks: number[],
117
+ floor?: number,
118
+ ceiling?: number,
119
+ ): number[] {
120
+ if (!ticks || ticks.length === 0) return ticks;
62
121
 
63
- function yearsFormat(ticks) {
64
- return ticks.map((tick) => `FY ${tick % 100}-${(tick % 100) + 1}`);
122
+ const out = ticks.slice();
123
+
124
+ if (floor !== undefined && out[0] <= floor) {
125
+ out[0] = floor;
126
+ }
127
+ if (ceiling !== undefined && out[out.length - 1] >= ceiling) {
128
+ out[out.length - 1] = ceiling;
129
+ }
130
+ return out;
65
131
  }
66
132
 
67
- numberOfTicks = tickCount(chartWidth, chartHeight);
133
+ // Compute ticks
134
+ let computedTickCount = $derived(
135
+ numberOfTicks ?? tickCount(chartWidth, chartHeight),
136
+ );
137
+
138
+ let rawTicks = $derived(
139
+ generateTicks(min, max, computedTickCount, floor, ceiling),
140
+ );
141
+
142
+ let ticksOrdered = $derived(
143
+ polarity === "standard" ? rawTicks : rawTicks.reverse(),
144
+ );
68
145
 
69
- ticksArray = generateTicks(values, numberOfTicks, floor, ceiling);
70
- let yearTicks = yearsInput ? yearsFormat(ticksArray) : [];
146
+ ticksArray = ticksOrdered;
71
147
  </script>
72
148
 
73
149
  {#if axisFunction && ticksArray && orientation.axis && orientation.position}
74
150
  {#each ticksArray as tick, index}
75
151
  <g
76
- transform="translate({orientation.axis === 'x'
77
- ? axisFunction(tick)
78
- : 0},{orientation.axis === 'y' ? axisFunction(tick) : 0})"
152
+ transform="translate(
153
+ {orientation.axis === 'x' ? axisValue(axisFunction, tick) : 0},
154
+ {orientation.axis === 'y' ? axisValue(axisFunction, tick) : 0}
155
+ )"
79
156
  >
80
- <path
81
- d={orientation.axis === "y"
82
- ? orientation.position === "left"
83
- ? "M0 0 l-8 0"
84
- : "M0 0 l8 0"
85
- : orientation.position === "top"
86
- ? "M0 0 l0 -8"
87
- : "M0 0 l0 8"}
88
- stroke="black"
89
- stroke-width="2px"
90
- ></path>
157
+ {#if showTickMarks}
158
+ <path
159
+ d={orientation.axis === "y"
160
+ ? orientation.position === "left"
161
+ ? `M1 0 l-8 0`
162
+ : `M1 0 l8 0`
163
+ : orientation.position === "top"
164
+ ? "M0 -1 l0 -8"
165
+ : "M0 -1 l0 8"}
166
+ stroke="grey"
167
+ stroke-width={strokeWidth}
168
+ ></path>
169
+ {/if}
170
+ {#if showGridlines}
171
+ <path
172
+ d={orientation.axis === "y"
173
+ ? orientation.position === "left"
174
+ ? `M0 0 l${chartWidth} 0`
175
+ : `M0 0 l-${chartWidth} 0`
176
+ : orientation.position === "top"
177
+ ? `M0 0 l0 ${chartHeight}`
178
+ : `M0 0 l0 -${chartHeight}`}
179
+ stroke="grey"
180
+ stroke-width={strokeWidth}
181
+ ></path>
182
+ {/if}
91
183
  <text
92
- transform="translate({orientation.axis === 'x'
184
+ transform="translate(
185
+ {orientation.axis === 'x'
93
186
  ? 0
94
187
  : orientation.position === 'left'
95
188
  ? -10
96
- : 10}, {orientation.axis === 'y'
189
+ : 10},
190
+ {orientation.axis === 'y'
97
191
  ? 5
98
192
  : orientation.position === 'top'
99
193
  ? -10
100
- : 23})"
101
- font-size="19"
194
+ : fontSize * 1.4}
195
+ )"
196
+ font-size={fontSize}
102
197
  text-anchor={orientation.axis === "x"
103
198
  ? "middle"
104
199
  : orientation.position === "left"
105
200
  ? "end"
106
201
  : "start"}
107
- fill="black"
202
+ fill="grey"
108
203
  >
109
- {yearsInput ? yearTicks[index] : prefix + tick + suffix}
204
+ {labelFormatter ? labelFormatter(tick, index) : defaultLabel(tick)}
110
205
  </text>
111
206
  </g>
112
207
  {/each}
@@ -1,33 +1,28 @@
1
- export default Ticks;
2
- type Ticks = {
3
- $on?(type: string, callback: (e: any) => void): () => void;
4
- $set?(props: Partial<$$ComponentProps>): void;
5
- };
6
- declare const Ticks: import("svelte").Component<{
7
- ticksArray?: any;
8
- prefix: any;
9
- suffix: any;
10
- chartWidth: any;
11
- chartHeight: any;
12
- axisFunction: any;
13
- values: any;
14
- numberOfTicks: any;
15
- floor: any;
16
- ceiling: any;
17
- orientation: any;
18
- yearsInput: any;
19
- }, {}, "ticksArray">;
1
+ type Axis = "x" | "y";
2
+ type Position = "left" | "right" | "top" | "bottom";
3
+ interface Orientation {
4
+ axis: Axis;
5
+ position: Position;
6
+ }
7
+ type Polarity = "standard" | "reverse";
20
8
  type $$ComponentProps = {
21
- ticksArray?: any;
22
- prefix: any;
23
- suffix: any;
24
- chartWidth: any;
25
- chartHeight: any;
9
+ ticksArray?: number[];
10
+ chartWidth: number;
11
+ chartHeight: number;
26
12
  axisFunction: any;
27
- values: any;
28
- numberOfTicks: any;
29
- floor: any;
30
- ceiling: any;
31
- orientation: any;
32
- yearsInput: any;
13
+ min: number;
14
+ max: number;
15
+ numberOfTicks?: number;
16
+ floor?: number;
17
+ ceiling?: number;
18
+ orientation: Orientation;
19
+ labelFormatter?: (tick: number, index: number) => string;
20
+ fontSize?: number;
21
+ polarity?: Polarity;
22
+ showGridlines?: Boolean;
23
+ showTickMarks?: Boolean;
24
+ strokeWidth?: number;
33
25
  };
26
+ declare const Ticks: import("svelte").Component<$$ComponentProps, {}, "ticksArray">;
27
+ type Ticks = ReturnType<typeof Ticks>;
28
+ export default Ticks;
@@ -8,6 +8,7 @@
8
8
  import { highlight } from "./../../../utils/syntax-highlighting/shikiHighlight";
9
9
  import Lines from "./Lines.svelte";
10
10
  import ValueLabel from "./ValueLabel.svelte";
11
+ import Axis from "../axis/Axis.svelte";
11
12
 
12
13
  let {
13
14
  series,
@@ -15,17 +16,18 @@
15
16
  x,
16
17
  lineChartData,
17
18
 
18
- xFunction = (number) => {
19
- return scaleLinear()
20
- .domain([2015, 2022])
21
- .range([0, svgWidth - paddingLeft - paddingRight])(number);
19
+ xScale = (number) => {
20
+ return scaleLinear().domain([xTickMin, xTickMax]).range([0, chartWidth])(
21
+ number,
22
+ );
22
23
  },
23
- yFunction = (number) => {
24
- return scaleLinear()
25
- .domain([0, 100])
26
- .range([svgHeight - paddingTop - paddingBottom, 0])(number);
24
+ yScale = (number) => {
25
+ return scaleLinear().domain([yTickMin, yTickMax]).range([chartHeight, 0])(
26
+ number,
27
+ );
27
28
  },
28
- lineFunction = line()
29
+
30
+ lineScale = line()
29
31
  .x((d) => xFunction(d[x]))
30
32
  .y((d) => yFunction(d[y]))
31
33
  .curve(curveLinear),
@@ -108,6 +110,10 @@
108
110
  paddingBottom = 50,
109
111
  paddingLeft = 50,
110
112
  paddingRight = 150,
113
+ xFloor = undefined,
114
+ xCeiling = undefined,
115
+ yFloor = undefined,
116
+ yCeiling = undefined,
111
117
  activeMarkerId = undefined,
112
118
  chartBackgroundColor = "#f5f5f5",
113
119
  seriesLabels = $bindable(false),
@@ -157,6 +163,18 @@
157
163
  },
158
164
  } = $props();
159
165
 
166
+ let xTicks = $state([]);
167
+ let yTicks = $state([]);
168
+
169
+ const xTickMin = $derived(xTicks.length ? Math.min(...xTicks) : undefined);
170
+ const xTickMax = $derived(xTicks.length ? Math.max(...xTicks) : undefined);
171
+ const yTickMin = $derived(yTicks.length ? Math.min(...yTicks) : undefined);
172
+ const yTickMax = $derived(yTicks.length ? Math.max(...yTicks) : undefined);
173
+
174
+ let xFunction = $derived((value) => xScale(value));
175
+ let yFunction = $derived((value) => yScale(value));
176
+ let lineFunction = $derived((value) => lineScale(value));
177
+
160
178
  const colorValues = Array.isArray(colors) ? colors : Object.values(colors);
161
179
  const lineColorMap = {};
162
180
 
@@ -197,7 +215,7 @@
197
215
  let chartHeight = $derived(svgHeight - paddingTop - paddingBottom);
198
216
  let areaFunction = $derived(
199
217
  area()
200
- .y0((d) => yFunction(0))
218
+ .y0((d) => yFunction(yTickMin))
201
219
  .x((d) => xFunction(d.x))
202
220
  .y1((d) => yFunction(d.y))
203
221
  .curve(curveLinear),
@@ -292,17 +310,29 @@
292
310
  >
293
311
  {#if svgWidth}
294
312
  <g transform="translate({paddingLeft},{paddingTop})">
295
- <g data-role="y-axis">
296
- <path d="M0 0 l0 {chartHeight}" stroke="black" stroke-width="2px"
297
- ></path>
298
- </g>
299
- <g data-role="x-axis">
300
- <path
301
- d="M0 {chartHeight} l{chartWidth} 0"
302
- stroke="black"
303
- stroke-width="2px"
304
- ></path>
305
- </g>
313
+ <Axis
314
+ bind:ticksArray={yTicks}
315
+ {chartHeight}
316
+ {chartWidth}
317
+ orientation={{ axis: "y", position: "left" }}
318
+ range={[chartHeight, 0]}
319
+ domain={[yTickMin, yTickMax]}
320
+ values={lineChartData.lines.flatMap((l) => l.data.map((d) => d[y]))}
321
+ ceiling={yCeiling ?? yTickMax}
322
+ floor={yFloor ?? yTickMin}
323
+ ></Axis>
324
+ <!--X axis-->
325
+ <Axis
326
+ bind:ticksArray={xTicks}
327
+ {chartWidth}
328
+ {chartHeight}
329
+ orientation={{ axis: "x", position: "bottom" }}
330
+ values={lineChartData.lines.flatMap((l) => l.data.map((d) => d[x]))}
331
+ range={[0, chartWidth]}
332
+ domain={[xTickMin, xTickMax]}
333
+ ceiling={xCeiling ?? xTickMax}
334
+ floor={xFloor ?? xTickMin}
335
+ ></Axis>
306
336
  <g data-role="lines-group">
307
337
  <Lines
308
338
  {tieredDataObject}
@@ -8,9 +8,9 @@ declare const LineChart: import("svelte").Component<{
8
8
  y: any;
9
9
  x: any;
10
10
  lineChartData: any;
11
- xFunction?: Function;
12
- yFunction?: Function;
13
- lineFunction?: any;
11
+ xScale?: Function;
12
+ yScale?: Function;
13
+ lineScale?: any;
14
14
  labelText?: Function;
15
15
  onClickSeries?: Function;
16
16
  onMouseLeaveSeries?: Function;
@@ -34,6 +34,10 @@ declare const LineChart: import("svelte").Component<{
34
34
  paddingBottom?: number;
35
35
  paddingLeft?: number;
36
36
  paddingRight?: number;
37
+ xFloor?: any;
38
+ xCeiling?: any;
39
+ yFloor?: any;
40
+ yCeiling?: any;
37
41
  activeMarkerId?: any;
38
42
  chartBackgroundColor?: string;
39
43
  seriesLabels?: boolean;
@@ -50,9 +54,9 @@ type $$ComponentProps = {
50
54
  y: any;
51
55
  x: any;
52
56
  lineChartData: any;
53
- xFunction?: Function;
54
- yFunction?: Function;
55
- lineFunction?: any;
57
+ xScale?: Function;
58
+ yScale?: Function;
59
+ lineScale?: any;
56
60
  labelText?: Function;
57
61
  onClickSeries?: Function;
58
62
  onMouseLeaveSeries?: Function;
@@ -76,6 +80,10 @@ type $$ComponentProps = {
76
80
  paddingBottom?: number;
77
81
  paddingLeft?: number;
78
82
  paddingRight?: number;
83
+ xFloor?: any;
84
+ xCeiling?: any;
85
+ yFloor?: any;
86
+ yCeiling?: any;
79
87
  activeMarkerId?: any;
80
88
  chartBackgroundColor?: string;
81
89
  seriesLabels?: boolean;