@product7/product7-js 0.1.6 → 0.1.8

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.6",
3
+ "version": "0.1.8",
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",
@@ -253,6 +253,49 @@ export const messengerComponentsStyles = `
253
253
  COMPOSE AREA
254
254
  ======================================== */
255
255
 
256
+ /* ========================================
257
+ EMOJI PICKER
258
+ ======================================== */
259
+
260
+ .messenger-emoji-picker-container {
261
+ padding: 0 var(--spacing-3) var(--spacing-2);
262
+ }
263
+
264
+ .messenger-emoji-picker-container emoji-picker {
265
+ width: 100%;
266
+ height: 320px;
267
+ border-radius: var(--radius-xl);
268
+ border: 1px solid var(--msg-border);
269
+ box-shadow: var(--shadow-lg);
270
+
271
+ --background: var(--msg-bg);
272
+ --border-color: var(--msg-border);
273
+ --border-radius: 12px;
274
+ --category-font-color: var(--msg-text-secondary);
275
+ --category-font-size: 0.7rem;
276
+ --emoji-size: 1.35rem;
277
+ --emoji-padding: 0.3rem;
278
+ --input-border-color: var(--msg-border);
279
+ --input-border-radius: 8px;
280
+ --input-font-color: var(--msg-text);
281
+ --input-font-size: 0.8rem;
282
+ --input-placeholder-color: var(--msg-text-tertiary);
283
+ --input-padding: 0.375rem 0.625rem;
284
+ --outline-color: var(--color-primary);
285
+ --num-columns: 8;
286
+ --indicator-color: var(--color-primary);
287
+ --hover-background: var(--msg-bg-hover);
288
+ }
289
+
290
+ .messenger-widget.theme-dark .messenger-emoji-picker-container emoji-picker {
291
+ --background: var(--msg-bg);
292
+ --border-color: var(--msg-border);
293
+ --category-font-color: var(--msg-text-secondary);
294
+ --input-font-color: var(--msg-text);
295
+ --input-placeholder-color: var(--msg-text-tertiary);
296
+ --hover-background: var(--msg-bg-hover);
297
+ }
298
+
256
299
  .messenger-chat-compose {
257
300
  display: flex;
258
301
  flex-direction: column;
@@ -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
  `;
@@ -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
  }
@@ -9,6 +9,8 @@ export class ChatView {
9
9
  this._typingIndicator = null;
10
10
  this._isConversationClosed = false;
11
11
  this._pendingAttachments = [];
12
+ this._emojiPickerOpen = false;
13
+ this._emojiOutsideHandler = null;
12
14
  }
13
15
 
14
16
  render() {
@@ -85,11 +87,8 @@ export class ChatView {
85
87
  <span class="messenger-chat-subtitle">The team can also help</span>
86
88
  </div>
87
89
  <div class="messenger-chat-header-actions">
88
- <button class="sdk-btn-icon" aria-label="More options">
89
- <iconify-icon icon="ph:dots-three-outline-duotone" width="20" height="20"></iconify-icon>
90
- </button>
91
- <button class="sdk-close-btn" aria-label="Close">
92
- <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>
93
92
  </button>
94
93
  </div>
95
94
  </div>
@@ -129,7 +128,7 @@ export class ChatView {
129
128
  <button class="sdk-btn-icon messenger-compose-attach" aria-label="Attach file">
130
129
  <iconify-icon icon="ph:paperclip-duotone" width="20" height="20"></iconify-icon>
131
130
  </button>
132
- <button class="sdk-btn-icon" aria-label="Emoji">
131
+ <button class="sdk-btn-icon messenger-emoji-btn" aria-label="Emoji">
133
132
  <iconify-icon icon="ph:smiley-duotone" width="20" height="20"></iconify-icon>
134
133
  </button>
135
134
  </div>
@@ -366,11 +365,12 @@ export class ChatView {
366
365
  this.state.setView('messages');
367
366
  });
368
367
 
369
- this.element
370
- .querySelector('.sdk-close-btn')
371
- .addEventListener('click', () => {
368
+ const mobileCloseBtn = this.element.querySelector('.messenger-mobile-close-btn');
369
+ if (mobileCloseBtn) {
370
+ mobileCloseBtn.addEventListener('click', () => {
372
371
  this.state.setOpen(false);
373
372
  });
373
+ }
374
374
 
375
375
  const input = this.element.querySelector('.messenger-compose-input');
376
376
  const sendBtn = this.element.querySelector('.messenger-compose-send');
@@ -398,6 +398,14 @@ export class ChatView {
398
398
  });
399
399
  }
400
400
 
401
+ const emojiBtn = this.element.querySelector('.messenger-emoji-btn');
402
+ if (emojiBtn) {
403
+ emojiBtn.addEventListener('click', (e) => {
404
+ e.stopPropagation();
405
+ this._toggleEmojiPicker();
406
+ });
407
+ }
408
+
401
409
  const attachBtn = this.element.querySelector('.messenger-compose-attach');
402
410
  const fileInput = this.element.querySelector(
403
411
  '.messenger-compose-file-input'
@@ -528,6 +536,85 @@ export class ChatView {
528
536
  this._updateSendButtonState();
529
537
  }
530
538
 
539
+ _loadEmojiPicker() {
540
+ if (document.querySelector('#product7-emoji-picker-script')) {
541
+ return Promise.resolve();
542
+ }
543
+ return new Promise((resolve, reject) => {
544
+ const script = document.createElement('script');
545
+ script.id = 'product7-emoji-picker-script';
546
+ script.type = 'module';
547
+ script.src =
548
+ 'https://cdn.jsdelivr.net/npm/emoji-picker-element@1/index.js';
549
+ script.onload = resolve;
550
+ script.onerror = reject;
551
+ document.head.appendChild(script);
552
+ });
553
+ }
554
+
555
+ async _toggleEmojiPicker() {
556
+ const existing = this.element.querySelector(
557
+ '.messenger-emoji-picker-container'
558
+ );
559
+ if (existing) {
560
+ existing.remove();
561
+ this._emojiPickerOpen = false;
562
+ if (this._emojiOutsideHandler) {
563
+ document.removeEventListener('click', this._emojiOutsideHandler);
564
+ this._emojiOutsideHandler = null;
565
+ }
566
+ return;
567
+ }
568
+
569
+ try {
570
+ await this._loadEmojiPicker();
571
+ } catch {
572
+ return;
573
+ }
574
+
575
+ const container = document.createElement('div');
576
+ container.className = 'messenger-emoji-picker-container';
577
+
578
+ const picker = document.createElement('emoji-picker');
579
+ container.appendChild(picker);
580
+
581
+ const compose = this.element.querySelector('.messenger-chat-compose');
582
+ compose.parentNode.insertBefore(container, compose);
583
+ this._emojiPickerOpen = true;
584
+
585
+ picker.addEventListener('emoji-click', (e) => {
586
+ this._insertEmoji(e.detail.unicode);
587
+ container.remove();
588
+ this._emojiPickerOpen = false;
589
+ if (this._emojiOutsideHandler) {
590
+ document.removeEventListener('click', this._emojiOutsideHandler);
591
+ this._emojiOutsideHandler = null;
592
+ }
593
+ });
594
+
595
+ this._emojiOutsideHandler = (e) => {
596
+ if (!container.contains(e.target) && !e.target.closest('.messenger-emoji-btn')) {
597
+ container.remove();
598
+ this._emojiPickerOpen = false;
599
+ document.removeEventListener('click', this._emojiOutsideHandler);
600
+ this._emojiOutsideHandler = null;
601
+ }
602
+ };
603
+ setTimeout(() => document.addEventListener('click', this._emojiOutsideHandler), 0);
604
+ }
605
+
606
+ _insertEmoji(emoji) {
607
+ const input = this.element.querySelector('.messenger-compose-input');
608
+ if (!input) return;
609
+ const start = input.selectionStart;
610
+ const end = input.selectionEnd;
611
+ input.value =
612
+ input.value.slice(0, start) + emoji + input.value.slice(end);
613
+ input.selectionStart = input.selectionEnd = start + emoji.length;
614
+ input.focus();
615
+ input.dispatchEvent(new Event('input'));
616
+ }
617
+
531
618
  _startTyping() {
532
619
  if (this._isConversationClosed) return;
533
620
  if (!this._isTyping && this.state.activeConversationId) {
@@ -585,6 +672,9 @@ export class ChatView {
585
672
  clearTimeout(this._typingTimeout);
586
673
  }
587
674
  this._stopTyping();
675
+ if (this._emojiOutsideHandler) {
676
+ document.removeEventListener('click', this._emojiOutsideHandler);
677
+ }
588
678
  if (this.element && this.element.parentNode) {
589
679
  this.element.parentNode.removeChild(this.element);
590
680
  }
@@ -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 {