# HG changeset patch # User Oscar Cortez # Date 1609272846 21600 # Tue Dec 29 14:14:06 2020 -0600 # Node ID 6076c8721b2cfda383121d0414c0f97c2c878b8d # Parent 0000000000000000000000000000000000000000 Initial work on simple ComparisonTable lib diff --git a/.hgignore b/.hgignore new file mode 100644 --- /dev/null +++ b/.hgignore @@ -0,0 +1,4 @@ +node_modules +build +.DS_Store +.vscode \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 diff --git a/babelrc.config.js b/babelrc.config.js new file mode 100644 --- /dev/null +++ b/babelrc.config.js @@ -0,0 +1,14 @@ +module.exports = { + presets: [ + "@babel/preset-env", + ], + plugins: [ + '@babel/plugin-transform-runtime', + ['@babel/plugin-proposal-class-properties', { 'loose': true }], + ['@babel/plugin-proposal-decorators', { 'legacy': true }], + '@babel/plugin-transform-async-to-generator', + '@babel/plugin-transform-arrow-functions', + '@babel/plugin-proposal-object-rest-spread', + '@babel/plugin-proposal-export-default-from' + ] +} \ No newline at end of file diff --git a/config/default.json b/config/default.json new file mode 100644 --- /dev/null +++ b/config/default.json @@ -0,0 +1,6 @@ +{ + "uglify": false, + "sourcemap": false, + "open": true, + "publicPath": "/" +} \ No newline at end of file diff --git a/config/production.json b/config/production.json new file mode 100644 --- /dev/null +++ b/config/production.json @@ -0,0 +1,6 @@ +{ + "uglify": true, + "sourcemap": true, + "open": false, + "publicPath": "./" +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 --- /dev/null +++ b/package.json @@ -0,0 +1,54 @@ +{ + "name": "jana", + "version": "1.0.0", + "description": "VanillaJS comparison table", + "main": "dist/index.js", + "scripts": { + "build": "NODE_ENV=production webpack", + "start": "webpack-dev-server" + }, + "repository": { + "type": "mercurial", + "url": "" + }, + "engines": { + "node": ">=14.15.3" + }, + "keywords": [ + "table", + "compare", + "vanilla" + ], + "author": "Oscar Cortez ", + "license": "MIT", + "bugs": { + "url": "--" + }, + "homepage": "", + "devDependencies": { + "@babel/core": "^7.12.10", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-decorators": "^7.12.12", + "@babel/plugin-proposal-export-default-from": "^7.12.1", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-transform-arrow-functions": "^7.12.1", + "@babel/plugin-transform-async-to-generator": "^7.12.1", + "@babel/plugin-transform-runtime": "^7.12.10", + "@babel/preset-env": "^7.12.11", + "@babel/preset-react": "^7.12.10", + "autoprefixer": "^9.8.6", + "babel-loader": "^8.2.2", + "config": "^3.3.3", + "css-loader": "^3.6.0", + "cssnano": "^4.1.10", + "html-webpack-plugin": "^3.2.0", + "node-sass": "^4.14.1", + "postcss-loader": "^3.0.0", + "sass-loader": "^8.0.2", + "style-loader": "^1.3.0", + "webpack": "^4.44.2", + "webpack-cli": "^3.3.12", + "webpack-dev-server": "^3.11.0" + }, + "dependencies": {} +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: [ + require('autoprefixer'), + require('cssnano'), + ] +}; \ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 --- /dev/null +++ b/src/index.js @@ -0,0 +1,122 @@ +import './styles.scss'; +import defaultConfig from './lib/config'; +import ValidationError from './lib/error'; +import { QuestionTemplate, AnswerTemplate } from './lib/dom'; + + +class ComparisonTable { + constructor(container, questions, answers, config) { + this.container = container; + this.questions = questions; + this.answers = answers; + this.config = Object.assign(defaultConfig, config); + + this.mode = 'a'; // q -> Question | a -> Answer + this.questionIndex = 0; + this.answerIndex = 0; + this.selected = []; + this.initialized = false; + }; + + addStep(question, answer, index) { + console.log(question, answer) + }; + + addEvents() { + const _this = this; + document.addEventListener('keydown', (event) => { + if (event.key === 'n') { + if (_this.mode === 'q') { + _this.selected.push(false); + }; + _this.render(); + }; + if (event.key === 'y') { + _this.selected.push(true); + _this.render(); + }; + }); + document.addEventListener('questionNext', (event) => { + _this.mode = 'q'; + _this.render(); + }); + document.addEventListener('questionYes', (event) => { + _this.selected.push(false); + _this.render(); + }); + document.addEventListener('questionNo', (event) => { + _this.selected.push(false); + _this.render(); + }); + }; + + renderQuestion(question) { + const rendered_question = QuestionTemplate(this.questionIndex, this.questions.length, question['title']); + this.container.innerHTML = rendered_question; + }; + + renderAnswer(answer) { + if (this.config['options'].length !== Object.keys(answer).length) { + throw new ValidationError('Not enought options for the answer'); + }; + const question_title = this.questions[this.questionIndex - 1]['title']; + const formatted_answer = this.config['options'].map(item => { + let formatted = {}; + Object.assign(formatted, item); + formatted['title'] = answer[item['key']]; + return formatted; + }); + const rendered_answer = AnswerTemplate(this.questionIndex - 1, this.questions.length, question_title, formatted_answer); + this.container.innerHTML = rendered_answer; + }; + + renderFinal() { + this.container.innerHTML = '

The Final

'; + }; + + nextQuestion() { + const question = this.questions[this.questionIndex] + this.renderQuestion(question); + if (this.config.onNext && (typeof this.config.onNext == 'function')) { + this.config.onNext(); + }; + this.questionIndex += 1; + }; + + nextAnswer() { + const answer = this.answers[this.answerIndex]; + this.renderAnswer(answer); + if (this.config.onNext && (typeof this.config.onNext == 'function')) { + this.config.onNext(); + }; + this.answerIndex += 1; + }; + + render() { + if (this.questionIndex === this.questions.length && this.answerIndex === this.answers.length) { + this.renderFinal(); + } else if (this.mode === 'q') { + this.nextQuestion(); + this.mode = 'a'; + } else if (this.mode === 'a') { + this.nextAnswer(); + this.mode = 'q'; + }; + }; + + init() { + if (this.questions.length !== this.answers.length) { + throw new ValidationError('Questions and Answer should be equals in size'); + }; + this.addEvents(); + this.renderQuestion(this.questions[this.questionIndex]); + this.questionIndex = 1; + if (this.config.onInit && (typeof this.config.onInit == 'function')) { + this.config.onInit(); + }; + this.initialized = true; + }; + +}; + +export default ComparisonTable; diff --git a/src/lib/config.js b/src/lib/config.js new file mode 100644 --- /dev/null +++ b/src/lib/config.js @@ -0,0 +1,35 @@ +export default { + konami: { + enabled: true, + yes: 89, + no: 78, + next: 78, + }, + yes: { + text: 'Yes', + key: 'y', + color: '', + }, + no: { + text: 'No', + key: 'n', + color: '', + }, + next: { + text: 'Next', + key: 'n', + color: '', + }, + onInit: function () { + console.log('') + }, + onSelected: function () { + console.log('') + }, + onNext: function () { + console.log('') + }, + onFinish: function () { + console.log('Finish') + }, +} \ No newline at end of file diff --git a/src/lib/dom.js b/src/lib/dom.js new file mode 100644 --- /dev/null +++ b/src/lib/dom.js @@ -0,0 +1,36 @@ +export const QuestionTemplate = (index, total, question) => ` +
+ + ${index}/${total} + + + +

${question}

+
+ + +
+ ${index === 0 ? (` +
+

or type y or n

+
`) : ''} +
+`; + +export const AnswerTemplate = (index, total, question, answers) => ` +
+ + ${index}/${total} + + + +

${question}

+
+ ${answers.map(answer => `
+ +

${answer['title']}

+
`)} +
+ +
+`; diff --git a/src/lib/error.js b/src/lib/error.js new file mode 100644 --- /dev/null +++ b/src/lib/error.js @@ -0,0 +1,8 @@ +class ValidationError extends Error { + constructor(message) { + super(message); + this.name = 'ValidationError'; + } +}; + +export default ValidationError; diff --git a/src/styles.scss b/src/styles.scss new file mode 100644 --- /dev/null +++ b/src/styles.scss @@ -0,0 +1,98 @@ +.question, .answer { + min-height: 100%; + padding: 1rem; + display: flex; + position: relative; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.question__title, +.answer__title { + margin-top: 1rem; + margin-bottom: 0; + text-align: center; + min-height: calc(75px + 1rem); + max-width: 90%; + display: flex; + align-items: center; +} + +.question__action, +.answer__action { + margin-top: 2rem; + border: none; + height: 50px; + border-radius: 25px; + font-size: 20px; + padding: 0 2rem; + color: #fff; + background-color: #3c689b; + border: 1px solid #3c689b; + cursor: pointer; +} +.question__action:first-child { + margin-right: 0.5rem; +} +.question__action:last-child { + margin-left: 0.5rem; +} + +.answer__options { + display: flex; + flex-direction: row; + justify-content: space-between; +} +.answer__option { + padding: 2rem 0.5rem 0; + text-align: center; + flex: 0 1 calc(50% - 2rem); + border-radius: 5px; +} +.answer__option__logo { + width: 100%; +} +.answer__option__content { + line-height: 180%; + font-size: 16px; + font-smooth: grayscale; +} + +.progress-circle { + position: absolute; + top: 1.5rem; + right: 4rem; + display: inline-block; + height: 40px; +} +.progress-circle__text { + fill: inherit; + font-size: 12px; +} +.progress-circle__background { + fill: none; + stroke: #eef9ff; + transition: stroke 250ms ease-in-out; + stroke-width: 3.8; +} +.progress-circle__completed { + stroke: #3c689b; + fill: none; + stroke-width: 3.8; + stroke-linecap: round; + stroke-dasharray: 0,100; + transition: stroke-dasharray .25s ease-in-out; +} + +.keyboard { + margin-top: 1rem; +} +.keyboard__key { + display: inline-block; + border: 1px solid #ccdce6; + border-radius: 3px; + padding: 0.3rem 0.5rem; + box-shadow: 1px 1px 1px 1px rgba(113, 113, 113, 0.12); + background-color: inherit; +} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,59 @@ +const path = require('path'); +const webpack = require('webpack'); +const HTMLWebpackPlugin = require('html-webpack-plugin'); +const config = require('config'); + +/*-------------------------------------------------*/ + +module.exports = { + // webpack optimization mode + mode: ( process.env.NODE_ENV ? process.env.NODE_ENV : 'development' ), + + // entry file(s) + entry: './src/index.js', + + // output file(s) and chunks + output: { + library: 'ComparisonTable', + libraryTarget: 'umd', + globalObject: '(typeof self !== "undefined" ? self : this)', + libraryExport: 'default', + path: path.resolve(__dirname, 'dist'), + filename: 'index.js', + publicPath: config.get('publicPath') + }, + + // module/loaders configuration + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: ['babel-loader'] + }, + { + test: /\.scss$/, + use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'] + } + ] + }, + + plugins: [ + new HTMLWebpackPlugin({ + template: path.resolve(__dirname, 'index.html') + }) + ], + + // development server configuration + devServer: { + + // must be `true` for SPAs + historyApiFallback: true, + + // open browser on server start + open: config.get('open') + }, + + // generate source map + devtool: ( 'production' === process.env.NODE_ENV ? 'source-map' : 'cheap-module-eval-source-map' ), +};