Skip to content

Commit cc9725f

Browse files
author
Hamed
committed
first version
1 parent bc06e29 commit cc9725f

File tree

6 files changed

+3690
-1
lines changed

6 files changed

+3690
-1
lines changed

.gitignore

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
# Created by .ignore support plugin (hsz.mobi)
2+
### Example user template template
3+
### Example user template
4+
5+
# IntelliJ project files
6+
.idea
7+
*.iml
8+
out
9+
gen
10+
11+
### Node template
112
# Logs
213
logs
314
*.log
@@ -36,7 +47,7 @@ build/Release
3647
node_modules/
3748
jspm_packages/
3849

39-
# TypeScript v1 declaration files
50+
# Typescript v1 declaration files
4051
typings/
4152

4253
# Optional npm cache directory
@@ -59,3 +70,5 @@ typings/
5970

6071
# next.js build output
6172
.next
73+
74+
dist/

package.json

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"name": "autocomplete2",
3+
"version": "1.0.0",
4+
"description": "Utilities for suggesting and autocompleting text in autosuggest and autocomplete components",
5+
"main": "dist/Autocomplete.js",
6+
"files": [
7+
"src",
8+
"dist",
9+
"tests"
10+
],
11+
"directories": {
12+
"test": "tests"
13+
},
14+
"scripts": {
15+
"build": "webpack --hide-modules",
16+
"test": "mocha tests"
17+
},
18+
"repository": {
19+
"type": "git",
20+
"url": "git+https://github.com/parsisolution/autocomplete.git"
21+
},
22+
"keywords": [
23+
"autocomplete",
24+
"autosuggest",
25+
"text",
26+
"utility",
27+
"complete",
28+
"suggest",
29+
"replace"
30+
],
31+
"author": "Hamed Ehtesham",
32+
"license": "MIT",
33+
"bugs": {
34+
"url": "https://github.com/parsisolution/autocomplete/issues"
35+
},
36+
"homepage": "https://github.com/parsisolution/autocomplete#readme",
37+
"devDependencies": {
38+
"babel-core": "^6.26.3",
39+
"babel-loader": "^7.1.4",
40+
"babel-plugin-transform-object-rest-spread": "^6.26.0",
41+
"babel-plugin-transform-runtime": "^6.23.0",
42+
"babel-preset-env": "^1.7.0",
43+
"chai": "^4.1.2",
44+
"chai-as-promised": "^7.1.1",
45+
"lodash": "^4.17.10",
46+
"mocha": "^5.2.0",
47+
"template-banner-webpack-plugin": "^1.1.2",
48+
"uglifyjs-webpack-plugin": "^1.2.5",
49+
"webpack": "^3.10.0"
50+
}
51+
}

src/Autocomplete.js

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
class Autocomplete {
2+
3+
constructor(options) {
4+
this.options = options;
5+
}
6+
7+
static replace(text, position, replaceValue, removeTrailing = false, afterSpace = false) {
8+
let beginning = text.substring(0, position);
9+
let trailing = text.substring(position);
10+
11+
let lastIndexOf = this.lastWhitespaceIndex(beginning);
12+
let start = beginning.substr(0, lastIndexOf + 1);
13+
let end = trailing;
14+
if (removeTrailing) {
15+
let indexOf = trailing.indexOf(' ');
16+
17+
end = '';
18+
if (indexOf !== -1)
19+
end = trailing.substr(indexOf);
20+
}
21+
22+
if (afterSpace && !end.startsWith(' '))
23+
end = ' ' + end;
24+
25+
return {
26+
before: beginning.length - start.length,
27+
after: trailing.length - end.length,
28+
text: start + replaceValue + end,
29+
};
30+
}
31+
32+
static lastWhitespaceIndex(beginning) {
33+
let lastSpaceIndex = beginning.lastIndexOf(' ');
34+
let lastLineBreakIndex = beginning.lastIndexOf('\n');
35+
return lastSpaceIndex > lastLineBreakIndex ? lastSpaceIndex : lastLineBreakIndex;
36+
}
37+
38+
suggest(text, position) {
39+
let beginning = text.substring(0, position);
40+
// let trailing = text.substring(position);
41+
42+
let lastWord = beginning;
43+
let lastIndexOf = Autocomplete.lastWhitespaceIndex(beginning);
44+
if (lastIndexOf !== -1) {
45+
lastWord = beginning.substr(lastIndexOf + 1);
46+
}
47+
48+
let suggests = [], found = false;
49+
50+
for (let option of this.options) {
51+
let step = option;
52+
let list = step.list;
53+
if (typeof list !== 'function') {
54+
const data = list;
55+
if (list instanceof Array) {
56+
list = search => {
57+
let filtered = [];
58+
59+
let filter = require('lodash/fp/filter');
60+
if (search)
61+
filtered = filter(o => o.startsWith(search))(data);
62+
else if (search === '')
63+
filtered = data;
64+
65+
return new Promise(resolve => {
66+
resolve(filtered);
67+
});
68+
};
69+
} else {
70+
list = search => {
71+
let filtered = [];
72+
73+
let cloneDeep = require('lodash/fp/cloneDeep');
74+
let keys = require('lodash/fp/keys');
75+
let filter = require('lodash/fp/filter');
76+
let start = '';
77+
step = cloneDeep(option);
78+
while (!(step.list instanceof Array)) {
79+
let changed = false;
80+
for (let key of keys(step.list)) {
81+
if (search.startsWith(start + key)) {
82+
let next_step = step.list[key];
83+
if (typeof next_step === 'object') {
84+
if (next_step.hasOwnProperty('list')) {
85+
if (!next_step.hasOwnProperty('trigger') || typeof next_step.trigger !== 'string')
86+
next_step.trigger = step.list.__trigger__;
87+
if (!next_step.hasOwnProperty('color') || typeof next_step.color !== 'string')
88+
next_step.color = step.list.__color__;
89+
changed = true;
90+
step = next_step;
91+
start += key + step.trigger;
92+
}
93+
}
94+
}
95+
}
96+
if (!changed)
97+
if (typeof step === 'function')
98+
return new Promise(resolve => {
99+
filtered = step(search);
100+
resolve(filtered);
101+
});
102+
else if (!(step.list instanceof Array))
103+
step.list = keys(step.list);
104+
}
105+
if (option !== step) {
106+
step.trigger = option.trigger + start;
107+
if (search.startsWith(start))
108+
search = search.substr(start.length);
109+
}
110+
if (search)
111+
filtered = filter(o => o !== '__trigger__' && o !== '__color__' && o.startsWith(search))(step.list);
112+
else if (search === '')
113+
filtered = filter(o => o !== '__trigger__' && o !== '__color__')(step.list);
114+
115+
return new Promise(resolve => {
116+
resolve(filtered);
117+
});
118+
};
119+
}
120+
}
121+
122+
let result = undefined;
123+
if (!step.pattern)
124+
result = lastWord.startsWith(step.trigger) ? lastWord.substr(step.trigger.length) : false;
125+
else {
126+
let pattern = new RegExp(step.pattern);
127+
pattern = new RegExp(step.trigger + pattern.source, pattern.flags);
128+
result = pattern.exec(lastWord);
129+
}
130+
131+
if (result || result === '') {
132+
found = true;
133+
list(result).then(filtered => {
134+
suggests.push(...filtered.map(d => {
135+
return {t: step.trigger, c: step.color, d}
136+
}));
137+
});
138+
}
139+
}
140+
141+
return new Promise((resolve, reject) => {
142+
if (found)
143+
resolve(suggests);
144+
else
145+
reject(new Error('not found!'));
146+
});
147+
}
148+
}
149+
150+
module.exports = Autocomplete;

0 commit comments

Comments
 (0)