Skip to content

Commit

Permalink
new option eventList: onInputSubmit, called at every input submit. Can
Browse files Browse the repository at this point in the history
be used to build chat dinamically based on an API, for example.
  • Loading branch information
eduardotkoller committed Jul 12, 2018
1 parent a6b8746 commit b955606
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 50 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ Features:
* Fork conversation based on answer
* Access previous answers to use on questions
* Messages that doesn't expect answer
* Dynamically create new questions (like using an API)! [Demo](https://eduardotkoller.github.io/convForm/api_example.html) - please see the example code inside the html to understand how it works

To build the chat, just wrap the form inside an element, and call .convform() on it's jquery selector. The function requires a placeholder for the user input.
To build the chat, just wrap the form inside an element, and call .convform() on it's jquery selector.

Example:

Expand Down Expand Up @@ -120,7 +121,7 @@ You can add an options object as a parameter to the convForm function, containin
* ```typeInputUi```: 'input' or 'textarea', to choose the type of the html element to use as the user's input
* ```timeOutFirstQuestion```: time in ms as the duration for the load-up animation before the first question
* ```buttonClassStyle```: class for the user's submit answer button
* ```eventList```: an object with functions to be called at specific times, the only supported at the moment is ```onSubmitForm```, and the function is called with the convState as a parameter.
* ```eventList```: an object with functions to be called at specific times, the only supported at the moment are ```onSubmitForm``` (function is called with the convState as a parameter) and ```onInputSubmit``` (function called with the convState as the first parameter, and a ready callback function to print the next question as the second parameter)
* ```formIdName```: html id for the form
* ```inputIdName```: html id for the user's input
* ```loadSpinnerVisible```: class for the loadSpinner
Expand Down
66 changes: 66 additions & 0 deletions api_example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>convForm - example</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="dist/jquery.convform.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="demo.css">
</head>
<body>
<section id="demo">
<div class="vertical-align">
<div class="container">
<div class="row">
<div class="col-sm-6 col-sm-offset-3 col-xs-offset-0">
<div class="card no-border">
<div id="chat">
<form action="" method="GET" class="hidden">
<select data-conv-question="Hello! This is an example use of the plugin to dynamically generate questions (like using an API). This is the only question that was written on the initial HTML. To end the loop, select END." name="first-question">
<option value="understood">Understood</option>
<option value="okay">Okay, captain!</option>
</select>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<script type="text/javascript" src="jquery-1.12.3.min.js"></script>
<script type="text/javascript" src="dist/autosize.min.js"></script>
<script type="text/javascript" src="dist/jquery.convform.js"></script>

<script>
jQuery(function($){
var count = 0;
var convForm = $('#chat').convform({eventList:{onInputSubmit: function(convState, ready) {
//here you send the response to your API, get the results and build the next question
//when ready, call 'ready' callback (passed as the second parameter)
if(convState.current.answer.value==='end') {
convState.current.next = false;
//emulating random response time (100-600ms)
setTimeout(ready, Math.random()*500+100);
} else {
convState.current.next = convState.newState({
type: 'select',
name: 'dynamic-question-'+count,
questions: ['This question state was built on your previous answer (you answered: '+convState.current.answer.text+')'],
answers: [
{text: 'Answer 1', value: '1'},
{text: 'Answer 2', value: '2'},
{text: 'END', value: 'end'}
]
});
//emulating random response time (100-600ms)
setTimeout(ready, Math.random()*500+100);
}
count++;
}}});
});
</script>
</body>
</html>
42 changes: 21 additions & 21 deletions dist/jquery.convform.css
Original file line number Diff line number Diff line change
Expand Up @@ -119,26 +119,26 @@ form.convFormDynamic input.userInputDynamic {
margin-right: 2.5%;
}

div#chat.conv-form-wrapper div#messages {
div.conv-form-wrapper div#messages {
max-height: 71vh;
height: auto !important;
overflow-y: scroll;
}
div#chat.conv-form-wrapper div#messages:after {
div.conv-form-wrapper div#messages:after {
content: '';
display: table;
clear: both;
}
div#chat.conv-form-wrapper {
div.conv-form-wrapper {
position: relative;
}
div#chat.conv-form-wrapper div.wrapper-messages {
div.conv-form-wrapper div.wrapper-messages {
position: relative;
height: 577px;
max-height: 71vh;
overflow-y: scroll;
}
div#chat:before {
div.conv-form-wrapper:before {
content: '';
position: absolute;
width: 100%;
Expand All @@ -150,12 +150,12 @@ div#chat:before {
background: linear-gradient(#fff, transparent);
}
@media (max-width: 767px) {
div#chat.conv-form-wrapper div.wrapper-messages, div#chat.conv-form-wrapper div#messages {
div.conv-form-wrapper div.wrapper-messages, div.conv-form-wrapper div#messages {
max-height: 71vh;
}
}

div#chat div.wrapper-messages::-webkit-scrollbar, div#feed ul::-webkit-scrollbar, div#chat div.options::-webkit-scrollbar {
div.conv-form-wrapper div.wrapper-messages::-webkit-scrollbar, div#feed ul::-webkit-scrollbar, div.conv-form-wrapper div.options::-webkit-scrollbar {
width: 0px;
height: 0px;
/* remove scrollbar space */
Expand All @@ -179,7 +179,7 @@ textarea.userInputDynamic {
margin: 7px 10px;
}

div#chat.conv-form-wrapper div#messages {
div.conv-form-wrapper div#messages {
transition: bottom 0.15s, padding-bottom 0.15s;
position: absolute;
bottom: 0;
Expand All @@ -188,7 +188,7 @@ div#chat.conv-form-wrapper div#messages {
padding-bottom: 20px;
/*max-height: 71vh;*/
}
div#chat div.options {
div.conv-form-wrapper div.options {
word-wrap: normal;
white-space: nowrap;
overflow-x: scroll;
Expand All @@ -197,12 +197,12 @@ div#chat div.options {
width: 100%;
transform: translateY(-5px);
}
div#chat div.options:after {
div.conv-form-wrapper div.options:after {
content: '';
display: table;
clear: both;
}
div#chat div.options div.option {
div.conv-form-wrapper div.options div.option {
padding: 7px 12px;
border: 1px solid rgba(6,153,184,0.3);
display: inline-block;
Expand All @@ -213,19 +213,19 @@ div#chat div.options div.option {
border-radius: 20px;
font-size: 0.9rem;
}
div#chat.conv-form-wrapper div.message {
div.conv-form-wrapper div.message {
animation: slideTop 0.15s ease;
}
div#chat.conv-form-wrapper div.message:after {
div.conv-form-wrapper div.message:after {
content: '';
display: table;
clear: both;
}
div#chat.conv-form-wrapper div.message.ready {
div.conv-form-wrapper div.message.ready {
animation: bounceIn 0.2s ease;
transform-origin: 0 0 0;
}
div#chat.conv-form-wrapper div#messages div.message {
div.conv-form-wrapper div#messages div.message {
border-radius: 20px;
padding: 12px 22px;
font-size: 0.905rem;
Expand All @@ -239,13 +239,13 @@ div#chat.conv-form-wrapper div#messages div.message {
max-width: 65%;
word-wrap: break-word;
}
div#chat.conv-form-wrapper div#messages div.message.to {
div.conv-form-wrapper div#messages div.message.to {
background: #efefef;
color: #6f6f6f;
float: left;
border-top-left-radius: 0;
}
div#chat.conv-form-wrapper div#messages div.message.from {
div.conv-form-wrapper div#messages div.message.from {
background: #06b79a;
color: #fff;
border-top-right-radius: 0;
Expand All @@ -270,14 +270,14 @@ div#chat.conv-form-wrapper div#messages div.message.from {
transform: scale(1.0, 1.0);
}
}
div#chat div.options div.option:hover {
div.conv-form-wrapper div.options div.option:hover {
background: #eeeeee;
}
div#chat div.options div.option.selected {
div.conv-form-wrapper div.options div.option.selected {
background: #06b79a;
color: #fff;
}
div#chat div.options div.option.selected:hover {
div.conv-form-wrapper div.options div.option.selected:hover {
background: #069c7f;
}

Expand Down Expand Up @@ -312,7 +312,7 @@ button.submit.glow {
.dragscroll {
cursor: grab;
}
div#chat div#messages::-webkit-scrollbar, div#feed ul::-webkit-scrollbar {
div.conv-form-wrapper div#messages::-webkit-scrollbar, div#feed ul::-webkit-scrollbar {
width: 0px;
/* remove scrollbar space */
background: transparent;
Expand Down
71 changes: 47 additions & 24 deletions dist/jquery.convform.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
function SingleConvState(input){
this.input = input;
this.answer = '';
this.next = false;
return this;
}
};
SingleConvState.prototype.hasNext = function(){
return this.next;
};
Expand All @@ -15,7 +16,21 @@ function ConvState(wrapper, SingleConvState, form, params) {
this.scrollDown = function() {
$(this.wrapper).find('#messages').stop().animate({scrollTop: $(this.wrapper).find('#messages')[0].scrollHeight}, 600);
}.bind(this);
}
};
ConvState.prototype.newState = function(options) {
var input = $.extend(true, {}, {
name: '',
noAnswer: false,
required: true,
questions: ['You forgot the question!'],
type: 'text',
multiple: false,
selected: "",
answers: []
}, options);
input.element = $('<input type="text" name="'+input.name+'"/>');
return new SingleConvState(input);
};
ConvState.prototype.next = function(){
if(this.current.input.hasOwnProperty('callback')) {
window[this.current.input.callback](this);
Expand Down Expand Up @@ -60,21 +75,20 @@ ConvState.prototype.printQuestion = function(){
}
}
}
var messageObj = $('<div class="message to typing"><div class="typing_loader"></div></div>');
setTimeout(function(){
$(this.wrapper).find('#messages').append(messageObj);
this.scrollDown();
}.bind(this), 100);
var messageObj = $(this.wrapper).find('.message.typing');
setTimeout(function(){
messageObj.html(question);
messageObj.removeClass('typing').addClass('ready');
if(this.current.input.type=="select"){
this.printAnswers(this.current.input.answers, this.current.input.multiple);
}
this.scrollDown();
if(this.current.input.hasOwnProperty('noAnswer')) {
if(this.current.input.hasOwnProperty('noAnswer') && this.current.input.noAnswer===true) {
if(this.next()){
setTimeout(function(){
var messageObj = $('<div class="message to typing"><div class="typing_loader"></div></div>');
$(this.wrapper).find('#messages').append(messageObj);
this.scrollDown();
this.printQuestion();
}.bind(this),200);
} else {
Expand Down Expand Up @@ -141,9 +155,11 @@ ConvState.prototype.answerWith = function(answerText, answerObject) {
if(this.current.input.type == 'tel')
answerObject = answerObject.replace(/\s|\(|\)|-/g, "");
this.answers[this.current.input.name] = {text: answerText, value: answerObject};
this.current.answer = {text: answerText, value: answerObject};
//console.log('previous answer: ', answerObject);
} else {
this.answers[this.current.input.name] = answerObject;
this.current.answer = answerObject;
}
if(this.current.input.type == 'select' && !this.current.input.multiple) {
$(this.current.input.element).val(answerObject.value).change();
Expand All @@ -169,22 +185,31 @@ ConvState.prototype.answerWith = function(answerText, answerObject) {
setTimeout(function(){
$(this.wrapper).find("#messages").append(message);
this.scrollDown();
}.bind(this), 300);
}.bind(this), 100);

$(this.form).append(this.current.input.element);
//goes to next state and prints question
if(this.next()){
setTimeout(function(){
this.printQuestion();
}.bind(this), 300);
} else {
this.parameters.eventList.onSubmitForm(this);
}
var messageObj = $('<div class="message to typing"><div class="typing_loader"></div></div>');
setTimeout(function(){
$(this.wrapper).find('#messages').append(messageObj);
this.scrollDown();
}.bind(this), 150);

this.parameters.eventList.onInputSubmit(this, function(){
//goes to next state and prints question
if(this.next()){
setTimeout(function(){
this.printQuestion();
}.bind(this), 300);
} else {
this.parameters.eventList.onSubmitForm(this);
}
}.bind(this));
};

(function($){
$.fn.convform = function(options){
var wrapper = this;
$(this).addClass('conv-form-wrapper');

var parameters = $.extend(true, {}, {
placeHolder : 'Type Here',
Expand All @@ -196,7 +221,8 @@ ConvState.prototype.answerWith = function(answerText, answerObject) {
console.log('completed');
convState.form.submit();
return true;
}
},
onInputSubmit : function(convState, readyCallback) {readyCallback()}
},
formIdName : 'convForm',
inputIdName : 'userInput',
Expand Down Expand Up @@ -288,6 +314,9 @@ ConvState.prototype.answerWith = function(answerText, answerObject) {
//prints first question
setTimeout(function() {
$.when($('div.spinLoader').addClass('hidden')).done(function() {
var messageObj = $('<div class="message to typing"><div class="typing_loader"></div></div>');
$(state.wrapper).find('#messages').append(messageObj);
state.scrollDown();
state.printQuestion();
});
}, parameters.timeOutFirstQuestion);
Expand Down Expand Up @@ -443,9 +472,3 @@ ConvState.prototype.answerWith = function(answerText, answerObject) {
}
}
})( jQuery );

$(function(){
//instantiate conversation form on .conv-form-wrapper (default class for plugin);
var convForm = $('.conv-form-wrapper').convform();
console.log(convForm);
});
Loading

0 comments on commit b955606

Please sign in to comment.