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

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 +282 -0
  3. package/dist/components/data-vis/Histogram.svelte.d.ts +75 -0
  4. package/dist/components/data-vis/axis/Axis.svelte +145 -34
  5. package/dist/components/data-vis/axis/Axis.svelte.d.ts +34 -30
  6. package/dist/components/data-vis/axis/Ticks.svelte +163 -60
  7. package/dist/components/data-vis/axis/Ticks.svelte.d.ts +26 -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 +255 -117
  11. package/dist/components/data-vis/position-chart/PositionChart.svelte.d.ts +28 -4
  12. package/dist/components/data-vis/position-chart/PositionChartAxis.svelte +39 -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,215 @@
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
+ type LabelFormatter = (
15
+ tick: number,
16
+ index: number,
17
+ ticksArrayLength: number,
18
+ ) => string | number;
19
+
20
+ // Props with defaults (Svelte 5 runes)
4
21
  let {
5
- ticksArray = $bindable(),
6
- prefix,
7
- suffix,
22
+ ticksArray = $bindable<number[]>(),
8
23
  chartWidth,
9
24
  chartHeight,
10
25
  axisFunction,
11
- values,
26
+ min,
27
+ max,
12
28
  numberOfTicks,
13
29
  floor,
14
30
  ceiling,
15
31
  orientation,
16
- yearsInput,
32
+ fontSize = 19,
33
+ polarity = "standard",
34
+ showGridlines = false,
35
+ showTickMarks = false,
36
+
37
+ strokeWidth = 2,
38
+ labelFormatter = undefined as LabelFormatter | undefined,
39
+ }: {
40
+ ticksArray?: number[]; // bindable
41
+ chartWidth: number;
42
+ chartHeight: number;
43
+ axisFunction: any;
44
+ min: number;
45
+ max: number;
46
+ numberOfTicks?: number;
47
+ floor?: number;
48
+ ceiling?: number;
49
+ orientation: Orientation;
50
+ fontSize?: number;
51
+ polarity?: Polarity;
52
+ showGridlines?: Boolean;
53
+ showTickMarks?: Boolean;
54
+
55
+ strokeWidth?: number;
56
+ labelFormatter?: LabelFormatter;
17
57
  } = $props();
58
+ function axisValue(fn: any, tick: number): number {
59
+ // Try single-call first: axisFunction(tick)
60
+ try {
61
+ const v = fn(tick);
62
+ if (typeof v === "number") return v;
63
+ } catch {
64
+ // ignore
65
+ }
18
66
 
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];
67
+ // Fallback: axisFunction()(tick)
68
+ const inner = fn();
69
+ return inner(tick);
70
+ }
38
71
 
39
- let stepPower = Decimal.pow(
72
+ function generateTicks(
73
+ min: number,
74
+ max: number,
75
+ numTicks: number,
76
+ floorVal?: number,
77
+ ceilingVal?: number,
78
+ ): number[] {
79
+ let minVal =
80
+ floorVal !== undefined ? new Decimal(floorVal) : new Decimal(min);
81
+
82
+ let maxVal =
83
+ ceilingVal !== undefined ? new Decimal(ceilingVal) : new Decimal(max);
84
+
85
+ const rangeVal = maxVal.minus(minVal);
86
+ const roughStep = rangeVal.div(numTicks - 1);
87
+ const normalizedSteps = [
88
+ 1, 2, 2.5, 3, 4, 5, 6, 8, 10, 12, 15, 25, 30, 35, 40, 45,
89
+ ];
90
+ const stepPower = Decimal.pow(
40
91
  10,
41
92
  -Math.floor(Math.log10(roughStep.toNumber())),
42
93
  );
43
- let normalizedStep = roughStep.mul(stepPower);
44
- let optimalStep = new Decimal(
45
- normalizedSteps.find((step) => step >= normalizedStep.toNumber()),
46
- ).div(stepPower);
47
94
 
48
- let scaleMin = minVal.div(optimalStep).floor().mul(optimalStep);
49
- let scaleMax = maxVal.div(optimalStep).ceil().mul(optimalStep);
95
+ const normalizedStep = roughStep.mul(stepPower);
96
+ const chosen = normalizedSteps.find(
97
+ (step) => step >= normalizedStep.toNumber(),
98
+ );
99
+ const optimalStep = new Decimal(chosen ?? 10).div(stepPower);
100
+
101
+ const scaleMin = minVal.div(optimalStep).floor().mul(optimalStep);
102
+ const scaleMax = maxVal.div(optimalStep).ceil().mul(optimalStep);
50
103
 
51
- let ticks = [];
104
+ const ticks: number[] = [];
52
105
  for (let i = scaleMin; i.lte(scaleMax); i = i.plus(optimalStep)) {
53
106
  ticks.push(i.toNumber());
54
107
  }
55
108
  return ticks;
56
109
  }
57
110
 
58
- function tickCount(chartWidth, chartHeight) {
59
- let tickNum = orientation.axis === "y" ? chartHeight / 50 : chartWidth / 50;
111
+ // Default label when no labelFormatter is supplied
112
+ function defaultLabel(tick: number): string {
113
+ return String(tick);
114
+ }
115
+
116
+ function tickCount(w: number, h: number): number {
117
+ // Keep behavior aligned with your original code.
118
+ const tickNum = orientation.axis === "y" ? h / 50 : w / 50;
60
119
  return tickNum;
61
120
  }
121
+ function clampTickEnds(
122
+ ticks: number[],
123
+ floor?: number,
124
+ ceiling?: number,
125
+ ): number[] {
126
+ if (!ticks || ticks.length === 0) return ticks;
62
127
 
63
- function yearsFormat(ticks) {
64
- return ticks.map((tick) => `FY ${tick % 100}-${(tick % 100) + 1}`);
128
+ const out = ticks.slice();
129
+
130
+ if (floor !== undefined && out[0] <= floor) {
131
+ out[0] = floor;
132
+ }
133
+ if (ceiling !== undefined && out[out.length - 1] >= ceiling) {
134
+ out[out.length - 1] = ceiling;
135
+ }
136
+ return out;
65
137
  }
66
138
 
67
- numberOfTicks = tickCount(chartWidth, chartHeight);
139
+ // Compute ticks
140
+ let computedTickCount = $derived(
141
+ numberOfTicks ?? tickCount(chartWidth, chartHeight),
142
+ );
143
+
144
+ let rawTicks = $derived(
145
+ generateTicks(min, max, computedTickCount, floor, ceiling),
146
+ );
147
+
148
+ let ticksOrdered = $derived(
149
+ polarity === "standard" ? rawTicks : rawTicks.reverse(),
150
+ );
68
151
 
69
- ticksArray = generateTicks(values, numberOfTicks, floor, ceiling);
70
- let yearTicks = yearsInput ? yearsFormat(ticksArray) : [];
152
+ ticksArray = ticksOrdered;
71
153
  </script>
72
154
 
73
155
  {#if axisFunction && ticksArray && orientation.axis && orientation.position}
74
156
  {#each ticksArray as tick, index}
75
157
  <g
76
- transform="translate({orientation.axis === 'x'
77
- ? axisFunction(tick)
78
- : 0},{orientation.axis === 'y' ? axisFunction(tick) : 0})"
158
+ transform="translate(
159
+ {orientation.axis === 'x' ? axisValue(axisFunction, tick) : 0},
160
+ {orientation.axis === 'y' ? axisValue(axisFunction, tick) : 0}
161
+ )"
79
162
  >
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>
163
+ {#if showTickMarks}
164
+ <path
165
+ d={orientation.axis === "y"
166
+ ? orientation.position === "left"
167
+ ? `M1 0 l-8 0`
168
+ : `M1 0 l8 0`
169
+ : orientation.position === "top"
170
+ ? "M0 -1 l0 -8"
171
+ : "M0 -1 l0 8"}
172
+ stroke="grey"
173
+ stroke-width={strokeWidth}
174
+ ></path>
175
+ {/if}
176
+ {#if showGridlines}
177
+ <path
178
+ d={orientation.axis === "y"
179
+ ? orientation.position === "left"
180
+ ? `M0 0 l${chartWidth} 0`
181
+ : `M0 0 l-${chartWidth} 0`
182
+ : orientation.position === "top"
183
+ ? `M0 0 l0 ${chartHeight}`
184
+ : `M0 0 l0 -${chartHeight}`}
185
+ stroke="grey"
186
+ stroke-width={strokeWidth}
187
+ ></path>
188
+ {/if}
91
189
  <text
92
- transform="translate({orientation.axis === 'x'
190
+ transform="translate(
191
+ {orientation.axis === 'x'
93
192
  ? 0
94
193
  : orientation.position === 'left'
95
194
  ? -10
96
- : 10}, {orientation.axis === 'y'
195
+ : 10},
196
+ {orientation.axis === 'y'
97
197
  ? 5
98
198
  : orientation.position === 'top'
99
199
  ? -10
100
- : 23})"
101
- font-size="19"
200
+ : fontSize * 1.4}
201
+ )"
202
+ font-size={fontSize}
102
203
  text-anchor={orientation.axis === "x"
103
204
  ? "middle"
104
205
  : orientation.position === "left"
105
206
  ? "end"
106
207
  : "start"}
107
- fill="black"
208
+ fill="grey"
108
209
  >
109
- {yearsInput ? yearTicks[index] : prefix + tick + suffix}
210
+ {labelFormatter
211
+ ? labelFormatter(tick, index, ticksArray.length)
212
+ : defaultLabel(tick)}
110
213
  </text>
111
214
  </g>
112
215
  {/each}
@@ -1,33 +1,29 @@
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";
8
+ type LabelFormatter = (tick: number, index: number, ticksArrayLength: number) => string | number;
20
9
  type $$ComponentProps = {
21
- ticksArray?: any;
22
- prefix: any;
23
- suffix: any;
24
- chartWidth: any;
25
- chartHeight: any;
10
+ ticksArray?: number[];
11
+ chartWidth: number;
12
+ chartHeight: number;
26
13
  axisFunction: any;
27
- values: any;
28
- numberOfTicks: any;
29
- floor: any;
30
- ceiling: any;
31
- orientation: any;
32
- yearsInput: any;
14
+ min: number;
15
+ max: number;
16
+ numberOfTicks?: number;
17
+ floor?: number;
18
+ ceiling?: number;
19
+ orientation: Orientation;
20
+ fontSize?: number;
21
+ polarity?: Polarity;
22
+ showGridlines?: Boolean;
23
+ showTickMarks?: Boolean;
24
+ strokeWidth?: number;
25
+ labelFormatter?: LabelFormatter;
33
26
  };
27
+ declare const Ticks: import("svelte").Component<$$ComponentProps, {}, "ticksArray">;
28
+ type Ticks = ReturnType<typeof Ticks>;
29
+ 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;