@w5-ui/svelte 0.1.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/README.md +8 -0
- package/package.json +57 -0
- package/src/lib/components/Accordion.svelte +3 -0
- package/src/lib/components/AccordionItem.svelte +40 -0
- package/src/lib/components/ActionMenu.svelte +96 -0
- package/src/lib/components/ActivityFeed.svelte +60 -0
- package/src/lib/components/Alert.svelte +19 -0
- package/src/lib/components/AnnotatedTimeSeries.svelte +90 -0
- package/src/lib/components/ArcDiagram.svelte +74 -0
- package/src/lib/components/AreaChart.svelte +64 -0
- package/src/lib/components/Avatar.svelte +30 -0
- package/src/lib/components/AvatarGroup.svelte +35 -0
- package/src/lib/components/Badge.svelte +25 -0
- package/src/lib/components/Banner.svelte +37 -0
- package/src/lib/components/BarChart.svelte +52 -0
- package/src/lib/components/BeeswarmChart.svelte +88 -0
- package/src/lib/components/BoxPlot.svelte +66 -0
- package/src/lib/components/Breadcrumb.svelte +31 -0
- package/src/lib/components/BubbleChart.svelte +69 -0
- package/src/lib/components/BulletChart.svelte +70 -0
- package/src/lib/components/BumpChart.svelte +109 -0
- package/src/lib/components/Button.svelte +25 -0
- package/src/lib/components/CIDRInput.svelte +142 -0
- package/src/lib/components/CalendarHeatmap.svelte +86 -0
- package/src/lib/components/Callout.svelte +38 -0
- package/src/lib/components/CandlestickChart.svelte +66 -0
- package/src/lib/components/Card.svelte +11 -0
- package/src/lib/components/Carousel.svelte +64 -0
- package/src/lib/components/Checkbox.svelte +45 -0
- package/src/lib/components/ChordDiagram.svelte +90 -0
- package/src/lib/components/CirclePacking.svelte +119 -0
- package/src/lib/components/CodeBlock.svelte +49 -0
- package/src/lib/components/ColorPicker.svelte +91 -0
- package/src/lib/components/ColumnToggle.svelte +96 -0
- package/src/lib/components/Combobox.svelte +138 -0
- package/src/lib/components/CommandPalette.svelte +152 -0
- package/src/lib/components/ConfirmDialog.svelte +84 -0
- package/src/lib/components/ContextMenu.svelte +107 -0
- package/src/lib/components/ContextualHelp.svelte +73 -0
- package/src/lib/components/ContourPlot.svelte +164 -0
- package/src/lib/components/CopyButton.svelte +33 -0
- package/src/lib/components/CorrelationMatrix.svelte +90 -0
- package/src/lib/components/CountUp.svelte +49 -0
- package/src/lib/components/CumulativeDistribution.svelte +67 -0
- package/src/lib/components/DatePicker.svelte +201 -0
- package/src/lib/components/DateRangePicker.svelte +249 -0
- package/src/lib/components/Dendrogram.svelte +128 -0
- package/src/lib/components/Donut.svelte +49 -0
- package/src/lib/components/DotPlot.svelte +121 -0
- package/src/lib/components/Drawer.svelte +60 -0
- package/src/lib/components/DualAxisChart.svelte +80 -0
- package/src/lib/components/DumbbellChart.svelte +56 -0
- package/src/lib/components/DurationInput.svelte +118 -0
- package/src/lib/components/EmptyState.svelte +21 -0
- package/src/lib/components/ErrorBandChart.svelte +69 -0
- package/src/lib/components/ExpandableRow.svelte +42 -0
- package/src/lib/components/Field.svelte +40 -0
- package/src/lib/components/FileUpload.svelte +96 -0
- package/src/lib/components/FilterBuilder.svelte +160 -0
- package/src/lib/components/FlameGraph.svelte +88 -0
- package/src/lib/components/ForceGraph.svelte +104 -0
- package/src/lib/components/FunnelChart.svelte +50 -0
- package/src/lib/components/GanttChart.svelte +49 -0
- package/src/lib/components/Gauge.svelte +53 -0
- package/src/lib/components/GroupedList.svelte +87 -0
- package/src/lib/components/HealthBar.svelte +19 -0
- package/src/lib/components/HeatMap.svelte +64 -0
- package/src/lib/components/HexbinChart.svelte +100 -0
- package/src/lib/components/Histogram.svelte +93 -0
- package/src/lib/components/HorizonChart.svelte +71 -0
- package/src/lib/components/HoverCard.svelte +45 -0
- package/src/lib/components/IPInput.svelte +103 -0
- package/src/lib/components/IcicleChart.svelte +87 -0
- package/src/lib/components/IconButton.svelte +19 -0
- package/src/lib/components/InlineEdit.svelte +72 -0
- package/src/lib/components/Input.svelte +18 -0
- package/src/lib/components/InputGroup.svelte +14 -0
- package/src/lib/components/JsonViewer.svelte +15 -0
- package/src/lib/components/JsonViewerNode.svelte +59 -0
- package/src/lib/components/KVTable.svelte +18 -0
- package/src/lib/components/KanbanBoard.svelte +141 -0
- package/src/lib/components/Kbd.svelte +16 -0
- package/src/lib/components/LineChart.svelte +67 -0
- package/src/lib/components/LogViewer.svelte +84 -0
- package/src/lib/components/LollipopChart.svelte +79 -0
- package/src/lib/components/MACInput.svelte +98 -0
- package/src/lib/components/MarimekkoChart.svelte +108 -0
- package/src/lib/components/MatrixChart.svelte +55 -0
- package/src/lib/components/Menubar.svelte +131 -0
- package/src/lib/components/MirroredBarChart.svelte +120 -0
- package/src/lib/components/Modal.svelte +90 -0
- package/src/lib/components/MultiSelect.svelte +165 -0
- package/src/lib/components/NightingaleChart.svelte +63 -0
- package/src/lib/components/NotificationPanel.svelte +170 -0
- package/src/lib/components/NumberInput.svelte +93 -0
- package/src/lib/components/OTPInput.svelte +94 -0
- package/src/lib/components/Pagination.svelte +58 -0
- package/src/lib/components/ParallelCoordinates.svelte +98 -0
- package/src/lib/components/ParetoChart.svelte +88 -0
- package/src/lib/components/PasswordInput.svelte +62 -0
- package/src/lib/components/PieChart.svelte +91 -0
- package/src/lib/components/Pill.svelte +12 -0
- package/src/lib/components/PolarHeatmap.svelte +85 -0
- package/src/lib/components/Popover.svelte +89 -0
- package/src/lib/components/ProgressBar.svelte +29 -0
- package/src/lib/components/PunchCard.svelte +73 -0
- package/src/lib/components/QuadrantChart.svelte +122 -0
- package/src/lib/components/RadarChart.svelte +71 -0
- package/src/lib/components/RadialBarChart.svelte +76 -0
- package/src/lib/components/RadioGroup.svelte +48 -0
- package/src/lib/components/RangeSlider.svelte +68 -0
- package/src/lib/components/RankedList.svelte +49 -0
- package/src/lib/components/ResizablePanel.svelte +77 -0
- package/src/lib/components/RidgelinePlot.svelte +103 -0
- package/src/lib/components/RowToggle.svelte +19 -0
- package/src/lib/components/SankeyDiagram.svelte +100 -0
- package/src/lib/components/ScatterPlot.svelte +54 -0
- package/src/lib/components/SearchBox.svelte +11 -0
- package/src/lib/components/SegmentedControl.svelte +51 -0
- package/src/lib/components/Select.svelte +110 -0
- package/src/lib/components/SelectionToolbar.svelte +42 -0
- package/src/lib/components/Sidebar.svelte +44 -0
- package/src/lib/components/Signal.svelte +12 -0
- package/src/lib/components/Skeleton.svelte +23 -0
- package/src/lib/components/SkipLink.svelte +9 -0
- package/src/lib/components/Slider.svelte +48 -0
- package/src/lib/components/SlopeChart.svelte +91 -0
- package/src/lib/components/SortHeader.svelte +19 -0
- package/src/lib/components/SortableList.svelte +87 -0
- package/src/lib/components/Sparkline.svelte +32 -0
- package/src/lib/components/SparklineMatrix.svelte +84 -0
- package/src/lib/components/Spinner.svelte +6 -0
- package/src/lib/components/SplitButton.svelte +102 -0
- package/src/lib/components/Spoiler.svelte +41 -0
- package/src/lib/components/StackedBarChart.svelte +83 -0
- package/src/lib/components/StackedProgress.svelte +32 -0
- package/src/lib/components/StarRating.svelte +64 -0
- package/src/lib/components/Stat.svelte +24 -0
- package/src/lib/components/StatusIndicator.svelte +10 -0
- package/src/lib/components/StepChart.svelte +74 -0
- package/src/lib/components/Stepper.svelte +40 -0
- package/src/lib/components/StreamGraph.svelte +93 -0
- package/src/lib/components/StripeChart.svelte +52 -0
- package/src/lib/components/SunburstChart.svelte +80 -0
- package/src/lib/components/SwitchPortGrid.svelte +45 -0
- package/src/lib/components/TabPanel.svelte +17 -0
- package/src/lib/components/Tabs.svelte +45 -0
- package/src/lib/components/Tag.svelte +19 -0
- package/src/lib/components/TagInput.svelte +92 -0
- package/src/lib/components/Textarea.svelte +31 -0
- package/src/lib/components/ThresholdAreaChart.svelte +72 -0
- package/src/lib/components/TimePicker.svelte +116 -0
- package/src/lib/components/TimeRange.svelte +104 -0
- package/src/lib/components/Timeline.svelte +32 -0
- package/src/lib/components/Toast.svelte +52 -0
- package/src/lib/components/Toggle.svelte +23 -0
- package/src/lib/components/ToggleGroup.svelte +32 -0
- package/src/lib/components/Tooltip.svelte +16 -0
- package/src/lib/components/Topbar.svelte +63 -0
- package/src/lib/components/TransferList.svelte +184 -0
- package/src/lib/components/TreeItem.svelte +61 -0
- package/src/lib/components/TreeMap.svelte +90 -0
- package/src/lib/components/TreeView.svelte +130 -0
- package/src/lib/components/UptimeTimeline.svelte +86 -0
- package/src/lib/components/VennDiagram.svelte +91 -0
- package/src/lib/components/ViolinPlot.svelte +114 -0
- package/src/lib/components/VirtualList.svelte +38 -0
- package/src/lib/components/WaffleChart.svelte +68 -0
- package/src/lib/components/WaterfallChart.svelte +96 -0
- package/src/lib/components/WordCloud.svelte +103 -0
- package/src/lib/icons/AlarmIcon.svelte +5 -0
- package/src/lib/icons/BellIcon.svelte +6 -0
- package/src/lib/icons/CaretIcon.svelte +8 -0
- package/src/lib/icons/ClientsIcon.svelte +5 -0
- package/src/lib/icons/CloseIcon.svelte +6 -0
- package/src/lib/icons/DashboardIcon.svelte +7 -0
- package/src/lib/icons/DevicesIcon.svelte +5 -0
- package/src/lib/icons/DownloadIcon.svelte +6 -0
- package/src/lib/icons/HelpIcon.svelte +7 -0
- package/src/lib/icons/InfraIcon.svelte +7 -0
- package/src/lib/icons/IntegrationsIcon.svelte +5 -0
- package/src/lib/icons/LogsIcon.svelte +6 -0
- package/src/lib/icons/PlusIcon.svelte +6 -0
- package/src/lib/icons/PortsIcon.svelte +6 -0
- package/src/lib/icons/SearchIcon.svelte +10 -0
- package/src/lib/icons/SecurityIcon.svelte +4 -0
- package/src/lib/icons/SettingsIcon.svelte +5 -0
- package/src/lib/icons/TopologyIcon.svelte +7 -0
- package/src/lib/icons/UpdatesIcon.svelte +6 -0
- package/src/lib/icons/VpnIcon.svelte +5 -0
- package/src/lib/icons/WifiIcon.svelte +5 -0
- package/src/lib/icons/WirelessIcon.svelte +4 -0
- package/src/lib/icons.ts +22 -0
- package/src/lib/index.ts +275 -0
- package/src/lib/styles.css +2 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script context="module" lang="ts">
|
|
2
|
+
let counter = 0;
|
|
3
|
+
export interface RadioOption {
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<script lang="ts">
|
|
12
|
+
export let legend: string;
|
|
13
|
+
export let name: string | undefined = undefined;
|
|
14
|
+
export let options: RadioOption[] = [];
|
|
15
|
+
export let value: string | undefined = undefined;
|
|
16
|
+
export let srOnlyLegend: boolean = false;
|
|
17
|
+
export let horizontal: boolean = false;
|
|
18
|
+
|
|
19
|
+
const groupName = name ?? `dash-ui-rg-${++counter}`;
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<fieldset class="radio-group {horizontal ? 'radio-group--h' : ''}">
|
|
23
|
+
<legend class={srOnlyLegend ? 'sr-only' : 'radio-group__legend'}>{legend}</legend>
|
|
24
|
+
{#each options as opt}
|
|
25
|
+
{@const optId = `${groupName}-${opt.value}`}
|
|
26
|
+
<label
|
|
27
|
+
for={optId}
|
|
28
|
+
class="radio-option{opt.disabled ? ' radio-option--disabled' : ''}"
|
|
29
|
+
>
|
|
30
|
+
<input
|
|
31
|
+
type="radio"
|
|
32
|
+
id={optId}
|
|
33
|
+
name={groupName}
|
|
34
|
+
value={opt.value}
|
|
35
|
+
checked={value === opt.value}
|
|
36
|
+
disabled={opt.disabled}
|
|
37
|
+
class="radio"
|
|
38
|
+
on:change={() => { value = opt.value; }}
|
|
39
|
+
/>
|
|
40
|
+
<span class="radio-text">
|
|
41
|
+
<span>{opt.label}</span>
|
|
42
|
+
{#if opt.description}
|
|
43
|
+
<span class="radio-description">{opt.description}</span>
|
|
44
|
+
{/if}
|
|
45
|
+
</span>
|
|
46
|
+
</label>
|
|
47
|
+
{/each}
|
|
48
|
+
</fieldset>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createEventDispatcher } from 'svelte';
|
|
3
|
+
|
|
4
|
+
export let label: string | undefined = undefined;
|
|
5
|
+
export let min = 0;
|
|
6
|
+
export let max = 100;
|
|
7
|
+
export let step = 1;
|
|
8
|
+
export let low = 0;
|
|
9
|
+
export let high = 100;
|
|
10
|
+
export let suffix: string | undefined = undefined;
|
|
11
|
+
export let disabled = false;
|
|
12
|
+
|
|
13
|
+
const dispatch = createEventDispatcher<{ change: [number, number] }>();
|
|
14
|
+
|
|
15
|
+
$: lowPct = max === min ? 0 : ((low - min) / (max - min)) * 100;
|
|
16
|
+
$: highPct = max === min ? 0 : ((high - min) / (max - min)) * 100;
|
|
17
|
+
$: lowOnTop = low >= max - step;
|
|
18
|
+
|
|
19
|
+
function handleLow(e: Event) {
|
|
20
|
+
low = Math.min(Number((e.target as HTMLInputElement).value), high - step);
|
|
21
|
+
dispatch('change', [low, high]);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function handleHigh(e: Event) {
|
|
25
|
+
high = Math.max(Number((e.target as HTMLInputElement).value), low + step);
|
|
26
|
+
dispatch('change', [low, high]);
|
|
27
|
+
}
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<div class="range-slider">
|
|
31
|
+
{#if label}
|
|
32
|
+
<div class="range-slider__header">
|
|
33
|
+
<span class="range-slider__label">{label}</span>
|
|
34
|
+
<span class="range-slider__value" aria-live="polite">{low}{suffix ?? ''} – {high}{suffix ?? ''}</span>
|
|
35
|
+
</div>
|
|
36
|
+
{/if}
|
|
37
|
+
<div
|
|
38
|
+
class="range-slider__wrap"
|
|
39
|
+
style="--rs-low:{lowPct}%;--rs-high:{highPct}%"
|
|
40
|
+
role="group"
|
|
41
|
+
aria-label={label}
|
|
42
|
+
>
|
|
43
|
+
<div class="range-slider__track-bg"></div>
|
|
44
|
+
<div class="range-slider__fill"></div>
|
|
45
|
+
<input
|
|
46
|
+
type="range"
|
|
47
|
+
class="range-slider__input"
|
|
48
|
+
{min} {max} {step}
|
|
49
|
+
value={low}
|
|
50
|
+
{disabled}
|
|
51
|
+
style="z-index:{lowOnTop ? 2 : 1}"
|
|
52
|
+
aria-label={label ? `${label} minimum` : 'Minimum'}
|
|
53
|
+
aria-valuetext={suffix ? `${low}${suffix}` : String(low)}
|
|
54
|
+
on:input={handleLow}
|
|
55
|
+
/>
|
|
56
|
+
<input
|
|
57
|
+
type="range"
|
|
58
|
+
class="range-slider__input"
|
|
59
|
+
{min} {max} {step}
|
|
60
|
+
value={high}
|
|
61
|
+
{disabled}
|
|
62
|
+
style="z-index:{lowOnTop ? 1 : 2}"
|
|
63
|
+
aria-label={label ? `${label} maximum` : 'Maximum'}
|
|
64
|
+
aria-valuetext={suffix ? `${high}${suffix}` : String(high)}
|
|
65
|
+
on:input={handleHigh}
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export interface RankedItem {
|
|
3
|
+
label: string;
|
|
4
|
+
value: number;
|
|
5
|
+
sublabel?: string;
|
|
6
|
+
color?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export let items: RankedItem[];
|
|
10
|
+
export let unit: string | undefined = undefined;
|
|
11
|
+
export let max: number | undefined = undefined;
|
|
12
|
+
export let ariaLabel: string = 'Ranked list';
|
|
13
|
+
|
|
14
|
+
$: resolvedMax = max ?? Math.max(...items.map((i) => i.value), 1);
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<table class="rl" aria-label={ariaLabel}>
|
|
18
|
+
<caption class="sr-only">{ariaLabel}</caption>
|
|
19
|
+
<thead class="sr-only">
|
|
20
|
+
<tr>
|
|
21
|
+
<th scope="col">#</th>
|
|
22
|
+
<th scope="col">Name</th>
|
|
23
|
+
<th scope="col">Distribution</th>
|
|
24
|
+
<th scope="col">{unit ?? 'Value'}</th>
|
|
25
|
+
</tr>
|
|
26
|
+
</thead>
|
|
27
|
+
<tbody>
|
|
28
|
+
{#each items as item, i}
|
|
29
|
+
<tr class="rl-row">
|
|
30
|
+
<td class="rl-rank">{i + 1}</td>
|
|
31
|
+
<td class="rl-label-cell">
|
|
32
|
+
<div class="rl-label">{item.label}</div>
|
|
33
|
+
{#if item.sublabel}<div class="rl-sublabel">{item.sublabel}</div>{/if}
|
|
34
|
+
</td>
|
|
35
|
+
<td class="rl-bar-col" aria-hidden="true">
|
|
36
|
+
<div class="rl-bar-track">
|
|
37
|
+
<div
|
|
38
|
+
class="rl-bar-fill"
|
|
39
|
+
style="width:{((item.value / resolvedMax) * 100).toFixed(1)}%;background:{item.color ?? '#006FFF'}"
|
|
40
|
+
></div>
|
|
41
|
+
</div>
|
|
42
|
+
</td>
|
|
43
|
+
<td class="rl-val">
|
|
44
|
+
{item.value.toLocaleString()}{#if unit}<span class="rl-unit"> {unit}</span>{/if}
|
|
45
|
+
</td>
|
|
46
|
+
</tr>
|
|
47
|
+
{/each}
|
|
48
|
+
</tbody>
|
|
49
|
+
</table>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<script context="module" lang="ts">
|
|
2
|
+
export type ResizableOrientation = 'horizontal' | 'vertical';
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
export let defaultSize: number = 50;
|
|
7
|
+
export let min: number = 20;
|
|
8
|
+
export let max: number = 80;
|
|
9
|
+
export let orientation: ResizableOrientation = 'vertical';
|
|
10
|
+
export let label: string = 'Resize panels';
|
|
11
|
+
export let style: string = '';
|
|
12
|
+
|
|
13
|
+
let size = defaultSize;
|
|
14
|
+
let container: HTMLDivElement;
|
|
15
|
+
|
|
16
|
+
function clamp(v: number) { return Math.min(max, Math.max(min, v)); }
|
|
17
|
+
|
|
18
|
+
function onMouseDown(e: MouseEvent) {
|
|
19
|
+
e.preventDefault();
|
|
20
|
+
|
|
21
|
+
function onMove(me: MouseEvent) {
|
|
22
|
+
if (!container) return;
|
|
23
|
+
const rect = container.getBoundingClientRect();
|
|
24
|
+
const pct = orientation === 'vertical'
|
|
25
|
+
? ((me.clientX - rect.left) / rect.width) * 100
|
|
26
|
+
: ((me.clientY - rect.top) / rect.height) * 100;
|
|
27
|
+
size = clamp(Math.round(pct));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function onUp() {
|
|
31
|
+
window.removeEventListener('mousemove', onMove);
|
|
32
|
+
window.removeEventListener('mouseup', onUp);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
window.addEventListener('mousemove', onMove);
|
|
36
|
+
window.addEventListener('mouseup', onUp);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function onKeyDown(e: KeyboardEvent) {
|
|
40
|
+
const step = e.shiftKey ? 10 : 1;
|
|
41
|
+
if (orientation === 'vertical') {
|
|
42
|
+
if (e.key === 'ArrowLeft') { e.preventDefault(); size = clamp(size - step); }
|
|
43
|
+
if (e.key === 'ArrowRight') { e.preventDefault(); size = clamp(size + step); }
|
|
44
|
+
} else {
|
|
45
|
+
if (e.key === 'ArrowUp') { e.preventDefault(); size = clamp(size - step); }
|
|
46
|
+
if (e.key === 'ArrowDown') { e.preventDefault(); size = clamp(size + step); }
|
|
47
|
+
}
|
|
48
|
+
if (e.key === 'Home') { e.preventDefault(); size = min; }
|
|
49
|
+
if (e.key === 'End') { e.preventDefault(); size = max; }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
$: firstStyle = orientation === 'vertical'
|
|
53
|
+
? `flex: 0 0 ${size}%; min-width: 0;`
|
|
54
|
+
: `flex: 0 0 ${size}%; min-height: 0;`;
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<div bind:this={container} class="rp rp--{orientation}" {style}>
|
|
58
|
+
<div class="rp-pane" style={firstStyle}>
|
|
59
|
+
<slot name="first" />
|
|
60
|
+
</div>
|
|
61
|
+
<!-- svelte-ignore a11y-no-noninteractive-tabindex a11y-no-noninteractive-element-interactions -->
|
|
62
|
+
<div
|
|
63
|
+
role="separator"
|
|
64
|
+
tabindex="0"
|
|
65
|
+
aria-orientation={orientation}
|
|
66
|
+
aria-valuenow={size}
|
|
67
|
+
aria-valuemin={min}
|
|
68
|
+
aria-valuemax={max}
|
|
69
|
+
aria-label={label}
|
|
70
|
+
class="rp-handle"
|
|
71
|
+
on:mousedown={onMouseDown}
|
|
72
|
+
on:keydown={onKeyDown}
|
|
73
|
+
/>
|
|
74
|
+
<div class="rp-pane rp-pane--second">
|
|
75
|
+
<slot name="second" />
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export interface RidgelineSeries {
|
|
3
|
+
label: string;
|
|
4
|
+
values: number[];
|
|
5
|
+
color?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export let series: RidgelineSeries[] = [];
|
|
9
|
+
export let xRange: [number, number] | undefined = undefined;
|
|
10
|
+
export let height: number = 180;
|
|
11
|
+
export let ariaLabel: string = 'Ridgeline plot';
|
|
12
|
+
|
|
13
|
+
const COLORS = ['#006FFF', '#00C8C8', '#F5A623', '#7FB6FF', '#A878F5', '#F56342'];
|
|
14
|
+
const SQRT_TWO_PI = Math.sqrt(2 * Math.PI);
|
|
15
|
+
const NUM_PTS = 80;
|
|
16
|
+
|
|
17
|
+
function bw(vals: number[]): number {
|
|
18
|
+
const n = vals.length;
|
|
19
|
+
const m = vals.reduce((a, b) => a + b, 0) / n;
|
|
20
|
+
const s = Math.sqrt(vals.reduce((a, b) => a + (b - m) ** 2, 0) / n);
|
|
21
|
+
return Math.max(0.9 * s * Math.pow(n, -0.2), 0.5);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function kde(vals: number[], h: number, pts: number[]): number[] {
|
|
25
|
+
const n = vals.length;
|
|
26
|
+
const c = 1 / (n * h * SQRT_TWO_PI);
|
|
27
|
+
return pts.map(x => c * vals.reduce((acc, v) => {
|
|
28
|
+
const z = (x - v) / h;
|
|
29
|
+
return acc + Math.exp(-0.5 * z * z);
|
|
30
|
+
}, 0));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const VW = 380;
|
|
34
|
+
$: VH = height;
|
|
35
|
+
const PAD_L = 56;
|
|
36
|
+
const PAD_R = 12;
|
|
37
|
+
const PAD_T = 16;
|
|
38
|
+
const PAD_B = 20;
|
|
39
|
+
|
|
40
|
+
$: TRACK_W = VW - PAD_L - PAD_R;
|
|
41
|
+
$: TRACK_H = VH - PAD_T - PAD_B;
|
|
42
|
+
$: n = series.length;
|
|
43
|
+
$: rowH = TRACK_H / n;
|
|
44
|
+
$: maxDensH = rowH * 1.6;
|
|
45
|
+
|
|
46
|
+
$: allVals = series.flatMap(s => s.values);
|
|
47
|
+
$: xMin = xRange ? xRange[0] : Math.min(...allVals);
|
|
48
|
+
$: xMax = xRange ? xRange[1] : Math.max(...allVals);
|
|
49
|
+
|
|
50
|
+
$: evalPts = Array.from({ length: NUM_PTS }, (_, i) =>
|
|
51
|
+
xMin + (i / (NUM_PTS - 1)) * (xMax - xMin)
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
function xSvg(v: number): number {
|
|
55
|
+
return PAD_L + ((v - xMin) / (xMax - xMin)) * TRACK_W;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
$: ridges = series.map((s, si) => {
|
|
59
|
+
const h = bw(s.values);
|
|
60
|
+
const dens = kde(s.values, h, evalPts);
|
|
61
|
+
const maxD = Math.max(...dens, 1e-9);
|
|
62
|
+
const baselineY = PAD_T + (si + 1) * rowH;
|
|
63
|
+
const color = s.color ?? COLORS[si % COLORS.length];
|
|
64
|
+
|
|
65
|
+
const d = [
|
|
66
|
+
`M${xSvg(xMin).toFixed(1)},${baselineY.toFixed(1)}`,
|
|
67
|
+
...evalPts.map((v, i) => `L${xSvg(v).toFixed(1)},${(baselineY - (dens[i] / maxD) * maxDensH).toFixed(1)}`),
|
|
68
|
+
`L${xSvg(xMax).toFixed(1)},${baselineY.toFixed(1)}`,
|
|
69
|
+
'Z',
|
|
70
|
+
].join(' ');
|
|
71
|
+
|
|
72
|
+
return { d, baselineY, color, label: s.label };
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
$: reversedRidges = [...ridges].reverse();
|
|
76
|
+
|
|
77
|
+
$: xTicks = [0, 0.25, 0.5, 0.75, 1].map(t => {
|
|
78
|
+
const v = xMin + t * (xMax - xMin);
|
|
79
|
+
return { v, x: xSvg(v) };
|
|
80
|
+
});
|
|
81
|
+
</script>
|
|
82
|
+
|
|
83
|
+
<div role="img" aria-label={ariaLabel} style="width:100%;">
|
|
84
|
+
<svg
|
|
85
|
+
viewBox="0 0 {VW} {VH}"
|
|
86
|
+
style="width:100%;height:auto;display:block;"
|
|
87
|
+
aria-hidden="true"
|
|
88
|
+
focusable="false"
|
|
89
|
+
>
|
|
90
|
+
{#each xTicks as tick (tick.v)}
|
|
91
|
+
<line x1={tick.x} y1={PAD_T} x2={tick.x} y2={VH - PAD_B} stroke="rgba(255,255,255,0.06)" stroke-width="1" />
|
|
92
|
+
<text x={tick.x} y={VH - PAD_B + 12} fill="#6E7079" font-size="8" text-anchor="middle" font-family="inherit">{tick.v.toFixed(0)}</text>
|
|
93
|
+
{/each}
|
|
94
|
+
{#each reversedRidges as r (r.label)}
|
|
95
|
+
<line x1={PAD_L} y1={r.baselineY} x2={VW - PAD_R} y2={r.baselineY} stroke="rgba(255,255,255,0.08)" stroke-width="0.5" />
|
|
96
|
+
<path d={r.d} fill={r.color} opacity="0.45" />
|
|
97
|
+
<path d={r.d} fill="none" stroke={r.color} stroke-width="1.2" opacity="0.9" />
|
|
98
|
+
{/each}
|
|
99
|
+
{#each ridges as r, si (si)}
|
|
100
|
+
<text x={PAD_L - 4} y={r.baselineY - 2} fill="#A4A7B5" font-size="8" text-anchor="end" font-family="inherit">{r.label}</text>
|
|
101
|
+
{/each}
|
|
102
|
+
</svg>
|
|
103
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script context="module" lang="ts">
|
|
2
|
+
let counter = 0;
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import Toggle from './Toggle.svelte';
|
|
7
|
+
export let title: string;
|
|
8
|
+
export let description: string;
|
|
9
|
+
export let on: boolean = false;
|
|
10
|
+
const descId = `dash-ui-rowtoggle-${++counter}`;
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div class="row-toggle">
|
|
14
|
+
<div class="info">
|
|
15
|
+
<div class="t">{title}</div>
|
|
16
|
+
<div class="d" id={descId}>{description}</div>
|
|
17
|
+
</div>
|
|
18
|
+
<Toggle bind:on ariaLabel={title} ariaDescribedBy={descId} on:toggle />
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export interface SankeyLink { source: string; target: string; value: number; }
|
|
3
|
+
export interface SankeyNode { id: string; label: string; color?: string; }
|
|
4
|
+
|
|
5
|
+
export let nodes: SankeyNode[] = [];
|
|
6
|
+
export let links: SankeyLink[] = [];
|
|
7
|
+
export let height: number = 180;
|
|
8
|
+
export let ariaLabel: string = 'Sankey diagram';
|
|
9
|
+
|
|
10
|
+
const VW = 320;
|
|
11
|
+
const NODE_W = 10;
|
|
12
|
+
const NODE_GAP = 8;
|
|
13
|
+
const PAD = 16;
|
|
14
|
+
const PALETTE = ['#006FFF', '#00C875', '#A78BFA', '#F5A623', '#50B8E7', '#F04949'];
|
|
15
|
+
|
|
16
|
+
interface PositionedNode { id: string; label: string; color: string; x: number; y: number; h: number; side: 'left' | 'right'; }
|
|
17
|
+
interface PathData { d: string; color: string; }
|
|
18
|
+
|
|
19
|
+
$: ({ positioned, paths } = (() => {
|
|
20
|
+
if (!nodes.length || !links.length) return { positioned: [] as PositionedNode[], paths: [] as PathData[] };
|
|
21
|
+
|
|
22
|
+
const nodeMap = new Map(nodes.map((n, i) => [n.id, { ...n, color: n.color ?? PALETTE[i % PALETTE.length] }]));
|
|
23
|
+
const outflow = new Map<string, number>();
|
|
24
|
+
const inflow = new Map<string, number>();
|
|
25
|
+
for (const l of links) {
|
|
26
|
+
outflow.set(l.source, (outflow.get(l.source) ?? 0) + l.value);
|
|
27
|
+
inflow.set(l.target, (inflow.get(l.target) ?? 0) + l.value);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const leftIds = [...new Set(links.map(l => l.source))].filter(id => !inflow.has(id));
|
|
31
|
+
const rightIds = [...new Set(links.map(l => l.target))].filter(id => !outflow.has(id));
|
|
32
|
+
const totalFlow = links.reduce((s, l) => s + l.value, 0);
|
|
33
|
+
|
|
34
|
+
const leftAvailH = height - PAD * 2 - (leftIds.length - 1) * NODE_GAP;
|
|
35
|
+
const rightAvailH = height - PAD * 2 - (rightIds.length - 1) * NODE_GAP;
|
|
36
|
+
|
|
37
|
+
const positioned: PositionedNode[] = [];
|
|
38
|
+
|
|
39
|
+
let y = PAD;
|
|
40
|
+
for (const id of leftIds) {
|
|
41
|
+
const n = nodeMap.get(id)!;
|
|
42
|
+
const h = Math.max(4, (outflow.get(id) ?? 0) / totalFlow * leftAvailH);
|
|
43
|
+
positioned.push({ id, label: n.label, color: n.color, x: PAD, y, h, side: 'left' });
|
|
44
|
+
y += h + NODE_GAP;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
y = PAD;
|
|
48
|
+
for (const id of rightIds) {
|
|
49
|
+
const n = nodeMap.get(id)!;
|
|
50
|
+
const h = Math.max(4, (inflow.get(id) ?? 0) / totalFlow * rightAvailH);
|
|
51
|
+
positioned.push({ id, label: n.label, color: n.color, x: VW - PAD - NODE_W, y, h, side: 'right' });
|
|
52
|
+
y += h + NODE_GAP;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const sourcePort = new Map<string, number>(positioned.filter(n => n.side === 'left').map(n => [n.id, n.y]));
|
|
56
|
+
const targetPort = new Map<string, number>(positioned.filter(n => n.side === 'right').map(n => [n.id, n.y]));
|
|
57
|
+
|
|
58
|
+
const paths: PathData[] = [];
|
|
59
|
+
for (const l of links) {
|
|
60
|
+
const srcNode = positioned.find(n => n.id === l.source);
|
|
61
|
+
const tgtNode = positioned.find(n => n.id === l.target);
|
|
62
|
+
if (!srcNode || !tgtNode) continue;
|
|
63
|
+
|
|
64
|
+
const lh = Math.max(1, l.value / totalFlow * leftAvailH);
|
|
65
|
+
const sy = sourcePort.get(l.source)!;
|
|
66
|
+
const ty = targetPort.get(l.target)!;
|
|
67
|
+
sourcePort.set(l.source, sy + lh);
|
|
68
|
+
targetPort.set(l.target, ty + lh);
|
|
69
|
+
|
|
70
|
+
const x1 = srcNode.x + NODE_W;
|
|
71
|
+
const x2 = tgtNode.x;
|
|
72
|
+
const cx = (x1 + x2) / 2;
|
|
73
|
+
const d = `M${x1},${sy.toFixed(1)} C${cx},${sy.toFixed(1)} ${cx},${ty.toFixed(1)} ${x2},${ty.toFixed(1)} L${x2},${(ty + lh).toFixed(1)} C${cx},${(ty + lh).toFixed(1)} ${cx},${(sy + lh).toFixed(1)} ${x1},${(sy + lh).toFixed(1)} Z`;
|
|
74
|
+
paths.push({ d, color: srcNode.color });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { positioned, paths };
|
|
78
|
+
})());
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<div role="img" aria-label={ariaLabel} style="width:100%;">
|
|
82
|
+
<svg viewBox="0 0 {VW} {height}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
|
|
83
|
+
{#each paths as p, i (i)}
|
|
84
|
+
<path d={p.d} fill={p.color} fill-opacity="0.35" />
|
|
85
|
+
{/each}
|
|
86
|
+
{#each positioned as n (n.id)}
|
|
87
|
+
<g>
|
|
88
|
+
<rect x={n.x} y={n.y.toFixed(1)} width={NODE_W} height={n.h.toFixed(1)} fill={n.color} rx="2" />
|
|
89
|
+
<text
|
|
90
|
+
x={n.side === 'left' ? n.x + NODE_W + 5 : n.x - 5}
|
|
91
|
+
y={(n.y + n.h / 2 + 4).toFixed(1)}
|
|
92
|
+
text-anchor={n.side === 'left' ? 'start' : 'end'}
|
|
93
|
+
fill="#C8C9D0"
|
|
94
|
+
font-size="9"
|
|
95
|
+
font-family="inherit"
|
|
96
|
+
>{n.label}</text>
|
|
97
|
+
</g>
|
|
98
|
+
{/each}
|
|
99
|
+
</svg>
|
|
100
|
+
</div>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export interface ScatterPoint {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
color?: string;
|
|
6
|
+
r?: number;
|
|
7
|
+
key?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export let points: ScatterPoint[] = [];
|
|
11
|
+
export let xRange: [number, number] | undefined = undefined;
|
|
12
|
+
export let yRange: [number, number] | undefined = undefined;
|
|
13
|
+
export let height: number = 160;
|
|
14
|
+
export let ariaLabel: string = 'Scatter plot';
|
|
15
|
+
|
|
16
|
+
const VW = 400;
|
|
17
|
+
const PAD = { t: 12, r: 8, b: 12, l: 8 };
|
|
18
|
+
|
|
19
|
+
$: xs = points.map((p) => p.x);
|
|
20
|
+
$: ys = points.map((p) => p.y);
|
|
21
|
+
$: x0 = xRange ? xRange[0] : xs.length ? Math.min(...xs) : 0;
|
|
22
|
+
$: x1 = xRange ? xRange[1] : xs.length ? Math.max(...xs) : 1;
|
|
23
|
+
$: y0 = yRange ? yRange[0] : ys.length ? Math.min(...ys) : 0;
|
|
24
|
+
$: y1 = yRange ? yRange[1] : ys.length ? Math.max(...ys) : 1;
|
|
25
|
+
$: xSpan = x1 - x0 || 1;
|
|
26
|
+
$: ySpan = y1 - y0 || 1;
|
|
27
|
+
$: chartW = VW - PAD.l - PAD.r;
|
|
28
|
+
$: chartH = height - PAD.t - PAD.b;
|
|
29
|
+
|
|
30
|
+
$: gridLines = [0.25, 0.5, 0.75].map((f) => ({
|
|
31
|
+
gx: (PAD.l + f * chartW).toFixed(1),
|
|
32
|
+
gy: (PAD.t + (1 - f) * chartH).toFixed(1),
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
$: dots = points.map((p, i) => ({
|
|
36
|
+
cx: (PAD.l + ((p.x - x0) / xSpan) * chartW).toFixed(1),
|
|
37
|
+
cy: (PAD.t + chartH - ((p.y - y0) / ySpan) * chartH).toFixed(1),
|
|
38
|
+
r: p.r ?? 5,
|
|
39
|
+
color: p.color ?? '#006FFF',
|
|
40
|
+
key: p.key ?? String(i),
|
|
41
|
+
}));
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<div role="img" aria-label={ariaLabel} style="width:100%;">
|
|
45
|
+
<svg viewBox="0 0 {VW} {height}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
|
|
46
|
+
{#each gridLines as g}
|
|
47
|
+
<line x1={g.gx} y1={PAD.t} x2={g.gx} y2={PAD.t + chartH} stroke="rgba(255,255,255,0.06)" stroke-width="1" />
|
|
48
|
+
<line x1={PAD.l} y1={g.gy} x2={VW - PAD.r} y2={g.gy} stroke="rgba(255,255,255,0.06)" stroke-width="1" />
|
|
49
|
+
{/each}
|
|
50
|
+
{#each dots as d (d.key)}
|
|
51
|
+
<circle cx={d.cx} cy={d.cy} r={d.r} fill={d.color} fill-opacity="0.8" />
|
|
52
|
+
{/each}
|
|
53
|
+
</svg>
|
|
54
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import SearchIcon from '../icons/SearchIcon.svelte';
|
|
3
|
+
export let placeholder: string = '';
|
|
4
|
+
export let value: string = '';
|
|
5
|
+
export let ariaLabel: string = '';
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<div class="search" role="search">
|
|
9
|
+
<span aria-hidden="true" style="display:inline-flex"><SearchIcon size={14} style="color:#6E7079" /></span>
|
|
10
|
+
<input type="search" {placeholder} aria-label={ariaLabel || placeholder} bind:value on:input on:change />
|
|
11
|
+
</div>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<script context="module" lang="ts">
|
|
2
|
+
export interface SegmentOption {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<script lang="ts">
|
|
10
|
+
import { tick } from 'svelte';
|
|
11
|
+
|
|
12
|
+
export let label: string;
|
|
13
|
+
export let options: SegmentOption[] = [];
|
|
14
|
+
export let value: string = options[0]?.value ?? '';
|
|
15
|
+
|
|
16
|
+
let root: HTMLDivElement;
|
|
17
|
+
|
|
18
|
+
async function handleKeyDown(e: KeyboardEvent) {
|
|
19
|
+
const enabled = options.filter((o) => !o.disabled);
|
|
20
|
+
const idx = enabled.findIndex((o) => o.value === value);
|
|
21
|
+
let next = -1;
|
|
22
|
+
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
|
|
23
|
+
e.preventDefault();
|
|
24
|
+
next = (idx + 1) % enabled.length;
|
|
25
|
+
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
next = (idx - 1 + enabled.length) % enabled.length;
|
|
28
|
+
}
|
|
29
|
+
if (next !== -1) {
|
|
30
|
+
value = enabled[next].value;
|
|
31
|
+
await tick();
|
|
32
|
+
root?.querySelector<HTMLButtonElement>('button[tabindex="0"]')?.focus();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<div bind:this={root} role="radiogroup" aria-label={label} class="seg-ctrl" tabindex="-1" on:keydown={handleKeyDown}>
|
|
38
|
+
{#each options as opt (opt.value)}
|
|
39
|
+
<button
|
|
40
|
+
type="button"
|
|
41
|
+
role="radio"
|
|
42
|
+
aria-checked={value === opt.value}
|
|
43
|
+
disabled={opt.disabled}
|
|
44
|
+
tabindex={value === opt.value ? 0 : -1}
|
|
45
|
+
class="seg-ctrl__btn{value === opt.value ? ' seg-ctrl__btn--active' : ''}"
|
|
46
|
+
on:click={() => { value = opt.value; }}
|
|
47
|
+
>
|
|
48
|
+
{opt.label}
|
|
49
|
+
</button>
|
|
50
|
+
{/each}
|
|
51
|
+
</div>
|