@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,86 @@
1
+ <script lang="ts">
2
+ export interface CalendarDay {
3
+ date: string;
4
+ value: number;
5
+ }
6
+
7
+ export let data: CalendarDay[] = [];
8
+ export let maxValue: number | undefined = undefined;
9
+ export let ariaLabel: string = 'Calendar heatmap';
10
+
11
+ const CELL = 10;
12
+ const GAP = 2;
13
+ const STEP = CELL + GAP;
14
+ const WEEKS = 52;
15
+ const DAYS = 7;
16
+ const LEFT = 22;
17
+ const TOP = 16;
18
+ const COLORS = ['rgba(255,255,255,0.06)', '#0d2a5e', '#1a4da6', '#2979ff', '#5ba4ff'];
19
+ const DAY_LABELS = ['Mon', '', 'Wed', '', 'Fri', '', ''];
20
+ const W = LEFT + WEEKS * STEP;
21
+ const H = TOP + DAYS * STEP + 2;
22
+
23
+ function lvl(v: number, mx: number): number {
24
+ if (!v) return 0;
25
+ const r = v / mx;
26
+ return r < 0.25 ? 1 : r < 0.5 ? 2 : r < 0.75 ? 3 : 4;
27
+ }
28
+
29
+ function toDateStr(d: Date): string {
30
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
31
+ }
32
+
33
+ $: ({ cells, monthLabels } = (() => {
34
+ const byDate = new Map(data.map((d) => [d.date, d.value]));
35
+ const mx = maxValue ?? Math.max(1, ...data.map((d) => d.value));
36
+
37
+ const today = new Date();
38
+ today.setHours(0, 0, 0, 0);
39
+ const start = new Date(today);
40
+ start.setDate(start.getDate() - WEEKS * 7 + 1);
41
+ start.setDate(start.getDate() - start.getDay());
42
+
43
+ const cells: { x: number; y: number; fill: string }[] = [];
44
+ const seenMonths = new Set<string>();
45
+ const monthLabels: { x: number; label: string }[] = [];
46
+
47
+ for (let w = 0; w < WEEKS; w++) {
48
+ for (let d = 0; d < DAYS; d++) {
49
+ const date = new Date(start);
50
+ date.setDate(start.getDate() + w * 7 + d);
51
+ const monthKey = `${date.getFullYear()}-${date.getMonth()}`;
52
+ if (!seenMonths.has(monthKey) && d === 0) {
53
+ seenMonths.add(monthKey);
54
+ monthLabels.push({ x: LEFT + w * STEP, label: date.toLocaleString('en', { month: 'short' }) });
55
+ }
56
+ const val = byDate.get(toDateStr(date)) ?? 0;
57
+ cells.push({ x: LEFT + w * STEP, y: TOP + d * STEP, fill: COLORS[lvl(val, mx)] });
58
+ }
59
+ }
60
+
61
+ return { cells, monthLabels };
62
+ })());
63
+ </script>
64
+
65
+ <div role="img" aria-label={ariaLabel} style="width:100%;overflow-x:auto;">
66
+ <svg viewBox="0 0 {W} {H}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
67
+ {#each DAY_LABELS as label, d}
68
+ {#if label}
69
+ <text
70
+ x={LEFT - 3}
71
+ y={TOP + d * STEP + CELL * 0.8}
72
+ text-anchor="end"
73
+ fill="#6E7079"
74
+ font-size="7"
75
+ font-family="inherit"
76
+ >{label}</text>
77
+ {/if}
78
+ {/each}
79
+ {#each monthLabels as m}
80
+ <text x={m.x} y={TOP - 3} fill="#6E7079" font-size="7" font-family="inherit">{m.label}</text>
81
+ {/each}
82
+ {#each cells as c, i (i)}
83
+ <rect x={c.x} y={c.y} width={CELL} height={CELL} rx="2" fill={c.fill} />
84
+ {/each}
85
+ </svg>
86
+ </div>
@@ -0,0 +1,38 @@
1
+ <script context="module" lang="ts">
2
+ export type CalloutVariant = 'info' | 'success' | 'warn' | 'danger' | 'tip';
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ export let variant: CalloutVariant = 'info';
7
+ export let title: string | undefined = undefined;
8
+ </script>
9
+
10
+ <div class="callout callout--{variant}" role="note">
11
+ {#if variant === 'info'}
12
+ <svg class="callout-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
13
+ <path fill-rule="evenodd" d="M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1ZM7.25 5.5a.75.75 0 1 1 1.5 0 .75.75 0 0 1-1.5 0ZM7 7.5h2v4H7v-4Z" />
14
+ </svg>
15
+ {:else if variant === 'success'}
16
+ <svg class="callout-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
17
+ <path fill-rule="evenodd" d="M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1Zm3.78 5.47-4.25 4.5a.75.75 0 0 1-1.06.03L4.72 9.22a.75.75 0 1 1 1.06-1.06l1.22 1.22 3.72-3.94a.75.75 0 1 1 1.06 1.03Z" />
18
+ </svg>
19
+ {:else if variant === 'warn'}
20
+ <svg class="callout-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
21
+ <path fill-rule="evenodd" d="M8.9 1.6a1 1 0 0 0-1.8 0L.6 13a1 1 0 0 0 .9 1.5h13a1 1 0 0 0 .9-1.5L8.9 1.6ZM7.25 6.5a.75.75 0 1 1 1.5 0v3a.75.75 0 0 1-1.5 0v-3Zm.75 6a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z" />
22
+ </svg>
23
+ {:else if variant === 'danger'}
24
+ <svg class="callout-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
25
+ <path fill-rule="evenodd" d="M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1ZM5.97 5.97a.75.75 0 0 1 1.06 0L8 6.94l.97-.97a.75.75 0 1 1 1.06 1.06L9.06 8l.97.97a.75.75 0 0 1-1.06 1.06L8 9.06l-.97.97a.75.75 0 0 1-1.06-1.06L6.94 8l-.97-.97a.75.75 0 0 1 0-1.06Z" />
26
+ </svg>
27
+ {:else}
28
+ <svg class="callout-icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
29
+ <path d="M8 1a5 5 0 0 1 3 9.01V11H5v-.99A5 5 0 0 1 8 1Zm-1 12h2v1.5a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5V13Z" />
30
+ </svg>
31
+ {/if}
32
+ <div class="callout-body">
33
+ {#if title}
34
+ <div class="callout-title">{title}</div>
35
+ {/if}
36
+ <div><slot /></div>
37
+ </div>
38
+ </div>
@@ -0,0 +1,66 @@
1
+ <script lang="ts">
2
+ export interface CandlestickBar {
3
+ label: string;
4
+ open: number;
5
+ close: number;
6
+ high: number;
7
+ low: number;
8
+ }
9
+
10
+ export let bars: CandlestickBar[] = [];
11
+ export let yRange: [number, number] | undefined = undefined;
12
+ export let height = 180;
13
+ export let unit = '';
14
+ export let upColor = '#00C875';
15
+ export let downColor = '#FF7B7B';
16
+ export let ariaLabel = 'Candlestick chart';
17
+
18
+ const VW = 340;
19
+ const PAD_L = 28;
20
+ const PAD_R = 8;
21
+ const PAD_T = 8;
22
+ const PAD_B = 20;
23
+ const PLOT_W = VW - PAD_L - PAD_R;
24
+
25
+ $: n = bars.length;
26
+ $: PLOT_H = height - PAD_T - PAD_B;
27
+ $: allVals = bars.flatMap((b) => [b.low, b.high]);
28
+ $: minV = yRange ? yRange[0] : allVals.length ? Math.min(...allVals) : 0;
29
+ $: maxV = yRange ? yRange[1] : allVals.length ? Math.max(...allVals) : 1;
30
+ $: vRange = maxV - minV || 1;
31
+ $: yTicks = [minV, (minV + maxV) / 2, maxV];
32
+ $: candleW = Math.max(4, (PLOT_W / (n || 1)) * 0.5);
33
+
34
+ function tx(i: number): number {
35
+ return PAD_L + ((i + 0.5) / (n || 1)) * PLOT_W;
36
+ }
37
+
38
+ function ty(v: number): number {
39
+ return PAD_T + (1 - (v - minV) / vRange) * PLOT_H;
40
+ }
41
+ </script>
42
+
43
+ {#if bars.length > 0}
44
+ <div role="img" aria-label={ariaLabel} style="width:100%;">
45
+ <svg viewBox="0 0 {VW} {height}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
46
+ {#each yTicks as v, i (i)}
47
+ <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" />
48
+ <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>
49
+ {/each}
50
+
51
+ {#each bars as b, i (i)}
52
+ {@const x = tx(i)}
53
+ {@const color = b.close >= b.open ? upColor : downColor}
54
+ {@const bodyTop = ty(Math.max(b.open, b.close))}
55
+ {@const bodyBot = ty(Math.min(b.open, b.close))}
56
+ {@const bodyH = Math.max(1, bodyBot - bodyTop)}
57
+ <line x1={x} y1={ty(b.high)} x2={x} y2={ty(b.low)} stroke={color} stroke-width="1" />
58
+ <rect x={x - candleW / 2} y={bodyTop} width={candleW} height={bodyH} fill={color} />
59
+ {/each}
60
+
61
+ {#each bars as b, i (i)}
62
+ <text x={tx(i)} y={height - 4} fill="#6E7079" font-size="9" text-anchor="middle" font-family="inherit">{b.label}</text>
63
+ {/each}
64
+ </svg>
65
+ </div>
66
+ {/if}
@@ -0,0 +1,11 @@
1
+ <script lang="ts">
2
+ export let span: number | undefined = undefined;
3
+ let className = '';
4
+ export { className as class };
5
+ export let style: string = '';
6
+ $: spanStyle = span !== undefined ? `grid-column: span ${span};` : '';
7
+ </script>
8
+
9
+ <div class="card {className}" style="{spanStyle} {style}">
10
+ <slot />
11
+ </div>
@@ -0,0 +1,64 @@
1
+ <script lang="ts">
2
+ export let slides: Array<{ id: string; title: string; description?: string; color?: string }> =
3
+ [];
4
+ export let label: string;
5
+ let klass = '';
6
+ export { klass as class };
7
+
8
+ let index = 0;
9
+
10
+ function prev() {
11
+ index = (index - 1 + slides.length) % slides.length;
12
+ }
13
+ function next() {
14
+ index = (index + 1) % slides.length;
15
+ }
16
+ </script>
17
+
18
+ <section
19
+ class="carousel {klass}"
20
+ aria-roledescription="carousel"
21
+ aria-label={label}
22
+ >
23
+ <div class="carousel-track" aria-live="polite" aria-atomic="false">
24
+ {#each slides as slide, i (slide.id)}
25
+ <div
26
+ role="group"
27
+ aria-roledescription="slide"
28
+ aria-label="{i + 1} of {slides.length}"
29
+ class="carousel-slide{i === index ? ' is-active' : ''}"
30
+ aria-hidden={i !== index ? true : undefined}
31
+ >
32
+ {#if slide.color}
33
+ <div class="carousel-slide-accent" style="background:{slide.color}" aria-hidden="true" />
34
+ {/if}
35
+ <div class="carousel-slide-body">
36
+ <div class="carousel-slide-title">{slide.title}</div>
37
+ {#if slide.description}
38
+ <div class="carousel-slide-desc">{slide.description}</div>
39
+ {/if}
40
+ </div>
41
+ </div>
42
+ {/each}
43
+ </div>
44
+ <div class="carousel-controls">
45
+ <button type="button" class="carousel-btn" aria-label="Previous slide" on:click={prev}
46
+ >&#8249;</button
47
+ >
48
+ <div role="tablist" aria-label="Choose slide to display" class="carousel-indicators">
49
+ {#each slides as slide, i (slide.id)}
50
+ <button
51
+ type="button"
52
+ role="tab"
53
+ aria-selected={i === index}
54
+ aria-label="Slide {i + 1}: {slide.title}"
55
+ class="carousel-dot{i === index ? ' is-active' : ''}"
56
+ on:click={() => (index = i)}
57
+ />
58
+ {/each}
59
+ </div>
60
+ <button type="button" class="carousel-btn" aria-label="Next slide" on:click={next}
61
+ >&#8250;</button
62
+ >
63
+ </div>
64
+ </section>
@@ -0,0 +1,45 @@
1
+ <script context="module" lang="ts">
2
+ let counter = 0;
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ export let checked: boolean = false;
7
+ export let indeterminate: boolean = false;
8
+ export let label: string | undefined = undefined;
9
+ export let id: string | undefined = undefined;
10
+ export let disabled: boolean = false;
11
+
12
+ const uid = `dash-ui-cb-${++counter}`;
13
+ $: inputId = id ?? uid;
14
+
15
+ let el: HTMLInputElement;
16
+ $: if (el) {
17
+ el.checked = checked;
18
+ el.indeterminate = indeterminate;
19
+ }
20
+ </script>
21
+
22
+ {#if label}
23
+ <label class="checkbox-label" for={inputId}>
24
+ <input
25
+ bind:this={el}
26
+ type="checkbox"
27
+ id={inputId}
28
+ {disabled}
29
+ class="checkbox"
30
+ on:change
31
+ {...$$restProps}
32
+ />
33
+ <span>{label}</span>
34
+ </label>
35
+ {:else}
36
+ <input
37
+ bind:this={el}
38
+ type="checkbox"
39
+ id={inputId}
40
+ {disabled}
41
+ class="checkbox"
42
+ on:change
43
+ {...$$restProps}
44
+ />
45
+ {/if}
@@ -0,0 +1,90 @@
1
+ <script lang="ts">
2
+ export interface ChordNode { label: string; color?: string; }
3
+
4
+ export let nodes: ChordNode[];
5
+ export let matrix: number[][];
6
+ export let ariaLabel: string = 'Chord diagram';
7
+
8
+ const PALETTE = ['#006FFF', '#00C875', '#A78BFA', '#F5A623', '#50B8E7', '#F04949'];
9
+ const CX = 160, CY = 160, R_IN = 108, R_ARC = 125, R_LBL = 138, PAD = 0.04;
10
+
11
+ function pt(r: number, a: number) {
12
+ return `${(CX + r * Math.cos(a)).toFixed(1)},${(CY + r * Math.sin(a)).toFixed(1)}`;
13
+ }
14
+
15
+ function arcSlice(r1: number, r2: number, a1: number, a2: number) {
16
+ const lg = a2 - a1 > Math.PI ? 1 : 0;
17
+ return `M${pt(r2, a1)} A${r2},${r2} 0 ${lg},1 ${pt(r2, a2)} L${pt(r1, a2)} A${r1},${r1} 0 ${lg},0 ${pt(r1, a1)} Z`;
18
+ }
19
+
20
+ function ribbon(a1: number, a2: number, b1: number, b2: number) {
21
+ const la = +(a2 - a1 > Math.PI);
22
+ const lb = +(b2 - b1 > Math.PI);
23
+ return `M${pt(R_IN, a1)} A${R_IN},${R_IN} 0 ${la},1 ${pt(R_IN, a2)} Q${CX},${CY} ${pt(R_IN, b2)} A${R_IN},${R_IN} 0 ${lb},0 ${pt(R_IN, b1)} Q${CX},${CY} ${pt(R_IN, a1)}Z`;
24
+ }
25
+
26
+ type Arc = { d: string; fill: string };
27
+ type Chord = { d: string; fill: string };
28
+ type Label = { x: number; y: number; anchor: string; text: string };
29
+
30
+ $: ({ arcs, chords, labels } = (() => {
31
+ const n = nodes.length;
32
+ const totals = nodes.map((_, i) => matrix[i].reduce((s, v) => s + v, 0));
33
+ const grand = totals.reduce((s, v) => s + v, 0);
34
+ if (!grand) return { arcs: [] as Arc[], chords: [] as Chord[], labels: [] as Label[] };
35
+
36
+ const scale = (2 * Math.PI - PAD * n) / grand;
37
+ const nodeA: { start: number; end: number; offsets: number[] }[] = [];
38
+ let angle = -Math.PI / 2;
39
+
40
+ for (let i = 0; i < n; i++) {
41
+ const span = totals[i] * scale;
42
+ const offsets: number[] = [];
43
+ let off = angle;
44
+ for (let j = 0; j < n; j++) {
45
+ offsets.push(off);
46
+ off += matrix[i][j] * scale;
47
+ }
48
+ nodeA.push({ start: angle, end: angle + span, offsets });
49
+ angle += span + PAD;
50
+ }
51
+
52
+ const arcs: Arc[] = nodeA.map((a, i) => ({
53
+ d: arcSlice(R_IN + 2, R_ARC, a.start, a.end),
54
+ fill: nodes[i].color ?? PALETTE[i % PALETTE.length],
55
+ }));
56
+
57
+ const chords: Chord[] = [];
58
+ for (let i = 0; i < n; i++) {
59
+ for (let j = i + 1; j < n; j++) {
60
+ const vij = matrix[i][j], vji = matrix[j][i];
61
+ if (!vij && !vji) continue;
62
+ const a1 = nodeA[i].offsets[j], a2 = a1 + vij * scale;
63
+ const b1 = nodeA[j].offsets[i], b2 = b1 + vji * scale;
64
+ chords.push({ d: ribbon(a1, a2, b1, b2), fill: (nodes[i].color ?? PALETTE[i % PALETTE.length]) + '55' });
65
+ }
66
+ }
67
+
68
+ const labels: Label[] = nodeA.map((a, i) => {
69
+ const mid = (a.start + a.end) / 2;
70
+ const anchor = Math.cos(mid) > 0.2 ? 'start' : Math.cos(mid) < -0.2 ? 'end' : 'middle';
71
+ return { x: CX + R_LBL * Math.cos(mid), y: CY + R_LBL * Math.sin(mid) + 4, anchor, text: nodes[i].label };
72
+ });
73
+
74
+ return { arcs, chords, labels };
75
+ })());
76
+ </script>
77
+
78
+ <div role="img" aria-label={ariaLabel} style="width:100%;">
79
+ <svg viewBox="0 0 320 320" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
80
+ {#each chords as c, i (i)}
81
+ <path d={c.d} fill={c.fill} />
82
+ {/each}
83
+ {#each arcs as a, i (i)}
84
+ <path d={a.d} fill={a.fill} />
85
+ {/each}
86
+ {#each labels as l, i (i)}
87
+ <text x={l.x.toFixed(1)} y={l.y.toFixed(1)} text-anchor={l.anchor} fill="#fff" font-size="10" font-weight="600" font-family="inherit">{l.text}</text>
88
+ {/each}
89
+ </svg>
90
+ </div>
@@ -0,0 +1,119 @@
1
+ <script lang="ts">
2
+ export interface CirclePackItem {
3
+ id: string;
4
+ label: string;
5
+ value: number;
6
+ color?: string;
7
+ }
8
+
9
+ export let items: CirclePackItem[];
10
+ export let height = 200;
11
+ export let ariaLabel = 'Circle packing chart';
12
+
13
+ const PALETTE = ['#006FFF', '#00C875', '#F5A623', '#A78BFA', '#FF7B7B', '#00C8C8', '#FB923C', '#4797FF'];
14
+ const VW = 360;
15
+ const MAX_R = 72;
16
+ const MIN_R = 10;
17
+
18
+ function pack(radii: number[]): { cx: number; cy: number }[] {
19
+ const pos: { cx: number; cy: number; r: number }[] = [];
20
+ for (let i = 0; i < radii.length; i++) {
21
+ const r = radii[i];
22
+ if (i === 0) { pos.push({ cx: 0, cy: 0, r }); continue; }
23
+ if (i === 1) { pos.push({ cx: pos[0].r + r, cy: 0, r }); continue; }
24
+
25
+ const candidates: { cx: number; cy: number }[] = [];
26
+ for (let a = 0; a < pos.length; a++) {
27
+ for (let b = a + 1; b < pos.length; b++) {
28
+ const pa = pos[a], pb = pos[b];
29
+ const da = pa.r + r, db = pb.r + r;
30
+ const dx = pb.cx - pa.cx, dy = pb.cy - pa.cy;
31
+ const dist = Math.hypot(dx, dy);
32
+ if (dist > da + db || dist < Math.abs(da - db)) continue;
33
+ const cosA = (da * da + dist * dist - db * db) / (2 * da * dist);
34
+ if (cosA < -1 || cosA > 1) continue;
35
+ const acos = Math.acos(Math.max(-1, Math.min(1, cosA)));
36
+ const base = Math.atan2(dy, dx);
37
+ for (const sign of [1, -1]) {
38
+ candidates.push({
39
+ cx: pa.cx + da * Math.cos(base + sign * acos),
40
+ cy: pa.cy + da * Math.sin(base + sign * acos),
41
+ });
42
+ }
43
+ }
44
+ const pa = pos[a];
45
+ const ang = Math.atan2(-pa.cy, -pa.cx);
46
+ candidates.push({
47
+ cx: pa.cx + (pa.r + r) * Math.cos(ang),
48
+ cy: pa.cy + (pa.r + r) * Math.sin(ang),
49
+ });
50
+ }
51
+
52
+ let best: { cx: number; cy: number } | null = null;
53
+ let bestDist = Infinity;
54
+ for (const c of candidates) {
55
+ const overlap = pos.some((p) => Math.hypot(c.cx - p.cx, c.cy - p.cy) < p.r + r - 0.4);
56
+ if (!overlap) {
57
+ const d = Math.hypot(c.cx, c.cy);
58
+ if (d < bestDist) { bestDist = d; best = c; }
59
+ }
60
+ }
61
+ const last = pos[pos.length - 1];
62
+ pos.push({ cx: best?.cx ?? last.cx + last.r + r + 2, cy: best?.cy ?? 0, r });
63
+ }
64
+ return pos.map(({ cx, cy }) => ({ cx, cy }));
65
+ }
66
+
67
+ $: layout = (() => {
68
+ if (!items.length) return null;
69
+ const sorted = [...items].sort((a, b) => b.value - a.value);
70
+ const maxV = sorted[0].value;
71
+ const radii = sorted.map((it) => Math.max(MIN_R, Math.round(MAX_R * Math.sqrt(it.value / maxV))));
72
+ const positions = pack(radii);
73
+
74
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
75
+ positions.forEach((p, i) => {
76
+ minX = Math.min(minX, p.cx - radii[i]);
77
+ minY = Math.min(minY, p.cy - radii[i]);
78
+ maxX = Math.max(maxX, p.cx + radii[i]);
79
+ maxY = Math.max(maxY, p.cy + radii[i]);
80
+ });
81
+ const w = maxX - minX, h = maxY - minY;
82
+ const pad = 8;
83
+ const scale = Math.min((VW - pad * 2) / w, (height - pad * 2) / h);
84
+ const offX = (VW - w * scale) / 2 - minX * scale;
85
+ const offY = (height - h * scale) / 2 - minY * scale;
86
+
87
+ return sorted.map((item, i) => {
88
+ const r = radii[i] * scale;
89
+ const cx = positions[i].cx * scale + offX;
90
+ const cy = positions[i].cy * scale + offY;
91
+ const color = item.color ?? PALETTE[i % PALETTE.length];
92
+ return { id: item.id, label: item.label, value: item.value, cx, cy, r, color };
93
+ });
94
+ })();
95
+ </script>
96
+
97
+ {#if layout}
98
+ <div role="img" aria-label={ariaLabel} style="width:100%;">
99
+ <svg viewBox="0 0 {VW} {height}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
100
+ {#each layout as c (c.id)}
101
+ <g>
102
+ <circle cx={c.cx} cy={c.cy} r={c.r} fill={c.color} fill-opacity={0.25} stroke={c.color} stroke-width={1.5} />
103
+ {#if c.r >= 18}
104
+ <text x={c.cx} y={c.cy - (c.r >= 26 ? 5 : 0)} text-anchor="middle" dominant-baseline="middle"
105
+ font-size={Math.min(11, c.r * 0.38)} fill="#E2E4ED" font-family="inherit" font-weight={500}>
106
+ {c.label}
107
+ </text>
108
+ {/if}
109
+ {#if c.r >= 26}
110
+ <text x={c.cx} y={c.cy + 10} text-anchor="middle" dominant-baseline="middle"
111
+ font-size={Math.min(9, c.r * 0.3)} fill={c.color} font-family="inherit">
112
+ {c.value}
113
+ </text>
114
+ {/if}
115
+ </g>
116
+ {/each}
117
+ </svg>
118
+ </div>
119
+ {/if}
@@ -0,0 +1,49 @@
1
+ <script lang="ts">
2
+ import { onDestroy } from 'svelte';
3
+ export let code: string = '';
4
+ export let label: string = 'Code block';
5
+ export let language: string = '';
6
+ let className = '';
7
+ export { className as class };
8
+
9
+ let copied = false;
10
+ let timer: ReturnType<typeof setTimeout> | null = null;
11
+
12
+ function handleCopy() {
13
+ navigator.clipboard.writeText(code).then(() => {
14
+ copied = true;
15
+ if (timer) clearTimeout(timer);
16
+ timer = setTimeout(() => { copied = false; }, 2000);
17
+ });
18
+ }
19
+
20
+ onDestroy(() => { if (timer) clearTimeout(timer); });
21
+ </script>
22
+
23
+ <div role="region" aria-label={label} class="code-block {className}">
24
+ <div class="code-block__header">
25
+ {#if language}
26
+ <span class="code-block__lang">{language}</span>
27
+ {:else}
28
+ <span></span>
29
+ {/if}
30
+ <button
31
+ type="button"
32
+ class="copy-btn{copied ? ' copy-btn--done' : ''}"
33
+ aria-label={copied ? 'Copied!' : 'Copy code'}
34
+ on:click={handleCopy}
35
+ >
36
+ {#if copied}
37
+ <svg viewBox="0 0 14 14" width="14" height="14" fill="none" aria-hidden="true" focusable="false">
38
+ <path d="M2.5 7.5l3.5 3.5L12 3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
39
+ </svg>
40
+ {:else}
41
+ <svg viewBox="0 0 14 14" width="14" height="14" fill="none" aria-hidden="true" focusable="false">
42
+ <rect x="4" y="1" width="9" height="10" rx="1.5" stroke="currentColor" stroke-width="1.5" />
43
+ <rect x="1" y="4" width="9" height="10" rx="1.5" stroke="currentColor" stroke-width="1.5" />
44
+ </svg>
45
+ {/if}
46
+ </button>
47
+ </div>
48
+ <pre class="code-block__pre"><code class="code-block__code">{code}</code></pre>
49
+ </div>
@@ -0,0 +1,91 @@
1
+ <script context="module" lang="ts">
2
+ let counter = 0;
3
+ export interface ColorSwatchDef {
4
+ value: string;
5
+ label: string;
6
+ color: string;
7
+ }
8
+ export const COLOR_SWATCHES: ColorSwatchDef[] = [
9
+ { value: 'blue', label: 'Blue', color: '#006FFF' },
10
+ { value: 'green', label: 'Green', color: '#00B070' },
11
+ { value: 'amber', label: 'Amber', color: '#F5A623' },
12
+ { value: 'red', label: 'Red', color: '#F03A3A' },
13
+ { value: 'purple', label: 'Purple', color: '#9B59B6' },
14
+ { value: 'teal', label: 'Teal', color: '#1ABC9C' },
15
+ { value: 'pink', label: 'Pink', color: '#E91E8C' },
16
+ { value: 'orange', label: 'Orange', color: '#FF6B35' },
17
+ { value: 'slate', label: 'Slate', color: '#6E7079' },
18
+ { value: 'light', label: 'Light', color: '#E8E8EC' },
19
+ ];
20
+ </script>
21
+
22
+ <script lang="ts">
23
+ export let label: string = 'Colour';
24
+ export let srOnlyLabel: boolean = false;
25
+ export let value: string = COLOR_SWATCHES[0].value;
26
+ export let swatches: ColorSwatchDef[] = COLOR_SWATCHES;
27
+ export let disabled: boolean = false;
28
+ export let onChange: ((v: string) => void) | undefined = undefined;
29
+
30
+ const groupId = `dash-ui-cp-${++counter}`;
31
+
32
+ function handleKeyDown(e: KeyboardEvent) {
33
+ const cells = Array.from(
34
+ (e.currentTarget as HTMLElement).querySelectorAll<HTMLInputElement>('input[type="radio"]'),
35
+ );
36
+ if (!cells.length) return;
37
+ const idx = cells.findIndex((c) => c.value === value);
38
+ let next = idx;
39
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') next = (idx + 1) % cells.length;
40
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp')
41
+ next = (idx - 1 + cells.length) % cells.length;
42
+ else if (e.key === 'Home') next = 0;
43
+ else if (e.key === 'End') next = cells.length - 1;
44
+ else return;
45
+ e.preventDefault();
46
+ cells[next].focus();
47
+ value = cells[next].value;
48
+ onChange?.(value);
49
+ }
50
+
51
+ $: hasMatch = swatches.some((s) => s.value === value);
52
+ </script>
53
+
54
+ <div class="color-picker{disabled ? ' color-picker--disabled' : ''}">
55
+ <div id="{groupId}-label" class="color-picker__label{srOnlyLabel ? ' sr-only' : ''}"
56
+ >{label}</div
57
+ >
58
+ <div
59
+ role="radiogroup"
60
+ aria-labelledby="{groupId}-label"
61
+ class="color-picker__grid"
62
+ tabindex="-1"
63
+ on:keydown={handleKeyDown}
64
+ >
65
+ {#each swatches as sw, i}
66
+ {@const checked = sw.value === value}
67
+ {@const tabIdx = checked || (i === 0 && !hasMatch) ? 0 : -1}
68
+ <label class="color-picker__swatch" title={sw.label}>
69
+ <input
70
+ type="radio"
71
+ name={groupId}
72
+ value={sw.value}
73
+ {checked}
74
+ {disabled}
75
+ class="sr-only"
76
+ tabindex={tabIdx}
77
+ aria-label={sw.label}
78
+ on:change={() => {
79
+ value = sw.value;
80
+ onChange?.(value);
81
+ }}
82
+ />
83
+ <span
84
+ class="color-picker__circle{checked ? ' color-picker__circle--selected' : ''}"
85
+ style="background:{sw.color}"
86
+ aria-hidden="true"
87
+ />
88
+ </label>
89
+ {/each}
90
+ </div>
91
+ </div>