├── .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 |
7 |
8 |
9 |
10 |
11 |
12 |
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 |
100 |
101 |
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 |
4 |
5 |
16 |
17 |
25 |
26 |
27 |
28 |
29 |
30 | Thank you. 🙏
31 |
32 | Great work, the survey is completed, and our demo is done. You can review your answers or press submit.
33 |
34 |
35 |
Note: No data will be saved and/or sent in this demo.
36 |
37 |
38 |
39 |
40 |
41 |
42 |
50 | {{ language.submitText }}
51 |
52 |
56 |
57 |
58 |
59 | Submitted succesfully.
60 |
61 |
62 |
63 |
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 |
4 |
5 |
17 |
18 |
29 |
30 |
31 |
32 |
33 | You did it!
34 |
35 | Review your answers or press Calculate score to see your result.
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
50 | Calculate score
51 |
52 |
57 |
58 |
59 | Your time: {{ formattedTime }}
60 | "You scored {{ score }} out of {{ total }}. There's a lot of room for improvement."
61 | "You scored {{ score }} out of {{ total }}. Not bad at all!"
62 | "You scored {{ score }} out of {{ total }}. Wow, that's impressive!"
63 |
64 |
65 |
66 |
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 |
4 |
5 |
6 |
13 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
Submit issue > Step 3/3
26 |
27 | Please wait, submitting...
28 |
29 |
30 |
Your ticket number is: {{ getTicket() }}
31 |
Thank You 😊. Our support team will contact you as soon as possible.
32 |
33 |
34 |
35 |
Support page > Ticket status
36 |
37 | Please wait, checking...
38 |
39 |
40 |
Good news - the wheels are turning, your ticket No. {{ formatTicket(questions[2].model) }} is being processed!😉
41 |
Have a great day!
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
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 |
4 |
126 |
127 |
128 |
811 |
812 |
815 |
--------------------------------------------------------------------------------
/src/components/FlowFormQuestion.vue:
--------------------------------------------------------------------------------
1 | // Single question template and logic
2 |
3 |
4 |
92 |
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 |
2 |
3 |
12 |
13 |
14 | {{ option.choiceLabel() }}
15 |
16 |
17 |
18 | {{ answerLabel }}
19 |
20 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/components/QuestionTypes/EmailType.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/QuestionTypes/FileType.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/components/QuestionTypes/IconRateType.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/QuestionTypes/LongTextType.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
20 |
21 |
82 |
--------------------------------------------------------------------------------
/src/components/QuestionTypes/MatrixType.vue:
--------------------------------------------------------------------------------
1 |
2 |
86 |
87 |
88 |
170 |
--------------------------------------------------------------------------------
/src/components/QuestionTypes/MultipleChoiceType.vue:
--------------------------------------------------------------------------------
1 |
2 |
49 |
50 |
51 |
249 |
--------------------------------------------------------------------------------
/src/components/QuestionTypes/MultiplePictureChoiceType.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/QuestionTypes/NumberType.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/QuestionTypes/OpinionScaleType.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
27 |
28 |
29 | 1 -
30 | {{ question.labelLeft }}
31 |
32 |
33 | {{ question.options.length }} -
34 | {{ question.labelRight }}
35 |
36 |
37 |
38 |
39 |
40 |
205 |
--------------------------------------------------------------------------------
/src/components/QuestionTypes/PasswordType.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/QuestionTypes/PhoneType.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/QuestionTypes/SectionBreakType.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ question.content }}
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/components/QuestionTypes/TextType.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
22 |
40 |
41 |
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 | }
--------------------------------------------------------------------------------