@rosettadash/svelte 0.1.1 → 0.1.3

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 (46) hide show
  1. package/package.json +26 -2
  2. package/src/domain/i18n/app-language-select/AppLanguageSelect.svelte +48 -0
  3. package/src/domain/i18n/app-language-select/index.d.ts +2 -0
  4. package/src/domain/i18n/app-language-select/index.js +3 -0
  5. package/src/domain/i18n/app-language-select/index.js.map +1 -0
  6. package/src/domain/i18n/app-language-select/types.d.ts +16 -0
  7. package/src/domain/i18n/app-language-select/types.js +2 -0
  8. package/src/domain/i18n/app-language-select/types.js.map +1 -0
  9. package/src/index.d.ts +4 -0
  10. package/src/layout/scroll-region/ScrollRegion.svelte +25 -0
  11. package/src/layout/scroll-region/index.d.ts +2 -0
  12. package/src/layout/scroll-region/index.js +3 -0
  13. package/src/layout/scroll-region/index.js.map +1 -0
  14. package/src/layout/scroll-region/types.d.ts +6 -0
  15. package/src/layout/scroll-region/types.js +2 -0
  16. package/src/layout/scroll-region/types.js.map +1 -0
  17. package/src/visual/chart/bar/BarChart.svelte +67 -5
  18. package/src/visual/chart/bar/index.d.ts +1 -1
  19. package/src/visual/chart/bar/types.d.ts +7 -0
  20. package/src/visual/chart/line/LineChart.svelte +91 -5
  21. package/src/visual/chart/line/index.d.ts +1 -1
  22. package/src/visual/chart/line/types.d.ts +8 -0
  23. package/src/visual/display/geo-map/GeoMap.svelte +52 -0
  24. package/src/visual/display/geo-map/index.d.ts +2 -0
  25. package/src/visual/display/geo-map/index.js +3 -0
  26. package/src/visual/display/geo-map/index.js.map +1 -0
  27. package/src/visual/display/geo-map/types.d.ts +18 -0
  28. package/src/visual/display/geo-map/types.js +2 -0
  29. package/src/visual/display/geo-map/types.js.map +1 -0
  30. package/src/visual/input/date-range/DateRangeFilter.svelte +19 -2
  31. package/src/visual/input/date-range/types.d.ts +1 -0
  32. package/src/visual/input/select/SelectInput.svelte +14 -4
  33. package/src/visual/media/youtube-embed/YoutubeEmbed.svelte +45 -0
  34. package/src/visual/media/youtube-embed/index.d.ts +2 -0
  35. package/src/visual/media/youtube-embed/index.js +3 -0
  36. package/src/visual/media/youtube-embed/index.js.map +1 -0
  37. package/src/visual/media/youtube-embed/types.d.ts +10 -0
  38. package/src/visual/media/youtube-embed/types.js +2 -0
  39. package/src/visual/media/youtube-embed/types.js.map +1 -0
  40. package/src/visual/news/language-select/NewsLanguageSelect.svelte +3 -3
  41. package/src/visual/news/region-select/NewsRegionSelect.svelte +3 -3
  42. package/src/visual/news/type-select/NewsTypeSelect.svelte +3 -3
  43. package/src/visual/table/DataTable.svelte +21 -8
  44. package/src/visual/table/types.d.ts +3 -1
  45. package/src/visual/wasm/media/WasmMedia.svelte +6 -0
  46. package/src/visual/wasm/media/types.d.ts +6 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rosettadash/svelte",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "RosettaDash Svelte 5 runtime — same import paths as @rosettadash/web-components. Storybook: github.com/KevinEverywhere/rosettadash (port 6010).",
5
5
  "author": "Kevin Ready <kevin@planetkevin.com>",
6
6
  "type": "module",
@@ -15,6 +15,12 @@
15
15
  "import": "./src/index.js",
16
16
  "default": "./src/index.js"
17
17
  },
18
+ "./domain/i18n/app-language-select": {
19
+ "types": "./src/domain/i18n/app-language-select/types.d.ts",
20
+ "svelte": "./src/domain/i18n/app-language-select/AppLanguageSelect.svelte",
21
+ "import": "./src/domain/i18n/app-language-select/AppLanguageSelect.svelte",
22
+ "default": "./src/domain/i18n/app-language-select/AppLanguageSelect.svelte"
23
+ },
18
24
  "./domain/person-invite": {
19
25
  "types": "./src/domain/person-invite/types.d.ts",
20
26
  "svelte": "./src/domain/person-invite/PersonInvite.svelte",
@@ -135,6 +141,12 @@
135
141
  "import": "./src/layout/modal/ModalLayout.svelte",
136
142
  "default": "./src/layout/modal/ModalLayout.svelte"
137
143
  },
144
+ "./layout/scroll-region": {
145
+ "types": "./src/layout/scroll-region/types.d.ts",
146
+ "svelte": "./src/layout/scroll-region/ScrollRegion.svelte",
147
+ "import": "./src/layout/scroll-region/ScrollRegion.svelte",
148
+ "default": "./src/layout/scroll-region/ScrollRegion.svelte"
149
+ },
138
150
  "./layout/tabs": {
139
151
  "types": "./src/layout/tabs/types.d.ts",
140
152
  "svelte": "./src/layout/tabs/TabsLayout.svelte",
@@ -201,6 +213,12 @@
201
213
  "import": "./src/visual/display/3d-scene/ThreeScenePointCloud.svelte",
202
214
  "default": "./src/visual/display/3d-scene/ThreeScenePointCloud.svelte"
203
215
  },
216
+ "./visual/display/geo-map": {
217
+ "types": "./src/visual/display/geo-map/types.d.ts",
218
+ "svelte": "./src/visual/display/geo-map/GeoMap.svelte",
219
+ "import": "./src/visual/display/geo-map/GeoMap.svelte",
220
+ "default": "./src/visual/display/geo-map/GeoMap.svelte"
221
+ },
204
222
  "./visual/input/checkbox": {
205
223
  "types": "./src/visual/input/checkbox/types.d.ts",
206
224
  "svelte": "./src/visual/input/checkbox/CheckboxInput.svelte",
@@ -267,6 +285,12 @@
267
285
  "import": "./src/visual/media/video-source/VideoSource.svelte",
268
286
  "default": "./src/visual/media/video-source/VideoSource.svelte"
269
287
  },
288
+ "./visual/media/youtube-embed": {
289
+ "types": "./src/visual/media/youtube-embed/types.d.ts",
290
+ "svelte": "./src/visual/media/youtube-embed/YoutubeEmbed.svelte",
291
+ "import": "./src/visual/media/youtube-embed/YoutubeEmbed.svelte",
292
+ "default": "./src/visual/media/youtube-embed/YoutubeEmbed.svelte"
293
+ },
270
294
  "./visual/news/article-detail": {
271
295
  "types": "./src/visual/news/article-detail/types.d.ts",
272
296
  "svelte": "./src/visual/news/article-detail/NewsArticleDetail.svelte",
@@ -370,7 +394,7 @@
370
394
  "LICENSE"
371
395
  ],
372
396
  "dependencies": {
373
- "@rosettadash/web-components": "0.1.1",
397
+ "@rosettadash/web-components": "0.1.3",
374
398
  "tslib": "^2.3.0"
375
399
  },
376
400
  "peerDependencies": {
@@ -0,0 +1,48 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import {
4
+ DB_APP_LANGUAGE_SELECT_TAG,
5
+ registerRdAppLanguageSelect,
6
+ } from '@rosettadash/web-components/domain/i18n/app-language-select';
7
+ import {
8
+ attachHostEvents,
9
+ setHostAttribute,
10
+ setHostProperty,
11
+ } from '../../../lib/custom-element-host';
12
+ import type { AppLanguageSelectProps } from './types';
13
+
14
+ let {
15
+ locales,
16
+ value,
17
+ defaultLocale,
18
+ label,
19
+ placeholder,
20
+ className,
21
+ onLocaleChange,
22
+ }: AppLanguageSelectProps = $props();
23
+
24
+ let host: HTMLElement;
25
+ let detachEvents = () => {};
26
+
27
+ onMount(() => {
28
+ registerRdAppLanguageSelect();
29
+ detachEvents = attachHostEvents(host, {
30
+ 'locale-change': (detail) =>
31
+ onLocaleChange?.(detail as { locale: string }),
32
+ });
33
+ return () => detachEvents();
34
+ });
35
+
36
+ $effect(() => {
37
+ if (!host) {
38
+ return;
39
+ }
40
+ setHostProperty(host, 'locales', locales);
41
+ setHostAttribute(host, 'value', value);
42
+ setHostAttribute(host, 'default-locale', defaultLocale);
43
+ setHostAttribute(host, 'label', label);
44
+ setHostAttribute(host, 'placeholder', placeholder);
45
+ });
46
+ </script>
47
+
48
+ <svelte:element this={DB_APP_LANGUAGE_SELECT_TAG} bind:this={host} class={className} />
@@ -0,0 +1,2 @@
1
+ export type { AppLanguageSelectProps, AppLocaleOption } from './types';
2
+ /** Component entry is `AppLanguageSelect.svelte` (see package exports). */
@@ -0,0 +1,3 @@
1
+ export {};
2
+ /** Component entry is `AppLanguageSelect.svelte` (see package exports). */
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../packages/svelte/src/domain/i18n/app-language-select/index.ts"],"names":[],"mappings":";AACA,2EAA2E"}
@@ -0,0 +1,16 @@
1
+ export interface AppLocaleOption {
2
+ code: string;
3
+ label: string;
4
+ nativeLabel?: string;
5
+ }
6
+ export interface AppLanguageSelectProps {
7
+ locales?: AppLocaleOption[];
8
+ value?: string;
9
+ defaultLocale?: string;
10
+ label?: string;
11
+ placeholder?: string;
12
+ className?: string;
13
+ onLocaleChange?: (detail: {
14
+ locale: string;
15
+ }) => void;
16
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../../packages/svelte/src/domain/i18n/app-language-select/types.ts"],"names":[],"mappings":""}
package/src/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export type { AppLocaleOption, AppLanguageSelectProps } from './domain/i18n/app-language-select/types';
1
2
  export type { PersonInviteProps } from './domain/person-invite/types';
2
3
  export type { RoleAssignProps } from './domain/role-assign/types';
3
4
  export type { RoleGateProps } from './domain/role-gate/types';
@@ -18,6 +19,7 @@ export type { CollapsibleProps } from './layout/collapsible/types';
18
19
  export type { FlexLayoutProps } from './layout/flex/types';
19
20
  export type { GridLayoutProps } from './layout/grid/types';
20
21
  export type { ModalLayoutProps } from './layout/modal/types';
22
+ export type { ScrollRegionProps } from './layout/scroll-region/types';
21
23
  export type { TabsLayoutProps } from './layout/tabs/types';
22
24
  export type { TimerProps } from './logic/timer/types';
23
25
  export type { BarChartProps } from './visual/chart/bar/types';
@@ -29,6 +31,7 @@ export type { ThreeGeoGlobeProps } from './visual/display/3d-geo-globe/types';
29
31
  export type { ThreeGltfModelProps } from './visual/display/3d-gltf-model/types';
30
32
  export type { ThreeScatterPlotProps } from './visual/display/3d-scatter/types';
31
33
  export type { ThreeScenePointCloudProps } from './visual/display/3d-scene/types';
34
+ export type { GeoMapProps } from './visual/display/geo-map/types';
32
35
  export type { CheckboxInputProps } from './visual/input/checkbox/types';
33
36
  export type { DateRangeFilterProps } from './visual/input/date-range/types';
34
37
  export type { NumberInputProps } from './visual/input/number/types';
@@ -40,6 +43,7 @@ export type { LinkListItem, LinkListProps } from './visual/link-list/types';
40
43
  export type { EquirectPreviewMode, EquirectViewportProps } from './visual/media/equirect-viewport/types';
41
44
  export type { LiveCaptureProps } from './visual/media/live-capture/types';
42
45
  export type { VideoFileDetail, VideoSourceProps } from './visual/media/video-source/types';
46
+ export type { YoutubeEmbedProps } from './visual/media/youtube-embed/types';
43
47
  export type { NewsArticleDetailProps } from './visual/news/article-detail/types';
44
48
  export type { NewsLanguageSelectProps } from './visual/news/language-select/types';
45
49
  export type { NewsRegionSelectProps } from './visual/news/region-select/types';
@@ -0,0 +1,25 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { ScrollRegionProps } from './types';
4
+
5
+ type Props = ScrollRegionProps & { children?: Snippet };
6
+
7
+ let {
8
+ className,
9
+ title,
10
+ maxHeight,
11
+ overlayScrollbar,
12
+ children,
13
+ }: Props = $props();
14
+ const rootClass = $derived(['rd-scroll-region', className].filter(Boolean).join(' '));
15
+ </script>
16
+
17
+ <section
18
+ class={[rootClass, overlayScrollbar === false ? '' : 'rd-scroll-region--overlay-scrollbar'].filter(Boolean).join(' ')}
19
+ data-testid="rd-scroll-region"
20
+ aria-label={title ?? 'Scrollable content'}
21
+ style={maxHeight ? `max-height: ${maxHeight}` : undefined}
22
+ >
23
+ {#if title}<header class="rd-scroll-region__header">{title}</header>{/if}
24
+ <div class="rd-scroll-region__body">{@render children?.()}</div>
25
+ </section>
@@ -0,0 +1,2 @@
1
+ export type { ScrollRegionProps } from './types';
2
+ /** Component entry is `ScrollRegion.svelte` (see package exports). */
@@ -0,0 +1,3 @@
1
+ export {};
2
+ /** Component entry is `ScrollRegion.svelte` (see package exports). */
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/svelte/src/layout/scroll-region/index.ts"],"names":[],"mappings":";AACA,sEAAsE"}
@@ -0,0 +1,6 @@
1
+ export interface ScrollRegionProps {
2
+ title?: string;
3
+ maxHeight?: string;
4
+ overlayScrollbar?: boolean;
5
+ className?: string;
6
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../packages/svelte/src/layout/scroll-region/types.ts"],"names":[],"mappings":""}
@@ -1,22 +1,84 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
- import type { BarChartProps } from './types';
3
+ import type { BarChartBar, BarChartProps } from './types';
4
4
 
5
5
  type Props = BarChartProps & { children?: Snippet };
6
6
 
7
+ const FALLBACK_BARS: BarChartBar[] = [
8
+ { label: 'A', value: 40 },
9
+ { label: 'B', value: 65 },
10
+ { label: 'C', value: 55 },
11
+ { label: 'D', value: 80 },
12
+ { label: 'E', value: 48 },
13
+ ];
14
+
7
15
  let {
8
16
  className,
9
17
  title,
18
+ bars,
19
+ yAxisLabel,
20
+ valueFormat,
10
21
  children,
11
22
  }: Props = $props();
12
- const barHeights = [40, 65, 55, 80, 48];
23
+
24
+ function defaultFormat(value: number): string {
25
+ if (value >= 1_000_000) {
26
+ return `${(value / 1_000_000).toFixed(1)}M`;
27
+ }
28
+ if (value >= 1_000) {
29
+ return `${(value / 1_000).toFixed(0)}K`;
30
+ }
31
+ return String(Math.round(value));
32
+ }
33
+
13
34
  const rootClass = $derived(['rd-chart-bar', className].filter(Boolean).join(' '));
35
+ const series = $derived(bars?.length ? bars : FALLBACK_BARS);
36
+ const format = $derived(valueFormat ?? defaultFormat);
37
+ const maxValue = $derived(Math.max(...series.map((bar) => bar.value), 1));
38
+ const ticks = $derived.by(() => {
39
+ const max = maxValue;
40
+ if (max <= 0) {
41
+ return [0];
42
+ }
43
+ const count = 4;
44
+ const step = max / (count - 1);
45
+ return Array.from({ length: count }, (_, index) => step * index);
46
+ });
14
47
  </script>
15
48
 
16
- <section class={rootClass} data-testid="rd-chart-bar">
49
+ <section class={rootClass} data-testid="rd-chart-bar" role="img" aria-label={title ?? 'Bar chart'}>
17
50
  <header class="rd-chart-bar__header"><span>{title ?? 'Bar chart'}</span></header>
18
- <div class="rd-chart-bar__bars" aria-hidden="true">
19
- {#each barHeights as h, i (i)}<div class="rd-chart-bar__bar-wrap"><div class="rd-chart-bar__bar" style:height="{h}%"></div></div>{/each}
51
+ <div class="rd-chart-bar__body">
52
+ <div class="rd-chart-bar__y-axis" aria-hidden="true">
53
+ <div class="rd-chart-bar__y-ticks">
54
+ {#each [...ticks].reverse() as tick (tick)}
55
+ <span class="rd-chart-bar__y-tick">{format(tick)}</span>
56
+ {/each}
57
+ </div>
58
+ {#if yAxisLabel}<span class="rd-chart-bar__y-label">{yAxisLabel}</span>{/if}
59
+ </div>
60
+ <div class="rd-chart-bar__plot">
61
+ <div class="rd-chart-bar__grid-lines" aria-hidden="true">
62
+ {#each ticks as tick (tick)}
63
+ <span class="rd-chart-bar__grid-line" style:bottom={`${(tick / maxValue) * 100}%`}></span>
64
+ {/each}
65
+ </div>
66
+ <div class="rd-chart-bar__bars">
67
+ {#each series as bar (bar.label)}
68
+ <div class="rd-chart-bar__bar-group">
69
+ <div class="rd-chart-bar__bar-wrap">
70
+ <div
71
+ class="rd-chart-bar__bar"
72
+ style:height={`${(bar.value / maxValue) * 100}%`}
73
+ title={`${bar.label}: ${format(bar.value)}`}
74
+ ></div>
75
+ </div>
76
+ <span class="rd-chart-bar__x-label">{bar.label}</span>
77
+ <span class="rd-chart-bar__value">{format(bar.value)}</span>
78
+ </div>
79
+ {/each}
80
+ </div>
81
+ </div>
20
82
  </div>
21
83
  {@render children?.()}
22
84
  </section>
@@ -1,2 +1,2 @@
1
- export type { BarChartProps } from './types';
1
+ export type { BarChartBar, BarChartProps } from './types';
2
2
  /** Component entry is `BarChart.svelte` (see package exports). */
@@ -1,4 +1,11 @@
1
+ export interface BarChartBar {
2
+ label: string;
3
+ value: number;
4
+ }
1
5
  export interface BarChartProps {
2
6
  title?: string;
7
+ bars?: BarChartBar[];
8
+ yAxisLabel?: string;
9
+ valueFormat?: (value: number) => string;
3
10
  className?: string;
4
11
  }
@@ -1,21 +1,107 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
- import type { LineChartProps } from './types';
3
+ import type { LineChartPoint, LineChartProps } from './types';
4
4
 
5
5
  type Props = LineChartProps & { children?: Snippet };
6
6
 
7
+ const FALLBACK_POINTS: LineChartPoint[] = [
8
+ { x: '2019', y: 42 },
9
+ { x: '2020', y: 18 },
10
+ { x: '2021', y: 12 },
11
+ { x: '2022', y: 19 },
12
+ { x: '2023', y: 31 },
13
+ { x: '2024', y: 36 },
14
+ ];
15
+
16
+ const CHART_WIDTH = 320;
17
+ const CHART_HEIGHT = 200;
18
+ const PAD_LEFT = 58;
19
+ const PAD_RIGHT = 12;
20
+ const PAD_TOP = 16;
21
+ const PAD_BOTTOM = 36;
22
+ const Y_TICK_X = PAD_LEFT - 6;
23
+
7
24
  let {
8
25
  className,
9
26
  title,
27
+ points,
28
+ xAxisLabel,
29
+ yAxisLabel,
30
+ valueFormat,
10
31
  children,
11
32
  }: Props = $props();
33
+
34
+ function defaultFormat(value: number): string {
35
+ if (value >= 1_000_000) {
36
+ return `${(value / 1_000_000).toFixed(1)}M`;
37
+ }
38
+ if (value >= 1_000) {
39
+ return `${(value / 1_000).toFixed(0)}K`;
40
+ }
41
+ return String(Math.round(value));
42
+ }
43
+
12
44
  const rootClass = $derived(['rd-chart-line', className].filter(Boolean).join(' '));
45
+ const series = $derived(points?.length ? points : FALLBACK_POINTS);
46
+ const format = $derived(valueFormat ?? defaultFormat);
47
+ const values = $derived(series.map((point) => point.y));
48
+ const minY = $derived(Math.min(...values) >= 0 ? 0 : Math.min(...values));
49
+ const maxY = $derived(Math.max(...values));
50
+ const rangeY = $derived(maxY - minY || 1);
51
+ const ticks = $derived.by(() => {
52
+ if (minY === maxY) {
53
+ return [minY];
54
+ }
55
+ const count = 4;
56
+ const step = (maxY - minY) / (count - 1);
57
+ return Array.from({ length: count }, (_, index) => minY + step * index);
58
+ });
59
+ const plotH = CHART_HEIGHT - PAD_TOP - PAD_BOTTOM;
60
+ const coords = $derived.by(() => {
61
+ const plotW = CHART_WIDTH - PAD_LEFT - PAD_RIGHT;
62
+ const lastIndex = Math.max(series.length - 1, 1);
63
+ return series.map((point, index) => {
64
+ const x = PAD_LEFT + (index / lastIndex) * plotW;
65
+ const y = PAD_TOP + plotH - ((point.y - minY) / rangeY) * plotH;
66
+ return { x, y, label: String(point.x) };
67
+ });
68
+ });
69
+ const polyline = $derived(coords.map(({ x, y }) => `${x},${y}`).join(' '));
13
70
  </script>
14
71
 
15
- <section class={rootClass} data-testid="rd-chart-line">
72
+ <section class={rootClass} data-testid="rd-chart-line" role="img" aria-label={title ?? 'Line chart'}>
16
73
  <header class="rd-chart-line__header"><span>{title ?? 'Line chart'}</span></header>
17
- <svg viewBox="0 0 240 96" class="rd-chart-line__svg" aria-hidden="true">
18
- <polyline class="rd-chart-line__line" points="0,80 40,60 80,65 120,40 160,45 200,20 240,30" />
19
- </svg>
74
+ <div class="rd-chart-line__body">
75
+ <svg viewBox="0 0 {CHART_WIDTH} {CHART_HEIGHT}" class="rd-chart-line__svg" aria-hidden="true">
76
+ {#each ticks as tick (tick)}
77
+ {@const y = PAD_TOP + plotH - ((tick - minY) / rangeY) * plotH}
78
+ <g class="rd-chart-line__tick">
79
+ <line x1={PAD_LEFT} y1={y} x2={CHART_WIDTH - PAD_RIGHT} y2={y} class="rd-chart-line__grid" />
80
+ <text x={Y_TICK_X} y={y + 4} class="rd-chart-line__tick-label">{format(tick)}</text>
81
+ </g>
82
+ {/each}
83
+ <polyline class="rd-chart-line__line" points={polyline} />
84
+ {#each coords as point (point.label)}
85
+ <g class="rd-chart-line__point">
86
+ <circle cx={point.x} cy={point.y} r="3.5" class="rd-chart-line__dot" />
87
+ <text x={point.x} y={CHART_HEIGHT - 10} class="rd-chart-line__x-label">{point.label}</text>
88
+ </g>
89
+ {/each}
90
+ {#if yAxisLabel}
91
+ <text
92
+ x="12"
93
+ y={PAD_TOP + plotH / 2}
94
+ class="rd-chart-line__axis-label rd-chart-line__axis-label--y">{yAxisLabel}</text
95
+ >
96
+ {/if}
97
+ {#if xAxisLabel}
98
+ <text
99
+ x={PAD_LEFT + (CHART_WIDTH - PAD_LEFT - PAD_RIGHT) / 2}
100
+ y={CHART_HEIGHT - 2}
101
+ class="rd-chart-line__axis-label rd-chart-line__axis-label--x">{xAxisLabel}</text
102
+ >
103
+ {/if}
104
+ </svg>
105
+ </div>
20
106
  {@render children?.()}
21
107
  </section>
@@ -1,2 +1,2 @@
1
- export type { LineChartProps } from './types';
1
+ export type { LineChartPoint, LineChartProps } from './types';
2
2
  /** Component entry is `LineChart.svelte` (see package exports). */
@@ -1,4 +1,12 @@
1
+ export interface LineChartPoint {
2
+ x: string | number;
3
+ y: number;
4
+ }
1
5
  export interface LineChartProps {
2
6
  title?: string;
7
+ points?: LineChartPoint[];
8
+ xAxisLabel?: string;
9
+ yAxisLabel?: string;
10
+ valueFormat?: (value: number) => string;
3
11
  className?: string;
4
12
  }
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import {
4
+ DB_GEO_MAP_TAG,
5
+ registerRdGeoMap,
6
+ } from '@rosettadash/web-components/visual/display/geo-map';
7
+ import {
8
+ attachHostEvents,
9
+ setHostAttribute,
10
+ setHostProperty,
11
+ } from '../../../lib/custom-element-host';
12
+ import type { GeoMapProps } from './types';
13
+
14
+ let {
15
+ provider,
16
+ tileUrl,
17
+ apiKey,
18
+ center,
19
+ zoom,
20
+ markers,
21
+ selectedId,
22
+ className,
23
+ onMarkerSelect,
24
+ }: GeoMapProps = $props();
25
+
26
+ let host: HTMLElement;
27
+ let detachEvents = () => {};
28
+
29
+ onMount(() => {
30
+ registerRdGeoMap();
31
+ detachEvents = attachHostEvents(host, {
32
+ 'marker-select': (detail) =>
33
+ onMarkerSelect?.(detail as { id: string; lat: number; lng: number }),
34
+ });
35
+ return () => detachEvents();
36
+ });
37
+
38
+ $effect(() => {
39
+ if (!host) {
40
+ return;
41
+ }
42
+ setHostAttribute(host, 'provider', provider);
43
+ setHostAttribute(host, 'tile-url', tileUrl);
44
+ setHostAttribute(host, 'api-key', apiKey);
45
+ setHostAttribute(host, 'center', center);
46
+ setHostAttribute(host, 'zoom', zoom);
47
+ setHostProperty(host, 'markers', markers);
48
+ setHostAttribute(host, 'selected-id', selectedId);
49
+ });
50
+ </script>
51
+
52
+ <svelte:element this={DB_GEO_MAP_TAG} bind:this={host} class={className} />
@@ -0,0 +1,2 @@
1
+ export type { GeoMapProps, GeoMapMarker, GeoMapProvider } from './types';
2
+ /** Component entry is `GeoMap.svelte` (see package exports). */
@@ -0,0 +1,3 @@
1
+ export {};
2
+ /** Component entry is `GeoMap.svelte` (see package exports). */
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../packages/svelte/src/visual/display/geo-map/index.ts"],"names":[],"mappings":";AACA,gEAAgE"}
@@ -0,0 +1,18 @@
1
+ import type { GeoMapMarker, GeoMapProvider } from '@rosettadash/web-components/visual/display/geo-map';
2
+ export type { GeoMapMarker, GeoMapProvider };
3
+ export interface GeoMapProps {
4
+ provider?: GeoMapProvider;
5
+ tileUrl?: string;
6
+ apiKey?: string;
7
+ center?: string;
8
+ zoom?: number;
9
+ markers?: GeoMapMarker[];
10
+ selectedId?: string;
11
+ className?: string;
12
+ onMarkerSelect?: (detail: {
13
+ id: string;
14
+ lat: number;
15
+ lng: number;
16
+ }) => void;
17
+ }
18
+ export { default as GeoMap } from './GeoMap.svelte';
@@ -0,0 +1,2 @@
1
+ export { default as GeoMap } from './GeoMap.svelte';
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../../packages/svelte/src/visual/display/geo-map/types.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAC"}
@@ -10,17 +10,34 @@
10
10
  startDate,
11
11
  endDate,
12
12
  presetLabel,
13
+ granularity,
14
+ onChange,
13
15
  children,
14
16
  }: Props = $props();
15
17
  const rootClass = $derived(['rd-input-date-range', className].filter(Boolean).join(' '));
18
+ const inputType = $derived(granularity === 'month' ? 'month' : 'date');
16
19
  </script>
17
20
 
18
21
  <section class={rootClass} data-testid="rd-input-date-range">
19
22
  {#if label}<span class="rd-field__label">{label}</span>{/if}
20
23
  <div class="rd-date-range__controls">
21
- <input type="date" class="rd-input" value={startDate ?? ''} />
24
+ <input
25
+ type={inputType}
26
+ class="rd-input"
27
+ value={startDate ?? ''}
28
+ aria-label={`${label ?? 'Date range'} start`}
29
+ onchange={(event) =>
30
+ onChange?.({ startDate: event.currentTarget.value, endDate: endDate ?? '' })}
31
+ />
22
32
  <span class="rd-date-range__sep">to</span>
23
- <input type="date" class="rd-input" value={endDate ?? ''} />
33
+ <input
34
+ type={inputType}
35
+ class="rd-input"
36
+ value={endDate ?? ''}
37
+ aria-label={`${label ?? 'Date range'} end`}
38
+ onchange={(event) =>
39
+ onChange?.({ startDate: startDate ?? '', endDate: event.currentTarget.value })}
40
+ />
24
41
  </div>
25
42
  {#if presetLabel}<span class="rd-date-range__preset">{presetLabel}</span>{/if}
26
43
  {@render children?.()}
@@ -3,6 +3,7 @@ export interface DateRangeFilterProps {
3
3
  startDate?: string;
4
4
  endDate?: string;
5
5
  presetLabel?: string;
6
+ granularity?: 'date' | 'month';
6
7
  onChange?: (range: {
7
8
  startDate: string;
8
9
  endDate: string;
@@ -9,17 +9,27 @@
9
9
  label,
10
10
  placeholder,
11
11
  options,
12
- value,
12
+ value = $bindable(''),
13
+ onChange,
13
14
  children,
14
15
  }: Props = $props();
15
16
  const rootClass = $derived(['rd-input-select', className].filter(Boolean).join(' '));
17
+
18
+ function handleChange(event: Event) {
19
+ const target = event.currentTarget;
20
+ if (!(target instanceof HTMLSelectElement)) {
21
+ return;
22
+ }
23
+ value = target.value;
24
+ onChange?.(target.value);
25
+ }
16
26
  </script>
17
27
 
18
28
  <section class={rootClass} data-testid="rd-input-select">
19
29
  {#if label}<span class="rd-field__label">{label}</span>{/if}
20
- <select class="rd-select" value={value ?? ''}>
21
- <option value="">{placeholder ?? 'Select…'}</option>
22
- {#each options ?? [] as o (o.value)}<option value={o.value}>{o.label}</option>{/each}
30
+ <select class="rd-select" onchange={handleChange}>
31
+ <option value="" selected={!(value ?? '')}>{placeholder ?? 'Select…'}</option>
32
+ {#each options ?? [] as o (o.value)}<option value={o.value} selected={o.value === (value ?? '')}>{o.label}</option>{/each}
23
33
  </select>
24
34
  {@render children?.()}
25
35
  </section>
@@ -0,0 +1,45 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import {
4
+ DB_YOUTUBE_EMBED_TAG,
5
+ registerRdYoutubeEmbed,
6
+ } from '@rosettadash/web-components/visual/media/youtube-embed';
7
+ import { setHostAttribute } from '../../../lib/custom-element-host';
8
+ import type { YoutubeEmbedProps } from './types';
9
+
10
+ let {
11
+ videoId,
12
+ url,
13
+ start,
14
+ autoplay,
15
+ mute,
16
+ controls,
17
+ title,
18
+ className,
19
+ }: YoutubeEmbedProps = $props();
20
+
21
+ let host: HTMLElement;
22
+
23
+ onMount(() => {
24
+ registerRdYoutubeEmbed();
25
+ });
26
+
27
+ $effect(() => {
28
+ if (!host) {
29
+ return;
30
+ }
31
+ setHostAttribute(host, 'video-id', videoId);
32
+ setHostAttribute(host, 'url', url);
33
+ setHostAttribute(host, 'start', start);
34
+ setHostAttribute(host, 'autoplay', autoplay);
35
+ setHostAttribute(host, 'mute', mute);
36
+ if (controls === false) {
37
+ host.setAttribute('controls', 'false');
38
+ } else {
39
+ host.removeAttribute('controls');
40
+ }
41
+ setHostAttribute(host, 'embed-title', title);
42
+ });
43
+ </script>
44
+
45
+ <svelte:element this={DB_YOUTUBE_EMBED_TAG} bind:this={host} class={className} />
@@ -0,0 +1,2 @@
1
+ export type { YoutubeEmbedProps } from './types';
2
+ /** Component entry is `YoutubeEmbed.svelte` (see package exports). */
@@ -0,0 +1,3 @@
1
+ export {};
2
+ /** Component entry is `YoutubeEmbed.svelte` (see package exports). */
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../packages/svelte/src/visual/media/youtube-embed/index.ts"],"names":[],"mappings":";AACA,sEAAsE"}
@@ -0,0 +1,10 @@
1
+ export interface YoutubeEmbedProps {
2
+ videoId?: string;
3
+ url?: string;
4
+ start?: number;
5
+ autoplay?: boolean;
6
+ mute?: boolean;
7
+ controls?: boolean;
8
+ title?: string;
9
+ className?: string;
10
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../../packages/svelte/src/visual/media/youtube-embed/types.ts"],"names":[],"mappings":""}
@@ -17,9 +17,9 @@
17
17
 
18
18
  <section class={rootClass} data-testid="rd-news-language-select">
19
19
  {#if label}<span class="rd-field__label">{label}</span>{/if}
20
- <select class="rd-select" value={value ?? ''}>
21
- <option value="">{placeholder ?? 'Select…'}</option>
22
- {#each options ?? [] as o (o.value)}<option value={o.value}>{o.label}</option>{/each}
20
+ <select class="rd-select">
21
+ <option value="" selected={!(value ?? '')}>{placeholder ?? 'Select…'}</option>
22
+ {#each options ?? [] as o (o.value)}<option value={o.value} selected={o.value === (value ?? '')}>{o.label}</option>{/each}
23
23
  </select>
24
24
  {@render children?.()}
25
25
  </section>
@@ -17,9 +17,9 @@
17
17
 
18
18
  <section class={rootClass} data-testid="rd-news-region-select">
19
19
  {#if label}<span class="rd-field__label">{label}</span>{/if}
20
- <select class="rd-select" value={value ?? ''}>
21
- <option value="">{placeholder ?? 'Select…'}</option>
22
- {#each options ?? [] as o (o.value)}<option value={o.value}>{o.label}</option>{/each}
20
+ <select class="rd-select">
21
+ <option value="" selected={!(value ?? '')}>{placeholder ?? 'Select…'}</option>
22
+ {#each options ?? [] as o (o.value)}<option value={o.value} selected={o.value === (value ?? '')}>{o.label}</option>{/each}
23
23
  </select>
24
24
  {@render children?.()}
25
25
  </section>
@@ -17,9 +17,9 @@
17
17
 
18
18
  <section class={rootClass} data-testid="rd-news-type-select">
19
19
  {#if label}<span class="rd-field__label">{label}</span>{/if}
20
- <select class="rd-select" value={value ?? ''}>
21
- <option value="">{placeholder ?? 'Select…'}</option>
22
- {#each options ?? [] as o (o.value)}<option value={o.value}>{o.label}</option>{/each}
20
+ <select class="rd-select">
21
+ <option value="" selected={!(value ?? '')}>{placeholder ?? 'Select…'}</option>
22
+ {#each options ?? [] as o (o.value)}<option value={o.value} selected={o.value === (value ?? '')}>{o.label}</option>{/each}
23
23
  </select>
24
24
  {@render children?.()}
25
25
  </section>
@@ -8,20 +8,33 @@
8
8
  className,
9
9
  title,
10
10
  rows,
11
+ selectedRowId,
12
+ onRowSelect,
11
13
  children,
12
14
  }: Props = $props();
13
15
  const rootClass = $derived(['rd-table', className].filter(Boolean).join(' '));
16
+ const selectable = $derived(Boolean(onRowSelect));
14
17
  </script>
15
18
 
16
19
  <section class={rootClass} data-testid="rd-table">
17
20
  <header class="rd-table__header"><span>{title ?? 'Data table'}</span></header>
18
- <table class="rd-table__table">
19
- <thead><tr><th>Name</th><th>Status</th><th>Amount</th><th>Date</th></tr></thead>
20
- <tbody>
21
- {#each rows ?? [] as row (row.id)}
22
- <tr><td>{row.name}</td><td>{row.status}</td><td>{row.amount}</td><td>{row.date}</td></tr>
23
- {/each}
24
- </tbody>
25
- </table>
21
+ <div class="rd-table__scroll">
22
+ <table class="rd-table__table">
23
+ <thead><tr><th>Name</th><th>Status</th><th>Amount</th><th>Date</th></tr></thead>
24
+ <tbody>
25
+ {#each rows ?? [] as row (row.id)}
26
+ <tr
27
+ class="rd-table__row"
28
+ class:rd-table__row--selected={row.id === selectedRowId}
29
+ class:rd-table__row--interactive={selectable}
30
+ aria-selected={row.id === selectedRowId ? 'true' : undefined}
31
+ onclick={selectable ? () => onRowSelect?.(row.id) : undefined}
32
+ >
33
+ <td>{row.name}</td><td>{row.status}</td><td>{row.amount}</td><td>{row.date}</td>
34
+ </tr>
35
+ {/each}
36
+ </tbody>
37
+ </table>
38
+ </div>
26
39
  {@render children?.()}
27
40
  </section>
@@ -2,12 +2,14 @@ export interface DataTableRow {
2
2
  id: string;
3
3
  name?: string;
4
4
  status?: string;
5
- amount?: number;
5
+ amount?: string | number;
6
6
  date?: string;
7
7
  [key: string]: string | number | undefined;
8
8
  }
9
9
  export interface DataTableProps {
10
10
  title?: string;
11
11
  rows?: DataTableRow[];
12
+ selectedRowId?: string;
13
+ onRowSelect?: (rowId: string) => void;
12
14
  className?: string;
13
15
  }
@@ -26,11 +26,14 @@
26
26
  yaw,
27
27
  pitch,
28
28
  horizontalFov,
29
+ reverse,
29
30
  inputFile,
30
31
  cropRegion,
32
+ recordRange,
31
33
  className,
32
34
  onProgress,
33
35
  onExtractComplete,
36
+ onExtractError,
34
37
  onMetadata,
35
38
  }: WasmMediaProps = $props();
36
39
 
@@ -48,6 +51,7 @@
48
51
  metadata: Record<string, string | number | boolean | null | undefined>;
49
52
  },
50
53
  ),
54
+ 'extract-error': (detail) => onExtractError?.(detail as { message: string }),
51
55
  metadata: (detail) =>
52
56
  onMetadata?.(
53
57
  detail as Record<string, string | number | boolean | null | undefined>,
@@ -74,8 +78,10 @@
74
78
  setHostAttribute(host, 'yaw', yaw);
75
79
  setHostAttribute(host, 'pitch', pitch);
76
80
  setHostAttribute(host, 'horizontal-fov', horizontalFov);
81
+ setHostAttribute(host, 'reverse', reverse);
77
82
  setHostProperty(host, 'inputFile', inputFile);
78
83
  setHostProperty(host, 'cropRegion', cropRegion);
84
+ setHostProperty(host, 'recordRange', recordRange);
79
85
  });
80
86
  </script>
81
87
 
@@ -1,3 +1,4 @@
1
+ import type { AuthoringRecordRange } from '@rosettadash/core';
1
2
  export interface WasmMediaProps {
2
3
  label?: string;
3
4
  operation?: string;
@@ -13,8 +14,10 @@ export interface WasmMediaProps {
13
14
  yaw?: number;
14
15
  pitch?: number;
15
16
  horizontalFov?: number;
17
+ reverse?: boolean;
16
18
  inputFile?: File | Blob | null;
17
19
  cropRegion?: Record<string, string | number | boolean | null | undefined> | null;
20
+ recordRange?: AuthoringRecordRange | null;
18
21
  className?: string;
19
22
  onProgress?: (detail: {
20
23
  progress: number;
@@ -23,5 +26,8 @@ export interface WasmMediaProps {
23
26
  blob: Blob;
24
27
  metadata: Record<string, string | number | boolean | null | undefined>;
25
28
  }) => void;
29
+ onExtractError?: (detail: {
30
+ message: string;
31
+ }) => void;
26
32
  onMetadata?: (detail: Record<string, string | number | boolean | null | undefined>) => void;
27
33
  }