@aspect-ops/exon-ui 0.1.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 (52) hide show
  1. package/README.md +341 -16
  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/NumberInput/NumberInput.svelte +293 -0
  29. package/dist/components/NumberInput/NumberInput.svelte.d.ts +16 -0
  30. package/dist/components/NumberInput/index.d.ts +1 -0
  31. package/dist/components/NumberInput/index.js +1 -0
  32. package/dist/components/Pagination/Pagination.svelte +243 -0
  33. package/dist/components/Pagination/Pagination.svelte.d.ts +10 -0
  34. package/dist/components/Pagination/index.d.ts +1 -0
  35. package/dist/components/Pagination/index.js +1 -0
  36. package/dist/components/Popover/PopoverTrigger.svelte +1 -3
  37. package/dist/components/ToggleGroup/ToggleGroup.svelte +91 -0
  38. package/dist/components/ToggleGroup/ToggleGroup.svelte.d.ts +13 -0
  39. package/dist/components/ToggleGroup/ToggleGroupItem.svelte +158 -0
  40. package/dist/components/ToggleGroup/ToggleGroupItem.svelte.d.ts +9 -0
  41. package/dist/components/ToggleGroup/index.d.ts +3 -0
  42. package/dist/components/ToggleGroup/index.js +2 -0
  43. package/dist/components/ViewCounter/ViewCounter.svelte +157 -0
  44. package/dist/components/ViewCounter/ViewCounter.svelte.d.ts +17 -0
  45. package/dist/components/ViewCounter/index.d.ts +1 -0
  46. package/dist/components/ViewCounter/index.js +1 -0
  47. package/dist/index.d.ts +13 -1
  48. package/dist/index.js +12 -0
  49. package/dist/styles/tokens.css +2 -1
  50. package/dist/types/index.d.ts +1 -1
  51. package/dist/types/input.d.ts +35 -0
  52. package/package.json +2 -1
@@ -0,0 +1,206 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import type { Snippet } from 'svelte';
4
+
5
+ interface ThemeVariables {
6
+ primaryColor?: string;
7
+ primaryTextColor?: string;
8
+ primaryBorderColor?: string;
9
+ lineColor?: string;
10
+ secondaryColor?: string;
11
+ tertiaryColor?: string;
12
+ background?: string;
13
+ mainBkg?: string;
14
+ nodeBorder?: string;
15
+ clusterBkg?: string;
16
+ titleColor?: string;
17
+ edgeLabelBackground?: string;
18
+ }
19
+
20
+ interface Props {
21
+ /** Mermaid diagram code. If not provided, uses slot content */
22
+ code?: string;
23
+ /** Custom theme variables */
24
+ theme?: ThemeVariables;
25
+ /** Custom class */
26
+ class?: string;
27
+ /** Slot content for diagram code */
28
+ children?: Snippet;
29
+ }
30
+
31
+ // Default ExonPro theme
32
+ const defaultTheme: ThemeVariables = {
33
+ primaryColor: '#4654A3',
34
+ primaryTextColor: '#270949',
35
+ primaryBorderColor: '#4654A3',
36
+ lineColor: '#270949',
37
+ secondaryColor: '#FF3131',
38
+ tertiaryColor: '#f3f4f6'
39
+ };
40
+
41
+ let { code, theme = defaultTheme, class: className = '', children }: Props = $props();
42
+
43
+ let containerRef: HTMLDivElement | undefined = $state();
44
+ let slotRef: HTMLDivElement | undefined = $state();
45
+ let isLoading = $state(true);
46
+ let hasError = $state(false);
47
+ let errorMessage = $state('');
48
+ let renderedSvg = $state('');
49
+
50
+ // Get diagram code from prop or slot
51
+ function getDiagramCode(): string {
52
+ if (code) return code;
53
+ if (slotRef) {
54
+ return slotRef.textContent?.trim() || '';
55
+ }
56
+ return '';
57
+ }
58
+
59
+ onMount(async () => {
60
+ try {
61
+ // Dynamically import mermaid (type assertion for dynamic import)
62
+ const mermaidModule = await import('mermaid');
63
+ const mermaid = mermaidModule.default;
64
+
65
+ // Initialize mermaid with theme
66
+ mermaid.initialize({
67
+ startOnLoad: false,
68
+ theme: 'base',
69
+ themeVariables: { ...defaultTheme, ...theme },
70
+ securityLevel: 'loose',
71
+ fontFamily: 'inherit'
72
+ });
73
+
74
+ const diagramCode = getDiagramCode();
75
+ if (!diagramCode) {
76
+ hasError = true;
77
+ errorMessage = 'No diagram code provided';
78
+ isLoading = false;
79
+ return;
80
+ }
81
+
82
+ // Generate unique ID
83
+ const id = `mermaid-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
84
+
85
+ // Render the diagram
86
+ const { svg } = await mermaid.render(id, diagramCode);
87
+ renderedSvg = svg;
88
+ } catch (error) {
89
+ hasError = true;
90
+ errorMessage = error instanceof Error ? error.message : 'Failed to render diagram';
91
+ } finally {
92
+ isLoading = false;
93
+ }
94
+ });
95
+ </script>
96
+
97
+ <div class="mermaid-container {className}" bind:this={containerRef}>
98
+ <!-- Hidden slot for extracting diagram code -->
99
+ <div bind:this={slotRef} style="display: none;">
100
+ {#if children}
101
+ {@render children()}
102
+ {/if}
103
+ </div>
104
+
105
+ {#if isLoading}
106
+ <div class="mermaid-loading">
107
+ <div class="mermaid-spinner"></div>
108
+ <span>Rendering diagram...</span>
109
+ </div>
110
+ {:else if hasError}
111
+ <div class="mermaid-error" role="alert">
112
+ <svg
113
+ xmlns="http://www.w3.org/2000/svg"
114
+ width="24"
115
+ height="24"
116
+ viewBox="0 0 24 24"
117
+ fill="none"
118
+ stroke="currentColor"
119
+ stroke-width="2"
120
+ >
121
+ <circle cx="12" cy="12" r="10" />
122
+ <line x1="12" y1="8" x2="12" y2="12" />
123
+ <line x1="12" y1="16" x2="12.01" y2="16" />
124
+ </svg>
125
+ <div>
126
+ <strong>Failed to render diagram</strong>
127
+ <p>{errorMessage}</p>
128
+ </div>
129
+ </div>
130
+ {:else}
131
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
132
+ <div class="mermaid-diagram">{@html renderedSvg}</div>
133
+ {/if}
134
+ </div>
135
+
136
+ <style>
137
+ .mermaid-container {
138
+ width: 100%;
139
+ font-family: inherit;
140
+ }
141
+
142
+ /* Loading */
143
+ .mermaid-loading {
144
+ display: flex;
145
+ flex-direction: column;
146
+ align-items: center;
147
+ justify-content: center;
148
+ gap: var(--space-md, 1rem);
149
+ padding: var(--space-xl, 2rem);
150
+ color: var(--color-text-muted, #6b7280);
151
+ font-size: var(--text-sm, 0.875rem);
152
+ }
153
+
154
+ .mermaid-spinner {
155
+ width: 2rem;
156
+ height: 2rem;
157
+ border: 3px solid var(--color-border, #e5e7eb);
158
+ border-top-color: var(--color-primary, #3b82f6);
159
+ border-radius: var(--radius-full, 9999px);
160
+ animation: spin 0.75s linear infinite;
161
+ }
162
+
163
+ @keyframes spin {
164
+ to {
165
+ transform: rotate(360deg);
166
+ }
167
+ }
168
+
169
+ /* Error */
170
+ .mermaid-error {
171
+ display: flex;
172
+ align-items: flex-start;
173
+ gap: var(--space-md, 1rem);
174
+ padding: var(--space-md, 1rem);
175
+ background: var(--color-error-bg, #fee2e2);
176
+ color: var(--color-error, #dc2626);
177
+ border-radius: var(--radius-md, 0.375rem);
178
+ font-size: var(--text-sm, 0.875rem);
179
+ }
180
+
181
+ .mermaid-error svg {
182
+ flex-shrink: 0;
183
+ margin-top: 2px;
184
+ }
185
+
186
+ .mermaid-error strong {
187
+ display: block;
188
+ font-weight: var(--font-semibold, 600);
189
+ }
190
+
191
+ .mermaid-error p {
192
+ margin: var(--space-xs, 0.25rem) 0 0;
193
+ opacity: 0.9;
194
+ }
195
+
196
+ /* Diagram */
197
+ .mermaid-diagram {
198
+ width: 100%;
199
+ overflow-x: auto;
200
+ }
201
+
202
+ .mermaid-diagram :global(svg) {
203
+ max-width: 100%;
204
+ height: auto;
205
+ }
206
+ </style>
@@ -0,0 +1,28 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface ThemeVariables {
3
+ primaryColor?: string;
4
+ primaryTextColor?: string;
5
+ primaryBorderColor?: string;
6
+ lineColor?: string;
7
+ secondaryColor?: string;
8
+ tertiaryColor?: string;
9
+ background?: string;
10
+ mainBkg?: string;
11
+ nodeBorder?: string;
12
+ clusterBkg?: string;
13
+ titleColor?: string;
14
+ edgeLabelBackground?: string;
15
+ }
16
+ interface Props {
17
+ /** Mermaid diagram code. If not provided, uses slot content */
18
+ code?: string;
19
+ /** Custom theme variables */
20
+ theme?: ThemeVariables;
21
+ /** Custom class */
22
+ class?: string;
23
+ /** Slot content for diagram code */
24
+ children?: Snippet;
25
+ }
26
+ declare const Mermaid: import("svelte").Component<Props, {}, "">;
27
+ type Mermaid = ReturnType<typeof Mermaid>;
28
+ export default Mermaid;
@@ -0,0 +1 @@
1
+ export { default as Mermaid } from './Mermaid.svelte';
@@ -0,0 +1 @@
1
+ export { default as Mermaid } from './Mermaid.svelte';
@@ -0,0 +1,21 @@
1
+ // Type declarations for mermaid
2
+ declare module 'mermaid' {
3
+ interface MermaidConfig {
4
+ startOnLoad?: boolean;
5
+ theme?: string;
6
+ themeVariables?: Record<string, string | undefined>;
7
+ securityLevel?: string;
8
+ fontFamily?: string;
9
+ }
10
+
11
+ interface RenderResult {
12
+ svg: string;
13
+ }
14
+
15
+ const mermaid: {
16
+ initialize: (config: MermaidConfig) => void;
17
+ render: (id: string, code: string) => Promise<RenderResult>;
18
+ };
19
+
20
+ export default mermaid;
21
+ }
@@ -0,0 +1,293 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ value?: number | null;
4
+ min?: number;
5
+ max?: number;
6
+ step?: number;
7
+ disabled?: boolean;
8
+ placeholder?: string;
9
+ name?: string;
10
+ id?: string;
11
+ error?: boolean;
12
+ class?: string;
13
+ onValueChange?: (value: number | null) => void;
14
+ }
15
+
16
+ let {
17
+ value = $bindable(null),
18
+ min,
19
+ max,
20
+ step = 1,
21
+ disabled = false,
22
+ placeholder,
23
+ name,
24
+ id,
25
+ error = false,
26
+ class: className = '',
27
+ onValueChange
28
+ }: Props = $props();
29
+
30
+ let inputValue = $state(value?.toString() ?? '');
31
+
32
+ // Sync internal state with external value prop
33
+ $effect(() => {
34
+ inputValue = value?.toString() ?? '';
35
+ });
36
+
37
+ const isDecrementDisabled = $derived(
38
+ disabled || (min !== undefined && value !== null && value <= min)
39
+ );
40
+ const isIncrementDisabled = $derived(
41
+ disabled || (max !== undefined && value !== null && value >= max)
42
+ );
43
+
44
+ function handleIncrement() {
45
+ if (isIncrementDisabled) return;
46
+
47
+ const currentVal = value ?? 0;
48
+ const newValue = currentVal + step;
49
+ const clampedValue = max !== undefined ? Math.min(newValue, max) : newValue;
50
+
51
+ value = clampedValue;
52
+ onValueChange?.(clampedValue);
53
+ }
54
+
55
+ function handleDecrement() {
56
+ if (isDecrementDisabled) return;
57
+
58
+ const currentVal = value ?? 0;
59
+ const newValue = currentVal - step;
60
+ const clampedValue = min !== undefined ? Math.max(newValue, min) : newValue;
61
+
62
+ value = clampedValue;
63
+ onValueChange?.(clampedValue);
64
+ }
65
+
66
+ function handleInput(e: Event & { currentTarget: HTMLInputElement }) {
67
+ inputValue = e.currentTarget.value;
68
+
69
+ if (inputValue === '') {
70
+ value = null;
71
+ onValueChange?.(null);
72
+ return;
73
+ }
74
+
75
+ const parsed = parseFloat(inputValue);
76
+ if (!isNaN(parsed)) {
77
+ value = parsed;
78
+ onValueChange?.(parsed);
79
+ }
80
+ }
81
+
82
+ function handleBlur() {
83
+ if (value === null) {
84
+ inputValue = '';
85
+ return;
86
+ }
87
+
88
+ // Clamp value to min/max on blur
89
+ let clampedValue = value;
90
+ if (min !== undefined && clampedValue < min) {
91
+ clampedValue = min;
92
+ }
93
+ if (max !== undefined && clampedValue > max) {
94
+ clampedValue = max;
95
+ }
96
+
97
+ if (clampedValue !== value) {
98
+ value = clampedValue;
99
+ onValueChange?.(clampedValue);
100
+ }
101
+
102
+ inputValue = clampedValue.toString();
103
+ }
104
+
105
+ function handleKeyDown(e: KeyboardEvent) {
106
+ if (e.key === 'ArrowUp') {
107
+ e.preventDefault();
108
+ handleIncrement();
109
+ } else if (e.key === 'ArrowDown') {
110
+ e.preventDefault();
111
+ handleDecrement();
112
+ }
113
+ }
114
+ </script>
115
+
116
+ <div
117
+ class="number-input {error ? 'number-input--error' : ''} {disabled
118
+ ? 'number-input--disabled'
119
+ : ''} {className}"
120
+ >
121
+ <button
122
+ type="button"
123
+ class="number-input__button number-input__button--decrement"
124
+ disabled={isDecrementDisabled}
125
+ onclick={handleDecrement}
126
+ aria-label="Decrement value"
127
+ >
128
+ <svg
129
+ xmlns="http://www.w3.org/2000/svg"
130
+ width="16"
131
+ height="16"
132
+ viewBox="0 0 24 24"
133
+ fill="none"
134
+ stroke="currentColor"
135
+ stroke-width="2"
136
+ stroke-linecap="round"
137
+ stroke-linejoin="round"
138
+ >
139
+ <line x1="5" y1="12" x2="19" y2="12"></line>
140
+ </svg>
141
+ </button>
142
+
143
+ <input
144
+ type="text"
145
+ inputmode="decimal"
146
+ class="number-input__field"
147
+ role="spinbutton"
148
+ aria-valuemin={min}
149
+ aria-valuemax={max}
150
+ aria-valuenow={value ?? undefined}
151
+ {name}
152
+ {id}
153
+ {placeholder}
154
+ {disabled}
155
+ aria-invalid={error}
156
+ value={inputValue}
157
+ oninput={handleInput}
158
+ onblur={handleBlur}
159
+ onkeydown={handleKeyDown}
160
+ />
161
+
162
+ <button
163
+ type="button"
164
+ class="number-input__button number-input__button--increment"
165
+ disabled={isIncrementDisabled}
166
+ onclick={handleIncrement}
167
+ aria-label="Increment value"
168
+ >
169
+ <svg
170
+ xmlns="http://www.w3.org/2000/svg"
171
+ width="16"
172
+ height="16"
173
+ viewBox="0 0 24 24"
174
+ fill="none"
175
+ stroke="currentColor"
176
+ stroke-width="2"
177
+ stroke-linecap="round"
178
+ stroke-linejoin="round"
179
+ >
180
+ <line x1="12" y1="5" x2="12" y2="19"></line>
181
+ <line x1="5" y1="12" x2="19" y2="12"></line>
182
+ </svg>
183
+ </button>
184
+ </div>
185
+
186
+ <style>
187
+ .number-input {
188
+ display: flex;
189
+ align-items: stretch;
190
+ width: 100%;
191
+ gap: 0;
192
+ }
193
+
194
+ .number-input--disabled {
195
+ opacity: 0.5;
196
+ }
197
+
198
+ .number-input__button {
199
+ display: flex;
200
+ align-items: center;
201
+ justify-content: center;
202
+ min-width: var(--touch-target-min, 44px);
203
+ min-height: var(--touch-target-min, 44px);
204
+ padding: 0;
205
+ border: 1px solid var(--color-border, #e5e7eb);
206
+ background: var(--color-bg, #ffffff);
207
+ color: var(--color-text, #1f2937);
208
+ font-family: inherit;
209
+ cursor: pointer;
210
+ transition: all var(--transition-fast, 150ms ease);
211
+ -webkit-tap-highlight-color: transparent;
212
+ }
213
+
214
+ .number-input__button:hover:not(:disabled) {
215
+ background: var(--color-bg-muted, #f3f4f6);
216
+ border-color: var(--color-border-hover, #d1d5db);
217
+ }
218
+
219
+ .number-input__button:active:not(:disabled) {
220
+ background: var(--color-border, #e5e7eb);
221
+ }
222
+
223
+ .number-input__button:focus-visible {
224
+ outline: 2px solid var(--color-primary, #3b82f6);
225
+ outline-offset: 2px;
226
+ z-index: 1;
227
+ }
228
+
229
+ .number-input__button:disabled {
230
+ opacity: 0.3;
231
+ cursor: not-allowed;
232
+ pointer-events: none;
233
+ }
234
+
235
+ .number-input__button--decrement {
236
+ border-top-left-radius: var(--radius-md, 0.375rem);
237
+ border-bottom-left-radius: var(--radius-md, 0.375rem);
238
+ border-right: none;
239
+ }
240
+
241
+ .number-input__button--increment {
242
+ border-top-right-radius: var(--radius-md, 0.375rem);
243
+ border-bottom-right-radius: var(--radius-md, 0.375rem);
244
+ border-left: none;
245
+ }
246
+
247
+ .number-input__field {
248
+ flex: 1;
249
+ min-width: 0;
250
+ min-height: var(--touch-target-min, 44px);
251
+ padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
252
+ border: 1px solid var(--color-border, #e5e7eb);
253
+ border-left: none;
254
+ border-right: none;
255
+ background: var(--color-bg, #ffffff);
256
+ color: var(--color-text, #1f2937);
257
+ font-family: inherit;
258
+ font-size: var(--text-sm, 0.875rem);
259
+ line-height: 1.5;
260
+ text-align: center;
261
+ transition:
262
+ border-color var(--transition-fast, 150ms ease),
263
+ box-shadow var(--transition-fast, 150ms ease);
264
+ -webkit-tap-highlight-color: transparent;
265
+ }
266
+
267
+ .number-input__field::placeholder {
268
+ color: var(--color-text-muted, #9ca3af);
269
+ }
270
+
271
+ .number-input__field:focus {
272
+ outline: none;
273
+ border-color: var(--color-primary, #3b82f6);
274
+ box-shadow: 0 0 0 3px var(--color-primary-alpha, rgba(59, 130, 246, 0.1));
275
+ z-index: 1;
276
+ }
277
+
278
+ .number-input__field:disabled {
279
+ background: var(--color-bg-muted, #f3f4f6);
280
+ cursor: not-allowed;
281
+ }
282
+
283
+ /* Error state */
284
+ .number-input--error .number-input__field,
285
+ .number-input--error .number-input__button {
286
+ border-color: var(--color-error, #ef4444);
287
+ }
288
+
289
+ .number-input--error .number-input__field:focus {
290
+ border-color: var(--color-error, #ef4444);
291
+ box-shadow: 0 0 0 3px var(--color-error-alpha, rgba(239, 68, 68, 0.1));
292
+ }
293
+ </style>
@@ -0,0 +1,16 @@
1
+ interface Props {
2
+ value?: number | null;
3
+ min?: number;
4
+ max?: number;
5
+ step?: number;
6
+ disabled?: boolean;
7
+ placeholder?: string;
8
+ name?: string;
9
+ id?: string;
10
+ error?: boolean;
11
+ class?: string;
12
+ onValueChange?: (value: number | null) => void;
13
+ }
14
+ declare const NumberInput: import("svelte").Component<Props, {}, "value">;
15
+ type NumberInput = ReturnType<typeof NumberInput>;
16
+ export default NumberInput;
@@ -0,0 +1 @@
1
+ export { default as NumberInput } from './NumberInput.svelte';
@@ -0,0 +1 @@
1
+ export { default as NumberInput } from './NumberInput.svelte';