@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,164 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export interface ContourPoint {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export let points: ContourPoint[] = [];
|
|
8
|
+
export let xRange: [number, number] | undefined = undefined;
|
|
9
|
+
export let yRange: [number, number] | undefined = undefined;
|
|
10
|
+
export let height = 200;
|
|
11
|
+
export let bandwidth = 3.5;
|
|
12
|
+
export let color = '#006FFF';
|
|
13
|
+
export let ariaLabel = 'Contour plot';
|
|
14
|
+
|
|
15
|
+
const VW = 340;
|
|
16
|
+
const PAD_L = 28;
|
|
17
|
+
const PAD_R = 8;
|
|
18
|
+
const PAD_T = 8;
|
|
19
|
+
const PAD_B = 20;
|
|
20
|
+
const GW = 38;
|
|
21
|
+
const GH = 28;
|
|
22
|
+
const LEVELS = [0.12, 0.30, 0.54, 0.78];
|
|
23
|
+
|
|
24
|
+
function buildGrid(pts: Array<{ gx: number; gy: number }>, bw: number): number[][] {
|
|
25
|
+
const bw2 = bw * bw;
|
|
26
|
+
const g: number[][] = Array.from({ length: GH + 1 }, () => new Array(GW + 1).fill(0));
|
|
27
|
+
for (let r = 0; r <= GH; r++) {
|
|
28
|
+
for (let c = 0; c <= GW; c++) {
|
|
29
|
+
let d = 0;
|
|
30
|
+
for (const p of pts) {
|
|
31
|
+
const dx = c - p.gx, dy = r - p.gy;
|
|
32
|
+
d += Math.exp(-0.5 * (dx * dx + dy * dy) / bw2);
|
|
33
|
+
}
|
|
34
|
+
g[r][c] = d;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
let mx = 0;
|
|
38
|
+
for (const row of g) for (const v of row) if (v > mx) mx = v;
|
|
39
|
+
if (mx > 0) for (const row of g) for (let c = 0; c < row.length; c++) row[c] /= mx;
|
|
40
|
+
return g;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function edgeCross(v0: number, v1: number, t: number): number {
|
|
44
|
+
const d = v1 - v0;
|
|
45
|
+
return Math.abs(d) < 1e-9 ? 0.5 : Math.max(0, Math.min(1, (t - v0) / d));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type P = [number, number];
|
|
49
|
+
|
|
50
|
+
function marchCell(
|
|
51
|
+
tl: number, tr: number, bl: number, br: number, t: number
|
|
52
|
+
): Array<[P, P]> {
|
|
53
|
+
const a = tl >= t ? 1 : 0;
|
|
54
|
+
const b = tr >= t ? 1 : 0;
|
|
55
|
+
const c = bl >= t ? 1 : 0;
|
|
56
|
+
const d = br >= t ? 1 : 0;
|
|
57
|
+
const T = (): P => [edgeCross(tl, tr, t), 0];
|
|
58
|
+
const R = (): P => [1, edgeCross(tr, br, t)];
|
|
59
|
+
const B = (): P => [edgeCross(bl, br, t), 1];
|
|
60
|
+
const L = (): P => [0, edgeCross(tl, bl, t)];
|
|
61
|
+
switch ((a << 3) | (b << 2) | (c << 1) | d) {
|
|
62
|
+
case 0: case 15: return [];
|
|
63
|
+
case 1: return [[R(), B()]];
|
|
64
|
+
case 2: return [[B(), L()]];
|
|
65
|
+
case 3: return [[L(), R()]];
|
|
66
|
+
case 4: return [[T(), R()]];
|
|
67
|
+
case 5: return [[T(), B()]];
|
|
68
|
+
case 6: { const m = (tl+tr+bl+br)/4; return m >= t ? [[T(),L()],[R(),B()]] : [[T(),R()],[L(),B()]]; }
|
|
69
|
+
case 7: return [[T(), L()]];
|
|
70
|
+
case 8: return [[T(), L()]];
|
|
71
|
+
case 9: { const m = (tl+tr+bl+br)/4; return m >= t ? [[T(),R()],[L(),B()]] : [[T(),L()],[R(),B()]]; }
|
|
72
|
+
case 10: return [[T(), B()]];
|
|
73
|
+
case 11: return [[T(), R()]];
|
|
74
|
+
case 12: return [[L(), R()]];
|
|
75
|
+
case 13: return [[B(), L()]];
|
|
76
|
+
case 14: return [[R(), B()]];
|
|
77
|
+
}
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
$: PW = VW - PAD_L - PAD_R;
|
|
82
|
+
$: PH = height - PAD_T - PAD_B;
|
|
83
|
+
$: xs = points.map((p) => p.x);
|
|
84
|
+
$: ys = points.map((p) => p.y);
|
|
85
|
+
$: xMin = xRange ? xRange[0] : (xs.length ? Math.min(...xs) : 0);
|
|
86
|
+
$: xMax = xRange ? xRange[1] : (xs.length ? Math.max(...xs) : 1);
|
|
87
|
+
$: yMin = yRange ? yRange[0] : (ys.length ? Math.min(...ys) : 0);
|
|
88
|
+
$: yMax = yRange ? yRange[1] : (ys.length ? Math.max(...ys) : 1);
|
|
89
|
+
$: xSpan = xMax - xMin || 1;
|
|
90
|
+
$: ySpan = yMax - yMin || 1;
|
|
91
|
+
|
|
92
|
+
$: gridPts = points.map((p) => ({
|
|
93
|
+
gx: ((p.x - xMin) / xSpan) * GW,
|
|
94
|
+
gy: (1 - (p.y - yMin) / ySpan) * GH,
|
|
95
|
+
}));
|
|
96
|
+
$: grid = buildGrid(gridPts, bandwidth);
|
|
97
|
+
$: cellW = PW / GW;
|
|
98
|
+
$: cellH = PH / GH;
|
|
99
|
+
|
|
100
|
+
function svgX(col: number, lx: number): number { return PAD_L + (col + lx) * cellW; }
|
|
101
|
+
function svgY(row: number, ly: number): number { return PAD_T + (row + ly) * cellH; }
|
|
102
|
+
function tyY(v: number): number { return PAD_T + (1 - (v - yMin) / ySpan) * PH; }
|
|
103
|
+
function txX(v: number): number { return PAD_L + ((v - xMin) / xSpan) * PW; }
|
|
104
|
+
|
|
105
|
+
$: yTicks = [yMin, (yMin + yMax) / 2, yMax];
|
|
106
|
+
$: xTicks = [xMin, (xMin + xMax) / 2, xMax];
|
|
107
|
+
|
|
108
|
+
$: levelLines = LEVELS.map((t, li) => {
|
|
109
|
+
const opacity = 0.28 + li * 0.18;
|
|
110
|
+
const sw = 0.55 + li * 0.28;
|
|
111
|
+
const lines: Array<{ key: string; x1: number; y1: number; x2: number; y2: number }> = [];
|
|
112
|
+
for (let r = 0; r < GH; r++) {
|
|
113
|
+
for (let col = 0; col < GW; col++) {
|
|
114
|
+
const segs = marchCell(
|
|
115
|
+
grid[r][col], grid[r][col + 1],
|
|
116
|
+
grid[r + 1][col], grid[r + 1][col + 1], t
|
|
117
|
+
);
|
|
118
|
+
segs.forEach(([[lx0, ly0], [lx1, ly1]], si) => {
|
|
119
|
+
lines.push({
|
|
120
|
+
key: `${r}-${col}-${si}`,
|
|
121
|
+
x1: svgX(col, lx0), y1: svgY(r, ly0),
|
|
122
|
+
x2: svgX(col, lx1), y2: svgY(r, ly1),
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return { opacity, sw, lines };
|
|
128
|
+
});
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
{#if points.length > 0}
|
|
132
|
+
<div role="img" aria-label={ariaLabel} style="width:100%;">
|
|
133
|
+
<svg viewBox="0 0 {VW} {height}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
|
|
134
|
+
{#each yTicks as v, i (i)}
|
|
135
|
+
<line x1={PAD_L} y1={tyY(v)} x2={PAD_L + PW} y2={tyY(v)} stroke="rgba(255,255,255,0.06)" stroke-width="1" />
|
|
136
|
+
<text x={PAD_L - 4} y={tyY(v) + 3} fill="#6E7079" font-size="8" text-anchor="end" font-family="inherit">{Math.round(v)}</text>
|
|
137
|
+
{/each}
|
|
138
|
+
|
|
139
|
+
{#each points as p, i (i)}
|
|
140
|
+
<circle
|
|
141
|
+
cx={PAD_L + ((p.x - xMin) / xSpan) * PW}
|
|
142
|
+
cy={PAD_T + (1 - (p.y - yMin) / ySpan) * PH}
|
|
143
|
+
r="1.5"
|
|
144
|
+
fill={color}
|
|
145
|
+
opacity="0.2"
|
|
146
|
+
/>
|
|
147
|
+
{/each}
|
|
148
|
+
|
|
149
|
+
{#each levelLines as { opacity, sw, lines }, li (li)}
|
|
150
|
+
{#each lines as s (s.key)}
|
|
151
|
+
<line
|
|
152
|
+
x1={s.x1} y1={s.y1} x2={s.x2} y2={s.y2}
|
|
153
|
+
stroke={color} stroke-width={sw} opacity={opacity}
|
|
154
|
+
stroke-linecap="round"
|
|
155
|
+
/>
|
|
156
|
+
{/each}
|
|
157
|
+
{/each}
|
|
158
|
+
|
|
159
|
+
{#each xTicks as v, i (i)}
|
|
160
|
+
<text x={txX(v)} y={height - 4} fill="#6E7079" font-size="8" text-anchor="middle" font-family="inherit">{Math.round(v)}</text>
|
|
161
|
+
{/each}
|
|
162
|
+
</svg>
|
|
163
|
+
</div>
|
|
164
|
+
{/if}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export let text: string;
|
|
3
|
+
export let label: string = 'Copy';
|
|
4
|
+
|
|
5
|
+
let copied = false;
|
|
6
|
+
let timer: ReturnType<typeof setTimeout>;
|
|
7
|
+
|
|
8
|
+
function handleCopy() {
|
|
9
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
10
|
+
copied = true;
|
|
11
|
+
clearTimeout(timer);
|
|
12
|
+
timer = setTimeout(() => { copied = false; }, 2000);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<button
|
|
18
|
+
type="button"
|
|
19
|
+
class="copy-btn {copied ? 'copy-btn--done' : ''}"
|
|
20
|
+
aria-label={copied ? 'Copied!' : label}
|
|
21
|
+
on:click={handleCopy}
|
|
22
|
+
>
|
|
23
|
+
{#if copied}
|
|
24
|
+
<svg viewBox="0 0 14 14" width="14" height="14" fill="none" aria-hidden="true" focusable="false">
|
|
25
|
+
<path d="M2.5 7.5l3.5 3.5L12 3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
26
|
+
</svg>
|
|
27
|
+
{:else}
|
|
28
|
+
<svg viewBox="0 0 14 14" width="14" height="14" fill="none" aria-hidden="true" focusable="false">
|
|
29
|
+
<rect x="4" y="1" width="9" height="10" rx="1.5" stroke="currentColor" stroke-width="1.5"/>
|
|
30
|
+
<rect x="1" y="4" width="9" height="10" rx="1.5" stroke="currentColor" stroke-width="1.5"/>
|
|
31
|
+
</svg>
|
|
32
|
+
{/if}
|
|
33
|
+
</button>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export let labels: string[] = [];
|
|
3
|
+
export let data: number[][] = [];
|
|
4
|
+
export let cellSize: number = 52;
|
|
5
|
+
export let ariaLabel: string = 'Correlation matrix';
|
|
6
|
+
|
|
7
|
+
const LABEL_W = 68;
|
|
8
|
+
const LABEL_H = 26;
|
|
9
|
+
const DARK = [26, 29, 43];
|
|
10
|
+
const BLUE = [0, 111, 255];
|
|
11
|
+
const RED = [255, 59, 48];
|
|
12
|
+
|
|
13
|
+
function lerp(a: number, b: number, t: number): number {
|
|
14
|
+
return Math.round(a + (b - a) * t);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function cellColor(v: number): string {
|
|
18
|
+
const t = Math.max(0, Math.min(1, Math.abs(v)));
|
|
19
|
+
const [r0, g0, b0] = DARK;
|
|
20
|
+
if (v >= 0) {
|
|
21
|
+
const [r1, g1, b1] = BLUE;
|
|
22
|
+
return `rgb(${lerp(r0, r1, t)},${lerp(g0, g1, t)},${lerp(b0, b1, t)})`;
|
|
23
|
+
}
|
|
24
|
+
const [r1, g1, b1] = RED;
|
|
25
|
+
return `rgb(${lerp(r0, r1, t)},${lerp(g0, g1, t)},${lerp(b0, b1, t)})`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
$: n = labels.length;
|
|
29
|
+
$: svgW = LABEL_W + n * cellSize;
|
|
30
|
+
$: svgH = LABEL_H + n * cellSize;
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<div role="img" aria-label={ariaLabel} style="width:100%;">
|
|
34
|
+
<svg
|
|
35
|
+
viewBox="0 0 {svgW} {svgH}"
|
|
36
|
+
style="width:100%;height:auto;display:block;"
|
|
37
|
+
aria-hidden="true"
|
|
38
|
+
focusable="false"
|
|
39
|
+
>
|
|
40
|
+
{#each labels as label, j (j)}
|
|
41
|
+
<text
|
|
42
|
+
x={LABEL_W + j * cellSize + cellSize / 2}
|
|
43
|
+
y={LABEL_H - 4}
|
|
44
|
+
text-anchor="middle"
|
|
45
|
+
font-size="9"
|
|
46
|
+
fill="#A4A7B5"
|
|
47
|
+
font-family="inherit"
|
|
48
|
+
>{label}</text>
|
|
49
|
+
{/each}
|
|
50
|
+
|
|
51
|
+
{#each labels as label, i (i)}
|
|
52
|
+
<text
|
|
53
|
+
x={LABEL_W - 4}
|
|
54
|
+
y={LABEL_H + i * cellSize + cellSize / 2}
|
|
55
|
+
text-anchor="end"
|
|
56
|
+
dominant-baseline="middle"
|
|
57
|
+
font-size="9"
|
|
58
|
+
fill="#A4A7B5"
|
|
59
|
+
font-family="inherit"
|
|
60
|
+
>{label}</text>
|
|
61
|
+
{/each}
|
|
62
|
+
|
|
63
|
+
{#each data as row, i (i)}
|
|
64
|
+
{#each row as v, j (`${i}-${j}`)}
|
|
65
|
+
{@const x = LABEL_W + j * cellSize}
|
|
66
|
+
{@const y = LABEL_H + i * cellSize}
|
|
67
|
+
<g>
|
|
68
|
+
<rect
|
|
69
|
+
x={x + 1}
|
|
70
|
+
y={y + 1}
|
|
71
|
+
width={cellSize - 2}
|
|
72
|
+
height={cellSize - 2}
|
|
73
|
+
fill={cellColor(v)}
|
|
74
|
+
rx="3"
|
|
75
|
+
/>
|
|
76
|
+
<text
|
|
77
|
+
x={x + cellSize / 2}
|
|
78
|
+
y={y + cellSize / 2}
|
|
79
|
+
text-anchor="middle"
|
|
80
|
+
dominant-baseline="middle"
|
|
81
|
+
font-size="10"
|
|
82
|
+
fill={Math.abs(v) >= 0.5 ? '#fff' : '#A4A7B5'}
|
|
83
|
+
font-family="inherit"
|
|
84
|
+
font-weight={i === j ? '700' : '400'}
|
|
85
|
+
>{v.toFixed(2)}</text>
|
|
86
|
+
</g>
|
|
87
|
+
{/each}
|
|
88
|
+
{/each}
|
|
89
|
+
</svg>
|
|
90
|
+
</div>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount, onDestroy } from 'svelte';
|
|
3
|
+
|
|
4
|
+
export let from: number = 0;
|
|
5
|
+
export let to: number;
|
|
6
|
+
export let duration: number = 1200;
|
|
7
|
+
export let decimals: number = 0;
|
|
8
|
+
export let prefix: string = '';
|
|
9
|
+
export let suffix: string = '';
|
|
10
|
+
export let separator: string = ',';
|
|
11
|
+
let klass: string = '';
|
|
12
|
+
export { klass as class };
|
|
13
|
+
|
|
14
|
+
let current = from;
|
|
15
|
+
let rafId: number;
|
|
16
|
+
|
|
17
|
+
function easeOut(t: number) { return 1 - Math.pow(1 - t, 3); }
|
|
18
|
+
|
|
19
|
+
function fmt(n: number) {
|
|
20
|
+
const fixed = n.toFixed(decimals);
|
|
21
|
+
if (!separator) return fixed;
|
|
22
|
+
const [int, dec] = fixed.split('.');
|
|
23
|
+
const intFmt = int.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
|
|
24
|
+
return dec !== undefined ? `${intFmt}.${dec}` : intFmt;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
$: label = `${prefix}${fmt(to)}${suffix}`;
|
|
28
|
+
$: display = `${prefix}${fmt(current)}${suffix}`;
|
|
29
|
+
|
|
30
|
+
onMount(() => {
|
|
31
|
+
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
32
|
+
if (reduced) { current = to; return; }
|
|
33
|
+
const t0 = performance.now();
|
|
34
|
+
const range = to - from;
|
|
35
|
+
function tick(now: number) {
|
|
36
|
+
const t = Math.min((now - t0) / duration, 1);
|
|
37
|
+
current = from + range * easeOut(t);
|
|
38
|
+
if (t < 1) rafId = requestAnimationFrame(tick);
|
|
39
|
+
}
|
|
40
|
+
rafId = requestAnimationFrame(tick);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
onDestroy(() => { if (rafId) cancelAnimationFrame(rafId); });
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<span class={klass || undefined}>
|
|
47
|
+
<span aria-hidden="true">{display}</span>
|
|
48
|
+
<span class="sr-only">{label}</span>
|
|
49
|
+
</span>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export interface CdfSeries {
|
|
3
|
+
label: string;
|
|
4
|
+
color: string;
|
|
5
|
+
values: number[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export let series: CdfSeries[] = [];
|
|
9
|
+
export let guides: number[] = [50, 95];
|
|
10
|
+
export let height: number = 160;
|
|
11
|
+
export let unit: string = '';
|
|
12
|
+
export let ariaLabel: string = 'Cumulative distribution chart';
|
|
13
|
+
|
|
14
|
+
const W = 400, PL = 38, PR = 12, PT = 14, PB = 28;
|
|
15
|
+
|
|
16
|
+
$: allVals = series.flatMap((s) => s.values);
|
|
17
|
+
$: xMin = allVals.length ? Math.min(...allVals) : 0;
|
|
18
|
+
$: xMax = allVals.length ? Math.max(...allVals) : 1;
|
|
19
|
+
$: xRange = (xMax - xMin) || 1;
|
|
20
|
+
$: cW = W - PL - PR;
|
|
21
|
+
$: cH = height - PT - PB;
|
|
22
|
+
|
|
23
|
+
function toX(v: number) { return PL + ((v - xMin) / xRange) * cW; }
|
|
24
|
+
function toY(p: number) { return PT + (1 - p) * cH; }
|
|
25
|
+
|
|
26
|
+
$: paths = series.map((s) => {
|
|
27
|
+
const sorted = [...s.values].sort((a, b) => a - b);
|
|
28
|
+
const n = sorted.length;
|
|
29
|
+
let d = `M${toX(xMin).toFixed(1)},${toY(0).toFixed(1)}`;
|
|
30
|
+
sorted.forEach((v, i) => {
|
|
31
|
+
d += ` H${toX(v).toFixed(1)} V${toY((i + 1) / n).toFixed(1)}`;
|
|
32
|
+
});
|
|
33
|
+
d += ` H${toX(xMax).toFixed(1)}`;
|
|
34
|
+
return { label: s.label, color: s.color, d };
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
$: yTicks = [0, 0.25, 0.5, 0.75, 1].map((p) => ({ y: toY(p), label: `${Math.round(p * 100)}%` }));
|
|
38
|
+
$: xStep = xRange / 4;
|
|
39
|
+
$: xTicks = Array.from({ length: 5 }, (_, i) => {
|
|
40
|
+
const v = xMin + xStep * i;
|
|
41
|
+
return { x: toX(v), label: `${Math.round(v)}${unit}` };
|
|
42
|
+
});
|
|
43
|
+
$: guideLines = guides.map((g) => ({ p: g, y: toY(g / 100) }));
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
{#if allVals.length}
|
|
47
|
+
<div role="img" aria-label={ariaLabel} style="line-height:0;">
|
|
48
|
+
<svg viewBox="0 0 {W} {height}" width="100%" height={height} aria-hidden="true" focusable="false">
|
|
49
|
+
{#each yTicks as t (t.label)}
|
|
50
|
+
<line x1={PL} x2={W - PR} y1={t.y} y2={t.y} stroke="rgba(255,255,255,0.06)" stroke-width="1" />
|
|
51
|
+
{/each}
|
|
52
|
+
{#each guideLines as g (g.p)}
|
|
53
|
+
<line x1={PL} x2={W - PR} y1={g.y} y2={g.y} stroke="rgba(255,255,255,0.18)" stroke-width="1" stroke-dasharray="4 3" />
|
|
54
|
+
<text x={W - PR - 2} y={g.y - 3} text-anchor="end" fill="#6E7079" font-size="9" font-family="inherit">p{g.p}</text>
|
|
55
|
+
{/each}
|
|
56
|
+
{#each paths as s (s.label)}
|
|
57
|
+
<path d={s.d} fill="none" stroke={s.color} stroke-width="2" stroke-linejoin="round" />
|
|
58
|
+
{/each}
|
|
59
|
+
{#each yTicks as t (t.label)}
|
|
60
|
+
<text x={PL - 4} y={t.y + 4} text-anchor="end" fill="#6E7079" font-size="9" font-family="inherit">{t.label}</text>
|
|
61
|
+
{/each}
|
|
62
|
+
{#each xTicks as t, i (i)}
|
|
63
|
+
<text x={t.x} y={height - PB + 14} text-anchor="middle" fill="#6E7079" font-size="9" font-family="inherit">{t.label}</text>
|
|
64
|
+
{/each}
|
|
65
|
+
</svg>
|
|
66
|
+
</div>
|
|
67
|
+
{/if}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
<script context="module" lang="ts">
|
|
2
|
+
let counter = 0;
|
|
3
|
+
const DAYS = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
|
|
4
|
+
const MONTHS = [
|
|
5
|
+
'January', 'February', 'March', 'April', 'May', 'June',
|
|
6
|
+
'July', 'August', 'September', 'October', 'November', 'December',
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
function isSameDay(a: Date, b: Date) {
|
|
10
|
+
return a.getFullYear() === b.getFullYear() &&
|
|
11
|
+
a.getMonth() === b.getMonth() &&
|
|
12
|
+
a.getDate() === b.getDate();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function formatDate(d: Date) {
|
|
16
|
+
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
|
17
|
+
const dd = String(d.getDate()).padStart(2, '0');
|
|
18
|
+
return `${mm}/${dd}/${d.getFullYear()}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function buildGrid(year: number, month: number): Date[] {
|
|
22
|
+
const first = new Date(year, month, 1);
|
|
23
|
+
const last = new Date(year, month + 1, 0);
|
|
24
|
+
const grid: Date[] = [];
|
|
25
|
+
for (let i = first.getDay() - 1; i >= 0; i--) {
|
|
26
|
+
grid.push(new Date(year, month, -i));
|
|
27
|
+
}
|
|
28
|
+
for (let d = 1; d <= last.getDate(); d++) {
|
|
29
|
+
grid.push(new Date(year, month, d));
|
|
30
|
+
}
|
|
31
|
+
while (grid.length < 42) {
|
|
32
|
+
grid.push(new Date(year, month + 1, grid.length - first.getDay() - last.getDate() + 1));
|
|
33
|
+
}
|
|
34
|
+
return grid;
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<script lang="ts">
|
|
39
|
+
import { createEventDispatcher, onMount, onDestroy, tick } from 'svelte';
|
|
40
|
+
|
|
41
|
+
export let value: Date | null = null;
|
|
42
|
+
export let placeholder: string = 'Pick a date';
|
|
43
|
+
export let disabled: boolean = false;
|
|
44
|
+
|
|
45
|
+
const dispatch = createEventDispatcher<{ change: Date }>();
|
|
46
|
+
const uid = `dash-ui-dp-${++counter}`;
|
|
47
|
+
const calId = `${uid}-cal`;
|
|
48
|
+
const today = new Date();
|
|
49
|
+
|
|
50
|
+
let open = false;
|
|
51
|
+
let viewYear = value?.getFullYear() ?? today.getFullYear();
|
|
52
|
+
let viewMonth = value?.getMonth() ?? today.getMonth();
|
|
53
|
+
let focusIdx = 0;
|
|
54
|
+
let triggerEl: HTMLButtonElement;
|
|
55
|
+
let calEl: HTMLDivElement;
|
|
56
|
+
let dayEls: (HTMLButtonElement | null)[] = Array(42).fill(null);
|
|
57
|
+
|
|
58
|
+
$: grid = buildGrid(viewYear, viewMonth);
|
|
59
|
+
$: label = `${MONTHS[viewMonth]} ${viewYear}`;
|
|
60
|
+
|
|
61
|
+
function openCal() {
|
|
62
|
+
if (disabled) return;
|
|
63
|
+
const initial = value
|
|
64
|
+
? grid.findIndex((d) => isSameDay(d, value!))
|
|
65
|
+
: grid.findIndex((d) => isSameDay(d, today));
|
|
66
|
+
focusIdx = Math.max(0, initial);
|
|
67
|
+
open = true;
|
|
68
|
+
tick().then(() => dayEls[focusIdx]?.focus());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function closeCal() {
|
|
72
|
+
open = false;
|
|
73
|
+
triggerEl?.focus();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function select(d: Date) {
|
|
77
|
+
dispatch('change', d);
|
|
78
|
+
closeCal();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function prevMonth() {
|
|
82
|
+
if (viewMonth === 0) { viewYear -= 1; viewMonth = 11; }
|
|
83
|
+
else viewMonth -= 1;
|
|
84
|
+
tick().then(() => dayEls[focusIdx]?.focus());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function nextMonth() {
|
|
88
|
+
if (viewMonth === 11) { viewYear += 1; viewMonth = 0; }
|
|
89
|
+
else viewMonth += 1;
|
|
90
|
+
tick().then(() => dayEls[focusIdx]?.focus());
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function onGridKey(e: KeyboardEvent, idx: number) {
|
|
94
|
+
let next = idx;
|
|
95
|
+
if (e.key === 'ArrowRight') { e.preventDefault(); next = idx + 1; }
|
|
96
|
+
else if (e.key === 'ArrowLeft') { e.preventDefault(); next = idx - 1; }
|
|
97
|
+
else if (e.key === 'ArrowDown') { e.preventDefault(); next = idx + 7; }
|
|
98
|
+
else if (e.key === 'ArrowUp') { e.preventDefault(); next = idx - 7; }
|
|
99
|
+
else if (e.key === 'Home') { e.preventDefault(); next = Math.floor(idx / 7) * 7; }
|
|
100
|
+
else if (e.key === 'End') { e.preventDefault(); next = Math.floor(idx / 7) * 7 + 6; }
|
|
101
|
+
else if (e.key === 'PageDown') { e.preventDefault(); nextMonth(); return; }
|
|
102
|
+
else if (e.key === 'PageUp') { e.preventDefault(); prevMonth(); return; }
|
|
103
|
+
else if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); select(grid[idx]); return; }
|
|
104
|
+
else if (e.key === 'Escape') { e.preventDefault(); closeCal(); return; }
|
|
105
|
+
else if (e.key === 'Tab') { closeCal(); return; }
|
|
106
|
+
else return;
|
|
107
|
+
if (next < 0) { prevMonth(); return; }
|
|
108
|
+
if (next >= grid.length) { nextMonth(); return; }
|
|
109
|
+
focusIdx = next;
|
|
110
|
+
tick().then(() => dayEls[focusIdx]?.focus());
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function handleOutside(e: MouseEvent) {
|
|
114
|
+
if (open && !calEl?.contains(e.target as Node) && !triggerEl?.contains(e.target as Node)) {
|
|
115
|
+
open = false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
onMount(() => document.addEventListener('mousedown', handleOutside));
|
|
120
|
+
onDestroy(() => document.removeEventListener('mousedown', handleOutside));
|
|
121
|
+
</script>
|
|
122
|
+
|
|
123
|
+
<div class="dp-root">
|
|
124
|
+
<button
|
|
125
|
+
bind:this={triggerEl}
|
|
126
|
+
type="button"
|
|
127
|
+
class="btn dp-trigger"
|
|
128
|
+
aria-haspopup="dialog"
|
|
129
|
+
aria-expanded={open}
|
|
130
|
+
aria-controls={open ? calId : undefined}
|
|
131
|
+
{disabled}
|
|
132
|
+
on:click={() => open ? closeCal() : openCal()}
|
|
133
|
+
>
|
|
134
|
+
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
|
|
135
|
+
<rect x="1" y="2" width="12" height="11" rx="1.5" />
|
|
136
|
+
<path d="M1 6h12M4 1v2M10 1v2" />
|
|
137
|
+
</svg>
|
|
138
|
+
{value ? formatDate(value) : placeholder}
|
|
139
|
+
</button>
|
|
140
|
+
|
|
141
|
+
{#if open}
|
|
142
|
+
<div
|
|
143
|
+
id={calId}
|
|
144
|
+
bind:this={calEl}
|
|
145
|
+
role="dialog"
|
|
146
|
+
aria-modal="true"
|
|
147
|
+
aria-label={`Choose date, ${label}`}
|
|
148
|
+
class="dp-cal"
|
|
149
|
+
>
|
|
150
|
+
<div class="dp-header">
|
|
151
|
+
<button type="button" class="dp-nav" on:click={prevMonth} aria-label="Previous month">
|
|
152
|
+
<svg width="8" height="12" viewBox="0 0 8 12" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
|
|
153
|
+
<path d="M7 1L2 6l5 5" />
|
|
154
|
+
</svg>
|
|
155
|
+
</button>
|
|
156
|
+
<span class="dp-month-label" aria-live="polite">{label}</span>
|
|
157
|
+
<button type="button" class="dp-nav" on:click={nextMonth} aria-label="Next month">
|
|
158
|
+
<svg width="8" height="12" viewBox="0 0 8 12" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
|
|
159
|
+
<path d="M1 1l5 5-5 5" />
|
|
160
|
+
</svg>
|
|
161
|
+
</button>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<table role="grid" class="dp-grid" aria-label={label}>
|
|
165
|
+
<thead>
|
|
166
|
+
<tr>
|
|
167
|
+
{#each DAYS as d}
|
|
168
|
+
<th scope="col" abbr={d}>{d}</th>
|
|
169
|
+
{/each}
|
|
170
|
+
</tr>
|
|
171
|
+
</thead>
|
|
172
|
+
<tbody>
|
|
173
|
+
{#each Array(6) as _, row}
|
|
174
|
+
<tr>
|
|
175
|
+
{#each Array(7) as _, col}
|
|
176
|
+
{@const idx = row * 7 + col}
|
|
177
|
+
{@const day = grid[idx]}
|
|
178
|
+
{@const outside = day.getMonth() !== viewMonth}
|
|
179
|
+
{@const isToday = isSameDay(day, today)}
|
|
180
|
+
{@const isSelected = value ? isSameDay(day, value) : false}
|
|
181
|
+
<td role="gridcell" aria-selected={isSelected}>
|
|
182
|
+
<button
|
|
183
|
+
bind:this={dayEls[idx]}
|
|
184
|
+
type="button"
|
|
185
|
+
tabindex={idx === focusIdx ? 0 : -1}
|
|
186
|
+
class="dp-day{outside ? ' dp-day--outside' : ''}{isToday && !isSelected ? ' dp-day--today' : ''}{isSelected ? ' dp-day--selected' : ''}"
|
|
187
|
+
aria-label={day.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' })}
|
|
188
|
+
on:keydown={(e) => onGridKey(e, idx)}
|
|
189
|
+
on:click={() => select(day)}
|
|
190
|
+
>
|
|
191
|
+
{day.getDate()}
|
|
192
|
+
</button>
|
|
193
|
+
</td>
|
|
194
|
+
{/each}
|
|
195
|
+
</tr>
|
|
196
|
+
{/each}
|
|
197
|
+
</tbody>
|
|
198
|
+
</table>
|
|
199
|
+
</div>
|
|
200
|
+
{/if}
|
|
201
|
+
</div>
|