@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,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>