@w5-ui/svelte 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/package.json +57 -0
- package/src/lib/components/Accordion.svelte +3 -0
- package/src/lib/components/AccordionItem.svelte +40 -0
- package/src/lib/components/ActionMenu.svelte +96 -0
- package/src/lib/components/ActivityFeed.svelte +60 -0
- package/src/lib/components/Alert.svelte +19 -0
- package/src/lib/components/AnnotatedTimeSeries.svelte +90 -0
- package/src/lib/components/ArcDiagram.svelte +74 -0
- package/src/lib/components/AreaChart.svelte +64 -0
- package/src/lib/components/Avatar.svelte +30 -0
- package/src/lib/components/AvatarGroup.svelte +35 -0
- package/src/lib/components/Badge.svelte +25 -0
- package/src/lib/components/Banner.svelte +37 -0
- package/src/lib/components/BarChart.svelte +52 -0
- package/src/lib/components/BeeswarmChart.svelte +88 -0
- package/src/lib/components/BoxPlot.svelte +66 -0
- package/src/lib/components/Breadcrumb.svelte +31 -0
- package/src/lib/components/BubbleChart.svelte +69 -0
- package/src/lib/components/BulletChart.svelte +70 -0
- package/src/lib/components/BumpChart.svelte +109 -0
- package/src/lib/components/Button.svelte +25 -0
- package/src/lib/components/CIDRInput.svelte +142 -0
- package/src/lib/components/CalendarHeatmap.svelte +86 -0
- package/src/lib/components/Callout.svelte +38 -0
- package/src/lib/components/CandlestickChart.svelte +66 -0
- package/src/lib/components/Card.svelte +11 -0
- package/src/lib/components/Carousel.svelte +64 -0
- package/src/lib/components/Checkbox.svelte +45 -0
- package/src/lib/components/ChordDiagram.svelte +90 -0
- package/src/lib/components/CirclePacking.svelte +119 -0
- package/src/lib/components/CodeBlock.svelte +49 -0
- package/src/lib/components/ColorPicker.svelte +91 -0
- package/src/lib/components/ColumnToggle.svelte +96 -0
- package/src/lib/components/Combobox.svelte +138 -0
- package/src/lib/components/CommandPalette.svelte +152 -0
- package/src/lib/components/ConfirmDialog.svelte +84 -0
- package/src/lib/components/ContextMenu.svelte +107 -0
- package/src/lib/components/ContextualHelp.svelte +73 -0
- package/src/lib/components/ContourPlot.svelte +164 -0
- package/src/lib/components/CopyButton.svelte +33 -0
- package/src/lib/components/CorrelationMatrix.svelte +90 -0
- package/src/lib/components/CountUp.svelte +49 -0
- package/src/lib/components/CumulativeDistribution.svelte +67 -0
- package/src/lib/components/DatePicker.svelte +201 -0
- package/src/lib/components/DateRangePicker.svelte +249 -0
- package/src/lib/components/Dendrogram.svelte +128 -0
- package/src/lib/components/Donut.svelte +49 -0
- package/src/lib/components/DotPlot.svelte +121 -0
- package/src/lib/components/Drawer.svelte +60 -0
- package/src/lib/components/DualAxisChart.svelte +80 -0
- package/src/lib/components/DumbbellChart.svelte +56 -0
- package/src/lib/components/DurationInput.svelte +118 -0
- package/src/lib/components/EmptyState.svelte +21 -0
- package/src/lib/components/ErrorBandChart.svelte +69 -0
- package/src/lib/components/ExpandableRow.svelte +42 -0
- package/src/lib/components/Field.svelte +40 -0
- package/src/lib/components/FileUpload.svelte +96 -0
- package/src/lib/components/FilterBuilder.svelte +160 -0
- package/src/lib/components/FlameGraph.svelte +88 -0
- package/src/lib/components/ForceGraph.svelte +104 -0
- package/src/lib/components/FunnelChart.svelte +50 -0
- package/src/lib/components/GanttChart.svelte +49 -0
- package/src/lib/components/Gauge.svelte +53 -0
- package/src/lib/components/GroupedList.svelte +87 -0
- package/src/lib/components/HealthBar.svelte +19 -0
- package/src/lib/components/HeatMap.svelte +64 -0
- package/src/lib/components/HexbinChart.svelte +100 -0
- package/src/lib/components/Histogram.svelte +93 -0
- package/src/lib/components/HorizonChart.svelte +71 -0
- package/src/lib/components/HoverCard.svelte +45 -0
- package/src/lib/components/IPInput.svelte +103 -0
- package/src/lib/components/IcicleChart.svelte +87 -0
- package/src/lib/components/IconButton.svelte +19 -0
- package/src/lib/components/InlineEdit.svelte +72 -0
- package/src/lib/components/Input.svelte +18 -0
- package/src/lib/components/InputGroup.svelte +14 -0
- package/src/lib/components/JsonViewer.svelte +15 -0
- package/src/lib/components/JsonViewerNode.svelte +59 -0
- package/src/lib/components/KVTable.svelte +18 -0
- package/src/lib/components/KanbanBoard.svelte +141 -0
- package/src/lib/components/Kbd.svelte +16 -0
- package/src/lib/components/LineChart.svelte +67 -0
- package/src/lib/components/LogViewer.svelte +84 -0
- package/src/lib/components/LollipopChart.svelte +79 -0
- package/src/lib/components/MACInput.svelte +98 -0
- package/src/lib/components/MarimekkoChart.svelte +108 -0
- package/src/lib/components/MatrixChart.svelte +55 -0
- package/src/lib/components/Menubar.svelte +131 -0
- package/src/lib/components/MirroredBarChart.svelte +120 -0
- package/src/lib/components/Modal.svelte +90 -0
- package/src/lib/components/MultiSelect.svelte +165 -0
- package/src/lib/components/NightingaleChart.svelte +63 -0
- package/src/lib/components/NotificationPanel.svelte +170 -0
- package/src/lib/components/NumberInput.svelte +93 -0
- package/src/lib/components/OTPInput.svelte +94 -0
- package/src/lib/components/Pagination.svelte +58 -0
- package/src/lib/components/ParallelCoordinates.svelte +98 -0
- package/src/lib/components/ParetoChart.svelte +88 -0
- package/src/lib/components/PasswordInput.svelte +62 -0
- package/src/lib/components/PieChart.svelte +91 -0
- package/src/lib/components/Pill.svelte +12 -0
- package/src/lib/components/PolarHeatmap.svelte +85 -0
- package/src/lib/components/Popover.svelte +89 -0
- package/src/lib/components/ProgressBar.svelte +29 -0
- package/src/lib/components/PunchCard.svelte +73 -0
- package/src/lib/components/QuadrantChart.svelte +122 -0
- package/src/lib/components/RadarChart.svelte +71 -0
- package/src/lib/components/RadialBarChart.svelte +76 -0
- package/src/lib/components/RadioGroup.svelte +48 -0
- package/src/lib/components/RangeSlider.svelte +68 -0
- package/src/lib/components/RankedList.svelte +49 -0
- package/src/lib/components/ResizablePanel.svelte +77 -0
- package/src/lib/components/RidgelinePlot.svelte +103 -0
- package/src/lib/components/RowToggle.svelte +19 -0
- package/src/lib/components/SankeyDiagram.svelte +100 -0
- package/src/lib/components/ScatterPlot.svelte +54 -0
- package/src/lib/components/SearchBox.svelte +11 -0
- package/src/lib/components/SegmentedControl.svelte +51 -0
- package/src/lib/components/Select.svelte +110 -0
- package/src/lib/components/SelectionToolbar.svelte +42 -0
- package/src/lib/components/Sidebar.svelte +44 -0
- package/src/lib/components/Signal.svelte +12 -0
- package/src/lib/components/Skeleton.svelte +23 -0
- package/src/lib/components/SkipLink.svelte +9 -0
- package/src/lib/components/Slider.svelte +48 -0
- package/src/lib/components/SlopeChart.svelte +91 -0
- package/src/lib/components/SortHeader.svelte +19 -0
- package/src/lib/components/SortableList.svelte +87 -0
- package/src/lib/components/Sparkline.svelte +32 -0
- package/src/lib/components/SparklineMatrix.svelte +84 -0
- package/src/lib/components/Spinner.svelte +6 -0
- package/src/lib/components/SplitButton.svelte +102 -0
- package/src/lib/components/Spoiler.svelte +41 -0
- package/src/lib/components/StackedBarChart.svelte +83 -0
- package/src/lib/components/StackedProgress.svelte +32 -0
- package/src/lib/components/StarRating.svelte +64 -0
- package/src/lib/components/Stat.svelte +24 -0
- package/src/lib/components/StatusIndicator.svelte +10 -0
- package/src/lib/components/StepChart.svelte +74 -0
- package/src/lib/components/Stepper.svelte +40 -0
- package/src/lib/components/StreamGraph.svelte +93 -0
- package/src/lib/components/StripeChart.svelte +52 -0
- package/src/lib/components/SunburstChart.svelte +80 -0
- package/src/lib/components/SwitchPortGrid.svelte +45 -0
- package/src/lib/components/TabPanel.svelte +17 -0
- package/src/lib/components/Tabs.svelte +45 -0
- package/src/lib/components/Tag.svelte +19 -0
- package/src/lib/components/TagInput.svelte +92 -0
- package/src/lib/components/Textarea.svelte +31 -0
- package/src/lib/components/ThresholdAreaChart.svelte +72 -0
- package/src/lib/components/TimePicker.svelte +116 -0
- package/src/lib/components/TimeRange.svelte +104 -0
- package/src/lib/components/Timeline.svelte +32 -0
- package/src/lib/components/Toast.svelte +52 -0
- package/src/lib/components/Toggle.svelte +23 -0
- package/src/lib/components/ToggleGroup.svelte +32 -0
- package/src/lib/components/Tooltip.svelte +16 -0
- package/src/lib/components/Topbar.svelte +63 -0
- package/src/lib/components/TransferList.svelte +184 -0
- package/src/lib/components/TreeItem.svelte +61 -0
- package/src/lib/components/TreeMap.svelte +90 -0
- package/src/lib/components/TreeView.svelte +130 -0
- package/src/lib/components/UptimeTimeline.svelte +86 -0
- package/src/lib/components/VennDiagram.svelte +91 -0
- package/src/lib/components/ViolinPlot.svelte +114 -0
- package/src/lib/components/VirtualList.svelte +38 -0
- package/src/lib/components/WaffleChart.svelte +68 -0
- package/src/lib/components/WaterfallChart.svelte +96 -0
- package/src/lib/components/WordCloud.svelte +103 -0
- package/src/lib/icons/AlarmIcon.svelte +5 -0
- package/src/lib/icons/BellIcon.svelte +6 -0
- package/src/lib/icons/CaretIcon.svelte +8 -0
- package/src/lib/icons/ClientsIcon.svelte +5 -0
- package/src/lib/icons/CloseIcon.svelte +6 -0
- package/src/lib/icons/DashboardIcon.svelte +7 -0
- package/src/lib/icons/DevicesIcon.svelte +5 -0
- package/src/lib/icons/DownloadIcon.svelte +6 -0
- package/src/lib/icons/HelpIcon.svelte +7 -0
- package/src/lib/icons/InfraIcon.svelte +7 -0
- package/src/lib/icons/IntegrationsIcon.svelte +5 -0
- package/src/lib/icons/LogsIcon.svelte +6 -0
- package/src/lib/icons/PlusIcon.svelte +6 -0
- package/src/lib/icons/PortsIcon.svelte +6 -0
- package/src/lib/icons/SearchIcon.svelte +10 -0
- package/src/lib/icons/SecurityIcon.svelte +4 -0
- package/src/lib/icons/SettingsIcon.svelte +5 -0
- package/src/lib/icons/TopologyIcon.svelte +7 -0
- package/src/lib/icons/UpdatesIcon.svelte +6 -0
- package/src/lib/icons/VpnIcon.svelte +5 -0
- package/src/lib/icons/WifiIcon.svelte +5 -0
- package/src/lib/icons/WirelessIcon.svelte +4 -0
- package/src/lib/icons.ts +22 -0
- package/src/lib/index.ts +275 -0
- package/src/lib/styles.css +2 -0
package/README.md
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@w5-ui/svelte",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Svelte 4 components for the Dash UI design system - mirrors the @w5-ui/react API 1:1. Part of Webber's design systems.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"svelte",
|
|
7
|
+
"components",
|
|
8
|
+
"design-system",
|
|
9
|
+
"dashboard",
|
|
10
|
+
"ui-library",
|
|
11
|
+
"dash-ui"
|
|
12
|
+
],
|
|
13
|
+
"author": "Webber Takken (https://github.com/webbertakken)",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"homepage": "https://github.com/webbertakken/dash-ui#readme",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/webbertakken/dash-ui.git",
|
|
19
|
+
"directory": "packages/svelte"
|
|
20
|
+
},
|
|
21
|
+
"bugs": "https://github.com/webbertakken/dash-ui/issues",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"sideEffects": [
|
|
24
|
+
"**/*.css",
|
|
25
|
+
"**/*.svelte"
|
|
26
|
+
],
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public",
|
|
29
|
+
"provenance": true
|
|
30
|
+
},
|
|
31
|
+
"svelte": "./src/lib/index.ts",
|
|
32
|
+
"main": "./src/lib/index.ts",
|
|
33
|
+
"types": "./src/lib/index.ts",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"svelte": "./src/lib/index.ts",
|
|
37
|
+
"types": "./src/lib/index.ts",
|
|
38
|
+
"default": "./src/lib/index.ts"
|
|
39
|
+
},
|
|
40
|
+
"./styles.css": "./src/lib/styles.css",
|
|
41
|
+
"./components/*.svelte": "./src/lib/components/*.svelte"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"svelte": ">=4"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@w5-ui/assets": "workspace:*",
|
|
48
|
+
"@w5-ui/tokens": "workspace:*"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"svelte": "^4.2.19",
|
|
52
|
+
"typescript": "^5.5.0"
|
|
53
|
+
},
|
|
54
|
+
"files": [
|
|
55
|
+
"src/lib/**"
|
|
56
|
+
]
|
|
57
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export let title: string;
|
|
3
|
+
export let defaultOpen = false;
|
|
4
|
+
|
|
5
|
+
let open = defaultOpen;
|
|
6
|
+
const uid = Math.random().toString(36).slice(2, 9);
|
|
7
|
+
const btnId = `acc-btn-${uid}`;
|
|
8
|
+
const panelId = `acc-panel-${uid}`;
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<div class="accordion-item">
|
|
12
|
+
<h3 class="accordion-header">
|
|
13
|
+
<button
|
|
14
|
+
id={btnId}
|
|
15
|
+
type="button"
|
|
16
|
+
class="accordion-trigger"
|
|
17
|
+
aria-expanded={open}
|
|
18
|
+
aria-controls={panelId}
|
|
19
|
+
on:click={() => (open = !open)}
|
|
20
|
+
>
|
|
21
|
+
<span>{title}</span>
|
|
22
|
+
<svg
|
|
23
|
+
class="accordion-chevron"
|
|
24
|
+
class:open
|
|
25
|
+
viewBox="0 0 16 16"
|
|
26
|
+
fill="none"
|
|
27
|
+
aria-hidden="true"
|
|
28
|
+
>
|
|
29
|
+
<path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
|
30
|
+
</svg>
|
|
31
|
+
</button>
|
|
32
|
+
</h3>
|
|
33
|
+
{#if open}
|
|
34
|
+
<div id={panelId} role="region" aria-labelledby={btnId} class="accordion-panel">
|
|
35
|
+
<div class="accordion-content">
|
|
36
|
+
<slot />
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
{/if}
|
|
40
|
+
</div>
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<script context="module" lang="ts">
|
|
2
|
+
let counter = 0;
|
|
3
|
+
export interface ActionMenuItem { id: string; label: string; danger?: boolean; disabled?: boolean; }
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
import { createEventDispatcher, onMount, onDestroy } from 'svelte';
|
|
8
|
+
|
|
9
|
+
export let items: ActionMenuItem[] = [];
|
|
10
|
+
export let label: string = 'Actions';
|
|
11
|
+
|
|
12
|
+
const dispatch = createEventDispatcher<{ action: string }>();
|
|
13
|
+
const uid = `dash-ui-am-${++counter}`;
|
|
14
|
+
const menuId = `${uid}-menu`;
|
|
15
|
+
|
|
16
|
+
let open = false;
|
|
17
|
+
let activeIdx = 0;
|
|
18
|
+
let triggerEl: HTMLButtonElement;
|
|
19
|
+
let wrapperEl: HTMLDivElement;
|
|
20
|
+
|
|
21
|
+
function toggle() {
|
|
22
|
+
if (!open) activeIdx = 0;
|
|
23
|
+
open = !open;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function activate(id: string) {
|
|
27
|
+
dispatch('action', id);
|
|
28
|
+
open = false;
|
|
29
|
+
triggerEl?.focus();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function onKeyDown(e: KeyboardEvent) {
|
|
33
|
+
if (!open) {
|
|
34
|
+
if (['ArrowDown', 'Enter', ' '].includes(e.key)) {
|
|
35
|
+
e.preventDefault();
|
|
36
|
+
activeIdx = 0;
|
|
37
|
+
open = true;
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (e.key === 'Escape') { e.preventDefault(); open = false; triggerEl?.focus(); }
|
|
42
|
+
else if (e.key === 'ArrowDown') { e.preventDefault(); activeIdx = Math.min(activeIdx + 1, items.length - 1); }
|
|
43
|
+
else if (e.key === 'ArrowUp') { e.preventDefault(); activeIdx = Math.max(activeIdx - 1, 0); }
|
|
44
|
+
else if (e.key === 'Home') { e.preventDefault(); activeIdx = 0; }
|
|
45
|
+
else if (e.key === 'End') { e.preventDefault(); activeIdx = items.length - 1; }
|
|
46
|
+
else if (e.key === 'Enter' || e.key === ' ') {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
const item = items[activeIdx];
|
|
49
|
+
if (item && !item.disabled) activate(item.id);
|
|
50
|
+
}
|
|
51
|
+
else if (e.key === 'Tab') { open = false; }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function handleOutside(e: MouseEvent) {
|
|
55
|
+
if (open && !wrapperEl?.contains(e.target as Node)) open = false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
onMount(() => document.addEventListener('mousedown', handleOutside));
|
|
59
|
+
onDestroy(() => document.removeEventListener('mousedown', handleOutside));
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<div class="action-menu-root" bind:this={wrapperEl}>
|
|
63
|
+
<button
|
|
64
|
+
bind:this={triggerEl}
|
|
65
|
+
type="button"
|
|
66
|
+
class="icon-btn action-menu-trigger"
|
|
67
|
+
aria-label={label}
|
|
68
|
+
aria-haspopup="menu"
|
|
69
|
+
aria-expanded={open}
|
|
70
|
+
aria-controls={open ? menuId : undefined}
|
|
71
|
+
on:click={toggle}
|
|
72
|
+
on:keydown={onKeyDown}
|
|
73
|
+
>
|
|
74
|
+
<svg width="14" height="14" viewBox="0 0 14 14" aria-hidden="true" focusable="false" fill="currentColor">
|
|
75
|
+
<circle cx="7" cy="2.5" r="1.2" />
|
|
76
|
+
<circle cx="7" cy="7" r="1.2" />
|
|
77
|
+
<circle cx="7" cy="11.5" r="1.2" />
|
|
78
|
+
</svg>
|
|
79
|
+
</button>
|
|
80
|
+
{#if open}
|
|
81
|
+
<ul id={menuId} role="menu" aria-label={label} class="action-menu">
|
|
82
|
+
{#each items as item, idx (item.id)}
|
|
83
|
+
<li
|
|
84
|
+
role="menuitem"
|
|
85
|
+
tabindex="-1"
|
|
86
|
+
aria-disabled={item.disabled}
|
|
87
|
+
data-active={idx === activeIdx ? 'true' : undefined}
|
|
88
|
+
data-danger={item.danger ? 'true' : undefined}
|
|
89
|
+
class="action-menu-item"
|
|
90
|
+
on:mouseenter={() => { activeIdx = idx; }}
|
|
91
|
+
on:mousedown|preventDefault={() => { if (!item.disabled) activate(item.id); }}
|
|
92
|
+
>{item.label}</li>
|
|
93
|
+
{/each}
|
|
94
|
+
</ul>
|
|
95
|
+
{/if}
|
|
96
|
+
</div>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script context="module" lang="ts">
|
|
2
|
+
export type ActivitySeverity = 'info' | 'success' | 'warn' | 'error' | 'neutral';
|
|
3
|
+
export interface ActivityItem {
|
|
4
|
+
id: string;
|
|
5
|
+
title: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
time: string;
|
|
8
|
+
severity?: ActivitySeverity;
|
|
9
|
+
}
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
import { afterUpdate } from 'svelte';
|
|
14
|
+
export let items: ActivityItem[] = [];
|
|
15
|
+
export let label: string = 'Activity feed';
|
|
16
|
+
export let busy: boolean = false;
|
|
17
|
+
export let maxHeight: number = 360;
|
|
18
|
+
export let autoScroll: boolean = false;
|
|
19
|
+
|
|
20
|
+
let endEl: HTMLDivElement;
|
|
21
|
+
|
|
22
|
+
afterUpdate(() => {
|
|
23
|
+
if (autoScroll && endEl) endEl.scrollIntoView({ block: 'nearest' });
|
|
24
|
+
});
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<div
|
|
28
|
+
role="feed"
|
|
29
|
+
aria-label={label}
|
|
30
|
+
aria-busy={busy}
|
|
31
|
+
class="activity-feed"
|
|
32
|
+
style:max-height="{maxHeight}px"
|
|
33
|
+
style:overflow-y="auto"
|
|
34
|
+
>
|
|
35
|
+
{#each items as item, i (item.id)}
|
|
36
|
+
<article
|
|
37
|
+
class="af-item af-item--{item.severity ?? 'neutral'}"
|
|
38
|
+
aria-labelledby="af-title-{item.id}"
|
|
39
|
+
aria-describedby={item.description ? `af-desc-${item.id}` : undefined}
|
|
40
|
+
aria-posinset={i + 1}
|
|
41
|
+
aria-setsize={items.length}
|
|
42
|
+
tabindex="0"
|
|
43
|
+
>
|
|
44
|
+
<div class="af-dot" aria-hidden="true" />
|
|
45
|
+
<div class="af-body">
|
|
46
|
+
<div class="af-header">
|
|
47
|
+
<span id="af-title-{item.id}" class="af-title">{item.title}</span>
|
|
48
|
+
<span class="af-time">{item.time}</span>
|
|
49
|
+
</div>
|
|
50
|
+
{#if item.description}
|
|
51
|
+
<p id="af-desc-{item.id}" class="af-desc">{item.description}</p>
|
|
52
|
+
{/if}
|
|
53
|
+
</div>
|
|
54
|
+
</article>
|
|
55
|
+
{/each}
|
|
56
|
+
{#if items.length === 0}
|
|
57
|
+
<div class="af-empty" role="status">No activity</div>
|
|
58
|
+
{/if}
|
|
59
|
+
<div bind:this={endEl} />
|
|
60
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import CloseIcon from '../icons/CloseIcon.svelte';
|
|
3
|
+
export let variant: 'success' | 'warn' | 'danger' | 'info' = 'info';
|
|
4
|
+
export let onDismiss: (() => void) | undefined = undefined;
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<div
|
|
8
|
+
class="alert alert-{variant}"
|
|
9
|
+
role={variant === 'danger' ? 'alert' : 'status'}
|
|
10
|
+
aria-live={variant === 'danger' ? 'assertive' : 'polite'}
|
|
11
|
+
aria-atomic="true"
|
|
12
|
+
>
|
|
13
|
+
<span class="alert-body"><slot /></span>
|
|
14
|
+
{#if onDismiss}
|
|
15
|
+
<button type="button" class="alert-dismiss icon-btn" on:click={onDismiss} aria-label="Dismiss alert">
|
|
16
|
+
<CloseIcon />
|
|
17
|
+
</button>
|
|
18
|
+
{/if}
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export interface TimeSeriesAnnotation {
|
|
3
|
+
index: number;
|
|
4
|
+
label: string;
|
|
5
|
+
color?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export let data: number[] = [];
|
|
9
|
+
export let labels: string[] = [];
|
|
10
|
+
export let annotations: TimeSeriesAnnotation[] = [];
|
|
11
|
+
export let color = '#006FFF';
|
|
12
|
+
export let height = 160;
|
|
13
|
+
export let ariaLabel = 'Annotated time series';
|
|
14
|
+
|
|
15
|
+
const VW = 400;
|
|
16
|
+
const PAD = { t: 16, r: 8, b: 28, l: 32 };
|
|
17
|
+
|
|
18
|
+
$: n = data.length;
|
|
19
|
+
$: chartW = VW - PAD.l - PAD.r;
|
|
20
|
+
$: chartH = height - PAD.t - PAD.b;
|
|
21
|
+
$: minV = n >= 2 ? Math.min(...data) : 0;
|
|
22
|
+
$: maxV = n >= 2 ? Math.max(...data) || 1 : 1;
|
|
23
|
+
$: range = maxV - minV || 1;
|
|
24
|
+
$: bot = PAD.t + chartH;
|
|
25
|
+
|
|
26
|
+
function toX(i: number): number {
|
|
27
|
+
return PAD.l + (i / (n - 1)) * chartW;
|
|
28
|
+
}
|
|
29
|
+
function toY(v: number): number {
|
|
30
|
+
return PAD.t + chartH - ((v - minV) / range) * chartH;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
$: pts = n >= 2 ? data.map((v, i) => [toX(i), toY(v)] as [number, number]) : [];
|
|
34
|
+
|
|
35
|
+
$: linePath = pts.length
|
|
36
|
+
? pts.map(([x, y], i) => `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`).join(' ')
|
|
37
|
+
: '';
|
|
38
|
+
|
|
39
|
+
$: areaPath = pts.length
|
|
40
|
+
? `${linePath} L${pts[n - 1][0].toFixed(1)},${bot} L${pts[0][0].toFixed(1)},${bot} Z`
|
|
41
|
+
: '';
|
|
42
|
+
|
|
43
|
+
$: gridLines = [0, 0.5, 1].map((f) => ({
|
|
44
|
+
y: PAD.t + (1 - f) * chartH,
|
|
45
|
+
label: String(Math.round(minV + f * range)),
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
$: stride = n <= 8 ? 1 : Math.ceil(n / 6);
|
|
49
|
+
$: xLabels = n >= 2
|
|
50
|
+
? data
|
|
51
|
+
.map((_, i) => ({ x: toX(i), text: labels[i] ?? String(i), i }))
|
|
52
|
+
.filter(({ i }) => i % stride === 0 || i === n - 1)
|
|
53
|
+
: [];
|
|
54
|
+
|
|
55
|
+
$: markers = annotations.map((a) => ({
|
|
56
|
+
x: toX(Math.max(0, Math.min(n - 1, a.index))),
|
|
57
|
+
label: a.label,
|
|
58
|
+
color: a.color ?? '#F5A623',
|
|
59
|
+
}));
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
{#if n >= 2}
|
|
63
|
+
<div role="img" aria-label={ariaLabel} style="width:100%;">
|
|
64
|
+
<svg viewBox="0 0 {VW} {height}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
|
|
65
|
+
{#each gridLines as g, i (i)}
|
|
66
|
+
<line x1={PAD.l} y1={g.y} x2={VW - PAD.r} y2={g.y} stroke="rgba(255,255,255,0.06)" stroke-width="1" />
|
|
67
|
+
<text x={PAD.l - 4} y={g.y + 3} fill="#6E7079" font-size="8" text-anchor="end" font-family="inherit">{g.label}</text>
|
|
68
|
+
{/each}
|
|
69
|
+
|
|
70
|
+
<path d={areaPath} fill={color} fill-opacity="0.08" />
|
|
71
|
+
<path d={linePath} fill="none" stroke={color} stroke-width="1.5" stroke-linejoin="round" stroke-linecap="round" />
|
|
72
|
+
|
|
73
|
+
{#each markers as m, i (i)}
|
|
74
|
+
<line x1={m.x} y1={PAD.t} x2={m.x} y2={PAD.t + chartH} stroke={m.color} stroke-width="1" stroke-dasharray="3,2" opacity="0.85" />
|
|
75
|
+
<text x={m.x + 2} y={PAD.t + 8} fill={m.color} font-size="7" font-family="inherit">{m.label}</text>
|
|
76
|
+
{/each}
|
|
77
|
+
|
|
78
|
+
{#each xLabels as l (l.i)}
|
|
79
|
+
<text
|
|
80
|
+
x={l.x}
|
|
81
|
+
y={height - 6}
|
|
82
|
+
fill="#6E7079"
|
|
83
|
+
font-size="8"
|
|
84
|
+
text-anchor={l.i === 0 ? 'start' : l.i === n - 1 ? 'end' : 'middle'}
|
|
85
|
+
font-family="inherit"
|
|
86
|
+
>{l.text}</text>
|
|
87
|
+
{/each}
|
|
88
|
+
</svg>
|
|
89
|
+
</div>
|
|
90
|
+
{/if}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export interface ArcNode {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
color?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ArcLink {
|
|
9
|
+
source: string;
|
|
10
|
+
target: string;
|
|
11
|
+
value?: number;
|
|
12
|
+
color?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export let nodes: ArcNode[];
|
|
16
|
+
export let links: ArcLink[];
|
|
17
|
+
export let height = 180;
|
|
18
|
+
export let ariaLabel = 'Arc diagram';
|
|
19
|
+
|
|
20
|
+
const PALETTE = ['#006FFF', '#00C875', '#F5A623', '#A78BFA', '#FF7B7B', '#00C8C8', '#FB923C'];
|
|
21
|
+
const VW = 400;
|
|
22
|
+
const PAD = { t: 12, r: 20, b: 36, l: 20 };
|
|
23
|
+
|
|
24
|
+
$: layout = (() => {
|
|
25
|
+
const n = nodes.length;
|
|
26
|
+
if (!n) return null;
|
|
27
|
+
const chartW = VW - PAD.l - PAD.r;
|
|
28
|
+
const baseY = height - PAD.b;
|
|
29
|
+
const maxArcH = baseY - PAD.t;
|
|
30
|
+
|
|
31
|
+
const xMap: Record<string, number> = {};
|
|
32
|
+
const colorMap: Record<string, string> = {};
|
|
33
|
+
|
|
34
|
+
const dots = nodes.map((node, i) => {
|
|
35
|
+
const x = n > 1 ? PAD.l + (i / (n - 1)) * chartW : VW / 2;
|
|
36
|
+
xMap[node.id] = x;
|
|
37
|
+
const color = node.color ?? PALETTE[i % PALETTE.length];
|
|
38
|
+
colorMap[node.id] = color;
|
|
39
|
+
return { x, y: baseY, color, label: node.label };
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const arcs = links.flatMap((link) => {
|
|
43
|
+
const x1 = xMap[link.source];
|
|
44
|
+
const x2 = xMap[link.target];
|
|
45
|
+
if (x1 == null || x2 == null) return [];
|
|
46
|
+
const midX = (x1 + x2) / 2;
|
|
47
|
+
const dist = Math.abs(x2 - x1);
|
|
48
|
+
const arcH = Math.min(dist * 0.45, maxArcH * 0.9);
|
|
49
|
+
const ctl = baseY - arcH;
|
|
50
|
+
const color = link.color ?? colorMap[link.source] ?? PALETTE[0];
|
|
51
|
+
const sw = link.value != null ? Math.max(1, Math.min(4, link.value / 10)) : 1.5;
|
|
52
|
+
return [{ d: `M ${x1} ${baseY} Q ${midX} ${ctl} ${x2} ${baseY}`, color, sw }];
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return { dots, arcs, baseY };
|
|
56
|
+
})();
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
{#if layout}
|
|
60
|
+
<div role="img" aria-label={ariaLabel} style="width:100%;">
|
|
61
|
+
<svg viewBox="0 0 {VW} {height}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
|
|
62
|
+
<line x1={PAD.l} y1={layout.baseY} x2={VW - PAD.r} y2={layout.baseY} stroke="rgba(255,255,255,0.08)" stroke-width={1} />
|
|
63
|
+
{#each layout.arcs as arc, i (i)}
|
|
64
|
+
<path d={arc.d} fill="none" stroke={arc.color} stroke-width={arc.sw} stroke-opacity={0.6} />
|
|
65
|
+
{/each}
|
|
66
|
+
{#each layout.dots as dot, i (i)}
|
|
67
|
+
<circle cx={dot.x} cy={dot.y} r={5} fill={dot.color} />
|
|
68
|
+
{/each}
|
|
69
|
+
{#each layout.dots as dot, i (i)}
|
|
70
|
+
<text x={dot.x} y={layout.baseY + 18} text-anchor="middle" font-size={9} fill="#A4A7B5" font-family="inherit">{dot.label}</text>
|
|
71
|
+
{/each}
|
|
72
|
+
</svg>
|
|
73
|
+
</div>
|
|
74
|
+
{/if}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export interface AreaChartSeries {
|
|
3
|
+
label: string;
|
|
4
|
+
color: string;
|
|
5
|
+
values: number[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export let series: AreaChartSeries[] = [];
|
|
9
|
+
export let labels: string[] = [];
|
|
10
|
+
export let height: number = 160;
|
|
11
|
+
export let ariaLabel: string = 'Area 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
|
+
$: chartW = VW - PAD.l - PAD.r;
|
|
18
|
+
$: chartH = height - PAD.t - PAD.b;
|
|
19
|
+
|
|
20
|
+
$: totals = Array.from({ length: n }, (_, i) =>
|
|
21
|
+
series.reduce((acc, s) => acc + (s.values[i] ?? 0), 0),
|
|
22
|
+
);
|
|
23
|
+
$: maxV = totals.length ? Math.max(...totals, 1) : 1;
|
|
24
|
+
|
|
25
|
+
function toX(i: number): number {
|
|
26
|
+
return PAD.l + (n > 1 ? (i / (n - 1)) * chartW : chartW / 2);
|
|
27
|
+
}
|
|
28
|
+
function toY(v: number): number {
|
|
29
|
+
return PAD.t + chartH - (v / maxV) * chartH;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
$: gridLines = [0, 0.25, 0.5, 0.75, 1].map((f) => PAD.t + (1 - f) * chartH);
|
|
33
|
+
|
|
34
|
+
$: areas = n > 0
|
|
35
|
+
? series.map((s, j) => {
|
|
36
|
+
const bottoms = Array.from({ length: n }, (_, i) =>
|
|
37
|
+
series.slice(0, j).reduce((acc, sr) => acc + (sr.values[i] ?? 0), 0),
|
|
38
|
+
);
|
|
39
|
+
const tops = bottoms.map((b, i) => b + (s.values[i] ?? 0));
|
|
40
|
+
const topPts = tops.map((v, i) => `${toX(i).toFixed(1)},${toY(v).toFixed(1)}`);
|
|
41
|
+
const botPts = bottoms
|
|
42
|
+
.map((v, i) => `${toX(i).toFixed(1)},${toY(v).toFixed(1)}`)
|
|
43
|
+
.reverse();
|
|
44
|
+
return { color: s.color, label: s.label, d: `M ${topPts.join(' L ')} L ${botPts.join(' L ')} Z` };
|
|
45
|
+
})
|
|
46
|
+
: [];
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<div role="img" aria-label={ariaLabel} style="width:100%;">
|
|
50
|
+
<svg viewBox="0 0 {VW} {height}" style="width:100%;height:auto;display:block;" aria-hidden="true" focusable="false">
|
|
51
|
+
{#each gridLines as y}
|
|
52
|
+
<line x1={PAD.l} y1={y} x2={VW - PAD.r} y2={y} stroke="rgba(255,255,255,0.06)" stroke-width="1" />
|
|
53
|
+
{/each}
|
|
54
|
+
{#each areas as s (s.label)}
|
|
55
|
+
<path d={s.d} fill={s.color} fill-opacity="0.75" />
|
|
56
|
+
{/each}
|
|
57
|
+
{#if labels.length > 0}
|
|
58
|
+
{#each labels as lbl, i}
|
|
59
|
+
{@const x = PAD.l + (labels.length > 1 ? (i / (labels.length - 1)) * chartW : chartW / 2)}
|
|
60
|
+
<text x={x} y={height - 4} text-anchor="middle" fill="#6E7079" font-size="9" font-family="inherit">{lbl}</text>
|
|
61
|
+
{/each}
|
|
62
|
+
{/if}
|
|
63
|
+
</svg>
|
|
64
|
+
</div>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script context="module" lang="ts">
|
|
2
|
+
export type AvatarSize = 'sm' | 'md' | 'lg';
|
|
3
|
+
export type AvatarStatus = 'online' | 'offline' | 'away';
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
export let initials: string = '?';
|
|
8
|
+
export let src: string | undefined = undefined;
|
|
9
|
+
export let alt: string | undefined = undefined;
|
|
10
|
+
export let size: AvatarSize = 'md';
|
|
11
|
+
export let status: AvatarStatus | undefined = undefined;
|
|
12
|
+
export let className: string = '';
|
|
13
|
+
export let style: string = '';
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<span
|
|
17
|
+
class="avatar avatar-{size}{className ? ` ${className}` : ''}"
|
|
18
|
+
role="img"
|
|
19
|
+
aria-label={alt ?? (src ? '' : initials)}
|
|
20
|
+
{style}
|
|
21
|
+
>
|
|
22
|
+
{#if src}
|
|
23
|
+
<img {src} alt="" />
|
|
24
|
+
{:else}
|
|
25
|
+
{initials}
|
|
26
|
+
{/if}
|
|
27
|
+
{#if status}
|
|
28
|
+
<span class="avatar-status avatar-status-{status}" aria-hidden="true"></span>
|
|
29
|
+
{/if}
|
|
30
|
+
</span>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script context="module" lang="ts">
|
|
2
|
+
export interface AvatarItem {
|
|
3
|
+
initials?: string;
|
|
4
|
+
src?: string;
|
|
5
|
+
alt?: string;
|
|
6
|
+
}
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<script lang="ts">
|
|
10
|
+
import Avatar from './Avatar.svelte';
|
|
11
|
+
import type { AvatarSize } from './Avatar.svelte';
|
|
12
|
+
|
|
13
|
+
export let avatars: AvatarItem[] = [];
|
|
14
|
+
export let max: number = 5;
|
|
15
|
+
export let size: AvatarSize = 'md';
|
|
16
|
+
export let ariaLabel: string = 'Avatar group';
|
|
17
|
+
|
|
18
|
+
$: visible = avatars.slice(0, max);
|
|
19
|
+
$: overflow = avatars.length - visible.length;
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<span class="avatar-group" role="list" aria-label={ariaLabel}>
|
|
23
|
+
{#each visible as av, i (i)}
|
|
24
|
+
<span role="listitem" class="avatar-group-item">
|
|
25
|
+
<Avatar initials={av.initials} src={av.src} alt={av.alt} {size} />
|
|
26
|
+
</span>
|
|
27
|
+
{/each}
|
|
28
|
+
{#if overflow > 0}
|
|
29
|
+
<span
|
|
30
|
+
role="listitem"
|
|
31
|
+
class="avatar avatar-{size} avatar-overflow"
|
|
32
|
+
aria-label="{overflow} more"
|
|
33
|
+
>+{overflow}</span>
|
|
34
|
+
{/if}
|
|
35
|
+
</span>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script context="module" lang="ts">
|
|
2
|
+
export type BadgeColor = 'danger' | 'warn' | 'info' | 'success' | 'neutral';
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
export let count: number | undefined = undefined;
|
|
7
|
+
export let dot: boolean = false;
|
|
8
|
+
export let max: number = 99;
|
|
9
|
+
export let showZero: boolean = false;
|
|
10
|
+
export let color: BadgeColor = 'danger';
|
|
11
|
+
let klass: string = '';
|
|
12
|
+
export { klass as class };
|
|
13
|
+
|
|
14
|
+
$: show = dot || (count !== undefined && (showZero || count > 0));
|
|
15
|
+
$: label = count !== undefined ? (count > max ? `${max}+` : String(count)) : '';
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<span class="badge-wrapper{klass ? ` ${klass}` : ''}">
|
|
19
|
+
<slot />
|
|
20
|
+
{#if show}
|
|
21
|
+
<span class="badge badge-{color}{dot ? ' badge-dot' : ''}" aria-hidden="true">
|
|
22
|
+
{#if !dot}{label}{/if}
|
|
23
|
+
</span>
|
|
24
|
+
{/if}
|
|
25
|
+
</span>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script context="module" lang="ts">
|
|
2
|
+
export type BannerVariant = 'info' | 'success' | 'warn' | 'danger';
|
|
3
|
+
export interface BannerAction {
|
|
4
|
+
label: string;
|
|
5
|
+
onClick: () => void;
|
|
6
|
+
}
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<script lang="ts">
|
|
10
|
+
import CloseIcon from '../icons/CloseIcon.svelte';
|
|
11
|
+
export let variant: BannerVariant = 'info';
|
|
12
|
+
export let title: string | undefined = undefined;
|
|
13
|
+
export let action: BannerAction | undefined = undefined;
|
|
14
|
+
export let onDismiss: (() => void) | undefined = undefined;
|
|
15
|
+
|
|
16
|
+
$: isUrgent = variant === 'danger' || variant === 'warn';
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<div
|
|
20
|
+
class="banner banner--{variant}"
|
|
21
|
+
role={isUrgent ? 'alert' : 'status'}
|
|
22
|
+
aria-live={isUrgent ? 'assertive' : 'polite'}
|
|
23
|
+
aria-atomic="true"
|
|
24
|
+
>
|
|
25
|
+
<div class="banner__body">
|
|
26
|
+
{#if title}<span class="banner__title">{title}</span>{/if}
|
|
27
|
+
<span><slot /></span>
|
|
28
|
+
</div>
|
|
29
|
+
{#if action}
|
|
30
|
+
<button type="button" class="banner__action" on:click={action.onClick}>{action.label}</button>
|
|
31
|
+
{/if}
|
|
32
|
+
{#if onDismiss}
|
|
33
|
+
<button type="button" class="banner__dismiss icon-btn" on:click={onDismiss} aria-label="Dismiss banner">
|
|
34
|
+
<CloseIcon />
|
|
35
|
+
</button>
|
|
36
|
+
{/if}
|
|
37
|
+
</div>
|