@rosettadash/svelte 0.1.2 → 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.
- package/package.json +8 -2
- package/src/index.d.ts +1 -0
- package/src/layout/scroll-region/ScrollRegion.svelte +25 -0
- package/src/layout/scroll-region/index.d.ts +2 -0
- package/src/layout/scroll-region/index.js +3 -0
- package/src/layout/scroll-region/index.js.map +1 -0
- package/src/layout/scroll-region/types.d.ts +6 -0
- package/src/layout/scroll-region/types.js +2 -0
- package/src/layout/scroll-region/types.js.map +1 -0
- package/src/visual/chart/bar/BarChart.svelte +67 -5
- package/src/visual/chart/bar/index.d.ts +1 -1
- package/src/visual/chart/bar/types.d.ts +7 -0
- package/src/visual/chart/line/LineChart.svelte +91 -5
- package/src/visual/chart/line/index.d.ts +1 -1
- package/src/visual/chart/line/types.d.ts +8 -0
- package/src/visual/input/date-range/DateRangeFilter.svelte +19 -2
- package/src/visual/input/date-range/types.d.ts +1 -0
- package/src/visual/input/select/SelectInput.svelte +14 -4
- package/src/visual/news/language-select/NewsLanguageSelect.svelte +3 -3
- package/src/visual/news/region-select/NewsRegionSelect.svelte +3 -3
- package/src/visual/news/type-select/NewsTypeSelect.svelte +3 -3
- package/src/visual/table/DataTable.svelte +21 -8
- package/src/visual/table/types.d.ts +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rosettadash/svelte",
|
|
3
|
-
"version": "0.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",
|
|
@@ -141,6 +141,12 @@
|
|
|
141
141
|
"import": "./src/layout/modal/ModalLayout.svelte",
|
|
142
142
|
"default": "./src/layout/modal/ModalLayout.svelte"
|
|
143
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
|
+
},
|
|
144
150
|
"./layout/tabs": {
|
|
145
151
|
"types": "./src/layout/tabs/types.d.ts",
|
|
146
152
|
"svelte": "./src/layout/tabs/TabsLayout.svelte",
|
|
@@ -388,7 +394,7 @@
|
|
|
388
394
|
"LICENSE"
|
|
389
395
|
],
|
|
390
396
|
"dependencies": {
|
|
391
|
-
"@rosettadash/web-components": "0.1.
|
|
397
|
+
"@rosettadash/web-components": "0.1.3",
|
|
392
398
|
"tslib": "^2.3.0"
|
|
393
399
|
},
|
|
394
400
|
"peerDependencies": {
|
package/src/index.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export type { CollapsibleProps } from './layout/collapsible/types';
|
|
|
19
19
|
export type { FlexLayoutProps } from './layout/flex/types';
|
|
20
20
|
export type { GridLayoutProps } from './layout/grid/types';
|
|
21
21
|
export type { ModalLayoutProps } from './layout/modal/types';
|
|
22
|
+
export type { ScrollRegionProps } from './layout/scroll-region/types';
|
|
22
23
|
export type { TabsLayoutProps } from './layout/tabs/types';
|
|
23
24
|
export type { TimerProps } from './logic/timer/types';
|
|
24
25
|
export type { BarChartProps } from './visual/chart/bar/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 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/svelte/src/layout/scroll-region/index.ts"],"names":[],"mappings":";AACA,sEAAsE"}
|
|
@@ -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
|
-
|
|
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-
|
|
19
|
-
|
|
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,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
|
-
<
|
|
18
|
-
<
|
|
19
|
-
|
|
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
|
}
|
|
@@ -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
|
|
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
|
|
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?.()}
|
|
@@ -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"
|
|
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>
|
|
@@ -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"
|
|
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"
|
|
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"
|
|
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
|
-
<
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
}
|