@product7/product7-js 0.1.7 → 0.1.9

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": "@product7/product7-js",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "JavaScript SDK for integrating Product7 feedback widgets into any website",
5
5
  "main": "dist/product7-js.js",
6
6
  "module": "src/index.js",
@@ -154,7 +154,14 @@ export const messengerCoreStyles = `
154
154
  margin-left: 0;
155
155
  }
156
156
 
157
+ .messenger-mobile-close-btn {
158
+ display: none;
159
+ }
160
+
157
161
  @media (max-width: 480px) {
162
+ .messenger-mobile-close-btn {
163
+ display: flex;
164
+ }
158
165
  .messenger-panel {
159
166
  width: 100%;
160
167
  height: 100%;
@@ -777,42 +777,35 @@ export const messengerViewsStyles = `
777
777
  .messenger-changelog-list {
778
778
  display: flex;
779
779
  flex-direction: column;
780
+ gap: var(--spacing-3);
781
+ padding: var(--spacing-4) var(--spacing-5);
780
782
  }
781
783
 
782
784
  .messenger-changelog-card {
783
785
  display: flex;
784
- align-items: center;
785
- gap: var(--spacing-3);
786
- padding: var(--spacing-4) var(--spacing-5);
787
- border-bottom: 1px solid var(--msg-border);
786
+ flex-direction: column;
787
+ border: 1px solid var(--msg-border);
788
+ border-radius: 10px;
789
+ overflow: hidden;
788
790
  cursor: pointer;
789
- transition: background var(--transition-base);
790
- }
791
-
792
- .messenger-changelog-card:last-child {
793
- border-bottom: none;
791
+ transition: box-shadow var(--transition-base), transform var(--transition-base);
794
792
  }
795
793
 
796
794
  .messenger-changelog-card:hover {
797
- background: var(--msg-bg-hover);
798
- }
799
-
800
- .messenger-changelog-card:hover .messenger-changelog-arrow {
801
- color: var(--color-primary);
802
- transform: translateX(3px);
795
+ box-shadow: var(--shadow-md);
796
+ transform: translateY(-1px);
803
797
  }
804
798
 
805
799
  .messenger-changelog-card:active {
806
- transform: translateY(1px);
800
+ transform: translateY(0);
807
801
  transition-duration: 100ms;
808
802
  }
809
803
 
810
804
  .messenger-changelog-content {
811
- flex: 1;
812
- min-width: 0;
813
805
  display: flex;
814
806
  flex-direction: column;
815
- gap: 3px;
807
+ gap: var(--spacing-1);
808
+ padding: var(--spacing-3) var(--spacing-4);
816
809
  }
817
810
 
818
811
  .messenger-changelog-meta {
@@ -850,9 +843,6 @@ export const messengerViewsStyles = `
850
843
  font-weight: var(--font-weight-semibold);
851
844
  color: var(--msg-text);
852
845
  line-height: var(--line-height-snug);
853
- white-space: nowrap;
854
- overflow: hidden;
855
- text-overflow: ellipsis;
856
846
  }
857
847
 
858
848
  .messenger-changelog-description {
@@ -867,24 +857,15 @@ export const messengerViewsStyles = `
867
857
  }
868
858
 
869
859
  .messenger-changelog-thumb {
870
- width: 64px;
871
- height: 64px;
872
- flex-shrink: 0;
873
- border-radius: 8px;
860
+ width: 100%;
874
861
  overflow: hidden;
875
- border: 1px solid var(--msg-border);
862
+ border-bottom: 1px solid var(--msg-border);
876
863
  }
877
864
 
878
865
  .messenger-changelog-thumb img {
879
866
  width: 100%;
880
- height: 100%;
867
+ height: 180px;
881
868
  display: block;
882
869
  object-fit: cover;
883
870
  }
884
-
885
- .messenger-changelog-arrow {
886
- flex-shrink: 0;
887
- color: var(--msg-text-tertiary);
888
- transition: all var(--transition-base);
889
- }
890
871
  `;
@@ -368,6 +368,32 @@ export const surveyStyles = `
368
368
  RATING SCALE (generic)
369
369
  ======================================== */
370
370
 
371
+ .feedback-survey-stars {
372
+ display: flex;
373
+ gap: var(--spacing-2);
374
+ justify-content: center;
375
+ }
376
+
377
+ .feedback-survey-star-btn {
378
+ background: none;
379
+ border: none;
380
+ cursor: pointer;
381
+ font-size: 2rem;
382
+ color: var(--color-neutral-300);
383
+ padding: 0 2px;
384
+ line-height: 1;
385
+ transition: color var(--transition-fast), transform var(--transition-fast);
386
+ }
387
+
388
+ .feedback-survey-star-btn.filled,
389
+ .feedback-survey-star-btn.hovered {
390
+ color: #f59e0b;
391
+ }
392
+
393
+ .feedback-survey-star-btn:active {
394
+ transform: scale(0.9);
395
+ }
396
+
371
397
  .feedback-survey-rating-scale {
372
398
  display: flex;
373
399
  gap: 0;
@@ -427,14 +427,16 @@ export class SurveyWidget extends BaseWidget {
427
427
  const topSurveyType = this.surveyOptions.surveyType;
428
428
  const configType = config.survey_type;
429
429
  let ratingType;
430
- if (topSurveyType === 'ces') {
431
- ratingType = 'ces';
432
- } else if (topSurveyType === 'nps') {
430
+ if (topSurveyType === 'nps') {
433
431
  ratingType = 'nps';
434
- } else if (topSurveyType === 'csat' && !configType) {
432
+ } else if (configType) {
433
+ ratingType = configType;
434
+ } else if (topSurveyType === 'ces') {
435
+ ratingType = 'ces';
436
+ } else if (topSurveyType === 'csat') {
435
437
  ratingType = 'emoji';
436
438
  } else {
437
- ratingType = configType || topSurveyType || 'csat';
439
+ ratingType = topSurveyType || 'csat';
438
440
  }
439
441
  const pageAnswer = this.surveyState.pageAnswers[pageId] || {};
440
442
  const currentRating = pageAnswer.rating;
@@ -526,6 +528,22 @@ export class SurveyWidget extends BaseWidget {
526
528
  `;
527
529
  }
528
530
 
531
+ if (ratingType === 'star') {
532
+ const starScale = Number.isFinite(scale) && scale >= 2 ? scale : 5;
533
+ return `
534
+ <div class="feedback-survey-stars" data-page-id="${pageId}">
535
+ ${[...Array(starScale).keys()]
536
+ .map((i) => {
537
+ const score = i + 1;
538
+ const filled = currentRating >= score ? ' filled' : '';
539
+ return `<button class="feedback-survey-page-rating-btn feedback-survey-star-btn${filled}" data-page-id="${pageId}" data-score="${score}">★</button>`;
540
+ })
541
+ .join('')}
542
+ </div>
543
+ ${labels}
544
+ `;
545
+ }
546
+
529
547
  return `
530
548
  <div class="feedback-survey-rating-scale" data-page-id="${pageId}">
531
549
  ${[...Array(scale).keys()]
@@ -781,6 +799,8 @@ export class SurveyWidget extends BaseWidget {
781
799
  const pageId = page.id || `page_${this.surveyState.currentPageIndex}`;
782
800
 
783
801
  if (page.type === 'rating') {
802
+ const isStarRating = !!this.surveyElement.querySelector('.feedback-survey-star-btn');
803
+
784
804
  this.surveyElement
785
805
  .querySelectorAll('.feedback-survey-page-rating-btn')
786
806
  .forEach((btn) => {
@@ -789,12 +809,44 @@ export class SurveyWidget extends BaseWidget {
789
809
  if (Number.isNaN(score)) return;
790
810
  this._setPageAnswer(pageId, { rating: score });
791
811
 
792
- this.surveyElement
793
- .querySelectorAll('.feedback-survey-page-rating-btn')
794
- .forEach((item) => item.classList.remove('selected'));
795
- btn.classList.add('selected');
812
+ if (isStarRating) {
813
+ this.surveyElement
814
+ .querySelectorAll('.feedback-survey-star-btn')
815
+ .forEach((star) => {
816
+ const starScore = parseInt(star.dataset.score);
817
+ star.classList.toggle('filled', starScore <= score);
818
+ });
819
+ } else {
820
+ this.surveyElement
821
+ .querySelectorAll('.feedback-survey-page-rating-btn')
822
+ .forEach((item) => item.classList.remove('selected'));
823
+ btn.classList.add('selected');
824
+ }
796
825
  });
826
+
827
+ if (isStarRating) {
828
+ btn.addEventListener('mouseenter', () => {
829
+ const hoverScore = parseInt(btn.dataset.score);
830
+ this.surveyElement
831
+ .querySelectorAll('.feedback-survey-star-btn')
832
+ .forEach((star) => {
833
+ const starScore = parseInt(star.dataset.score);
834
+ star.classList.toggle('hovered', starScore <= hoverScore);
835
+ });
836
+ });
837
+ }
797
838
  });
839
+
840
+ if (isStarRating) {
841
+ const container = this.surveyElement.querySelector('.feedback-survey-stars');
842
+ if (container) {
843
+ container.addEventListener('mouseleave', () => {
844
+ this.surveyElement
845
+ .querySelectorAll('.feedback-survey-star-btn')
846
+ .forEach((star) => star.classList.remove('hovered'));
847
+ });
848
+ }
849
+ }
798
850
  }
799
851
 
800
852
  if (page.type === 'multiple_choice') {
@@ -29,8 +29,8 @@ export class ChangelogView {
29
29
  <div class="messenger-changelog-header">
30
30
  <div class="messenger-changelog-header-top">
31
31
  <h2>Latest Updates</h2>
32
- <button class="sdk-close-btn messenger-changelog-close-btn" aria-label="Close">
33
- <iconify-icon icon="ph:x-duotone" width="18" height="18"></iconify-icon>
32
+ <button class="sdk-close-btn messenger-changelog-close-btn messenger-mobile-close-btn" aria-label="Close">
33
+ <iconify-icon icon="ph:x" width="18" height="18"></iconify-icon>
34
34
  </button>
35
35
  </div>
36
36
  </div>
@@ -70,30 +70,13 @@ export class ChangelogView {
70
70
  }
71
71
 
72
72
  _renderChangelogCard(item) {
73
- const dateStr = this._formatDate(item.publishedAt || item.date);
74
- const labelsHtml =
75
- item.labels && item.labels.length > 0
76
- ? item.labels
77
- .map((label) => {
78
- const bg = label.color || '#E5E7EB';
79
- const color = this._getContrastColor(bg);
80
- return `<span class="messenger-changelog-tag" style="background:${bg};color:${color};">${label.name}</span>`;
81
- })
82
- .join('')
83
- : '';
84
-
85
73
  return `
86
74
  <div class="messenger-changelog-card" data-changelog-id="${item.id}">
75
+ ${item.coverImage ? `<div class="messenger-changelog-thumb"><img src="${item.coverImage}" alt="${item.title}" onerror="this.parentElement.style.display='none';" /></div>` : ''}
87
76
  <div class="messenger-changelog-content">
88
- <div class="messenger-changelog-meta">
89
- ${dateStr ? `<span class="messenger-changelog-date">${dateStr}</span>` : ''}
90
- ${labelsHtml}
91
- </div>
92
77
  <h3 class="messenger-changelog-title">${item.title}</h3>
93
78
  ${item.description ? `<p class="messenger-changelog-description">${this._stripHtml(item.description)}</p>` : ''}
94
79
  </div>
95
- ${item.coverImage ? `<div class="messenger-changelog-thumb"><img src="${item.coverImage}" alt="${item.title}" onerror="this.style.display='none';" /></div>` : ''}
96
- <iconify-icon icon="ph:caret-right" class="messenger-changelog-arrow" width="16" height="16"></iconify-icon>
97
80
  </div>
98
81
  `;
99
82
  }
@@ -136,11 +119,12 @@ export class ChangelogView {
136
119
  }
137
120
 
138
121
  _attachEvents() {
139
- this.element
140
- .querySelector('.messenger-changelog-close-btn')
141
- .addEventListener('click', () => {
122
+ const mobileCloseBtn = this.element.querySelector('.messenger-changelog-close-btn');
123
+ if (mobileCloseBtn) {
124
+ mobileCloseBtn.addEventListener('click', () => {
142
125
  this.state.setOpen(false);
143
126
  });
127
+ }
144
128
 
145
129
  this._attachChangelogEvents();
146
130
  }
@@ -87,11 +87,8 @@ export class ChatView {
87
87
  <span class="messenger-chat-subtitle">The team can also help</span>
88
88
  </div>
89
89
  <div class="messenger-chat-header-actions">
90
- <button class="sdk-btn-icon" aria-label="More options">
91
- <iconify-icon icon="ph:dots-three-outline-duotone" width="20" height="20"></iconify-icon>
92
- </button>
93
- <button class="sdk-close-btn" aria-label="Close">
94
- <iconify-icon icon="ph:x-duotone" width="18" height="18"></iconify-icon>
90
+ <button class="sdk-btn-icon sdk-close-btn messenger-mobile-close-btn" aria-label="Close">
91
+ <iconify-icon icon="ph:x" width="18" height="18"></iconify-icon>
95
92
  </button>
96
93
  </div>
97
94
  </div>
@@ -368,11 +365,12 @@ export class ChatView {
368
365
  this.state.setView('messages');
369
366
  });
370
367
 
371
- this.element
372
- .querySelector('.sdk-close-btn')
373
- .addEventListener('click', () => {
368
+ const mobileCloseBtn = this.element.querySelector('.messenger-mobile-close-btn');
369
+ if (mobileCloseBtn) {
370
+ mobileCloseBtn.addEventListener('click', () => {
374
371
  this.state.setOpen(false);
375
372
  });
373
+ }
376
374
 
377
375
  const input = this.element.querySelector('.messenger-compose-input');
378
376
  const sendBtn = this.element.querySelector('.messenger-compose-send');
@@ -67,8 +67,8 @@ export class ConversationsView {
67
67
  this.element.innerHTML = `
68
68
  <div class="messenger-conversations-header">
69
69
  <h2>Messages</h2>
70
- <button class="sdk-close-btn" aria-label="Close">
71
- <iconify-icon icon="ph:x-duotone" width="18" height="18"></iconify-icon>
70
+ <button class="sdk-close-btn messenger-mobile-close-btn" aria-label="Close">
71
+ <iconify-icon icon="ph:x" width="18" height="18"></iconify-icon>
72
72
  </button>
73
73
  </div>
74
74
 
@@ -28,8 +28,8 @@ export class HelpView {
28
28
  <div class="messenger-help-header">
29
29
  <div class="messenger-help-header-top">
30
30
  <h2>Help</h2>
31
- <button class="sdk-close-btn messenger-help-close-btn" aria-label="Close">
32
- <iconify-icon icon="ph:x-duotone" width="18" height="18"></iconify-icon>
31
+ <button class="sdk-close-btn messenger-help-close-btn messenger-mobile-close-btn" aria-label="Close">
32
+ <iconify-icon icon="ph:x" width="18" height="18"></iconify-icon>
33
33
  </button>
34
34
  </div>
35
35
  <div class="messenger-help-search-wrap">
@@ -196,11 +196,12 @@ export class HelpView {
196
196
  }
197
197
 
198
198
  _attachEvents() {
199
- this.element
200
- .querySelector('.messenger-help-close-btn')
201
- .addEventListener('click', () => {
199
+ const mobileCloseBtn = this.element.querySelector('.messenger-help-close-btn');
200
+ if (mobileCloseBtn) {
201
+ mobileCloseBtn.addEventListener('click', () => {
202
202
  this.state.setOpen(false);
203
203
  });
204
+ }
204
205
 
205
206
  const searchInput = this.element.querySelector(
206
207
  '.messenger-help-search-input'
package/types/index.d.ts CHANGED
@@ -45,6 +45,7 @@ declare module '@product7/product7-js' {
45
45
  label?: string;
46
46
  text?: string;
47
47
  customStyles?: Record<string, string>;
48
+ trigger?: boolean | string | Element;
48
49
  }
49
50
 
50
51
  export interface WidgetConfigMap {