@capillarytech/creatives-library 8.0.354 → 8.0.355
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
|
@@ -79,6 +79,69 @@ const errorMessageForTags = {
|
|
|
79
79
|
TAG_BRACKET_COUNT_MISMATCH_ERROR: 'tagBracketCountMismatchError'
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
+
// Isolated input for EMAIL template-name: only this tiny component re-renders on each keystroke.
|
|
83
|
+
// formData is updated only on blur (onCommit), eliminating all re-renders during typing.
|
|
84
|
+
class HighFreqInput extends React.Component {
|
|
85
|
+
constructor(props) {
|
|
86
|
+
super(props);
|
|
87
|
+
this.state = { localValue: props.value || '' };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
componentDidUpdate(prevProps) {
|
|
91
|
+
if (prevProps.value !== this.props.value && this.state.localValue !== this.props.value) {
|
|
92
|
+
this.setState({ localValue: this.props.value || '' });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
handleChange = (e) => {
|
|
97
|
+
this.setState({ localValue: e.target.value });
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
handleBlur = (e) => {
|
|
101
|
+
this.props.onCommit(this.state.localValue);
|
|
102
|
+
if (this.props.onBlur) this.props.onBlur(e);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
render() {
|
|
106
|
+
const { value: _v, onCommit: _oc, onBlur: _ob, ...rest } = this.props;
|
|
107
|
+
return <CapInput {...rest} value={this.state.localValue} onChange={this.handleChange} onBlur={this.handleBlur} />;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Isolated wrapper for EMAIL template-subject: blur-only commit, same as HighFreqInput.
|
|
112
|
+
class HighFreqTagInput extends React.Component {
|
|
113
|
+
constructor(props) {
|
|
114
|
+
super(props);
|
|
115
|
+
this.state = { localInputValue: props.inputValue || '' };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
componentDidUpdate(prevProps) {
|
|
119
|
+
if (prevProps.inputValue !== this.props.inputValue && this.state.localInputValue !== this.props.inputValue) {
|
|
120
|
+
this.setState({ localInputValue: this.props.inputValue || '' });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
handleInputChange = (e) => {
|
|
125
|
+
this.setState({ localInputValue: e.target.value });
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
handleBlur = () => {
|
|
129
|
+
this.props.onCommit(this.state.localInputValue);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
render() {
|
|
133
|
+
const { inputValue: _iv, onCommit: _oc, inputOnChange: _ic, ...rest } = this.props;
|
|
134
|
+
return (
|
|
135
|
+
<CapTagListWithInput
|
|
136
|
+
{...rest}
|
|
137
|
+
inputValue={this.state.localInputValue}
|
|
138
|
+
inputOnChange={this.handleInputChange}
|
|
139
|
+
inputProps={{ ...(this.props.inputProps || {}), onBlur: this.handleBlur }}
|
|
140
|
+
/>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
82
145
|
class FormBuilder extends React.Component { // eslint-disable-line react/prefer-stateless-function
|
|
83
146
|
constructor(props) {
|
|
84
147
|
super(props);
|
|
@@ -352,6 +415,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
352
415
|
this.setState({tabCount: nextProps.tabCount});
|
|
353
416
|
}
|
|
354
417
|
if (nextProps.startValidation && nextProps.startValidation !== false && this.props.startValidation !== nextProps.startValidation) {
|
|
418
|
+
if (this.debouncedUpdateFormData) this.debouncedUpdateFormData.flush();
|
|
355
419
|
this.setState({checkValidation: true});
|
|
356
420
|
this.validateForm(null, null, true, true, () => {
|
|
357
421
|
//triggering the saveFormData or onSubmit when validation sets isFormValid to TRUE
|
|
@@ -3410,26 +3474,25 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3410
3474
|
? formatMessage(messages.personalizationTagsErrorMessage)
|
|
3411
3475
|
: (errorType === TAG_BRACKET_COUNT_MISMATCH_ERROR ? formatMessage(globalMessages.unbalanacedCurlyBraces) : (val.errorMessage && ifError ? val.errorMessage : ''));
|
|
3412
3476
|
if (styling === 'semantic') {
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
!aiContentBotDisabled && (
|
|
3477
|
+
const isEmailStandaloneHighFreq = val.standalone && this.props.schema?.channel?.toUpperCase() === EMAIL;
|
|
3478
|
+
if (isEmailStandaloneHighFreq) {
|
|
3479
|
+
columns.push(
|
|
3480
|
+
<CapColumn key={val.id} span={val.width} offset={val.offset} style={val.style || {}}>
|
|
3481
|
+
<HighFreqInput
|
|
3482
|
+
id={val.id}
|
|
3483
|
+
errorMessage={errorMessageText}
|
|
3484
|
+
label={val.label}
|
|
3485
|
+
inductiveText={val.inductiveText}
|
|
3486
|
+
className={`input-primary chart-name-input${ifError ? ' error' : ''}`}
|
|
3487
|
+
style={val.style ? val.style : {}}
|
|
3488
|
+
placeholder={val.placeholder}
|
|
3489
|
+
onCommit={(newValue) => this.performFormDataUpdate(newValue, val)}
|
|
3490
|
+
onBlur={(e) => this.handleFieldBlur(e, val)}
|
|
3491
|
+
value={value || ""}
|
|
3492
|
+
disabled={val.disabled}
|
|
3493
|
+
size={val.size || "default"}
|
|
3494
|
+
/>
|
|
3495
|
+
{!aiContentBotDisabled && (
|
|
3433
3496
|
<CapAskAira.ContentGenerationBot
|
|
3434
3497
|
text={value || ""}
|
|
3435
3498
|
setText={this.handleSetText.bind(this, val)}
|
|
@@ -3438,12 +3501,48 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3438
3501
|
rootStyle={{
|
|
3439
3502
|
bottom: "0.2rem",
|
|
3440
3503
|
right: "0.2rem",
|
|
3441
|
-
left: "auto",
|
|
3504
|
+
left: "auto",
|
|
3442
3505
|
}}
|
|
3443
3506
|
/>
|
|
3444
3507
|
)}
|
|
3445
|
-
|
|
3446
|
-
|
|
3508
|
+
</CapColumn>
|
|
3509
|
+
);
|
|
3510
|
+
} else {
|
|
3511
|
+
columns.push(
|
|
3512
|
+
<CapColumn key={val.id} span={val.width} offset={val.offset} style={val.style || {}}>
|
|
3513
|
+
<CapInput
|
|
3514
|
+
id={val.id}
|
|
3515
|
+
errorMessage={errorMessageText}
|
|
3516
|
+
label={val.label}
|
|
3517
|
+
inductiveText={val.inductiveText}
|
|
3518
|
+
className={`input-primary chart-name-input${ifError ? ' error' : ''}`}
|
|
3519
|
+
// fluid={val.fluid}
|
|
3520
|
+
style={val.style ? val.style : {}}
|
|
3521
|
+
placeholder={val.placeholder}
|
|
3522
|
+
onChange={(e) => this.updateFormData(e.target.value, val)}
|
|
3523
|
+
onBlur={(e) => this.handleFieldBlur(e, val)}
|
|
3524
|
+
value={value || ""}
|
|
3525
|
+
defaultValue={isVersionEnable ? this.state.formData[`${this.state.currentTab - 1}`][val.id] : this.state.formData[val.id]}
|
|
3526
|
+
disabled={val.disabled}
|
|
3527
|
+
size={val.size || "default"}
|
|
3528
|
+
/>
|
|
3529
|
+
{this.props.schema?.channel === EMAIL &&
|
|
3530
|
+
!aiContentBotDisabled && (
|
|
3531
|
+
<CapAskAira.ContentGenerationBot
|
|
3532
|
+
text={value || ""}
|
|
3533
|
+
setText={this.handleSetText.bind(this, val)}
|
|
3534
|
+
iconPlacement="float-br"
|
|
3535
|
+
iconSize="1.6rem"
|
|
3536
|
+
rootStyle={{
|
|
3537
|
+
bottom: "0.2rem",
|
|
3538
|
+
right: "0.2rem",
|
|
3539
|
+
left: "auto",
|
|
3540
|
+
}}
|
|
3541
|
+
/>
|
|
3542
|
+
)}
|
|
3543
|
+
</CapColumn>
|
|
3544
|
+
);
|
|
3545
|
+
}
|
|
3447
3546
|
}
|
|
3448
3547
|
break;
|
|
3449
3548
|
|
|
@@ -3684,37 +3783,48 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3684
3783
|
isBEEAppEnableForCapTagList === false ||
|
|
3685
3784
|
channelForCapTagList !== 'EMAIL'
|
|
3686
3785
|
) {
|
|
3786
|
+
const isEmailStandaloneSubject = val.standalone && channelForCapTagList === EMAIL && val.id === 'template-subject';
|
|
3787
|
+
const tagListProps = {
|
|
3788
|
+
key: `input-${val.id}`,
|
|
3789
|
+
inputId: val.id,
|
|
3790
|
+
inputValue: this.state.formData[val.id] || '',
|
|
3791
|
+
inputPlaceholder: val.placeholder || '',
|
|
3792
|
+
inputErrorMessage: val.errorMessage && ifError ? val.errorMessage : '',
|
|
3793
|
+
inputRequired: val.required || false,
|
|
3794
|
+
inputDisabled: val.disabled || false,
|
|
3795
|
+
headingText: val.label || '',
|
|
3796
|
+
headingStyle: val.headingStyle || { marginTop: '3%', marginRight: '79%' },
|
|
3797
|
+
headingType: "h4",
|
|
3798
|
+
onTagSelect: (data) => this.callChildEvent(data, val, 'onTagSelect'),
|
|
3799
|
+
onContextChange: this.props.onContextChange,
|
|
3800
|
+
location: this.props.location,
|
|
3801
|
+
tags: this.props.tags ? this.props.tags : [],
|
|
3802
|
+
injectedTags: this.props.injectedTags ? this.props.injectedTags : {},
|
|
3803
|
+
className: val.className ? val.className : '',
|
|
3804
|
+
userLocale: this.state.translationLang,
|
|
3805
|
+
selectedOfferDetails: this.props.selectedOfferDetails,
|
|
3806
|
+
eventContextTags: this.props?.eventContextTags,
|
|
3807
|
+
waitEventContextTags: this.props?.waitEventContextTags,
|
|
3808
|
+
moduleFilterEnabled: moduleFilterEnabledForCapTagList,
|
|
3809
|
+
containerStyle: val.style || {},
|
|
3810
|
+
inputProps: val.inputProps || {},
|
|
3811
|
+
showInput: val.showInput !== false,
|
|
3812
|
+
showTagList: val.showTagList !== false,
|
|
3813
|
+
restrictPersonalization: this.props.restrictPersonalization,
|
|
3814
|
+
};
|
|
3687
3815
|
columns.push(
|
|
3688
3816
|
<CapColumn key={`input-${val.id}`} offset={val.offset} span={val.width ? val.width : ''} style={val.style ? val.style : {marginBottom: '16px'}}>
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
headingType="h4"
|
|
3701
|
-
onTagSelect={(data) => this.callChildEvent(data, val, 'onTagSelect')}
|
|
3702
|
-
onContextChange={this.props.onContextChange}
|
|
3703
|
-
location={this.props.location}
|
|
3704
|
-
tags={this.props.tags ? this.props.tags : []}
|
|
3705
|
-
injectedTags={this.props.injectedTags ? this.props.injectedTags : {}}
|
|
3706
|
-
className={val.className ? val.className : ''}
|
|
3707
|
-
userLocale={this.state.translationLang}
|
|
3708
|
-
selectedOfferDetails={this.props.selectedOfferDetails}
|
|
3709
|
-
eventContextTags={this.props?.eventContextTags}
|
|
3710
|
-
waitEventContextTags={this.props?.waitEventContextTags}
|
|
3711
|
-
moduleFilterEnabled={moduleFilterEnabledForCapTagList}
|
|
3712
|
-
containerStyle={val.style || {}}
|
|
3713
|
-
inputProps={val.inputProps || {}}
|
|
3714
|
-
showInput={val.showInput !== false}
|
|
3715
|
-
showTagList={val.showTagList !== false}
|
|
3716
|
-
restrictPersonalization={this.props.restrictPersonalization}
|
|
3717
|
-
/>
|
|
3817
|
+
{isEmailStandaloneSubject ? (
|
|
3818
|
+
<HighFreqTagInput
|
|
3819
|
+
{...tagListProps}
|
|
3820
|
+
onCommit={(newValue) => this.performFormDataUpdate(newValue, val)}
|
|
3821
|
+
/>
|
|
3822
|
+
) : (
|
|
3823
|
+
<CapTagListWithInput
|
|
3824
|
+
{...tagListProps}
|
|
3825
|
+
inputOnChange={(e) => this.updateFormData(e.target.value, val)}
|
|
3826
|
+
/>
|
|
3827
|
+
)}
|
|
3718
3828
|
</CapColumn>
|
|
3719
3829
|
);
|
|
3720
3830
|
}
|
|
@@ -1,4 +1,48 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
|
|
3
|
+
// Isolated input for the email template name field.
|
|
4
|
+
// Manages its own value in local state so keystrokes only re-render this
|
|
5
|
+
// small component, not the entire CreativesContainer → Email → FormBuilder tree.
|
|
6
|
+
class TemplateNameInputField extends React.Component {
|
|
7
|
+
constructor(props) {
|
|
8
|
+
super(props);
|
|
9
|
+
this.state = { localValue: props.initialValue || '' };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
componentDidUpdate(prevProps) {
|
|
13
|
+
// Sync from props only when the external value changed AND the user hasn't
|
|
14
|
+
// diverged from the previous prop value. This handles async data-load in edit
|
|
15
|
+
// mode without overwriting what the user is actively typing.
|
|
16
|
+
if (
|
|
17
|
+
prevProps.initialValue !== this.props.initialValue &&
|
|
18
|
+
this.state.localValue === (prevProps.initialValue || '')
|
|
19
|
+
) {
|
|
20
|
+
this.setState({ localValue: this.props.initialValue || '' });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
handleChange = (ev) => {
|
|
25
|
+
const { value } = ev.currentTarget;
|
|
26
|
+
this.setState({ localValue: value });
|
|
27
|
+
if (this.props.onChange) this.props.onChange(value);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
handleBlur = () => {
|
|
31
|
+
if (this.props.onBlur) this.props.onBlur(this.state.localValue);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
render() {
|
|
35
|
+
const { onChange: _onChange, initialValue: _initialValue, onBlur: _ob, ...rest } = this.props;
|
|
36
|
+
return (
|
|
37
|
+
<CapInput
|
|
38
|
+
{...rest}
|
|
39
|
+
value={this.state.localValue}
|
|
40
|
+
onChange={this.handleChange}
|
|
41
|
+
onBlur={this.handleBlur}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
2
46
|
import PropTypes from 'prop-types';
|
|
3
47
|
import {
|
|
4
48
|
CAP_SPACE_16, CAP_SPACE_32, CAP_SPACE_56, CAP_SPACE_64,
|
|
@@ -1753,30 +1797,22 @@ export class Creatives extends React.Component {
|
|
|
1753
1797
|
} />
|
|
1754
1798
|
)
|
|
1755
1799
|
|
|
1756
|
-
templateNameComponentInput = ({ formData, onFormDataChange, name }) =>
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
onChange={(ev) => {
|
|
1773
|
-
const { value } = ev.currentTarget;
|
|
1774
|
-
// Use optimized update for better performance
|
|
1775
|
-
this.updateTemplateNameImmediately(value, formData, onFormDataChange);
|
|
1776
|
-
}}
|
|
1777
|
-
/>
|
|
1778
|
-
);
|
|
1779
|
-
}
|
|
1800
|
+
templateNameComponentInput = ({ formData, onFormDataChange, name }) => (
|
|
1801
|
+
<TemplateNameInputField
|
|
1802
|
+
initialValue={name}
|
|
1803
|
+
suffix={<span />}
|
|
1804
|
+
onBlur={(committedValue) => {
|
|
1805
|
+
this.performTemplateNameUpdate(committedValue, formData, onFormDataChange);
|
|
1806
|
+
this.setState({ isEditName: false });
|
|
1807
|
+
}}
|
|
1808
|
+
onChange={(value) => {
|
|
1809
|
+
const isEmptyTemplateName = !value.trim();
|
|
1810
|
+
if (this.state.isTemplateNameEmpty !== isEmptyTemplateName) {
|
|
1811
|
+
this.setState({ isTemplateNameEmpty: isEmptyTemplateName });
|
|
1812
|
+
}
|
|
1813
|
+
}}
|
|
1814
|
+
/>
|
|
1815
|
+
)
|
|
1780
1816
|
|
|
1781
1817
|
showTemplateName = ({ formData, onFormDataChange }) => { //gets called from email/index after template data is fetched
|
|
1782
1818
|
const {
|