@fullsession.io/fs-feedback-widget 1.11.0 → 1.13.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fullsession.io/fs-feedback-widget",
3
- "version": "1.11.0",
3
+ "version": "1.13.0",
4
4
  "scripts": {
5
5
  "build": "rollup -c",
6
6
  "dev": "rollup -c -w",
package/src/App.svelte CHANGED
@@ -15,6 +15,7 @@
15
15
  $: widgetLanguage = feedback.wgLanguage;
16
16
  $: widgetAlternateColor = feedback.wgAlternateColor;
17
17
  $: wgPosition = feedback.wgPosition;
18
+ $: widgetThanksComponentActivation = feedback.wgThanksMsgActivation;
18
19
  $: widgetThanksMsg = feedback.wgThanksMsg;
19
20
  $: WidgetTriggers = feedback.wgTriggers;
20
21
  $: document.documentElement.style.setProperty("--widgetColor", widgetColor);
@@ -205,7 +206,13 @@
205
206
 
206
207
  const openThanksMessage = () => {
207
208
  visible = !visible;
208
- thanksMessageVisible = !thanksMessageVisible;
209
+ if (widgetThanksComponentActivation) {
210
+ thanksMessageVisible = !thanksMessageVisible;
211
+ } else {
212
+ setTimeout(function () {
213
+ buttonVisible = !buttonVisible;
214
+ }, 350);
215
+ }
209
216
  };
210
217
 
211
218
  const closeThanksMessage = () => {
@@ -5,68 +5,32 @@
5
5
  export let emotIconType;
6
6
  export let width = "40px";
7
7
  export let height = "40px";
8
- let emotIcons =
8
+ // Recompute emotIcons and icons reactively when `emotIconType` changes
9
+ $: emotIcons =
9
10
  emotIconType == "0"
10
11
  ? icons_1
11
12
  : emotIconType == "1"
12
13
  ? icons_2
13
14
  : emotIconType == "2"
14
15
  ? icons_3
15
- : null;
16
- let icons = [
17
- {
18
- box: 50,
19
- name: "love",
20
- svg: emotIcons[0],
21
- },
22
- {
23
- box: 50,
24
- name: "unSelectedLove",
25
- svg: emotIcons[1],
26
- },
27
- {
28
- box: 50,
29
- name: "like",
30
- svg: emotIcons[2],
31
- },
32
- {
33
- box: 50,
34
- name: "unSelectedLike",
35
- svg: emotIcons[3],
36
- },
37
- {
38
- box: 50,
39
- name: "neutral",
40
- svg: emotIcons[4],
41
- },
42
- {
43
- box: 50,
44
- name: "unSelectedNeutral",
45
- svg: emotIcons[5],
46
- },
47
- {
48
- box: 50,
49
- name: "dislike",
50
- svg: emotIcons[6],
51
- },
52
- {
53
- box: 50,
54
- name: "unSelectedDislike",
55
- svg: emotIcons[7],
56
- },
16
+ : icons_1;
57
17
 
58
- {
59
- box: 50,
60
- name: "hate",
61
- svg: emotIcons[8],
62
- },
63
- {
64
- box: 50,
65
- name: "unSelectedHate",
66
- svg: emotIcons[9],
67
- },
68
- ];
69
- let displayIcon = icons.find((e) => e.name === name);
18
+ $: icons = emotIcons
19
+ ? [
20
+ { box: 50, name: "love", svg: emotIcons[0] },
21
+ { box: 50, name: "unSelectedLove", svg: emotIcons[1] },
22
+ { box: 50, name: "like", svg: emotIcons[2] },
23
+ { box: 50, name: "unSelectedLike", svg: emotIcons[3] },
24
+ { box: 50, name: "neutral", svg: emotIcons[4] },
25
+ { box: 50, name: "unSelectedNeutral", svg: emotIcons[5] },
26
+ { box: 50, name: "dislike", svg: emotIcons[6] },
27
+ { box: 50, name: "unSelectedDislike", svg: emotIcons[7] },
28
+ { box: 50, name: "hate", svg: emotIcons[8] },
29
+ { box: 50, name: "unSelectedHate", svg: emotIcons[9] },
30
+ ]
31
+ : [];
32
+
33
+ $: displayIcon = icons.find((e) => e.name === name) || icons[0];
70
34
  </script>
71
35
 
72
36
  {#if emotIconType === "2"}
@@ -32,12 +32,13 @@
32
32
 
33
33
 
34
34
  .fs-thanks-txt {
35
- width: 61%;
36
- padding: 20px 0;
35
+ width: 100%;
36
+ padding: 20px;
37
37
  text-align: center;
38
38
  align-self: center;
39
39
  padding-bottom: 20px;
40
- overflow: hidden;
40
+ overflow-y: auto;
41
+ max-height: 100%;
41
42
  }
42
43
 
43
44
  fsContainerApp {
@@ -121,7 +122,9 @@ fsContainerApp {
121
122
  display: flex;
122
123
  flex-direction: column;
123
124
  pointer-events: auto;
124
- height: auto;
125
+ width: 320px;
126
+ height: 150px;
127
+ border-radius: 5px;
125
128
  background-color: rgb(255, 255, 255);
126
129
  margin-right: var(--widgetMarginRight);
127
130
  margin-left: var(--widgetMarginLeft);
@@ -155,12 +158,88 @@ fsContainerApp {
155
158
  margin-left: 90% !important;
156
159
  }
157
160
 
161
+ /* Mobile Responsive Styles */
162
+ @media (max-width: 768px) {
163
+ .fsCont {
164
+ width: 90vw;
165
+ max-width: 320px;
166
+ margin-right: 5vw;
167
+ margin-left: 5vw;
168
+ }
169
+
170
+ .fsContNps {
171
+ width: 95vw !important;
172
+ max-width: 400px;
173
+ }
174
+
175
+ .fsThanksMessageCont {
176
+ width: 90vw;
177
+ max-width: 320px;
178
+ margin-right: 5vw;
179
+ margin-left: 5vw;
180
+ }
181
+
182
+ .fsWidget {
183
+ width: 32px;
184
+ height: 110px;
185
+ }
186
+
187
+ #fsFeedbackTxt {
188
+ font-size: 13px;
189
+ }
190
+
191
+ .fsCloseCont {
192
+ margin-left: 82%;
193
+ }
194
+
195
+ .fsNpsCloseCont {
196
+ margin-left: 88% !important;
197
+ }
198
+
199
+ .fs-thanks-txt {
200
+ width: 100%;
201
+ padding: 15px;
202
+ font-size: 14px;
203
+ }
204
+ }
158
205
 
159
-
160
-
161
-
162
- /* @media (min-width: 640px) {
163
- body {
164
- max-width: none;
165
- }
166
- } */
206
+ @media (max-width: 480px) {
207
+ .fsCont {
208
+ width: 95vw;
209
+ max-width: 300px;
210
+ }
211
+
212
+ .fsContNps {
213
+ width: 98vw !important;
214
+ max-width: 380px;
215
+ }
216
+
217
+ .fsThanksMessageCont {
218
+ width: 95vw;
219
+ max-width: 300px;
220
+ }
221
+
222
+ .fsWidget {
223
+ width: 30px;
224
+ height: 100px;
225
+ }
226
+
227
+ #fsFeedbackTxt {
228
+ font-size: 12px;
229
+ }
230
+
231
+ .fsCloseCont {
232
+ width: 24px;
233
+ height: 24px;
234
+ margin-left: 80%;
235
+ }
236
+
237
+ #fsCloseIcon {
238
+ font-size: 13px;
239
+ }
240
+
241
+ .fs-thanks-txt {
242
+ font-size: 13px;
243
+ padding: 12px;
244
+ }
245
+ }
@@ -1,6 +1,8 @@
1
1
  <script>
2
2
  import Icons from "../Icons/reactionIcons.svelte";
3
3
  import "../widgetPages/reactionpage.css";
4
+ import { onMount } from "svelte";
5
+
4
6
  export let feedbackData;
5
7
  export let wgType;
6
8
  $: widgetComponent = 1;
@@ -20,30 +22,43 @@
20
22
  $: loveDisplay = "none";
21
23
  $: buttonColor = feedbackData.wgAccentColor;
22
24
  $: widgetQuestion = feedbackData.wgQuestion;
25
+ $: widgetEmailComponentActivation = feedbackData.wgEmailReqActivation;
23
26
  $: widgetEmailReqMsg = feedbackData.wgEmailReqMsg;
24
27
  $: widgetPlaceholder = feedbackData.wgFeedbackPlaceholder;
25
28
  $: widgetEmotIconType = feedbackData.wgReactionStyle;
26
- $: document.documentElement.style.setProperty(
27
- "--pointerMargin",
28
- pointerMargin + "%"
29
- );
30
- $: document.documentElement.style.setProperty("--hateDisplay", hateDisplay);
31
- $: document.documentElement.style.setProperty(
32
- "--dislikeDisplay",
33
- dislikeDisplay
34
- );
35
- $: document.documentElement.style.setProperty(
36
- "--neutralDisplay",
37
- neutralDisplay
38
- );
39
- $: document.documentElement.style.setProperty("--likeDisplay", likeDisplay);
40
- $: document.documentElement.style.setProperty("--loveDisplay", loveDisplay);
41
- $: document.documentElement.style.setProperty("--buttonColor", buttonColor);
29
+
30
+ // Set CSS custom properties only once on mount, then update individually as needed
31
+ // This avoids triggering multiple style recalculations on every reactive update
32
+ onMount(() => {
33
+ const root = document.documentElement.style;
34
+ root.setProperty("--pointerMargin", pointerMargin + "%");
35
+ root.setProperty("--hateDisplay", hateDisplay);
36
+ root.setProperty("--dislikeDisplay", dislikeDisplay);
37
+ root.setProperty("--neutralDisplay", neutralDisplay);
38
+ root.setProperty("--likeDisplay", likeDisplay);
39
+ root.setProperty("--loveDisplay", loveDisplay);
40
+ root.setProperty("--buttonColor", buttonColor);
41
+ });
42
+
43
+ // Update CSS properties only when specific values change
44
+ $: if (typeof window !== 'undefined') {
45
+ document.documentElement.style.setProperty("--pointerMargin", pointerMargin + "%");
46
+ }
47
+ $: if (typeof window !== 'undefined') {
48
+ document.documentElement.style.setProperty("--hateDisplay", hateDisplay);
49
+ document.documentElement.style.setProperty("--dislikeDisplay", dislikeDisplay);
50
+ document.documentElement.style.setProperty("--neutralDisplay", neutralDisplay);
51
+ document.documentElement.style.setProperty("--likeDisplay", likeDisplay);
52
+ document.documentElement.style.setProperty("--loveDisplay", loveDisplay);
53
+ }
54
+ $: if (typeof window !== 'undefined') {
55
+ document.documentElement.style.setProperty("--buttonColor", buttonColor);
56
+ }
42
57
 
43
58
  let count = 0;
44
- $: buttonState = false;
59
+ let buttonState = false;
60
+ let emailError = "";
45
61
  let checkTextAreaInterval;
46
- let checkEmailAreaInterval;
47
62
 
48
63
  export let closeHandler = (
49
64
  email,
@@ -53,13 +68,6 @@
53
68
  ) => {};
54
69
  export let closeHandlerBeforeFinishing = (email, comment, reactionType) => {};
55
70
 
56
- // const commentButtonDisable = () =>{
57
-
58
- // let source = document.querySelector(".fsTextArea");
59
-
60
- // source.addEventListener('propertychange', commentHandler);
61
-
62
- // }
63
71
  const commentHandler = () => {
64
72
  let input = document.querySelector(".fsTextArea");
65
73
 
@@ -72,27 +80,30 @@
72
80
  comment = input.value;
73
81
  };
74
82
 
75
- // const EmailButtonDisable = () =>{
76
-
77
- // let source = document.querySelector("#fsEmailTextArea");
78
-
79
- // source.addEventListener('propertychange', emailHandler);
80
-
81
- // }
82
-
83
- const emailHandler = () => {
84
- let input = document.querySelector("#fsEmailTextArea");
83
+ // Ultra-fast email handler - just updates value, no validation during typing
84
+ const emailHandler = (event) => {
85
+ email = event.target.value;
86
+ emailError = ""; // Clear error when user types
87
+ // Enable button if there's any content
88
+ buttonState = email.length > 0;
89
+ };
85
90
 
86
- if (
87
- !/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(input.value) ||
88
- input.value === ""
89
- ) {
90
- buttonState = false; //button remains disabled
91
- } else {
92
- buttonState = true; //button is enabled
93
- email = "";
94
- email = document.getElementById("fsEmailTextArea").value;
91
+ // Validate only when user clicks Send - much better performance
92
+ const validateAndSubmitEmail = () => {
93
+ const trimmedEmail = email.trim();
94
+
95
+ // Quick client-side validation
96
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
97
+
98
+ if (!trimmedEmail || !emailRegex.test(trimmedEmail)) {
99
+ emailError = "Please enter a valid email address";
100
+ buttonState = false;
101
+ return;
95
102
  }
103
+
104
+ // Valid - proceed with submission
105
+ emailError = "";
106
+ closeHandler(email, comment, reactionType, questionAnswer);
96
107
  };
97
108
 
98
109
  const changeStyle = (prevState) => {
@@ -179,19 +190,29 @@
179
190
  clearInterval(checkTextAreaInterval);
180
191
  document.querySelector(".fsTextArea").value = "";
181
192
  buttonState = false;
182
- widgetComponent = state;
193
+ if (widgetEmailComponentActivation) {
194
+ widgetComponent = state;
195
+ } else {
196
+ closeHandler(email, comment, reactionType, questionAnswer);
197
+ }
183
198
  };
184
199
 
185
200
  const openEmailView = (state) => {
186
201
  clearInterval(checkTextAreaInterval);
187
202
  document.querySelector(".fsAnswerTextArea").value = "";
188
203
  buttonState = false;
189
- widgetComponent = state;
204
+ if (widgetEmailComponentActivation) {
205
+ widgetComponent = state;
206
+ } else {
207
+ closeHandler(email, comment, reactionType, questionAnswer);
208
+ }
190
209
  };
191
210
 
192
211
  const endEmailInterval = () => {
193
- clearInterval(checkEmailAreaInterval);
194
- document.querySelector("#fsEmailTextArea").value = "";
212
+ const el = document.querySelector("#fsEmailTextArea");
213
+ if (el) el.value = "";
214
+ email = "";
215
+ emailError = "";
195
216
  };
196
217
  </script>
197
218
 
@@ -366,7 +387,7 @@
366
387
  </div>
367
388
  </div>
368
389
  {:else if widgetComponent === 2 && wgType === 2}
369
- <div id="fsTxtNps">Tell us more about your experience</div>
390
+ <div id="fsTxtNps">{widgetPlaceholder}</div>
370
391
  <div class="fsTextAreaCont-qa-view">
371
392
  <textarea
372
393
  class="fsTextArea"
@@ -390,12 +411,16 @@
390
411
  </div>
391
412
  <div id="fsEmailInputCont">
392
413
  <input
393
- type="text"
414
+ type="email"
394
415
  id="fsEmailTextArea"
395
416
  placeholder="email@domain.com"
396
- on:keyup={() => emailHandler()}
417
+ autocomplete="email"
418
+ on:input={emailHandler}
397
419
  />
398
420
  </div>
421
+ {#if emailError}
422
+ <div class="fsEmailError">{emailError}</div>
423
+ {/if}
399
424
  <div class="fsEmailFooter">
400
425
  <p
401
426
  id="fsSkipText"
@@ -411,9 +436,7 @@
411
436
  {:else if buttonState == true}
412
437
  <button
413
438
  class="fsSendButtonContCommentComp"
414
- on:click={() =>
415
- closeHandler(email, comment, reactionType, questionAnswer)}
416
- on:click={() => endEmailInterval()}
439
+ on:click={validateAndSubmitEmail}
417
440
  >
418
441
  <p class="fsSendButtonCommentComponent">Send</p>
419
442
  </button>