@foxford/sdk 5.0.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.
@@ -0,0 +1,270 @@
1
+ // src/plugins/autocomplete-email/index.ts
2
+ import levenstein from "js-levenshtein";
3
+ import merge from "lodash/merge";
4
+ var DEFAULT_DOMAINS = [
5
+ "gmail.com",
6
+ "mail.ru",
7
+ "yandex.ru",
8
+ "icloud.com",
9
+ "bk.ru",
10
+ "list.ru",
11
+ "inbox.ru",
12
+ "rambler.ru",
13
+ "internet.ru",
14
+ "ya.ru",
15
+ "mail.com",
16
+ "yandex.com",
17
+ "foxford.ru",
18
+ "hotmail.com",
19
+ "vk.com",
20
+ "yahoo.com",
21
+ "outlook.com",
22
+ "yandex.by",
23
+ "yandex.kz",
24
+ "xmail.ru",
25
+ "tut.by",
26
+ "msn.com",
27
+ "me.com"
28
+ ];
29
+ var DEFAULT_OPTIONS = {
30
+ emailDomains: DEFAULT_DOMAINS,
31
+ suggestionClass: "emailAutoSugg",
32
+ suggestionColor: "var(--silver, #b8b8b8)",
33
+ suitableColor: "var(--silver, #b8b8b8)",
34
+ suitableEmailColor: "var(--accent, #1A96F6)",
35
+ suitableFontSize: "12px",
36
+ suitableLevenshteinTreshold: 5,
37
+ suitableLineHeight: "1.7"
38
+ };
39
+ var KEY_CODES = {
40
+ BACKSPACE: "Backspace",
41
+ DELETE: "Delete",
42
+ DOWN_ARROW: "ArrowDown",
43
+ ENTER: "Enter",
44
+ RIGHT_ARROW: "ArrowRight",
45
+ TAB: "Tab"
46
+ };
47
+ var EmailAutocomplete = class {
48
+ constructor(node, options) {
49
+ this.options = DEFAULT_OPTIONS;
50
+ this.containerValue = null;
51
+ this.suggOverlay = null;
52
+ this.value = "";
53
+ this.suggestion = "";
54
+ this.leftOffset = null;
55
+ this.suitableExists = false;
56
+ this.suitableContainer = null;
57
+ this.bindListeners = () => {
58
+ if (this.suggOverlay) this.suggOverlay.addEventListener("mousedown touchstart", this.onFocus);
59
+ this.node.addEventListener("keyup", this.displaySuggestion);
60
+ this.node.addEventListener("paste", this.onPaste);
61
+ this.node.addEventListener("blur", this.autocomplete);
62
+ this.node.addEventListener("keydown", this.onKeydown);
63
+ };
64
+ this.onFocus = () => {
65
+ this.node.focus();
66
+ };
67
+ this.onKeydown = (e) => {
68
+ if ([KEY_CODES.BACKSPACE, KEY_CODES.DELETE].includes(e.code)) {
69
+ this.hideSuitableEmail();
70
+ return;
71
+ }
72
+ if (![KEY_CODES.DOWN_ARROW, KEY_CODES.ENTER, KEY_CODES.RIGHT_ARROW, KEY_CODES.TAB].includes(e.code) || KEY_CODES.TAB === e.code && !this.suggestion)
73
+ return;
74
+ this.node.addEventListener("blur", this.autocomplete);
75
+ const { target } = e;
76
+ if (target instanceof HTMLElement) {
77
+ target.blur();
78
+ }
79
+ };
80
+ this.onPaste = (e) => {
81
+ if (!e.clipboardData) throw new Error("Not found clipboardData");
82
+ const text = e.clipboardData.getData("text/plain");
83
+ this.value = text;
84
+ this.node.removeEventListener("blur", this.autocomplete);
85
+ const list = this.findSuitableEmails(text);
86
+ this.showSuitableEmail(list);
87
+ };
88
+ this.findSuitableEmails = (text) => {
89
+ const strArr = text.split("@");
90
+ let lastPart = text;
91
+ if (strArr.length <= 1) return [];
92
+ lastPart = strArr.pop() || "";
93
+ const distances = (this.options.emailDomains || []).map((item) => ({ distance: levenstein(lastPart, item), item }));
94
+ const treshold = this.options.suitableLevenshteinTreshold;
95
+ const preparedDistances = distances.sort((a, b) => a.distance - b.distance).filter((item) => item.distance <= treshold);
96
+ const suitableList = preparedDistances.slice(0, 3).map((obj) => `${strArr.join("")}@${obj.item}`);
97
+ return suitableList;
98
+ };
99
+ this.showSuitableEmail = (suitableList) => {
100
+ if (suitableList.length === 0) return;
101
+ if (this.suitableContainer) this.hideSuitableEmail();
102
+ const suitableEmail = suitableList[0];
103
+ if (this.value === suitableEmail) return;
104
+ const text = document.createTextNode("\u0412\u043E\u0437\u043C\u043E\u0436\u043D\u043E, \u0432\u044B \u0438\u043C\u0435\u043B\u0438 \u0432 \u0432\u0438\u0434\u0443 ");
105
+ const email = document.createElement("span");
106
+ const suitableMessage = document.createElement("span");
107
+ suitableMessage.style.display = "block";
108
+ suitableMessage.style.fontFamily = this.nodeStyle.getPropertyValue("font-family");
109
+ suitableMessage.style.fontWeight = this.nodeStyle.getPropertyValue("font-weight");
110
+ suitableMessage.style.letterSpacing = this.nodeStyle.getPropertyValue("letter-spacing");
111
+ suitableMessage.style.color = this.options.suitableColor || "";
112
+ suitableMessage.style.fontSize = this.options.suitableFontSize || "";
113
+ suitableMessage.style.lineHeight = this.options.suitableLineHeight || "";
114
+ email.style.fontFamily = this.nodeStyle.getPropertyValue("font-family");
115
+ email.style.fontWeight = this.nodeStyle.getPropertyValue("font-weight");
116
+ email.style.letterSpacing = this.nodeStyle.getPropertyValue("letter-spacing");
117
+ email.style.color = this.options.suitableEmailColor || "";
118
+ email.style.fontSize = this.options.suitableFontSize || "";
119
+ email.style.lineHeight = this.options.suitableLineHeight || "";
120
+ email.style.cursor = "pointer";
121
+ if (suitableEmail) email.textContent = suitableEmail;
122
+ suitableMessage.append(text);
123
+ suitableMessage.append(email);
124
+ this.suitableContainer = suitableMessage;
125
+ this.node.after(this.suitableContainer);
126
+ email.addEventListener("click", this.onClickSuitableEmail, { once: true });
127
+ this.suitableExists = true;
128
+ if (typeof this.options.onShowSuitableEmail === "function") this.options.onShowSuitableEmail();
129
+ };
130
+ this.hideSuitableEmail = () => {
131
+ if (this.suitableContainer) {
132
+ this.suitableContainer.remove();
133
+ this.suitableContainer = null;
134
+ }
135
+ if (this.suggOverlay) {
136
+ this.suggOverlay.textContent = "";
137
+ }
138
+ this.suitableExists = false;
139
+ if (typeof this.options.onHideSuitableEmail === "function") this.options.onHideSuitableEmail();
140
+ };
141
+ this.onClickSuitableEmail = (e) => {
142
+ e.preventDefault();
143
+ e.stopPropagation();
144
+ if (!(e.target instanceof HTMLSpanElement)) return;
145
+ this.changeValue(e.target.textContent || "");
146
+ this.hideSuitableEmail();
147
+ this.node.addEventListener("blur", this.autocomplete);
148
+ if (typeof this.options.onClickSuitableEmail === "function") this.options.onClickSuitableEmail();
149
+ };
150
+ this.autocomplete = (e) => {
151
+ e.stopPropagation();
152
+ if (this.suitableExists) this.hideSuitableEmail();
153
+ if (typeof this.suggestion === "undefined" || typeof this.suggestion === "string" && this.suggestion.length < 1) {
154
+ const list = this.findSuitableEmails(this.value || "");
155
+ this.showSuitableEmail(list);
156
+ return false;
157
+ }
158
+ const value = `${this.value || ""}${this.suggestion || ""}`;
159
+ this.clear();
160
+ this.changeValue(value);
161
+ return true;
162
+ };
163
+ this.changeValue = (value) => {
164
+ if (typeof this.options.onAutocomplete === "function") this.options.onAutocomplete(value);
165
+ else this.node.value = value;
166
+ };
167
+ this.displaySuggestion = (event) => {
168
+ this.value = this.node.value;
169
+ this.suggestion = this.suggest(this.value);
170
+ if (!this.suggOverlay || !this.containerValue) return;
171
+ if (!this.suggestion.length) {
172
+ this.suggOverlay.textContent = "";
173
+ } else {
174
+ event.preventDefault();
175
+ }
176
+ this.suggOverlay.textContent = this.suggestion;
177
+ this.containerValue.textContent = this.value || "";
178
+ if (this.leftOffset === null) {
179
+ let w = this.node.clientWidth;
180
+ w -= parseFloat(this.nodeStyle.paddingLeft) + parseFloat(this.nodeStyle.paddingRight);
181
+ this.leftOffset = (this.node.offsetWidth - w) / 2;
182
+ }
183
+ const cvalWidth = this.containerValue.clientWidth;
184
+ if (this.node.offsetWidth > cvalWidth) {
185
+ this.suggOverlay.style.left = `${this.leftOffset + cvalWidth}px`;
186
+ }
187
+ };
188
+ this.destroy = () => {
189
+ var _a;
190
+ (_a = this.suggOverlay) == null ? void 0 : _a.removeEventListener("mousedown touchstart", this.onFocus);
191
+ this.node.removeEventListener("keyup", this.displaySuggestion);
192
+ this.node.removeEventListener("paste", this.onPaste);
193
+ this.node.removeEventListener("blur", this.autocomplete);
194
+ this.node.removeEventListener("keydown", this.onKeydown);
195
+ this.suggOverlay = null;
196
+ this.containerValue = null;
197
+ this.suitableContainer = null;
198
+ };
199
+ this.clear = () => {
200
+ if (this.suggOverlay instanceof HTMLSpanElement) this.suggOverlay.textContent = "";
201
+ if (this.containerValue instanceof HTMLSpanElement) this.containerValue.textContent = "";
202
+ };
203
+ if (!(node instanceof HTMLInputElement)) throw new TypeError("Node must be html input element");
204
+ this.node = node;
205
+ this.nodeRects = this.node.getBoundingClientRect();
206
+ this.options = merge(this.options, options);
207
+ this.suggOverlay = document.createElement("span");
208
+ this.containerValue = document.createElement("span");
209
+ this.suitableContainer = document.createElement("span");
210
+ this.nodeStyle = getComputedStyle(this.node);
211
+ }
212
+ apply() {
213
+ if (this.node.eac !== void 0 && this.node.eac !== null) return;
214
+ this.node.eac = this;
215
+ const parentNode = this.node.parentElement;
216
+ let topOffset = 0;
217
+ if (parentNode) {
218
+ const parentRects = parentNode.getBoundingClientRect();
219
+ topOffset = Math.abs(parentRects.top - this.nodeRects.top);
220
+ }
221
+ this.containerValue = document.createElement("span");
222
+ this.containerValue.style.visibility = "hidden";
223
+ this.containerValue.style.position = "absolute";
224
+ this.containerValue.style.display = "inline-block";
225
+ this.containerValue.style.fontFamily = this.nodeStyle.getPropertyValue("font-family");
226
+ this.containerValue.style.fontWeight = this.nodeStyle.getPropertyValue("font-weight");
227
+ this.containerValue.style.fontSize = this.nodeStyle.getPropertyValue("font-size");
228
+ this.containerValue.style.letterSpacing = this.nodeStyle.getPropertyValue("letter-spacing");
229
+ const border = (this.node.offsetHeight - this.node.clientHeight) / 2;
230
+ this.suggOverlay = document.createElement("span");
231
+ if (typeof this.options.suggestionClass === "string") this.suggOverlay.classList.add(this.options.suggestionClass);
232
+ this.suggOverlay.style.display = "flex";
233
+ this.suggOverlay.style.alignItems = "center";
234
+ this.suggOverlay.style.position = "absolute";
235
+ this.suggOverlay.style.top = `${topOffset}px`;
236
+ this.suggOverlay.style.left = "0";
237
+ this.suggOverlay.style.height = `${this.node.offsetHeight}px`;
238
+ this.suggOverlay.style.paddingTop = `${parseInt(this.nodeStyle.getPropertyValue("padding-top"), 10) + border}px`;
239
+ this.suggOverlay.style.paddingBottom = `${parseInt(this.nodeStyle.getPropertyValue("padding-bottom"), 10) + border}px`;
240
+ this.suggOverlay.style.lineHeight = this.nodeStyle.getPropertyValue("line-height");
241
+ this.suggOverlay.style.fontSize = this.nodeStyle.getPropertyValue("font-size");
242
+ this.suggOverlay.style.color = this.options.suggestionColor || "";
243
+ this.containerValue.style.fontFamily = this.nodeStyle.getPropertyValue("font-family");
244
+ this.containerValue.style.fontWeight = this.nodeStyle.getPropertyValue("font-weight");
245
+ this.containerValue.style.fontSize = this.nodeStyle.getPropertyValue("font-size");
246
+ this.containerValue.style.letterSpacing = this.nodeStyle.getPropertyValue("letter-spacing");
247
+ this.node.after(this.containerValue);
248
+ this.containerValue.after(this.suggOverlay);
249
+ this.bindListeners();
250
+ }
251
+ suggest(str) {
252
+ const strArr = str.split("@");
253
+ let lastPart = str;
254
+ if (strArr.length > 1) {
255
+ lastPart = strArr.pop() || "";
256
+ if (!lastPart.length) {
257
+ return "";
258
+ }
259
+ } else {
260
+ return "";
261
+ }
262
+ const match = (this.options.emailDomains || []).filter((domain) => {
263
+ return domain.indexOf(lastPart) === 0;
264
+ }).shift() || "";
265
+ return match.replace(lastPart, "");
266
+ }
267
+ };
268
+ export {
269
+ EmailAutocomplete
270
+ };
@@ -0,0 +1,24 @@
1
+ // @flow
2
+ import type { SdkPlugin } from "../index";
3
+
4
+ declare interface EacOptions {
5
+ emailDomains?: Array<string>;
6
+ suggestionClass?: string;
7
+ suggestionColor?: string;
8
+ onAutocomplete?: (_value: string) => void;
9
+ onShowSuitableEmail?: () => void;
10
+ onClickSuitableEmail?: () => void;
11
+ onHideSuitableEmail?: () => void;
12
+ suitableColor?: string;
13
+ suitableEmailColor?: string;
14
+ suitableFontSize?: string;
15
+ suitableLineHeight?: string;
16
+ suitableLevenshteinTreshold?: number;
17
+ }
18
+ declare class EmailAutocomplete implements SdkPlugin {
19
+ constructor(node: HTMLInputElement, options?: EacOptions): this;
20
+ apply(): void;
21
+ destroy: () => void;
22
+ clear: () => void;
23
+ }
24
+ declare export { EmailAutocomplete };