JSFiddle - React, Tailwind, and code Playground
by blineberry
HTML
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js"></script>
<script src="https://rawgithub.com/rniemeyer/knockout-sortable/master/build/knockout-sortable.js"></script>
<ul class="questionlist" data-bind="sortable: { data: questions, options: { handle: '.sort-handle', cursor: 'move', placeholder: 'sort-placeholder', axis: 'y', revert: true, start: sortStart } }">
<li class="questionlist-item">
<div class="sort questionlist-item-sort">
<button class="sort-up" data-bind="click: $parent.moveQuestionUp, visible: $index() > 0"></button>
<div class="sort-handle"></div>
<button class="sort-down" data-bind="click: $parent.moveQuestionDown, visible: $index() < $parent.questions().length - 1"></button>
</div>
<div class="question questionlist-question">
<button data-bind="click: editToggle, text: isEditing() ? 'Save' : 'Edit'"></button> <button data-bind="click: $parent.removeQuestion">Remove</button>
<div class="view" data-bind="visible: !isEditing()">
<div class="question-text" data-bind="text: text()"></div>
<ul class="question-answerlist" data-bind="foreach: choices, visible: visible() === 'multiple'">
<li>
<div class="question-answer">
<input data-bind="attr: { name: $parentContext.$index, id: $parentContext.$index() + '-' + $index(), type: $parent.type() === 'mc-multiple' ? 'checkbox' : 'radio', value: $data }, checked: $parent.value" />
<label data-bind="attr: { for: $parentContext.$index() + '-' + $index() }, text: $data"></label>
</div>
</li>
</ul>
<div class="question-answer">
<select data-bind="visible: visible() === 'select', options: choices, attr { multiple: type() === 'mc-multiple' , size: choices().length > 15 ?...
CSS
.questionlist {
margin: 0;
padding: 0;
list-style: none;
}
.questionlist-item {
margin: 1.5em 0;
padding: 1.5em;
clear: both;
border-radius: 10px;
background: rgba(255,255,255,0.9);
}
.question-text {
font-weight: bold;
}
.question-answerlist {
margin: 0;
list-style: none;
}
.question-answer select {
margin-left: 40px;
}
.questionlist-item-sort {
float: left;
}
.questionlist-question {
margin-left: 60px;
}
.sort-up, .sort-down {
border: 0 none;
background: transparent;
height: 44px;
width: 44px;
position: relative;
cursor: pointer;
}
.sort-up:before, .sort-down:before {
content: '';
position: absolute;
left: 0;
height: 0;
width: 0;
border-style: solid;
cursor: pointer;
}
.sort-up {
margin-bottom: 5px;
}
.sort-up:before {
bottom: 0;
border-color: transparent transparent #DDD;
border-width: 0 22px 22px;
}
.sort-down {
margin-top: 5px;
}
.sort-down:before {
top: 0;
border-color: #DDD transparent transparent;
border-width: 22px 22px 0;
}
.sort-handle {
width: 44px;
height: 44px;
background: #DDD;
cursor: move;
}
.sort-placeholder {
border: 4px dashed yellow;
height: 3em;
padding: 1.5em;
margin: 1.5em;
clear: both;
background: lightyellow;
line-height: 1.5;
}
.ui-sortable-helper {
background: lightyellow;
opacity: 0.9;
}
JavaScript
function QuestionListVM() {
var self = this;
self.test = "Test";
self.questions = ko.observableArray([
new Question('Which charities do you give to annually? (check all that apply)', [
'Unicef',
'United Way',
'American Cancer Society',
'Other Charity'
], 'mc-multiple'),
new Question('What\'s your favorite coffee?', [
'Espresso',
'Macchiatto',
'Cortado',
'Cappuccino',
'Latte',
'Mocha',
'Drip',
'Coffee? Blech.'
], 'mc-single'),
new Question('True or false: beer is proof that God loves us and wants us to be happy.', [
'True',
'False'], 'mc-single'),
new Question('If you were a beer, what beer would you be?', [], 'txt-short'),
new Question('In 100 words or more, please describe a solar eclipse.', [], 'txt-long')
]);
for (var i = 0; i < self.questions().length; i++) {
self.questions()[i].order(i);
}
self.sortQuestions = function() {
self.questions.sort(function(a,b) {
return a.order() - b.order();
});
};
self.addQuestion = function() {
self.questions.push(new Question(null, null, null, true, self.questions().length));
};
self.removeQuestion = function(question) {
self.questions.remove(question);
};
self.moveQuestionUp = function(question) {
curIndex = self.questions().indexOf(question);
if (curIndex > 0) {
self.questions.splice(curIndex - 1, 2, self.questions()[curIndex], self.questions()[curIndex - 1]);
}
}
self.moveQuestionDown = function(question) {
curIndex = self.questions().indexOf(question);
if (curIndex + 1 < self.questions().length) {
self.questions.splice(curIndex, 2, self.questions()[curIndex + 1],...