@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,93 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export interface HistogramBin {
|
|
3
|
+
x0: number;
|
|
4
|
+
x1: number;
|
|
5
|
+
count: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export let bins: HistogramBin[] = [];
|
|
9
|
+
export let height: number = 160;
|
|
10
|
+
export let color: string = '#006FFF';
|
|
11
|
+
export let xUnit: string = '';
|
|
12
|
+
export let ariaLabel: string = 'Histogram';
|
|
13
|
+
|
|
14
|
+
const VW = 320;
|
|
15
|
+
const PAD_L = 36;
|
|
16
|
+
const PAD_R = 8;
|
|
17
|
+
const PAD_T = 8;
|
|
18
|
+
const PAD_B = 28;
|
|
19
|
+
|
|
20
|
+
$: PLOT_W = VW - PAD_L - PAD_R;
|
|
21
|
+
$: PLOT_H = height - PAD_T - PAD_B;
|
|
22
|
+
$: maxCount = Math.max(...bins.map((b) => b.count), 1);
|
|
23
|
+
$: barW = PLOT_W / bins.length;
|
|
24
|
+
|
|
25
|
+
$: yTicks = [0, 1, 2, 3].map((i) => Math.round((maxCount * i) / 3));
|
|
26
|
+
|
|
27
|
+
$: xStep = bins.length <= 6 ? 1 : Math.ceil(bins.length / 5);
|
|
28
|
+
$: xLabels = (() => {
|
|
29
|
+
const labels: { x: number; label: string }[] = [];
|
|
30
|
+
bins.forEach((bin, i) => {
|
|
31
|
+
if (i % xStep === 0) {
|
|
32
|
+
labels.push({ x: PAD_L + i * barW, label: String(bin.x0) + xUnit });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
labels.push({ x: PAD_L + PLOT_W, label: String(bins[bins.length - 1].x1) + xUnit });
|
|
36
|
+
return labels;
|
|
37
|
+
})();
|
|
38
|
+
|
|
39
|
+
function bh(count: number): number {
|
|
40
|
+
return (count / maxCount) * PLOT_H;
|
|
41
|
+
}
|
|
42
|
+
function by(count: number): number {
|
|
43
|
+
return PAD_T + PLOT_H - bh(count);
|
|
44
|
+
}
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<div role="img" aria-label={ariaLabel} style="width:100%;">
|
|
48
|
+
<svg
|
|
49
|
+
viewBox="0 0 {VW} {height}"
|
|
50
|
+
style="width:100%;height:auto;display:block;"
|
|
51
|
+
aria-hidden="true"
|
|
52
|
+
focusable="false"
|
|
53
|
+
>
|
|
54
|
+
{#each yTicks as tick, i (i)}
|
|
55
|
+
{@const ty = PAD_T + PLOT_H - (tick / maxCount) * PLOT_H}
|
|
56
|
+
<g>
|
|
57
|
+
<line
|
|
58
|
+
x1={PAD_L} y1={ty}
|
|
59
|
+
x2={PAD_L + PLOT_W} y2={ty}
|
|
60
|
+
stroke="rgba(255,255,255,0.06)" stroke-width="1"
|
|
61
|
+
/>
|
|
62
|
+
<text
|
|
63
|
+
x={PAD_L - 4} y={ty + 4}
|
|
64
|
+
fill="#6E7079" font-size="9" text-anchor="end" font-family="inherit"
|
|
65
|
+
>{tick}</text>
|
|
66
|
+
</g>
|
|
67
|
+
{/each}
|
|
68
|
+
|
|
69
|
+
{#each bins as bin, i (i)}
|
|
70
|
+
<rect
|
|
71
|
+
x={PAD_L + i * barW + 1}
|
|
72
|
+
y={by(bin.count)}
|
|
73
|
+
width={Math.max(barW - 2, 1)}
|
|
74
|
+
height={bh(bin.count)}
|
|
75
|
+
fill={color}
|
|
76
|
+
opacity="0.85"
|
|
77
|
+
/>
|
|
78
|
+
{/each}
|
|
79
|
+
|
|
80
|
+
<line
|
|
81
|
+
x1={PAD_L} y1={PAD_T + PLOT_H}
|
|
82
|
+
x2={PAD_L + PLOT_W} y2={PAD_T + PLOT_H}
|
|
83
|
+
stroke="rgba(255,255,255,0.12)" stroke-width="1"
|
|
84
|
+
/>
|
|
85
|
+
|
|
86
|
+
{#each xLabels as lbl, i (i)}
|
|
87
|
+
<text
|
|
88
|
+
x={lbl.x} y={PAD_T + PLOT_H + 14}
|
|
89
|
+
fill="#6E7079" font-size="9" text-anchor="middle" font-family="inherit"
|
|
90
|
+
>{lbl.label}</text>
|
|
91
|
+
{/each}
|
|
92
|
+
</svg>
|
|
93
|
+
</div>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export interface HorizonSeries {
|
|
3
|
+
label: string;
|
|
4
|
+
values: number[];
|
|
5
|
+
color?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export let series: HorizonSeries[] = [];
|
|
9
|
+
export let xLabels: string[] | undefined = undefined;
|
|
10
|
+
export let bands = 3;
|
|
11
|
+
export let ariaLabel = 'Horizon chart';
|
|
12
|
+
|
|
13
|
+
const LABEL_W = 72;
|
|
14
|
+
const ROW_H = 24;
|
|
15
|
+
const ROW_GAP = 3;
|
|
16
|
+
const X_H = 14;
|
|
17
|
+
const W = 400;
|
|
18
|
+
|
|
19
|
+
$: n = series[0]?.values.length ?? 0;
|
|
20
|
+
$: allVals = series.flatMap((s) => s.values);
|
|
21
|
+
$: maxVal = Math.max(...allVals, 1);
|
|
22
|
+
$: step = maxVal / bands;
|
|
23
|
+
$: chartW = W - LABEL_W;
|
|
24
|
+
$: colW = chartW / Math.max(n, 1);
|
|
25
|
+
$: totalH = series.length * (ROW_H + ROW_GAP) - ROW_GAP + (xLabels ? X_H + 4 : 0);
|
|
26
|
+
|
|
27
|
+
interface Band { x: number; y: number; w: number; h: number; alpha: number; color: string; }
|
|
28
|
+
interface Row { rowY: number; color: string; label: string; bands: Band[]; }
|
|
29
|
+
|
|
30
|
+
$: rows = series.map((s, si) => {
|
|
31
|
+
const rowY = si * (ROW_H + ROW_GAP);
|
|
32
|
+
const color = s.color ?? '#006FFF';
|
|
33
|
+
const rowBands: Band[] = [];
|
|
34
|
+
s.values.forEach((v, ti) => {
|
|
35
|
+
for (let k = 0; k < bands; k++) {
|
|
36
|
+
const lo = k * step;
|
|
37
|
+
const fill = Math.min(Math.max(v - lo, 0), step) / step;
|
|
38
|
+
if (fill <= 0) continue;
|
|
39
|
+
const alpha = 0.25 + 0.3 * k;
|
|
40
|
+
const rH = fill * ROW_H;
|
|
41
|
+
rowBands.push({ x: LABEL_W + ti * colW, y: rowY + ROW_H - rH, w: Math.max(colW - 0.5, 0.5), h: rH, alpha, color });
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return { rowY, color, label: s.label, bands: rowBands } as Row;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
$: xTickPositions = xLabels
|
|
48
|
+
? xLabels.map((label, i) => ({
|
|
49
|
+
label,
|
|
50
|
+
x: LABEL_W + (i / Math.max(xLabels!.length - 1, 1)) * chartW,
|
|
51
|
+
anchor: i === 0 ? 'start' : i === xLabels!.length - 1 ? 'end' : 'middle',
|
|
52
|
+
}))
|
|
53
|
+
: [];
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
{#if series.length}
|
|
57
|
+
<div role="img" aria-label={ariaLabel} style="width:100%;">
|
|
58
|
+
<svg viewBox="0 0 {W} {totalH}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
|
|
59
|
+
{#each rows as row, si (si)}
|
|
60
|
+
<text x={LABEL_W - 6} y={row.rowY + ROW_H / 2 + 4} text-anchor="end" fill="#C8C9D0" font-size="9" font-family="inherit">{row.label}</text>
|
|
61
|
+
<rect x={LABEL_W} y={row.rowY} width={chartW} height={ROW_H} fill="rgba(255,255,255,0.04)" rx="2" />
|
|
62
|
+
{#each row.bands as b, bi (bi)}
|
|
63
|
+
<rect x={b.x} y={b.y} width={b.w} height={b.h} fill={b.color} opacity={b.alpha} rx="1" />
|
|
64
|
+
{/each}
|
|
65
|
+
{/each}
|
|
66
|
+
{#each xTickPositions as tick, i (i)}
|
|
67
|
+
<text x={tick.x} y={totalH} text-anchor={tick.anchor} fill="#6E7079" font-size="8" font-family="inherit">{tick.label}</text>
|
|
68
|
+
{/each}
|
|
69
|
+
</svg>
|
|
70
|
+
</div>
|
|
71
|
+
{/if}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script context="module" lang="ts">
|
|
2
|
+
let counter = 0;
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { onDestroy } from 'svelte';
|
|
7
|
+
export let placement: 'top' | 'bottom' | 'left' | 'right' = 'bottom';
|
|
8
|
+
export let delay: number = 300;
|
|
9
|
+
let className = '';
|
|
10
|
+
export { className as class };
|
|
11
|
+
|
|
12
|
+
const id = `dash-ui-hovercard-${++counter}`;
|
|
13
|
+
let open = false;
|
|
14
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
15
|
+
|
|
16
|
+
function show() {
|
|
17
|
+
if (timer) clearTimeout(timer);
|
|
18
|
+
timer = setTimeout(() => { open = true; }, delay);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function hide() {
|
|
22
|
+
if (timer) clearTimeout(timer);
|
|
23
|
+
open = false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
onDestroy(() => { if (timer) clearTimeout(timer); });
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<div
|
|
30
|
+
role="group"
|
|
31
|
+
class="hovercard-wrapper hovercard-{placement} {className}"
|
|
32
|
+
on:mouseenter={show}
|
|
33
|
+
on:mouseleave={hide}
|
|
34
|
+
on:focusin={show}
|
|
35
|
+
on:focusout={hide}
|
|
36
|
+
>
|
|
37
|
+
<div class="hovercard-trigger" aria-describedby={open ? id : undefined}>
|
|
38
|
+
<slot name="trigger" />
|
|
39
|
+
</div>
|
|
40
|
+
{#if open}
|
|
41
|
+
<div {id} role="tooltip" class="hovercard">
|
|
42
|
+
<slot name="content" />
|
|
43
|
+
</div>
|
|
44
|
+
{/if}
|
|
45
|
+
</div>
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<script context="module" lang="ts">
|
|
2
|
+
let counter = 0;
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { createEventDispatcher } from 'svelte';
|
|
7
|
+
|
|
8
|
+
export let label: string | undefined = undefined;
|
|
9
|
+
export let value: string = '0.0.0.0';
|
|
10
|
+
export let disabled: boolean = false;
|
|
11
|
+
let className = '';
|
|
12
|
+
export { className as class };
|
|
13
|
+
|
|
14
|
+
const dispatch = createEventDispatcher<{ change: string }>();
|
|
15
|
+
const uid = `dash-ui-ip-${++counter}`;
|
|
16
|
+
|
|
17
|
+
function clampOctet(s: string): string {
|
|
18
|
+
if (s === '') return s;
|
|
19
|
+
const n = Number(s);
|
|
20
|
+
if (isNaN(n)) return s;
|
|
21
|
+
return String(Math.min(255, Math.max(0, n)));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseIP(ip: string): [string, string, string, string] {
|
|
25
|
+
const parts = ip.split('.');
|
|
26
|
+
return [parts[0] ?? '0', parts[1] ?? '0', parts[2] ?? '0', parts[3] ?? '0'];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let octets: [string, string, string, string] = parseIP(value);
|
|
30
|
+
let inputs: (HTMLInputElement | null)[] = [null, null, null, null];
|
|
31
|
+
|
|
32
|
+
function focusAt(i: number) {
|
|
33
|
+
const el = inputs[i];
|
|
34
|
+
if (el) { el.focus(); el.select(); }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function commitOctet(i: number, raw: string) {
|
|
38
|
+
if (!/^\d{0,3}$/.test(raw)) return;
|
|
39
|
+
octets[i] = clampOctet(raw);
|
|
40
|
+
octets = octets as [string, string, string, string];
|
|
41
|
+
value = octets.join('.');
|
|
42
|
+
dispatch('change', value);
|
|
43
|
+
if (raw.length === 3 && i < 3) setTimeout(() => focusAt(i + 1), 0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function handleKey(i: number, e: KeyboardEvent) {
|
|
47
|
+
const el = inputs[i]!;
|
|
48
|
+
if (e.key === '.') {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
if (i < 3) focusAt(i + 1);
|
|
51
|
+
} else if (e.key === 'ArrowRight' && el.selectionStart === el.value.length) {
|
|
52
|
+
if (i < 3) { e.preventDefault(); focusAt(i + 1); }
|
|
53
|
+
} else if (e.key === 'ArrowLeft' && el.selectionStart === 0) {
|
|
54
|
+
if (i > 0) { e.preventDefault(); focusAt(i - 1); }
|
|
55
|
+
} else if (e.key === 'Backspace' && el.value === '' && i > 0) {
|
|
56
|
+
e.preventDefault(); focusAt(i - 1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function handlePaste(e: ClipboardEvent) {
|
|
61
|
+
const text = e.clipboardData?.getData('text').trim() ?? '';
|
|
62
|
+
if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(text)) {
|
|
63
|
+
e.preventDefault();
|
|
64
|
+
octets = text.split('.').map(clampOctet) as [string, string, string, string];
|
|
65
|
+
value = octets.join('.');
|
|
66
|
+
dispatch('change', value);
|
|
67
|
+
setTimeout(() => focusAt(3), 0);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<div class="ip-input-wrapper {className}">
|
|
73
|
+
{#if label}
|
|
74
|
+
<label for="{uid}-0" class="ip-input__label">{label}</label>
|
|
75
|
+
{/if}
|
|
76
|
+
<div class="ip-input{disabled ? ' ip-input--disabled' : ''}" role="group" aria-label={label ?? 'IP address'}>
|
|
77
|
+
{#each octets as oct, i}
|
|
78
|
+
<input
|
|
79
|
+
bind:this={inputs[i]}
|
|
80
|
+
id={i === 0 ? `${uid}-0` : undefined}
|
|
81
|
+
type="text"
|
|
82
|
+
inputmode="numeric"
|
|
83
|
+
pattern="[0-9]*"
|
|
84
|
+
role="spinbutton"
|
|
85
|
+
aria-label="Octet {i + 1} of 4"
|
|
86
|
+
aria-valuenow={oct === '' ? 0 : Number(oct)}
|
|
87
|
+
aria-valuemin={0}
|
|
88
|
+
aria-valuemax={255}
|
|
89
|
+
value={oct}
|
|
90
|
+
{disabled}
|
|
91
|
+
maxlength={3}
|
|
92
|
+
class="ip-input__octet"
|
|
93
|
+
on:input={(e) => commitOctet(i, e.currentTarget.value)}
|
|
94
|
+
on:keydown={(e) => handleKey(i, e)}
|
|
95
|
+
on:paste={handlePaste}
|
|
96
|
+
on:focus={(e) => e.currentTarget.select()}
|
|
97
|
+
/>
|
|
98
|
+
{#if i < 3}
|
|
99
|
+
<span class="ip-input__dot" aria-hidden="true">.</span>
|
|
100
|
+
{/if}
|
|
101
|
+
{/each}
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export interface IcicleNode {
|
|
3
|
+
label: string;
|
|
4
|
+
value?: number;
|
|
5
|
+
color?: string;
|
|
6
|
+
children?: IcicleNode[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export let root: IcicleNode;
|
|
10
|
+
export let height = 160;
|
|
11
|
+
export let ariaLabel = 'Icicle chart';
|
|
12
|
+
|
|
13
|
+
const PALETTE = ['#FF7B7B', '#F5A623', '#A78BFA', '#34D399', '#006FFF', '#00C875', '#FB923C'];
|
|
14
|
+
const VW = 340;
|
|
15
|
+
const GAP = 1.5;
|
|
16
|
+
|
|
17
|
+
interface FlatCell {
|
|
18
|
+
x: number; y: number; w: number; h: number;
|
|
19
|
+
label: string; color: string; depth: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function nodeValue(node: IcicleNode): number {
|
|
23
|
+
if (node.children?.length) return node.children.reduce((s, c) => s + nodeValue(c), 0);
|
|
24
|
+
return node.value ?? 1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function treeDepth(node: IcicleNode): number {
|
|
28
|
+
if (!node.children?.length) return 0;
|
|
29
|
+
return 1 + Math.max(...node.children.map(treeDepth));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function flatten(node: IcicleNode, x: number, y: number, w: number, levelH: number, depth: number, inheritColor: string, colorIdx: { n: number }): FlatCell[] {
|
|
33
|
+
let color = node.color ?? '';
|
|
34
|
+
if (!color) {
|
|
35
|
+
if (depth === 0) color = 'rgba(255,255,255,0.10)';
|
|
36
|
+
else if (depth === 1) color = PALETTE[colorIdx.n++ % PALETTE.length];
|
|
37
|
+
else color = inheritColor;
|
|
38
|
+
}
|
|
39
|
+
const cells: FlatCell[] = [{ x, y, w, h: levelH, label: node.label, color, depth }];
|
|
40
|
+
if (node.children?.length && w > 0) {
|
|
41
|
+
const total = nodeValue(node);
|
|
42
|
+
let cx = x;
|
|
43
|
+
for (const child of node.children) {
|
|
44
|
+
const cw = total > 0 ? (nodeValue(child) / total) * w : 0;
|
|
45
|
+
cells.push(...flatten(child, cx, y + levelH, cw, levelH, depth + 1, color, colorIdx));
|
|
46
|
+
cx += cw;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return cells;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
$: depth = treeDepth(root);
|
|
53
|
+
$: levelH = height / (depth + 1);
|
|
54
|
+
$: cells = flatten(root, 0, 0, VW, levelH, 0, PALETTE[0], { n: 0 });
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
{#if cells.length > 0}
|
|
58
|
+
<div role="img" aria-label={ariaLabel} style="width:100%;">
|
|
59
|
+
<svg viewBox="0 0 {VW} {height}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
|
|
60
|
+
{#each cells as cell, i (i)}
|
|
61
|
+
{#if cell.w > GAP * 2}
|
|
62
|
+
<g>
|
|
63
|
+
<rect
|
|
64
|
+
x={cell.x + GAP / 2}
|
|
65
|
+
y={cell.y + GAP / 2}
|
|
66
|
+
width={Math.max(0, cell.w - GAP)}
|
|
67
|
+
height={Math.max(0, cell.h - GAP)}
|
|
68
|
+
fill={cell.color}
|
|
69
|
+
fill-opacity={cell.depth === 0 ? 1 : cell.depth === 1 ? 0.7 : 0.85}
|
|
70
|
+
rx={2}
|
|
71
|
+
/>
|
|
72
|
+
{#if cell.w > 32}
|
|
73
|
+
<text
|
|
74
|
+
x={cell.x + cell.w / 2}
|
|
75
|
+
y={cell.y + cell.h / 2 + 4}
|
|
76
|
+
text-anchor="middle"
|
|
77
|
+
font-size={Math.min(10, (cell.w - GAP) / 6, cell.h - 4)}
|
|
78
|
+
fill="#fff"
|
|
79
|
+
font-family="inherit"
|
|
80
|
+
>{cell.label}</text>
|
|
81
|
+
{/if}
|
|
82
|
+
</g>
|
|
83
|
+
{/if}
|
|
84
|
+
{/each}
|
|
85
|
+
</svg>
|
|
86
|
+
</div>
|
|
87
|
+
{/if}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export let title: string | undefined = undefined;
|
|
3
|
+
export let type: 'button' | 'submit' | 'reset' = 'button';
|
|
4
|
+
let className = '';
|
|
5
|
+
export { className as class };
|
|
6
|
+
export let style: string = '';
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<button
|
|
10
|
+
{type}
|
|
11
|
+
class="icon-btn {className}"
|
|
12
|
+
{title}
|
|
13
|
+
{style}
|
|
14
|
+
{...$$restProps}
|
|
15
|
+
aria-label={$$restProps['aria-label'] ?? title}
|
|
16
|
+
on:click
|
|
17
|
+
>
|
|
18
|
+
<slot />
|
|
19
|
+
</button>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { tick } from 'svelte';
|
|
3
|
+
|
|
4
|
+
export let value: string;
|
|
5
|
+
export let onconfirm: (v: string) => void;
|
|
6
|
+
export let label: string;
|
|
7
|
+
export let placeholder: string = '';
|
|
8
|
+
|
|
9
|
+
let editing = false;
|
|
10
|
+
let draft = value;
|
|
11
|
+
let inputEl: HTMLInputElement;
|
|
12
|
+
let editBtnEl: HTMLButtonElement;
|
|
13
|
+
|
|
14
|
+
async function startEdit() {
|
|
15
|
+
draft = value;
|
|
16
|
+
editing = true;
|
|
17
|
+
await tick();
|
|
18
|
+
inputEl?.focus();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function confirm() {
|
|
22
|
+
const trimmed = draft.trim();
|
|
23
|
+
if (trimmed) onconfirm(trimmed);
|
|
24
|
+
editing = false;
|
|
25
|
+
await tick();
|
|
26
|
+
editBtnEl?.focus();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function cancel() {
|
|
30
|
+
draft = value;
|
|
31
|
+
editing = false;
|
|
32
|
+
await tick();
|
|
33
|
+
editBtnEl?.focus();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
37
|
+
if (e.key === 'Enter') { e.preventDefault(); confirm(); }
|
|
38
|
+
if (e.key === 'Escape') { e.preventDefault(); cancel(); }
|
|
39
|
+
}
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
{#if editing}
|
|
43
|
+
<span class="ie ie--editing">
|
|
44
|
+
<input
|
|
45
|
+
bind:this={inputEl}
|
|
46
|
+
class="ie__input"
|
|
47
|
+
aria-label="Edit {label}"
|
|
48
|
+
bind:value={draft}
|
|
49
|
+
{placeholder}
|
|
50
|
+
on:keydown={handleKeydown}
|
|
51
|
+
/>
|
|
52
|
+
<button type="button" class="ie__btn ie__btn--save" aria-label="Save" on:click={confirm}>
|
|
53
|
+
<svg viewBox="0 0 14 14" width="12" height="12" fill="none" aria-hidden="true" focusable="false">
|
|
54
|
+
<path d="M2.5 7.5l3.5 3.5L12 3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
55
|
+
</svg>
|
|
56
|
+
</button>
|
|
57
|
+
<button type="button" class="ie__btn ie__btn--cancel" aria-label="Cancel" on:click={cancel}>
|
|
58
|
+
<svg viewBox="0 0 14 14" width="12" height="12" fill="none" aria-hidden="true" focusable="false">
|
|
59
|
+
<path d="M3 3l8 8M11 3l-8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
60
|
+
</svg>
|
|
61
|
+
</button>
|
|
62
|
+
</span>
|
|
63
|
+
{:else}
|
|
64
|
+
<span class="ie">
|
|
65
|
+
<span class="ie__text">{value}</span>
|
|
66
|
+
<button bind:this={editBtnEl} type="button" class="ie__edit" aria-label="Edit {label}" on:click={startEdit}>
|
|
67
|
+
<svg viewBox="0 0 14 14" width="12" height="12" fill="none" aria-hidden="true" focusable="false">
|
|
68
|
+
<path d="M9.5 2l2.5 2.5-8 8H1.5v-2.5l8-8z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
69
|
+
</svg>
|
|
70
|
+
</button>
|
|
71
|
+
</span>
|
|
72
|
+
{/if}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export let value: string = '';
|
|
3
|
+
export let placeholder: string = '';
|
|
4
|
+
export let type: string = 'text';
|
|
5
|
+
let className = '';
|
|
6
|
+
export { className as class };
|
|
7
|
+
export let style: string = '';
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
{#if type === 'text'}
|
|
11
|
+
<input class="input {className}" type="text" {placeholder} bind:value {style} on:input on:change on:focus on:blur />
|
|
12
|
+
{:else if type === 'password'}
|
|
13
|
+
<input class="input {className}" type="password" {placeholder} bind:value {style} on:input on:change on:focus on:blur />
|
|
14
|
+
{:else if type === 'number'}
|
|
15
|
+
<input class="input {className}" type="number" {placeholder} bind:value {style} on:input on:change on:focus on:blur />
|
|
16
|
+
{:else}
|
|
17
|
+
<input class="input {className}" {type} {placeholder} value={value} {style} on:input on:change on:focus on:blur />
|
|
18
|
+
{/if}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export let prefix: string | undefined = undefined;
|
|
3
|
+
export let suffix: string | undefined = undefined;
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<div class="input-group">
|
|
7
|
+
{#if prefix !== undefined}
|
|
8
|
+
<span class="input-group-addon input-group-addon--prefix">{prefix}</span>
|
|
9
|
+
{/if}
|
|
10
|
+
<slot />
|
|
11
|
+
{#if suffix !== undefined}
|
|
12
|
+
<span class="input-group-addon input-group-addon--suffix">{suffix}</span>
|
|
13
|
+
{/if}
|
|
14
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script context="module" lang="ts">
|
|
2
|
+
export type { JsVal } from './JsonViewerNode.svelte';
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import JsonViewerNode from './JsonViewerNode.svelte';
|
|
7
|
+
export let data: unknown;
|
|
8
|
+
export let label: string = 'JSON viewer';
|
|
9
|
+
export let defaultExpanded: boolean = true;
|
|
10
|
+
export let maxDepth: number = 3;
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div class="json-viewer" role="tree" aria-label={label}>
|
|
14
|
+
<JsonViewerNode v={data} depth={0} {maxDepth} startOpen={defaultExpanded} />
|
|
15
|
+
</div>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export type JsVal = string | number | boolean | null | JsVal[] | { [k: string]: JsVal };
|
|
3
|
+
export let v: JsVal;
|
|
4
|
+
export let k: string | undefined = undefined;
|
|
5
|
+
export let depth = 0;
|
|
6
|
+
export let maxDepth = 3;
|
|
7
|
+
export let startOpen = true;
|
|
8
|
+
|
|
9
|
+
$: isArr = Array.isArray(v);
|
|
10
|
+
$: isObj = v !== null && typeof v === 'object';
|
|
11
|
+
$: expandable = isArr || isObj;
|
|
12
|
+
|
|
13
|
+
let open = startOpen && depth < maxDepth;
|
|
14
|
+
|
|
15
|
+
$: entries = isArr
|
|
16
|
+
? (v as JsVal[]).map((item: JsVal, i: number) => ({ ek: String(i), ev: item, arrItem: true }))
|
|
17
|
+
: isObj
|
|
18
|
+
? Object.entries(v as { [key: string]: JsVal }).map(([ek, ev]) => ({ ek, ev, arrItem: false }))
|
|
19
|
+
: [];
|
|
20
|
+
$: count = entries.length;
|
|
21
|
+
$: ob = isArr ? '[' : '{';
|
|
22
|
+
$: cb = isArr ? ']' : '}';
|
|
23
|
+
$: indent = depth * 14;
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
{#if !expandable}
|
|
27
|
+
<div role="treeitem" aria-selected="false" class="jv-row" style:padding-left="{indent}px">
|
|
28
|
+
{#if k != null}<span class="jv-key">"{k}"</span><span class="jv-colon">: </span>{/if}
|
|
29
|
+
{#if v === null}<span class="jv-null">null</span>
|
|
30
|
+
{:else if typeof v === 'string'}<span class="jv-string">"{v}"</span>
|
|
31
|
+
{:else if typeof v === 'number'}<span class="jv-number">{v}</span>
|
|
32
|
+
{:else}<span class="jv-boolean">{String(v)}</span>{/if}
|
|
33
|
+
</div>
|
|
34
|
+
{:else}
|
|
35
|
+
<div role="treeitem" aria-selected="false" aria-expanded={open} class="jv-node">
|
|
36
|
+
<button
|
|
37
|
+
type="button"
|
|
38
|
+
class="jv-toggle-row"
|
|
39
|
+
on:click={() => (open = !open)}
|
|
40
|
+
aria-label="{open ? 'Collapse' : 'Expand'} {k ?? (isArr ? 'array' : 'object')}"
|
|
41
|
+
style:padding-left="{indent}px"
|
|
42
|
+
>
|
|
43
|
+
<span class="jv-caret" aria-hidden="true">{open ? '▾' : '▸'}</span>
|
|
44
|
+
{#if k != null}<span class="jv-key">"{k}"</span><span class="jv-colon">: </span>{/if}
|
|
45
|
+
<span class="jv-bracket">{ob}</span>
|
|
46
|
+
{#if !open}<span class="jv-count"> {count} </span><span class="jv-bracket">{cb}</span>{/if}
|
|
47
|
+
</button>
|
|
48
|
+
{#if open}
|
|
49
|
+
<div role="group" class="jv-children">
|
|
50
|
+
{#each entries as { ek, ev, arrItem } (ek)}
|
|
51
|
+
<svelte:self v={ev} k={arrItem ? undefined : ek} depth={depth + 1} {maxDepth} {startOpen} />
|
|
52
|
+
{/each}
|
|
53
|
+
<div class="jv-row jv-close" style:padding-left="{indent}px">
|
|
54
|
+
<span class="jv-bracket">{cb}</span>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
{/if}
|
|
58
|
+
</div>
|
|
59
|
+
{/if}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export let rows: Array<{ label: string; value: string; valueColor?: string }> = [];
|
|
3
|
+
export let caption: string | undefined = undefined;
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<table class="kv-table">
|
|
7
|
+
{#if caption}
|
|
8
|
+
<caption class="sr-only">{caption}</caption>
|
|
9
|
+
{/if}
|
|
10
|
+
<tbody>
|
|
11
|
+
{#each rows as { label, value, valueColor }}
|
|
12
|
+
<tr class="kv-table__row">
|
|
13
|
+
<th scope="row" class="kv-table__label">{label}</th>
|
|
14
|
+
<td class="kv-table__value" style={valueColor ? `color:${valueColor}` : undefined}>{value}</td>
|
|
15
|
+
</tr>
|
|
16
|
+
{/each}
|
|
17
|
+
</tbody>
|
|
18
|
+
</table>
|