@softwareone/spi-sv5-library 1.7.10 → 1.8.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.
@@ -4,12 +4,16 @@
4
4
  interface AccordionProps {
5
5
  title: string;
6
6
  disableBorder?: boolean;
7
- content: Snippet;
7
+ isOpen?: boolean;
8
+ children: Snippet;
8
9
  }
9
10
 
10
- let { title, disableBorder = false, content }: AccordionProps = $props();
11
-
12
- let isOpen = $state(false);
11
+ let {
12
+ title,
13
+ disableBorder = false,
14
+ isOpen = $bindable(false),
15
+ children
16
+ }: AccordionProps = $props();
13
17
 
14
18
  const toggleAccordion = () => {
15
19
  isOpen = !isOpen;
@@ -19,9 +23,13 @@
19
23
  <aside class="accordion-container" class:border={!disableBorder}>
20
24
  <div class="accordion-content">
21
25
  <section class="header" class:is-open={isOpen}>
22
- <button class="button" onclick={toggleAccordion}>
23
- <span class="material-icons-outlined icon-span">
24
- {isOpen ? 'keyboard_arrow_down' : 'keyboard_arrow_right'}
26
+ <button
27
+ class="button"
28
+ onclick={toggleAccordion}
29
+ aria-label={isOpen ? 'Collapse accordion' : 'Expand accordion'}
30
+ >
31
+ <span class="material-icons-outlined icon-span" class:rotate={isOpen}>
32
+ keyboard_arrow_right
25
33
  </span>
26
34
  </button>
27
35
 
@@ -29,7 +37,9 @@
29
37
  </section>
30
38
 
31
39
  {#if isOpen}
32
- {@render content()}
40
+ <div class="content">
41
+ {@render children()}
42
+ </div>
33
43
  {/if}
34
44
  </div>
35
45
  </aside>
@@ -38,22 +48,20 @@
38
48
  .accordion-container {
39
49
  width: 100%;
40
50
  height: 100%;
41
- padding: 32px;
42
- display: flex;
43
51
 
44
52
  --border-color: #e0e5e8;
45
53
  --color: #472aff;
46
54
  }
47
55
 
48
56
  .accordion-content {
49
- flex-direction: column;
50
- flex: 1 1 0;
51
- gap: 4px;
52
- display: flex;
53
57
  font-size: 14px;
54
58
  line-height: 20px;
55
59
  }
56
60
 
61
+ .content {
62
+ padding: 10px 10px 0 28px;
63
+ }
64
+
57
65
  .is-open {
58
66
  padding-bottom: 10px;
59
67
  }
@@ -70,6 +78,7 @@
70
78
  .border {
71
79
  border: 1px solid var(--border-color);
72
80
  border-radius: 8px;
81
+ padding: 32px;
73
82
  }
74
83
 
75
84
  .button {
@@ -85,5 +94,10 @@
85
94
  .icon-span {
86
95
  color: var(--color);
87
96
  font-size: 20px;
97
+ transition: transform 0.2s ease-in-out;
98
+ }
99
+
100
+ .icon-span.rotate {
101
+ transform: rotate(90deg);
88
102
  }
89
103
  </style>
@@ -2,8 +2,9 @@ import type { Snippet } from 'svelte';
2
2
  interface AccordionProps {
3
3
  title: string;
4
4
  disableBorder?: boolean;
5
- content: Snippet;
5
+ isOpen?: boolean;
6
+ children: Snippet;
6
7
  }
7
- declare const Accordion: import("svelte").Component<AccordionProps, {}, "">;
8
+ declare const Accordion: import("svelte").Component<AccordionProps, {}, "isOpen">;
8
9
  type Accordion = ReturnType<typeof Accordion>;
9
10
  export default Accordion;
@@ -0,0 +1,210 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+
4
+ import ChatBotFooter from './ChatBotFooter.svelte';
5
+ import ChatBotHeader from './ChatBotHeader.svelte';
6
+ import ChatBotMessages from './ChatBotMessages.svelte';
7
+ import {
8
+ type ChatBotMessage,
9
+ type Chat,
10
+ type ChatMessage,
11
+ type ChatSessionStart,
12
+ type UserMessage,
13
+ type ChatBotReplyMessage
14
+ } from './chatbotState.svelte';
15
+
16
+ interface Props {
17
+ sendmessage: (chat: Chat) => Promise<ChatBotReplyMessage | undefined>;
18
+ title?: string;
19
+ }
20
+
21
+ let { sendmessage, title = 'Chat Bot' }: Props = $props();
22
+
23
+ const CHATBOT_MESSAGES_STORAGE_KEY = 'chatbot-messages';
24
+
25
+ let isOpen = $state(false);
26
+ let isLoading = $state(false);
27
+ let messages = $state<ChatBotMessage[]>([]);
28
+
29
+ const initializeChatBot = async () => {
30
+ const chat: ChatSessionStart = { type: 'session_init' };
31
+ await handleMessage(chat);
32
+ };
33
+
34
+ const onsend = async (text: string) => {
35
+ const chat: ChatMessage = { type: 'text', text };
36
+ const userMessage: UserMessage = {
37
+ type: 'user',
38
+ content: text
39
+ };
40
+ setMessages(userMessage);
41
+ await handleMessage(chat);
42
+ };
43
+
44
+ const handleMessage = async (chat: Chat) => {
45
+ isLoading = true;
46
+ const chatBotReplyMessage = await sendmessage(chat);
47
+ if (chatBotReplyMessage) setMessages(chatBotReplyMessage);
48
+ isLoading = false;
49
+ };
50
+
51
+ const setMessages = (message: ChatBotMessage) => {
52
+ messages = [...messages, message];
53
+ storeChatMessages(messages);
54
+ };
55
+
56
+ const handleClose = () => {
57
+ toggleModal();
58
+ messages = [];
59
+ clearChatMessages();
60
+ };
61
+
62
+ const toggleModal = () => {
63
+ isOpen = !isOpen;
64
+ };
65
+
66
+ function fetchChatMessages() {
67
+ const storedMessages = localStorage.getItem(CHATBOT_MESSAGES_STORAGE_KEY);
68
+ const messages: ChatBotMessage[] = storedMessages ? JSON.parse(storedMessages) : [];
69
+ return messages;
70
+ }
71
+
72
+ const storeChatMessages = (messages: ChatBotMessage[]) => {
73
+ localStorage.setItem(CHATBOT_MESSAGES_STORAGE_KEY, JSON.stringify(messages));
74
+ };
75
+
76
+ const clearChatMessages = () => {
77
+ localStorage.removeItem(CHATBOT_MESSAGES_STORAGE_KEY);
78
+ };
79
+
80
+ onMount(() => {
81
+ messages = fetchChatMessages();
82
+ });
83
+
84
+ $effect(() => {
85
+ if (isOpen && messages.length === 0) {
86
+ initializeChatBot();
87
+ }
88
+ });
89
+ </script>
90
+
91
+ <div class="container">
92
+ {#if isOpen}
93
+ <div class="chatbot-container">
94
+ <ChatBotHeader {title} onclose={handleClose} onminimize={toggleModal} />
95
+ <div class="chatbot-body">
96
+ <ChatBotMessages {messages} {isLoading} oncardaction={handleMessage} />
97
+ </div>
98
+ <ChatBotFooter {onsend} {isLoading} />
99
+ </div>
100
+ {:else}
101
+ <button class="chatbot-toggle" onclick={toggleModal} aria-label="Open chat">
102
+ <span class="material-icons">chat_bubble_outline</span>
103
+ </button>
104
+ {/if}
105
+ </div>
106
+
107
+ <style>
108
+ .container {
109
+ --chatbot-primary: #472aff;
110
+ --chatbot-primary-hover: #3520bf;
111
+ --chatbot-bg: #ffffff;
112
+ --chatbot-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
113
+ --chatbot-radius: 16px;
114
+ --chatbot-transition: all 0.3s ease;
115
+ }
116
+
117
+ .chatbot-container {
118
+ position: fixed;
119
+ bottom: 24px;
120
+ right: 24px;
121
+ width: 400px;
122
+ height: 600px;
123
+ display: flex;
124
+ flex-direction: column;
125
+ border-radius: var(--chatbot-radius);
126
+ box-shadow: var(--chatbot-shadow);
127
+ background: var(--chatbot-bg);
128
+ animation: slideUp 0.3s ease;
129
+ transition: var(--chatbot-transition);
130
+ z-index: 1000;
131
+ }
132
+
133
+ .chatbot-body {
134
+ flex: 1;
135
+ overflow-y: auto;
136
+ padding: 10px;
137
+ background-color: #f9fafb;
138
+ }
139
+
140
+ .chatbot-toggle {
141
+ position: fixed;
142
+ bottom: 24px;
143
+ right: 24px;
144
+ width: 60px;
145
+ height: 60px;
146
+ border-radius: 50%;
147
+ background: var(--chatbot-primary);
148
+ color: var(--chatbot-bg);
149
+ border: none;
150
+ cursor: pointer;
151
+ display: flex;
152
+ align-items: center;
153
+ justify-content: center;
154
+ box-shadow: var(--chatbot-shadow);
155
+ transition: var(--chatbot-transition);
156
+ z-index: 999;
157
+ animation: fadeIn 0.3s ease;
158
+ }
159
+
160
+ .chatbot-toggle:hover {
161
+ background: var(--chatbot-primary-hover);
162
+ transform: scale(1.1);
163
+ box-shadow: 0 6px 24px rgba(0, 0, 0, 0.2);
164
+ }
165
+
166
+ .chatbot-toggle:active {
167
+ transform: scale(0.95);
168
+ }
169
+
170
+ .chatbot-toggle .material-icons {
171
+ font-size: 28px;
172
+ }
173
+
174
+ @media (max-width: 480px) {
175
+ .chatbot-container {
176
+ width: 100vw;
177
+ height: 100vh;
178
+ bottom: 0;
179
+ right: 0;
180
+ border-radius: 0;
181
+ }
182
+
183
+ .chatbot-toggle {
184
+ bottom: 16px;
185
+ right: 16px;
186
+ }
187
+ }
188
+
189
+ @keyframes slideUp {
190
+ from {
191
+ transform: translateY(20px);
192
+ opacity: 0;
193
+ }
194
+ to {
195
+ transform: translateY(0);
196
+ opacity: 1;
197
+ }
198
+ }
199
+
200
+ @keyframes fadeIn {
201
+ from {
202
+ opacity: 0;
203
+ transform: scale(0.8);
204
+ }
205
+ to {
206
+ opacity: 1;
207
+ transform: scale(1);
208
+ }
209
+ }
210
+ </style>
@@ -0,0 +1,8 @@
1
+ import { type Chat, type ChatBotReplyMessage } from './chatbotState.svelte';
2
+ interface Props {
3
+ sendmessage: (chat: Chat) => Promise<ChatBotReplyMessage | undefined>;
4
+ title?: string;
5
+ }
6
+ declare const ChatBot: import("svelte").Component<Props, {}, "">;
7
+ type ChatBot = ReturnType<typeof ChatBot>;
8
+ export default ChatBot;
@@ -0,0 +1,53 @@
1
+ <script lang="ts">
2
+ import { Input, Button } from '../index.js';
3
+
4
+ interface Props {
5
+ onsend: (text: string) => Promise<void>;
6
+ isLoading: boolean;
7
+ }
8
+
9
+ let { onsend, isLoading }: Props = $props();
10
+
11
+ let message = $state('');
12
+
13
+ const handleSend = async () => {
14
+ await onsend(message);
15
+ message = '';
16
+ };
17
+
18
+ const handleKeyPress = (event: KeyboardEvent) => {
19
+ if (event.key === 'Enter' && !event.shiftKey) {
20
+ event.preventDefault();
21
+ handleSend();
22
+ }
23
+ };
24
+ </script>
25
+
26
+ <div class="chatbot-input">
27
+ <div class="input">
28
+ <Input
29
+ placeholder="Type a message"
30
+ bind:value={message}
31
+ disableValidationColor
32
+ onkeydown={handleKeyPress}
33
+ />
34
+ </div>
35
+ <Button
36
+ variant="primary"
37
+ variantColor="primary"
38
+ disabled={!message || isLoading}
39
+ onclick={handleSend}>Send</Button
40
+ >
41
+ </div>
42
+
43
+ <style>
44
+ .input {
45
+ width: 100%;
46
+ }
47
+
48
+ .chatbot-input {
49
+ display: flex;
50
+ gap: 10px;
51
+ padding: 10px 14px;
52
+ }
53
+ </style>
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ onsend: (text: string) => Promise<void>;
3
+ isLoading: boolean;
4
+ }
5
+ declare const ChatBotFooter: import("svelte").Component<Props, {}, "">;
6
+ type ChatBotFooter = ReturnType<typeof ChatBotFooter>;
7
+ export default ChatBotFooter;
@@ -0,0 +1,66 @@
1
+ <script lang="ts">
2
+ type Props = {
3
+ title: string;
4
+ onclose: VoidFunction;
5
+ onminimize: VoidFunction;
6
+ };
7
+ let { title, onclose, onminimize }: Props = $props();
8
+ </script>
9
+
10
+ <div class="chatbot-header">
11
+ <h3 class="chatbot-title">{title}</h3>
12
+ <div class="header-actions">
13
+ <button type="button" class="icon-btn" onclick={onminimize} aria-label="Minimize chat">
14
+ <span class="material-icons">remove</span>
15
+ </button>
16
+ <button type="button" class="icon-btn" onclick={onclose} aria-label="Close chat">
17
+ <span class="material-icons">close</span>
18
+ </button>
19
+ </div>
20
+ </div>
21
+
22
+ <style>
23
+ .chatbot-header {
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: space-between;
27
+ padding: 12px 16px;
28
+ background: #472aff;
29
+ color: #fff;
30
+ border-top-left-radius: 16px;
31
+ border-top-right-radius: 16px;
32
+ }
33
+
34
+ .chatbot-title {
35
+ margin: 0;
36
+ font-size: 15px;
37
+ font-weight: 600;
38
+ }
39
+
40
+ .header-actions {
41
+ display: flex;
42
+ gap: 4px;
43
+ align-items: center;
44
+ }
45
+
46
+ .icon-btn {
47
+ background: transparent;
48
+ border: none;
49
+ color: #fff;
50
+ cursor: pointer;
51
+ padding: 4px;
52
+ border-radius: 6px;
53
+ display: flex;
54
+ align-items: center;
55
+ justify-content: center;
56
+ transition: background-color 0.2s ease;
57
+ }
58
+
59
+ .icon-btn:hover {
60
+ background-color: rgba(255, 255, 255, 0.2);
61
+ }
62
+
63
+ .icon-btn .material-icons {
64
+ font-size: 18px;
65
+ }
66
+ </style>
@@ -0,0 +1,8 @@
1
+ type Props = {
2
+ title: string;
3
+ onclose: VoidFunction;
4
+ onminimize: VoidFunction;
5
+ };
6
+ declare const ChatBotHeader: import("svelte").Component<Props, {}, "">;
7
+ type ChatBotHeader = ReturnType<typeof ChatBotHeader>;
8
+ export default ChatBotHeader;
@@ -0,0 +1,278 @@
1
+ <script lang="ts">
2
+ import type { Attachment } from 'svelte/attachments';
3
+
4
+ import {
5
+ Action,
6
+ AdaptiveCard,
7
+ HostConfig,
8
+ SubmitAction,
9
+ type IAdaptiveCard
10
+ } from 'adaptivecards';
11
+
12
+ import type { Chat, ChatBotMessage } from './chatbotState.svelte';
13
+
14
+ interface Props {
15
+ messages: ChatBotMessage[];
16
+ isLoading: boolean;
17
+ oncardaction: (chat: Chat) => void;
18
+ }
19
+
20
+ let { messages, isLoading, oncardaction }: Props = $props();
21
+
22
+ const renderAdaptiveCard = (content: IAdaptiveCard): Attachment<HTMLElement> => {
23
+ return (element) => {
24
+ const adaptiveCard = new AdaptiveCard();
25
+
26
+ adaptiveCard.hostConfig = createHostConfig();
27
+ adaptiveCard.onExecuteAction = handleActionExecution;
28
+ adaptiveCard.parse(content);
29
+
30
+ const rendered = adaptiveCard.render();
31
+ if (rendered) {
32
+ rendered.classList.add('adaptive-card');
33
+ element.appendChild(rendered);
34
+ }
35
+ };
36
+ };
37
+
38
+ const createHostConfig = () => {
39
+ return new HostConfig({
40
+ fontFamily: 'Poppins, sans-serif'
41
+ });
42
+ };
43
+
44
+ const handleActionExecution = (action: Action) => {
45
+ if (action instanceof SubmitAction) {
46
+ const chat = action.data as Chat;
47
+ oncardaction(chat);
48
+ }
49
+ };
50
+
51
+ const scrollIntoView: Attachment<HTMLDivElement> = (element: HTMLDivElement) => {
52
+ isLoading;
53
+ messages.length;
54
+ element.scrollIntoView({ behavior: 'smooth', block: 'end' });
55
+ };
56
+ </script>
57
+
58
+ <div {@attach scrollIntoView} class="messages">
59
+ {#each messages as message}
60
+ {@const isBot = message.type === 'bot'}
61
+ {@const isUser = message.type === 'user'}
62
+
63
+ <div class={['message', isBot && 'bot', isUser && 'user']}>
64
+ {#if isBot && typeof message.content !== 'string'}
65
+ <div {@attach renderAdaptiveCard(message.content)}></div>
66
+ {:else}
67
+ <p class="message-text">{message.content}</p>
68
+ {/if}
69
+ </div>
70
+ {/each}
71
+
72
+ {#if isLoading}
73
+ <div class="loading">
74
+ {#each Array(3) as _}
75
+ <span class="loading-dot"></span>
76
+ {/each}
77
+ </div>
78
+ {/if}
79
+ </div>
80
+
81
+ <style>
82
+ .messages {
83
+ --color-bg-messages: #f9fafb;
84
+ --color-bg-bot: #f8f8ff;
85
+ --color-bg-user: #472aff;
86
+ --color-text-primary: #1a1a1a;
87
+ --color-text-inverse: #ffffff;
88
+ --color-border-bot: #543bfa;
89
+ --color-scrollbar: #b5b6ff;
90
+ --color-scrollbar-hover: #8a8bff;
91
+ --color-button-primary: #472aff;
92
+ --color-button-hover: #3520bf;
93
+ --color-button-active: #2a1894;
94
+ --radius-sm: 8px;
95
+ --radius-md: 12px;
96
+ --spacing-xs: 6px;
97
+ --spacing-sm: 10px;
98
+ --spacing-md: 14px;
99
+ --spacing-lg: 16px;
100
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06);
101
+ --shadow-md: 0 2px 10px rgba(0, 0, 0, 0.06);
102
+ --shadow-hover: 0 4px 10px rgba(0, 0, 0, 0.08);
103
+
104
+ padding: var(--spacing-lg);
105
+ display: flex;
106
+ flex-direction: column;
107
+ gap: var(--spacing-md);
108
+ background: var(--color-bg-messages);
109
+ border-radius: var(--radius-md);
110
+ box-shadow: var(--shadow-md);
111
+ }
112
+
113
+ .message {
114
+ padding: var(--spacing-md);
115
+ border-radius: var(--radius-md);
116
+ max-width: 95%;
117
+ word-break: break-word;
118
+ overflow-wrap: break-word;
119
+ line-height: 1.5;
120
+ animation: fadeIn 0.35s ease forwards;
121
+ transition: all 0.25s ease;
122
+ }
123
+
124
+ .message.user {
125
+ align-self: flex-end;
126
+ background-color: var(--color-bg-user);
127
+ color: var(--color-text-inverse);
128
+ border-radius: var(--radius-md) var(--radius-md) 0 var(--radius-md);
129
+ max-width: 80%;
130
+ box-shadow: var(--shadow-sm);
131
+ }
132
+
133
+ .message.bot {
134
+ align-self: flex-start;
135
+ background-color: var(--color-bg-bot);
136
+ color: var(--color-text-primary);
137
+ border-radius: var(--radius-md) var(--radius-md) var(--radius-md) 0;
138
+ box-shadow: var(--shadow-sm);
139
+ border-left: 4px solid var(--color-border-bot);
140
+ }
141
+
142
+ .message-text {
143
+ margin: 0;
144
+ line-height: 1.4;
145
+ }
146
+
147
+ .loading {
148
+ display: flex;
149
+ gap: var(--spacing-xs);
150
+ padding: var(--spacing-sm);
151
+ }
152
+
153
+ .loading-dot {
154
+ width: 8px;
155
+ height: 8px;
156
+ border-radius: 50%;
157
+ background-color: var(--color-border-bot);
158
+ animation: bounce 1.4s infinite ease-in-out both;
159
+ }
160
+
161
+ .loading-dot:nth-child(1) {
162
+ animation-delay: -0.32s;
163
+ }
164
+
165
+ .loading-dot:nth-child(2) {
166
+ animation-delay: -0.16s;
167
+ }
168
+
169
+ :global(.adaptive-card),
170
+ :global(.adaptive-card .ac-container),
171
+ :global(.adaptive-card .ac-columnSet),
172
+ :global(.adaptive-card .ac-column),
173
+ :global(.adaptive-card .ac-actionSet) {
174
+ width: 100%;
175
+ max-width: 100%;
176
+ margin: 0;
177
+ padding: 0;
178
+ box-sizing: border-box;
179
+ }
180
+
181
+ :global(.adaptive-card .ac-textBlock) {
182
+ margin-bottom: var(--spacing-xs);
183
+ line-height: 1.5;
184
+ word-break: break-word;
185
+ }
186
+
187
+ :global(.adaptive-card .ac-pushButton),
188
+ :global(.adaptive-card button),
189
+ :global(.adaptive-card .ac-actionSet .ac-button) {
190
+ all: unset;
191
+ box-sizing: border-box;
192
+ display: inline-block;
193
+ text-align: center;
194
+ padding: 12px 16px;
195
+ border-radius: var(--radius-sm);
196
+ width: 100%;
197
+ cursor: pointer;
198
+ background: var(--color-button-primary);
199
+ color: var(--color-text-inverse);
200
+ font-size: 14px;
201
+ font-weight: 400;
202
+ line-height: 1.4;
203
+ min-height: 44px;
204
+ box-shadow: var(--shadow-sm);
205
+ transition:
206
+ transform 0.2s ease,
207
+ box-shadow 0.2s ease,
208
+ background-color 0.2s ease;
209
+ }
210
+
211
+ :global(.adaptive-card .ac-pushButton:hover),
212
+ :global(.adaptive-card button:hover),
213
+ :global(.adaptive-card .ac-button:hover) {
214
+ background: var(--color-button-hover);
215
+ transform: translateY(-1px);
216
+ box-shadow: var(--shadow-hover);
217
+ }
218
+
219
+ :global(.adaptive-card .ac-pushButton:active),
220
+ :global(.adaptive-card button:active),
221
+ :global(.adaptive-card .ac-button:active) {
222
+ background: var(--color-button-active);
223
+ transform: translateY(0);
224
+ box-shadow: none;
225
+ }
226
+
227
+ :global(.adaptive-card .ac-actionSet) {
228
+ display: flex;
229
+ flex-wrap: wrap;
230
+ justify-content: center;
231
+ gap: var(--spacing-sm);
232
+ }
233
+
234
+ :global(.adaptive-card .ac-actionSet .ac-pushButton),
235
+ :global(.adaptive-card .ac-actionSet button),
236
+ :global(.adaptive-card .ac-actionSet .ac-button) {
237
+ max-width: 280px;
238
+ text-align: center;
239
+ margin: 0 auto;
240
+ }
241
+
242
+ :global(.adaptive-card .ac-actionSet.faq-buttons .ac-pushButton),
243
+ :global(.adaptive-card .ac-actionSet.faq-buttons button),
244
+ :global(.adaptive-card .ac-actionSet.faq-buttons .ac-button) {
245
+ width: 100%;
246
+ max-width: 100%;
247
+ text-align: left;
248
+ }
249
+
250
+ @media (min-width: 640px) {
251
+ :global(.adaptive-card .ac-actionSet) {
252
+ flex-direction: column;
253
+ align-items: stretch;
254
+ }
255
+ }
256
+
257
+ @keyframes fadeIn {
258
+ from {
259
+ opacity: 0;
260
+ transform: translateY(10px);
261
+ }
262
+ to {
263
+ opacity: 1;
264
+ transform: translateY(0);
265
+ }
266
+ }
267
+
268
+ @keyframes bounce {
269
+ 0%,
270
+ 80%,
271
+ 100% {
272
+ transform: scale(0);
273
+ }
274
+ 40% {
275
+ transform: scale(1);
276
+ }
277
+ }
278
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { Chat, ChatBotMessage } from './chatbotState.svelte';
2
+ interface Props {
3
+ messages: ChatBotMessage[];
4
+ isLoading: boolean;
5
+ oncardaction: (chat: Chat) => void;
6
+ }
7
+ declare const ChatBotMessages: import("svelte").Component<Props, {}, "">;
8
+ type ChatBotMessages = ReturnType<typeof ChatBotMessages>;
9
+ export default ChatBotMessages;
@@ -0,0 +1,27 @@
1
+ import type { IAdaptiveCard } from 'adaptivecards';
2
+ export interface UserMessage {
3
+ type: 'user';
4
+ content: string;
5
+ }
6
+ export interface ChatBotReplyMessage {
7
+ type: 'bot';
8
+ content: IAdaptiveCard | string;
9
+ }
10
+ export type ChatBotMessage = UserMessage | ChatBotReplyMessage;
11
+ export interface ChatModuleSelector {
12
+ type: 'module_select';
13
+ module: string;
14
+ }
15
+ export interface ChatFAQSelector {
16
+ type: 'faq_select';
17
+ module: string;
18
+ question: string;
19
+ }
20
+ export interface ChatMessage {
21
+ type: 'text';
22
+ text: string;
23
+ }
24
+ export interface ChatSessionStart {
25
+ type: 'session_init';
26
+ }
27
+ export type Chat = ChatModuleSelector | ChatFAQSelector | ChatSessionStart | ChatMessage;
@@ -0,0 +1 @@
1
+ export {};
@@ -2,9 +2,10 @@
2
2
  interface SpinnerProps {
3
3
  show: boolean;
4
4
  size?: 'sm' | 'md' | 'lg';
5
+ isFixedPosition?: boolean;
5
6
  }
6
7
 
7
- let { show = $bindable(false), size = 'lg' }: SpinnerProps = $props();
8
+ let { show = $bindable(false), size = 'lg', isFixedPosition = false }: SpinnerProps = $props();
8
9
 
9
10
  const pixelSizes: Record<string, number> = {
10
11
  sm: 16,
@@ -14,20 +15,20 @@
14
15
  </script>
15
16
 
16
17
  {#if show}
17
- <div class="spinner-container">
18
- <div class="spinner" style="width: {pixelSizes[size]}px; height: {pixelSizes[size]}px;">
19
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" fill="none">
20
- <path
21
- d="M40 20C40 31.0457 31.0457 40 20 40C8.9543 40 0 31.0457 0 20C0 8.9543 8.9543 0 20 0C31.0457 0 40 8.9543 40 20ZM6.27298 20C6.27298 27.5812 12.4188 33.727 20 33.727C27.5812 33.727 33.727 27.5812 33.727 20C33.727 12.4188 27.5812 6.27298 20 6.27298C12.4188 6.27298 6.27298 12.4188 6.27298 20Z"
22
- fill="#E0E5E8"
23
- />
24
- <path
25
- d="M20 3.13649C20 1.40425 21.4128 -0.0251732 23.1238 0.245376C27.2709 0.901143 31.1358 2.85154 34.1421 5.85786C37.8929 9.60859 40 14.6957 40 20C40 25.3043 37.8929 30.3914 34.1421 34.1421C31.1358 37.1485 27.2709 39.0989 23.1238 39.7546C21.4128 40.0252 20 38.5957 20 36.8635C20 35.1313 21.4222 33.7627 23.1094 33.3703C25.5876 32.7939 27.8783 31.5346 29.7065 29.7065C32.2808 27.1322 33.727 23.6406 33.727 20C33.727 16.3594 32.2808 12.8678 29.7065 10.2935C27.8783 8.46537 25.5876 7.20612 23.1094 6.62973C21.4222 6.23731 20 4.86873 20 3.13649Z"
26
- fill="#472AFF"
27
- />
28
- </svg>
18
+ <div class={['spinner-container', isFixedPosition ? 'fixed' : 'absolute']}>
19
+ <div class="spinner" style="width: {pixelSizes[size]}px; height: {pixelSizes[size]}px;">
20
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" fill="none">
21
+ <path
22
+ d="M40 20C40 31.0457 31.0457 40 20 40C8.9543 40 0 31.0457 0 20C0 8.9543 8.9543 0 20 0C31.0457 0 40 8.9543 40 20ZM6.27298 20C6.27298 27.5812 12.4188 33.727 20 33.727C27.5812 33.727 33.727 27.5812 33.727 20C33.727 12.4188 27.5812 6.27298 20 6.27298C12.4188 6.27298 6.27298 12.4188 6.27298 20Z"
23
+ fill="#E0E5E8"
24
+ />
25
+ <path
26
+ d="M20 3.13649C20 1.40425 21.4128 -0.0251732 23.1238 0.245376C27.2709 0.901143 31.1358 2.85154 34.1421 5.85786C37.8929 9.60859 40 14.6957 40 20C40 25.3043 37.8929 30.3914 34.1421 34.1421C31.1358 37.1485 27.2709 39.0989 23.1238 39.7546C21.4128 40.0252 20 38.5957 20 36.8635C20 35.1313 21.4222 33.7627 23.1094 33.3703C25.5876 32.7939 27.8783 31.5346 29.7065 29.7065C32.2808 27.1322 33.727 23.6406 33.727 20C33.727 16.3594 32.2808 12.8678 29.7065 10.2935C27.8783 8.46537 25.5876 7.20612 23.1094 6.62973C21.4222 6.23731 20 4.86873 20 3.13649Z"
27
+ fill="#472AFF"
28
+ />
29
+ </svg>
30
+ </div>
29
31
  </div>
30
- </div>
31
32
  {/if}
32
33
 
33
34
  <style>
@@ -37,13 +38,20 @@
37
38
  align-items: center;
38
39
  width: 100%;
39
40
  height: 100%;
40
- position: absolute;
41
41
  top: 0;
42
42
  left: 0;
43
43
  background-color: rgba(255, 255, 255, 0.8);
44
44
  z-index: 1000;
45
45
  }
46
46
 
47
+ .fixed {
48
+ position: fixed;
49
+ }
50
+
51
+ .absolute {
52
+ position: absolute;
53
+ }
54
+
47
55
  .spinner {
48
56
  animation: spin 0.8s linear infinite;
49
57
  }
@@ -1,6 +1,7 @@
1
1
  interface SpinnerProps {
2
2
  show: boolean;
3
3
  size?: 'sm' | 'md' | 'lg';
4
+ isFixedPosition?: boolean;
4
5
  }
5
6
  declare const Spinner: import("svelte").Component<SpinnerProps, {}, "show">;
6
7
  type Spinner = ReturnType<typeof Spinner>;
@@ -114,7 +114,7 @@
114
114
  height: 100%;
115
115
  position: relative;
116
116
  border-radius: 0px 0px 16px 16px;
117
- overflow: hidden;
117
+ overflow-wrap: break-word;
118
118
  padding: 24px;
119
119
  box-sizing: border-box;
120
120
  min-height: 300px;
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import Avatar from './Avatar/Avatar.svelte';
3
3
  import Breadcrumbs from './Breadcrumbs/Breadcrumbs.svelte';
4
4
  import Button from './Button/Button.svelte';
5
5
  import Card from './Card/Card.svelte';
6
+ import ChatBot from './ChatBot/ChatBot.svelte';
6
7
  import Chips from './Chips/Chips.svelte';
7
8
  import DeleteConfirmationModal from './DeleteConfirmation/DeleteConfirmation.svelte';
8
9
  import ErrorPage from './ErrorPage/ErrorPage.svelte';
@@ -45,6 +46,7 @@ import { validateSchema } from './Form/FormController/helper.js';
45
46
  import { createZodString } from './Form/FormController/zod-validations.js';
46
47
  import { getSubMenuItemsFromMenu } from './Menu/MenuState.svelte';
47
48
  import type { BreadcrumbsNameMap } from './Breadcrumbs/breadcrumbsState.svelte.js';
49
+ import type { Chat, ChatBotReplyMessage, ChatBotMessage } from './ChatBot/chatbotState.svelte';
48
50
  import type { FormError, FormContext } from './Form/FormController/types.js';
49
51
  import type { HighlightPanelColumn } from './HighlightPanel/highlightPanelState.svelte.js';
50
52
  import type { HomeItem } from './Menu/MenuState.svelte.js';
@@ -58,4 +60,4 @@ import type { Toast } from './Toast/toastState.svelte';
58
60
  import type { WaffleItem } from './Waffle/waffleState.svelte.js';
59
61
  import type { AttachFileFormConfig, FileValidationCallback } from './Form/AttachFile/attachFile.svelte.js';
60
62
  import type { NotificationProps } from './Notification/notificationState.svelte.js';
61
- export { Accordion, Avatar, Breadcrumbs, Button, Card, Chips, DeleteConfirmationModal, ErrorPage, Footer, Form, Header, HeaderAccount, HeaderLoader, HeaderLogo, HighlightPanel, Home, Input, Link, Menu, Modal, Notification, Processing, ProgressPage, ProgressWizard, Search, Select, Sidebar, Spinner, Switcher, Tabs, TextArea, Toaster, Toggle, Tooltip, Waffle, AttachFile, addBreadcrumbsNameMap, addToast, getProgressWizardContext, setProgressWizardStepsContext, setStepValidity, setFormContext, getFormContext, validateSchema, createZodString, getSubMenuItemsFromMenu, ChipType, ColumnType, ImageType, type BreadcrumbsNameMap, type HighlightPanelColumn, type HomeItem, type MainMenu, type MenuItem, type ModalProps, type ProgressWizardStep, type SelectOption, type SwitcherOption, type Tab, type Toast, type WaffleItem, type FormError, type FormContext, type SubMenuItem, type AttachFileFormConfig, type FileValidationCallback, type NotificationProps };
63
+ export { Accordion, Avatar, Breadcrumbs, Button, Card, ChatBot, Chips, DeleteConfirmationModal, ErrorPage, Footer, Form, Header, HeaderAccount, HeaderLoader, HeaderLogo, HighlightPanel, Home, Input, Link, Menu, Modal, Notification, Processing, ProgressPage, ProgressWizard, Search, Select, Sidebar, Spinner, Switcher, Tabs, TextArea, Toaster, Toggle, Tooltip, Waffle, AttachFile, addBreadcrumbsNameMap, addToast, getProgressWizardContext, setProgressWizardStepsContext, setStepValidity, setFormContext, getFormContext, validateSchema, createZodString, getSubMenuItemsFromMenu, ChipType, ColumnType, ImageType, type BreadcrumbsNameMap, type Chat, type ChatBotMessage, type ChatBotReplyMessage, type HighlightPanelColumn, type HomeItem, type MainMenu, type MenuItem, type ModalProps, type ProgressWizardStep, type SelectOption, type SwitcherOption, type Tab, type Toast, type WaffleItem, type FormError, type FormContext, type SubMenuItem, type AttachFileFormConfig, type FileValidationCallback, type NotificationProps };
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import Avatar from './Avatar/Avatar.svelte';
4
4
  import Breadcrumbs from './Breadcrumbs/Breadcrumbs.svelte';
5
5
  import Button from './Button/Button.svelte';
6
6
  import Card from './Card/Card.svelte';
7
+ import ChatBot from './ChatBot/ChatBot.svelte';
7
8
  import Chips from './Chips/Chips.svelte';
8
9
  import DeleteConfirmationModal from './DeleteConfirmation/DeleteConfirmation.svelte';
9
10
  import ErrorPage from './ErrorPage/ErrorPage.svelte';
@@ -48,7 +49,7 @@ import { createZodString } from './Form/FormController/zod-validations.js';
48
49
  import { getSubMenuItemsFromMenu } from './Menu/MenuState.svelte';
49
50
  export {
50
51
  // Components
51
- Accordion, Avatar, Breadcrumbs, Button, Card, Chips, DeleteConfirmationModal, ErrorPage, Footer, Form, Header, HeaderAccount, HeaderLoader, HeaderLogo, HighlightPanel, Home, Input, Link, Menu, Modal, Notification, Processing, ProgressPage, ProgressWizard, Search, Select, Sidebar, Spinner, Switcher, Tabs, TextArea, Toaster, Toggle, Tooltip, Waffle, AttachFile,
52
+ Accordion, Avatar, Breadcrumbs, Button, Card, ChatBot, Chips, DeleteConfirmationModal, ErrorPage, Footer, Form, Header, HeaderAccount, HeaderLoader, HeaderLogo, HighlightPanel, Home, Input, Link, Menu, Modal, Notification, Processing, ProgressPage, ProgressWizard, Search, Select, Sidebar, Spinner, Switcher, Tabs, TextArea, Toaster, Toggle, Tooltip, Waffle, AttachFile,
52
53
  // Functions and helpers
53
54
  addBreadcrumbsNameMap, addToast, getProgressWizardContext, setProgressWizardStepsContext, setStepValidity, setFormContext, getFormContext, validateSchema, createZodString, getSubMenuItemsFromMenu,
54
55
  // Enums
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softwareone/spi-sv5-library",
3
- "version": "1.7.10",
3
+ "version": "1.8.0",
4
4
  "description": "Svelte components",
5
5
  "keywords": [
6
6
  "svelte",
@@ -41,6 +41,7 @@
41
41
  }
42
42
  },
43
43
  "peerDependencies": {
44
+ "adaptivecards": "^3.0.5",
44
45
  "svelte": "^5.0.0",
45
46
  "sveltekit-superforms": "^2.0.0",
46
47
  "zod": "^4.0.0"
@@ -51,6 +52,9 @@
51
52
  },
52
53
  "zod": {
53
54
  "optional": true
55
+ },
56
+ "adaptivecards": {
57
+ "optional": true
54
58
  }
55
59
  },
56
60
  "devDependencies": {
@@ -59,15 +63,16 @@
59
63
  "@sveltejs/kit": "^2.48.4",
60
64
  "@sveltejs/package": "^2.5.4",
61
65
  "@sveltejs/vite-plugin-svelte": "^6.2.1",
66
+ "adaptivecards": "^3.0.5",
62
67
  "prettier": "^3.6.2",
63
68
  "prettier-plugin-svelte": "^3.4.0",
64
69
  "publint": "^0.3.15",
65
70
  "svelte": "^5.43.5",
66
71
  "svelte-check": "^4.3.3",
72
+ "sveltekit-superforms": "^2.28.1",
67
73
  "typescript": "^5.9.3",
68
74
  "vite": "^6.3.0",
69
- "zod": "^4.1.12",
70
- "sveltekit-superforms": "^2.28.1"
75
+ "zod": "^4.1.12"
71
76
  },
72
77
  "dependencies": {
73
78
  "@sveltejs/kit": "^2.48.4"