@nectary/labs 2.3.4 → 2.4.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/README.md +354 -0
- package/color-select/index.d.ts +39 -0
- package/color-select/index.js +96 -0
- package/index.d.ts +2 -0
- package/index.js +3 -0
- package/package.json +2 -6
- package/{phone-preview.d.ts → phone-preview/index.d.ts} +18 -7
- package/phone-preview/index.js +94 -0
- package/phone-preview-rcs-channel/index.d.ts +49 -0
- package/phone-preview-rcs-channel/index.js +175 -0
- package/phone-preview-rcs-channel-actions/index.d.ts +37 -0
- package/phone-preview-rcs-channel-actions/index.js +126 -0
- package/phone-preview-rcs-channel-info/index.d.ts +29 -0
- package/phone-preview-rcs-channel-info/index.js +64 -0
- package/phone-preview-rcs-channel-info-option/index.d.ts +40 -0
- package/phone-preview-rcs-channel-info-option/index.js +106 -0
- package/phone-preview-rcs-channel-options/index.d.ts +27 -0
- package/phone-preview-rcs-channel-options/index.js +46 -0
- package/phone-preview-rcs-channel-tabs/index.d.ts +34 -0
- package/phone-preview-rcs-channel-tabs/index.js +79 -0
- package/{phone-preview-rcs-chat.d.ts → phone-preview-rcs-chat/index.d.ts} +20 -8
- package/phone-preview-rcs-chat/index.js +79 -0
- package/phone-preview-rcs-chat-message/index.d.ts +35 -0
- package/phone-preview-rcs-chat-message/index.js +48 -0
- package/utils/element.d.ts +9 -0
- package/utils/element.js +35 -0
- package/utils/index.d.ts +1 -1
- package/utils/index.js +1 -1
- package/Readme.md +0 -1
- package/color-select.d.ts +0 -34
- package/color-select.js +0 -79
- package/phone-preview-rcs-channel.d.ts +0 -50
- package/phone-preview-rcs-channel.js +0 -319
- package/phone-preview-rcs-chat.js +0 -162
- package/phone-preview.js +0 -120
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import '@nectary/components/icon';
|
|
2
|
+
import { NectaryElement } from '../utils';
|
|
3
|
+
import '../phone-preview-rcs-channel-actions';
|
|
4
|
+
import '../phone-preview-rcs-channel-info';
|
|
5
|
+
import '../phone-preview-rcs-channel-info-option';
|
|
6
|
+
import '../phone-preview-rcs-channel-options';
|
|
7
|
+
import '../phone-preview-rcs-channel-tabs';
|
|
8
|
+
import type React from 'react';
|
|
9
|
+
export declare class PhonePreviewRcsChannel extends NectaryElement {
|
|
10
|
+
#private;
|
|
11
|
+
constructor();
|
|
12
|
+
connectedCallback(): void;
|
|
13
|
+
disconnectedCallback(): void;
|
|
14
|
+
static get observedAttributes(): string[];
|
|
15
|
+
attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
|
|
16
|
+
get name(): string;
|
|
17
|
+
set name(value: string);
|
|
18
|
+
get description(): string;
|
|
19
|
+
set description(value: string);
|
|
20
|
+
get color(): string;
|
|
21
|
+
set color(value: string);
|
|
22
|
+
get banner(): string;
|
|
23
|
+
set banner(value: string);
|
|
24
|
+
get logo(): string;
|
|
25
|
+
set logo(value: string);
|
|
26
|
+
}
|
|
27
|
+
type RcsChannelProps = {
|
|
28
|
+
name?: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
color?: string;
|
|
31
|
+
banner?: string;
|
|
32
|
+
logo?: string;
|
|
33
|
+
};
|
|
34
|
+
type RcsChannelElementProps = Partial<{
|
|
35
|
+
[K in keyof RcsChannelProps]: RcsChannelProps[K] | string;
|
|
36
|
+
}>;
|
|
37
|
+
declare global {
|
|
38
|
+
interface HTMLElementTagNameMap {
|
|
39
|
+
'sinch-labs-phone-preview-rcs-channel': RcsChannelElementProps & HTMLElement;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
declare module 'react' {
|
|
43
|
+
namespace JSX {
|
|
44
|
+
interface IntrinsicElements {
|
|
45
|
+
'sinch-labs-phone-preview-rcs-channel': RcsChannelElementProps & React.ClassAttributes<HTMLElement> & React.HTMLAttributes<HTMLElement>;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import '@nectary/components/icon';
|
|
2
|
+
import { defineCustomElement, NectaryElement } from '../utils';
|
|
3
|
+
import '../phone-preview-rcs-channel-actions';
|
|
4
|
+
import '../phone-preview-rcs-channel-info';
|
|
5
|
+
import '../phone-preview-rcs-channel-info-option';
|
|
6
|
+
import '../phone-preview-rcs-channel-options';
|
|
7
|
+
import '../phone-preview-rcs-channel-tabs';
|
|
8
|
+
import templateHTML from './template.html';
|
|
9
|
+
const template = document.createElement('template');
|
|
10
|
+
template.innerHTML = templateHTML;
|
|
11
|
+
export class PhonePreviewRcsChannel extends NectaryElement {
|
|
12
|
+
#bannerImg;
|
|
13
|
+
#logoImg;
|
|
14
|
+
#brandName;
|
|
15
|
+
#brandDescription;
|
|
16
|
+
#actions;
|
|
17
|
+
#tabs;
|
|
18
|
+
#infoContainer;
|
|
19
|
+
#optionsContainer;
|
|
20
|
+
#controller = null;
|
|
21
|
+
#currentTab = 0;
|
|
22
|
+
constructor() {
|
|
23
|
+
super();
|
|
24
|
+
const shadowRoot = this.attachShadow();
|
|
25
|
+
shadowRoot.appendChild(template.content.cloneNode(true));
|
|
26
|
+
this.#bannerImg = shadowRoot.querySelector('#banner-img');
|
|
27
|
+
this.#logoImg = shadowRoot.querySelector('#logo-img');
|
|
28
|
+
this.#brandName = shadowRoot.querySelector('#brand-name');
|
|
29
|
+
this.#brandDescription = shadowRoot.querySelector('#brand-description');
|
|
30
|
+
this.#actions = shadowRoot.querySelector('#actions');
|
|
31
|
+
this.#tabs = shadowRoot.querySelector('#tabs');
|
|
32
|
+
this.#infoContainer = shadowRoot.querySelector('#info-container');
|
|
33
|
+
this.#optionsContainer = shadowRoot.querySelector('#options-container');
|
|
34
|
+
}
|
|
35
|
+
connectedCallback() {
|
|
36
|
+
super.connectedCallback();
|
|
37
|
+
this.#controller = new AbortController();
|
|
38
|
+
const { signal } = this.#controller;
|
|
39
|
+
this.#tabs.addEventListener('-tab-change', this.#onTabChange, { signal });
|
|
40
|
+
// Listen for changes in slotted children
|
|
41
|
+
this.addEventListener('slotchange', this.#onSlotChange, { signal });
|
|
42
|
+
// Watch for changes to child elements
|
|
43
|
+
const observer = new MutationObserver(() => {
|
|
44
|
+
this.#updateActionsFromSlottedChildren();
|
|
45
|
+
});
|
|
46
|
+
observer.observe(this, { childList: true, subtree: true });
|
|
47
|
+
// Clean up observer when disconnected
|
|
48
|
+
signal.addEventListener('abort', () => {
|
|
49
|
+
observer.disconnect();
|
|
50
|
+
});
|
|
51
|
+
this.#updateUI();
|
|
52
|
+
this.#updateTabContent();
|
|
53
|
+
}
|
|
54
|
+
disconnectedCallback() {
|
|
55
|
+
super.disconnectedCallback();
|
|
56
|
+
this.#controller?.abort();
|
|
57
|
+
this.#controller = null;
|
|
58
|
+
}
|
|
59
|
+
static get observedAttributes() {
|
|
60
|
+
return ['name', 'description', 'color', 'banner', 'logo'];
|
|
61
|
+
}
|
|
62
|
+
attributeChangedCallback(name, oldVal, newVal) {
|
|
63
|
+
if (oldVal === newVal) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
switch (name) {
|
|
67
|
+
case 'name':
|
|
68
|
+
case 'description':
|
|
69
|
+
case 'color':
|
|
70
|
+
case 'banner':
|
|
71
|
+
case 'logo':
|
|
72
|
+
this.#updateUI();
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
get name() {
|
|
77
|
+
return this.getAttribute('name') ?? '';
|
|
78
|
+
}
|
|
79
|
+
set name(value) {
|
|
80
|
+
this.setAttribute('name', value);
|
|
81
|
+
}
|
|
82
|
+
get description() {
|
|
83
|
+
return this.getAttribute('description') ?? '';
|
|
84
|
+
}
|
|
85
|
+
set description(value) {
|
|
86
|
+
this.setAttribute('description', value);
|
|
87
|
+
}
|
|
88
|
+
get color() {
|
|
89
|
+
return this.getAttribute('color') ?? '';
|
|
90
|
+
}
|
|
91
|
+
set color(value) {
|
|
92
|
+
this.setAttribute('color', value);
|
|
93
|
+
}
|
|
94
|
+
get banner() {
|
|
95
|
+
return this.getAttribute('banner') ?? '';
|
|
96
|
+
}
|
|
97
|
+
set banner(value) {
|
|
98
|
+
this.setAttribute('banner', value);
|
|
99
|
+
}
|
|
100
|
+
get logo() {
|
|
101
|
+
return this.getAttribute('logo') ?? '';
|
|
102
|
+
}
|
|
103
|
+
set logo(value) {
|
|
104
|
+
this.setAttribute('logo', value);
|
|
105
|
+
}
|
|
106
|
+
#updateUI() {
|
|
107
|
+
if (!this.isDomConnected) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// Transparent placeholder for empty images
|
|
111
|
+
const transparentIcon = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
|
112
|
+
// Update banner image
|
|
113
|
+
this.#bannerImg.src = this.banner !== '' ? this.banner : transparentIcon;
|
|
114
|
+
// Update logo image
|
|
115
|
+
this.#logoImg.src = this.logo !== '' ? this.logo : transparentIcon;
|
|
116
|
+
// Update brand name
|
|
117
|
+
this.#brandName.textContent = this.name !== '' ? this.name : 'Brand name';
|
|
118
|
+
// Update brand description
|
|
119
|
+
this.#brandDescription.textContent = this.description !== '' ? this.description : 'Brand description';
|
|
120
|
+
// Update banner color
|
|
121
|
+
if (this.color !== '') {
|
|
122
|
+
this.style.setProperty('--banner-color', this.color);
|
|
123
|
+
}
|
|
124
|
+
// Update actions component with data from slotted children
|
|
125
|
+
this.#updateActionsFromSlottedChildren();
|
|
126
|
+
// Update tabs color
|
|
127
|
+
this.#tabs.setAttribute('color', this.color);
|
|
128
|
+
}
|
|
129
|
+
#updateActionsFromSlottedChildren() {
|
|
130
|
+
// Get slotted contact options
|
|
131
|
+
const slottedElements = this.querySelectorAll('sinch-labs-phone-preview-rcs-channel-info-option');
|
|
132
|
+
// Find first phone, website, and email from slotted children
|
|
133
|
+
let firstPhone = '';
|
|
134
|
+
let firstWebsite = '';
|
|
135
|
+
let firstEmail = '';
|
|
136
|
+
for (const element of slottedElements) {
|
|
137
|
+
const type = element.getAttribute('type');
|
|
138
|
+
const contact = element.getAttribute('contact') ?? '';
|
|
139
|
+
if (type === 'phone' && firstPhone === '') {
|
|
140
|
+
firstPhone = contact;
|
|
141
|
+
}
|
|
142
|
+
else if (type === 'website' && firstWebsite === '') {
|
|
143
|
+
firstWebsite = contact;
|
|
144
|
+
}
|
|
145
|
+
else if (type === 'email' && firstEmail === '') {
|
|
146
|
+
firstEmail = contact;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
this.#actions.setAttribute('phone', firstPhone);
|
|
150
|
+
this.#actions.setAttribute('website', firstWebsite);
|
|
151
|
+
this.#actions.setAttribute('email', firstEmail);
|
|
152
|
+
}
|
|
153
|
+
#updateTabContent() {
|
|
154
|
+
// No longer needed since we use slots
|
|
155
|
+
this.#updateTabVisibility();
|
|
156
|
+
}
|
|
157
|
+
#updateTabVisibility() {
|
|
158
|
+
if (this.#currentTab === 0) {
|
|
159
|
+
this.#infoContainer.style.display = 'block';
|
|
160
|
+
this.#optionsContainer.style.display = 'none';
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
this.#infoContainer.style.display = 'none';
|
|
164
|
+
this.#optionsContainer.style.display = 'block';
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
#onSlotChange = () => {
|
|
168
|
+
this.#updateActionsFromSlottedChildren();
|
|
169
|
+
};
|
|
170
|
+
#onTabChange = (event) => {
|
|
171
|
+
this.#currentTab = event.detail;
|
|
172
|
+
this.#updateTabVisibility();
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
defineCustomElement('sinch-labs-phone-preview-rcs-channel', PhonePreviewRcsChannel);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import '@nectary/components/icon';
|
|
2
|
+
import { NectaryElement } from '../utils';
|
|
3
|
+
import type React from 'react';
|
|
4
|
+
export declare class PhonePreviewRcsChannelActions extends NectaryElement {
|
|
5
|
+
#private;
|
|
6
|
+
constructor();
|
|
7
|
+
connectedCallback(): void;
|
|
8
|
+
static get observedAttributes(): string[];
|
|
9
|
+
attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
|
|
10
|
+
get phone(): string;
|
|
11
|
+
set phone(value: string);
|
|
12
|
+
get website(): string;
|
|
13
|
+
set website(value: string);
|
|
14
|
+
get email(): string;
|
|
15
|
+
set email(value: string);
|
|
16
|
+
}
|
|
17
|
+
type Props = {
|
|
18
|
+
phone?: string;
|
|
19
|
+
website?: string;
|
|
20
|
+
email?: string;
|
|
21
|
+
};
|
|
22
|
+
type ElementProps = Partial<{
|
|
23
|
+
[K in keyof Props]: Props[K] | string;
|
|
24
|
+
}>;
|
|
25
|
+
declare global {
|
|
26
|
+
interface HTMLElementTagNameMap {
|
|
27
|
+
'sinch-labs-phone-preview-rcs-channel-actions': ElementProps & HTMLElement;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
declare module 'react' {
|
|
31
|
+
namespace JSX {
|
|
32
|
+
interface IntrinsicElements {
|
|
33
|
+
'sinch-labs-phone-preview-rcs-channel-actions': ElementProps & React.ClassAttributes<HTMLElement> & React.HTMLAttributes<HTMLElement>;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import '@nectary/components/icon';
|
|
2
|
+
import { defineCustomElement, NectaryElement } from '../utils';
|
|
3
|
+
import templateHTML from './template.html';
|
|
4
|
+
const template = document.createElement('template');
|
|
5
|
+
template.innerHTML = templateHTML;
|
|
6
|
+
console.log(template);
|
|
7
|
+
export class PhonePreviewRcsChannelActions extends NectaryElement {
|
|
8
|
+
#callAction;
|
|
9
|
+
#websiteAction;
|
|
10
|
+
#emailAction;
|
|
11
|
+
constructor() {
|
|
12
|
+
super();
|
|
13
|
+
console.log('[PhonePreviewRcsChannelActions] Constructor called');
|
|
14
|
+
const shadowRoot = this.attachShadow();
|
|
15
|
+
shadowRoot.appendChild(template.content.cloneNode(true));
|
|
16
|
+
this.#callAction = shadowRoot.querySelector('#call-action');
|
|
17
|
+
this.#websiteAction = shadowRoot.querySelector('#website-action');
|
|
18
|
+
this.#emailAction = shadowRoot.querySelector('#email-action');
|
|
19
|
+
console.log('[PhonePreviewRcsChannelActions] Elements queried:', {
|
|
20
|
+
callAction: this.#callAction,
|
|
21
|
+
websiteAction: this.#websiteAction,
|
|
22
|
+
emailAction: this.#emailAction,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
connectedCallback() {
|
|
26
|
+
console.log('[PhonePreviewRcsChannelActions] Connected to DOM');
|
|
27
|
+
super.connectedCallback();
|
|
28
|
+
this.#updateUI();
|
|
29
|
+
}
|
|
30
|
+
static get observedAttributes() {
|
|
31
|
+
return ['phone', 'website', 'email'];
|
|
32
|
+
}
|
|
33
|
+
attributeChangedCallback(name, oldVal, newVal) {
|
|
34
|
+
console.log(`[PhonePreviewRcsChannelActions] Attribute "${name}" changed from "${oldVal}" to "${newVal}"`);
|
|
35
|
+
if (oldVal === newVal) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
this.#updateUI();
|
|
39
|
+
}
|
|
40
|
+
get phone() {
|
|
41
|
+
return this.getAttribute('phone') ?? '';
|
|
42
|
+
}
|
|
43
|
+
set phone(value) {
|
|
44
|
+
console.log(`[PhonePreviewRcsChannelActions] Setting phone to "${value}"`);
|
|
45
|
+
if (value !== '') {
|
|
46
|
+
this.setAttribute('phone', value);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
this.removeAttribute('phone');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
get website() {
|
|
53
|
+
return this.getAttribute('website') ?? '';
|
|
54
|
+
}
|
|
55
|
+
set website(value) {
|
|
56
|
+
console.log(`[PhonePreviewRcsChannelActions] Setting website to "${value}"`);
|
|
57
|
+
if (value !== '') {
|
|
58
|
+
this.setAttribute('website', value);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
this.removeAttribute('website');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
get email() {
|
|
65
|
+
return this.getAttribute('email') ?? '';
|
|
66
|
+
}
|
|
67
|
+
set email(value) {
|
|
68
|
+
console.log(`[PhonePreviewRcsChannelActions] Setting email to "${value}"`);
|
|
69
|
+
if (value !== '') {
|
|
70
|
+
this.setAttribute('email', value);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
this.removeAttribute('email');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
#updateUI() {
|
|
77
|
+
console.log('[PhonePreviewRcsChannelActions] Updating UI');
|
|
78
|
+
if (!this.isDomConnected) {
|
|
79
|
+
console.log('[PhonePreviewRcsChannelActions] DOM not connected, skipping UI update');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// Get contact values directly as strings
|
|
83
|
+
const phoneNumber = this.phone;
|
|
84
|
+
const websiteUrl = this.website;
|
|
85
|
+
const emailAddress = this.email;
|
|
86
|
+
console.log('[PhonePreviewRcsChannelActions] Current values:', {
|
|
87
|
+
phone: phoneNumber,
|
|
88
|
+
website: websiteUrl,
|
|
89
|
+
email: emailAddress,
|
|
90
|
+
});
|
|
91
|
+
// Update call action
|
|
92
|
+
if (phoneNumber !== '') {
|
|
93
|
+
this.#callAction.href = `tel:${phoneNumber}`;
|
|
94
|
+
this.#callAction.removeAttribute('inert');
|
|
95
|
+
console.log(`[PhonePreviewRcsChannelActions] Enabling call action with href: ${this.#callAction.href}`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.#callAction.href = '#';
|
|
99
|
+
this.#callAction.setAttribute('inert', '');
|
|
100
|
+
console.log('[PhonePreviewRcsChannelActions] Disabling call action');
|
|
101
|
+
}
|
|
102
|
+
// Update website action
|
|
103
|
+
if (websiteUrl !== '') {
|
|
104
|
+
this.#websiteAction.href = websiteUrl;
|
|
105
|
+
this.#websiteAction.removeAttribute('inert');
|
|
106
|
+
console.log(`[PhonePreviewRcsChannelActions] Enabling website action with href: ${this.#websiteAction.href}`);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
this.#websiteAction.href = '#';
|
|
110
|
+
this.#websiteAction.setAttribute('inert', '');
|
|
111
|
+
console.log('[PhonePreviewRcsChannelActions] Disabling website action');
|
|
112
|
+
}
|
|
113
|
+
// Update email action
|
|
114
|
+
if (emailAddress !== '') {
|
|
115
|
+
this.#emailAction.href = `mailto:${emailAddress}`;
|
|
116
|
+
this.#emailAction.removeAttribute('inert');
|
|
117
|
+
console.log(`[PhonePreviewRcsChannelActions] Enabling email action with href: ${this.#emailAction.href}`);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
this.#emailAction.href = '#';
|
|
121
|
+
this.#emailAction.setAttribute('inert', '');
|
|
122
|
+
console.log('[PhonePreviewRcsChannelActions] Disabling email action');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
defineCustomElement('sinch-labs-phone-preview-rcs-channel-actions', PhonePreviewRcsChannelActions);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import '@nectary/components/icon';
|
|
2
|
+
import { NectaryElement } from '../utils';
|
|
3
|
+
import '../phone-preview-rcs-channel-info-option';
|
|
4
|
+
import type React from 'react';
|
|
5
|
+
export declare class PhonePreviewRcsChannelInfo extends NectaryElement {
|
|
6
|
+
#private;
|
|
7
|
+
constructor();
|
|
8
|
+
connectedCallback(): void;
|
|
9
|
+
disconnectedCallback(): void;
|
|
10
|
+
static get observedAttributes(): never[];
|
|
11
|
+
attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
|
|
12
|
+
}
|
|
13
|
+
type Props = {};
|
|
14
|
+
type ElementProps = Partial<{
|
|
15
|
+
[K in keyof Props]: Props[K] | string;
|
|
16
|
+
}>;
|
|
17
|
+
declare global {
|
|
18
|
+
interface HTMLElementTagNameMap {
|
|
19
|
+
'sinch-labs-phone-preview-rcs-channel-info': ElementProps & HTMLElement;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
declare module 'react' {
|
|
23
|
+
namespace JSX {
|
|
24
|
+
interface IntrinsicElements {
|
|
25
|
+
'sinch-labs-phone-preview-rcs-channel-info': ElementProps & React.ClassAttributes<HTMLElement> & React.HTMLAttributes<HTMLElement>;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import '@nectary/components/icon';
|
|
2
|
+
import { defineCustomElement, NectaryElement } from '../utils';
|
|
3
|
+
import '../phone-preview-rcs-channel-info-option';
|
|
4
|
+
import templateHTML from './template.html';
|
|
5
|
+
const template = document.createElement('template');
|
|
6
|
+
template.innerHTML = templateHTML;
|
|
7
|
+
export class PhonePreviewRcsChannelInfo extends NectaryElement {
|
|
8
|
+
#optionSlot;
|
|
9
|
+
#controller = null;
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
const shadowRoot = this.attachShadow();
|
|
13
|
+
shadowRoot.appendChild(template.content.cloneNode(true));
|
|
14
|
+
this.#optionSlot = shadowRoot.querySelector('slot');
|
|
15
|
+
}
|
|
16
|
+
connectedCallback() {
|
|
17
|
+
super.connectedCallback();
|
|
18
|
+
this.#controller = new AbortController();
|
|
19
|
+
const { signal } = this.#controller;
|
|
20
|
+
this.#optionSlot.addEventListener('slotchange', this.#onSlotChange, { signal });
|
|
21
|
+
this.#onSlotChange();
|
|
22
|
+
}
|
|
23
|
+
disconnectedCallback() {
|
|
24
|
+
super.disconnectedCallback();
|
|
25
|
+
this.#controller?.abort();
|
|
26
|
+
this.#controller = null;
|
|
27
|
+
}
|
|
28
|
+
static get observedAttributes() {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
attributeChangedCallback(name, oldVal, newVal) {
|
|
32
|
+
if (oldVal === newVal) {
|
|
33
|
+
// No change needed
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
#onSlotChange = () => {
|
|
37
|
+
const options = this.#getOptionElements();
|
|
38
|
+
// If no options are provided, show placeholder data
|
|
39
|
+
if (options.length === 0) {
|
|
40
|
+
this.#showPlaceholders();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
#getOptionElements() {
|
|
44
|
+
const assignedElements = this.#optionSlot.assignedElements();
|
|
45
|
+
return assignedElements.filter((el) => el.tagName.toLowerCase() === 'sinch-labs-phone-preview-rcs-channel-info-option');
|
|
46
|
+
}
|
|
47
|
+
#showPlaceholders() {
|
|
48
|
+
// Create placeholder options when no content is provided
|
|
49
|
+
const placeholders = [
|
|
50
|
+
{ type: 'phone', contact: '+1234567890', label: 'Contact us' },
|
|
51
|
+
{ type: 'website', contact: 'https://company.com', label: 'Contact us' },
|
|
52
|
+
{ type: 'email', contact: 'mail@company.com', label: 'Contact us' },
|
|
53
|
+
];
|
|
54
|
+
placeholders.forEach(({ type, contact, label }) => {
|
|
55
|
+
const option = document.createElement('sinch-labs-phone-preview-rcs-channel-info-option');
|
|
56
|
+
option.setAttribute('type', type);
|
|
57
|
+
option.setAttribute('contact', contact);
|
|
58
|
+
option.setAttribute('label', label);
|
|
59
|
+
option.setAttribute('placeholder', '');
|
|
60
|
+
this.appendChild(option);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
defineCustomElement('sinch-labs-phone-preview-rcs-channel-info', PhonePreviewRcsChannelInfo);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import '@nectary/components/icon';
|
|
2
|
+
import { NectaryElement } from '../utils';
|
|
3
|
+
import type React from 'react';
|
|
4
|
+
export declare class PhonePreviewRcsChannelInfoOption extends NectaryElement {
|
|
5
|
+
#private;
|
|
6
|
+
constructor();
|
|
7
|
+
connectedCallback(): void;
|
|
8
|
+
static get observedAttributes(): string[];
|
|
9
|
+
attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
|
|
10
|
+
get type(): string;
|
|
11
|
+
set type(value: string);
|
|
12
|
+
get contact(): string;
|
|
13
|
+
set contact(value: string);
|
|
14
|
+
get label(): string;
|
|
15
|
+
set label(value: string);
|
|
16
|
+
get placeholder(): boolean;
|
|
17
|
+
set placeholder(value: boolean);
|
|
18
|
+
}
|
|
19
|
+
type Props = {
|
|
20
|
+
type?: 'phone' | 'website' | 'email';
|
|
21
|
+
contact?: string;
|
|
22
|
+
label?: string;
|
|
23
|
+
placeholder?: boolean;
|
|
24
|
+
};
|
|
25
|
+
type ElementProps = Partial<{
|
|
26
|
+
[K in keyof Props]: Props[K] | string;
|
|
27
|
+
}>;
|
|
28
|
+
declare global {
|
|
29
|
+
interface HTMLElementTagNameMap {
|
|
30
|
+
'sinch-labs-phone-preview-rcs-channel-info-option': ElementProps & HTMLElement;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
declare module 'react' {
|
|
34
|
+
namespace JSX {
|
|
35
|
+
interface IntrinsicElements {
|
|
36
|
+
'sinch-labs-phone-preview-rcs-channel-info-option': ElementProps & React.ClassAttributes<HTMLElement> & React.HTMLAttributes<HTMLElement>;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import '@nectary/components/icon';
|
|
2
|
+
import { defineCustomElement, NectaryElement } from '../utils';
|
|
3
|
+
import templateHTML from './template.html';
|
|
4
|
+
const template = document.createElement('template');
|
|
5
|
+
template.innerHTML = templateHTML;
|
|
6
|
+
export class PhonePreviewRcsChannelInfoOption extends NectaryElement {
|
|
7
|
+
#link;
|
|
8
|
+
#icon;
|
|
9
|
+
#contact;
|
|
10
|
+
#label;
|
|
11
|
+
constructor() {
|
|
12
|
+
super();
|
|
13
|
+
const shadowRoot = this.attachShadow();
|
|
14
|
+
shadowRoot.appendChild(template.content.cloneNode(true));
|
|
15
|
+
this.#link = shadowRoot.querySelector('a');
|
|
16
|
+
this.#icon = shadowRoot.querySelector('sinch-icon');
|
|
17
|
+
this.#contact = shadowRoot.querySelector('#contact');
|
|
18
|
+
this.#label = shadowRoot.querySelector('#label');
|
|
19
|
+
}
|
|
20
|
+
connectedCallback() {
|
|
21
|
+
super.connectedCallback();
|
|
22
|
+
this.#updateUI();
|
|
23
|
+
}
|
|
24
|
+
static get observedAttributes() {
|
|
25
|
+
return ['type', 'contact', 'label', 'placeholder'];
|
|
26
|
+
}
|
|
27
|
+
attributeChangedCallback(name, oldVal, newVal) {
|
|
28
|
+
if (oldVal === newVal) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
this.#updateUI();
|
|
32
|
+
}
|
|
33
|
+
get type() {
|
|
34
|
+
return this.getAttribute('type') ?? 'phone';
|
|
35
|
+
}
|
|
36
|
+
set type(value) {
|
|
37
|
+
this.setAttribute('type', value);
|
|
38
|
+
}
|
|
39
|
+
get contact() {
|
|
40
|
+
return this.getAttribute('contact') ?? '';
|
|
41
|
+
}
|
|
42
|
+
set contact(value) {
|
|
43
|
+
this.setAttribute('contact', value);
|
|
44
|
+
}
|
|
45
|
+
get label() {
|
|
46
|
+
return this.getAttribute('label') ?? '';
|
|
47
|
+
}
|
|
48
|
+
set label(value) {
|
|
49
|
+
this.setAttribute('label', value);
|
|
50
|
+
}
|
|
51
|
+
get placeholder() {
|
|
52
|
+
return this.hasAttribute('placeholder');
|
|
53
|
+
}
|
|
54
|
+
set placeholder(value) {
|
|
55
|
+
if (value) {
|
|
56
|
+
this.setAttribute('placeholder', '');
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
this.removeAttribute('placeholder');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
#updateUI() {
|
|
63
|
+
if (!this.isDomConnected) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const type = this.type;
|
|
67
|
+
const contact = this.contact;
|
|
68
|
+
const label = this.label;
|
|
69
|
+
const isPlaceholder = this.placeholder;
|
|
70
|
+
// Set icon based on type
|
|
71
|
+
let iconName = 'fa-phone';
|
|
72
|
+
let href = `tel:${contact}`;
|
|
73
|
+
switch (type) {
|
|
74
|
+
case 'website':
|
|
75
|
+
iconName = 'fa-earth-americas';
|
|
76
|
+
href = contact;
|
|
77
|
+
break;
|
|
78
|
+
case 'email':
|
|
79
|
+
iconName = 'envelope';
|
|
80
|
+
href = `mailto:${contact}`;
|
|
81
|
+
break;
|
|
82
|
+
case 'phone':
|
|
83
|
+
default:
|
|
84
|
+
iconName = 'fa-phone';
|
|
85
|
+
href = `tel:${contact}`;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
// Update icon
|
|
89
|
+
this.#icon.setAttribute('name', iconName);
|
|
90
|
+
// Update link
|
|
91
|
+
this.#link.href = href;
|
|
92
|
+
this.#link.target = '_blank';
|
|
93
|
+
// Set inert if it's a placeholder
|
|
94
|
+
if (isPlaceholder) {
|
|
95
|
+
this.#link.setAttribute('inert', '');
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.#link.removeAttribute('inert');
|
|
99
|
+
}
|
|
100
|
+
// Update contact text
|
|
101
|
+
this.#contact.textContent = contact;
|
|
102
|
+
// Update label text
|
|
103
|
+
this.#label.textContent = label;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
defineCustomElement('sinch-labs-phone-preview-rcs-channel-info-option', PhonePreviewRcsChannelInfoOption);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { NectaryElement } from '../utils';
|
|
2
|
+
import type React from 'react';
|
|
3
|
+
export declare class PhonePreviewRcsChannelOptions extends NectaryElement {
|
|
4
|
+
#private;
|
|
5
|
+
constructor();
|
|
6
|
+
connectedCallback(): void;
|
|
7
|
+
disconnectedCallback(): void;
|
|
8
|
+
static get observedAttributes(): never[];
|
|
9
|
+
attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
|
|
10
|
+
}
|
|
11
|
+
type Props = {};
|
|
12
|
+
type ElementProps = Partial<{
|
|
13
|
+
[K in keyof Props]: Props[K] | string;
|
|
14
|
+
}>;
|
|
15
|
+
declare global {
|
|
16
|
+
interface HTMLElementTagNameMap {
|
|
17
|
+
'sinch-labs-phone-preview-rcs-channel-options': ElementProps & HTMLElement;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
declare module 'react' {
|
|
21
|
+
namespace JSX {
|
|
22
|
+
interface IntrinsicElements {
|
|
23
|
+
'sinch-labs-phone-preview-rcs-channel-options': ElementProps & React.ClassAttributes<HTMLElement> & React.HTMLAttributes<HTMLElement>;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export {};
|