@social-mail/social-mail-client 1.2.147 → 1.2.149

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.
Files changed (81) hide show
  1. package/content/json/countries.json +1577 -0
  2. package/dist/admin/AdminAppIndex.pack.js +559 -54
  3. package/dist/admin/AdminAppIndex.pack.js.map +1 -1
  4. package/dist/admin/AdminAppIndex.pack.min.js +60 -9
  5. package/dist/admin/AdminAppIndex.pack.min.js.map +1 -1
  6. package/dist/common/controls/countries/CountrySelect.d.ts +4 -0
  7. package/dist/common/controls/countries/CountrySelect.d.ts.map +1 -0
  8. package/dist/common/controls/countries/CountrySelect.js +49 -0
  9. package/dist/common/controls/countries/CountrySelect.js.map +1 -0
  10. package/dist/common/controls/phone-editor/PhoneEditor.d.ts +5 -0
  11. package/dist/common/controls/phone-editor/PhoneEditor.d.ts.map +1 -0
  12. package/dist/common/controls/phone-editor/PhoneEditor.js +25 -0
  13. package/dist/common/controls/phone-editor/PhoneEditor.js.map +1 -0
  14. package/dist/common/form/AppForm.d.ts +3 -0
  15. package/dist/common/form/AppForm.d.ts.map +1 -0
  16. package/dist/common/form/AppForm.js +41 -0
  17. package/dist/common/form/AppForm.js.map +1 -0
  18. package/dist/common/pages/user/forgot/ForgotPassword.d.ts +24 -0
  19. package/dist/common/pages/user/forgot/ForgotPassword.d.ts.map +1 -0
  20. package/dist/common/pages/user/forgot/ForgotPassword.js +219 -0
  21. package/dist/common/pages/user/forgot/ForgotPassword.js.map +1 -0
  22. package/dist/common/pages/user/login/LoginPage.d.ts +3 -0
  23. package/dist/common/pages/user/login/LoginPage.d.ts.map +1 -1
  24. package/dist/common/pages/user/login/LoginPage.js +88 -35
  25. package/dist/common/pages/user/login/LoginPage.js.map +1 -1
  26. package/dist/common/pages/user/multi-factor/MultiFactorSetupPage.d.ts.map +1 -1
  27. package/dist/common/pages/user/multi-factor/MultiFactorSetupPage.js +8 -8
  28. package/dist/common/pages/user/multi-factor/MultiFactorSetupPage.js.map +1 -1
  29. package/dist/common/pages/user/multi-factor/SetupMultiFactorPage.d.ts.map +1 -1
  30. package/dist/common/pages/user/multi-factor/SetupMultiFactorPage.js +6 -6
  31. package/dist/common/pages/user/multi-factor/SetupMultiFactorPage.js.map +1 -1
  32. package/dist/public/channel/ChannelView.pack.js +8 -4
  33. package/dist/public/channel/ChannelView.pack.js.map +1 -1
  34. package/dist/public/channel/ChannelView.pack.min.js +3 -3
  35. package/dist/public/channel/ChannelView.pack.min.js.map +1 -1
  36. package/dist/site-editor/blocks/Billboard.js +4 -2
  37. package/dist/site-editor/blocks/Billboard.js.map +1 -1
  38. package/dist/site-editor/blocks/OutlineText.d.ts +2 -0
  39. package/dist/site-editor/blocks/OutlineText.d.ts.map +1 -0
  40. package/dist/site-editor/blocks/OutlineText.js +37 -0
  41. package/dist/site-editor/blocks/OutlineText.js.map +1 -0
  42. package/dist/site-editor/drag-drop/DragDropHelper.d.ts.map +1 -1
  43. package/dist/site-editor/drag-drop/DragDropHelper.js +6 -0
  44. package/dist/site-editor/drag-drop/DragDropHelper.js.map +1 -1
  45. package/dist/site-editor/editor/HtmlPageEditor.d.ts.map +1 -1
  46. package/dist/site-editor/editor/HtmlPageEditor.js +5 -3
  47. package/dist/site-editor/editor/HtmlPageEditor.js.map +1 -1
  48. package/dist/site-editor/editor/SelectedElement.js +1 -1
  49. package/dist/site-editor/editor/SelectedElement.js.map +1 -1
  50. package/dist/tsconfig.tsbuildinfo +1 -1
  51. package/dist/web/AppIndex.pack.js +565 -60
  52. package/dist/web/AppIndex.pack.js.map +1 -1
  53. package/dist/web/AppIndex.pack.min.js +61 -10
  54. package/dist/web/AppIndex.pack.min.js.map +1 -1
  55. package/node_modules/@web-atoms/core/dist/services/FetchBuilder.d.ts.map +1 -1
  56. package/node_modules/@web-atoms/core/dist/services/FetchBuilder.js +2 -1
  57. package/node_modules/@web-atoms/core/dist/services/FetchBuilder.js.map +1 -1
  58. package/node_modules/@web-atoms/core/dist/services/http/JsonError.d.ts.map +1 -1
  59. package/node_modules/@web-atoms/core/dist/services/http/JsonError.js +5 -2
  60. package/node_modules/@web-atoms/core/dist/services/http/JsonError.js.map +1 -1
  61. package/node_modules/@web-atoms/core/dist/tsconfig.tsbuildinfo +1 -1
  62. package/node_modules/@web-atoms/core/package.json +1 -1
  63. package/node_modules/@web-atoms/core/src/services/FetchBuilder.ts +5 -1
  64. package/node_modules/@web-atoms/core/src/services/http/JsonError.ts +3 -2
  65. package/node_modules/@web-atoms/module-loader/package.json +1 -1
  66. package/node_modules/@web-atoms/module-loader/umd/src/AmdLoader.ts +1 -1
  67. package/node_modules/@web-atoms/module-loader/umd.js +1 -1
  68. package/node_modules/@web-atoms/module-loader/umd.js.map +1 -1
  69. package/package.json +3 -3
  70. package/src/common/controls/countries/CountrySelect.tsx +39 -0
  71. package/src/common/controls/phone-editor/PhoneEditor.tsx +12 -0
  72. package/src/common/form/AppForm.tsx +30 -0
  73. package/src/common/pages/user/forgot/ForgotPassword.tsx +229 -0
  74. package/src/common/pages/user/login/LoginPage.tsx +89 -20
  75. package/src/common/pages/user/multi-factor/MultiFactorSetupPage.tsx +9 -8
  76. package/src/common/pages/user/multi-factor/SetupMultiFactorPage.tsx +5 -4
  77. package/src/site-editor/blocks/Billboard.tsx +3 -3
  78. package/src/site-editor/blocks/OutlineText.tsx +27 -0
  79. package/src/site-editor/drag-drop/DragDropHelper.ts +6 -0
  80. package/src/site-editor/editor/HtmlPageEditor.tsx +2 -0
  81. package/src/site-editor/editor/SelectedElement.tsx +1 -1
@@ -0,0 +1,229 @@
1
+ import Bind from "@web-atoms/core/dist/core/Bind";
2
+ import XNode from "@web-atoms/core/dist/core/XNode";
3
+ import styled from "@web-atoms/core/dist/style/styled";
4
+ import Form from "@web-atoms/web-controls/dist/basic/Form";
5
+ import FormField from "@web-atoms/web-controls/dist/basic/FormField";
6
+ import { ContentPage } from "@web-atoms/web-controls/dist/mobile-app/MobileApp";
7
+ import { CountrySelect, formatForCountry, isdForCountry } from "../../../controls/countries/CountrySelect";
8
+ import MaskedInput from "@web-atoms/web-controls/dist/basic/MaskedInput";
9
+ import FetchBuilder from "@web-atoms/core/dist/services/FetchBuilder";
10
+ import Action from "@web-atoms/core/dist/view-model/Action";
11
+ import PopupService from "@web-atoms/core/dist/web/services/PopupService";
12
+ import PasswordBox from "@web-atoms/web-controls/dist/basic/PasswordBox";
13
+
14
+ const css = styled.css `
15
+ & [data-border=frame] {
16
+ padding: 10px;
17
+ border-radius: 10px;
18
+ gap: 10px;
19
+
20
+ & h3 {
21
+ margin-block-start: 20px;
22
+ margin-block-end: 20px;
23
+ }
24
+
25
+ & input {
26
+ outline: none;
27
+ border: none;
28
+ border-bottom: solid 1px #80808080;
29
+ min-width: 250px;
30
+ }
31
+ }
32
+ `.installLocal();
33
+
34
+ interface IVerification {
35
+ id?: string;
36
+ code?: string;
37
+ verified?: string;
38
+ }
39
+
40
+ export default class ForgotPassword extends ContentPage {
41
+
42
+ model: {
43
+ username: string;
44
+ password: string;
45
+ passwordAgain: string;
46
+ phoneNumber: string;
47
+ country: string;
48
+ phoneNumberText: string;
49
+ captcha: IVerification;
50
+ phone: IVerification;
51
+ } = {
52
+ username: "",
53
+ password: "",
54
+ passwordAgain: "",
55
+ phoneNumber: "",
56
+ country: "US",
57
+ phoneNumberText: "",
58
+ phone: {
59
+ code: ""
60
+ },
61
+ captcha: {
62
+ code: ""
63
+ }
64
+ };
65
+
66
+ async init() {
67
+
68
+ this.hideToolbar = true;
69
+
70
+ this.title = "Reset Password";
71
+
72
+ const { id } = await FetchBuilder.put("/api/users/captcha").responseAsJson<{ id }>();
73
+ this.model.captcha.id = id;
74
+
75
+
76
+ this.render(<div class={css}>
77
+ <div data-layout="center-all">
78
+ <Form data-border="frame">
79
+ <h3>
80
+ To reset password, enter following.
81
+ </h3>
82
+
83
+ <FormField label="Username">
84
+ <input
85
+ autofocus={true}
86
+ value={Bind.twoWaysImmediate(() => this.model.username)}/>
87
+ </FormField>
88
+
89
+ <FormField label="Phone Number">
90
+ <div data-layout="row">
91
+ <CountrySelect
92
+ value={Bind.twoWaysImmediate(() => this.model.country)}
93
+ />
94
+ <MaskedInput
95
+ data-mask={Bind.oneWayAsync(() => formatForCountry(this.model.country))}
96
+ value={Bind.twoWaysImmediate(() => this.model.phoneNumberText)}
97
+ />
98
+ </div>
99
+ </FormField>
100
+
101
+
102
+ <FormField label="Captcha">
103
+ <img src={`/api/users/captcha/${id}`}/>
104
+ </FormField>
105
+ <FormField label="Enter 5 Digit Code">
106
+ <input
107
+ type="tel"
108
+ value={Bind.twoWaysImmediate(() => this.model.captcha.code)}/>
109
+ </FormField>
110
+ <div data-layout="row">
111
+ <button
112
+ data-layout="go-button"
113
+ data-submit-event="ask-code"
114
+ >Next</button>
115
+ </div>
116
+
117
+ </Form>
118
+ </div>
119
+ </div>);
120
+ }
121
+
122
+ @Action({ onEvent: "ask-code"})
123
+ async resetPassword() {
124
+
125
+ const { model } = this;
126
+
127
+ if (model.phone.verified) {
128
+ if (!model.password) {
129
+ throw new Error("Password is required");
130
+ }
131
+ }
132
+
133
+ if (model.password) {
134
+
135
+ if (model.password.length < 7) {
136
+ throw new Error("Password is cannot be less than 7 characters");
137
+ }
138
+
139
+ if (/\s/i.test(model.password)) {
140
+ throw new Error("Password must not any whitespace characters");
141
+ }
142
+
143
+ if (model.passwordAgain !== model.password) {
144
+ throw new Error("Passwords do not match");
145
+ }
146
+ }
147
+
148
+
149
+ const isdCode = await isdForCountry(model.country);
150
+ const phoneNumber = model.phoneNumberText.replace(/[^0-9]/g,"");
151
+ model.phoneNumber = isdCode.startsWith("+") ? isdCode + phoneNumber: `+${isdCode}${phoneNumber}`;
152
+
153
+ const { userID, phone, captcha } = await FetchBuilder.post("/api/users/password/reset/")
154
+ .jsonBody(this.model)
155
+ .responseAsJson<{ phone: IVerification, captcha: IVerification, userID: any }>();
156
+
157
+ if (captcha?.verified) {
158
+ model.captcha.verified = captcha.verified;
159
+ }
160
+
161
+ if (userID) {
162
+ await PopupService.alert({ message: "Password has been reset successully, please login with your new password." });
163
+ location.href = "/";
164
+ return;
165
+ }
166
+
167
+ if(phone.verified) {
168
+ this.model.phone.verified = phone.verified;
169
+ this.askPasswords();
170
+ return;
171
+ }
172
+
173
+ if (phone.id) {
174
+ this.model.phone.id = phone.id;
175
+ }
176
+
177
+ this.askCode();
178
+ }
179
+
180
+ askCode() {
181
+ this.renderer = <div class={css}>
182
+ <div data-layout="center-all">
183
+ <Form data-border="frame">
184
+ <h3>
185
+ Enter verification code.
186
+ </h3>
187
+ <FormField label="Code">
188
+ <input type="tel" value={Bind.twoWaysImmediate(() => this.model.phone.code)}/>
189
+ </FormField>
190
+ <div data-layout="row">
191
+ <button
192
+ data-layout="go-button"
193
+ data-submit-event="ask-code">Next</button>
194
+ </div>
195
+
196
+ </Form>
197
+ </div>
198
+ </div>;
199
+ }
200
+
201
+ askPasswords() {
202
+ this.renderer = <div class={css}>
203
+ <div data-layout="center-all">
204
+ <Form data-border="frame">
205
+ <h3>
206
+ Enter new password.
207
+ </h3>
208
+ <FormField label="Password">
209
+ <PasswordBox
210
+ value={Bind.twoWaysImmediate(() => this.model.password)}
211
+ />
212
+ </FormField>
213
+ <FormField label="Password Again">
214
+ <PasswordBox
215
+ value={Bind.twoWaysImmediate(() => this.model.passwordAgain)}
216
+ />
217
+ </FormField>
218
+ <div data-layout="row">
219
+ <button
220
+ data-layout="go-button"
221
+ data-submit-event="ask-code">Next</button>
222
+ </div>
223
+
224
+ </Form>
225
+ </div>
226
+ </div>;
227
+ }
228
+
229
+ }
@@ -14,6 +14,11 @@ import PasswordStrength from "../../../controls/password/PasswordStrength";
14
14
  import { Validators } from "../../../validators/Validators";
15
15
  import ToggleButtonBar from "@web-atoms/web-controls/dist/basic/ToggleButtonBar";
16
16
  import { SaveIconTextButton } from "../../../controls/buttons/IconButton";
17
+ import styled from "@web-atoms/core/dist/style/styled";
18
+ import PageNavigator from "@web-atoms/web-controls/dist/PageNavigator";
19
+ import ForgotPassword from "../forgot/ForgotPassword";
20
+ import PopupService from "@web-atoms/core/dist/web/services/PopupService";
21
+ import AppForm from "../../../form/AppForm";
17
22
 
18
23
  const authTypes = [{
19
24
  label: "Authenticator (TOTP)",
@@ -37,6 +42,16 @@ export default class LoginPage extends ContentPage {
37
42
  private authType = "totp";
38
43
 
39
44
  async init() {
45
+
46
+ this.footerRenderer = () => <div data-layout="row">
47
+ <a
48
+ target="_blank"
49
+ data-margin-left="auto"
50
+ href="https://socialmail.me">Social Mail</a>
51
+ </div>;
52
+
53
+ this.hideToolbar = true;
54
+
40
55
  this.model = LoginSession.create({});
41
56
  this.model.userName = "";
42
57
  this.model.checkPassword = "";
@@ -44,23 +59,34 @@ export default class LoginPage extends ContentPage {
44
59
  this.model.oneTimePassword = "";
45
60
  this.render(<div>
46
61
  <div data-layout="center-all">
47
- <Form>
62
+ <AppForm>
63
+ <h3>
64
+ Sign in to continue
65
+ </h3>
48
66
  <FormField label="Username">
49
- <input value={Bind.twoWaysImmediate(() => this.model.userName)}/>
67
+ <input
68
+ autofocus={true}
69
+ value={Bind.twoWaysImmediate(() => this.model.userName)}/>
50
70
  </FormField>
51
71
  <FormField label="Password">
52
72
  <PasswordBox
53
73
  value={Bind.twoWaysImmediate(() => this.model.checkPassword)}
54
74
  />
55
75
  </FormField>
56
- <div>
76
+ <div data-layout="row">
57
77
  <button
58
78
  data-layout="accent-button"
59
79
  data-submit-event="login"
60
80
  text="Login"
61
81
  />
62
82
  </div>
63
- </Form>
83
+
84
+ { <div data-layout="row">
85
+ <span
86
+ data-click-event="forgot-password"
87
+ data-layout="link">Forgot password?</span>
88
+ </div>}
89
+ </AppForm>
64
90
  </div>
65
91
  </div>);
66
92
  }
@@ -78,41 +104,73 @@ export default class LoginPage extends ContentPage {
78
104
  return;
79
105
  }
80
106
 
107
+ this.showAuthenticator();
108
+
109
+ }
110
+
111
+ showAuthenticator() {
81
112
  this.renderer = <div data-layout="center-all">
82
- <Form>
83
- <FormField label="Enter">
84
- <ToggleButtonBar items={authTypes} value={Bind.twoWays(() => this.authType)}/>
85
- </FormField>
113
+ <AppForm>
114
+ <h4>Enter Authenticator Code</h4>
86
115
  <FormField
87
- label="Code"
88
- style-display={Bind.oneWay(() => this.authType === "totp")}>
89
- <input type="tel" value={Bind.twoWaysImmediate(() => this.model.timeToken)}/>
116
+ label="Code">
117
+ <input
118
+ autofocus={true}
119
+ type="tel" value={Bind.twoWaysImmediate(() => this.model.timeToken)} />
90
120
  </FormField>
121
+ <div data-layout="row">
122
+ <button
123
+ event-click={() => this.showOTP()}
124
+ data-layout="link">
125
+ Enter One Time Password instead.
126
+ </button>
127
+ </div>
128
+ <div data-layout="command-row">
129
+ <SaveIconTextButton
130
+ event-click={() => this.login()}
131
+ text="Go" />
132
+ </div>
133
+ </AppForm>
134
+ </div>;
135
+ }
136
+
137
+ showOTP() {
138
+ this.renderer = <div data-layout="center-all">
139
+ <AppForm>
140
+ <h4>Enter One Time Password</h4>
91
141
  <FormField
92
- label="One Time Password"
93
- style-display={Bind.oneWay(() => this.authType !== "totp")}>
94
- <input type="tel" value={Bind.twoWaysImmediate(() => this.model.oneTimePassword)}/>
142
+ label="Password">
143
+ <input
144
+ autofocus={true}
145
+ type="tel" value={Bind.twoWaysImmediate(() => this.model.oneTimePassword)} />
95
146
  </FormField>
147
+ <div data-layout="row">
148
+ <button
149
+ event-click={() => this.showAuthenticator()}
150
+ data-layout="link">
151
+ Enter Authenticator Code instad.
152
+ </button>
153
+ </div>
96
154
  <div data-layout="command-row">
97
155
  <SaveIconTextButton
98
156
  event-click={() => this.login()}
99
- text="Go"
100
- />
157
+ text="Go" />
101
158
  </div>
102
- </Form>
159
+ </AppForm>
103
160
  </div>;
104
-
105
161
  }
106
162
 
163
+
107
164
  async showChangePassword() {
108
165
  this.model.newPassword = "";
109
166
 
110
167
  this.renderer = <div>
111
168
  <div data-layout="center-all">
112
- <Form>
169
+ <AppForm>
113
170
  <div>Update your password, your one time password is expired.</div>
114
171
  <FormField
115
172
  label="Password"
173
+ autofocus={true}
116
174
  required={true}
117
175
  error={Bind.oneWay(() => Validators.isValidPassword(this.password)
118
176
  ? ""
@@ -137,10 +195,21 @@ export default class LoginPage extends ContentPage {
137
195
  data-submit-event="login"
138
196
  text="Change Password & Login"/>
139
197
  </div>
140
- </Form>
198
+ </AppForm>
141
199
  </div>
142
200
  </div>;
143
201
 
144
202
  }
145
203
 
204
+ @Action({ onEvent: "forgot-password"})
205
+ async forgotPassword() {
206
+ if (/socialmail\.me/i.test(location.href)) {
207
+ PageNavigator.openPage(ForgotPassword);
208
+ return;
209
+ }
210
+ await PopupService.alert({
211
+ message: "Please contact your administrator"
212
+ });
213
+ }
214
+
146
215
  }
@@ -10,6 +10,7 @@ import Bind from "@web-atoms/core/dist/core/Bind";
10
10
  import Action from "@web-atoms/core/dist/view-model/Action";
11
11
  import AtomRepeater from "@web-atoms/web-controls/dist/basic/AtomRepeater";
12
12
  import { DateFormats } from "../../../date/DateFormats";
13
+ import AppForm from "../../../form/AppForm";
13
14
 
14
15
  export default class MultiFactorSetupPage extends ContentPage {
15
16
 
@@ -33,7 +34,7 @@ export default class MultiFactorSetupPage extends ContentPage {
33
34
 
34
35
  if (multiFactor) {
35
36
  this.renderer = <div data-layout="center-page-900px">
36
- <Form>
37
+ <AppForm>
37
38
  <FormField label="Methods">
38
39
  <AtomRepeater
39
40
  for="table"
@@ -57,7 +58,7 @@ export default class MultiFactorSetupPage extends ContentPage {
57
58
  text="Disable"
58
59
  />
59
60
  </div>
60
- </Form>
61
+ </AppForm>
61
62
  </div>;
62
63
  return;
63
64
  }
@@ -67,7 +68,7 @@ export default class MultiFactorSetupPage extends ContentPage {
67
68
  || this.model.authFactors.filter((i) => i.method === "one-time").length < 8;
68
69
 
69
70
  this.renderer = <div data-layout="center-page-900px">
70
- <Form>
71
+ <AppForm>
71
72
  <FormField label="Methods">
72
73
  <AtomRepeater
73
74
  for="table"
@@ -106,7 +107,7 @@ export default class MultiFactorSetupPage extends ContentPage {
106
107
  text="Disable"
107
108
  />
108
109
  </div>}
109
- </Form>
110
+ </AppForm>
110
111
  </div>;
111
112
  }
112
113
 
@@ -151,7 +152,7 @@ export default class MultiFactorSetupPage extends ContentPage {
151
152
  const authFactor = await this.userService.createAuthFactor(this.model);
152
153
  this.authFactor = authFactor;
153
154
  this.renderer = <div data-layout="center-page-900px">
154
- <Form>
155
+ <AppForm>
155
156
  <FormField label="">
156
157
  <div>Scan following QR Code in any compatible Authenticator App. Enter the generated code and hit the verify button.</div>
157
158
  </FormField>
@@ -173,7 +174,7 @@ export default class MultiFactorSetupPage extends ContentPage {
173
174
  text="Verify"
174
175
  />
175
176
  </div>
176
- </Form>
177
+ </AppForm>
177
178
  </div>;
178
179
  }
179
180
 
@@ -194,7 +195,7 @@ export default class MultiFactorSetupPage extends ContentPage {
194
195
  }
195
196
  text += "\r\n";
196
197
  this.renderer = <div data-layout="center-page-900px">
197
- <Form>
198
+ <AppForm>
198
199
  <FormField label="One Time Passwords">
199
200
  <div>Please copy and save these passwords somewhere safe. You can use
200
201
  them if you cannot access Authenticator app.
@@ -215,7 +216,7 @@ export default class MultiFactorSetupPage extends ContentPage {
215
216
  text="Go"
216
217
  />
217
218
  </div>
218
- </Form>
219
+ </AppForm>
219
220
  </div>;
220
221
  }
221
222
 
@@ -10,6 +10,7 @@ import Bind from "@web-atoms/core/dist/core/Bind";
10
10
  import Action from "@web-atoms/core/dist/view-model/Action";
11
11
  import AtomRepeater from "@web-atoms/web-controls/dist/basic/AtomRepeater";
12
12
  import { DateFormats } from "../../../date/DateFormats";
13
+ import AppForm from "../../../form/AppForm";
13
14
 
14
15
  export default class SetupMultiFactorPage extends PopupWindowPage {
15
16
 
@@ -61,7 +62,7 @@ export default class SetupMultiFactorPage extends PopupWindowPage {
61
62
  const authFactor = await this.userService.createAuthFactor(this.model);
62
63
  this.authFactor = authFactor;
63
64
  this.renderer = <div data-layout="center-page-900px">
64
- <Form>
65
+ <AppForm>
65
66
  <FormField label="">
66
67
  <div>Scan following QR Code in any compatible Authenticator App. Enter the generated code and hit the verify button.</div>
67
68
  </FormField>
@@ -83,7 +84,7 @@ export default class SetupMultiFactorPage extends PopupWindowPage {
83
84
  text="Verify"
84
85
  />
85
86
  </div>
86
- </Form>
87
+ </AppForm>
87
88
  </div>;
88
89
  }
89
90
 
@@ -118,7 +119,7 @@ export default class SetupMultiFactorPage extends PopupWindowPage {
118
119
 
119
120
  text += "\r\n";
120
121
  this.renderer = <div data-layout="center-page-900px">
121
- <Form>
122
+ <AppForm>
122
123
  <FormField label="One Time Passwords">
123
124
  <div>Please copy and save these one time codes somewhere safe. You can use
124
125
  them if you cannot access Authenticator app.
@@ -139,7 +140,7 @@ export default class SetupMultiFactorPage extends PopupWindowPage {
139
140
  text="Setup Authenticator"
140
141
  />
141
142
  </div>
142
- </Form>
143
+ </AppForm>
143
144
  </div>;
144
145
  }
145
146
 
@@ -71,10 +71,10 @@ StylerSets.register("low", styled.css `
71
71
  );
72
72
 
73
73
  export const Billboard = Block({
74
- className: "fad fa-symbols",
74
+ className: "fad fa-text-size",
75
75
  title: "Add Billboard",
76
76
  factory: () => <section styler-layout="billboard">
77
- <h1></h1>
78
- <h4></h4>
77
+ <h1 styler-color="border-color">Billboard</h1>
78
+ <h4>Billboard</h4>
79
79
  </section>
80
80
  });
@@ -0,0 +1,27 @@
1
+ import XNode from "@web-atoms/core/dist/core/XNode";
2
+ import { Block } from "./Block";
3
+ import styled from "@web-atoms/core/dist/style/styled";
4
+ import { StylerSets } from "../styler/StylerSets";
5
+
6
+ styled.css `
7
+ -webkit-text-fill-color: transparent;
8
+ -webkit-text-stroke-color: currentColor;
9
+ -webkit-text-stroke-width: 1px;
10
+ text-decoration: none;
11
+ font-style: normal;
12
+ font-weight: 900;
13
+ text-align: center;
14
+ padding-left: 7px;
15
+ font-family: monospace;
16
+ font-size: 1.5rem;
17
+ `.installGlobal(".editor-outlined-text");
18
+
19
+ export const OutlineText = Block({
20
+ className: "editor-outlined-text",
21
+ title: "Add Text with Outline",
22
+ text:"A",
23
+ factory: () => <div
24
+ styler-text-outline="size1px">
25
+ Outline
26
+ </div>
27
+ });
@@ -23,12 +23,14 @@ export function InstallDragDropHelper(editor: HtmlPageEditor) {
23
23
  const dropTarget = (editor as any).dropTarget as SelectedElement;
24
24
 
25
25
  document.addEventListener("dragstart", (e) => {
26
+ setTimeout(() => document.body.contentEditable = "false", 0);
26
27
  dragged = e.target as HTMLElement;
27
28
  e.dataTransfer.dropEffect = "move";
28
29
  e.dataTransfer.setDragImage(dragged, 0, 0);
29
30
  });
30
31
 
31
32
  document.addEventListener("dragover", (e) => {
33
+ setTimeout(() => document.body.contentEditable = "false", 0);
32
34
  const target = dropTarget.getDropTarget( dragged, e.target as HTMLElement);
33
35
  e.preventDefault();
34
36
  e.dataTransfer.dropEffect = "copy";
@@ -68,6 +70,7 @@ export function InstallDragDropHelper(editor: HtmlPageEditor) {
68
70
  e.preventDefault();
69
71
  dropTarget.clear();
70
72
  dragged = null;
73
+ setTimeout(() => document.body.contentEditable = "true", 0);
71
74
  });
72
75
 
73
76
  document.addEventListener("drop", (e) => {
@@ -87,6 +90,7 @@ export function InstallDragDropHelper(editor: HtmlPageEditor) {
87
90
  target.appendChild(insert);
88
91
  dropTarget.clear();
89
92
  editor.commit();
93
+ setTimeout(() => document.body.contentEditable = "true", 0);
90
94
  return;
91
95
  }
92
96
  switch(dropTarget.dropAnchor) {
@@ -101,10 +105,12 @@ export function InstallDragDropHelper(editor: HtmlPageEditor) {
101
105
  }
102
106
  editor.commit();
103
107
  dropTarget.clear();
108
+ setTimeout(() => document.body.contentEditable = "true", 0);
104
109
  });
105
110
 
106
111
  document.addEventListener("dragleave", (e) => {
107
112
  dropTarget.clear();
113
+ setTimeout(() => document.body.contentEditable = "true", 0);
108
114
  });
109
115
 
110
116
  setTimeout(() => {
@@ -34,6 +34,7 @@ import { descendentElementIterator } from "@web-atoms/core/dist/web/core/AtomUI"
34
34
  import { PriceTable } from "../blocks/PriceBox";
35
35
  import { BindableProperty } from "@web-atoms/core/dist/core/BindableProperty";
36
36
  import { Billboard } from "../blocks/Billboard";
37
+ import { OutlineText } from "../blocks/OutlineText";
37
38
 
38
39
  styled.css `
39
40
  display: grid;
@@ -286,6 +287,7 @@ export default class HtmlPageEditor extends AtomControl {
286
287
  <PriceTable/>
287
288
  <TypeWriterComponent/>
288
289
  <Billboard/>
290
+ <OutlineText/>
289
291
  </EditorToolbar>
290
292
  <div data-element="editor"
291
293
  style-max-width={Bind.oneWay(() => this.maxWidth > 768 ? "" : this.maxWidth + "px" )}>
@@ -66,7 +66,7 @@ export class SelectedElement {
66
66
  const isDropTarget = /^(main|section)$/i;
67
67
 
68
68
  // check if dragged has root only....
69
- if (isDropTarget.test(dragged.tagName)) {
69
+ if (isDropTarget.test(dragged?.tagName)) {
70
70
  let start = target;
71
71
  while (!isDropTarget.test(start.tagName)) {
72
72
  start = start.parentElement;