@swr-data-lab/components 1.12.5 → 1.13.0

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.
package/package.json CHANGED
@@ -67,7 +67,7 @@
67
67
  "svelte": "./src/index.js"
68
68
  }
69
69
  },
70
- "version": "1.12.5",
70
+ "version": "1.13.0",
71
71
  "overrides": {
72
72
  "storybook": "$storybook"
73
73
  }
@@ -0,0 +1,13 @@
1
+ import { Story, Meta, Primary, Controls, Stories } from '@storybook/addon-docs/blocks';
2
+
3
+ import * as ChartListStories from './ChartList.stories.svelte';
4
+
5
+ <Meta of={ChartListStories} />
6
+
7
+ # Chart List
8
+
9
+ Utility component for displaying a list of charts with associated embed URLs during development (typically on the `/` route of your Svelte project).
10
+
11
+ <Stories />
12
+
13
+ <Controls />
@@ -0,0 +1,48 @@
1
+ <script module>
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import { within, expect } from 'storybook/test';
4
+
5
+ import DesignTokens from '../DesignTokens/DesignTokens.svelte';
6
+
7
+ import ChartList from './ChartList.svelte';
8
+
9
+ const { Story } = defineMeta({
10
+ title: 'Meta/ChartList',
11
+ component: ChartList
12
+ });
13
+
14
+ const testCharts = [
15
+ { title: 'Baden-Württemberg Loosers', slug: 'bw-loosers' },
16
+ { title: 'Baden-Württemberg Winners', slug: 'bw-winners' },
17
+ { title: 'Rheinland-Pfalz Loosers', slug: 'rp-loosers' },
18
+ { title: 'Rheinland-Pfalz Winners', slug: 'rp-winners' }
19
+ ];
20
+ </script>
21
+
22
+ <Story
23
+ name="Default"
24
+ asChild
25
+ play={async ({ canvasElement, step }) => {
26
+ const canvas = within(canvasElement);
27
+
28
+ await step('Project title renders', async () => {
29
+ const titleEl = canvas.getByTestId('chartlist-project-title');
30
+ expect(titleEl).toHaveTextContent('Grafiken für p110: Wie sieht der Wald von morgen aus?');
31
+ });
32
+
33
+ await step('All chart list items render', async () => {
34
+ testCharts.forEach((c) => {
35
+ const el = canvas.getByText(c.title);
36
+ expect(el).toBeTruthy();
37
+ });
38
+ });
39
+ }}
40
+ >
41
+ <DesignTokens>
42
+ <ChartList
43
+ baseUrl="https://static.datenhub.net/apps/p110_wald-klimawandel/main"
44
+ charts={testCharts}
45
+ project="p110: Wie sieht der Wald von morgen aus?"
46
+ />
47
+ </DesignTokens>
48
+ </Story>
@@ -0,0 +1,117 @@
1
+ <script lang="ts">
2
+ import { dev } from '$app/environment';
3
+
4
+ type ProjectPrefix = 'p' | 't';
5
+ type ProjectIdentifier = `${ProjectPrefix}${number}: ${string}`;
6
+
7
+ interface ChartSpec {
8
+ title: string;
9
+ slug: string;
10
+ }
11
+ interface ChartListProps {
12
+ project?: ProjectIdentifier;
13
+ charts?: ChartSpec[];
14
+ baseUrl?: string;
15
+ }
16
+ let { project, charts, baseUrl }: ChartListProps = $props();
17
+ </script>
18
+
19
+ <main>
20
+ <div class="inner">
21
+ <h1 data-testid="chartlist-project-title">Grafiken für {project}</h1>
22
+ {#if charts}
23
+ <table>
24
+ <thead>
25
+ <tr>
26
+ <th>Title</th>
27
+ {#if baseUrl}
28
+ <th>Embed URL</th>
29
+ {/if}
30
+ </tr>
31
+ </thead>
32
+ <tbody>
33
+ {#each charts as chart}
34
+ <tr>
35
+ <td>
36
+ <a href="./{chart.slug}{dev ? '' : '.html'}">{chart.title}</a>
37
+ </td>
38
+ {#if baseUrl}
39
+ <td>
40
+ <input type="text" value={`${baseUrl}/${chart.slug}.html`} />
41
+ </td>
42
+ {/if}
43
+ </tr>
44
+ {/each}
45
+ </tbody>
46
+ </table>
47
+ {/if}
48
+ </div>
49
+ </main>
50
+
51
+ <style>
52
+ :global(*) {
53
+ margin: 0;
54
+ padding: 0;
55
+ box-sizing: border-box;
56
+ color: inherit;
57
+ }
58
+ main {
59
+ display: flex;
60
+ justify-content: center;
61
+ align-items: center;
62
+ flex-flow: column;
63
+ font-family: var(--swr-sans);
64
+ font-size: var(--fs-small-1);
65
+ max-width: 60rem;
66
+ margin: 0 auto;
67
+ }
68
+ .inner {
69
+ width: 100%;
70
+ border: 1px solid rgb(0, 0, 0);
71
+ }
72
+ h1 {
73
+ font-size: var(--fs-small-1);
74
+ width: 100%;
75
+ border-bottom: 1px solid black;
76
+ padding-bottom: 0.2em;
77
+ background-color: rgb(233, 238, 245);
78
+ }
79
+ table {
80
+ border-collapse: collapse;
81
+ border-spacing: 0;
82
+ width: 100%;
83
+ }
84
+ a {
85
+ display: block;
86
+ text-decoration: none;
87
+ }
88
+ th,
89
+ td,
90
+ h1 {
91
+ padding: 0.2em 0.4em;
92
+ text-align: left;
93
+ }
94
+ th {
95
+ border-bottom: 1px solid black;
96
+ }
97
+ tr {
98
+ border-bottom: 1px solid black;
99
+ &:last-child {
100
+ border-bottom: 0;
101
+ }
102
+ }
103
+
104
+ input {
105
+ display: block;
106
+ width: 100%;
107
+ padding: 0.1em 0.3em;
108
+ }
109
+
110
+ a:hover,
111
+ a:focus-visible {
112
+ text-decoration: underline;
113
+ }
114
+ a:last-child {
115
+ border-bottom: 0;
116
+ }
117
+ </style>
@@ -0,0 +1,2 @@
1
+ import ChartList from './ChartList.svelte';
2
+ export default ChartList;
package/src/Intro.mdx CHANGED
@@ -1,7 +1,7 @@
1
- import { Meta } from "@storybook/addon-docs/blocks";
1
+ import { Meta } from '@storybook/addon-docs/blocks';
2
2
 
3
3
  <Meta title="About" />
4
4
 
5
5
  # SWR Data Lab Components
6
6
 
7
- Experimental component library for SWR Data Lab interactives.
7
+ Experimental component library for SWR Data Lab interactives.
package/src/app.d.ts CHANGED
@@ -10,4 +10,4 @@ declare global {
10
10
  }
11
11
  }
12
12
 
13
- export { };
13
+ export {};
package/src/index.js CHANGED
@@ -39,3 +39,6 @@ export { default as Select } from './Select/Select.svelte';
39
39
 
40
40
  // Deprecated
41
41
  export { default as Autocomplete } from './Autocomplete/Autocomplete.svelte';
42
+
43
+ // Meta
44
+ export { default as ChartList } from './ChartList/ChartList.svelte';
@@ -4,6 +4,10 @@
4
4
  FillLayoutProps,
5
5
  FillPaintProps,
6
6
  LineLayoutProps,
7
+ CircleLayoutProps,
8
+ CirclePaintProps,
9
+ SymbolPaintProps,
10
+ SymbolLayoutProps,
7
11
  LinePaintProps,
8
12
  MapGeoJSONFeature,
9
13
  MapLayerMouseEvent
@@ -22,8 +26,8 @@
22
26
  visible?: boolean;
23
27
  minZoom?: number;
24
28
  maxZoom?: number;
25
- paint?: LinePaintProps | FillPaintProps;
26
- layout?: LineLayoutProps | FillLayoutProps;
29
+ paint?: LinePaintProps | FillPaintProps | CirclePaintProps | SymbolPaintProps;
30
+ layout?: LineLayoutProps | FillLayoutProps | CircleLayoutProps | SymbolLayoutProps;
27
31
  hovered?: MapGeoJSONFeature | undefined;
28
32
  selected?: MapGeoJSONFeature | undefined;
29
33
 
@@ -1,17 +1,17 @@
1
- import { timeFormat } from "d3-time-format"
1
+ import { timeFormat } from 'd3-time-format';
2
2
 
3
3
  const formats = {
4
- dayMonthYear: "%-d.%-m.%Y",
5
- dayMonth: "%-d.%-m.",
6
- dayMonthYearShort: "%-d.%-m.%y",
7
- dayMonthHourMinute: "%-d.%-m., %-H.%M Uhr",
8
- dayMonthYearHourMinute: "%-d.%-m.%Y, %-H.%M Uhr",
9
- hourMinuteSophora: "%-H:%M Uhr",
10
- hourMinute: "%-H.%M",
11
- }
4
+ dayMonthYear: '%-d.%-m.%Y',
5
+ dayMonth: '%-d.%-m.',
6
+ dayMonthYearShort: '%-d.%-m.%y',
7
+ dayMonthHourMinute: '%-d.%-m., %-H.%M Uhr',
8
+ dayMonthYearHourMinute: '%-d.%-m.%Y, %-H.%M Uhr',
9
+ hourMinuteSophora: '%-H:%M Uhr',
10
+ hourMinute: '%-H.%M'
11
+ };
12
12
 
13
- const formatDate = ({ date, format = "dayMonthYear" }) => {
14
- return timeFormat(formats[format] || format)(new Date(date))
15
- }
13
+ const formatDate = ({ date, format = 'dayMonthYear' }) => {
14
+ return timeFormat(formats[format] || format)(new Date(date));
15
+ };
16
16
 
17
- export default formatDate
17
+ export default formatDate;
@@ -2,12 +2,12 @@ import { formatLocale } from 'd3-format';
2
2
 
3
3
  export const defaultFormat = ',.1f';
4
4
  export const defaultLocale = {
5
- decimal: ',',
6
- thousands: ' ',
7
- grouping: [3],
8
- currency: ['', '€']
5
+ decimal: ',',
6
+ thousands: ' ',
7
+ grouping: [3],
8
+ currency: ['', '€']
9
9
  };
10
10
 
11
11
  export default formatNumber = ({ number, format = defaultFormat, locale = defaultLocale }) => {
12
- return formatLocale(locale).format(format)(number);
12
+ return formatLocale(locale).format(format)(number);
13
13
  };
@@ -9,17 +9,17 @@ import { range } from 'd3-array';
9
9
  * @returns
10
10
  */
11
11
  const getColorsBetween = (from, to, props = { n: 1, includeFromTo: false }) => {
12
- const step = 1 / (props.n + 1);
13
- const fromColor = Color(from);
14
- const toColor = Color(to);
15
- const colorsBetween = range(1, props.n + 1)
16
- .map((i) => fromColor.mix(toColor, i * step))
17
- .map((c) => c.hex().toString());
18
- if (props.includeFromTo) {
19
- return [from, ...colorsBetween, to];
20
- } else {
21
- return colorsBetween;
22
- }
12
+ const step = 1 / (props.n + 1);
13
+ const fromColor = Color(from);
14
+ const toColor = Color(to);
15
+ const colorsBetween = range(1, props.n + 1)
16
+ .map((i) => fromColor.mix(toColor, i * step))
17
+ .map((c) => c.hex().toString());
18
+ if (props.includeFromTo) {
19
+ return [from, ...colorsBetween, to];
20
+ } else {
21
+ return colorsBetween;
22
+ }
23
23
  };
24
24
 
25
25
  export default getColorsBetween;
@@ -1,9 +1,9 @@
1
1
  const getComparisonDiffs = (a, b) => {
2
- return {
3
- diff: a - b,
4
- diffRel: (a / b - 1) * 100,
5
- diffTag: a > b ? 'higher' : a === b ? 'equal' : 'smaller'
6
- };
2
+ return {
3
+ diff: a - b,
4
+ diffRel: (a / b - 1) * 100,
5
+ diffTag: a > b ? 'higher' : a === b ? 'equal' : 'smaller'
6
+ };
7
7
  };
8
8
 
9
9
  export default getComparisonDiffs;
@@ -7,29 +7,29 @@ import { range } from 'd3-array';
7
7
  * @returns geojson
8
8
  */
9
9
  const getLaenderFromTopo = (topo) => {
10
- const states = [];
11
- const stateIds = range(1, 17).map((n) => String(n).padStart(2, '0'));
12
- const key = Object.keys(topo.objects)[0];
10
+ const states = [];
11
+ const stateIds = range(1, 17).map((n) => String(n).padStart(2, '0'));
12
+ const key = Object.keys(topo.objects)[0];
13
13
 
14
- stateIds.forEach((id) => {
15
- const state = {
16
- geometry: merge(
17
- topo,
18
- topo.objects[key].geometries.filter(
19
- (g) => g.properties.id && g.properties.id.startsWith(id)
20
- )
21
- ),
22
- id,
23
- type: 'Feature',
24
- properties: { id }
25
- };
26
- states.push(state);
27
- });
14
+ stateIds.forEach((id) => {
15
+ const state = {
16
+ geometry: merge(
17
+ topo,
18
+ topo.objects[key].geometries.filter(
19
+ (g) => g.properties.id && g.properties.id.startsWith(id)
20
+ )
21
+ ),
22
+ id,
23
+ type: 'Feature',
24
+ properties: { id }
25
+ };
26
+ states.push(state);
27
+ });
28
28
 
29
- return {
30
- type: 'FeatureCollection',
31
- features: states
32
- };
29
+ return {
30
+ type: 'FeatureCollection',
31
+ features: states
32
+ };
33
33
  };
34
34
 
35
35
  export default getLaenderFromTopo;
@@ -1,27 +1,27 @@
1
1
  const map = {
2
- "01": "Schleswig-Holstein",
3
- "02": "Hamburg",
4
- "03": "Niedersachsen",
5
- "04": "Bremen",
6
- "05": "Nordrhein-Westfalen",
7
- "06": "Hessen",
8
- "07": "Rheinland-Pfalz",
9
- "08": "Baden-Württemberg",
10
- "09": "Bayern",
11
- 10: "Saarland",
12
- 11: "Berlin",
13
- 12: "Brandenburg",
14
- 13: "Mecklenburg-Vorpommern",
15
- 14: "Sachsen",
16
- 15: "Sachsen-Anhalt",
17
- 16: "Thüringen",
18
- }
2
+ '01': 'Schleswig-Holstein',
3
+ '02': 'Hamburg',
4
+ '03': 'Niedersachsen',
5
+ '04': 'Bremen',
6
+ '05': 'Nordrhein-Westfalen',
7
+ '06': 'Hessen',
8
+ '07': 'Rheinland-Pfalz',
9
+ '08': 'Baden-Württemberg',
10
+ '09': 'Bayern',
11
+ 10: 'Saarland',
12
+ 11: 'Berlin',
13
+ 12: 'Brandenburg',
14
+ 13: 'Mecklenburg-Vorpommern',
15
+ 14: 'Sachsen',
16
+ 15: 'Sachsen-Anhalt',
17
+ 16: 'Thüringen'
18
+ };
19
19
 
20
20
  /**
21
21
  * Get nicename for land ags
22
22
  * @param {string} ags
23
23
  * @returns {string} nicename
24
24
  */
25
- const getLaenderNicenameFromAgs = (ags) => map[ags]
25
+ const getLaenderNicenameFromAgs = (ags) => map[ags];
26
26
 
27
- export default getLaenderNicenameFromAgs
27
+ export default getLaenderNicenameFromAgs;
@@ -7,7 +7,7 @@ import { SvelteComponent } from 'svelte';
7
7
  */
8
8
 
9
9
  const isSvelteComponent = (component) => {
10
- return SvelteComponent.isPrototypeOf(component) || typeof component === 'function';
10
+ return SvelteComponent.isPrototypeOf(component) || typeof component === 'function';
11
11
  };
12
12
 
13
13
  export default isSvelteComponent;
@@ -1,31 +1,37 @@
1
1
  const SELECTORS = {
2
- showScrollUp: "a.back-to-top",
3
- showPlayerbar: "#playerbar",
4
- showSharing: ".sharing",
5
- showAuthors: ".meta-top .meta-authors",
6
- allowZoom: "meta[name=viewport]",
7
- }
2
+ showScrollUp: 'a.back-to-top',
3
+ showPlayerbar: '#playerbar',
4
+ showSharing: '.sharing',
5
+ showAuthors: '.meta-top .meta-authors',
6
+ allowZoom: 'meta[name=viewport]'
7
+ };
8
8
 
9
- const IS_MOBILE = window.innerWidth < 768
9
+ const IS_MOBILE = window.innerWidth < 768;
10
10
 
11
11
  const prepareSophoraModel = (config) => {
12
- console.log("###SWRDATA### updating dom with following config: ", config)
13
- Object.entries(config).forEach(([k, v], _) => {
14
- const element = document.querySelector(SELECTORS[k])
15
- // show/hide elements
16
- if (k.startsWith("show")) {
17
- if ((v === "none" || (v === "mobile" && IS_MOBILE) || (v === "desktop" && !IS_MOBILE)) && element) {
18
- element.style.display = "none"
19
- element.style.visibility = "hidden"
20
- }
21
- }
22
- // enable/disable zoom
23
- if (k === "allowZoom") {
24
- if (!v && element) {
25
- element.setAttribute("content", "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0")
26
- }
27
- }
28
- })
29
- }
12
+ console.log('###SWRDATA### updating dom with following config: ', config);
13
+ Object.entries(config).forEach(([k, v], _) => {
14
+ const element = document.querySelector(SELECTORS[k]);
15
+ // show/hide elements
16
+ if (k.startsWith('show')) {
17
+ if (
18
+ (v === 'none' || (v === 'mobile' && IS_MOBILE) || (v === 'desktop' && !IS_MOBILE)) &&
19
+ element
20
+ ) {
21
+ element.style.display = 'none';
22
+ element.style.visibility = 'hidden';
23
+ }
24
+ }
25
+ // enable/disable zoom
26
+ if (k === 'allowZoom') {
27
+ if (!v && element) {
28
+ element.setAttribute(
29
+ 'content',
30
+ 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0'
31
+ );
32
+ }
33
+ }
34
+ });
35
+ };
30
36
 
31
- export default prepareSophoraModel
37
+ export default prepareSophoraModel;
@@ -6,9 +6,9 @@ const OFFSET = -50;
6
6
  * @param {number} offset
7
7
  */
8
8
  const scrollIntoViewWithOffset = (ref, offset) => {
9
- const yOffset = offset !== 'undefined' ? offset : OFFSET;
10
- const y = ref.getBoundingClientRect().top + window.pageYOffset + yOffset;
11
- window.scrollTo({ top: y, behavior: 'smooth' });
9
+ const yOffset = offset !== 'undefined' ? offset : OFFSET;
10
+ const y = ref.getBoundingClientRect().top + window.pageYOffset + yOffset;
11
+ window.scrollTo({ top: y, behavior: 'smooth' });
12
12
  };
13
13
 
14
14
  export default scrollIntoViewWithOffset;
@@ -1,12 +1,12 @@
1
1
  // Source: https://svelte.dev/playground/b130be5e485441a1842ae97e4ce4f244?version=5.20.0
2
2
 
3
3
  export default function slugify(str: string): string {
4
- return str
5
- .replace(/\s+/g, '-')
6
- .replace(/-+/g, '-')
7
- .trim()
8
- .normalize('NFKD')
9
- .replace(/[\u0300-\u036f]/g, '')
10
- .toLowerCase()
11
- .replace(/[^a-z0-9 -]/g, '')
4
+ return str
5
+ .replace(/\s+/g, '-')
6
+ .replace(/-+/g, '-')
7
+ .trim()
8
+ .normalize('NFKD')
9
+ .replace(/[\u0300-\u036f]/g, '')
10
+ .toLowerCase()
11
+ .replace(/[^a-z0-9 -]/g, '');
12
12
  }
@@ -6,8 +6,8 @@ import { feature } from 'topojson-client';
6
6
  * @returns {object} geojson
7
7
  */
8
8
  const topoToGeo = (topojson) => {
9
- const key = Object.keys(topojson.objects)[0];
10
- return feature(topojson, topojson.objects[key]);
9
+ const key = Object.keys(topojson.objects)[0];
10
+ return feature(topojson, topojson.objects[key]);
11
11
  };
12
12
 
13
13
  export default topoToGeo;