@subiz/widget-v4 4.1.135 → 4.1.136

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.
@@ -19,6 +19,14 @@
19
19
  @desktop-chat-button-margin-x: '@desktop_button.margin_x'; //20px;
20
20
  @desktop-chat-button-margin-y: '@desktop_button.margin_y'; // 20px;
21
21
 
22
+ @form-page-background-color: '@form_background_color';
23
+ @form-page-header-font-size: '@form_header_font_size';
24
+ @form-page-header-font-family: '@form_header_font_family';
25
+ @form-page-desc-font-size: '@form_desc_font_size';
26
+ @form-page-desc-font-family: '@form_desc_font_family';
27
+ @form-page-input-label-font-size: '@form_input_label_font_size';
28
+ @form-page-input-label-font-family: '@form_input_label_font_family';
29
+
22
30
  @mobile-chat-button-width: '@mobile_button.width'; // 70px;
23
31
  @mobile-chat-button-height: '@mobile_button.height'; //70px;
24
32
  @mobile-chat-button-background: '@background'; // '@mobile_button.background';
@@ -151,6 +159,9 @@ body div#sbz92 {
151
159
  .text-xl {
152
160
  font-size: 20px;
153
161
  }
162
+ .text-sm {
163
+ font-size: 14px;
164
+ }
154
165
  .text-center {
155
166
  text-align: center;
156
167
  }
@@ -0,0 +1,306 @@
1
+ @primary-color: '@primary_color'; //
2
+ @form-page-background-color: '@form_background_color';
3
+ @form-page-header-font-size: '@form_header_font_size';
4
+ @form-page-header-font-family: '@form_header_font_family';
5
+ @form-page-desc-font-size: '@form_desc_font_size';
6
+ @form-page-desc-font-family: '@form_desc_font_family';
7
+ @form-page-input-label-font-size: '@form_input_label_font_size';
8
+ @form-page-input-label-font-family: '@form_input_label_font_family';
9
+ @text-color: #fff;
10
+ @font-family-sans-serif: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif,
11
+ 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
12
+ @text-muted: #6c757d;
13
+ @font-size-base: 16px; // Assumes the browser default, typically `16px`
14
+ @spacer: 15px;
15
+ @spacer1: @spacer * 0.5; // 3.75px
16
+ @spacer2: @spacer * 1; // 7.5px
17
+ @spacer3: @spacer * 1.5; // 15px;
18
+ @spacer4: @spacer * 2; // 22.5px
19
+
20
+ // stop client css rule on all elements inside #subiz
21
+ #sbz92 * {
22
+ all: unset !important;
23
+ }
24
+
25
+ #sbz92 {
26
+ #makeImportant() !important;
27
+ }
28
+
29
+ // prevent #subiz inheriting client site styles
30
+ div#sbz92 {
31
+ all: initial;
32
+ }
33
+
34
+ // prevent #subiz inheriting client site !important styles
35
+ body div#sbz92 {
36
+ all: initial !important;
37
+ }
38
+
39
+ @import './font.less';
40
+ #makeImportant() {
41
+ @import 'base.less';
42
+
43
+ * {
44
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
45
+ -webkit-tap-highlight-color: transparent;
46
+
47
+ font-family: @font-family-sans-serif;
48
+ font-size: @font-size-base;
49
+
50
+ -webkit-box-sizing: border-box; // Safari/Chrome, other WebKit
51
+ -moz-box-sizing: border-box; // Firefox, other Gecko
52
+ box-sizing: border-box; // Opera/IE 8+
53
+
54
+ margin: 0;
55
+ padding: 0;
56
+ border: 0;
57
+ vertical-align: baseline;
58
+ }
59
+
60
+ button:focus {
61
+ outline: none;
62
+ }
63
+
64
+ .pos-relative {
65
+ position: relative;
66
+ }
67
+
68
+ // flex utils
69
+ .flex-row {
70
+ flex-direction: row;
71
+ }
72
+ .flex-column {
73
+ flex-direction: column;
74
+ }
75
+ .flex-grow-1 {
76
+ flex-grow: 1;
77
+ }
78
+ .align-self-end {
79
+ align-self: flex-end;
80
+ }
81
+ .align-items-end {
82
+ align-items: flex-end;
83
+ }
84
+ .justify-content-center {
85
+ justify-content: center;
86
+ }
87
+ .h-full {
88
+ height: 100%;
89
+ }
90
+ .w-full {
91
+ width: 100%;
92
+ }
93
+ .text-muted {
94
+ .color(@text-muted);
95
+ }
96
+ .text-lg {
97
+ font-size: 18px;
98
+ }
99
+ .text-xl {
100
+ font-size: 20px;
101
+ }
102
+ .text-center {
103
+ text-align: center;
104
+ }
105
+ .text-danger {
106
+ .color(@red);
107
+ }
108
+ .red {
109
+ .color(@red);
110
+ }
111
+
112
+ .text-italic {
113
+ font-style: italic;
114
+ }
115
+
116
+ // display utils
117
+ .d-none {
118
+ display: none;
119
+ }
120
+
121
+ .d-flex {
122
+ display: flex;
123
+ }
124
+
125
+ .d-inline-block {
126
+ display: inline-block;
127
+ }
128
+
129
+ // spacing utils
130
+ .ml-1 {
131
+ margin-left: @spacer1;
132
+ }
133
+ .pl-2 {
134
+ padding-left: @spacer2;
135
+ }
136
+ .pr-2 {
137
+ padding-right: @spacer2;
138
+ }
139
+ .pb-2 {
140
+ padding-bottom: @spacer2;
141
+ }
142
+ .mt-1 {
143
+ margin-top: @spacer1;
144
+ }
145
+ .mt-2 {
146
+ margin-top: @spacer2;
147
+ }
148
+ .ml-2 {
149
+ margin-left: @spacer2;
150
+ }
151
+ .mr-2 {
152
+ margin-right: @spacer2;
153
+ }
154
+ .pt-3 {
155
+ padding-top: @spacer3;
156
+ }
157
+ .pl-3 {
158
+ padding-left: @spacer3;
159
+ }
160
+ .pr-3 {
161
+ padding-right: @spacer3;
162
+ }
163
+ .pb-3 {
164
+ padding-bottom: @spacer3;
165
+ }
166
+ .mt-3 {
167
+ margin-top: @spacer3;
168
+ }
169
+ .ml-3 {
170
+ margin-left: @spacer3;
171
+ }
172
+ .mr-3 {
173
+ margin-right: @spacer3;
174
+ }
175
+ .mb-3 {
176
+ margin-bottom: @spacer3;
177
+ }
178
+ .mb-2 {
179
+ margin-bottom: @spacer2;
180
+ }
181
+ .mb-1 {
182
+ margin-bottom: @spacer1;
183
+ }
184
+
185
+ .muted {
186
+ .color(@text-muted);
187
+ }
188
+
189
+ .i {
190
+ font-style: italic;
191
+ }
192
+
193
+ .align-items-center {
194
+ align-items: center;
195
+ }
196
+
197
+ .text-truncate {
198
+ text-overflow: ellipsis;
199
+ white-space: nowrap;
200
+ overflow: hidden;
201
+ }
202
+
203
+ .flex_1 {
204
+ flex: 1;
205
+ flex-shrink: 0;
206
+ }
207
+
208
+ .justify-content-between {
209
+ justify-content: space-between;
210
+ }
211
+
212
+ .no-shrink {
213
+ flex-shrink: 0;
214
+ }
215
+ .form-page {
216
+ max-width: 700px;
217
+ width: 100%;
218
+ margin: 0 auto;
219
+ padding: 40px 20px;
220
+ }
221
+
222
+ .form-page-header {
223
+ margin-bottom: 20px;
224
+ }
225
+
226
+ .form-page-title {
227
+ font-size: @form-page-header-font-size;
228
+ font-family: @form-page-header-font-family;
229
+ }
230
+
231
+ .form-page-desc {
232
+ color: @text-muted;
233
+ margin-top: 2px;
234
+ font-size: @form-page-desc-font-size;
235
+ font-family: @form-page-desc-font-family;
236
+ }
237
+
238
+ .form-page-logo-wrapper {
239
+ margin-bottom: 30px;
240
+ }
241
+
242
+ img.form-page-logo {
243
+ width: 200px;
244
+ height: 45px;
245
+ }
246
+
247
+ .form-page-logo-text {
248
+ color: @primary-color;
249
+ font-size: 18px;
250
+ font-weight: 500;
251
+ &:hover {
252
+ color: @primary-color;
253
+ }
254
+ }
255
+
256
+ .form-page .form-component .label {
257
+ font-size: @form-page-input-label-font-size;
258
+ font-family: @form-page-input-label-font-family;
259
+ }
260
+
261
+ .form-page-body {
262
+ width: 100%;
263
+ height: 100%;
264
+ overflow: auto;
265
+ background-color: @form-page-background-color;
266
+ }
267
+
268
+ .btn--start-convo {
269
+ border-radius: 7px;
270
+ flex-shrink: 0;
271
+ display: flex;
272
+ align-items: center;
273
+ justify-content: center;
274
+ padding-left: 32px;
275
+ padding-right: 32px;
276
+ height: 40px;
277
+ .color(@text-color);
278
+ font-size: 16px;
279
+ font-weight: 500;
280
+ line-height: 20px;
281
+ text-align: center;
282
+ background: @primary-color;
283
+ // max-width: 270px;
284
+ width: 100%;
285
+ text-overflow: ellipsis;
286
+ white-space: nowrap;
287
+ overflow: hidden;
288
+
289
+ transition: filter 250ms ease;
290
+
291
+ &::after {
292
+ display: none;
293
+ }
294
+ &:hover {
295
+ filter: brightness(1.1);
296
+ }
297
+ }
298
+
299
+ .btn--start-convo:disabled {
300
+ opacity: 0.5;
301
+ cursor: default;
302
+ pointer-events: none;
303
+ }
304
+
305
+ @import 'form.less';
306
+ }
@@ -441,20 +441,6 @@ select {
441
441
  background-color: #fff;
442
442
  }
443
443
 
444
- .form-page {
445
- max-width: 700px;
446
- width: 100%;
447
- margin: 0 auto;
448
- padding: 40px 20px;
449
- }
450
-
451
- .form-page-header {
452
- margin-bottom: 30px;
453
- font-size: 18px;
454
- font-weight: 500;
455
- color: #ccc;
456
- }
457
-
458
444
  .form-wrapper.form-wrapper-attachment.form {
459
445
  padding: 12px 15px;
460
446
  margin-top: 5px;
@@ -260,6 +260,7 @@ class Form2 extends _common.Com {
260
260
  render() {
261
261
  let components = _index.default.get(this.props.formSetting, 'components') || [];
262
262
  let title = _index.default.get(this.props.formSetting, 'title') || '';
263
+ let desc = _index.default.get(this.props.formSetting, 'description') || '';
263
264
  let cls = 'form-wrapper form';
264
265
  if (this.props.mode === 'attachment') cls += ' form-wrapper-attachment';
265
266
  return (0, _preact.h)("div", {
@@ -268,9 +269,11 @@ class Form2 extends _common.Com {
268
269
  class: "form-component"
269
270
  }, this.props.t('you_have_submitted_info_successfully')), (0, _preact.h)("form", {
270
271
  onSubmit: this.onSubmit
271
- }, (0, _preact.h)("div", {
272
+ }, this.props.mode === 'attachment' && (title || desc) && (0, _preact.h)("div", {
272
273
  class: "form-component form-component-block"
273
- }, title), _index.default.map(components, this.renderFormComponent)));
274
+ }, title && (0, _preact.h)("div", null, title), desc && (0, _preact.h)("div", {
275
+ class: "text-sm text-muted"
276
+ }, desc)), _index.default.map(components, this.renderFormComponent)));
274
277
  }
275
278
  }
276
279
  exports.default = Form2;
@@ -12,10 +12,82 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
12
12
  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
13
13
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
14
14
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
15
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
16
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
17
+ const promiseMainCSS = (() => Promise.resolve().then(() => _interopRequireWildcard(require('./css'))))();
15
18
  class FormPage extends _common.Com {
16
19
  constructor(props) {
17
20
  super(props);
21
+ _defineProperty(this, "waitForFont", async fonts => {
22
+ if (_index.default.size(fonts) == 0) return;
23
+ let ready = {};
24
+ for (let t = 0; t < 10; t++) {
25
+ for (let i = 0; i < fonts.length; i++) {
26
+ try {
27
+ if (document.fonts.check('normal 16px ' + fonts[i])) {
28
+ ready[fonts[i]] = true;
29
+ }
30
+ console.log('ready', ready, Object.keys(ready), fonts.length, Object.keys(ready) === fonts.length);
31
+ if (_index.default.size(Object.keys(ready)) === fonts.length) {
32
+ return;
33
+ }
34
+ } catch (e) {}
35
+ }
36
+ await _index.default.sleep(200);
37
+ }
38
+ return;
39
+ });
40
+ _defineProperty(this, "initCss", async () => {
41
+ let css = (await promiseMainCSS).default.default.toString();
42
+ let chatbox = _index.default.find(this.props.store.matchPlugin(), plugin => plugin.type === 'chatbox') || {};
43
+ chatbox = chatbox.chatbox || {};
44
+ let setting = chatbox;
45
+ let outcss = _index.default.replaceCssVariable(css, setting);
46
+ let $cssStyleDiv = document.createElement('style');
47
+ document.head.appendChild($cssStyleDiv);
48
+ $cssStyleDiv.type = 'text/css';
49
+ $cssStyleDiv.id = 'subiz-style-for-form';
50
+ this.cssDiv = $cssStyleDiv;
51
+ _index.default.setCss('form', $cssStyleDiv, outcss);
52
+ });
53
+ _defineProperty(this, "insertCss", async (chatbox, formSetting) => {
54
+ let css = (await promiseMainCSS).default.default.toString();
55
+ let setting = chatbox;
56
+ if (formSetting.primary_color) setting.primary_color = formSetting.primary_color;
57
+ let headerFont = _index.default.get(formSetting, 'header_style.font_family');
58
+ let descFont = _index.default.get(formSetting, 'text_style.font_family');
59
+ let inputLabelFont = _index.default.get(formSetting, 'input_style.font_family');
60
+ setting.form_background_color = formSetting.background_color;
61
+ setting.form_header_font_size = _index.default.get(formSetting, 'header_style.font_size') || '20';
62
+ setting.form_header_font_family = headerFont;
63
+ setting.form_desc_font_size = _index.default.get(formSetting, 'text_style.font_size') || '16';
64
+ setting.form_desc_font_family = descFont;
65
+ setting.form_input_label_font_size = _index.default.get(formSetting, 'input_style.font_size') || '14';
66
+ setting.form_input_label_font_family = inputLabelFont;
67
+ let outcss = _index.default.replaceCssVariable(css, setting);
68
+ let fonts = [];
69
+ if (inputLabelFont && !fonts.includes(inputLabelFont)) fonts.push(inputLabelFont);
70
+ if (headerFont && !fonts.includes(headerFont)) fonts.push(headerFont);
71
+ if (descFont && !fonts.includes(descFont)) fonts.push(descFont);
72
+ if (_index.default.size(fonts)) {
73
+ _index.default.preloadWebfonts(fonts, [400, 500]);
74
+ await this.waitForFont(fonts);
75
+ }
76
+ _index.default.setCss('form', this.cssDiv, outcss);
77
+ });
18
78
  _defineProperty(this, "fetchFormSetting", async () => {
79
+ let chatbox = _index.default.find(this.props.store.matchPlugin(), plugin => plugin.type === 'chatbox') || {};
80
+ chatbox = chatbox.chatbox || {};
81
+ if (this.props.previewJson) {
82
+ let json = atob(this.props.previewJson);
83
+ json = decodeURIComponent(json);
84
+ json = _index.default.parseJSON(json);
85
+ await this.insertCss(chatbox, json);
86
+ this.setState({
87
+ formSetting: json
88
+ });
89
+ return;
90
+ }
19
91
  if (this.state.loading) return;
20
92
  this.setState({
21
93
  loading: true
@@ -31,6 +103,7 @@ class FormPage extends _common.Com {
31
103
  let formSetting = _index.default.get(res, 'body.form') || {};
32
104
  let submission = _index.default.get(res, 'body.form_submission') || {};
33
105
  let isSubmited = _index.default.size(submission) > 0;
106
+ await this.insertCss(chatbox, formSetting);
34
107
  this.setState({
35
108
  loading: false,
36
109
  loadError: false,
@@ -50,6 +123,12 @@ class FormPage extends _common.Com {
50
123
  this.setState({
51
124
  errorMessage: ''
52
125
  });
126
+ if (this.props.previewJson) {
127
+ this.setState({
128
+ isSubmited: true
129
+ });
130
+ return;
131
+ }
53
132
  if (this.state.submiting) return;
54
133
  this.setState({
55
134
  submiting: true
@@ -77,7 +156,8 @@ class FormPage extends _common.Com {
77
156
  loadError: ''
78
157
  };
79
158
  }
80
- componentDidMount() {
159
+ async componentDidMount() {
160
+ await this.initCss();
81
161
  this.fetchFormSetting();
82
162
  }
83
163
  render() {
@@ -111,11 +191,27 @@ class FormPage extends _common.Com {
111
191
  onClick: this.fetchFormSetting
112
192
  }, this.props.t('try_again'))));
113
193
  }
194
+ let brandLogo = _index.default.get(formSetting, 'logo.url');
114
195
  return (0, _preact.h)("div", {
196
+ class: "form-page-body"
197
+ }, (0, _preact.h)("div", {
115
198
  class: "form-page"
116
199
  }, (0, _preact.h)("div", {
200
+ class: "form-page-logo-wrapper"
201
+ }, (0, _preact.h)("a", {
202
+ href: formSetting.brand_url || 'javascript:;'
203
+ }, brandLogo ? (0, _preact.h)("img", {
204
+ src: brandLogo,
205
+ class: "form-page-logo"
206
+ }) : (0, _preact.h)("div", {
207
+ class: "form-page-logo-text"
208
+ }, _index.default.get(this.props.store.matchAccount(), 'name')))), (formSetting.title || formSetting.description) && (0, _preact.h)("div", {
117
209
  class: "form-page-header"
118
- }, 'HEADER'), (0, _preact.h)(_Form.default, {
210
+ }, (0, _preact.h)("div", {
211
+ class: "form-page-title"
212
+ }, formSetting.title), formSetting.description && (0, _preact.h)("div", {
213
+ class: "form-page-desc"
214
+ }, formSetting.description)), (0, _preact.h)(_Form.default, {
119
215
  store: this.props.store,
120
216
  formSetting: formSetting,
121
217
  t: this.props.t,
@@ -125,7 +221,7 @@ class FormPage extends _common.Com {
125
221
  submiting: submiting,
126
222
  onChangeFormValues: this.onChangeFormValues,
127
223
  onSubmit: this.onSubmit
128
- }));
224
+ })));
129
225
  }
130
226
  }
131
227
  exports.default = FormPage;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _default = exports.default = require('css-loader!less-loader!../../assets/less/form-page.less');
@@ -29,33 +29,25 @@ var $link = document.createElement('link');
29
29
  $link.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap';
30
30
  $link.rel = 'stylesheet';
31
31
  document.head.append($link);
32
- _index.default.preloadWebfonts(['Inter'], [400, 500]);
32
+ const PREVIEW_B64 = 'JTdCJTIyaWQlMjIlM0ElMjJhdHNocnBxeHp1dWZ6emRrenF1el9kbGJnd3lpYiUyMiUyQyUyMnRpdGxlJTIyJTNBJTIyJUM0JTkwJUUxJUJBJUI3dCUyMGwlRTElQkIlOEJjaCUyMGtoJUMzJUExbSUyMiUyQyUyMmNvbXBvbmVudHMlMjIlM0ElNUIlN0IlMjJ0eXBlJTIyJTNBJTIyaW5wdXQlMjIlMkMlMjJsYWJlbCUyMiUzQSUyMkglRTElQkIlOEQlMjB2JUMzJUEwJTIwdCVDMyVBQW4lMjIlMkMlMjJpbnB1dF90eXBlJTIyJTNBJTIydGV4dCUyMiUyQyUyMmlzX3JlcXVpcmVkJTIyJTNBdHJ1ZSUyQyUyMmtleSUyMiUzQSUyMmZ1bGxuYW1lJTIyJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmlucHV0JTIyJTJDJTIybGFiZWwlMjIlM0ElMjJTJUM0JTkwVCUyMiUyQyUyMmlucHV0X3R5cGUlMjIlM0ElMjJ0ZXh0JTIyJTJDJTIyaXNfcmVxdWlyZWQlMjIlM0F0cnVlJTJDJTIya2V5JTIyJTNBJTIycGhvbmUlMjIlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIyaW5wdXQlMjIlMkMlMjJsYWJlbCUyMiUzQSUyMkNoJUUxJUJCJThEbiUyMGNhJTIyJTJDJTIyaW5wdXRfdHlwZSUyMiUzQSUyMnJhZGlvJTIyJTJDJTIyaXNfcmVxdWlyZWQlMjIlM0F0cnVlJTJDJTIya2V5JTIyJTNBJTIyc2hpZnQlMjIlMkMlMjJpbnB1dF9vcHRpb25zJTIyJTNBJTVCJTdCJTIybGFiZWwlMjIlM0ElMjJCdSVFMSVCQiU5NWklMjBzJUMzJUExbmclMjIlMkMlMjJ2YWx1ZSUyMiUzQSUyMkJ1JUUxJUJCJTk1aSUyMHMlQzMlQTFuZyUyMiU3RCUyQyU3QiUyMmxhYmVsJTIyJTNBJTIyQnUlRTElQkIlOTVpJTIwY2hpJUUxJUJCJTgxdSUyMiUyQyUyMnZhbHVlJTIyJTNBJTIyQnUlRTElQkIlOTVpJTIwY2hpJUUxJUJCJTgxdSUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJpbnB1dCUyMiUyQyUyMmxhYmVsJTIyJTNBJTIyQ2glRTElQkIlOERuJTIwbmclQzMlQTB5JTIyJTJDJTIyaW5wdXRfdHlwZSUyMiUzQSUyMmRhdGUlMjIlMkMlMjJpc19yZXF1aXJlZCUyMiUzQXRydWUlMkMlMjJrZXklMjIlM0ElMjJzY2hlZHVsZSUyMiU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJidXR0b24lMjIlMkMlMjJsYWJlbCUyMiUzQSUyMiVDNCU5MCVFMSVCQSVCN3QlMjBsJUUxJUJCJThCY2glMjIlN0QlNUQlMkMlMjJwcmltYXJ5X2NvbG9yJTIyJTNBJTIycmdiKDEyOCUyQyUyMDEzNCUyQyUyMDI1NSklMjIlMkMlMjJkZXNjcmlwdGlvbiUyMiUzQSUyMk1vJTIwdGElMjBvJTIwZGF5JTIyJTJDJTIyYnJhbmRfdXJsJTIyJTNBJTIyaHR0cHMlM0ElMkYlMkZzYnouY29tJTIyJTJDJTIybG9nbyUyMiUzQSU3QiUyMnVybCUyMiUzQSUyMmh0dHBzJTNBJTJGJTJGbWVkbGF0ZWMudm4lMkZtZWRpYSUyRjExNSUyRmNvbnRlbnQlMkZsb2dvLXBjLnBuZyUyMiU3RCUyQyUyMmhlYWRlcl9zdHlsZSUyMiUzQSU3QiUyMmZvbnRfc2l6ZSUyMiUzQSUyMjIwJTIyJTJDJTIyZm9udF9mYW1pbHklMjIlM0ElMjJEYW5jaW5nJTIwU2NyaXB0JTIyJTdEJTdE';
33
33
  async function load() {
34
34
  let store = new _store.default(config.ApiURL, config.FileURL, config.RealtimeURL, true);
35
35
  window.store = store;
36
36
  let accid = getUrlParameter('id') || getUrlParameter('accid') || getUrlParameter('account-id') || 'acqsulrowbxiugvginhw';
37
37
  let mask = accid + '@' + 'abcd';
38
38
  let token = getUrlParameter('token');
39
+ let previewJson = getUrlParameter('preview');
39
40
  await store.init(accid);
40
- let acc = store.matchAccount() || {};
41
- let plugin = findChatbox(store) || {};
42
- let css = (await promiseMainCSS).default.default.toString();
43
- var outcss = _index.default.replaceCssVariable(css, plugin.chatbox);
44
- let $cssStyleDiv = document.createElement('style');
45
- document.head.appendChild($cssStyleDiv);
46
- $cssStyleDiv.type = 'text/css';
47
- $cssStyleDiv.id = 'subiz-style-for-order';
48
- _index.default.setCss('form', $cssStyleDiv, outcss);
49
41
  let t = key => {
50
42
  if (_locale === 'debug') return `{{${key}}}`;
51
43
  let messages = _index.default.get(common.langMessages, _locale, []);
52
44
  let message = _index.default.find(messages, message => message.key === key);
53
45
  return _index.default.get(message, 'message', key);
54
46
  };
55
- let _locale = plugin.locale || acc.locale || 'vi-VN';
47
+ let _locale = 'vi-VN';
56
48
  t.i18n = i18nText => {
57
49
  if (!i18nText) return '';
58
- let fallbackLocale = plugin.locale || acc.locale || 'vi-VN';
50
+ let fallbackLocale = 'vi-VN';
59
51
  fallbackLocale = fallbackLocale.replace('-', '_');
60
52
  let locale = _locale.replace('-', '_');
61
53
  return i18nText[locale] || i18nText[fallbackLocale];
@@ -65,7 +57,8 @@ async function load() {
65
57
  (0, _preact.render)((0, _preact.h)(_FormPage.default, {
66
58
  store: store,
67
59
  t: t,
68
- token: token
60
+ token: token,
61
+ previewJson: previewJson
69
62
  }), document.getElementById('form'));
70
63
  });
71
64
  }
@@ -613,6 +613,13 @@ function replaceCssVariable(css, setting) {
613
613
  tokens = tokenize(tokens, "'@z_index_3'");
614
614
  tokens = tokenize(tokens, "'@z_index_4'");
615
615
  tokens = tokenize(tokens, "'@z_index_5'");
616
+ tokens = tokenize(tokens, "'@form_background_color'");
617
+ tokens = tokenize(tokens, "'@form_header_font_size'");
618
+ tokens = tokenize(tokens, "'@form_header_font_family'");
619
+ tokens = tokenize(tokens, "'@form_desc_font_size'");
620
+ tokens = tokenize(tokens, "'@form_desc_font_family'");
621
+ tokens = tokenize(tokens, "'@form_input_label_font_size'");
622
+ tokens = tokenize(tokens, "'@form_input_label_font_family'");
616
623
  var primary_color = op.get(setting, 'primary_color') || op.get(DEFVAL, 'primary_color');
617
624
  let parseColor = parseCssColor(primary_color);
618
625
  if (!parseColor) parseColor = {
@@ -658,7 +665,7 @@ function replaceCssVariable(css, setting) {
658
665
  var path = item.type.substr(2, item.type.length - 3);
659
666
  var val = op.get(setting, path) || op.get(DEFVAL, path) || '';
660
667
  val = norm2(val);
661
- if (['mobile_button.width', 'mobile_button.height', 'mobile_button.margin_x', 'mobile_button.margin_y', 'desktop_button.width', 'desktop_button.height', 'desktop_button.margin_x', 'desktop_button.margin_y', 'width', 'height'].includes(path)) {
668
+ if (['mobile_button.width', 'mobile_button.height', 'mobile_button.margin_x', 'mobile_button.margin_y', 'desktop_button.width', 'desktop_button.height', 'desktop_button.margin_x', 'desktop_button.margin_y', 'form_header_font_size', 'form_desc_font_size', 'form_input_label_font_size', 'width', 'height'].includes(path)) {
662
669
  val += 'px';
663
670
  }
664
671
  if (path === 'height' && op.get(setting, 'window_mode') === 'full') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@subiz/widget-v4",
3
- "version": "4.1.135",
3
+ "version": "4.1.136",
4
4
  "main": "./compiled_src/lib.js",
5
5
  "scripts": {
6
6
  "dev": "npx webpack serve --config webpack.dev.js",
package/src/form/Form.js CHANGED
@@ -278,6 +278,7 @@ export default class Form2 extends Com {
278
278
  render() {
279
279
  let components = sb.get(this.props.formSetting, 'components') || []
280
280
  let title = sb.get(this.props.formSetting, 'title') || ''
281
+ let desc = sb.get(this.props.formSetting, 'description') || ''
281
282
  let cls = 'form-wrapper form'
282
283
  if (this.props.mode === 'attachment') cls += ' form-wrapper-attachment'
283
284
  return (
@@ -286,7 +287,12 @@ export default class Form2 extends Com {
286
287
  <div class='form-component'>{this.props.t('you_have_submitted_info_successfully')}</div>
287
288
  )}
288
289
  <form onSubmit={this.onSubmit}>
289
- <div class='form-component form-component-block'>{title}</div>
290
+ {this.props.mode === 'attachment' && (title || desc) && (
291
+ <div class='form-component form-component-block'>
292
+ {title && <div>{title}</div>}
293
+ {desc && <div class='text-sm text-muted'>{desc}</div>}
294
+ </div>
295
+ )}
290
296
  {sb.map(components, this.renderFormComponent)}
291
297
  </form>
292
298
  </div>
@@ -2,6 +2,7 @@ import {Com} from '../common.js'
2
2
  import {h, createRef} from 'preact'
3
3
  import Form from './Form.js'
4
4
  import sb from '../util/index.js'
5
+ const promiseMainCSS = (() => import(/*webpackChunkName: "form-page-css" */ './css'))()
5
6
 
6
7
  export default class FormPage extends Com {
7
8
  constructor(props) {
@@ -19,14 +20,93 @@ export default class FormPage extends Com {
19
20
  }
20
21
  }
21
22
 
22
- componentDidMount() {
23
+ async componentDidMount() {
24
+ await this.initCss()
23
25
  this.fetchFormSetting()
24
26
  }
25
27
 
28
+ waitForFont = async (fonts) => {
29
+ if (sb.size(fonts) == 0) return
30
+ // 2 sec timeout
31
+ let ready = {}
32
+ for (let t = 0; t < 10; t++) {
33
+ for (let i = 0; i < fonts.length; i++) {
34
+ try {
35
+ if (document.fonts.check('normal 16px ' + fonts[i])) {
36
+ ready[fonts[i]] = true
37
+ }
38
+ console.log('ready', ready, Object.keys(ready), fonts.length, Object.keys(ready) === fonts.length)
39
+ if (sb.size(Object.keys(ready)) === fonts.length) {
40
+ return
41
+ }
42
+ } catch (e) {}
43
+ }
44
+ await sb.sleep(200)
45
+ }
46
+ return
47
+ }
48
+
49
+ initCss = async () => {
50
+ let css = (await promiseMainCSS).default.default.toString()
51
+ let chatbox = sb.find(this.props.store.matchPlugin(), (plugin) => plugin.type === 'chatbox') || {}
52
+ chatbox = chatbox.chatbox || {}
53
+ let setting = chatbox
54
+ let outcss = sb.replaceCssVariable(css, setting)
55
+ let $cssStyleDiv = document.createElement('style')
56
+ document.head.appendChild($cssStyleDiv)
57
+ $cssStyleDiv.type = 'text/css'
58
+ $cssStyleDiv.id = 'subiz-style-for-form'
59
+
60
+ this.cssDiv = $cssStyleDiv
61
+ sb.setCss('form', $cssStyleDiv, outcss)
62
+ }
63
+
64
+ insertCss = async (chatbox, formSetting) => {
65
+ let css = (await promiseMainCSS).default.default.toString()
66
+ let setting = chatbox
67
+ if (formSetting.primary_color) setting.primary_color = formSetting.primary_color
68
+ let headerFont = sb.get(formSetting, 'header_style.font_family')
69
+ let descFont = sb.get(formSetting, 'text_style.font_family')
70
+ let inputLabelFont = sb.get(formSetting, 'input_style.font_family')
71
+ setting.form_background_color = formSetting.background_color
72
+ setting.form_header_font_size = sb.get(formSetting, 'header_style.font_size') || '20'
73
+ setting.form_header_font_family = headerFont
74
+ setting.form_desc_font_size = sb.get(formSetting, 'text_style.font_size') || '16'
75
+ setting.form_desc_font_family = descFont
76
+ setting.form_input_label_font_size = sb.get(formSetting, 'input_style.font_size') || '14'
77
+ setting.form_input_label_font_family = inputLabelFont
78
+ let outcss = sb.replaceCssVariable(css, setting)
79
+
80
+ let fonts = []
81
+ if (inputLabelFont && !fonts.includes(inputLabelFont)) fonts.push(inputLabelFont)
82
+ if (headerFont && !fonts.includes(headerFont)) fonts.push(headerFont)
83
+ if (descFont && !fonts.includes(descFont)) fonts.push(descFont)
84
+ if (sb.size(fonts)) {
85
+ sb.preloadWebfonts(fonts, [400, 500])
86
+ await this.waitForFont(fonts)
87
+ }
88
+
89
+ sb.setCss('form', this.cssDiv, outcss)
90
+ }
91
+
26
92
  fetchFormSetting = async () => {
93
+ // load css
94
+ let chatbox = sb.find(this.props.store.matchPlugin(), (plugin) => plugin.type === 'chatbox') || {}
95
+ chatbox = chatbox.chatbox || {}
96
+
97
+ if (this.props.previewJson) {
98
+ let json = atob(this.props.previewJson)
99
+ json = decodeURIComponent(json)
100
+ json = sb.parseJSON(json)
101
+ await this.insertCss(chatbox, json)
102
+ this.setState({formSetting: json})
103
+ return
104
+ }
27
105
  if (this.state.loading) return
28
106
  this.setState({loading: true})
29
107
  let res = await this.props.store.checkFormToken(this.props.store.matchAccount().id, this.props.token)
108
+ //await sb.sleep(5000)
109
+ //res.error = 'true'
30
110
  if (res.error) {
31
111
  this.setState({loading: false, loadError: true})
32
112
  return
@@ -34,6 +114,9 @@ export default class FormPage extends Com {
34
114
  let formSetting = sb.get(res, 'body.form') || {}
35
115
  let submission = sb.get(res, 'body.form_submission') || {}
36
116
  let isSubmited = sb.size(submission) > 0
117
+
118
+ await this.insertCss(chatbox, formSetting)
119
+
37
120
  this.setState({loading: false, loadError: false, formSetting, submission, isSubmited})
38
121
  }
39
122
 
@@ -43,6 +126,10 @@ export default class FormPage extends Com {
43
126
 
44
127
  onSubmit = async () => {
45
128
  this.setState({errorMessage: ''})
129
+ if (this.props.previewJson) {
130
+ this.setState({isSubmited: true})
131
+ return
132
+ }
46
133
  if (this.state.submiting) return
47
134
  this.setState({submiting: true})
48
135
  let res = await this.props.store.submitForm2(
@@ -79,20 +166,38 @@ export default class FormPage extends Com {
79
166
  </div>
80
167
  )
81
168
  }
169
+
170
+ let brandLogo = sb.get(formSetting, 'logo.url')
82
171
  return (
83
- <div class='form-page'>
84
- <div class='form-page-header'>{'HEADER'}</div>
85
- <Form
86
- store={this.props.store}
87
- formSetting={formSetting}
88
- t={this.props.t}
89
- submission={submission}
90
- errorMessage={errorMessage}
91
- isSubmited={isSubmited}
92
- submiting={submiting}
93
- onChangeFormValues={this.onChangeFormValues}
94
- onSubmit={this.onSubmit}
95
- />
172
+ <div class='form-page-body'>
173
+ <div class='form-page'>
174
+ <div class='form-page-logo-wrapper'>
175
+ <a href={formSetting.brand_url || 'javascript:;'}>
176
+ {brandLogo ? (
177
+ <img src={brandLogo} class='form-page-logo' />
178
+ ) : (
179
+ <div class='form-page-logo-text'>{sb.get(this.props.store.matchAccount(), 'name')}</div>
180
+ )}
181
+ </a>
182
+ </div>
183
+ {(formSetting.title || formSetting.description) && (
184
+ <div class='form-page-header'>
185
+ <div class='form-page-title'>{formSetting.title}</div>
186
+ {formSetting.description && <div class='form-page-desc'>{formSetting.description}</div>}
187
+ </div>
188
+ )}
189
+ <Form
190
+ store={this.props.store}
191
+ formSetting={formSetting}
192
+ t={this.props.t}
193
+ submission={submission}
194
+ errorMessage={errorMessage}
195
+ isSubmited={isSubmited}
196
+ submiting={submiting}
197
+ onChangeFormValues={this.onChangeFormValues}
198
+ onSubmit={this.onSubmit}
199
+ />
200
+ </div>
96
201
  </div>
97
202
  )
98
203
  }
@@ -0,0 +1 @@
1
+ export default require('css-loader!less-loader!../../assets/less/form-page.less')
package/src/form/index.js CHANGED
@@ -29,7 +29,9 @@ $link.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500&displa
29
29
  $link.rel = 'stylesheet'
30
30
  document.head.append($link)
31
31
 
32
- sb.preloadWebfonts(['Inter'], [400, 500])
32
+ // test json setting
33
+ const PREVIEW_B64 =
34
+ 'JTdCJTIyaWQlMjIlM0ElMjJhdHNocnBxeHp1dWZ6emRrenF1el9kbGJnd3lpYiUyMiUyQyUyMnRpdGxlJTIyJTNBJTIyJUM0JTkwJUUxJUJBJUI3dCUyMGwlRTElQkIlOEJjaCUyMGtoJUMzJUExbSUyMiUyQyUyMmNvbXBvbmVudHMlMjIlM0ElNUIlN0IlMjJ0eXBlJTIyJTNBJTIyaW5wdXQlMjIlMkMlMjJsYWJlbCUyMiUzQSUyMkglRTElQkIlOEQlMjB2JUMzJUEwJTIwdCVDMyVBQW4lMjIlMkMlMjJpbnB1dF90eXBlJTIyJTNBJTIydGV4dCUyMiUyQyUyMmlzX3JlcXVpcmVkJTIyJTNBdHJ1ZSUyQyUyMmtleSUyMiUzQSUyMmZ1bGxuYW1lJTIyJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmlucHV0JTIyJTJDJTIybGFiZWwlMjIlM0ElMjJTJUM0JTkwVCUyMiUyQyUyMmlucHV0X3R5cGUlMjIlM0ElMjJ0ZXh0JTIyJTJDJTIyaXNfcmVxdWlyZWQlMjIlM0F0cnVlJTJDJTIya2V5JTIyJTNBJTIycGhvbmUlMjIlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIyaW5wdXQlMjIlMkMlMjJsYWJlbCUyMiUzQSUyMkNoJUUxJUJCJThEbiUyMGNhJTIyJTJDJTIyaW5wdXRfdHlwZSUyMiUzQSUyMnJhZGlvJTIyJTJDJTIyaXNfcmVxdWlyZWQlMjIlM0F0cnVlJTJDJTIya2V5JTIyJTNBJTIyc2hpZnQlMjIlMkMlMjJpbnB1dF9vcHRpb25zJTIyJTNBJTVCJTdCJTIybGFiZWwlMjIlM0ElMjJCdSVFMSVCQiU5NWklMjBzJUMzJUExbmclMjIlMkMlMjJ2YWx1ZSUyMiUzQSUyMkJ1JUUxJUJCJTk1aSUyMHMlQzMlQTFuZyUyMiU3RCUyQyU3QiUyMmxhYmVsJTIyJTNBJTIyQnUlRTElQkIlOTVpJTIwY2hpJUUxJUJCJTgxdSUyMiUyQyUyMnZhbHVlJTIyJTNBJTIyQnUlRTElQkIlOTVpJTIwY2hpJUUxJUJCJTgxdSUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJpbnB1dCUyMiUyQyUyMmxhYmVsJTIyJTNBJTIyQ2glRTElQkIlOERuJTIwbmclQzMlQTB5JTIyJTJDJTIyaW5wdXRfdHlwZSUyMiUzQSUyMmRhdGUlMjIlMkMlMjJpc19yZXF1aXJlZCUyMiUzQXRydWUlMkMlMjJrZXklMjIlM0ElMjJzY2hlZHVsZSUyMiU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJidXR0b24lMjIlMkMlMjJsYWJlbCUyMiUzQSUyMiVDNCU5MCVFMSVCQSVCN3QlMjBsJUUxJUJCJThCY2glMjIlN0QlNUQlMkMlMjJwcmltYXJ5X2NvbG9yJTIyJTNBJTIycmdiKDEyOCUyQyUyMDEzNCUyQyUyMDI1NSklMjIlMkMlMjJkZXNjcmlwdGlvbiUyMiUzQSUyMk1vJTIwdGElMjBvJTIwZGF5JTIyJTJDJTIyYnJhbmRfdXJsJTIyJTNBJTIyaHR0cHMlM0ElMkYlMkZzYnouY29tJTIyJTJDJTIybG9nbyUyMiUzQSU3QiUyMnVybCUyMiUzQSUyMmh0dHBzJTNBJTJGJTJGbWVkbGF0ZWMudm4lMkZtZWRpYSUyRjExNSUyRmNvbnRlbnQlMkZsb2dvLXBjLnBuZyUyMiU3RCUyQyUyMmhlYWRlcl9zdHlsZSUyMiUzQSU3QiUyMmZvbnRfc2l6ZSUyMiUzQSUyMjIwJTIyJTJDJTIyZm9udF9mYW1pbHklMjIlM0ElMjJEYW5jaW5nJTIwU2NyaXB0JTIyJTdEJTdE'
33
35
 
34
36
  async function load() {
35
37
  let store = new Store(config.ApiURL, config.FileURL, config.RealtimeURL, true)
@@ -37,23 +39,11 @@ async function load() {
37
39
  let accid =
38
40
  getUrlParameter('id') || getUrlParameter('accid') || getUrlParameter('account-id') || 'acqsulrowbxiugvginhw'
39
41
  let mask = accid + '@' + 'abcd'
40
- let token = getUrlParameter('token')
42
+ let token = getUrlParameter('token')
43
+ let previewJson = getUrlParameter('preview')
44
+ //previewJson = PREVIEW_B64
41
45
  await store.init(accid)
42
46
 
43
- let acc = store.matchAccount() || {}
44
- let plugin = findChatbox(store) || {}
45
-
46
- // make sure you are still alive
47
- let css = (await promiseMainCSS).default.default.toString()
48
- var outcss = sb.replaceCssVariable(css, plugin.chatbox)
49
-
50
- let $cssStyleDiv = document.createElement('style')
51
- document.head.appendChild($cssStyleDiv)
52
- $cssStyleDiv.type = 'text/css'
53
- $cssStyleDiv.id = 'subiz-style-for-order'
54
-
55
- sb.setCss('form', $cssStyleDiv, outcss)
56
-
57
47
  let t = (key) => {
58
48
  if (_locale === 'debug') return `{{${key}}}`
59
49
 
@@ -61,10 +51,10 @@ async function load() {
61
51
  let message = sb.find(messages, (message) => message.key === key)
62
52
  return sb.get(message, 'message', key)
63
53
  }
64
- let _locale = plugin.locale || acc.locale || 'vi-VN'
54
+ let _locale = 'vi-VN'
65
55
  t.i18n = (i18nText) => {
66
56
  if (!i18nText) return ''
67
- let fallbackLocale = plugin.locale || acc.locale || 'vi-VN'
57
+ let fallbackLocale = 'vi-VN'
68
58
  fallbackLocale = fallbackLocale.replace('-', '_')
69
59
  let locale = _locale.replace('-', '_')
70
60
 
@@ -72,7 +62,7 @@ async function load() {
72
62
  }
73
63
  t.locale = () => _locale
74
64
  common.downloadLanguage(store, _locale).then(() => {
75
- render(<FormPage store={store} t={t} token={token}/>, document.getElementById('form'))
65
+ render(<FormPage store={store} t={t} token={token} previewJson={previewJson} />, document.getElementById('form'))
76
66
  })
77
67
  }
78
68
 
@@ -653,6 +653,13 @@ export function replaceCssVariable(css, setting) {
653
653
  tokens = tokenize(tokens, "'@z_index_3'")
654
654
  tokens = tokenize(tokens, "'@z_index_4'")
655
655
  tokens = tokenize(tokens, "'@z_index_5'")
656
+ tokens = tokenize(tokens, "'@form_background_color'")
657
+ tokens = tokenize(tokens, "'@form_header_font_size'")
658
+ tokens = tokenize(tokens, "'@form_header_font_family'")
659
+ tokens = tokenize(tokens, "'@form_desc_font_size'")
660
+ tokens = tokenize(tokens, "'@form_desc_font_family'")
661
+ tokens = tokenize(tokens, "'@form_input_label_font_size'")
662
+ tokens = tokenize(tokens, "'@form_input_label_font_family'")
656
663
 
657
664
  // make primary_color faded
658
665
  var primary_color = op.get(setting, 'primary_color') || op.get(DEFVAL, 'primary_color')
@@ -714,6 +721,9 @@ export function replaceCssVariable(css, setting) {
714
721
  'desktop_button.height',
715
722
  'desktop_button.margin_x',
716
723
  'desktop_button.margin_y',
724
+ 'form_header_font_size',
725
+ 'form_desc_font_size',
726
+ 'form_input_label_font_size',
717
727
  'width',
718
728
  'height',
719
729
  ].includes(path)