├── .browserslistrc ├── .editorconfig ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── babel.config.js ├── build └── rollup.config.js ├── examples ├── questionnaire │ ├── Example.vue │ ├── assets │ │ └── images │ │ │ ├── facebook.png │ │ │ ├── instagram.png │ │ │ ├── tiktok.png │ │ │ └── twitter.png │ └── main.js ├── quiz │ ├── Example.vue │ └── main.js └── support-page │ ├── Example.vue │ └── main.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── index.html └── logo.png ├── src ├── assets │ └── css │ │ ├── common.css │ │ └── themes │ │ ├── theme-green.css │ │ ├── theme-minimal.css │ │ └── theme-purple.css ├── components │ ├── FlowForm.vue │ ├── FlowFormQuestion.vue │ ├── Question.vue │ └── QuestionTypes │ │ ├── BaseType.vue │ │ ├── DateType.vue │ │ ├── DropdownType.vue │ │ ├── EmailType.vue │ │ ├── FileType.vue │ │ ├── IconRateType.vue │ │ ├── LongTextType.vue │ │ ├── MatrixType.vue │ │ ├── MultipleChoiceType.vue │ │ ├── MultiplePictureChoiceType.vue │ │ ├── NumberType.vue │ │ ├── OpinionScaleType.vue │ │ ├── PasswordType.vue │ │ ├── PhoneType.vue │ │ ├── SectionBreakType.vue │ │ ├── TextType.vue │ │ └── UrlType.vue ├── main.js ├── mixins │ ├── ComponentInstance.js │ └── IsMobile.js └── models │ ├── LanguageModel.js │ └── QuestionModel.js └── vue.config.js /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_size = 2 3 | indent_style = space -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: 'Bug:' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Screenshots** 20 | If applicable, add screenshots to help explain your problem. 21 | 22 | **Desktop (please complete the following information):** 23 | - Device: [e.g. iPhone6] 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Additional context** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: 'Feature request:' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /bin 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | 24 | # Misc files 25 | dev.cmd 26 | dist.cmd -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | ## Before creating an issue 4 | 5 | * Check the [GUIDE](https://www.ditdot.hr/en/docs/vue-flow-form-guide) to see if you can find the solution to your problem by yourself. 6 | * Check the version of Vue Flow Form you are using - use the last version according to [CHANGELOG](https://github.com/ditdot-dev/vue-flow-form/releases). 7 | * Perform the [Issues](https://github.com/ditdot-dev/vue-flow-form/issues) search to see if the problem has already been reported. 8 | * If the issue has been reported and is still open, add a comment to it instead of opening a new issue. 9 | * If the issue has been reported and is closed, open a new issue with a link to the closed issue. 10 | * If the issue hasn't been reported, open a new issue. 11 | 12 | ## Creating a bug report 13 | 14 | * Use a descriptive title and describe the exact steps to reproduce the problem. 15 | * Explain what kind of behavior you expected and why. 16 | * Share as much information about the problem as you can - include screenshots, GIFs, reports, etc. 17 | 18 | ## Suggesting an enhancement 19 | 20 | * Use a descriptive title and describe the feature request/change in details. 21 | * Explain why this enhancement would be useful. 22 | 23 | ## Creating a pull request 24 | 25 | * Make your changes on a feature branch. Follow the code style of the project, including indentation. 26 | * Make sure your commit messages are short, descriptive, and written in English. 27 | * Create a new PR with the code changes. 28 | * The PR description should clearly describe the problem and solution, with a reference to the appropriate issue if applicable. 29 | 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 - present, DITDOT Ltd. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue Flow Form 2 | 3 | Create conversational conditional-logic forms with Vue.js. 4 | 5 |

6 | License 7 | Version 8 | cdnjs 9 |

10 | 11 |

12 | v-form screenshots 13 |

14 | 15 | Starting with v2.0.0, Vue Flow Form has migrated from Vue 2 to **Vue 3**. If you're looking for README for Vue Flow Form v1.1.7, which works with Vue 2, check it out here. 16 | 17 | ## Live Demos 18 | 19 | * [Questionnaire example](https://www.ditdot.hr/demo/vff/questionnaire/) 20 | * [Support page example](https://www.ditdot.hr/demo/vff/support-page/) 21 | * [Quiz example](https://www.ditdot.hr/demo/vff/quiz/) 22 | 23 | ## Project Documentation 24 | 25 | * [Guide](https://www.ditdot.hr/en/docs/vue-flow-form-guide) 26 | 27 | ## Example Project 28 | 29 | Requirements: 30 | 31 | * [Node.js](https://nodejs.org/en/) version 10.0.0 or above (12.0.0+ recommended) 32 | * [npm](https://www.npmjs.com/get-npm) version 5+ (or [yarn](https://yarnpkg.com/lang/en/docs/install/) version 1.16+) 33 | * [Git](https://git-scm.com/) 34 | 35 | After checking the prerequisites, follow these simple steps to install and use Vue Form: 36 | 37 | ```shell 38 | # clone the repo 39 | $ git clone https://github.com/ditdot-dev/vue-flow-form.git myproject 40 | 41 | # go into app's directory and install dependencies: 42 | $ cd myproject 43 | ``` 44 | 45 | If you use npm: 46 | 47 | ```shell 48 | $ npm install 49 | 50 | # serve with hot reload at localhost:8080 by default. 51 | $ npm run serve 52 | 53 | # build for production 54 | $ npm run build 55 | ``` 56 | 57 | If you use yarn: 58 | 59 | ```shell 60 | $ yarn install 61 | 62 | # serve with hot reload at localhost:8080 by default. 63 | $ yarn serve 64 | 65 | # build for production 66 | $ yarn build 67 | ``` 68 | 69 | Made with [Vue.js](https://vuejs.org/) 70 | 71 | ## Usage as npm Package 72 | 73 | If you don't already have an existing Vue project, the easiest way to create one is to use [Vue CLI](https://cli.vuejs.org/): 74 | 75 | (For issues with Vue 3.13 and CLI 4 check [here](https://github.com/vuejs/vue-next/issues/4052)) 76 | 77 | ```shell 78 | $ npm install -g @vue/cli 79 | # OR 80 | $ yarn global add @vue/cli 81 | ``` 82 | 83 | And then create a project (refer to [Vue CLI documentation](https://cli.vuejs.org/guide/) and [issue tracker](https://github.com/vuejs/vue-cli/issues) for potential problems on Windows): 84 | 85 | ```shell 86 | $ vue create my-project 87 | $ cd my-project 88 | ``` 89 | 90 | To add Vue Flow Form as a dependency to your Vue project, use the following: 91 | 92 | ```shell 93 | $ npm install @ditdot-dev/vue-flow-form --save 94 | ``` 95 | 96 | And then in your App.vue file: 97 | 98 | ```html 99 | 102 | 103 | 134 | 135 | 143 | ``` 144 | 145 | ## Usage with Plain JavaScript via CDN 146 | 147 | HTML: 148 | 149 | ```html 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 |
167 | 168 | 169 | 170 | ``` 171 | 172 | JavaScript (content of app.js): 173 | 174 | ```js 175 | var app = Vue.createApp({ 176 | el: '#app', 177 | template: '', 178 | data: function() { 179 | return { 180 | language: new VueFlowForm.LanguageModel({ 181 | // Your language definitions here (optional). 182 | // You can leave out this prop if you want to use the default definitions. 183 | }), 184 | questions: [ 185 | new VueFlowForm.QuestionModel({ 186 | title: 'Question', 187 | type: VueFlowForm.QuestionType.MultipleChoice, 188 | options: [ 189 | new VueFlowForm.ChoiceOption({ 190 | label: 'Answer' 191 | }) 192 | ] 193 | }) 194 | ] 195 | } 196 | } 197 | }).component('FlowForm', VueFlowForm.FlowForm); 198 | 199 | const vm = app.mount('#app'); 200 | ``` 201 | 202 | ## Changelog 203 | 204 | Changes for each release are documented in the [release notes](https://github.com/ditdot-dev/vue-flow-form/releases). 205 | 206 | ## Stay in Touch 207 | 208 | * [Twitter](https://twitter.com/ditdot_info) 209 | * [DITDOT](https://www.ditdot.hr/en) 210 | 211 | ## License 212 | 213 | [MIT](https://github.com/ditdot-dev/vue-flow-form/blob/master/LICENSE) license. 214 | 215 | Copyright (c) 2020 - present, DITDOT Ltd. 216 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ["@vue/cli-plugin-babel/preset", { 4 | useBuiltIns: "entry" 5 | }] 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /build/rollup.config.js: -------------------------------------------------------------------------------- 1 | import commonjs from '@rollup/plugin-commonjs' 2 | import vue from 'rollup-plugin-vue' 3 | import buble from '@rollup/plugin-buble' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import cleanup from 'rollup-plugin-cleanup' 6 | import css from 'rollup-plugin-css-only' 7 | import postcss from 'rollup-plugin-postcss' 8 | import postcssImport from 'postcss-import' 9 | import { terser } from 'rollup-plugin-terser' 10 | import del from 'rollup-plugin-delete' 11 | import copy from 'rollup-plugin-copy' 12 | import replace from '@rollup/plugin-replace' 13 | 14 | const globals = { 15 | vue: 'Vue' 16 | } 17 | 18 | const external = ['vue'] 19 | 20 | const componentName = 'VueFlowForm' 21 | 22 | export default [ 23 | { 24 | input: 'src/main.js', 25 | external, 26 | output: { 27 | name: componentName, 28 | exports: 'named', 29 | globals, 30 | format: 'umd', 31 | file: 'dist/vue-flow-form.umd.js' 32 | }, 33 | plugins: [ 34 | del({ 35 | targets: 'dist/*' 36 | }), 37 | vue({ 38 | css: false, 39 | compileTemplate: true 40 | }), 41 | replace({ 42 | preventAssignment: true, 43 | 'process.env.NODE_ENV': JSON.stringify('production') 44 | }), 45 | postcss({ 46 | output: 'css', 47 | extract: 'vue-flow-form.css', 48 | plugins: [ 49 | postcssImport() 50 | ] 51 | }), 52 | css(), 53 | buble({ 54 | objectAssign: 'Object.assign', 55 | jsx: 'h', 56 | transforms: { 57 | dangerousForOf: true 58 | } 59 | }), 60 | commonjs(), 61 | resolve({ 62 | extensions: ['.mjs', '.js', '.json', '.node', '.vue'] 63 | }), 64 | del({ 65 | targets: 'dist/dist', 66 | hook: 'writeBundle' 67 | }) 68 | ] 69 | }, 70 | { 71 | input: 'src/main.js', 72 | external, 73 | output: { 74 | name: componentName, 75 | exports: 'named', 76 | globals, 77 | format: 'es', 78 | file: 'dist/vue-flow-form.esm.js' 79 | }, 80 | plugins: [ 81 | vue({ 82 | css: false, 83 | compileTemplate: true 84 | }), 85 | replace({ 86 | preventAssignment: true, 87 | 'process.env.NODE_ENV': JSON.stringify('production') 88 | }), 89 | postcss({ 90 | output: 'css', 91 | extract: 'vue-flow-form.css', 92 | plugins: [ 93 | postcssImport() 94 | ] 95 | }), 96 | css(), 97 | buble({ 98 | objectAssign: 'Object.assign', 99 | jsx: 'h', 100 | transforms: { 101 | dangerousForOf: true 102 | } 103 | }), 104 | commonjs(), 105 | resolve({ 106 | extensions: ['.mjs', '.js', '.json', '.node', '.vue'] 107 | }), 108 | del({ 109 | targets: 'dist/dist', 110 | hook: 'writeBundle' 111 | }) 112 | ] 113 | }, 114 | { 115 | input: 'src/main.js', 116 | external, 117 | output: { 118 | name: componentName, 119 | exports: 'named', 120 | globals, 121 | format: 'cjs', 122 | file: 'dist/vue-flow-form.common.js' 123 | }, 124 | plugins: [ 125 | vue({ 126 | css: false, 127 | compileTemplate: true 128 | }), 129 | replace({ 130 | preventAssignment: true, 131 | 'process.env.NODE_ENV': JSON.stringify('production') 132 | }), 133 | postcss({ 134 | output: 'css', 135 | extract: 'vue-flow-form.css', 136 | plugins: [ 137 | postcssImport() 138 | ] 139 | }), 140 | css(), 141 | buble({ 142 | objectAssign: 'Object.assign', 143 | jsx: 'h', 144 | transforms: { 145 | dangerousForOf: true 146 | } 147 | }), 148 | commonjs(), 149 | resolve({ 150 | extensions: ['.mjs', '.js', '.json', '.node', '.vue'] 151 | }), 152 | del({ 153 | targets: 'dist/dist', 154 | hook: 'writeBundle' 155 | }) 156 | ] 157 | }, 158 | { 159 | input: 'src/main.js', 160 | external, 161 | output: { 162 | name: componentName, 163 | exports: 'named', 164 | globals, 165 | format: 'umd', 166 | file: 'dist/vue-flow-form.umd.min.js' 167 | }, 168 | plugins: [ 169 | vue({ 170 | css: false, 171 | compileTemplate: true 172 | }), 173 | replace({ 174 | preventAssignment: true, 175 | 'process.env.NODE_ENV': JSON.stringify('production') 176 | }), 177 | postcss({ 178 | output: 'css', 179 | extract: 'vue-flow-form.min.css', 180 | plugins: [ 181 | postcssImport() 182 | ], 183 | sourceMap: true, 184 | minimize: true 185 | }), 186 | css(), 187 | buble({ 188 | objectAssign: 'Object.assign', 189 | jsx: 'h', 190 | transforms: { 191 | dangerousForOf: true 192 | } 193 | }), 194 | commonjs(), 195 | resolve({ 196 | extensions: ['.mjs', '.js', '.json', '.node', '.vue'] 197 | }), 198 | cleanup(), 199 | terser(), 200 | del({ 201 | targets: 'dist/dist', 202 | hook: 'writeBundle' 203 | }), 204 | copy({ 205 | targets: [ 206 | { 207 | src: 'src/assets/css/themes/theme-minimal.css', 208 | dest: 'dist', 209 | rename: 'vue-flow-form.theme-minimal.css' 210 | }, 211 | { 212 | src: 'src/assets/css/themes/theme-green.css', 213 | dest: 'dist', 214 | rename: 'vue-flow-form.theme-green.css' 215 | }, 216 | { 217 | src: 'src/assets/css/themes/theme-purple.css', 218 | dest: 'dist', 219 | rename: 'vue-flow-form.theme-purple.css' 220 | } 221 | ] 222 | }) 223 | ] 224 | } 225 | ] -------------------------------------------------------------------------------- /examples/questionnaire/Example.vue: -------------------------------------------------------------------------------- 1 | // Create and setup your form here 2 | 3 | 64 | 65 | 354 | 355 | -------------------------------------------------------------------------------- /examples/questionnaire/assets/images/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ditdot-dev/vue-flow-form/a8f30b81543c2070589db49d9f326d2ec3287587/examples/questionnaire/assets/images/facebook.png -------------------------------------------------------------------------------- /examples/questionnaire/assets/images/instagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ditdot-dev/vue-flow-form/a8f30b81543c2070589db49d9f326d2ec3287587/examples/questionnaire/assets/images/instagram.png -------------------------------------------------------------------------------- /examples/questionnaire/assets/images/tiktok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ditdot-dev/vue-flow-form/a8f30b81543c2070589db49d9f326d2ec3287587/examples/questionnaire/assets/images/tiktok.png -------------------------------------------------------------------------------- /examples/questionnaire/assets/images/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ditdot-dev/vue-flow-form/a8f30b81543c2070589db49d9f326d2ec3287587/examples/questionnaire/assets/images/twitter.png -------------------------------------------------------------------------------- /examples/questionnaire/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence 3 | https://github.com/ditdot-dev/vue-flow-form 4 | https://www.ditdot.hr/en 5 | */ 6 | 7 | import { createApp } from 'vue' 8 | import Example from './Example.vue' 9 | 10 | createApp(Example).mount('#app') 11 | -------------------------------------------------------------------------------- /examples/quiz/Example.vue: -------------------------------------------------------------------------------- 1 | // Create and setup your form here 2 | 3 | 67 | 68 | 353 | 354 | -------------------------------------------------------------------------------- /examples/quiz/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence 3 | https://github.com/ditdot-dev/vue-flow-form 4 | https://www.ditdot.hr/en 5 | */ 6 | 7 | import { createApp } from 'vue' 8 | import Example from './Example.vue' 9 | 10 | createApp(Example).mount('#app') 11 | -------------------------------------------------------------------------------- /examples/support-page/Example.vue: -------------------------------------------------------------------------------- 1 | // Create and setup your form here 2 | 3 | 55 | 56 | 235 | 236 | -------------------------------------------------------------------------------- /examples/support-page/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence 3 | https://github.com/ditdot-dev/vue-flow-form 4 | https://www.ditdot.hr/en 5 | */ 6 | 7 | import { createApp } from 'vue' 8 | import Example from './Example.vue' 9 | 10 | createApp(Example).mount('#app') 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ditdot-dev/vue-flow-form", 3 | "author": "DITDOT", 4 | "description": "Create conversational conditional-logic forms with Vue.js.", 5 | "version": "2.3.2", 6 | "private": false, 7 | "scripts": { 8 | "serve": "vue-cli-service serve", 9 | "build": "vue-cli-service build", 10 | "build:npm": "rollup --config build/rollup.config.js && npm run cssmin", 11 | "cssmin": "postcss dist/vue-flow-form.theme-minimal.css -o dist/vue-flow-form.theme-minimal.min.css && postcss dist/vue-flow-form.theme-green.css -o dist/vue-flow-form.theme-green.min.css && postcss dist/vue-flow-form.theme-purple.css -o dist/vue-flow-form.theme-purple.min.css", 12 | "build:examples": "vue-cli-service build --dest dist/questionnaire examples/questionnaire/main.js && vue-cli-service build --dest dist/quiz examples/quiz/main.js && vue-cli-service build --dest dist/support-page examples/support-page/main.js" 13 | }, 14 | "main": "dist/vue-flow-form.umd.js", 15 | "module": "dist/vue-flow-form.esm.js", 16 | "browser": "dist/vue-flow-form.common.js", 17 | "unpkg": "dist/vue-flow-form.umd.min.js", 18 | "files": [ 19 | "dist" 20 | ], 21 | "eslintConfig": { 22 | "root": true, 23 | "extends": [ 24 | "plugin:vue/vue3-essential" 25 | ], 26 | "rules": { 27 | "no-unused-vars": "off" 28 | } 29 | }, 30 | "dependencies": { 31 | "vue-textarea-autosize": "^1.1.1", 32 | "vue-the-mask": "^0.11.1" 33 | }, 34 | "devDependencies": { 35 | "@rollup/plugin-alias": "^3.1.1", 36 | "@rollup/plugin-buble": "^0.21.3", 37 | "@rollup/plugin-commonjs": "^21.0.1", 38 | "@rollup/plugin-node-resolve": "^13.0.0", 39 | "@rollup/plugin-replace": "^3.0.0", 40 | "@vue/cli-plugin-babel": "~5.0.8", 41 | "@vue/cli-service": "~5.0.8", 42 | "@vue/compiler-sfc": "^3.2.22", 43 | "babel-helper-vue-jsx-merge-props": "^2.0.2", 44 | "babel-plugin-syntax-jsx": "^6.18.0", 45 | "babel-plugin-transform-vue-jsx": "^3.7.0", 46 | "postcss": "^8.3.11", 47 | "postcss-cli": "^9.0.2", 48 | "postcss-import": "^12.0.1", 49 | "rollup": "^2.60.0", 50 | "rollup-plugin-cleanup": "^3.2.1", 51 | "rollup-plugin-copy": "^3.4.0", 52 | "rollup-plugin-css-only": "^3.1.0", 53 | "rollup-plugin-delete": "^2.0.0", 54 | "rollup-plugin-postcss": "^4.0.1", 55 | "rollup-plugin-terser": "^7.0.0", 56 | "rollup-plugin-vue": "^6.0.0", 57 | "vue": "^3.0.0", 58 | "vue-loader-v16": "^16.0.0-beta.5.4" 59 | }, 60 | "bugs": { 61 | "url": "https://github.com/ditdot-dev/vue-flow-form/issues" 62 | }, 63 | "homepage": "https://www.ditdot.hr/en/vue-flow-form", 64 | "jsdelivr": "dist/vue-flow-form.umd.min.js", 65 | "keywords": [ 66 | "vue", 67 | "vue3", 68 | "vuejs", 69 | "forms", 70 | "survey-form", 71 | "questionnaire", 72 | "quiz", 73 | "typeform", 74 | "form-generator", 75 | "vue-component", 76 | "conversational-ui" 77 | ], 78 | "license": "MIT", 79 | "repository": { 80 | "type": "git", 81 | "url": "https://github.com/ditdot-dev/vue-flow-form.git" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | cssnano: {} 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Vue Flow Form - Demo 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ditdot-dev/vue-flow-form/a8f30b81543c2070589db49d9f326d2ec3287587/public/logo.png -------------------------------------------------------------------------------- /src/assets/css/common.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence 3 | https://github.com/ditdot-dev/vue-flow-form 4 | https://www.ditdot.hr/en 5 | */ 6 | 7 | .vff { 8 | font-weight: 400; 9 | line-height: 1.34; 10 | min-height: 220px; 11 | -webkit-text-size-adjust: 100%; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-font-smoothing: antialiased; 14 | -o-font-smoothing: antialiased; 15 | } 16 | 17 | @media screen and (max-width: 1023px) { 18 | .vff, 19 | .vff-footer { 20 | font-size: 15px; 21 | } 22 | } 23 | 24 | @media screen and (min-width: 1366px) { 25 | .vff, 26 | .vff-footer { 27 | font-size: 18px; 28 | } 29 | } 30 | 31 | @media screen and (min-width: 1920px) { 32 | .vff { 33 | font-size: 22px; 34 | } 35 | } 36 | 37 | @media screen and (min-width: 2560px) { 38 | .vff { 39 | font-size: 25px; 40 | } 41 | } 42 | 43 | /* 44 | global 45 | */ 46 | 47 | .vff hr { 48 | box-sizing: content-box; 49 | overflow: visible; 50 | height: 0; 51 | } 52 | 53 | .vff pre, 54 | .vff code, 55 | .vff kbd { 56 | font-family: monospace, monospace; 57 | font-size: 1em; 58 | } 59 | 60 | .vff small { 61 | font-size: 80%; 62 | } 63 | 64 | .vff sub, 65 | .vff sup { 66 | font-size: 75%; 67 | line-height: 0; 68 | position: relative; 69 | vertical-align: baseline; 70 | } 71 | 72 | .vff sub { 73 | bottom: -0.25em; 74 | } 75 | 76 | .vff sup { 77 | top: -0.5em; 78 | } 79 | 80 | .vff b, 81 | .vff strong { 82 | font-weight: bolder; 83 | } 84 | 85 | .vff a { 86 | cursor: pointer; 87 | background-color: transparent; 88 | } 89 | 90 | .vff a, 91 | .vff a:hover, 92 | .vff a:active, 93 | .vff a:focus { 94 | outline: 0; 95 | text-decoration: none; 96 | } 97 | 98 | .vff ol, 99 | .vff ul, 100 | .vff table { 101 | margin-top: 0; 102 | margin-bottom: 22px; 103 | } 104 | 105 | .vff img { 106 | max-width: 100%; 107 | height: auto; 108 | margin-bottom: 22px; 109 | } 110 | 111 | /* utils */ 112 | .vff .text-thin { 113 | font-weight: 300; 114 | } 115 | 116 | .vff .disabled, 117 | .vff [disabled] { 118 | opacity: .4; 119 | user-select: none; 120 | pointer-events: none; 121 | cursor: not-allowed; 122 | } 123 | 124 | .vff .clearfix:after { 125 | visibility: hidden; 126 | display: block; 127 | font-size: 0; 128 | content: " "; 129 | clear: both; 130 | height: 0; 131 | } 132 | 133 | .vff ol.reset-list, 134 | .vff ul.reset-list { 135 | list-style: none; 136 | margin: 0; 137 | padding: 0; 138 | } 139 | 140 | /* 141 | vff-form 142 | */ 143 | 144 | .vff { 145 | width: 100%; 146 | padding: 0 10%; 147 | margin: 18vh 0 60px; 148 | position: relative; 149 | text-align: left; 150 | box-sizing: border-box; 151 | } 152 | 153 | header.vff-header + .vff { 154 | margin-top: 16vh; 155 | } 156 | 157 | header.vff-header + .vff.vff-not-standalone { 158 | margin-top: 0; 159 | } 160 | 161 | .vff *, 162 | .vff *:before, 163 | .vff *:after { 164 | box-sizing: inherit; 165 | } 166 | 167 | .vff .q-is-active { 168 | opacity: 1; 169 | } 170 | 171 | .vff .q-form.q-is-inactive { 172 | display: none; 173 | } 174 | 175 | .vff .f-full-width { 176 | display: block; 177 | width: 100%; 178 | } 179 | 180 | .vff .f-string-em { 181 | text-transform: uppercase; 182 | } 183 | 184 | .vff .f-enter { 185 | margin-bottom: 20px; 186 | } 187 | 188 | /* container */ 189 | .vff .f-container, 190 | header.vff-header .f-container{ 191 | padding: 0 8%; 192 | width: 100%; 193 | } 194 | 195 | .vff .f-container { 196 | margin-top: 1em; 197 | margin-bottom: 1em; 198 | } 199 | 200 | /* header */ 201 | header.vff-header { 202 | line-height: 1; 203 | padding: .9em 10% .8em; 204 | box-sizing: border-box; 205 | position: static; 206 | width: 100%; 207 | } 208 | 209 | .vff-header *, 210 | .vff-header *:before, 211 | .vff-header *:after { 212 | box-sizing: inherit; 213 | } 214 | 215 | header.vff-header .f-container { 216 | margin: 0; 217 | } 218 | 219 | header.vff-header img.f-logo, 220 | header.vff-header svg.f-logo { 221 | height: 18px; 222 | max-width: 240px; 223 | opacity: 1; 224 | } 225 | 226 | /* 227 | form elements 228 | */ 229 | 230 | .vff button, 231 | .vff input, 232 | .vff optgroup, 233 | .vff select, 234 | .vff textarea { 235 | font-family: inherit; 236 | font-size: 100%; 237 | line-height: 1.15; 238 | margin: 0; 239 | } 240 | 241 | .vff fieldset { 242 | padding: 0.35em 0.75em 0.625em; 243 | } 244 | 245 | .vff legend { 246 | box-sizing: border-box; 247 | color: inherit; 248 | display: table; 249 | max-width: 100%; 250 | padding: 0; 251 | white-space: normal; 252 | } 253 | 254 | .vff [type="number"]::-webkit-inner-spin-button, 255 | .vff [type="number"]::-webkit-outer-spin-button { 256 | height: auto; 257 | } 258 | 259 | .vff [type="search"] { 260 | -webkit-appearance: textfield; 261 | outline-offset: -2px; 262 | } 263 | 264 | .vff [type="search"]::-webkit-search-decoration { 265 | -webkit-appearance: none; 266 | } 267 | 268 | .vff [type="file"] { 269 | appearance: none; 270 | -moz-appearance: none; 271 | -webkit-appearance: none; 272 | border: 0; 273 | outline: 0; 274 | border-radius: 0; 275 | margin: 0 .2em; 276 | padding: .1em 0 .15em; 277 | font-size: .5em; 278 | line-height: normal; 279 | font-weight: 900; 280 | } 281 | 282 | .vff input[type="file"]:focus { 283 | outline: 1px dotted #000; 284 | outline-offset: 4px; 285 | } 286 | 287 | @media (prefers-color-scheme: dark) { 288 | .vff input[type="file"]:focus{ 289 | outline-color: #fff; 290 | } 291 | } 292 | 293 | .vff input[type="file"]::-webkit-file-upload-button { 294 | appearance: none; 295 | -moz-appearance: none; 296 | -webkit-appearance: none; 297 | outline: 0; 298 | border: 0; 299 | font: inherit; 300 | font-size: .86em; 301 | font-weight: 400; 302 | margin-right: .7em; 303 | text-align: center; 304 | max-width: 100%; 305 | min-width: 90px; 306 | min-height: 44px; 307 | display: inline-block; 308 | white-space: pre-wrap; 309 | cursor: pointer; 310 | padding: .6em 1.4em; 311 | background-color: #efefef; 312 | } 313 | 314 | .vff input[type="file"]::-webkit-file-upload-button:active { 315 | color: #000; 316 | } 317 | 318 | .vff input[type="file"]::file-selector-button { 319 | min-height: 44px; 320 | display: inline-block; 321 | white-space: pre-wrap; 322 | font: inherit; 323 | font-size: .86em; 324 | font-weight: 400; 325 | margin-right: .6em; 326 | max-width: 100%; 327 | min-width: 90px; 328 | } 329 | 330 | /* buttons */ 331 | .vff button, 332 | .vff [type="button"], 333 | .vff [type="reset"], 334 | .vff [type="submit"], 335 | .vff input { 336 | -webkit-appearance: none; 337 | overflow: visible; 338 | } 339 | 340 | /* default button */ 341 | .vff button { 342 | border: none; 343 | outline: 0; 344 | cursor: pointer; 345 | padding: .6em 1.4em; 346 | text-align: center; 347 | display: inline-block; 348 | min-height: 44px; 349 | white-space: pre-wrap; 350 | max-width: 100%; 351 | } 352 | 353 | .vff .o-btn-action { 354 | z-index: 1; 355 | line-height: 1.2; 356 | font-weight: 900; 357 | text-transform: lowercase; 358 | } 359 | 360 | .vff .o-btn-action span { 361 | font-size: 1em; 362 | user-select: none; 363 | text-decoration: none; 364 | transition: all 0.4s ease 0s; 365 | } 366 | 367 | .vff .f-enter-desc { 368 | font-size: .9em; 369 | display: inline-block; 370 | margin-left: .6em; 371 | margin-top: .7em; 372 | } 373 | 374 | .vff span.faux-form { 375 | border-bottom: 1px solid; 376 | display: inline-block; 377 | margin-right: .2em; 378 | margin-left: 0; 379 | position: relative; 380 | white-space: nowrap; 381 | } 382 | 383 | .vff select { 384 | -webkit-appearance: none; 385 | -moz-appearance: none; 386 | appearance: none; 387 | text-transform: none; 388 | background: transparent; 389 | border-radius: 0; 390 | border: 0; 391 | cursor: pointer; 392 | font-size: .5em; 393 | line-height: 1.32; 394 | margin: 0; 395 | opacity: 0; 396 | outline: 0; 397 | padding: .6em 8px; 398 | width: 100%; 399 | position: absolute; 400 | z-index: 1; 401 | } 402 | 403 | .vff input[type="text"], 404 | .vff input[type="number"], 405 | .vff input[type="tel"], 406 | .vff input[type="email"], 407 | .vff input[type="url"], 408 | .vff input[type="password"], 409 | .vff input[type="date"], 410 | .vff textarea { 411 | -webkit-appearance: none; 412 | -moz-appearance: none; 413 | appearance: none; 414 | background-color: transparent; 415 | border: 0; 416 | border-bottom: 1px solid; 417 | border-radius: 0; 418 | line-height: 1.48; 419 | margin: 0 .6em 0 0; 420 | outline: 0; 421 | padding: .16em .2em; 422 | font-size: .72em; 423 | font-weight: 900; 424 | } 425 | 426 | .vff .f-other.f-selected .f-label { 427 | font-weight: 900; 428 | } 429 | 430 | .vff textarea { 431 | overflow: auto; 432 | display: block; 433 | outline: none; 434 | resize: vertical; 435 | } 436 | 437 | .vff .f-full-width input[type="text"], 438 | .vff .f-full-width input[type="number"], 439 | .vff .f-full-width input[type="tel"], 440 | .vff .f-full-width input[type="email"], 441 | .vff .f-full-width input[type="url"], 442 | .vff .f-full-width input[type="password"], 443 | .vff .f-full-width input[type="date"], 444 | .vff .f-full-width input[type="file"], 445 | .vff .f-full-width textarea, 446 | .vff .f-full-width span.faux-form { 447 | width: 100%; 448 | padding-left: .16em; 449 | padding-right: .16em; 450 | } 451 | 452 | .vff .f-required { 453 | display: none; /* hides required asterisks */ 454 | } 455 | 456 | .vff .f-answer.f-full-width { 457 | margin-top: 26px; 458 | } 459 | 460 | .vff span.f-sub + .f-answer.f-full-width { 461 | margin-top: 22px; 462 | } 463 | 464 | .vff div.field-sectionbreak .f-answer { 465 | margin-top: 18px; 466 | } 467 | 468 | .vff span.f-empty { 469 | display: inline-block; 470 | min-width: 3em; 471 | padding-right: 28px; 472 | font-weight: 300; 473 | padding-bottom: 4px; 474 | } 475 | 476 | .vff span.f-answered { 477 | font-weight: 900; 478 | } 479 | 480 | /* faux select arrow */ 481 | .vff .f-arrow-down { 482 | display: inline-block; 483 | margin-left: .2em; 484 | pointer-events: none; 485 | width: 18px; 486 | height: 100%; 487 | position: absolute; 488 | right: 0; 489 | bottom: 0; 490 | line-height: 1.3; 491 | } 492 | 493 | .vff .f-arrow-down svg { 494 | width: 100%; 495 | height: auto; 496 | } 497 | 498 | /* iOS datepicker */ 499 | .vff.vff-is-ios .field-date:not(.f-has-value) .f-answer > span { 500 | position: relative; 501 | top: 0; 502 | left: 0; 503 | } 504 | 505 | .vff.vff-is-ios .field-date:not(.f-has-value) .f-answer > span::before { 506 | position: absolute; 507 | content: attr(data-placeholder); 508 | display: block; 509 | pointer-events: none; 510 | padding: 0.12em .2em; 511 | } 512 | 513 | .vff.vff-is-ios input[type="date"] { 514 | height: 32px; 515 | display: block; 516 | } 517 | 518 | /* 519 | links 520 | */ 521 | 522 | .vff a.f-link, 523 | .vff .field-submit .f-section-wrap a { 524 | color: inherit; 525 | border-bottom: 1px dotted; 526 | word-break: break-word; 527 | } 528 | 529 | .vff a.f-link:hover, 530 | .vff a.f-link:active, 531 | .vff .field-submit .f-section-wrap a:hover, 532 | .vff .field-submit .f-section-wrap a:active { 533 | color: inherit; 534 | border-bottom: none; 535 | } 536 | 537 | /* 538 | typography 539 | */ 540 | 541 | .vff li, 542 | .vff p, 543 | .vff .f-section-text, 544 | .vff span.f-tagline, 545 | .vff span.f-sub, 546 | .vff .fh2 span.f-label-scale { 547 | font-size: 1.1em; 548 | line-height: 1.34; 549 | } 550 | 551 | .vff h1, 552 | .vff .fh1 { 553 | font-weight: 900; 554 | font-size: 3em; 555 | margin: 0.67em 0; 556 | } 557 | 558 | .vff h2, 559 | .vff .fh2 { 560 | font-weight: 900; 561 | font-size: 2.4em; 562 | line-height: 1.34; 563 | padding-right: 1.8em; 564 | margin-bottom: 21px; 565 | } 566 | 567 | .vff h3, 568 | .vff .fh3 { 569 | font-weight: 300; 570 | font-size: 1.5em; 571 | margin-top: 0; 572 | margin-bottom: 0; 573 | } 574 | 575 | .vff .fh1, 576 | .vff .fh2, 577 | .vff .fh3 { 578 | display: block; 579 | } 580 | 581 | /* misc-typography */ 582 | .vff span.f-tagline, 583 | .vff span.f-sub { 584 | font-weight: 400; 585 | display: block; 586 | } 587 | 588 | .vff .fh2 span.f-tagline, 589 | .vff .fh2 span.f-sub, 590 | .vff .fh2 span.f-label-scale { 591 | font-size: .51em; 592 | } 593 | 594 | .vff .fh2 span.f-label-scale { 595 | font-size: .42em; 596 | } 597 | 598 | .vff span.f-tagline, 599 | .vff span.f-text { 600 | margin-bottom: 8px; 601 | } 602 | 603 | .vff span.f-sub { 604 | margin-bottom: 8px; 605 | margin-top: 5px; 606 | } 607 | 608 | .vff span.f-sub span:not(.f-string-em) { 609 | margin-right: .4em; 610 | } 611 | 612 | .vff span.f-sub span.f-help { 613 | display: block; 614 | } 615 | 616 | .vff span.f-sub span + span.f-help { 617 | margin-top: 0; 618 | } 619 | 620 | .vff span.f-text { 621 | margin-right: .6em; 622 | } 623 | 624 | /* description */ 625 | .vff p.f-description { 626 | margin-top: 0; 627 | padding-right: 4em; 628 | } 629 | 630 | .vff p.f-description span, 631 | .vff p.f-description a.f-link { 632 | margin-right: 8px; 633 | } 634 | 635 | /* 636 | f-radios (multiple choice) 637 | */ 638 | 639 | .vff ul.f-radios { 640 | margin: 0; 641 | padding: 0; 642 | list-style-type: none; 643 | max-width: 590px; 644 | min-width: 160px; 645 | } 646 | 647 | .vff ul.f-radios li { 648 | padding: .62em .68em; 649 | margin: .5em 0 .6em; 650 | font-weight: 300; 651 | line-height: 1.24; 652 | cursor: default; 653 | -webkit-touch-callout: none; 654 | -webkit-user-select: none; 655 | -ms-user-select: none; 656 | user-select: none; 657 | position: relative; 658 | overflow: hidden; 659 | } 660 | 661 | .vff ul.f-radios li.f-other input[type="text"] { 662 | border-bottom: none; 663 | width: 100%; 664 | padding: 0; 665 | margin: 0; 666 | border: 0; 667 | line-height: inherit; 668 | font-size: inherit; 669 | } 670 | 671 | .vff ul.f-radios li.f-other.f-focus { 672 | padding: .48em .68em; 673 | } 674 | 675 | .vff .f-radios-desc { 676 | display: block; 677 | } 678 | 679 | .vff .f-radios-desc, 680 | .vff ul.f-radios li, 681 | .vff ul.f-radios li input[type="text"] { 682 | font-size: .4em; 683 | } 684 | 685 | .vff ul.f-radios li div.f-label-wrap { 686 | display: -ms-flexbox; 687 | display: flex; 688 | width: 100%; 689 | } 690 | 691 | .vff ul.f-radios li span.f-label { 692 | margin-left: .4em; 693 | } 694 | 695 | .vff ul.f-radios li span.f-key { 696 | width: 16px; 697 | text-align: center; 698 | } 699 | 700 | /* 701 | f-radios (multiple picture choice) 702 | */ 703 | 704 | .vff .field-multiplepicturechoice ul.f-radios { 705 | max-width: 760px; 706 | min-width: auto; 707 | display: -ms-flexbox; 708 | display: flex; 709 | margin: 0 -8px 0 0; 710 | flex-flow: 0 1; 711 | -moz-box-align: stretch; 712 | align-items: stretch; 713 | flex-wrap: wrap; 714 | } 715 | 716 | .vff .field-multiplepicturechoice ul.f-radios li { 717 | position: relative; 718 | cursor: pointer; 719 | display: -ms-flexbox; 720 | display: flex; 721 | -moz-box-align: center; 722 | align-items: center; 723 | flex-direction: column; 724 | padding: 8px 8px 10px; 725 | margin-bottom: 8px; 726 | margin-right: 8px; 727 | -ms-flex: 0 0 calc(25% - 8px); 728 | flex: 0 0 calc(25% - 8px); 729 | font-size: 15px; 730 | line-height: 1.38; 731 | } 732 | 733 | .vff .field-multiplepicturechoice ul.f-radios li span.f-image { 734 | display: flex; 735 | display: -ms-flexbox; 736 | -moz-box-align: center; 737 | align-items: center; 738 | -moz-box-pack: center; 739 | justify-content: center; 740 | overflow: hidden; 741 | width: 100%; 742 | height: 140px; 743 | margin-bottom: 8px; 744 | } 745 | 746 | .vff .field-multiplepicturechoice ul.f-radios li span.f-image img { 747 | margin-bottom: 0; 748 | max-height: 100%; 749 | max-width: 100%; 750 | } 751 | 752 | /* 753 | field opinion scale 754 | */ 755 | 756 | .vff .field-opinionscale ul.f-radios { 757 | max-width: 760px; 758 | min-width: auto; 759 | display: -ms-flexbox; 760 | display: -webkit-box; 761 | display: flex; 762 | margin: 0 -8px 0 0; 763 | -webkit-box-orient: vertical; 764 | -webkit-box-direction: normal; 765 | -webkit-box-align: stretch; 766 | -ms-flex-align: stretch; 767 | align-items: stretch; 768 | -ms-flex-wrap: wrap; 769 | flex-wrap: wrap; 770 | } 771 | 772 | .vff .field-opinionscale ul.f-radios li { 773 | display: -ms-flexbox; 774 | display: -webkit-box; 775 | display: flex; 776 | -webkit-box-align: center; 777 | align-items: center; 778 | margin-bottom: 8px; 779 | position: relative; 780 | cursor: pointer; 781 | -ms-flex-align: center; 782 | -webkit-box-orient: vertical; 783 | -webkit-box-direction: normal; 784 | padding: 9px 8px 8px; 785 | margin-right: 8px; 786 | -ms-flex: 1 0 calc(10% - 8px); 787 | flex: 1 0 calc(10% - 8px); 788 | font-size: 15px; 789 | line-height: 1.38; 790 | min-height: 56px; 791 | } 792 | 793 | .vff .field-opinionscale ul.f-radios li .f-label-wrap { 794 | justify-content: center; 795 | } 796 | 797 | .vff .field-opinionscale ul.f-radios li .f-label { 798 | margin: 0; 799 | } 800 | 801 | .vff .f-label-scale-wrap { 802 | display: -ms-flexbox; 803 | display: flex; 804 | justify-content: space-between; 805 | max-width: 760px; 806 | min-width: auto; 807 | margin: 2px -8px .6em 0; 808 | } 809 | 810 | .vff .f-label-scale { 811 | margin-right: 8px; 812 | font-weight: 400; 813 | color: var(--vff-secondary-text-color); 814 | } 815 | 816 | .vff .f-label-scale-num { 817 | display: none; 818 | } 819 | 820 | /* 821 | field rate 822 | */ 823 | 824 | .vff .field-iconrate ul.f-radios { 825 | max-width: 760px; 826 | min-width: auto; 827 | display: -ms-flexbox; 828 | display: -webkit-box; 829 | display: flex; 830 | margin: 0 -8px 0 0; 831 | -webkit-box-orient: vertical; 832 | -webkit-box-direction: normal; 833 | -webkit-box-align: stretch; 834 | -ms-flex-align: stretch; 835 | align-items: stretch; 836 | } 837 | 838 | .vff .field-iconrate ul.f-radios li { 839 | max-width: 60px; 840 | height: 100%; 841 | flex: 1 0 0%; 842 | display: -ms-flexbox; 843 | display: flex; 844 | -webkit-box-align: center; 845 | align-items: center; 846 | -webkit-box-pack: center; 847 | justify-content: center; 848 | cursor: pointer; 849 | outline: none; 850 | border: none; 851 | position: relative; 852 | margin-top: 0; 853 | padding: 0 10px 0 0; 854 | } 855 | 856 | .vff .field-iconrate ul.f-radios li .f-icon-wrap { 857 | flex: 1 1 0%; 858 | display: -ms-flexbox; 859 | display: flex; 860 | -webkit-box-align: center; 861 | align-items: center; 862 | flex-direction: column; 863 | } 864 | 865 | .vff .field-iconrate ul.f-radios li .f-icon { 866 | margin-bottom: 8px; 867 | align-self: stretch; 868 | } 869 | 870 | .vff .field-iconrate ul.f-radios li .f-icon-wrap svg { 871 | height: 100%; 872 | width: 100%; 873 | max-height: 60px; 874 | max-width: 60px; 875 | fill: transparent; 876 | stroke: var(--vff-secondary-text-color); 877 | } 878 | 879 | .vff .field-iconrate ul.f-radios li.f-selected, 880 | .vff .field-iconrate ul.f-radios li:active { 881 | border-color: unset; 882 | background-color: unset; 883 | } 884 | 885 | .vff .field-iconrate ul.f-radios li.f-hovered .f-icon-wrap svg, 886 | .vff .field-iconrate ul.f-radios li:hover .f-icon-wrap svg { 887 | fill: var(--vff-tertiary-text-color); 888 | } 889 | 890 | .vff .field-iconrate ul.f-radios li.f-selected .f-icon-wrap svg, 891 | .vff .field-iconrate ul.f-radios li.f-previous .f-icon-wrap svg { 892 | fill: var(--vff-tertiary-text-color); 893 | stroke: var(--vff-main-text-color); 894 | } 895 | 896 | 897 | /* 898 | field matrix 899 | */ 900 | 901 | .vff .f-matrix-wrap { 902 | overflow-x: auto; 903 | } 904 | 905 | .vff .f-matrix-table { 906 | width: 100%; 907 | font-size: .5em; 908 | line-height: 1.36; 909 | font-weight: normal; 910 | margin-bottom: 0; 911 | } 912 | 913 | .vff .f-table-string { 914 | font-size: .84em; 915 | } 916 | 917 | .vff .f-table-cell { 918 | padding: .52em; 919 | min-width: 8.6em; 920 | text-align: left; 921 | } 922 | 923 | .vff .f-table-cell.f-row-cell { 924 | min-width: 9.2em; 925 | } 926 | 927 | .vff .f-row-cell { 928 | padding-left: 1em; 929 | } 930 | 931 | .vff .f-column-cell { 932 | font-weight: 900; 933 | text-align: center; 934 | } 935 | 936 | .vff .f-matrix-cell { 937 | text-align: center; 938 | } 939 | 940 | .vff .f-field-wrap, 941 | .vff .f-matrix-field { 942 | display: flex; 943 | justify-content: center; 944 | align-items: center; 945 | } 946 | 947 | .vff .f-field-mask { 948 | position: relative; 949 | display: inline-block; 950 | cursor: pointer; 951 | width: 1.4em; 952 | height: 1.4em; 953 | } 954 | 955 | .vff .f-field-svg { 956 | position: absolute; 957 | display: inline-block; 958 | top: 50%; 959 | left: 50%; 960 | transform: translate(-50%, -50%); 961 | color: #000; 962 | border: 1px solid currentColor; 963 | fill: transparent; 964 | width: 100%; 965 | height: 100%; 966 | transition: color 0.2s ease 0s; 967 | } 968 | 969 | .vff .f-radio-svg { 970 | border-radius: 100%; 971 | } 972 | 973 | .vff .f-radio-svg circle, 974 | .vff .f-checkbox-svg rect { 975 | fill: inherit; 976 | } 977 | 978 | .vff .f-field-control:checked ~ .f-field-mask .f-field-svg { 979 | color: inherit; 980 | } 981 | 982 | .vff .f-field-control:hover ~ .f-field-mask .f-field-svg, 983 | .vff .f-field-control:checked ~ .f-field-mask .f-field-svg { 984 | fill: currentColor; 985 | } 986 | 987 | .vff .f-field-control:focus { 988 | outline: none; 989 | } 990 | 991 | .vff .f-field-control:focus-visible ~ .f-field-mask { 992 | outline: 1px dotted; 993 | } 994 | 995 | .vff .f-field-control:focus-visible ~ .f-checkbox-mask { 996 | outline-offset: 2px; 997 | } 998 | 999 | /* 1000 | footer 1001 | */ 1002 | 1003 | .vff-footer { 1004 | position: fixed; 1005 | bottom: 0; 1006 | right: 0; 1007 | width: 100%; 1008 | font-weight: 300; 1009 | line-height: 1.2; 1010 | } 1011 | 1012 | .vff-footer .footer-inner-wrap { 1013 | text-align: right; 1014 | padding: 11px 20px 12px; 1015 | } 1016 | 1017 | .vff-footer .f-progress, 1018 | .vff-footer .f-nav, 1019 | .vff-footer .f-timer { 1020 | display: inline-block; 1021 | } 1022 | 1023 | .vff-footer .f-timer { 1024 | font-family: monospace; 1025 | font-size: 17px; 1026 | margin-left: .18em; 1027 | } 1028 | 1029 | .vff-footer .f-prev, 1030 | .vff-footer .f-next, 1031 | .vff-footer .f-progress { 1032 | margin: 0 .18em; 1033 | padding: .2em .2em; 1034 | } 1035 | 1036 | .vff-footer .f-progress { 1037 | top: -1px; 1038 | position: relative; 1039 | } 1040 | 1041 | .vff-footer a.f-disabled { 1042 | opacity: .4; 1043 | cursor: default; 1044 | pointer-events: none; 1045 | } 1046 | 1047 | .vff-footer .f-prev svg, 1048 | .vff-footer .f-next svg { 1049 | display: inline-block; 1050 | transition: fill 0.2s ease 0s; 1051 | width: 23px; 1052 | height: 13px; 1053 | } 1054 | 1055 | .vff-footer .f-nav-text { 1056 | display: none; 1057 | } 1058 | 1059 | /*progress-bar*/ 1060 | .vff-footer .f-progress-bar { 1061 | height: 1px; 1062 | position: static; 1063 | display: inline-block; 1064 | width: 64px; 1065 | vertical-align: middle; 1066 | margin: 0 6px 0 0; 1067 | } 1068 | 1069 | .vff-footer .f-progress-bar-inner { 1070 | height: 1px; 1071 | transition: width .3s ease; 1072 | } 1073 | 1074 | /*section breaks*/ 1075 | .vff .f-section-wrap { 1076 | margin-bottom: 30px; 1077 | } 1078 | 1079 | .vff .f-section-wrap > div, 1080 | .vff .f-submit { 1081 | margin-bottom: 20px; 1082 | } 1083 | 1084 | .vff .field-sectionbreak, 1085 | .vff .field-submit { 1086 | max-width: 920px; 1087 | } 1088 | 1089 | /* 1090 | not-standalone option 1091 | */ 1092 | 1093 | .vff.vff-not-standalone { 1094 | height: 100%; 1095 | margin-top: 0; 1096 | margin-bottom: 0; 1097 | padding-top: 50px; 1098 | padding-bottom: 100px; 1099 | } 1100 | 1101 | .vff.vff-not-standalone .f-container { 1102 | margin: 0; 1103 | } 1104 | 1105 | .vff.vff-not-standalone .vff-footer { 1106 | position: absolute; 1107 | bottom: 0; 1108 | width: 100%; 1109 | } 1110 | 1111 | /* 1112 | animations 1113 | */ 1114 | 1115 | .vff-animate { 1116 | -webkit-animation-duration: .4s; 1117 | animation-duration: .4s; 1118 | -webkit-animation-fill-mode: forwards; 1119 | animation-fill-mode: forwards; 1120 | } 1121 | 1122 | /* prevent Android Chrome flickering */ 1123 | .vff-animate * { 1124 | -webkit-backface-visibility: hidden; 1125 | backface-visibility: hidden; 1126 | } 1127 | 1128 | .vff .f-fade-in { 1129 | animation-name: ffadeIn; 1130 | } 1131 | 1132 | @keyframes ffadeIn { 1133 | 0% { 1134 | opacity: 0; 1135 | } 1136 | 1137 | 100% { 1138 | opacity: 1; 1139 | } 1140 | } 1141 | 1142 | .vff .f-fade-in-down { 1143 | animation-name: ffadeInDown; 1144 | } 1145 | 1146 | @keyframes ffadeInDown { 1147 | 0% { 1148 | opacity: 0; 1149 | transform: translate3d(0, -12px, 0); 1150 | } 1151 | 1152 | 100% { 1153 | opacity: 1; 1154 | transform: none; 1155 | } 1156 | } 1157 | 1158 | .vff .f-fade-in-up { 1159 | animation-name: ffadeInUp; 1160 | } 1161 | 1162 | @keyframes ffadeInUp { 1163 | 0% { 1164 | opacity: 0; 1165 | transform: translate3d(0, 12px, 0); 1166 | } 1167 | 1168 | 100% { 1169 | opacity: 1; 1170 | transform: none; 1171 | } 1172 | } 1173 | 1174 | /* 1175 | media-start 1176 | */ 1177 | 1178 | @media only screen and (min-width: 1440px) { 1179 | .vff .field-sectionbreak, 1180 | .vff .field-submit { 1181 | max-width: 960px; 1182 | } 1183 | 1184 | .vff h2, 1185 | .vff .fh2 { 1186 | padding-right: 2.2em; 1187 | } 1188 | } 1189 | 1190 | @media only screen and (min-width: 1920px) { 1191 | .vff .field-sectionbreak, 1192 | .vff .field-submit { 1193 | max-width: 1160px; 1194 | } 1195 | } 1196 | 1197 | @media screen and (max-width: 1366px) { 1198 | header.vff-header, 1199 | .vff { 1200 | padding-right: 5%; 1201 | padding-left: 5%; 1202 | } 1203 | } 1204 | 1205 | @media only screen and (max-width: 1023px) { 1206 | header.vff-header { 1207 | padding-left: 0; 1208 | padding-right: 0; 1209 | } 1210 | 1211 | .vff { 1212 | padding-left: 0; 1213 | padding-right: 0; 1214 | } 1215 | } 1216 | 1217 | @media only screen and (max-width: 767px) { 1218 | .vff h2, 1219 | .vff .fh2 { 1220 | font-size: 2.2em; 1221 | padding-right: 0; 1222 | } 1223 | 1224 | .vff input[type="text"], 1225 | .vff input[type="number"], 1226 | .vff input[type="tel"], 1227 | .vff input[type="email"], 1228 | .vff input[type="url"], 1229 | .vff input[type="password"], 1230 | .vff input[type="date"], 1231 | .vff input[type="file"], 1232 | .vff textarea { 1233 | font-size: .78em; 1234 | } 1235 | 1236 | .vff input[type="file"] { 1237 | font-size: .4em; 1238 | } 1239 | 1240 | .vff .fh2 span.f-sub, 1241 | .vff .fh2 span.f-tagline, 1242 | .vff .fh2 span.f-label-scale { 1243 | font-size: .58em; 1244 | } 1245 | 1246 | .vff .f-container, 1247 | header.vff-header .f-container { 1248 | padding: 0 5.5%; 1249 | } 1250 | 1251 | .vff span.f-empty { 1252 | min-width: 2.2em; 1253 | padding-right: 20px; 1254 | } 1255 | 1256 | .vff p.f-description { 1257 | padding-right: 0; 1258 | } 1259 | 1260 | .vff .field-multiplepicturechoice ul.f-radios li { 1261 | font-size: 14px; 1262 | } 1263 | 1264 | .vff .field-multiplepicturechoice ul.f-radios li span.f-image { 1265 | height: 90px; 1266 | } 1267 | } 1268 | 1269 | @media screen and (max-width: 479px) { 1270 | .vff { 1271 | margin-top: 10vh; 1272 | min-height: 180px; 1273 | } 1274 | 1275 | header.vff-header + .vff { 1276 | margin-top: 8vh; 1277 | } 1278 | 1279 | header.vff-header + .vff.vff-not-standalone { 1280 | margin-top: 0; 1281 | } 1282 | 1283 | .vff input[type="text"], 1284 | .vff input[type="number"], 1285 | .vff input[type="tel"], 1286 | .vff input[type="email"], 1287 | .vff input[type="url"], 1288 | .vff input[type="password"], 1289 | .vff input[type="date"], 1290 | .vff input[type="file"], 1291 | .vff textarea { 1292 | line-height: 1.4; 1293 | padding: .16em .2em; 1294 | } 1295 | 1296 | .vff input[type="file"] { 1297 | font-size: .6em; 1298 | } 1299 | 1300 | .vff select { 1301 | font-size: .72em; 1302 | padding-top: .2em; 1303 | padding-bottom: .2em; 1304 | } 1305 | 1306 | .vff button { 1307 | min-height: 42px; 1308 | } 1309 | 1310 | .vff .f-arrow-down { 1311 | width: 12px; 1312 | } 1313 | 1314 | .vff .fh2 span.f-sub, 1315 | .vff .fh2 span.f-tagline, 1316 | .vff .fh2 span.f-label-scale { 1317 | font-size: .64em; 1318 | } 1319 | 1320 | .vff .fh2 span.f-label-scale { 1321 | margin-bottom: .3em; 1322 | } 1323 | 1324 | .vff span.f-tagline { 1325 | line-height: 1.5; 1326 | } 1327 | 1328 | .vff span.f-text { 1329 | margin-right: 0; 1330 | } 1331 | 1332 | .vff h2, 1333 | .vff .fh2 { 1334 | font-size: 1.56em; 1335 | } 1336 | 1337 | .vff ul.f-radios li, 1338 | .vff ul.f-radios li input[type="text"] { 1339 | font-size: .62em; 1340 | } 1341 | 1342 | .vff .field-sectionbreak p { 1343 | font-size: 1em; 1344 | } 1345 | 1346 | .vff-footer .f-timer { 1347 | font-size: 16px; 1348 | } 1349 | 1350 | .vff .field-multiplepicturechoice ul.f-radios, 1351 | .vff .field-opinionscale ul.f-radios, 1352 | .vff .f-label-scale-wrap { 1353 | max-width: 330px; 1354 | } 1355 | 1356 | .vff .field-multiplepicturechoice ul.f-radios li { 1357 | -ms-flex: 0 0 calc(50% - 8px); 1358 | flex: 0 0 calc(50% - 8px); 1359 | } 1360 | 1361 | .vff .field-opinionscale ul.f-radios li { 1362 | -ms-flex: 0 0 calc(20% - 8px); 1363 | flex: 0 0 calc(20% - 8px); 1364 | } 1365 | 1366 | .vff .f-label-scale-wrap { 1367 | flex-direction: column; 1368 | } 1369 | 1370 | .vff .f-label-scale-num { 1371 | display: inline; 1372 | } 1373 | } 1374 | 1375 | @media screen and (max-height: 400px) { 1376 | .vff { 1377 | margin-top: 12vh; 1378 | min-height: 180px; 1379 | } 1380 | 1381 | header.vff-header { 1382 | position: static; 1383 | } 1384 | 1385 | .vff-footer { 1386 | font-size: 15px; 1387 | } 1388 | 1389 | .vff-footer .footer-inner-wrap { 1390 | padding-top: 8px; 1391 | padding-bottom: 8px; 1392 | } 1393 | 1394 | .vff .f-radios-desc, 1395 | .vff ul.f-radios li, 1396 | .vff ul.f-radios li input[type="text"] { 1397 | font-size: 14px; 1398 | } 1399 | 1400 | .vff h2, 1401 | .vff .fh2 { 1402 | font-size: 1.56em; 1403 | } 1404 | 1405 | .vff .fh2 span.f-sub, 1406 | .vff .fh2 span.f-tagline, 1407 | .vff .fh2 span.f-label-scale { 1408 | font-size: .6em; 1409 | } 1410 | 1411 | .vff .field-multiplepicturechoice ul.f-radios li span.f-image { 1412 | height: 90px; 1413 | } 1414 | } 1415 | -------------------------------------------------------------------------------- /src/assets/css/themes/theme-green.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence 3 | https://github.com/ditdot-dev/vue-flow-form 4 | https://www.ditdot.hr/en 5 | */ 6 | 7 | /* 8 | simple vue.js like theme with green background 9 | */ 10 | 11 | :root { 12 | --vff-bg-color: #3EAF7C; 13 | --vff-main-text-color: #fff; 14 | --vff-secondary-text-color: #B5EBD3; 15 | --vff-tertiary-text-color: #52B789; 16 | --vff-main-accent-color: #111; 17 | --vff-secondary-accent-color: #ECF80D; 18 | } 19 | 20 | .vff, 21 | .vff textarea, 22 | .vff input, 23 | .vff select, 24 | .vff select option, 25 | .vff kbd, 26 | .vff-header, 27 | .vff-footer { 28 | font-family: 'Poppins', sans-serif; 29 | } 30 | 31 | body { 32 | /* override if needed when using not-standalone option */ 33 | margin: 0; 34 | background-color: var(--vff-bg-color); 35 | } 36 | 37 | .vff { 38 | color: var(--vff-main-text-color); 39 | } 40 | 41 | .vff.vff-not-standalone { 42 | background-color: var(--vff-bg-color); 43 | color: var(--vff-main-text-color); 44 | } 45 | 46 | /* header */ 47 | header.vff-header { 48 | background-color: transparent; 49 | } 50 | 51 | header.vff-header svg.f-logo { 52 | fill: var(--vff-main-accent-color); 53 | } 54 | 55 | /* footer */ 56 | .vff-footer .footer-inner-wrap { 57 | background-color: rgba(62,175,124,0.75); 58 | } 59 | 60 | .vff textarea, 61 | .vff input, 62 | .vff select option { 63 | color: var(--vff-main-accent-color); 64 | } 65 | 66 | .vff input[type="text"], 67 | .vff input[type="number"], 68 | .vff input[type="tel"], 69 | .vff input[type="email"], 70 | .vff input[type="url"], 71 | .vff input[type="password"], 72 | .vff input[type="date"], 73 | .vff textarea, 74 | .vff span.faux-form { 75 | border-bottom-color: var(--vff-secondary-text-color); 76 | } 77 | 78 | .vff a, 79 | .vff a:active { 80 | color: var(--vff-main-text-color); 81 | } 82 | 83 | /* placeholder */ 84 | .vff ::-webkit-input-placeholder { /* Chrome/Opera/Safari */ 85 | color: var(--vff-secondary-text-color); 86 | opacity: 1; 87 | font-weight: 300; 88 | } 89 | 90 | .vff ::-moz-placeholder { /* Firefox 19+ */ 91 | color: var(--vff-secondary-text-color); 92 | opacity: 1; 93 | font-weight: 300; 94 | } 95 | 96 | .vff :-ms-input-placeholder { /* IE 10+ */ 97 | color: var(--vff-secondary-text-color)!important; 98 | opacity: 1!important; 99 | font-weight: 300!important; 100 | } 101 | 102 | .vff :-moz-placeholder { /* Firefox 18- */ 103 | color: #999; 104 | opacity: 1; 105 | font-weight: 300; 106 | } 107 | 108 | /* faux input type date placeholder for iOS */ 109 | .vff.vff-is-ios .field-date:not(.f-has-value) .f-answer > span::before { 110 | color: var(--vff-secondary-text-color); 111 | font-weight: 300; 112 | font-size: .72em; 113 | } 114 | 115 | /* field-multiplechoice */ 116 | .vff ul.f-radios li { 117 | border: 1px solid var(--vff-secondary-text-color); 118 | font-weight: 900; 119 | color: var(--vff-secondary-text-color); 120 | } 121 | 122 | .vff ul.f-radios li.f-other.f-selected input { 123 | color: inherit; 124 | } 125 | 126 | .vff ul.f-radios li.f-selected, 127 | .vff ul.f-radios li:active { 128 | border-color: var(--vff-main-text-color); 129 | color: var(--vff-main-text-color); 130 | background-color: var(--vff-tertiary-text-color); 131 | } 132 | 133 | .vff .f-key { 134 | color: var(--vff-secondary-accent-color); 135 | font-weight: 300; 136 | } 137 | 138 | /* field-dropdown */ 139 | .vff span.f-empty { 140 | color: var(--vff-secondary-text-color); 141 | } 142 | 143 | .vff span.f-answered{ 144 | color: var(--vff-main-text-color); 145 | } 146 | 147 | .vff .f-arrow-down svg { 148 | fill: var(--vff-main-text-color); 149 | } 150 | 151 | /* field matrix */ 152 | .vff .f-matrix-table { 153 | border-collapse: separate; 154 | border-spacing: 0 .6em; 155 | } 156 | 157 | .vff .f-matrix-table thead th { 158 | padding-bottom: 0; 159 | } 160 | 161 | .vff .f-matrix-table td { 162 | border: 1px solid var(--vff-secondary-text-color); 163 | border-right: hidden; 164 | border-left: hidden; 165 | } 166 | 167 | .vff .f-matrix-table td:first-child { 168 | border-left: 1px solid var(--vff-secondary-text-color); 169 | } 170 | 171 | .vff .f-matrix-table td:last-child { 172 | border-right: 1px solid var(--vff-secondary-text-color); 173 | } 174 | 175 | .vff .f-matrix-table thead td:first-child { 176 | border: none; 177 | } 178 | 179 | .vff .f-field-svg { 180 | color: var(--vff-secondary-text-color); 181 | border-width: 2px; 182 | } 183 | 184 | .vff .f-field-control:checked ~ .f-field-mask .f-field-svg { 185 | color: var(--vff-main-text-color); 186 | } 187 | 188 | /* matrix scrollbar */ 189 | .vff .f-matrix-wrap::-webkit-scrollbar { 190 | height: 10px; 191 | } 192 | 193 | .vff .f-matrix-wrap::-webkit-scrollbar-track { 194 | background-color: var(--vff-tertiary-text-color); 195 | } 196 | 197 | .vff .f-matrix-wrap::-webkit-scrollbar-thumb { 198 | background-color: var(--vff-secondary-text-color); 199 | } 200 | 201 | /* buttons */ 202 | .vff .o-btn-action { 203 | color: var(--vff-bg-color); 204 | background-color: var(--vff-main-text-color); 205 | } 206 | 207 | .vff .o-btn-action:hover, 208 | .vff .o-btn-action:focus { 209 | background-color: var(--vff-main-text-color); 210 | opacity: .9; 211 | } 212 | 213 | /* progress bar */ 214 | .vff-footer .f-prev svg, 215 | .vff-footer .f-next svg { 216 | fill: var(--vff-main-text-color); 217 | } 218 | 219 | .vff-footer .f-progress { 220 | color: var(--vff-secondary-text-color); 221 | } 222 | 223 | .vff-footer .f-progress-bar { 224 | background-color: var(--vff-secondary-text-color); 225 | } 226 | 227 | .vff-footer .f-progress-bar-inner { 228 | background-color: var(--vff-main-text-color); 229 | } 230 | 231 | .vff-footer .f-prev:hover, 232 | .vff-footer .f-next:hover{ 233 | background-color: rgba(0,0,0,0.07); 234 | } 235 | 236 | /* text-muted */ 237 | .vff span.f-tagline, 238 | .vff span.f-sub, 239 | .vff p.f-description, 240 | .vff .text-muted { 241 | color: var(--vff-secondary-text-color); 242 | } 243 | 244 | .vff .text-alert, 245 | .vff .f-invalid { 246 | color: red 247 | } 248 | 249 | .vff p.text-success { 250 | color: #ECF80D; 251 | } 252 | 253 | @media screen and (max-width: 479px), (max-height: 420px) { 254 | .vff-footer .footer-inner-wrap { 255 | background-color: rgba(55,158,112,0.7); 256 | } 257 | } 258 | 259 | /* 260 | dark mode styles 261 | */ 262 | 263 | @media (prefers-color-scheme: dark) { 264 | :root { 265 | --vff-bg-color: #313640; 266 | --vff-main-text-color: #fff; 267 | --vff-secondary-text-color: #AEB6BF; 268 | --vff-tertiary-text-color: #41464F; 269 | --vff-main-accent-color: #41B883; 270 | --vff-secondary-accent-color: #A0DBC1; 271 | } 272 | 273 | .vff textarea, 274 | .vff input, 275 | .vff select option { 276 | color: var(--vff-main-accent-color); 277 | } 278 | 279 | .vff ::-webkit-calendar-picker-indicator { 280 | filter: invert(100%); 281 | } 282 | 283 | .vff span.f-answered { 284 | color: var(--vff-main-accent-color); 285 | } 286 | 287 | .vff .f-arrow-down svg { 288 | fill: var(--vff-main-accent-color); 289 | } 290 | 291 | .vff .text-success { 292 | color: var(--vff-main-accent-color); 293 | } 294 | 295 | /* footer */ 296 | .vff-footer .footer-inner-wrap { 297 | background-color: var(--vff-bg-color); 298 | } 299 | 300 | .vff-footer .f-prev svg, 301 | .vff-footer .f-next svg { 302 | fill: var(--vff-main-accent-color); 303 | } 304 | 305 | .vff-footer .f-prev.f-disabled svg, 306 | .vff-footer .f-next.f-disabled svg { 307 | fill: var(--vff-main-text-color); 308 | } 309 | 310 | .vff-footer .footer-inner-wrap { 311 | background-color: rgba(49,54,64,0.75); 312 | } 313 | 314 | .vff-footer .f-prev:hover, 315 | .vff-footer .f-next:hover { 316 | background-color: rgba(0,0,0,0.2); 317 | } 318 | 319 | .vff-footer .f-progress-bar { 320 | background-color: var(--vff-secondary-text-color); 321 | filter: brightness(1); 322 | } 323 | 324 | .vff-footer .f-progress-bar-inner { 325 | background-color: var(--vff-main-accent-color); 326 | } 327 | 328 | /* field-multiplechoice */ 329 | .vff ul.f-radios li { 330 | color: var(--vff-secondary-text-color); 331 | } 332 | 333 | .vff .f-key { 334 | color: var(--vff-main-accent-color); 335 | font-weight: 300; 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /src/assets/css/themes/theme-minimal.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence 3 | https://github.com/ditdot-dev/vue-flow-form 4 | https://www.ditdot.hr/en 5 | */ 6 | 7 | /* 8 | basic theme color and font styles 9 | */ 10 | 11 | :root { 12 | --vff-bg-color: #fff; 13 | --vff-main-text-color: #181818; 14 | --vff-secondary-text-color: #999; 15 | --vff-tertiary-text-color: #f1f1f1; 16 | --vff-main-accent-color: #0DBC79; 17 | --vff-secondary-accent-color: #BBEBD5; 18 | } 19 | 20 | .vff, 21 | .vff textarea, 22 | .vff input, 23 | .vff select, 24 | .vff select option, 25 | .vff kbd, 26 | .vff-header, 27 | .vff-footer { 28 | font-family: 'Poppins', sans-serif; 29 | } 30 | 31 | body { 32 | /* override if needed when using not-standalone option */ 33 | margin: 0; 34 | background-color: var(--vff-bg-color); 35 | } 36 | 37 | .vff { 38 | color: var(--vff-main-text-color); 39 | } 40 | 41 | .vff.vff-not-standalone { 42 | background-color: var(--vff-bg-color); 43 | color: var(--vff-main-text-color); 44 | } 45 | 46 | header.vff-header { 47 | background-color: var(--vff-secondary-accent-color); 48 | } 49 | 50 | header.vff-header svg.f-logo { 51 | fill: rgba(0,0,0,0.9); 52 | } 53 | 54 | .vff input[type="text"], 55 | .vff input[type="number"], 56 | .vff input[type="tel"], 57 | .vff input[type="email"], 58 | .vff input[type="url"], 59 | .vff input[type="password"], 60 | .vff input[type="date"], 61 | .vff textarea, 62 | .vff span.faux-form { 63 | border-bottom-color: var(--vff-secondary-text-color); 64 | } 65 | 66 | .vff textarea, 67 | .vff input, 68 | .vff select option { 69 | color: var(--vff-main-text-color); 70 | } 71 | 72 | .vff a, 73 | .vff a:active { 74 | color: var(--vff-main-text-color); 75 | } 76 | 77 | /* placeholder */ 78 | .vff ::-webkit-input-placeholder { /* Chrome/Opera/Safari */ 79 | color: var(--vff-secondary-text-color); 80 | opacity: 1; 81 | font-weight: 300; 82 | } 83 | 84 | .vff ::-moz-placeholder { /* Firefox 19+ */ 85 | color: var(--vff-secondary-text-color); 86 | opacity: 1; 87 | font-weight: 300; 88 | } 89 | 90 | .vff :-ms-input-placeholder { /* IE 10+ */ 91 | color: var(--vff-secondary-text-color)!important; 92 | opacity: 1!important; 93 | font-weight: 300!important; 94 | } 95 | 96 | .vff :-moz-placeholder { /* Firefox 18- */ 97 | color: #999; 98 | opacity: 1; 99 | font-weight: 300; 100 | } 101 | 102 | /* faux input type date placeholder for iOS */ 103 | .vff.vff-is-ios .field-date:not(.f-has-value) .f-answer > span::before { 104 | color: var(--vff-secondary-text-color); 105 | font-weight: 300; 106 | font-size: .72em; 107 | } 108 | 109 | /* field-multiplechoice */ 110 | .vff ul.f-radios li { 111 | border: 1px solid var(--vff-secondary-text-color); 112 | } 113 | 114 | .vff ul.f-radios li.f-selected, 115 | .vff ul.f-radios li:active { 116 | border-color: var(--vff-main-text-color); 117 | background-color: var(--vff-tertiary-text-color); 118 | } 119 | 120 | .vff ul.f-radios li.f-other.f-selected input { 121 | color: inherit; 122 | } 123 | 124 | .vff .f-key{ 125 | color: var(--vff-secondary-text-color); 126 | } 127 | 128 | /* field-dropdown */ 129 | .vff span.f-empty { 130 | color: var(--vff-secondary-text-color); 131 | } 132 | 133 | .vff span.f-answered { 134 | color: var(--vff-main-text-color); 135 | } 136 | 137 | .vff .f-arrow-down svg { 138 | fill: var(--vff-main-text-color); 139 | } 140 | 141 | /* field matrix */ 142 | .vff .f-matrix-table { 143 | border-collapse: separate; 144 | border-spacing: 0 .6em; 145 | } 146 | 147 | .vff .f-matrix-table thead th { 148 | padding-bottom: 0; 149 | } 150 | 151 | .vff .f-matrix-table td { 152 | border: 1px solid var(--vff-secondary-text-color); 153 | border-right:hidden; 154 | border-left: hidden; 155 | } 156 | 157 | .vff .f-matrix-table td:first-child { 158 | border-left: 1px solid var(--vff-secondary-text-color); 159 | } 160 | 161 | .vff .f-matrix-table td:last-child { 162 | border-right: 1px solid var(--vff-secondary-text-color); 163 | } 164 | 165 | .vff .f-matrix-table thead td:first-child { 166 | border: none; 167 | } 168 | 169 | .vff .f-field-svg { 170 | color: var(--vff-secondary-text-color); 171 | border-width: 2px; 172 | } 173 | 174 | .vff .f-field-control:checked ~ .f-field-mask .f-field-svg { 175 | color: var(--vff-main-text-color); 176 | } 177 | 178 | /*matrix scrollbar*/ 179 | .vff .f-matrix-wrap::-webkit-scrollbar { 180 | height: 10px; 181 | } 182 | 183 | .vff .f-matrix-wrap::-webkit-scrollbar-track { 184 | background-color: var(--vff-tertiary-text-color); 185 | } 186 | 187 | .vff .f-matrix-wrap::-webkit-scrollbar-thumb { 188 | background-color: var(--vff-secondary-text-color); 189 | } 190 | 191 | /* buttons */ 192 | .vff .o-btn-action { 193 | color: var(--vff-bg-color); 194 | background-color: var(--vff-main-text-color); 195 | } 196 | 197 | .vff .o-btn-action:hover, 198 | .vff .o-btn-action:focus { 199 | background-color: var(--vff-main-text-color); 200 | opacity: .9; 201 | } 202 | 203 | /* footer */ 204 | .vff-footer .footer-inner-wrap { 205 | background-color: rgba(255,255,255,0.75); 206 | } 207 | 208 | @media screen and (max-width: 479px) { 209 | .vff-footer .footer-inner-wrap{ 210 | background-color: rgba(240,240,240,0.75); 211 | } 212 | } 213 | 214 | .vff-footer .f-prev svg, 215 | .vff-footer .f-next svg { 216 | fill: var(--vff-main-text-color); 217 | } 218 | 219 | .vff-footer .f-progress { 220 | color: var(--vff-secondary-text-color); 221 | } 222 | 223 | .vff-footer .f-progress-bar { 224 | background-color: var(--vff-secondary-text-color); 225 | filter: brightness(1.2); 226 | } 227 | 228 | .vff-footer .f-progress-bar-inner { 229 | background-color: var(--vff-main-text-color); 230 | } 231 | 232 | .vff-footer .f-prev:hover, 233 | .vff-footer .f-next:hover, 234 | .vff-footer .f-prev:focus, 235 | .vff-footer .f-next:focus { 236 | background-color: rgba(0,0,0,0.07); 237 | } 238 | 239 | /* alerts */ 240 | .vff .text-alert, 241 | .vff .f-invalid { 242 | color: #F5554A; 243 | } 244 | 245 | .vff .text-success { 246 | color: #4CAF50; 247 | } 248 | 249 | /* text-muted */ 250 | .vff span.f-tagline, 251 | .vff span.f-sub, 252 | .vff p.f-description, 253 | .vff .text-muted { 254 | color: var(--vff-secondary-text-color); 255 | } 256 | 257 | /* 258 | dark mode styles 259 | */ 260 | 261 | @media (prefers-color-scheme: dark) { 262 | :root { 263 | --vff-bg-color: #313640; 264 | --vff-main-text-color: #fff; 265 | --vff-secondary-text-color: #AEB6BF; 266 | --vff-tertiary-text-color: #41464F; 267 | --vff-main-accent-color: #41B883; 268 | --vff-secondary-accent-color: #A0DBC1; 269 | } 270 | 271 | header.vff-header { 272 | background-color: #313640; 273 | } 274 | 275 | header.vff-header svg.f-logo { 276 | fill: var(--vff-main-accent-color); 277 | } 278 | 279 | .vff textarea, 280 | .vff input, 281 | .vff select option { 282 | color: var(--vff-main-accent-color); 283 | } 284 | 285 | .vff input[type=date]::-webkit-calendar-picker-indicator { 286 | filter: invert(100%); 287 | } 288 | 289 | .vff span.f-answered { 290 | color: var(--vff-main-accent-color); 291 | } 292 | 293 | .vff .f-arrow-down svg { 294 | fill: var(--vff-main-accent-color); 295 | } 296 | 297 | .vff .text-success { 298 | color: var(--vff-main-accent-color); 299 | } 300 | 301 | /* footer */ 302 | .vff-footer .footer-inner-wrap { 303 | background-color: rgba(49,54,64,0.75); 304 | } 305 | 306 | .vff-footer .f-prev svg, 307 | .vff-footer .f-next svg { 308 | fill: var(--vff-main-accent-color); 309 | } 310 | 311 | .vff-footer .f-prev.f-disabled svg, 312 | .vff-footer .f-next.f-disabled svg { 313 | fill: var(--vff-main-text-color); 314 | } 315 | 316 | .vff-footer .f-prev:hover, 317 | .vff-footer .f-next:hover { 318 | background-color: rgba(0,0,0,0.2); 319 | } 320 | 321 | .vff-footer .f-progress-bar { 322 | background-color: var(--vff-secondary-text-color); 323 | filter: brightness(1); 324 | } 325 | 326 | .vff-footer .f-progress-bar-inner { 327 | background-color: var(--vff-main-accent-color); 328 | } 329 | 330 | /* field-multiplechoice */ 331 | .vff ul.f-radios li.f-selected{ 332 | color: var(--vff-main-accent-color); 333 | } 334 | 335 | .vff ul.f-radios li { 336 | font-weight: 900; 337 | color: var(--vff-secondary-text-color); 338 | } 339 | 340 | .vff .f-key { 341 | font-weight: 400; 342 | color: var(--vff-main-accent-color); 343 | } 344 | 345 | /* field matrix */ 346 | .vff .f-matrix-wrap::-webkit-scrollbar-thumb { 347 | background-color: var(--vff-main-accent-color); 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /src/assets/css/themes/theme-purple.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence 3 | https://github.com/ditdot-dev/vue-flow-form 4 | https://www.ditdot.hr/en 5 | */ 6 | 7 | /* 8 | basic theme color and font styles 9 | */ 10 | 11 | :root { 12 | --vff-bg-color: #F9F6FB; 13 | --vff-main-text-color: #181818; 14 | --vff-secondary-text-color: #7f8c8d; 15 | --vff-tertiary-text-color: #f1f1f1; 16 | --vff-main-accent-color: #27ae60; 17 | --vff-secondary-accent-color: #2ecc71; 18 | --vff-tertiary-accent-color: #8e44ad; 19 | --vff-header-color: var(--vff-tertiary-accent-color); 20 | --vff-header-border-color: transparent; 21 | --vff-button-color: var(--vff-main-accent-color); 22 | --vff-button-hover-color: var(--vff-secondary-accent-color); 23 | --vff-button-text-color: #fff; 24 | --vff-main-form-bg-color: #F9F6FB; 25 | --vff-secondary-form-bg-color: #475A6D; 26 | --vff-form-input-color: var(--vff-main-text-color); 27 | --vff-field-key-color: var(--vff-main-accent-color); 28 | --vff-arrow-color: var(--vff-main-text-color); 29 | --vff-arrow-hover-color: rgba(0,0,0,0.07); 30 | --vff-footer-color: rgba(249,246,251,0.75); 31 | --vff-progress-bar-color: var(--vff-main-text-color); 32 | } 33 | 34 | .vff, 35 | .vff textarea, 36 | .vff input, 37 | .vff select, 38 | .vff select option, 39 | .vff kbd, 40 | .vff-header, 41 | .vff-footer { 42 | font-family: 'Poppins', sans-serif; 43 | } 44 | 45 | body { 46 | /* override if needed when using not-standalone option */ 47 | margin: 0; 48 | background-color: var(--vff-bg-color); 49 | } 50 | 51 | .vff { 52 | color: var(--vff-main-text-color); 53 | } 54 | 55 | .vff.vff-not-standalone { 56 | background-color: var(--vff-bg-color); 57 | color: var(--vff-main-text-color); 58 | } 59 | 60 | header.vff-header { 61 | background-color: var(--vff-header-color); 62 | border-bottom: 2px solid var(--vff-header-border-color); 63 | padding-bottom: .6em; 64 | } 65 | 66 | header.vff-header svg.f-logo { 67 | fill: var(--vff-main-text-color); 68 | } 69 | 70 | .vff input[type="text"], 71 | .vff input[type="number"], 72 | .vff input[type="tel"], 73 | .vff input[type="email"], 74 | .vff input[type="url"], 75 | .vff input[type="password"], 76 | .vff input[type="date"], 77 | .vff textarea, 78 | .vff span.faux-form { 79 | border-bottom-color: var(--vff-secondary-text-color); 80 | } 81 | 82 | .vff textarea, 83 | .vff input, 84 | .vff select option { 85 | color: var(--vff-form-input-color); 86 | } 87 | 88 | .vff a, 89 | .vff a:active { 90 | color: var(--vff-main-text-color); 91 | } 92 | 93 | /* placeholder */ 94 | .vff ::-webkit-input-placeholder { /* Chrome/Opera/Safari */ 95 | color: var(--vff-secondary-text-color); 96 | opacity: 1; 97 | font-weight: 300; 98 | } 99 | 100 | .vff ::-moz-placeholder { /* Firefox 19+ */ 101 | color: var(--vff-secondary-text-color); 102 | opacity: 1; 103 | font-weight: 300; 104 | } 105 | 106 | .vff :-ms-input-placeholder { /* IE 10+ */ 107 | color: var(--vff-secondary-text-color)!important; 108 | opacity: 1!important; 109 | font-weight: 300!important; 110 | } 111 | 112 | .vff :-moz-placeholder { /* Firefox 18- */ 113 | color: #999; 114 | opacity: 1; 115 | font-weight: 300; 116 | } 117 | 118 | /* faux input type date placeholder for iOS */ 119 | .vff.vff-is-ios .field-date:not(.f-has-value) .f-answer > span::before{ 120 | color: var(--vff-secondary-text-color); 121 | font-weight: 300; 122 | font-size: .72em; 123 | } 124 | 125 | /* field-multiplechoice */ 126 | .vff ul.f-radios li { 127 | border: 1px solid var(--vff-secondary-form-bg-color); 128 | background-color: var(--vff-main-form-bg-color); 129 | } 130 | 131 | .vff ul.f-radios li.f-selected, 132 | .vff ul.f-radios li:active { 133 | border-color: var(--vff-secondary-form-bg-color); 134 | background-color: var(--vff-secondary-form-bg-color); 135 | color: var(--vff-tertiary-text-color); 136 | } 137 | 138 | .vff ul.f-radios li.f-other.f-selected input { 139 | color: inherit; 140 | } 141 | 142 | .vff .f-key { 143 | color: var(--vff-field-key-color); 144 | } 145 | 146 | /* field-rate */ 147 | .vff .field-iconrate ul.f-radios li.f-selected .f-icon-wrap svg, 148 | .vff .field-iconrate ul.f-radios li.f-previous .f-icon-wrap svg { 149 | stroke: var(--vff-secondary-form-bg-color); 150 | fill: var(--vff-secondary-form-bg-color); 151 | } 152 | 153 | /* field-dropdown */ 154 | .vff span.f-empty { 155 | color: var(--vff-secondary-text-color); 156 | } 157 | 158 | .vff span.f-answered { 159 | color: var(--vff-main-text-color); 160 | } 161 | 162 | .vff .f-arrow-down svg { 163 | fill: var(--vff-arrow-color); 164 | } 165 | 166 | /* field matrix */ 167 | .vff .f-matrix-table { 168 | border-collapse: separate; 169 | border-spacing: 0 .6em; 170 | } 171 | 172 | .vff .f-matrix-table thead th { 173 | padding-bottom: 0; 174 | } 175 | 176 | .vff .f-matrix-table td { 177 | border: 1px solid var(--vff-secondary-form-bg-color); 178 | border-right:hidden; 179 | border-left: hidden; 180 | } 181 | 182 | .vff .f-matrix-table td:first-child { 183 | border-left: 1px solid var(--vff-secondary-form-bg-color); 184 | } 185 | 186 | .vff .f-matrix-table td:last-child { 187 | border-right: 1px solid var(--vff-secondary-form-bg-color); 188 | } 189 | 190 | .vff .f-matrix-table thead td:first-child { 191 | border: none; 192 | } 193 | 194 | .vff .f-field-svg { 195 | color: var(--vff-secondary-text-color); 196 | border-width: 2px; 197 | } 198 | 199 | .vff .f-field-control:checked ~ .f-field-mask .f-field-svg { 200 | color: var(--vff-secondary-form-bg-color); 201 | } 202 | 203 | /* matrix scrollbar */ 204 | .vff .f-matrix-wrap::-webkit-scrollbar { 205 | height: 10px; 206 | } 207 | 208 | .vff .f-matrix-wrap::-webkit-scrollbar-track { 209 | background-color: var(--vff-tertiary-text-color); 210 | } 211 | 212 | .vff .f-matrix-wrap::-webkit-scrollbar-thumb { 213 | background-color: var(--vff-secondary-form-bg-color); 214 | } 215 | 216 | /* buttons */ 217 | .vff .o-btn-action { 218 | color: var(--vff-button-text-color); 219 | background-color: var(--vff-button-color); 220 | } 221 | 222 | .vff .o-btn-action:hover, 223 | .vff .o-btn-action:focus { 224 | background-color: var(--vff-button-hover-color); 225 | opacity: .9; 226 | } 227 | 228 | /* footer */ 229 | .vff-footer .footer-inner-wrap { 230 | background-color: var(--vff-footer-color); 231 | } 232 | 233 | @media screen and (max-width: 479px) { 234 | .vff-footer .footer-inner-wrap{ 235 | background-color: rgba(230,230,230,0.75); 236 | } 237 | } 238 | 239 | .vff-footer .f-prev svg, 240 | .vff-footer .f-next svg { 241 | fill: var(--vff-arrow-color); 242 | } 243 | 244 | .vff-footer .f-progress { 245 | color: var(--vff-secondary-text-color); 246 | } 247 | 248 | .vff-footer .f-progress-bar { 249 | background-color: var(--vff-secondary-text-color); 250 | filter: brightness(1.2); 251 | } 252 | 253 | .vff-footer .f-progress-bar-inner { 254 | background-color: var(--vff-progress-bar-color); 255 | } 256 | 257 | .vff-footer .f-prev:hover, 258 | .vff-footer .f-next:hover { 259 | background-color: var(--vff-arrow-hover-color); 260 | } 261 | 262 | /* alerts */ 263 | .vff .text-alert, 264 | .vff .f-invalid { 265 | color: #F5554A; 266 | } 267 | 268 | .vff .text-success { 269 | color: var(--vff-main-accent-color); 270 | } 271 | 272 | /* text-muted */ 273 | .vff span.f-tagline, 274 | .vff span.f-sub, 275 | .vff p.f-description, 276 | .vff .text-muted { 277 | color: var(--vff-secondary-text-color); 278 | } 279 | 280 | /* 281 | dark mode styles 282 | */ 283 | 284 | @media (prefers-color-scheme: dark) { 285 | :root { 286 | --vff-bg-color: #2c3e50; 287 | --vff-main-text-color: #fff; 288 | --vff-secondary-text-color: #bdc3c7; 289 | --vff-tertiary-text-color: #41464F; 290 | --vff-header-color: #34495e; 291 | --vff-header-border-color: var(--vff-tertiary-accent-color); 292 | --vff-main-form-bg-color: #34495e; 293 | --vff-secondary-form-bg-color: #ecf0f1; 294 | --vff-form-input-color: var(--vff-main-accent-color); 295 | --vff-field-key-color: var(--vff-main-accent-color); 296 | --vff-arrow-color: var(--vff-main-accent-color); 297 | --vff-arrow-hover-color: rgba(0,0,0,0.2); 298 | --vff-footer-color: #2c3e50; 299 | --vff-progress-bar-color: var(--vff-main-accent-color); 300 | } 301 | 302 | .vff span.f-answered { 303 | color: var(--vff-main-accent-color); 304 | } 305 | 306 | .vff ::-webkit-calendar-picker-indicator { 307 | filter: invert(100%); 308 | } 309 | 310 | /* footer */ 311 | .vff-footer .f-prev.f-disabled svg, 312 | .vff-footer .f-next.f-disabled svg { 313 | fill: var(--vff-main-text-color); 314 | } 315 | 316 | .vff-footer .f-progress-bar { 317 | filter: brightness(1); 318 | } 319 | 320 | .vff-footer .footer-inner-wrap { 321 | background-color: rgba(44,62,80,0.8); 322 | } 323 | 324 | /* field-multiplechoice */ 325 | .vff .f-key { 326 | font-weight: 400; 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /src/components/FlowForm.vue: -------------------------------------------------------------------------------- 1 | // Form template and logic 2 | 3 | 127 | 128 | 811 | 812 | 815 | -------------------------------------------------------------------------------- /src/components/FlowFormQuestion.vue: -------------------------------------------------------------------------------- 1 | // Single question template and logic 2 | 3 | 93 | 94 | 369 | -------------------------------------------------------------------------------- /src/components/Question.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/BaseType.vue: -------------------------------------------------------------------------------- 1 | // Used as the basis and extended by other Question Type components 2 | 3 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/DateType.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/DropdownType.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/EmailType.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/FileType.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/IconRateType.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/LongTextType.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 82 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/MatrixType.vue: -------------------------------------------------------------------------------- 1 | 87 | 88 | 170 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/MultipleChoiceType.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 249 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/MultiplePictureChoiceType.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/NumberType.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/OpinionScaleType.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 205 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/PasswordType.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/PhoneType.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/SectionBreakType.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/TextType.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 87 | -------------------------------------------------------------------------------- /src/components/QuestionTypes/UrlType.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // Import vue component 2 | import FlowForm from './components/FlowForm.vue' 3 | import Question from './components/Question.vue' 4 | import QuestionModel, { QuestionType, ChoiceOption, LinkOption, MaskPresets, MatrixRow, MatrixColumn } from './models/QuestionModel' 5 | import LanguageModel from './models/LanguageModel' 6 | 7 | // To allow use as module (npm/webpack/etc.) export component 8 | export { 9 | FlowForm, 10 | Question, 11 | QuestionModel, 12 | QuestionType, 13 | ChoiceOption, 14 | LinkOption, 15 | LanguageModel, 16 | MaskPresets, 17 | MatrixRow, 18 | MatrixColumn 19 | } -------------------------------------------------------------------------------- /src/mixins/ComponentInstance.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence 3 | https://github.com/ditdot-dev/vue-flow-form 4 | https://www.ditdot.hr/en 5 | */ 6 | 7 | const instances = {} 8 | 9 | export const ComponentInstance = { 10 | methods: { 11 | getInstance(id) { 12 | return instances[id] 13 | }, 14 | 15 | setInstance() { 16 | instances[this.id] = this 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/mixins/IsMobile.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence 3 | https://github.com/ditdot-dev/vue-flow-form 4 | https://www.ditdot.hr/en 5 | */ 6 | 7 | let 8 | isIos = false, 9 | isMobile = false 10 | 11 | if (typeof navigator !== 'undefined' && typeof document !== 'undefined') { 12 | // Simple mobile device/tablet detection 13 | isIos = navigator.userAgent.match(/iphone|ipad|ipod/i) || (navigator.userAgent.indexOf('Mac') !== -1 && 'ontouchend' in document) 14 | isMobile = isIos || navigator.userAgent.match(/android/i) 15 | } 16 | 17 | // Mixin that adds `isMobile` and `isIos` data variables 18 | export const IsMobile = { 19 | data() { 20 | return { 21 | isIos, 22 | isMobile 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/models/LanguageModel.js: -------------------------------------------------------------------------------- 1 | /*! 2 | Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence 3 | https://github.com/ditdot-dev/vue-flow-form 4 | https://www.ditdot.hr/en 5 | */ 6 | 7 | // Language data store 8 | 9 | export default class LanguageModel { 10 | constructor(options) { 11 | this.enterKey = 'Enter' 12 | this.shiftKey = 'Shift' 13 | this.ok = 'OK' 14 | this.continue = 'Continue' 15 | this.skip = 'Skip' 16 | this.pressEnter = 'Press :enterKey' 17 | this.multipleChoiceHelpText = 'Choose as many as you like' 18 | this.multipleChoiceHelpTextSingle = 'Choose only one answer' 19 | this.otherPrompt = 'Other' 20 | this.placeholder = 'Type your answer here...' 21 | this.submitText = 'Submit' 22 | this.longTextHelpText = ':shiftKey + :enterKey to make a line break.' 23 | this.prev = 'Prev' 24 | this.next = 'Next' 25 | this.percentCompleted = ':percent% completed' 26 | this.invalidPrompt = 'Please fill out the field correctly' 27 | this.thankYouText = 'Thank you!' 28 | this.successText = 'Your submission has been sent.' 29 | this.ariaOk = 'Press to continue' 30 | this.ariaRequired = 'This step is required' 31 | this.ariaPrev = 'Previous step' 32 | this.ariaNext = 'Next step' 33 | this.ariaSubmitText = 'Press to submit' 34 | this.ariaMultipleChoice = 'Press :letter to select' 35 | this.ariaTypeAnswer = 'Type your answer here' 36 | this.errorAllowedFileTypes = 'Invalid file type. Allowed file types: :fileTypes.' 37 | this.errorMaxFileSize = 'File(s) too large. Maximum allowed file size: :size.' 38 | this.errorMinFiles = 'Too few files added. Minimum allowed files: :min.' 39 | this.errorMaxFiles = 'Too many files added. Maximum allowed files: :max.' 40 | 41 | Object.assign(this, options || {}) 42 | } 43 | 44 | /** 45 | * Inserts a new CSS class into the language model string to format the :string 46 | * Use it in a component's v-html directive: v-html="language.formatString(language.languageString)" 47 | */ 48 | formatString(string, replacements) { 49 | return string.replace(/:(\w+)/g, (match, word) => { 50 | if (this[word]) { 51 | return '' + this[word] + '' 52 | } else if (replacements && replacements[word]) { 53 | return replacements[word] 54 | } 55 | 56 | return match 57 | }) 58 | } 59 | 60 | formatFileSize(bytes) { 61 | const 62 | units = ['B', 'kB', 'MB', 'GB', 'TB'], 63 | i = bytes > 0 ? Math.floor(Math.log(bytes) / Math.log(1024)) : 0 64 | 65 | return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + units[i]; 66 | } 67 | } 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/models/QuestionModel.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence 3 | https://github.com/ditdot-dev/vue-flow-form 4 | https://www.ditdot.hr/en 5 | */ 6 | 7 | // Global data store 8 | 9 | export const QuestionType = Object.freeze({ 10 | Date: 'FlowFormDateType', 11 | Dropdown: 'FlowFormDropdownType', 12 | Email: 'FlowFormEmailType', 13 | File: 'FlowFormFileType', 14 | LongText: 'FlowFormLongTextType', 15 | MultipleChoice: 'FlowFormMultipleChoiceType', 16 | MultiplePictureChoice: 'FlowFormMultiplePictureChoiceType', 17 | Number: 'FlowFormNumberType', 18 | Password: 'FlowFormPasswordType', 19 | Phone: 'FlowFormPhoneType', 20 | SectionBreak: 'FlowFormSectionBreakType', 21 | Text: 'FlowFormTextType', 22 | Url: 'FlowFormUrlType', 23 | Matrix: 'FlowFormMatrixType', 24 | OpinionScale: 'FlowFormOpinionScaleType', 25 | IconRate: 'FlowFormIconRateType', 26 | }) 27 | 28 | export const DropdownOptionBlank = Object.freeze({ 29 | label: '', 30 | value: '', 31 | disabled: true 32 | }) 33 | 34 | export const MaskPresets = Object.freeze({ 35 | Date: '##/##/####', 36 | DateIso: '####-##-##', 37 | PhoneUs: '(###) ###-####' 38 | }) 39 | 40 | export class ChoiceOption { 41 | constructor(options) { 42 | this.label = '' 43 | this.value = null 44 | this.selected = false 45 | this.imageSrc = null 46 | this.imageAlt = null 47 | 48 | Object.assign(this, options) 49 | } 50 | 51 | choiceLabel() { 52 | return this.label || this.value 53 | } 54 | 55 | choiceValue() { 56 | // Returns the value if it's anything other than the default (null). 57 | if (this.value !== null) { 58 | return this.value 59 | } 60 | 61 | // Returns any other non-empty property if the value has not been set. 62 | return this.label || this.imageAlt || this.imageSrc 63 | } 64 | 65 | toggle() { 66 | this.selected = !this.selected 67 | } 68 | } 69 | 70 | export class LinkOption { 71 | constructor(options) { 72 | this.url = '' 73 | this.text = '' 74 | this.target = '_blank' 75 | 76 | Object.assign(this, options) 77 | } 78 | } 79 | 80 | export class MatrixColumn { 81 | constructor(options) { 82 | this.value = '' 83 | this.label = '' 84 | 85 | Object.assign(this, options) 86 | } 87 | } 88 | 89 | export class MatrixRow { 90 | constructor(options) { 91 | this.id = '' 92 | this.label = '' 93 | 94 | Object.assign(this, options) 95 | } 96 | } 97 | 98 | export default class QuestionModel { 99 | constructor(options) { 100 | // Make sure the options variable is an object 101 | options = options || {} 102 | 103 | this.id = null 104 | this.answer = null 105 | this.answered = false 106 | this.index = 0 107 | this.options = [] 108 | this.description = '' 109 | this.className = '' 110 | this.type = null 111 | this.html = null 112 | this.required = false 113 | this.jump = null 114 | this.placeholder = null 115 | this.mask = '' 116 | this.multiple = false 117 | this.allowOther = false 118 | this.other = null 119 | this.language = null 120 | this.tagline = null 121 | this.title = null 122 | this.subtitle = null 123 | this.content = null 124 | this.inline = false 125 | this.helpText = null 126 | this.helpTextShow = true 127 | this.descriptionLink = [] 128 | this.min = null 129 | this.max = null 130 | this.maxLength = null 131 | this.nextStepOnAnswer = false 132 | this.accept = null 133 | this.maxSize = null 134 | this.rows = [] 135 | this.columns = [] 136 | this.labelLeft = null 137 | this.labelRight = null 138 | 139 | Object.assign(this, options) 140 | 141 | // Sets default mask and placeholder value on PhoneType question 142 | if (this.type === QuestionType.Phone) { 143 | if (!this.mask) { 144 | this.mask = MaskPresets.Phone 145 | } 146 | if (!this.placeholder) { 147 | this.placeholder = this.mask 148 | } 149 | } 150 | 151 | if (this.type === QuestionType.Url) { 152 | this.mask = null 153 | } 154 | 155 | if (this.type === QuestionType.Date && !this.placeholder) { 156 | this.placeholder = 'yyyy-mm-dd' 157 | } 158 | 159 | if (this.type !== QuestionType.Matrix && this.multiple && !Array.isArray(this.answer)) { 160 | this.answer = this.answer ? [this.answer] : [] 161 | } 162 | 163 | // Check if we have an answer already (when we have a pre-filled form) 164 | // and set the answered property accordingly 165 | if (!this.required && typeof options.answer !== 'undefined') { 166 | this.answered = true 167 | } else if (this.answer && (!this.multiple || this.answer.length)) { 168 | this.answered = true 169 | } 170 | 171 | this.resetOptions() 172 | } 173 | 174 | getJumpId() { 175 | let nextId = null 176 | 177 | if (typeof this.jump === 'function') { 178 | nextId = this.jump.call(this) 179 | } else if (this.jump[this.answer]) { 180 | nextId = this.jump[this.answer] 181 | } else if (this.jump['_other']) { 182 | nextId = this.jump['_other'] 183 | } 184 | 185 | return nextId 186 | } 187 | 188 | setAnswer(answer) { 189 | if (this.type === QuestionType.Number && answer !== '' && !isNaN(+answer)) { 190 | answer = +answer 191 | } 192 | 193 | this.answer = answer 194 | } 195 | 196 | setIndex(index) { 197 | if (!this.id) { 198 | this.id = 'q_' + index 199 | } 200 | 201 | this.index = index 202 | } 203 | 204 | resetOptions() { 205 | if (this.options) { 206 | const isArray = Array.isArray(this.answer) 207 | let numSelected = 0 208 | 209 | this.options.forEach(o => { 210 | const optionValue = o.choiceValue() 211 | 212 | if (this.answer === optionValue || (isArray && this.answer.indexOf(optionValue) !== -1)) { 213 | o.selected = true 214 | ++numSelected 215 | } else { 216 | o.selected = false 217 | } 218 | }) 219 | 220 | if (this.allowOther) { 221 | let otherAnswer = null 222 | 223 | if (isArray) { 224 | if (this.answer.length && this.answer.length !== numSelected) { 225 | otherAnswer = this.answer[this.answer.length - 1] 226 | } 227 | } else if (this.options.map(o => o.choiceValue()).indexOf(this.answer) === -1) { 228 | otherAnswer = this.answer 229 | } 230 | 231 | if (otherAnswer !== null) { 232 | this.other = otherAnswer 233 | } 234 | } 235 | } 236 | } 237 | 238 | resetAnswer() { 239 | this.answered = false 240 | this.answer = this.multiple ? [] : null 241 | this.other = null 242 | 243 | this.resetOptions() 244 | } 245 | 246 | isMultipleChoiceType() { 247 | return [QuestionType.MultipleChoice, QuestionType.MultiplePictureChoice].includes(this.type) 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | let entry = process.argv.pop() 2 | if (entry && entry.indexOf('.js') === -1) { 3 | entry = null 4 | } 5 | 6 | module.exports = { 7 | publicPath: '', 8 | pages: { 9 | index: { 10 | // Replace with your .js entry file path. 11 | // To see the quiz example, use 'examples/quiz/main.js' 12 | // To see the support page example, use 'examples/support-page/main.js' 13 | entry: entry || 'examples/questionnaire/main.js', 14 | template: 'public/index.html', 15 | filename: 'index.html' 16 | } 17 | } 18 | } --------------------------------------------------------------------------------