@communitiesuk/svelte-component-library 0.1.19-beta.20 → 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.
@@ -38,6 +38,8 @@
38
38
  tickStrokeWidth = 0.25,
39
39
  barStrokeWidth = 0,
40
40
  barStrokeColor = "white",
41
+ topLabel = true,
42
+ includeOutliers = true,
41
43
  } = $props();
42
44
 
43
45
  let xTicks = $state([]);
@@ -74,11 +76,18 @@
74
76
  const binner = $derived(
75
77
  bin().domain([domainXMin, domainXMax]).thresholds(binThresholds),
76
78
  );
79
+ const clampedDistribution = $derived(
80
+ distribution.map((d) => Math.min(Math.max(d, domainXMin), domainXMax)),
81
+ );
82
+
83
+ const binnedDistribution = $derived(
84
+ binner(includeOutliers ? clampedDistribution : distribution),
85
+ );
77
86
 
78
87
  const bins = $derived(
79
88
  polarity === "reverse"
80
- ? binner(distribution).toReversed()
81
- : binner(distribution),
89
+ ? binnedDistribution.toReversed()
90
+ : binnedDistribution,
82
91
  );
83
92
 
84
93
  const proportionsInBins = $derived(
@@ -138,24 +147,48 @@
138
147
  );
139
148
 
140
149
  $inspect({ colorScale });
150
+
151
+ const layout = $derived.by(() => {
152
+ let y = 0;
153
+
154
+ const topLabelY = topLabel ? y : null;
155
+ if (topLabel) y += 20;
156
+
157
+ const annotationY = annotationText ? y : null;
158
+ if (annotationText) y += 25;
159
+
160
+ const chartY = y;
161
+ y += height;
162
+
163
+ const xAxisY = showXAxis ? y : null;
164
+ if (showXAxis) y += 25;
165
+
166
+ return {
167
+ topLabelY,
168
+ annotationY,
169
+ chartY,
170
+ xAxisY,
171
+ totalHeight: y,
172
+ };
173
+ });
141
174
  </script>
142
175
 
143
176
  {#key containerWidth}
144
177
  <div class="scale-container" bind:clientWidth={containerWidth}>
145
- <svg
146
- width={containerWidth}
147
- height={height + 20 + (annotationText ? 25 : 0) + (showXAxis ? 25 : 0)}
148
- style="overflow: visible;"
149
- >
150
- <g transform="translate({padding / 2},0)">
151
- <g transform="translate(0,{annotationText ? 25 : 20})">
152
- <text dx={-2} dy={-4} fill="#666" font-size="0.75em"
153
- >Number of areas</text
154
- ></g
155
- >
156
-
157
- {#if annotationText}
158
- <g transform="translate({xScale(annotationValue)},0)">
178
+ <svg width={containerWidth} height={layout.totalHeight}>
179
+ <g transform="translate({padding / 2}, 0)">
180
+ {#if topLabel && layout.topLabelY !== null}
181
+ <text x={0} y={layout.topLabelY + 14} fill="#666" font-size="0.75em">
182
+ Number of areas
183
+ </text>
184
+ {/if}
185
+
186
+ {#if annotationText && layout.annotationY !== null}
187
+ <g
188
+ transform="translate({xScale(
189
+ annotationValue,
190
+ )}, {layout.annotationY})"
191
+ >
159
192
  <text
160
193
  fill="#555555"
161
194
  font-size="0.8em"
@@ -168,57 +201,61 @@
168
201
  </g>
169
202
  {/if}
170
203
 
171
- <g transform="translate(0,{annotationText ? 25 : 20})">
172
- <g>
173
- {#if showXAxis}
174
- <Axis
175
- bind:ticksArray={xTicks}
176
- chartHeight={height}
177
- chartWidth={containerWidth - padding}
178
- orientation={{ axis: "x", position: "bottom" }}
179
- domain={[xTickFirst, xTickLast]}
180
- min={minX}
181
- max={maxX}
182
- range={useRange}
183
- fontSize={13}
184
- {floor}
185
- {ceiling}
186
- {labelFormatter}
187
- {numberOfTicks}
188
- ></Axis>
189
- {/if}
190
- {#if showYAxis}
191
- <Axis
192
- bind:ticksArray={yTicks}
193
- chartHeight={height}
194
- chartWidth={containerWidth - padding}
195
- orientation={{ axis: "y", position: "left" }}
196
- min={minY}
197
- max={maxY}
198
- domain={[0, yTickLast]}
199
- range={[0, height]}
200
- fontSize={0}
201
- numberOfTicks={4}
202
- {showGridlines}
203
- {showTickMarks}
204
- strokeWidth={tickStrokeWidth}
205
- ></Axis>
206
- {/if}
207
- </g>
204
+ <g transform="translate(0, {layout.chartY})">
205
+ {#if showYAxis}
206
+ <Axis
207
+ bind:ticksArray={yTicks}
208
+ chartHeight={height}
209
+ chartWidth={containerWidth - padding}
210
+ orientation={{ axis: "y", position: "left" }}
211
+ min={minY}
212
+ max={maxY}
213
+ domain={[yTickLast, 0]}
214
+ range={[0, height]}
215
+ fontSize={0}
216
+ numberOfTicks={3}
217
+ {showGridlines}
218
+ {showTickMarks}
219
+ strokeWidth={tickStrokeWidth}
220
+ />
221
+ {/if}
222
+
208
223
  {#each bins as bin, i}
209
224
  {#key bin.x0}
225
+ {@const fullWidth = Math.abs(xScale(bin.x1) - xScale(bin.x0))}
226
+ {@const barWidth = fullWidth * 0.97}
227
+ {@const offset = (fullWidth - barWidth) / 2}
210
228
  <rect
211
- x={polarity === "reverse" ? xScale(bin.x1) : xScale(bin.x0)}
229
+ x={(polarity === "reverse" ? xScale(bin.x1) : xScale(bin.x0)) +
230
+ offset}
212
231
  y={height - yScale(bin.length)}
213
- width={Math.abs(xScale(bin.x1) - xScale(bin.x0))}
232
+ width={barWidth}
214
233
  height={yScale(bin.length)}
215
234
  fill={fill ?? colorScale[i]}
216
- stroke-width={barStrokeWidth}
217
- stroke={barStrokeColor}
218
235
  ></rect>
219
236
  {/key}
220
237
  {/each}
221
238
  </g>
239
+
240
+ {#if showXAxis && layout.xAxisY !== null}
241
+ <g transform="translate(0, {layout.xAxisY})">
242
+ <Axis
243
+ bind:ticksArray={xTicks}
244
+ chartHeight={height}
245
+ chartWidth={containerWidth - padding}
246
+ orientation={{ axis: "x", position: "bottom" }}
247
+ domain={[xTickFirst, xTickLast]}
248
+ min={minX}
249
+ max={maxX}
250
+ range={useRange}
251
+ fontSize={13}
252
+ {floor}
253
+ {ceiling}
254
+ {labelFormatter}
255
+ {numberOfTicks}
256
+ />
257
+ </g>
258
+ {/if}
222
259
  </g>
223
260
  </svg>
224
261
  </div>
@@ -35,6 +35,8 @@ declare const Histogram: import("svelte").Component<{
35
35
  tickStrokeWidth?: number;
36
36
  barStrokeWidth?: number;
37
37
  barStrokeColor?: string;
38
+ topLabel?: boolean;
39
+ includeOutliers?: boolean;
38
40
  }, {}, "containerWidth">;
39
41
  type $$ComponentProps = {
40
42
  averageValue?: any;
@@ -68,4 +70,6 @@ type $$ComponentProps = {
68
70
  tickStrokeWidth?: number;
69
71
  barStrokeWidth?: number;
70
72
  barStrokeColor?: string;
73
+ topLabel?: boolean;
74
+ includeOutliers?: boolean;
71
75
  };
@@ -10,7 +10,12 @@
10
10
  type AxisPosition = "bottom" | "top" | "left" | "right";
11
11
  type Orientation = { axis: AxisName; position: AxisPosition };
12
12
  type AxisProjector = (value: number) => number;
13
- type LabelFormatter = (tick: number, index: number) => string | number;
13
+ type LabelFormatter = (
14
+ tick: number,
15
+ index: number,
16
+ ticksArrayLength: number,
17
+ ) => string | number;
18
+
14
19
  type Polarity = "standard" | "reverse";
15
20
 
16
21
  let {
@@ -5,7 +5,7 @@ type Orientation = {
5
5
  axis: AxisName;
6
6
  position: AxisPosition;
7
7
  };
8
- type LabelFormatter = (tick: number, index: number) => string | number;
8
+ type LabelFormatter = (tick: number, index: number, ticksArrayLength: number) => string | number;
9
9
  type Polarity = "standard" | "reverse";
10
10
  type $$ComponentProps = {
11
11
  chartHeight?: number;
@@ -11,6 +11,12 @@
11
11
  }
12
12
  type Polarity = "standard" | "reverse";
13
13
 
14
+ type LabelFormatter = (
15
+ tick: number,
16
+ index: number,
17
+ ticksArrayLength: number,
18
+ ) => string | number;
19
+
14
20
  // Props with defaults (Svelte 5 runes)
15
21
  let {
16
22
  ticksArray = $bindable<number[]>(),
@@ -23,13 +29,13 @@
23
29
  floor,
24
30
  ceiling,
25
31
  orientation,
26
- labelFormatter,
27
32
  fontSize = 19,
28
33
  polarity = "standard",
29
34
  showGridlines = false,
30
35
  showTickMarks = false,
31
36
 
32
37
  strokeWidth = 2,
38
+ labelFormatter = undefined as LabelFormatter | undefined,
33
39
  }: {
34
40
  ticksArray?: number[]; // bindable
35
41
  chartWidth: number;
@@ -41,13 +47,13 @@
41
47
  floor?: number;
42
48
  ceiling?: number;
43
49
  orientation: Orientation;
44
- labelFormatter?: (tick: number, index: number) => string;
45
50
  fontSize?: number;
46
51
  polarity?: Polarity;
47
52
  showGridlines?: Boolean;
48
53
  showTickMarks?: Boolean;
49
54
 
50
55
  strokeWidth?: number;
56
+ labelFormatter?: LabelFormatter;
51
57
  } = $props();
52
58
  function axisValue(fn: any, tick: number): number {
53
59
  // Try single-call first: axisFunction(tick)
@@ -201,7 +207,9 @@
201
207
  : "start"}
202
208
  fill="grey"
203
209
  >
204
- {labelFormatter ? labelFormatter(tick, index) : defaultLabel(tick)}
210
+ {labelFormatter
211
+ ? labelFormatter(tick, index, ticksArray.length)
212
+ : defaultLabel(tick)}
205
213
  </text>
206
214
  </g>
207
215
  {/each}
@@ -5,6 +5,7 @@ interface Orientation {
5
5
  position: Position;
6
6
  }
7
7
  type Polarity = "standard" | "reverse";
8
+ type LabelFormatter = (tick: number, index: number, ticksArrayLength: number) => string | number;
8
9
  type $$ComponentProps = {
9
10
  ticksArray?: number[];
10
11
  chartWidth: number;
@@ -16,12 +17,12 @@ type $$ComponentProps = {
16
17
  floor?: number;
17
18
  ceiling?: number;
18
19
  orientation: Orientation;
19
- labelFormatter?: (tick: number, index: number) => string;
20
20
  fontSize?: number;
21
21
  polarity?: Polarity;
22
22
  showGridlines?: Boolean;
23
23
  showTickMarks?: Boolean;
24
24
  strokeWidth?: number;
25
+ labelFormatter?: LabelFormatter;
25
26
  };
26
27
  declare const Ticks: import("svelte").Component<$$ComponentProps, {}, "ticksArray">;
27
28
  type Ticks = ReturnType<typeof Ticks>;
@@ -93,6 +93,9 @@
93
93
  skew = false,
94
94
  showTickMarks = true,
95
95
  showGridlines = false,
96
+ labelFormatter = (tick, index, ticksArrayLength) => {
97
+ return tick;
98
+ },
96
99
  } = $props();
97
100
 
98
101
  let xTicks = $state([]);
@@ -272,6 +275,17 @@
272
275
  ),
273
276
  );
274
277
  }
278
+
279
+ let totalHeight = $derived(
280
+ chartHeight +
281
+ (showAxis ? 25 : 0) +
282
+ (showAverage ? 35 : 0) +
283
+ (showArrows ? 25 : 0),
284
+ );
285
+
286
+ let svgHeight = $derived(
287
+ chartHeight + (showAxis ? 25 : 0) + (showAverage ? 35 : 0),
288
+ );
275
289
  </script>
276
290
 
277
291
  {#if annotations.length}
@@ -331,9 +345,10 @@
331
345
  class="grid-container"
332
346
  bind:this={container}
333
347
  style="
334
- position: relative;
348
+ position: relative;
335
349
  grid-template-columns: {gridTemplateColumns};
336
350
  grid-template-rows: {gridTemplateRows};
351
+ height: {totalHeight}px;
337
352
  "
338
353
  >
339
354
  {#each allDataNormalized as positionChart, i}
@@ -357,18 +372,8 @@
357
372
  ></Button>
358
373
  </div>
359
374
  {/if}
360
- <div
361
- class="chart"
362
- style="height:{chartHeight +
363
- (showAxis ? 30 : 30) +
364
- (showArrows ? 30 : 0) +
365
- (showAverage ? 40 : 0)}px"
366
- bind:clientWidth={chartWidth}
367
- >
368
- <svg
369
- width={chartWidth}
370
- height={chartHeight + (showAxis ? 40 : 0) + (showAverage ? 40 : 0)}
371
- >
375
+ <div class="chart" bind:clientWidth={chartWidth}>
376
+ <svg width={chartWidth} height={svgHeight} overflow="visible">
372
377
  {#each range as number}
373
378
  <g
374
379
  transform="translate({markerRadius +
@@ -472,6 +477,7 @@
472
477
  {polarity}
473
478
  {showTickMarks}
474
479
  {showGridlines}
480
+ {labelFormatter}
475
481
  ></Axis>
476
482
  {/if}
477
483
  {#if showAverage}
@@ -479,7 +485,8 @@
479
485
  transform="translate({xFunction(
480
486
  xTickFirst,
481
487
  xTickLast,
482
- )(averageValue) + markerRadius}, {chartHeight * 1.7})"
488
+ )(averageValue) + markerRadius}, {chartHeight +
489
+ (showAxis ? 20 : 0)})"
483
490
  >
484
491
  <text
485
492
  fill="#444"
@@ -550,6 +557,29 @@
550
557
  {/if}
551
558
  </div>
552
559
 
560
+ <div style="content-visibility: hidden;">
561
+ {#if !showAxis}
562
+ <Axis
563
+ bind:ticksArray={xTicks}
564
+ {chartHeight}
565
+ chartWidth={chartWidth - markerRadius * 2}
566
+ orientation={{ axis: "x", position: "bottom" }}
567
+ range={[markerRadius, chartWidth - markerRadius]}
568
+ domain={[xTickFirst, xTickLast]}
569
+ {min}
570
+ {max}
571
+ fontSize={14}
572
+ {floor}
573
+ {ceiling}
574
+ {numberOfTicks}
575
+ {polarity}
576
+ {showTickMarks}
577
+ {showGridlines}
578
+ {labelFormatter}
579
+ ></Axis>
580
+ {/if}
581
+ </div>
582
+
553
583
  <style>
554
584
  .grid-container {
555
585
  display: grid;
@@ -53,6 +53,7 @@ declare const PositionChart: import("svelte").Component<{
53
53
  skew?: boolean;
54
54
  showTickMarks?: boolean;
55
55
  showGridlines?: boolean;
56
+ labelFormatter?: Function;
56
57
  }, {}, "chartWidth">;
57
58
  type $$ComponentProps = {
58
59
  value?: any;
@@ -104,4 +105,5 @@ type $$ComponentProps = {
104
105
  skew?: boolean;
105
106
  showTickMarks?: boolean;
106
107
  showGridlines?: boolean;
108
+ labelFormatter?: Function;
107
109
  };
@@ -53,6 +53,7 @@
53
53
  .axis {
54
54
  display: flex;
55
55
  justify-content: space-between;
56
+ margin-bottom: 0;
56
57
  }
57
58
 
58
59
  .left-label,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@communitiesuk/svelte-component-library",
3
- "version": "0.1.19-beta.20",
3
+ "version": "0.1.19-beta.21",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/communitiesuk/mhclg_svelte_component_library.git"