@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.
Files changed (195) hide show
  1. package/README.md +8 -0
  2. package/package.json +57 -0
  3. package/src/lib/components/Accordion.svelte +3 -0
  4. package/src/lib/components/AccordionItem.svelte +40 -0
  5. package/src/lib/components/ActionMenu.svelte +96 -0
  6. package/src/lib/components/ActivityFeed.svelte +60 -0
  7. package/src/lib/components/Alert.svelte +19 -0
  8. package/src/lib/components/AnnotatedTimeSeries.svelte +90 -0
  9. package/src/lib/components/ArcDiagram.svelte +74 -0
  10. package/src/lib/components/AreaChart.svelte +64 -0
  11. package/src/lib/components/Avatar.svelte +30 -0
  12. package/src/lib/components/AvatarGroup.svelte +35 -0
  13. package/src/lib/components/Badge.svelte +25 -0
  14. package/src/lib/components/Banner.svelte +37 -0
  15. package/src/lib/components/BarChart.svelte +52 -0
  16. package/src/lib/components/BeeswarmChart.svelte +88 -0
  17. package/src/lib/components/BoxPlot.svelte +66 -0
  18. package/src/lib/components/Breadcrumb.svelte +31 -0
  19. package/src/lib/components/BubbleChart.svelte +69 -0
  20. package/src/lib/components/BulletChart.svelte +70 -0
  21. package/src/lib/components/BumpChart.svelte +109 -0
  22. package/src/lib/components/Button.svelte +25 -0
  23. package/src/lib/components/CIDRInput.svelte +142 -0
  24. package/src/lib/components/CalendarHeatmap.svelte +86 -0
  25. package/src/lib/components/Callout.svelte +38 -0
  26. package/src/lib/components/CandlestickChart.svelte +66 -0
  27. package/src/lib/components/Card.svelte +11 -0
  28. package/src/lib/components/Carousel.svelte +64 -0
  29. package/src/lib/components/Checkbox.svelte +45 -0
  30. package/src/lib/components/ChordDiagram.svelte +90 -0
  31. package/src/lib/components/CirclePacking.svelte +119 -0
  32. package/src/lib/components/CodeBlock.svelte +49 -0
  33. package/src/lib/components/ColorPicker.svelte +91 -0
  34. package/src/lib/components/ColumnToggle.svelte +96 -0
  35. package/src/lib/components/Combobox.svelte +138 -0
  36. package/src/lib/components/CommandPalette.svelte +152 -0
  37. package/src/lib/components/ConfirmDialog.svelte +84 -0
  38. package/src/lib/components/ContextMenu.svelte +107 -0
  39. package/src/lib/components/ContextualHelp.svelte +73 -0
  40. package/src/lib/components/ContourPlot.svelte +164 -0
  41. package/src/lib/components/CopyButton.svelte +33 -0
  42. package/src/lib/components/CorrelationMatrix.svelte +90 -0
  43. package/src/lib/components/CountUp.svelte +49 -0
  44. package/src/lib/components/CumulativeDistribution.svelte +67 -0
  45. package/src/lib/components/DatePicker.svelte +201 -0
  46. package/src/lib/components/DateRangePicker.svelte +249 -0
  47. package/src/lib/components/Dendrogram.svelte +128 -0
  48. package/src/lib/components/Donut.svelte +49 -0
  49. package/src/lib/components/DotPlot.svelte +121 -0
  50. package/src/lib/components/Drawer.svelte +60 -0
  51. package/src/lib/components/DualAxisChart.svelte +80 -0
  52. package/src/lib/components/DumbbellChart.svelte +56 -0
  53. package/src/lib/components/DurationInput.svelte +118 -0
  54. package/src/lib/components/EmptyState.svelte +21 -0
  55. package/src/lib/components/ErrorBandChart.svelte +69 -0
  56. package/src/lib/components/ExpandableRow.svelte +42 -0
  57. package/src/lib/components/Field.svelte +40 -0
  58. package/src/lib/components/FileUpload.svelte +96 -0
  59. package/src/lib/components/FilterBuilder.svelte +160 -0
  60. package/src/lib/components/FlameGraph.svelte +88 -0
  61. package/src/lib/components/ForceGraph.svelte +104 -0
  62. package/src/lib/components/FunnelChart.svelte +50 -0
  63. package/src/lib/components/GanttChart.svelte +49 -0
  64. package/src/lib/components/Gauge.svelte +53 -0
  65. package/src/lib/components/GroupedList.svelte +87 -0
  66. package/src/lib/components/HealthBar.svelte +19 -0
  67. package/src/lib/components/HeatMap.svelte +64 -0
  68. package/src/lib/components/HexbinChart.svelte +100 -0
  69. package/src/lib/components/Histogram.svelte +93 -0
  70. package/src/lib/components/HorizonChart.svelte +71 -0
  71. package/src/lib/components/HoverCard.svelte +45 -0
  72. package/src/lib/components/IPInput.svelte +103 -0
  73. package/src/lib/components/IcicleChart.svelte +87 -0
  74. package/src/lib/components/IconButton.svelte +19 -0
  75. package/src/lib/components/InlineEdit.svelte +72 -0
  76. package/src/lib/components/Input.svelte +18 -0
  77. package/src/lib/components/InputGroup.svelte +14 -0
  78. package/src/lib/components/JsonViewer.svelte +15 -0
  79. package/src/lib/components/JsonViewerNode.svelte +59 -0
  80. package/src/lib/components/KVTable.svelte +18 -0
  81. package/src/lib/components/KanbanBoard.svelte +141 -0
  82. package/src/lib/components/Kbd.svelte +16 -0
  83. package/src/lib/components/LineChart.svelte +67 -0
  84. package/src/lib/components/LogViewer.svelte +84 -0
  85. package/src/lib/components/LollipopChart.svelte +79 -0
  86. package/src/lib/components/MACInput.svelte +98 -0
  87. package/src/lib/components/MarimekkoChart.svelte +108 -0
  88. package/src/lib/components/MatrixChart.svelte +55 -0
  89. package/src/lib/components/Menubar.svelte +131 -0
  90. package/src/lib/components/MirroredBarChart.svelte +120 -0
  91. package/src/lib/components/Modal.svelte +90 -0
  92. package/src/lib/components/MultiSelect.svelte +165 -0
  93. package/src/lib/components/NightingaleChart.svelte +63 -0
  94. package/src/lib/components/NotificationPanel.svelte +170 -0
  95. package/src/lib/components/NumberInput.svelte +93 -0
  96. package/src/lib/components/OTPInput.svelte +94 -0
  97. package/src/lib/components/Pagination.svelte +58 -0
  98. package/src/lib/components/ParallelCoordinates.svelte +98 -0
  99. package/src/lib/components/ParetoChart.svelte +88 -0
  100. package/src/lib/components/PasswordInput.svelte +62 -0
  101. package/src/lib/components/PieChart.svelte +91 -0
  102. package/src/lib/components/Pill.svelte +12 -0
  103. package/src/lib/components/PolarHeatmap.svelte +85 -0
  104. package/src/lib/components/Popover.svelte +89 -0
  105. package/src/lib/components/ProgressBar.svelte +29 -0
  106. package/src/lib/components/PunchCard.svelte +73 -0
  107. package/src/lib/components/QuadrantChart.svelte +122 -0
  108. package/src/lib/components/RadarChart.svelte +71 -0
  109. package/src/lib/components/RadialBarChart.svelte +76 -0
  110. package/src/lib/components/RadioGroup.svelte +48 -0
  111. package/src/lib/components/RangeSlider.svelte +68 -0
  112. package/src/lib/components/RankedList.svelte +49 -0
  113. package/src/lib/components/ResizablePanel.svelte +77 -0
  114. package/src/lib/components/RidgelinePlot.svelte +103 -0
  115. package/src/lib/components/RowToggle.svelte +19 -0
  116. package/src/lib/components/SankeyDiagram.svelte +100 -0
  117. package/src/lib/components/ScatterPlot.svelte +54 -0
  118. package/src/lib/components/SearchBox.svelte +11 -0
  119. package/src/lib/components/SegmentedControl.svelte +51 -0
  120. package/src/lib/components/Select.svelte +110 -0
  121. package/src/lib/components/SelectionToolbar.svelte +42 -0
  122. package/src/lib/components/Sidebar.svelte +44 -0
  123. package/src/lib/components/Signal.svelte +12 -0
  124. package/src/lib/components/Skeleton.svelte +23 -0
  125. package/src/lib/components/SkipLink.svelte +9 -0
  126. package/src/lib/components/Slider.svelte +48 -0
  127. package/src/lib/components/SlopeChart.svelte +91 -0
  128. package/src/lib/components/SortHeader.svelte +19 -0
  129. package/src/lib/components/SortableList.svelte +87 -0
  130. package/src/lib/components/Sparkline.svelte +32 -0
  131. package/src/lib/components/SparklineMatrix.svelte +84 -0
  132. package/src/lib/components/Spinner.svelte +6 -0
  133. package/src/lib/components/SplitButton.svelte +102 -0
  134. package/src/lib/components/Spoiler.svelte +41 -0
  135. package/src/lib/components/StackedBarChart.svelte +83 -0
  136. package/src/lib/components/StackedProgress.svelte +32 -0
  137. package/src/lib/components/StarRating.svelte +64 -0
  138. package/src/lib/components/Stat.svelte +24 -0
  139. package/src/lib/components/StatusIndicator.svelte +10 -0
  140. package/src/lib/components/StepChart.svelte +74 -0
  141. package/src/lib/components/Stepper.svelte +40 -0
  142. package/src/lib/components/StreamGraph.svelte +93 -0
  143. package/src/lib/components/StripeChart.svelte +52 -0
  144. package/src/lib/components/SunburstChart.svelte +80 -0
  145. package/src/lib/components/SwitchPortGrid.svelte +45 -0
  146. package/src/lib/components/TabPanel.svelte +17 -0
  147. package/src/lib/components/Tabs.svelte +45 -0
  148. package/src/lib/components/Tag.svelte +19 -0
  149. package/src/lib/components/TagInput.svelte +92 -0
  150. package/src/lib/components/Textarea.svelte +31 -0
  151. package/src/lib/components/ThresholdAreaChart.svelte +72 -0
  152. package/src/lib/components/TimePicker.svelte +116 -0
  153. package/src/lib/components/TimeRange.svelte +104 -0
  154. package/src/lib/components/Timeline.svelte +32 -0
  155. package/src/lib/components/Toast.svelte +52 -0
  156. package/src/lib/components/Toggle.svelte +23 -0
  157. package/src/lib/components/ToggleGroup.svelte +32 -0
  158. package/src/lib/components/Tooltip.svelte +16 -0
  159. package/src/lib/components/Topbar.svelte +63 -0
  160. package/src/lib/components/TransferList.svelte +184 -0
  161. package/src/lib/components/TreeItem.svelte +61 -0
  162. package/src/lib/components/TreeMap.svelte +90 -0
  163. package/src/lib/components/TreeView.svelte +130 -0
  164. package/src/lib/components/UptimeTimeline.svelte +86 -0
  165. package/src/lib/components/VennDiagram.svelte +91 -0
  166. package/src/lib/components/ViolinPlot.svelte +114 -0
  167. package/src/lib/components/VirtualList.svelte +38 -0
  168. package/src/lib/components/WaffleChart.svelte +68 -0
  169. package/src/lib/components/WaterfallChart.svelte +96 -0
  170. package/src/lib/components/WordCloud.svelte +103 -0
  171. package/src/lib/icons/AlarmIcon.svelte +5 -0
  172. package/src/lib/icons/BellIcon.svelte +6 -0
  173. package/src/lib/icons/CaretIcon.svelte +8 -0
  174. package/src/lib/icons/ClientsIcon.svelte +5 -0
  175. package/src/lib/icons/CloseIcon.svelte +6 -0
  176. package/src/lib/icons/DashboardIcon.svelte +7 -0
  177. package/src/lib/icons/DevicesIcon.svelte +5 -0
  178. package/src/lib/icons/DownloadIcon.svelte +6 -0
  179. package/src/lib/icons/HelpIcon.svelte +7 -0
  180. package/src/lib/icons/InfraIcon.svelte +7 -0
  181. package/src/lib/icons/IntegrationsIcon.svelte +5 -0
  182. package/src/lib/icons/LogsIcon.svelte +6 -0
  183. package/src/lib/icons/PlusIcon.svelte +6 -0
  184. package/src/lib/icons/PortsIcon.svelte +6 -0
  185. package/src/lib/icons/SearchIcon.svelte +10 -0
  186. package/src/lib/icons/SecurityIcon.svelte +4 -0
  187. package/src/lib/icons/SettingsIcon.svelte +5 -0
  188. package/src/lib/icons/TopologyIcon.svelte +7 -0
  189. package/src/lib/icons/UpdatesIcon.svelte +6 -0
  190. package/src/lib/icons/VpnIcon.svelte +5 -0
  191. package/src/lib/icons/WifiIcon.svelte +5 -0
  192. package/src/lib/icons/WirelessIcon.svelte +4 -0
  193. package/src/lib/icons.ts +22 -0
  194. package/src/lib/index.ts +275 -0
  195. package/src/lib/styles.css +2 -0
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ export interface BarChartSeries {
3
+ label: string;
4
+ color: string;
5
+ values: number[];
6
+ }
7
+
8
+ export let series: BarChartSeries[] = [];
9
+ export let labels: string[] = [];
10
+ export let height: number = 160;
11
+ export let ariaLabel: string = 'Bar chart';
12
+
13
+ const VW = 400;
14
+ const PAD = { t: 12, r: 8, b: 28, l: 8 };
15
+
16
+ $: n = series[0]?.values.length ?? 0;
17
+ $: m = series.length;
18
+ $: maxV = series.length ? Math.max(...series.flatMap((s) => s.values)) || 1 : 1;
19
+ $: chartW = VW - PAD.l - PAD.r;
20
+ $: chartH = height - PAD.t - PAD.b;
21
+ $: bot = PAD.t + chartH;
22
+ $: gw = n > 0 ? chartW / n : chartW;
23
+ $: gap = gw * 0.15;
24
+ $: barW = Math.max((gw - gap * (m + 1)) / m, 1);
25
+
26
+ $: gridLines = [0.25, 0.5, 0.75, 1].map((f) => PAD.t + (1 - f) * chartH);
27
+
28
+ $: bars = series.flatMap((s, si) =>
29
+ s.values.map((v, gi) => {
30
+ const x = PAD.l + gi * gw + gap * (si + 1) + barW * si;
31
+ const bh = (v / maxV) * chartH;
32
+ return { x: x.toFixed(1), y: (bot - bh).toFixed(1), w: barW.toFixed(1), h: bh.toFixed(1), color: s.color, key: `${si}-${gi}` };
33
+ }),
34
+ );
35
+ </script>
36
+
37
+ <div role="img" aria-label={ariaLabel} style="width:100%;">
38
+ <svg viewBox="0 0 {VW} {height}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
39
+ {#each gridLines as y}
40
+ <line x1={PAD.l} y1={y} x2={VW - PAD.r} y2={y} stroke="rgba(255,255,255,0.06)" stroke-width="1" />
41
+ {/each}
42
+ {#each bars as b (b.key)}
43
+ <rect x={b.x} y={b.y} width={b.w} height={b.h} fill={b.color} fill-opacity="0.9" rx="2" />
44
+ {/each}
45
+ {#if labels.length > 0}
46
+ {#each labels as lbl, i}
47
+ {@const x = (PAD.l + (i + 0.5) * gw).toFixed(1)}
48
+ <text x={x} y={height - 4} text-anchor="middle" fill="#6E7079" font-size="9">{lbl}</text>
49
+ {/each}
50
+ {/if}
51
+ </svg>
52
+ </div>
@@ -0,0 +1,88 @@
1
+ <script lang="ts">
2
+ export interface BeeswarmSeries {
3
+ label: string;
4
+ points: number[];
5
+ color?: string;
6
+ }
7
+
8
+ export let series: BeeswarmSeries[] = [];
9
+ export let yRange: [number, number] | undefined = undefined;
10
+ export let height = 200;
11
+ export let unit = '';
12
+ export let dotRadius = 4;
13
+ export let ariaLabel = 'Beeswarm chart';
14
+
15
+ const PALETTE = ['#006FFF', '#00C875', '#FF7B7B', '#F5C26B', '#A78BFA', '#34D399'];
16
+ const VW = 340;
17
+ const PAD_L = 28;
18
+ const PAD_R = 8;
19
+ const PAD_T = 8;
20
+ const PAD_B = 24;
21
+
22
+ function swarmedPositions(values: number[], tyFn: (v: number) => number, r: number, halfWidth: number): { x: number; y: number }[] {
23
+ const pts: { x: number; y: number }[] = [];
24
+ const sorted = [...values].sort((a, b) => a - b);
25
+ const step = r * 2 + 0.5;
26
+ for (const v of sorted) {
27
+ const cy = tyFn(v);
28
+ let chosen = 0;
29
+ let found = false;
30
+ for (let dist = 0; dist <= halfWidth && !found; dist += step) {
31
+ const candidates = dist === 0 ? [0] : [dist, -dist];
32
+ for (const cx of candidates) {
33
+ if (pts.every((p) => Math.hypot(cx - p.x, cy - p.y) >= r * 2)) {
34
+ chosen = cx;
35
+ found = true;
36
+ break;
37
+ }
38
+ }
39
+ }
40
+ pts.push({ x: chosen, y: cy });
41
+ }
42
+ return pts;
43
+ }
44
+
45
+ $: PLOT_W = VW - PAD_L - PAD_R;
46
+ $: PLOT_H = height - PAD_T - PAD_B;
47
+ $: n = series.length || 1;
48
+ $: colWidth = PLOT_W / n;
49
+ $: halfWidth = colWidth / 2 - dotRadius;
50
+ $: allPts = series.flatMap((s) => s.points);
51
+ $: minV = yRange ? yRange[0] : allPts.length ? Math.min(...allPts) : 0;
52
+ $: maxV = yRange ? yRange[1] : allPts.length ? Math.max(...allPts) : 1;
53
+ $: vRange = maxV - minV || 1;
54
+ $: yTicks = [minV, (minV + maxV) / 2, maxV];
55
+
56
+ function ty(v: number): number {
57
+ return PAD_T + (1 - (v - minV) / vRange) * PLOT_H;
58
+ }
59
+
60
+ function colCx(i: number): number {
61
+ return PAD_L + (i + 0.5) * colWidth;
62
+ }
63
+
64
+ $: swarmed = series.map((s) => swarmedPositions(s.points, ty, dotRadius, halfWidth));
65
+ </script>
66
+
67
+ {#if series.length > 0}
68
+ <div role="img" aria-label={ariaLabel} style="width:100%;">
69
+ <svg viewBox="0 0 {VW} {height}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
70
+ {#each yTicks as v, i (i)}
71
+ <line x1={PAD_L} y1={ty(v)} x2={PAD_L + PLOT_W} y2={ty(v)} stroke="rgba(255,255,255,0.06)" stroke-width="1" />
72
+ <text x={PAD_L - 4} y={ty(v) + 4} fill="#6E7079" font-size="8" text-anchor="end" font-family="inherit">{Math.round(v)}{unit}</text>
73
+ {/each}
74
+
75
+ {#each series as s, si (si)}
76
+ {@const cx = colCx(si)}
77
+ {@const color = s.color ?? PALETTE[si % PALETTE.length]}
78
+ {@const pts = swarmed[si]}
79
+ <g>
80
+ {#each pts as p, pi (pi)}
81
+ <circle cx={cx + p.x} cy={p.y} r={dotRadius} fill={color} fill-opacity="0.75" />
82
+ {/each}
83
+ <text x={cx} y={height - 4} fill="#6E7079" font-size="9" text-anchor="middle" font-family="inherit">{s.label}</text>
84
+ </g>
85
+ {/each}
86
+ </svg>
87
+ </div>
88
+ {/if}
@@ -0,0 +1,66 @@
1
+ <script lang="ts">
2
+ export interface BoxSeries {
3
+ label: string;
4
+ q0: number;
5
+ q1: number;
6
+ q2: number;
7
+ q3: number;
8
+ q4: number;
9
+ color?: string;
10
+ }
11
+
12
+ export let series: BoxSeries[] = [];
13
+ export let yRange: [number, number] | undefined = undefined;
14
+ export let height: number = 160;
15
+ export let ariaLabel: string = 'Box plot';
16
+
17
+ const VW = 400;
18
+ const PAD = { t: 12, r: 8, b: 28, l: 8 };
19
+
20
+ $: allVals = series.flatMap((s) => [s.q0, s.q1, s.q2, s.q3, s.q4]);
21
+ $: y0 = yRange ? yRange[0] : allVals.length ? Math.min(...allVals) : 0;
22
+ $: y1 = yRange ? yRange[1] : allVals.length ? Math.max(...allVals) : 1;
23
+ $: ySpan = y1 - y0 || 1;
24
+ $: chartW = VW - PAD.l - PAD.r;
25
+ $: chartH = height - PAD.t - PAD.b;
26
+ $: slotW = series.length > 0 ? chartW / series.length : chartW;
27
+
28
+ $: gridLines = [0.25, 0.5, 0.75, 1].map((f) => (PAD.t + (1 - f) * chartH).toFixed(1));
29
+
30
+ $: boxes = series.map((s, i) => {
31
+ const cx = PAD.l + (i + 0.5) * slotW;
32
+ const bw = slotW * 0.4;
33
+ const ty = (v: number) => PAD.t + chartH - ((v - y0) / ySpan) * chartH;
34
+ return {
35
+ key: String(i),
36
+ cx: cx.toFixed(1),
37
+ color: s.color ?? '#006FFF',
38
+ label: s.label,
39
+ whiskerY0: ty(s.q0).toFixed(1),
40
+ whiskerY4: ty(s.q4).toFixed(1),
41
+ capX0: (cx - bw / 2).toFixed(1),
42
+ capX1: (cx + bw / 2).toFixed(1),
43
+ boxX: (cx - bw / 2).toFixed(1),
44
+ boxW: bw.toFixed(1),
45
+ boxY: ty(s.q3).toFixed(1),
46
+ boxH: (ty(s.q1) - ty(s.q3)).toFixed(1),
47
+ medY: ty(s.q2).toFixed(1),
48
+ };
49
+ });
50
+ </script>
51
+
52
+ <div role="img" aria-label={ariaLabel} style="width:100%;">
53
+ <svg viewBox="0 0 {VW} {height}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
54
+ {#each gridLines as y}
55
+ <line x1={PAD.l} y1={y} x2={VW - PAD.r} y2={y} stroke="rgba(255,255,255,0.06)" stroke-width="1" />
56
+ {/each}
57
+ {#each boxes as b (b.key)}
58
+ <line x1={b.cx} y1={b.whiskerY4} x2={b.cx} y2={b.whiskerY0} stroke={b.color} stroke-width="1.5" stroke-opacity="0.6" />
59
+ <line x1={b.capX0} y1={b.whiskerY0} x2={b.capX1} y2={b.whiskerY0} stroke={b.color} stroke-width="1.5" stroke-opacity="0.6" />
60
+ <line x1={b.capX0} y1={b.whiskerY4} x2={b.capX1} y2={b.whiskerY4} stroke={b.color} stroke-width="1.5" stroke-opacity="0.6" />
61
+ <rect x={b.boxX} y={b.boxY} width={b.boxW} height={b.boxH} fill={b.color} fill-opacity="0.2" stroke={b.color} stroke-width="1.5" stroke-opacity="0.8" rx="2" />
62
+ <line x1={b.capX0} y1={b.medY} x2={b.capX1} y2={b.medY} stroke={b.color} stroke-width="2.5" />
63
+ <text x={b.cx} y={height - 4} text-anchor="middle" fill="#6E7079" font-size="9" font-family="inherit">{b.label}</text>
64
+ {/each}
65
+ </svg>
66
+ </div>
@@ -0,0 +1,31 @@
1
+ <script context="module" lang="ts">
2
+ export interface BreadcrumbItem { label: string; href?: string; }
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ import { createEventDispatcher } from 'svelte';
7
+ export let items: BreadcrumbItem[] = [];
8
+ let className = '';
9
+ export { className as class };
10
+ const dispatch = createEventDispatcher<{ navigate: number }>();
11
+ </script>
12
+
13
+ <nav aria-label="Breadcrumb" class="breadcrumb {className}">
14
+ <ol class="breadcrumb__list">
15
+ {#each items as item, i}
16
+ {@const isLast = i === items.length - 1}
17
+ <li class="breadcrumb__item">
18
+ {#if isLast}
19
+ <span aria-current="page" class="breadcrumb__current">{item.label}</span>
20
+ {:else if item.href}
21
+ <a href={item.href} class="breadcrumb__link">{item.label}</a>
22
+ {:else}
23
+ <button type="button" class="breadcrumb__link" on:click={() => dispatch('navigate', i)}>{item.label}</button>
24
+ {/if}
25
+ {#if !isLast}
26
+ <span aria-hidden="true" class="breadcrumb__sep">/</span>
27
+ {/if}
28
+ </li>
29
+ {/each}
30
+ </ol>
31
+ </nav>
@@ -0,0 +1,69 @@
1
+ <script lang="ts">
2
+ export interface BubblePoint {
3
+ x: number;
4
+ y: number;
5
+ size: number;
6
+ color?: string;
7
+ label?: string;
8
+ key?: string;
9
+ }
10
+
11
+ export let points: BubblePoint[] = [];
12
+ export let xRange: [number, number] | undefined = undefined;
13
+ export let yRange: [number, number] | undefined = undefined;
14
+ export let rRange: [number, number] = [4, 20];
15
+ export let height: number = 160;
16
+ export let ariaLabel: string = 'Bubble chart';
17
+
18
+ const VW = 400;
19
+ const PAD = { t: 12, r: 12, b: 12, l: 12 };
20
+
21
+ $: xs = points.map((p) => p.x);
22
+ $: ys = points.map((p) => p.y);
23
+ $: sizes = points.map((p) => p.size);
24
+ $: x0 = xRange ? xRange[0] : xs.length ? Math.min(...xs) : 0;
25
+ $: x1 = xRange ? xRange[1] : xs.length ? Math.max(...xs) : 1;
26
+ $: y0 = yRange ? yRange[0] : ys.length ? Math.min(...ys) : 0;
27
+ $: y1 = yRange ? yRange[1] : ys.length ? Math.max(...ys) : 1;
28
+ $: s0 = sizes.length ? Math.min(...sizes) : 0;
29
+ $: s1 = sizes.length ? Math.max(...sizes) : 1;
30
+ $: xSpan = x1 - x0 || 1;
31
+ $: ySpan = y1 - y0 || 1;
32
+ $: sSpan = s1 - s0 || 1;
33
+ $: chartW = VW - PAD.l - PAD.r;
34
+ $: chartH = height - PAD.t - PAD.b;
35
+
36
+ $: gridLines = [0.25, 0.5, 0.75].map((f) => ({
37
+ gx: (PAD.l + f * chartW).toFixed(1),
38
+ gy: (PAD.t + (1 - f) * chartH).toFixed(1),
39
+ }));
40
+
41
+ $: bubbles = points.map((p, i) => ({
42
+ cx: (PAD.l + ((p.x - x0) / xSpan) * chartW).toFixed(1),
43
+ cy: (PAD.t + chartH - ((p.y - y0) / ySpan) * chartH).toFixed(1),
44
+ r: (rRange[0] + ((p.size - s0) / sSpan) * (rRange[1] - rRange[0])).toFixed(1),
45
+ color: p.color ?? '#006FFF',
46
+ key: p.key ?? String(i),
47
+ }));
48
+ </script>
49
+
50
+ <div role="img" aria-label={ariaLabel} style="width:100%;">
51
+ <svg viewBox="0 0 {VW} {height}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
52
+ {#each gridLines as g}
53
+ <line x1={g.gx} y1={PAD.t} x2={g.gx} y2={PAD.t + chartH} stroke="rgba(255,255,255,0.06)" stroke-width="1" />
54
+ <line x1={PAD.l} y1={g.gy} x2={VW - PAD.r} y2={g.gy} stroke="rgba(255,255,255,0.06)" stroke-width="1" />
55
+ {/each}
56
+ {#each bubbles as b (b.key)}
57
+ <circle
58
+ cx={b.cx}
59
+ cy={b.cy}
60
+ r={b.r}
61
+ fill={b.color}
62
+ fill-opacity="0.6"
63
+ stroke={b.color}
64
+ stroke-opacity="0.9"
65
+ stroke-width="1.5"
66
+ />
67
+ {/each}
68
+ </svg>
69
+ </div>
@@ -0,0 +1,70 @@
1
+ <script lang="ts">
2
+ export interface BulletItem {
3
+ label: string;
4
+ value: number;
5
+ target?: number;
6
+ ranges?: [number, number];
7
+ unit?: string;
8
+ color?: string;
9
+ }
10
+
11
+ export let items: BulletItem[] = [];
12
+ export let max: number = 100;
13
+ export let ariaLabel: string = 'Bullet chart';
14
+
15
+ const VW = 400;
16
+ const LABEL_W = 110;
17
+ const VAL_W = 44;
18
+ const TRACK_H = 10;
19
+ const RANGE_H = 18;
20
+ const ROW_H = 34;
21
+ const PAD = 8;
22
+
23
+ $: trackW = VW - LABEL_W - VAL_W;
24
+ $: totalH = items.length * ROW_H + PAD * 2;
25
+
26
+ $: rows = items.map((item, i) => {
27
+ const y = PAD + i * ROW_H;
28
+ const midY = y + ROW_H / 2;
29
+ const rangeY = midY - RANGE_H / 2;
30
+ const trackY = midY - TRACK_H / 2;
31
+ const [r1, r2] = item.ranges ?? [50, 75];
32
+ const r1w = (r1 / max) * trackW;
33
+ const r2w = (r2 / max) * trackW;
34
+ const valW = Math.min(item.value / max, 1) * trackW;
35
+ const targetX = item.target != null ? (Math.min(item.target, max) / max) * trackW : null;
36
+ return {
37
+ midY,
38
+ rangeY,
39
+ trackY,
40
+ r1w,
41
+ r2w,
42
+ valW,
43
+ targetX,
44
+ color: item.color ?? '#006FFF',
45
+ unit: item.unit ?? '%',
46
+ label: item.label,
47
+ value: item.value,
48
+ };
49
+ });
50
+ </script>
51
+
52
+ <div role="img" aria-label={ariaLabel} style="width:100%;">
53
+ <svg viewBox="0 0 {VW} {totalH}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
54
+ {#each rows as row}
55
+ <text x={LABEL_W - 8} y={row.midY + 4} text-anchor="end" fill="#A4A7B5" font-size="11" font-family="inherit">
56
+ {row.label}
57
+ </text>
58
+ <rect x={LABEL_W} y={row.rangeY} width={trackW} height={RANGE_H} fill="rgba(255,255,255,0.04)" rx="2" />
59
+ <rect x={LABEL_W + row.r1w} y={row.rangeY} width={trackW - row.r1w} height={RANGE_H} fill="rgba(255,255,255,0.07)" rx="2" />
60
+ <rect x={LABEL_W + row.r2w} y={row.rangeY} width={trackW - row.r2w} height={RANGE_H} fill="rgba(255,255,255,0.10)" rx="2" />
61
+ <rect x={LABEL_W} y={row.trackY} width={row.valW} height={TRACK_H} fill={row.color} fill-opacity="0.9" rx="2" />
62
+ {#if row.targetX != null}
63
+ <rect x={LABEL_W + row.targetX - 1.5} y={row.rangeY} width="3" height={RANGE_H} fill="rgba(255,255,255,0.85)" rx="1" />
64
+ {/if}
65
+ <text x={LABEL_W + trackW + 6} y={row.midY + 4} fill="#fff" font-size="11" font-family="inherit" font-variant-numeric="tabular-nums">
66
+ {row.value}{row.unit}
67
+ </text>
68
+ {/each}
69
+ </svg>
70
+ </div>
@@ -0,0 +1,109 @@
1
+ <script lang="ts">
2
+ export interface BumpSeries {
3
+ label: string;
4
+ ranks: number[];
5
+ color?: string;
6
+ }
7
+
8
+ export let labels: string[] = [];
9
+ export let series: BumpSeries[] = [];
10
+ export let ariaLabel: string = 'Bump chart';
11
+
12
+ const VW = 380;
13
+ const PAD_L = 12;
14
+ const PAD_R = 76;
15
+ const PAD_T = 8;
16
+ const PAD_B = 20;
17
+ const ROW_H = 28;
18
+ const DOT_R = 5;
19
+ const COLORS = ['#006FFF', '#00C8C8', '#F5A623', '#7FB6FF', '#A878F5', '#F56342'];
20
+
21
+ $: maxRank = Math.max(...series.flatMap((s) => s.ranks), 1);
22
+ $: TRACK_W = VW - PAD_L - PAD_R;
23
+ $: svgH = PAD_T + maxRank * ROW_H + PAD_B;
24
+ $: rankRows = Array.from({ length: maxRank }, (_, i) => i);
25
+
26
+ function xOf(i: number): number {
27
+ return labels.length > 1
28
+ ? PAD_L + (i / (labels.length - 1)) * TRACK_W
29
+ : PAD_L + TRACK_W / 2;
30
+ }
31
+
32
+ function yOf(rank: number): number {
33
+ return PAD_T + (rank - 1) * ROW_H + ROW_H / 2;
34
+ }
35
+
36
+ function buildPath(ranks: number[]): string {
37
+ return ranks
38
+ .map((rank, i) => {
39
+ const x = xOf(i);
40
+ const y = yOf(rank);
41
+ if (i === 0) return `M${x},${y}`;
42
+ const prevX = xOf(i - 1);
43
+ const prevY = yOf(ranks[i - 1]);
44
+ const cpX = (prevX + x) / 2;
45
+ return `C${cpX},${prevY} ${cpX},${y} ${x},${y}`;
46
+ })
47
+ .join(' ');
48
+ }
49
+ </script>
50
+
51
+ <div role="img" aria-label={ariaLabel} style="width:100%;">
52
+ <svg
53
+ viewBox="0 0 {VW} {svgH}"
54
+ style="width:100%;height:auto;display:block;"
55
+ aria-hidden="true"
56
+ focusable="false"
57
+ >
58
+ {#each labels as label, i (i)}
59
+ <g>
60
+ <line
61
+ x1={xOf(i)} y1={PAD_T}
62
+ x2={xOf(i)} y2={PAD_T + maxRank * ROW_H}
63
+ stroke="rgba(255,255,255,0.06)" stroke-width="1"
64
+ />
65
+ <text
66
+ x={xOf(i)} y={svgH - 5}
67
+ fill="#6E7079" font-size="9" text-anchor="middle" font-family="inherit"
68
+ >{label}</text>
69
+ </g>
70
+ {/each}
71
+
72
+ {#each rankRows as r (r)}
73
+ <line
74
+ x1={PAD_L} y1={yOf(r + 1)}
75
+ x2={PAD_L + TRACK_W} y2={yOf(r + 1)}
76
+ stroke="rgba(255,255,255,0.04)" stroke-width="1"
77
+ />
78
+ {/each}
79
+
80
+ {#each series as s, si (si)}
81
+ {@const color = s.color ?? COLORS[si % COLORS.length]}
82
+ {@const lastRank = s.ranks[s.ranks.length - 1]}
83
+ <g>
84
+ <path
85
+ d={buildPath(s.ranks)}
86
+ fill="none"
87
+ stroke={color}
88
+ stroke-width="2.5"
89
+ opacity="0.85"
90
+ />
91
+ {#each s.ranks as rank, i (i)}
92
+ <circle
93
+ cx={xOf(i)} cy={yOf(rank)} r={DOT_R}
94
+ fill={color}
95
+ stroke="#1A1F2E"
96
+ stroke-width="1.5"
97
+ />
98
+ {/each}
99
+ <text
100
+ x={xOf(labels.length - 1) + DOT_R + 6}
101
+ y={yOf(lastRank) + 4}
102
+ fill={color}
103
+ font-size="10"
104
+ font-family="inherit"
105
+ >{s.label}</text>
106
+ </g>
107
+ {/each}
108
+ </svg>
109
+ </div>
@@ -0,0 +1,25 @@
1
+ <script lang="ts">
2
+ import Spinner from './Spinner.svelte';
3
+ export let variant: 'primary' | 'ghost' | 'danger' = 'ghost';
4
+ export let iconOnly = false;
5
+ export let loading = false;
6
+ export let title: string | undefined = undefined;
7
+ export let type: 'button' | 'submit' | 'reset' = 'button';
8
+ export let disabled = false;
9
+ let className = '';
10
+ export { className as class };
11
+ export let style: string = '';
12
+ </script>
13
+
14
+ <button
15
+ {type}
16
+ {title}
17
+ on:click
18
+ class="btn btn-{variant} {iconOnly ? 'btn-icon' : ''} {className}"
19
+ {style}
20
+ disabled={disabled || loading}
21
+ aria-busy={loading ? true : undefined}
22
+ >
23
+ {#if loading}<Spinner size="sm" />{/if}
24
+ <slot />
25
+ </button>
@@ -0,0 +1,142 @@
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/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-cidr-${++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 clampPrefix(s: string): string {
25
+ if (s === '') return s;
26
+ const n = Number(s);
27
+ if (isNaN(n)) return s;
28
+ return String(Math.min(32, Math.max(0, n)));
29
+ }
30
+
31
+ function parseCIDR(cidr: string): [string, string, string, string, string] {
32
+ const [ip = '0.0.0.0', prefix = '24'] = cidr.split('/');
33
+ const parts = ip.split('.');
34
+ return [parts[0] ?? '0', parts[1] ?? '0', parts[2] ?? '0', parts[3] ?? '0', prefix];
35
+ }
36
+
37
+ let fields: [string, string, string, string, string] = parseCIDR(value);
38
+ let inputs: (HTMLInputElement | null)[] = [null, null, null, null, null];
39
+
40
+ function focusAt(i: number) {
41
+ const el = inputs[i];
42
+ if (el) { el.focus(); el.select(); }
43
+ }
44
+
45
+ function commitFields() {
46
+ value = `${fields.slice(0, 4).join('.')}/${fields[4]}`;
47
+ dispatch('change', value);
48
+ }
49
+
50
+ function commitOctet(i: number, raw: string) {
51
+ if (!/^\d{0,3}$/.test(raw)) return;
52
+ fields[i] = clampOctet(raw);
53
+ fields = fields as [string, string, string, string, string];
54
+ commitFields();
55
+ if (raw.length === 3 && i < 4) setTimeout(() => focusAt(i + 1), 0);
56
+ }
57
+
58
+ function commitPrefix(raw: string) {
59
+ if (!/^\d{0,2}$/.test(raw)) return;
60
+ fields[4] = clampPrefix(raw);
61
+ fields = fields as [string, string, string, string, string];
62
+ commitFields();
63
+ }
64
+
65
+ function handleKey(i: number, e: KeyboardEvent) {
66
+ const el = inputs[i]!;
67
+ if (e.key === '.' && i < 3) {
68
+ e.preventDefault();
69
+ focusAt(i + 1);
70
+ } else if (e.key === '/' && i <= 3) {
71
+ e.preventDefault();
72
+ focusAt(4);
73
+ } else if (e.key === 'ArrowRight' && el.selectionStart === el.value.length) {
74
+ if (i < 4) { e.preventDefault(); focusAt(i + 1); }
75
+ } else if (e.key === 'ArrowLeft' && el.selectionStart === 0) {
76
+ if (i > 0) { e.preventDefault(); focusAt(i - 1); }
77
+ } else if (e.key === 'Backspace' && el.value === '' && i > 0) {
78
+ e.preventDefault(); focusAt(i - 1);
79
+ }
80
+ }
81
+
82
+ function handlePaste(e: ClipboardEvent) {
83
+ const text = e.clipboardData?.getData('text').trim() ?? '';
84
+ if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/.test(text)) {
85
+ e.preventDefault();
86
+ const [ip = '0.0.0.0', prefix = '0'] = text.split('/');
87
+ fields = [...ip.split('.').map(clampOctet), clampPrefix(prefix)] as [string, string, string, string, string];
88
+ commitFields();
89
+ setTimeout(() => focusAt(4), 0);
90
+ }
91
+ }
92
+ </script>
93
+
94
+ <div class="cidr-input-wrapper {className}">
95
+ {#if label}
96
+ <label for="{uid}-0" class="cidr-input__label">{label}</label>
97
+ {/if}
98
+ <div class="cidr-input{disabled ? ' cidr-input--disabled' : ''}" role="group" aria-label={label ?? 'CIDR'}>
99
+ {#each [0, 1, 2, 3] as i (i)}
100
+ <input
101
+ bind:this={inputs[i]}
102
+ id={i === 0 ? `${uid}-0` : undefined}
103
+ type="text"
104
+ inputmode="numeric"
105
+ pattern="[0-9]*"
106
+ role="spinbutton"
107
+ aria-label="Octet {i + 1} of 4"
108
+ aria-valuenow={fields[i] === '' ? 0 : Number(fields[i])}
109
+ aria-valuemin={0}
110
+ aria-valuemax={255}
111
+ value={fields[i]}
112
+ {disabled}
113
+ maxlength={3}
114
+ class="cidr-input__octet"
115
+ on:input={(e) => commitOctet(i, e.currentTarget.value)}
116
+ on:keydown={(e) => handleKey(i, e)}
117
+ on:paste={handlePaste}
118
+ on:focus={(e) => e.currentTarget.select()}
119
+ />
120
+ <span class="cidr-input__dot" aria-hidden="true">{i < 3 ? '.' : '/'}</span>
121
+ {/each}
122
+ <input
123
+ bind:this={inputs[4]}
124
+ type="text"
125
+ inputmode="numeric"
126
+ pattern="[0-9]*"
127
+ role="spinbutton"
128
+ aria-label="Prefix length"
129
+ aria-valuenow={fields[4] === '' ? 0 : Number(fields[4])}
130
+ aria-valuemin={0}
131
+ aria-valuemax={32}
132
+ value={fields[4]}
133
+ {disabled}
134
+ maxlength={2}
135
+ class="cidr-input__prefix"
136
+ on:input={(e) => commitPrefix(e.currentTarget.value)}
137
+ on:keydown={(e) => handleKey(4, e)}
138
+ on:paste={handlePaste}
139
+ on:focus={(e) => e.currentTarget.select()}
140
+ />
141
+ </div>
142
+ </div>