@aspect-ops/exon-ui 0.2.0 → 0.2.1

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 (38) hide show
  1. package/README.md +200 -0
  2. package/dist/components/Accordion/Accordion.svelte +2 -2
  3. package/dist/components/Accordion/AccordionItem.svelte +2 -2
  4. package/dist/components/Chatbot/ChatMessage.svelte +143 -0
  5. package/dist/components/Chatbot/ChatMessage.svelte.d.ts +8 -0
  6. package/dist/components/Chatbot/Chatbot.svelte +640 -0
  7. package/dist/components/Chatbot/Chatbot.svelte.d.ts +22 -0
  8. package/dist/components/Chatbot/index.d.ts +3 -0
  9. package/dist/components/Chatbot/index.js +2 -0
  10. package/dist/components/Chatbot/types.d.ts +48 -0
  11. package/dist/components/Chatbot/types.js +2 -0
  12. package/dist/components/ContactForm/ContactForm.svelte +564 -0
  13. package/dist/components/ContactForm/ContactForm.svelte.d.ts +44 -0
  14. package/dist/components/ContactForm/index.d.ts +1 -0
  15. package/dist/components/ContactForm/index.js +1 -0
  16. package/dist/components/DoughnutChart/DoughnutChart.svelte +372 -0
  17. package/dist/components/DoughnutChart/DoughnutChart.svelte.d.ts +25 -0
  18. package/dist/components/DoughnutChart/index.d.ts +1 -0
  19. package/dist/components/DoughnutChart/index.js +1 -0
  20. package/dist/components/FAB/FAB.svelte +5 -1
  21. package/dist/components/FAB/FABGroup.svelte +10 -2
  22. package/dist/components/FileUpload/FileUpload.svelte +12 -12
  23. package/dist/components/Mermaid/Mermaid.svelte +206 -0
  24. package/dist/components/Mermaid/Mermaid.svelte.d.ts +28 -0
  25. package/dist/components/Mermaid/index.d.ts +1 -0
  26. package/dist/components/Mermaid/index.js +1 -0
  27. package/dist/components/Mermaid/mermaid.d.ts +21 -0
  28. package/dist/components/Popover/PopoverTrigger.svelte +1 -3
  29. package/dist/components/ViewCounter/ViewCounter.svelte +157 -0
  30. package/dist/components/ViewCounter/ViewCounter.svelte.d.ts +17 -0
  31. package/dist/components/ViewCounter/index.d.ts +1 -0
  32. package/dist/components/ViewCounter/index.js +1 -0
  33. package/dist/index.d.ts +10 -1
  34. package/dist/index.js +9 -0
  35. package/dist/styles/tokens.css +2 -1
  36. package/dist/types/index.d.ts +1 -1
  37. package/dist/types/input.d.ts +20 -0
  38. package/package.json +2 -1
package/README.md CHANGED
@@ -1047,6 +1047,206 @@ Components optimized for Capacitor mobile apps with gesture support, haptic feed
1047
1047
 
1048
1048
  - `edges` - Array of edges: `'top'` | `'right'` | `'bottom'` | `'left'` (default: all)
1049
1049
 
1050
+ ## Website Components
1051
+
1052
+ ### Chatbot
1053
+
1054
+ AI-ready chat widget with session management, typing indicators, and lead capture.
1055
+
1056
+ ```svelte
1057
+ <script>
1058
+ import { Chatbot } from '@aspect-ops/exon-ui';
1059
+
1060
+ async function handleMessage(message, sessionId) {
1061
+ // Call your AI backend (OpenAI, Claude, etc.)
1062
+ const response = await fetch('/api/chat', {
1063
+ method: 'POST',
1064
+ body: JSON.stringify({ message, sessionId })
1065
+ });
1066
+ return (await response.json()).reply;
1067
+ }
1068
+
1069
+ async function handleLeadCapture(data, sessionId) {
1070
+ // Send to your CRM
1071
+ await fetch('/api/leads', {
1072
+ method: 'POST',
1073
+ body: JSON.stringify({ ...data, sessionId })
1074
+ });
1075
+ }
1076
+ </script>
1077
+
1078
+ <Chatbot
1079
+ title="Support Chat"
1080
+ subtitle="We typically reply within minutes"
1081
+ welcomeMessage="Hello! How can I help you today?"
1082
+ onSendMessage={handleMessage}
1083
+ onLeadCapture={handleLeadCapture}
1084
+ position="bottom-right"
1085
+ />
1086
+ ```
1087
+
1088
+ **Props:**
1089
+
1090
+ | Prop | Type | Default | Description |
1091
+ | ------------------- | ---------- | ---------------- | ----------------------------------------- |
1092
+ | `title` | `string` | `'Chat Support'` | Chat header title |
1093
+ | `subtitle` | `string` | - | Chat header subtitle |
1094
+ | `welcomeMessage` | `string` | - | Initial welcome message |
1095
+ | `placeholder` | `string` | `'Type...'` | Input placeholder text |
1096
+ | `defaultOpen` | `boolean` | `false` | Open chat on mount |
1097
+ | `position` | `string` | `'bottom-right'` | Position: bottom-right, bottom-left |
1098
+ | `inactivityTimeout` | `number` | `40000` | Auto-open after inactivity (ms), 0=off |
1099
+ | `onSendMessage` | `function` | - | Callback: `(msg, sessionId) => reply` |
1100
+ | `onLeadCapture` | `function` | - | Callback: `(data, sessionId) => void` |
1101
+ | `onEscalate` | `function` | - | Callback: `(sessionId, messages) => void` |
1102
+
1103
+ ### ContactForm
1104
+
1105
+ Customizable contact form with real-time validation and UTM tracking.
1106
+
1107
+ ```svelte
1108
+ <script>
1109
+ import { ContactForm } from '@aspect-ops/exon-ui';
1110
+
1111
+ async function handleSubmit(data) {
1112
+ const response = await fetch('/api/contact', {
1113
+ method: 'POST',
1114
+ body: JSON.stringify(data)
1115
+ });
1116
+ return { success: response.ok };
1117
+ }
1118
+ </script>
1119
+
1120
+ <ContactForm
1121
+ submitText="Send Message"
1122
+ successMessage="Thank you! We'll be in touch soon."
1123
+ onSubmit={handleSubmit}
1124
+ />
1125
+ ```
1126
+
1127
+ **Props:**
1128
+
1129
+ | Prop | Type | Default | Description |
1130
+ | ------------------ | --------------- | -------------- | ------------------------------------- |
1131
+ | `fields` | `FieldConfig[]` | Default fields | Array of field configurations |
1132
+ | `initialValues` | `object` | `{}` | Initial form values |
1133
+ | `submitText` | `string` | `'Submit'` | Submit button text |
1134
+ | `successMessage` | `string` | `'Thank you!'` | Success message after submit |
1135
+ | `privacyNotice` | `string` | - | Privacy policy text |
1136
+ | `extractUtmParams` | `boolean` | `true` | Extract UTM params from URL |
1137
+ | `onSubmit` | `function` | - | Callback: `(data) => { success }` |
1138
+ | `onValidate` | `function` | - | Callback: `(isValid, errors) => void` |
1139
+
1140
+ **Default fields:** firstName, lastName, email, phone, company, message
1141
+
1142
+ ### ViewCounter
1143
+
1144
+ Track and display page views with session-based deduplication.
1145
+
1146
+ ```svelte
1147
+ <script>
1148
+ import { ViewCounter } from '@aspect-ops/exon-ui';
1149
+
1150
+ async function getCount(slug) {
1151
+ const res = await fetch(`/api/views/${slug}`);
1152
+ return (await res.json()).count;
1153
+ }
1154
+
1155
+ async function trackView(slug) {
1156
+ const res = await fetch(`/api/views/${slug}`, { method: 'POST' });
1157
+ return (await res.json()).count;
1158
+ }
1159
+ </script>
1160
+
1161
+ <ViewCounter slug="blog-post-123" onGetCount={getCount} onTrackView={trackView} />
1162
+ ```
1163
+
1164
+ **Props:**
1165
+
1166
+ | Prop | Type | Default | Description |
1167
+ | ------------- | ---------- | ------- | ---------------------------- |
1168
+ | `slug` | `string` | - | Unique content identifier |
1169
+ | `showLabel` | `boolean` | `true` | Show "views" label |
1170
+ | `trackView` | `boolean` | `true` | Track view (or just display) |
1171
+ | `onGetCount` | `function` | - | Callback: `(slug) => count` |
1172
+ | `onTrackView` | `function` | - | Callback: `(slug) => count` |
1173
+
1174
+ ### DoughnutChart
1175
+
1176
+ Pure SVG doughnut chart with interactive legend (no external dependencies).
1177
+
1178
+ ```svelte
1179
+ <script>
1180
+ import { DoughnutChart } from '@aspect-ops/exon-ui';
1181
+
1182
+ const data = [
1183
+ { label: 'Direct', value: 45000 },
1184
+ { label: 'Referral', value: 28000 },
1185
+ { label: 'Organic', value: 32000 },
1186
+ { label: 'Social', value: 15000 }
1187
+ ];
1188
+ </script>
1189
+
1190
+ <DoughnutChart title="Traffic Sources" {data} size={200} showLegend showTotal />
1191
+ ```
1192
+
1193
+ **Props:**
1194
+
1195
+ | Prop | Type | Default | Description |
1196
+ | ------------ | ---------- | --------------- | ------------------------- |
1197
+ | `title` | `string` | - | Chart title |
1198
+ | `data` | `array` | `[]` | `[{ label, value }, ...]` |
1199
+ | `size` | `number` | `200` | Chart size in pixels |
1200
+ | `thickness` | `number` | `0.6` | Doughnut thickness (0-1) |
1201
+ | `colors` | `string[]` | Default palette | Custom color array |
1202
+ | `showLegend` | `boolean` | `true` | Show legend |
1203
+ | `showTotal` | `boolean` | `true` | Show center total |
1204
+
1205
+ ### Mermaid
1206
+
1207
+ Render Mermaid diagrams with customizable theming.
1208
+
1209
+ **Note:** Requires `mermaid` package: `npm install mermaid`
1210
+
1211
+ ```svelte
1212
+ <script>
1213
+ import { Mermaid } from '@aspect-ops/exon-ui';
1214
+
1215
+ const diagram = `flowchart TD
1216
+ A[Start] --> B{Decision}
1217
+ B -->|Yes| C[Action 1]
1218
+ B -->|No| D[Action 2]
1219
+ C --> E[End]
1220
+ D --> E`;
1221
+ </script>
1222
+
1223
+ <!-- Using code prop -->
1224
+ <Mermaid code={diagram} />
1225
+
1226
+ <!-- Using slot -->
1227
+ <Mermaid>flowchart LR A --> B --> C</Mermaid>
1228
+ ```
1229
+
1230
+ **Props:**
1231
+
1232
+ | Prop | Type | Default | Description |
1233
+ | ------- | -------- | ------------- | ---------------------- |
1234
+ | `code` | `string` | - | Mermaid diagram code |
1235
+ | `theme` | `object` | ExonPro theme | Custom theme variables |
1236
+
1237
+ **Theme variables:**
1238
+
1239
+ ```javascript
1240
+ const theme = {
1241
+ primaryColor: '#4654A3',
1242
+ primaryTextColor: '#270949',
1243
+ primaryBorderColor: '#4654A3',
1244
+ lineColor: '#270949',
1245
+ secondaryColor: '#FF3131',
1246
+ tertiaryColor: '#f3f4f6'
1247
+ };
1248
+ ```
1249
+
1050
1250
  ## Theming & Customization
1051
1251
 
1052
1252
  ### Quick Setup
@@ -25,8 +25,8 @@
25
25
  return value ? [value] : [];
26
26
  });
27
27
 
28
- // Context key
29
- const ACCORDION_KEY = Symbol('accordion');
28
+ // Context key - must match AccordionItem
29
+ const ACCORDION_KEY = 'exon-accordion-context';
30
30
 
31
31
  // Context API
32
32
  const accordionContext = {
@@ -11,8 +11,8 @@
11
11
 
12
12
  let { value, disabled = false, class: className = '', header, children }: Props = $props();
13
13
 
14
- // Context key (must match parent)
15
- const ACCORDION_KEY = Symbol('accordion');
14
+ // Context key (must match parent Accordion)
15
+ const ACCORDION_KEY = 'exon-accordion-context';
16
16
 
17
17
  // Get context from parent Accordion
18
18
  const ctx = getContext<{
@@ -0,0 +1,143 @@
1
+ <script lang="ts">
2
+ import type { ChatMessage } from './types.js';
3
+
4
+ interface Props {
5
+ message: ChatMessage;
6
+ class?: string;
7
+ }
8
+
9
+ let { message, class: className = '' }: Props = $props();
10
+
11
+ // Format message content (bold, links)
12
+ function formatContent(content: string): string {
13
+ // Convert **text** to bold
14
+ let formatted = content.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
15
+ // Convert [text](url) to links
16
+ formatted = formatted.replace(
17
+ /\[([^\]]+)\]\(([^)]+)\)/g,
18
+ '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
19
+ );
20
+ return formatted;
21
+ }
22
+
23
+ const formattedContent = $derived(formatContent(message.content));
24
+ const isUser = $derived(message.role === 'user');
25
+ const isSystem = $derived(message.role === 'system');
26
+ </script>
27
+
28
+ <div
29
+ class="chat-message chat-message--{message.role} {className}"
30
+ class:chat-message--user={isUser}
31
+ class:chat-message--assistant={!isUser && !isSystem}
32
+ class:chat-message--system={isSystem}
33
+ >
34
+ {#if !isUser && !isSystem}
35
+ <div class="chat-message__avatar">
36
+ <svg
37
+ xmlns="http://www.w3.org/2000/svg"
38
+ width="20"
39
+ height="20"
40
+ viewBox="0 0 24 24"
41
+ fill="none"
42
+ stroke="currentColor"
43
+ stroke-width="2"
44
+ stroke-linecap="round"
45
+ stroke-linejoin="round"
46
+ >
47
+ <path d="M12 8V4H8" />
48
+ <rect width="16" height="12" x="4" y="8" rx="2" />
49
+ <path d="M2 14h2" />
50
+ <path d="M20 14h2" />
51
+ <path d="M15 13v2" />
52
+ <path d="M9 13v2" />
53
+ </svg>
54
+ </div>
55
+ {/if}
56
+ <div class="chat-message__bubble">
57
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
58
+ {@html formattedContent}
59
+ </div>
60
+ {#if message.timestamp}
61
+ <div class="chat-message__time">
62
+ {new Date(message.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
63
+ </div>
64
+ {/if}
65
+ </div>
66
+
67
+ <style>
68
+ .chat-message {
69
+ display: flex;
70
+ gap: var(--space-sm, 0.5rem);
71
+ max-width: 85%;
72
+ margin-bottom: var(--space-sm, 0.5rem);
73
+ }
74
+
75
+ .chat-message--user {
76
+ margin-left: auto;
77
+ flex-direction: row-reverse;
78
+ }
79
+
80
+ .chat-message--assistant {
81
+ margin-right: auto;
82
+ }
83
+
84
+ .chat-message--system {
85
+ margin: var(--space-sm, 0.5rem) auto;
86
+ max-width: 90%;
87
+ }
88
+
89
+ .chat-message__avatar {
90
+ flex-shrink: 0;
91
+ width: 2rem;
92
+ height: 2rem;
93
+ display: flex;
94
+ align-items: center;
95
+ justify-content: center;
96
+ border-radius: var(--radius-full, 9999px);
97
+ background: var(--color-primary, #3b82f6);
98
+ color: var(--color-text-inverse, #ffffff);
99
+ }
100
+
101
+ .chat-message__bubble {
102
+ padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
103
+ border-radius: var(--radius-lg, 0.75rem);
104
+ font-size: var(--text-sm, 0.875rem);
105
+ line-height: var(--leading-relaxed, 1.625);
106
+ word-wrap: break-word;
107
+ }
108
+
109
+ .chat-message--user .chat-message__bubble {
110
+ background: var(--color-primary, #3b82f6);
111
+ color: var(--color-text-inverse, #ffffff);
112
+ border-bottom-right-radius: var(--radius-sm, 0.25rem);
113
+ }
114
+
115
+ .chat-message--assistant .chat-message__bubble {
116
+ background: var(--color-bg-muted, #f3f4f6);
117
+ color: var(--color-text, #1f2937);
118
+ border-bottom-left-radius: var(--radius-sm, 0.25rem);
119
+ }
120
+
121
+ .chat-message--system .chat-message__bubble {
122
+ background: var(--color-bg-elevated, #ffffff);
123
+ border: 1px solid var(--color-border, #e5e7eb);
124
+ color: var(--color-text-muted, #6b7280);
125
+ font-style: italic;
126
+ text-align: center;
127
+ }
128
+
129
+ .chat-message__bubble :global(a) {
130
+ color: inherit;
131
+ text-decoration: underline;
132
+ }
133
+
134
+ .chat-message__bubble :global(strong) {
135
+ font-weight: var(--font-semibold, 600);
136
+ }
137
+
138
+ .chat-message__time {
139
+ align-self: flex-end;
140
+ font-size: var(--text-xs, 0.75rem);
141
+ color: var(--color-text-subtle, #9ca3af);
142
+ }
143
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { ChatMessage } from './types.js';
2
+ interface Props {
3
+ message: ChatMessage;
4
+ class?: string;
5
+ }
6
+ declare const ChatMessage: import("svelte").Component<Props, {}, "">;
7
+ type ChatMessage = ReturnType<typeof ChatMessage>;
8
+ export default ChatMessage;