Revert "⌨️Add autocomplete widget with command line completion"

This commit is contained in:
Liyas Thomas
2019-08-31 22:29:32 +05:30
committed by GitHub
parent 7c49f58565
commit dd5dfdbabd
5 changed files with 581 additions and 808 deletions

View File

@@ -10,8 +10,6 @@
--bg-dark-color: #000000; --bg-dark-color: #000000;
// Background color // Background color
--bg-color: #121212; --bg-color: #121212;
// Auto-complete color
--atc-color: #212121;
// Text color // Text color
--fg-color: #FFF; --fg-color: #FFF;
@@ -28,8 +26,6 @@
--bg-dark-color: #ffffff; --bg-dark-color: #ffffff;
// Background color // Background color
--bg-color: #F6F8FA; --bg-color: #F6F8FA;
// Auto-complete color
--atc-color: #F1F1F1;
// Text color // Text color
--fg-color: #121212; --fg-color: #121212;

View File

@@ -1,190 +0,0 @@
<template>
<div class="autocomplete-wrapper">
<label>
<slot />
<input type="text"
:placeholder="placeholder"
v-model="value"
@input="updateSuggestions"
@keyup="updateSuggestions"
@click="updateSuggestions"
@keydown="handleKeystroke"
ref="acInput"
:spellcheck="spellcheck"
:autocapitalize="spellcheck"
:autocorrect="spellcheck">
<ul class="suggestions" v-if="suggestions.length > 0 && suggestionsVisible" :style="{ transform: `translate(${suggestionsOffsetLeft}px, 0)` }">
<li v-for="(suggestion, index) in suggestions" @click.prevent="forceSuggestion(suggestion)" :class="{ active: currentSuggestionIndex === index }">{{ suggestion }}</li>
</ul>
</label>
</div>
</template>
<style lang="scss" scoped>
.autocomplete-wrapper {
position: relative;
input:focus + ul.suggestions, ul.suggestions:hover {
display: block;
}
ul.suggestions {
display: none;
background-color: var(--atc-color);
position: absolute;
top: 90%;
margin: 0 4px;
left: 0;
padding: 0;
border-radius: 0 0 4px 4px;
z-index: 9999;
transition: transform 200ms ease-out;
li {
width: 100%;
display: block;
margin: 5px 0;
padding: 10px 10px;
font-weight: 700;
font-size: 18px;
font-family: monospace;
white-space: pre-wrap;
&:hover, &.active {
background-color: var(--ac-color);
}
}
}
}
</style>
<script>
const KEY_TAB = 9;
const KEY_ESC = 27;
const KEY_ARROW_UP = 38;
const KEY_ARROW_DOWN = 40;
export default {
props: {
spellcheck: {
type: Boolean,
default: true,
required: false
},
placeholder: {
type: String,
default: 'Start typing...',
required: false
},
source: {
type: Array,
required: true
},
value: {}
},
watch: {
value () {
this.$emit('input', this.value);
}
},
data () {
return {
value: "",
selectionStart: 0,
suggestionsOffsetLeft: 0,
currentSuggestionIndex: -1,
suggestionsVisible: false
}
},
methods: {
updateSuggestions (event) {
// Hide suggestions if ESC pressed.
if(event.which && event.which === KEY_ESC){
event.preventDefault();
this.suggestionsVisible = false;
this.currentSuggestionIndex = -1;
return;
}
// As suggestions is a reactive property, this implicitly
// causes suggestions to update.
this.selectionStart = this.$refs.acInput.selectionStart;
this.suggestionsOffsetLeft = (12 * this.selectionStart);
this.suggestionsVisible = true;
},
forceSuggestion (text) {
let input = this.value.substring(0, this.selectionStart);
this.value = input + text;
this.selectionStart = this.value.length;
this.suggestionsVisible = true;
this.currentSuggestionIndex = -1;
},
handleKeystroke (event) {
if(event.which === KEY_ARROW_UP){
event.preventDefault();
this.currentSuggestionIndex = this.currentSuggestionIndex - 1 >= 0
? this.currentSuggestionIndex - 1
: 0;
}else if(event.which === KEY_ARROW_DOWN){
event.preventDefault();
this.currentSuggestionIndex = this.currentSuggestionIndex < this.suggestions.length - 1
? this.currentSuggestionIndex + 1
: this.suggestions.length - 1;
}
if(event.which === KEY_TAB){
event.preventDefault();
let activeSuggestion = this.suggestions[this.currentSuggestionIndex >= 0 ? this.currentSuggestionIndex : 0];
if(activeSuggestion){
let input = this.value.substring(0, this.selectionStart);
this.value = input + activeSuggestion;
}
}
}
},
computed: {
/**
* Gets the suggestions list to be displayed under the input box.
*
* @returns {default.props.source|{type, required}}
*/
suggestions () {
let input = this.value.substring(0, this.selectionStart);
return this.source.filter((entry) => {
return entry.toLowerCase().startsWith(input.toLowerCase())
&& input.toLowerCase() !== entry.toLowerCase();
})
// Cut off the part that's already been typed.
.map((entry) => entry.substring(this.selectionStart))
// We only want the top 3 suggestions.
.slice(0, 3);
}
},
mounted () {
this.updateSuggestions({
target: this.$refs.acInput
});
}
}
</script>

7
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "postwoman", "name": "postwoman",
"version": "0.1.0", "version": "1.0.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -10128,11 +10128,6 @@
"resolved": "https://registry.npmjs.org/vue-virtual-scroll-list/-/vue-virtual-scroll-list-1.4.2.tgz", "resolved": "https://registry.npmjs.org/vue-virtual-scroll-list/-/vue-virtual-scroll-list-1.4.2.tgz",
"integrity": "sha512-jcXl1cYDxGZX+aF9vsUauXWnUkXm8oQxnvLTJ8UMTmMxwzbmlHX7vs0xGDdEej91vJpBNrdNNseWPxboTvI+UA==" "integrity": "sha512-jcXl1cYDxGZX+aF9vsUauXWnUkXm8oQxnvLTJ8UMTmMxwzbmlHX7vs0xGDdEej91vJpBNrdNNseWPxboTvI+UA=="
}, },
"vuejs-auto-complete": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/vuejs-auto-complete/-/vuejs-auto-complete-0.9.0.tgz",
"integrity": "sha512-7UV3s9bXdnsbGARhHcOuDAszGUsz7JpsFKBfHQuQvo4rfH0yQIru2Rb/x2bWU+m+VW4fS9DKDSYi6tY511QSIA=="
},
"vuex": { "vuex": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.1.1.tgz", "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.1.1.tgz",

View File

@@ -7,9 +7,11 @@
"scripts": { "scripts": {
"predev": "node build.js --dev", "predev": "node build.js --dev",
"dev": "nuxt", "dev": "nuxt",
"prebuild": "node build.js", "prebuild": "node build.js",
"build": "nuxt build", "build": "nuxt build",
"start": "nuxt start", "start": "nuxt start",
"pregenerate": "node build.js", "pregenerate": "node build.js",
"generate": "nuxt generate" "generate": "nuxt generate"
}, },
@@ -18,7 +20,6 @@
"@nuxtjs/pwa": "^3.0.0-0", "@nuxtjs/pwa": "^3.0.0-0",
"nuxt": "^2.9.2", "nuxt": "^2.9.2",
"vue-virtual-scroll-list": "^1.4.2", "vue-virtual-scroll-list": "^1.4.2",
"vuejs-auto-complete": "^0.9.0",
"vuex-persist": "^2.1.0" "vuex-persist": "^2.1.0"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -31,12 +31,14 @@
<pw-section class="blue-dark" label="Request Body" v-if="method === 'POST' || method === 'PUT' || method === 'PATCH'"> <pw-section class="blue-dark" label="Request Body" v-if="method === 'POST' || method === 'PUT' || method === 'PATCH'">
<ul> <ul>
<li> <li>
<autocomplete v-model="contentType" :source="validContentTypes" :spellcheck="false">Content Type</autocomplete> <label>Content Type</label>
<select v-model="contentType">
<option>application/json</option>
<option>www-form/urlencoded</option>
</select>
<span> <span>
<pw-toggle :on="rawInput" @change="rawInput = !rawInput"> <input v-model="rawInput" style="cursor: pointer;" type="checkbox" id="rawInput">
Raw input {{ rawInput ? "enabled" : "disabled" }} <label for="rawInput" style="cursor: pointer;">Raw Input</label>
</pw-toggle>
</span> </span>
</li> </li>
</ul> </ul>
@@ -190,11 +192,9 @@
</template> </template>
<script> <script>
import autocomplete from '../components/autocomplete';
import history from "../components/history"; import history from "../components/history";
import section from "../components/section"; import section from "../components/section";
import textareaAutoHeight from "../directives/textareaAutoHeight"; import textareaAutoHeight from "../directives/textareaAutoHeight";
import toggle from "../components/toggle";
const statusCategories = [{ const statusCategories = [{
name: 'informational', name: 'informational',
@@ -248,13 +248,10 @@
}, },
components: { components: {
'pw-section': section, 'pw-section': section,
'pw-toggle': toggle, history
history,
autocomplete
}, },
data() { data() {
return { return {
method: 'GET', method: 'GET',
url: 'https://reqres.in', url: 'https://reqres.in',
auth: 'None', auth: 'None',
@@ -273,33 +270,7 @@
headers: '', headers: '',
body: '' body: ''
}, },
previewEnabled: false, previewEnabled: false
/**
* These are content types that can be automatically
* serialized by postwoman.
*/
knownContentTypes: [
'application/json',
'application/x-www-form-urlencoded'
],
/**
* These are a list of Content Types known to Postwoman.
*/
validContentTypes: [
'application/json',
'application/hal+json',
'application/xml',
'application/x-www-form-urlencoded',
'text/html',
'text/plain'
]
}
},
watch: {
contentType(val) {
this.rawInput = !this.knownContentTypes.includes(val);
} }
}, },
computed: { computed: {
@@ -321,7 +292,7 @@
} = this } = this
if (this.contentType === 'application/json') { if (this.contentType === 'application/json') {
try { try {
const obj = JSON.parse(`{${bodyParams.filter(({key}) => !!key).map(({key, value}) => ` const obj = JSON.parse(`{${bodyParams.filter(({ key }) => !!key).map(({ key, value }) => `
"${key}": "${value}" "${key}": "${value}"
`).join()}}`) `).join()}}`)
return JSON.stringify(obj) return JSON.stringify(obj)
@@ -412,7 +383,7 @@
requestBody = this.rawInput ? this.rawParams : this.rawRequestBody; requestBody = this.rawInput ? this.rawParams : this.rawRequestBody;
Object.assign(headers, { Object.assign(headers, {
//'Content-Length': requestBody.length, 'Content-Length': requestBody.length,
'Content-Type': `${this.contentType}; charset=utf-8` 'Content-Type': `${this.contentType}; charset=utf-8`
}); });
} }
@@ -437,7 +408,7 @@
url: this.url + this.path + this.queryString, url: this.url + this.path + this.queryString,
auth, auth,
headers, headers,
data: requestBody.toString() data: requestBody
}); });
(() => { (() => {
@@ -561,23 +532,24 @@
} }
} }
}, },
setRouteQueryState() { setRouteQueryState () {
const flat = key => this[key] !== '' ? `${key}=${this[key]}&` : '' const flat = key => this[key] !== '' ? `${key}=${this[key]}&` : ''
const deep = key => { const deep = key => {
const haveItems = [...this[key]].length const haveItems = [...this[key]].length
if (haveItems && this[key]['value'] !== '') { if(haveItems && this[key]['value'] !== '') {
return `${key}=${JSON.stringify(this[key])}&` return `${key}=${JSON.stringify(this[key])}&`
} else return ''
} }
let flats = ['method', 'url', 'path', 'auth', 'httpUser', 'httpPassword', 'bearerToken', 'contentType'].map(item => flat(item)) else return ''
}
let flats = [ 'method', 'url', 'path', 'auth', 'httpUser', 'httpPassword', 'bearerToken','contentType'].map(item => flat(item))
let deeps = ['headers', 'params', 'bodyParams'].map(item => deep(item)) let deeps = ['headers', 'params', 'bodyParams'].map(item => deep(item))
this.$router.replace('/?' + flats.concat(deeps).join('').slice(0, -1)) this.$router.replace('/?'+ flats.concat(deeps).join('').slice(0,-1))
}, },
setRouteQueries(queries) { setRouteQueries(queries) {
if (typeof (queries) !== 'object') throw new Error('Route query parameters must be a Object') if(typeof(queries) !== 'object') throw new Error('Route query parameters must be a Object')
for (const key in queries) { for (const key in queries) {
if (key === 'headers' || key === 'params' || key === 'bodyParams') this[key] = JSON.parse(queries[key]) if(key === 'headers' || key === 'params' || key ==='bodyParams') this[key] = JSON.parse(queries[key])
else if (typeof (this[key]) === 'string') this[key] = queries[key]; else if (typeof(this[key]) === 'string') this[key] = queries[key];
} }
}, },
observeRequestButton() { observeRequestButton() {
@@ -587,7 +559,7 @@
entries.forEach(entry => { entries.forEach(entry => {
sendButtonElement.classList.toggle('show'); sendButtonElement.classList.toggle('show');
}); });
}, {threshold: 1}); }, { threshold: 1 });
observer.observe(requestElement); observer.observe(requestElement);
} }
@@ -612,7 +584,6 @@
], val => { ], val => {
this.setRouteQueryState() this.setRouteQueryState()
}) })
} }
} }