@communitiesuk/svelte-component-library 0.1.19-beta.27 → 0.1.19-beta.33

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.
@@ -42,42 +42,27 @@
42
42
  includeOutliers = true,
43
43
  } = $props();
44
44
 
45
- let xTicks = $state([]);
46
- let yTicks = $state([]);
47
-
48
- let xTickFirst = $derived(xTicks.length ? xTicks[0] : 0);
49
- let xTickLast = $derived(xTicks.length ? xTicks.at(-1) : 1);
50
-
51
- let domainXMin = $derived(Math.min(xTickFirst, xTickLast));
52
- let domainXMax = $derived(Math.max(xTickFirst, xTickLast));
53
-
54
- let yTickFirst = $derived(yTicks.length ? yTicks[0] : 0);
55
- let yTickLast = $derived(yTicks.length ? yTicks.at(-1) : 1);
56
-
57
- let domainYMin = $derived(Math.min(yTickFirst, yTickLast));
58
- let domainYMax = $derived(Math.max(yTickFirst, yTickLast));
59
-
60
45
  let useRange = $derived(
61
46
  polarity === "standard"
62
47
  ? [0, containerWidth - padding]
63
48
  : [containerWidth - padding, 0],
64
49
  );
65
50
 
66
- let xScale = $derived(
67
- scaleLinear().domain([domainXMin, domainXMax]).range(useRange),
68
- );
51
+ let xValueFirst = $derived(polarity === "standard" ? minX : maxX);
52
+ let xValueLast = $derived(polarity === "standard" ? maxX : minX);
53
+
54
+ let xScale = $derived(scaleLinear().domain([minX, maxX]).range(useRange));
69
55
 
70
56
  const segmentScale = $derived(
71
- scaleLinear().domain([0, nBins]).range([domainXMin, domainXMax]),
57
+ scaleLinear().domain([0, nBins]).range([minX, maxX]),
72
58
  );
73
59
 
74
60
  const binThresholds = $derived(d3range(1, nBins).map(segmentScale));
75
61
 
76
- const binner = $derived(
77
- bin().domain([domainXMin, domainXMax]).thresholds(binThresholds),
78
- );
62
+ const binner = $derived(bin().domain([minX, maxX]).thresholds(binThresholds));
63
+
79
64
  const clampedDistribution = $derived(
80
- distribution.map((d) => Math.min(Math.max(d, domainXMin), domainXMax)),
65
+ distribution.map((d) => Math.min(Math.max(d, minX), maxX)),
81
66
  );
82
67
 
83
68
  const binnedDistribution = $derived(
@@ -126,7 +111,7 @@
126
111
  .colors(2);
127
112
 
128
113
  const averageNormalised =
129
- (averageValue - xTickFirst) / (xTickLast - xTickFirst);
114
+ (averageValue - xValueFirst) / (xValueLast - xValueFirst);
130
115
 
131
116
  const binColors = chroma
132
117
  .scale([extremeColors[0], midColor, extremeColors[1]])
@@ -150,8 +135,8 @@
150
135
  if (
151
136
  !midColor ||
152
137
  averageValue == null ||
153
- xTickFirst == null ||
154
- xTickLast == null
138
+ xValueFirst == null ||
139
+ xValueLast == null
155
140
  ) {
156
141
  return [];
157
142
  }
@@ -160,8 +145,6 @@
160
145
  return interpolateColors(startColor, endColor, nBins, midColor, skew);
161
146
  });
162
147
 
163
- $inspect({ colorScale });
164
-
165
148
  const layout = $derived.by(() => {
166
149
  let y = 0;
167
150
 
@@ -194,6 +177,7 @@
194
177
  {#if topLabel && layout.topLabelY !== null}
195
178
  <text x={0} y={layout.topLabelY + 14} fill="#666" font-size="0.75em">
196
179
  Number of areas
180
+ {minX}, {maxX}
197
181
  </text>
198
182
  {/if}
199
183
 
@@ -217,21 +201,28 @@
217
201
 
218
202
  <g transform="translate(0, {layout.chartY})">
219
203
  {#if showYAxis}
220
- <Axis
221
- bind:ticksArray={yTicks}
222
- chartHeight={height}
223
- chartWidth={containerWidth - padding}
224
- orientation={{ axis: "y", position: "left" }}
225
- min={minY}
226
- max={maxY}
227
- domain={[yTickLast, 0]}
228
- range={[0, height]}
229
- fontSize={0}
230
- numberOfTicks={3}
231
- {showGridlines}
204
+ <!-- <Axis
205
+ bind:axisDomain
206
+ bind:ticksArray={ticksDomain}
207
+ {chartHeight}
208
+ chartWidth={chartWidth - markerRadius * 2}
209
+ orientation={{ axis: "x", position: "bottom" }}
210
+ range={[markerRadius, chartWidth - markerRadius]}
211
+ domain={[xValueFirst, xValueLast]}
212
+ {min}
213
+ {max}
214
+ fontSize={14}
215
+ {floor}
216
+ {ceiling}
217
+ {numberOfTicks}
218
+ {polarity}
232
219
  {showTickMarks}
233
- strokeWidth={tickStrokeWidth}
234
- />
220
+ {showGridlines}
221
+ {labelFormatter}
222
+ {niceTicks}
223
+ {markerRadius}
224
+ {distribution}
225
+ ></Axis> -->
235
226
  {/if}
236
227
 
237
228
  {#each bins as bin, i}
@@ -247,18 +238,26 @@
247
238
  height={yScale(bin.length)}
248
239
  fill={fill ?? colorScale[i]}
249
240
  ></rect>
241
+ <!-- <text
242
+ x={(polarity === "reverse" ? xScale(bin.x1) : xScale(bin.x0)) +
243
+ offset}
244
+ y={height - yScale(bin.length)}
245
+ font-size={5}
246
+ >{bin.length} areas between
247
+ {Math.round(bin.x0)} and {Math.round(bin.x1)}
248
+ </text> -->
250
249
  {/key}
251
250
  {/each}
252
251
  </g>
253
252
 
254
253
  {#if showXAxis && layout.xAxisY !== null}
255
254
  <g transform="translate(0, {layout.xAxisY})">
256
- <Axis
255
+ <!-- <Axis
257
256
  bind:ticksArray={xTicks}
258
257
  chartHeight={height}
259
258
  chartWidth={containerWidth - padding}
260
259
  orientation={{ axis: "x", position: "bottom" }}
261
- domain={[xTickFirst, xTickLast]}
260
+ domain={[xValueFirst, xValueLast]}
262
261
  min={minX}
263
262
  max={maxX}
264
263
  range={useRange}
@@ -267,7 +266,7 @@
267
266
  {ceiling}
268
267
  {labelFormatter}
269
268
  {numberOfTicks}
270
- />
269
+ /> -->
271
270
  </g>
272
271
  {/if}
273
272
  </g>
@@ -277,20 +276,27 @@
277
276
 
278
277
  <div style="content-visibility: hidden;">
279
278
  {#if !showXAxis}
280
- <Axis
281
- bind:ticksArray={xTicks}
282
- chartHeight={height}
283
- chartWidth={containerWidth - padding}
279
+ <!-- <Axis
280
+ bind:axisDomain
281
+ bind:ticksArray={ticksDomain}
282
+ {chartHeight}
283
+ chartWidth={chartWidth - markerRadius * 2}
284
284
  orientation={{ axis: "x", position: "bottom" }}
285
- domain={[xTickFirst, xTickLast]}
286
- min={minX}
287
- max={maxX}
288
- range={useRange}
289
- fontSize={13}
285
+ range={[markerRadius, chartWidth - markerRadius]}
286
+ domain={[xValueFirst, xValueLast]}
287
+ {min}
288
+ {max}
289
+ fontSize={14}
290
290
  {floor}
291
291
  {ceiling}
292
- {labelFormatter}
293
292
  {numberOfTicks}
294
- ></Axis>
293
+ {polarity}
294
+ {showTickMarks}
295
+ {showGridlines}
296
+ {labelFormatter}
297
+ {niceTicks}
298
+ {markerRadius}
299
+ {distribution}
300
+ ></Axis> -->
295
301
  {/if}
296
302
  </div>
@@ -20,35 +20,21 @@
20
20
 
21
21
  let {
22
22
  chartHeight = 100,
23
- chartWidth = $bindable<number>(200),
24
-
23
+ chartWidth = 200,
25
24
  numberOfTicks = undefined as number | undefined,
26
-
27
- // Bindable, but avoid binding undefined – initialize as [] for safety
25
+ axisDomain = $bindable<number[]>([]),
28
26
  ticksArray = $bindable<number[]>([]),
29
-
30
- // Values to derive ticks/domain from if ticksArray not provided
31
27
  min = undefined as number | undefined,
32
28
  max = undefined as number | undefined,
33
-
34
29
  orientation = { axis: "x", position: "bottom" } as Orientation,
35
-
36
30
  floor = undefined as number | undefined,
37
31
  ceiling = undefined as number | undefined,
38
-
39
32
  paddingTop = 100,
40
33
  paddingBottom = 100,
41
34
  paddingLeft = 0,
42
35
  paddingRight = 0,
43
-
44
36
  labelFormatter = undefined as LabelFormatter | undefined,
45
-
46
- // --- New inputs for D3 scale + optional overrides ---
47
- // A ready-made D3 continuous scale (linear/log/time, etc.)
48
- // For this component we use numeric-only; time scales also implement numeric mapping.
49
37
  scale = undefined as ScaleContinuousNumeric<number, number> | undefined,
50
-
51
- // Optional overrides for domain/range applied to a COPY of the provided scale
52
38
  domain = undefined as [number, number] | undefined,
53
39
  range = undefined as [number, number] | undefined,
54
40
  fontSize = 19,
@@ -57,14 +43,16 @@
57
43
  showTickMarks = false,
58
44
  strokeWidth = 2,
59
45
  niceTicks = true,
46
+ markerRadius = 0 as number,
47
+ distribution = [],
60
48
  }: {
61
49
  chartHeight?: number;
62
50
  chartWidth?: number;
63
51
  numberOfTicks?: number;
52
+ axisDomain?: number[];
64
53
  ticksArray?: number[];
65
54
  min?: number;
66
55
  max?: number;
67
-
68
56
  orientation?: Orientation;
69
57
  floor?: number;
70
58
  ceiling?: number;
@@ -73,56 +61,104 @@
73
61
  paddingLeft?: number;
74
62
  paddingRight?: number;
75
63
  labelFormatter?: LabelFormatter;
76
-
77
- // New
78
64
  scale?: ScaleContinuousNumeric<number, number>;
79
65
  domain?: [number, number];
80
66
  range?: [number, number];
81
67
  fontSize?: number;
82
68
  polarity?: Polarity;
83
- gridlines?: Boolean;
84
- strokeWidth?: Number;
85
- showGridlines?: Boolean;
86
- showTickMarks?: Boolean;
87
- niceTicks?: Boolean;
69
+ gridlines?: boolean;
70
+ strokeWidth?: number;
71
+ showGridlines?: boolean;
72
+ showTickMarks?: boolean;
73
+ niceTicks?: boolean;
74
+ markerRadius?: number;
75
+ distribution?: number[];
88
76
  } = $props();
89
77
 
90
- // --- Helpers to compute default domain/range when not supplied ---
91
- const innerWidth = $derived(Math.max(0, chartWidth));
92
- const innerHeight = $derived(Math.max(0, chartHeight));
93
-
94
- function computeDefaultDomain(): [number, number] {
95
- const arr =
96
- (ticksArray && ticksArray.length ? ticksArray : [min, max]) ?? [];
97
- const dMin =
98
- floor ?? (arr.length ? arr.reduce((a, b) => (a < b ? a : b)) : 0);
99
- const dMax =
100
- ceiling ?? (arr.length ? arr.reduce((a, b) => (a > b ? a : b)) : 1);
101
- return [dMin, dMax];
102
- }
103
-
104
- function computeDefaultRange(innerWidth, innerHeight): [number, number] {
105
- if (orientation.axis === "x") {
106
- return [0, innerWidth];
78
+ let minTick = $derived(ticksArray.length ? Math.min(...ticksArray) : 0);
79
+ let maxTick = $derived(ticksArray.length ? Math.max(...ticksArray) : 1);
80
+ let minValue = $derived(distribution.length ? Math.min(...distribution) : 0);
81
+ let maxValue = $derived(distribution.length ? Math.max(...distribution) : 1);
82
+
83
+ let leftPad = $derived(
84
+ niceTicks
85
+ ? polarity === "standard"
86
+ ? minValue < minTick
87
+ ? 0.1
88
+ : 0
89
+ : polarity === "reverse"
90
+ ? maxValue > maxTick
91
+ ? 0.1
92
+ : 0
93
+ : 0
94
+ : polarity === "standard"
95
+ ? minValue < min
96
+ ? 0.1
97
+ : 0
98
+ : polarity === "reverse"
99
+ ? maxValue > max
100
+ ? 0.1
101
+ : 0
102
+ : 0,
103
+ );
104
+
105
+ let rightPad = $derived(
106
+ niceTicks
107
+ ? polarity === "standard"
108
+ ? maxValue > maxTick
109
+ ? 0.1
110
+ : 0
111
+ : polarity === "reverse"
112
+ ? minValue < minTick
113
+ ? 0.1
114
+ : 0
115
+ : 0
116
+ : polarity === "standard"
117
+ ? maxValue > max
118
+ ? 0.1
119
+ : 0
120
+ : polarity === "reverse"
121
+ ? minValue < min
122
+ ? 0.1
123
+ : 0
124
+ : 0,
125
+ );
126
+
127
+ let widthForTicks = $derived(
128
+ chartWidth - chartWidth * leftPad - chartWidth * rightPad,
129
+ );
130
+ let heightForTicks = $derived(Math.max(0, chartHeight));
131
+
132
+ function calculateFullAxisDomain(
133
+ minTick,
134
+ maxTick,
135
+ leftPad,
136
+ rightPad,
137
+ polarity,
138
+ ) {
139
+ const ticksDomainRange = maxTick - minTick;
140
+ const axisDomainRange = ticksDomainRange / (1 - leftPad - rightPad);
141
+
142
+ if (polarity === "standard") {
143
+ return [
144
+ minTick - axisDomainRange * leftPad,
145
+ maxTick + axisDomainRange * rightPad,
146
+ ];
107
147
  } else {
108
- return [innerHeight, 0];
148
+ return [
149
+ maxTick + axisDomainRange * leftPad,
150
+ minTick - axisDomainRange * rightPad,
151
+ ];
109
152
  }
110
153
  }
111
- //Returns d3 scale function
112
- const resolvedScale = $derived(() => {
113
- const base: ScaleContinuousNumeric<number, number> = scale
114
- ? scale.copy()
115
- : scaleLinear<number, number>();
116
-
117
- const useDomain = domain ?? computeDefaultDomain();
118
- base.domain(useDomain);
119
154
 
120
- const useRange = range ?? computeDefaultRange(innerWidth, innerHeight);
121
- base.range(useRange);
155
+ let fullAxisDomain = $derived(
156
+ calculateFullAxisDomain(minTick, maxTick, leftPad, rightPad, polarity),
157
+ );
122
158
 
123
- return base;
159
+ $effect(() => {
160
+ axisDomain = fullAxisDomain;
124
161
  });
125
- const axisFunction: AxisProjector = $derived((v: number) => resolvedScale(v));
126
162
  </script>
127
163
 
128
164
  <g
@@ -132,34 +168,67 @@
132
168
  : chartWidth},{orientation.position === 'bottom' ? chartHeight : 0})"
133
169
  >
134
170
  <line
135
- x1={range[0]}
171
+ x1={markerRadius ?? 0}
136
172
  y1="0"
137
- x2={orientation.axis === "x" ? range[1] : 0}
173
+ x2={orientation.axis === "x"
174
+ ? markerRadius
175
+ ? chartWidth + markerRadius
176
+ : chartWidth
177
+ : 0}
138
178
  y2={orientation.axis === "y" ? chartHeight : 0}
139
179
  stroke="grey"
140
180
  stroke-width="2px"
141
181
  ></line>
142
- {#if ticksArray || (min && max)}
143
- {#key numberOfTicks}
144
- <Ticks
145
- bind:ticksArray
146
- {chartWidth}
147
- {chartHeight}
148
- {axisFunction}
149
- {min}
150
- {max}
151
- {numberOfTicks}
152
- {orientation}
153
- {floor}
154
- {ceiling}
155
- {labelFormatter}
156
- {fontSize}
157
- {polarity}
158
- {showGridlines}
159
- {showTickMarks}
160
- {strokeWidth}
161
- {niceTicks}
162
- />
163
- {/key}
164
- {/if}
182
+ <g
183
+ data-role="{orientation.axis}-axis"
184
+ transform="translate({orientation.position !== 'right'
185
+ ? chartWidth * leftPad + markerRadius
186
+ : chartWidth},{orientation.position === 'bottom' ? 0 : 0})"
187
+ >
188
+ {#if ticksArray || (min && max)}
189
+ {#key numberOfTicks}
190
+ {#if niceTicks}
191
+ <Ticks
192
+ bind:ticksArray
193
+ tickWidth={widthForTicks}
194
+ chartHeight={heightForTicks}
195
+ {min}
196
+ {max}
197
+ {numberOfTicks}
198
+ {orientation}
199
+ {floor}
200
+ {ceiling}
201
+ {labelFormatter}
202
+ {fontSize}
203
+ {polarity}
204
+ {showGridlines}
205
+ {showTickMarks}
206
+ {strokeWidth}
207
+ {niceTicks}
208
+ />
209
+ {:else}
210
+ <Ticks
211
+ bind:ticksArray
212
+ tickWidth={widthForTicks}
213
+ chartHeight={heightForTicks}
214
+ {min}
215
+ {max}
216
+ {numberOfTicks}
217
+ {orientation}
218
+ {floor}
219
+ {ceiling}
220
+ {labelFormatter}
221
+ {fontSize}
222
+ {polarity}
223
+ {showGridlines}
224
+ {showTickMarks}
225
+ {strokeWidth}
226
+ {niceTicks}
227
+ {leftPad}
228
+ {rightPad}
229
+ />
230
+ {/if}
231
+ {/key}
232
+ {/if}
233
+ </g>
165
234
  </g>
@@ -11,6 +11,7 @@ type $$ComponentProps = {
11
11
  chartHeight?: number;
12
12
  chartWidth?: number;
13
13
  numberOfTicks?: number;
14
+ axisDomain?: number[];
14
15
  ticksArray?: number[];
15
16
  min?: number;
16
17
  max?: number;
@@ -27,12 +28,14 @@ type $$ComponentProps = {
27
28
  range?: [number, number];
28
29
  fontSize?: number;
29
30
  polarity?: Polarity;
30
- gridlines?: Boolean;
31
- strokeWidth?: Number;
32
- showGridlines?: Boolean;
33
- showTickMarks?: Boolean;
34
- niceTicks?: Boolean;
31
+ gridlines?: boolean;
32
+ strokeWidth?: number;
33
+ showGridlines?: boolean;
34
+ showTickMarks?: boolean;
35
+ niceTicks?: boolean;
36
+ markerRadius?: number;
37
+ distribution?: number[];
35
38
  };
36
- declare const Axis: import("svelte").Component<$$ComponentProps, {}, "ticksArray" | "chartWidth">;
39
+ declare const Axis: import("svelte").Component<$$ComponentProps, {}, "ticksArray" | "axisDomain">;
37
40
  type Axis = ReturnType<typeof Axis>;
38
41
  export default Axis;