@onsvisual/svelte-components 1.0.0-next.6 → 1.0.0-next.7

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.
@@ -3,6 +3,7 @@
3
3
  import { withComponentDocs } from "../../js/withParams.js";
4
4
  import DataTile from "./DataTile.svelte";
5
5
  import componentDocs from "./docs/component.md?raw";
6
+ import data from "../demo-data/data-scatter.js";
6
7
 
7
8
  const { Story } = defineMeta({
8
9
  title: "Data visualisation/DataTile",
@@ -26,3 +27,16 @@
26
27
  source: "Source: ONS"
27
28
  }}
28
29
  />
30
+
31
+ <Story
32
+ name="Sparkline"
33
+ args={{
34
+ title: "Example sparkline",
35
+ subtitle: "Value in £ million",
36
+ mode: "sparkline",
37
+ data: data.map((d) => ({ x: d.year, y: d.value })),
38
+ value: "down 2.47",
39
+ caption: `Change from 1979 to 2016`,
40
+ source: "Source: ONS"
41
+ }}
42
+ />
@@ -1,11 +1,17 @@
1
1
  <script>
2
2
  import Card from "../../layout/Card/Card.svelte";
3
+ import Sparkline from "./Sparkline.svelte";
3
4
 
4
5
  /**
5
6
  * Title of card
6
7
  * @type {string}
7
8
  */
8
9
  export let title = "";
10
+ /**
11
+ * Optional: Subtitle of card
12
+ * @type {string}
13
+ */
14
+ export let subtitle = "";
9
15
  /**
10
16
  * Optional: URL link for card title
11
17
  * @type {string}
@@ -44,13 +50,21 @@
44
50
  </script>
45
51
 
46
52
  <Card {title} {href} mode="featured" {cls}>
53
+ {#if subtitle}
54
+ <p class="ons-u-fs-s">{subtitle}</p>
55
+ {/if}
56
+ {#if mode === "sparkline" && data}
57
+ <Sparkline {data} />
58
+ {/if}
47
59
  {#if value}
48
- <p class="headline-figures__figure ons-u-fs-3xl ons-u-fs-2xl@m">{value}</p>
60
+ <p class="headline-figures__figure ons-u-fs-3xl ons-u-fs-2xl@m ons-u-mb-no">{value}</p>
49
61
  {/if}
50
62
  {#if caption}
51
- <p class="headline-figures__supporting-text ons-u-fs-r">{@html caption}</p>
63
+ <p class="headline-figures__supporting-text ons-u-fs-r ons-u-mt-2xs ons-u-mb-no">
64
+ {@html caption}
65
+ </p>
52
66
  {/if}
53
67
  {#if source}
54
- <p class="ons-u-fs-s">{source}</p>
68
+ <p class="ons-u-fs-s ons-u-mt-s ons-u-mb-no">{source}</p>
55
69
  {/if}
56
70
  </Card>
@@ -10,6 +10,7 @@ export default class DataTile extends SvelteComponentTyped<{
10
10
  href?: string | undefined;
11
11
  mode?: "number" | "sparkline" | "bar" | undefined;
12
12
  value?: string | number | undefined;
13
+ subtitle?: string | undefined;
13
14
  }, {
14
15
  [evt: string]: CustomEvent<any>;
15
16
  }, {}> {
@@ -28,6 +29,7 @@ declare const __propDef: {
28
29
  href?: string | undefined;
29
30
  mode?: "number" | "sparkline" | "bar" | undefined;
30
31
  value?: string | number | undefined;
32
+ subtitle?: string | undefined;
31
33
  };
32
34
  events: {
33
35
  [evt: string]: CustomEvent<any>;
@@ -0,0 +1,117 @@
1
+ <script>
2
+ import { scaleLinear } from "d3-scale";
3
+ import { line } from "d3-shape";
4
+
5
+ /**
6
+ * Array of data in the format [{x: value, y: value}, etc]
7
+ * @type {object[]}
8
+ */
9
+ export let data;
10
+ /**
11
+ * Key for the X values
12
+ * @type {string}
13
+ */
14
+ export let xKey = "x";
15
+ /**
16
+ * Key for the Y values
17
+ * @type {string}
18
+ */
19
+ export let yKey = "y";
20
+
21
+ function minMax(data, key) {
22
+ const values = data.map((d) => d[key]);
23
+ return [Math.min(...values), Math.max(...values)];
24
+ }
25
+
26
+ const xDomain = [data[0][xKey], data[data.length - 1][xKey]];
27
+ const yDomain = minMax(data, yKey);
28
+
29
+ const xScale = scaleLinear().domain(xDomain).range([0, 100]);
30
+ const yScale = scaleLinear().domain(yDomain).range([100, 0]);
31
+ const lineFn = line()
32
+ .x((d) => xScale(d[xKey]))
33
+ .y((d) => yScale(d[yKey]));
34
+ </script>
35
+
36
+ <div class="sparkline-container">
37
+ <div
38
+ class="sparkline-point"
39
+ style={[data[data.length - 1]].map((d) => `right: 0; top: ${yScale(d[yKey])}%;`)[0]}
40
+ ></div>
41
+ <svg class="sparkline-chart" viewBox="0 0 100 100" preserveAspectRatio="none">
42
+ <path class="sparkline" d={lineFn(data)} />
43
+ </svg>
44
+ </div>
45
+ <div class="x-ticks">
46
+ {#each [data[0], data[data.length - 1]] as tick, i}
47
+ <div class="x-tick x-tick--{i === 0 ? 'start' : 'end'}">
48
+ <span class="ons-u-fs-s">{tick[xKey]}</span><br /><span class="x-tick--value"
49
+ >{tick[yKey]}</span
50
+ >
51
+ </div>
52
+ {/each}
53
+ </div>
54
+
55
+ <style>
56
+ .sparkline-container {
57
+ display: block;
58
+ height: 100px;
59
+ width: 100%;
60
+ position: relative;
61
+ }
62
+ .sparkline-chart {
63
+ display: block;
64
+ position: absolute;
65
+ height: 100%;
66
+ width: 100%;
67
+ overflow: visible;
68
+ }
69
+ .sparkline-point {
70
+ position: absolute;
71
+ display: inline-block;
72
+ width: 10px;
73
+ height: 10px;
74
+ border-radius: 50%;
75
+ background: var(--ons-color-branded);
76
+ transform: translate(50%, -50%);
77
+ }
78
+ .sparkline {
79
+ fill: none;
80
+ stroke: var(--ons-color-branded);
81
+ stroke-width: 2.5px;
82
+ vector-effect: non-scaling-stroke;
83
+ }
84
+ .x-ticks {
85
+ display: flex;
86
+ width: 100%;
87
+ flex-direction: row;
88
+ justify-content: space-between;
89
+ margin-bottom: 6px;
90
+ }
91
+ .x-tick {
92
+ position: relative;
93
+ padding-top: 10px;
94
+ line-height: 1.1;
95
+ }
96
+ .x-tick--value {
97
+ color: var(--ons-color-branded);
98
+ font-size: 1.25rem;
99
+ font-weight: bold;
100
+ }
101
+ .x-tick::before {
102
+ position: absolute;
103
+ content: " ";
104
+ height: 10px;
105
+ top: 0;
106
+ }
107
+ .x-tick--end {
108
+ text-align: right;
109
+ }
110
+ .x-tick--start::before {
111
+ border-left: 1px solid var(--ons-color-borders);
112
+ }
113
+ .x-tick--end::before {
114
+ right: 0;
115
+ border-right: 1px solid var(--ons-color-borders);
116
+ }
117
+ </style>
@@ -0,0 +1,27 @@
1
+ /** @typedef {typeof __propDef.props} SparklineProps */
2
+ /** @typedef {typeof __propDef.events} SparklineEvents */
3
+ /** @typedef {typeof __propDef.slots} SparklineSlots */
4
+ export default class Sparkline extends SvelteComponentTyped<{
5
+ data: object[];
6
+ xKey?: string | undefined;
7
+ yKey?: string | undefined;
8
+ }, {
9
+ [evt: string]: CustomEvent<any>;
10
+ }, {}> {
11
+ }
12
+ export type SparklineProps = typeof __propDef.props;
13
+ export type SparklineEvents = typeof __propDef.events;
14
+ export type SparklineSlots = typeof __propDef.slots;
15
+ import { SvelteComponentTyped } from "svelte";
16
+ declare const __propDef: {
17
+ props: {
18
+ data: object[];
19
+ xKey?: string | undefined;
20
+ yKey?: string | undefined;
21
+ };
22
+ events: {
23
+ [evt: string]: CustomEvent<any>;
24
+ };
25
+ slots: {};
26
+ };
27
+ export {};
@@ -0,0 +1,25 @@
1
+ <script module>
2
+ import { defineMeta } from "@storybook/addon-svelte-csf";
3
+ import { withComponentDocs } from "../../js/withParams.js";
4
+ import Icon from "./Icon.svelte";
5
+ import componentDocs from "./docs/component.md?raw";
6
+
7
+ const { Story } = defineMeta({
8
+ title: "Decorators/Icon",
9
+ component: Icon,
10
+ tags: ["autodocs"],
11
+ argTypes: {
12
+ type: {
13
+ control: { type: "select" }
14
+ },
15
+ size: {
16
+ control: { type: "select" }
17
+ }
18
+ },
19
+ parameters: withComponentDocs(componentDocs)
20
+ });
21
+ </script>
22
+
23
+ <Story name="Arrow icon" args={{ type: "arrow" }} />
24
+
25
+ <Story name="Search icon, large" args={{ type: "search", size: "3xl" }} />
@@ -2,9 +2,7 @@
2
2
  /** @typedef {typeof __propDef.events} IconEvents */
3
3
  /** @typedef {typeof __propDef.slots} IconSlots */
4
4
  export default class Icon extends SvelteComponentTyped<{
5
- type?: string | undefined;
6
- marginLeft?: boolean | undefined;
7
- marginRight?: boolean | undefined;
5
+ [x: string]: never;
8
6
  }, {
9
7
  [evt: string]: CustomEvent<any>;
10
8
  }, {}> {
@@ -15,9 +13,7 @@ export type IconSlots = typeof __propDef.slots;
15
13
  import { SvelteComponentTyped } from "svelte";
16
14
  declare const __propDef: {
17
15
  props: {
18
- type?: string | undefined;
19
- marginLeft?: boolean | undefined;
20
- marginRight?: boolean | undefined;
16
+ [x: string]: never;
21
17
  };
22
18
  events: {
23
19
  [evt: string]: CustomEvent<any>;
@@ -1,6 +1,23 @@
1
1
  <script>
2
+ /**
3
+ * Set the type of icon
4
+ * @type {"arrow"|"external"|"signout"|"print"|"download"|"tick"|"search"}
5
+ */
2
6
  export let type = "arrow";
7
+ /**
8
+ * Set the size of the icon
9
+ * @type {"s"|"m"|"l"|"xl"|"2xl"|"3xl"|null}
10
+ */
11
+ export let size = null;
12
+ /**
13
+ * Add a small margin on the left of the icon
14
+ * @type {boolean}
15
+ */
3
16
  export let marginLeft = false;
17
+ /**
18
+ * Add a small margin on the right of the icon
19
+ * @type {boolean}
20
+ */
4
21
  export let marginRight = false;
5
22
 
6
23
  const paths = {
@@ -8,13 +25,13 @@
8
25
  d: "m10 .2-.9.9c-.1.1-.1.4 0 .5l4 4H.6c-.2 0-.4.2-.4.4v1.2c0 .2.2.4.4.4h12.5l-3.9 3.7c-.2.2-.2.4 0 .6l.8.9c.2.2.4.2.6 0L16.8 7c.2-.2.2-.4 0-.6L10.7.3c-.3-.2-.5-.2-.7-.1z",
9
26
  viewBox: "0 0 17 13"
10
27
  },
11
- externalLink: {
28
+ external: {
12
29
  d: "M13.5,9H13a.5.5,0,0,0-.5.5v3h-9v-9h3A.5.5,0,0,0,7,3V2.5A.5.5,0,0,0,6.5,2h-4a.5.5,0,0,0-.5.5v11a.5.5,0,0,0,.5.5h11a.5.5,0,0,0,.5-.5v-4A.5.5,0,0,0,13.5,9Z M8.83,7.88a.51.51,0,0,0,.71,0l2.31-2.32,1.28,1.28A.51.51,0,0,0,14,6.49v-4a.52.52,0,0,0-.5-.5h-4A.51.51,0,0,0,9,2.52a.58.58,0,0,0,.14.33l1.28,1.28L8.12,6.46a.51.51,0,0,0,0,.71Z",
13
30
  viewBox: "2 2 12 12"
14
31
  },
15
32
  signout: {
16
33
  d: "M13.85,7.65l-2.5-2.5a.5.5,0,0,0-.71,0,.48.48,0,0,0-.15.36V7h-3a.5.5,0,0,0-.5.5v1a.5.5,0,0,0,.5.5h3v1.5A.49.49,0,0,0,11,11a.48.48,0,0,0,.34-.14l2.51-2.5a.49.49,0,0,0,0-.68Z M8.5,14h-6a.5.5,0,0,1-.5-.5V2.5A.5.5,0,0,1,2.5,2h6a.5.5,0,0,1,.5.5V3a.5.5,0,0,1-.5.5h-5v9h5A.5.5,0,0,1,9,13v.5A.5.5,0,0,1,8.5,14Z",
17
- viewBox: "0 0 12 12"
34
+ viewBox: "2 2 12 12"
18
35
  },
19
36
  print: {
20
37
  d: "M17 4H3C1.3 4 0 5.2 0 6.8v5.5h4V16h12v-3.7h4V6.8C20 5.2 18.7 4 17 4zm-3 10H6V9h8v5zm3-6a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-1-8H4v3h12V0z",
@@ -37,7 +54,7 @@
37
54
 
38
55
  {#if paths[type]}
39
56
  <svg
40
- class="ons-icon"
57
+ class="ons-icon {size ? `ons-icon--${size}` : ''}"
41
58
  class:ons-u-ml-2xs={marginLeft}
42
59
  class:ons-u-mr-2xs={marginRight}
43
60
  viewBox={paths[type].viewBox}
@@ -0,0 +1,29 @@
1
+ /** @typedef {typeof __propDef.props} IconProps */
2
+ /** @typedef {typeof __propDef.events} IconEvents */
3
+ /** @typedef {typeof __propDef.slots} IconSlots */
4
+ export default class Icon extends SvelteComponentTyped<{
5
+ type?: "search" | "arrow" | "external" | "signout" | "print" | "download" | "tick" | undefined;
6
+ size?: "s" | "m" | "l" | "xl" | "2xl" | "3xl" | null | undefined;
7
+ marginLeft?: boolean | undefined;
8
+ marginRight?: boolean | undefined;
9
+ }, {
10
+ [evt: string]: CustomEvent<any>;
11
+ }, {}> {
12
+ }
13
+ export type IconProps = typeof __propDef.props;
14
+ export type IconEvents = typeof __propDef.events;
15
+ export type IconSlots = typeof __propDef.slots;
16
+ import { SvelteComponentTyped } from "svelte";
17
+ declare const __propDef: {
18
+ props: {
19
+ type?: "search" | "arrow" | "external" | "signout" | "print" | "download" | "tick" | undefined;
20
+ size?: "s" | "m" | "l" | "xl" | "2xl" | "3xl" | null | undefined;
21
+ marginLeft?: boolean | undefined;
22
+ marginRight?: boolean | undefined;
23
+ };
24
+ events: {
25
+ [evt: string]: CustomEvent<any>;
26
+ };
27
+ slots: {};
28
+ };
29
+ export {};
@@ -0,0 +1,12 @@
1
+ A component to visually highlight text. Can be used for inline text or to provide a "badge".
2
+
3
+ Consists of a `<mark>` element with a custom background color, where the highlighted text will automatically appear black or white to maximise contrast.
4
+
5
+ <!-- prettier-ignore -->
6
+ ```html
7
+ <script>
8
+ import { Em } from "@onsvisual/svelte-components";
9
+ </script>
10
+
11
+ <Em color="steelblue">Highlighted text</Em>
12
+ ```
@@ -1,6 +1,6 @@
1
1
  <script>
2
2
  import { createEventDispatcher } from "svelte";
3
- import Icon from "./Icon.svelte";
3
+ import Icon from "../../decorators/Icon/Icon.svelte";
4
4
 
5
5
  const dispatch = createEventDispatcher();
6
6
 
@@ -26,7 +26,7 @@
26
26
  export let small = false;
27
27
  /**
28
28
  * Icon on button, eg. "arrow", "search"
29
- * @type {string}
29
+ * @type {"arrow"|"external"|"signout"|"print"|"download"|"tick"|"search"}
30
30
  */
31
31
  export let icon = "";
32
32
  /**
@@ -5,14 +5,14 @@ export default class Button extends SvelteComponentTyped<{
5
5
  cls?: string | undefined;
6
6
  small?: boolean | undefined;
7
7
  href?: string | undefined;
8
+ download?: string | undefined;
8
9
  type?: "button" | "reset" | "sumbit" | undefined;
9
10
  variant?: "secondary" | "primary" | "ghost" | undefined;
10
- icon?: string | undefined;
11
+ icon?: "search" | "arrow" | "external" | "signout" | "print" | "download" | "tick" | undefined;
11
12
  iconPosition?: "before" | "after" | undefined;
12
13
  disabled?: boolean | undefined;
13
14
  hideLabel?: boolean | undefined;
14
15
  arialabel?: string | undefined;
15
- download?: string | undefined;
16
16
  }, {
17
17
  click: CustomEvent<any>;
18
18
  } & {
@@ -31,14 +31,14 @@ declare const __propDef: {
31
31
  cls?: string | undefined;
32
32
  small?: boolean | undefined;
33
33
  href?: string | undefined;
34
+ download?: string | undefined;
34
35
  type?: "button" | "reset" | "sumbit" | undefined;
35
36
  variant?: "secondary" | "primary" | "ghost" | undefined;
36
- icon?: string | undefined;
37
+ icon?: "search" | "arrow" | "external" | "signout" | "print" | "download" | "tick" | undefined;
37
38
  iconPosition?: "before" | "after" | undefined;
38
39
  disabled?: boolean | undefined;
39
40
  hideLabel?: boolean | undefined;
40
41
  arialabel?: string | undefined;
41
- download?: string | undefined;
42
42
  };
43
43
  events: {
44
44
  click: CustomEvent<any>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onsvisual/svelte-components",
3
- "version": "1.0.0-next.6",
3
+ "version": "1.0.0-next.7",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "npm run build:package && npm run build:docs",
@@ -61,6 +61,8 @@
61
61
  "@vitest/browser": "3.2.3",
62
62
  "@vitest/coverage-v8": "3.2.3",
63
63
  "csso": "^5.0.5",
64
+ "d3-scale": "^4.0.2",
65
+ "d3-shape": "^3.2.0",
64
66
  "eslint": "^9.18.0",
65
67
  "eslint-config-prettier": "^10.0.1",
66
68
  "eslint-plugin-storybook": "^9.0.11",