├── .DS_Store
├── .babelrc
├── .commitlintrc.json
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .github
├── CODE_OF_CONDUCT.md
├── COMMIT_CONVENTION.md
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .npmignore
├── .nvmrc
├── .storybook
├── config.js
└── webpack.config.js
├── LICENSE.md
├── README.md
├── docs
├── preview-container-object.png
├── preview-container.png
└── preview-simple.png
├── jest.config.js
├── jest.setup.js
├── package-lock.json
├── package.json
├── rollup.config.js
├── src
├── __tests__
│ └── index.js
├── components
│ ├── __tests__
│ │ ├── __snapshots__
│ │ │ └── field-array.js.snap
│ │ └── field-array.js
│ └── field-array.vue
├── index.js
└── index.umd.js
└── stories
├── Container.vue
├── _index.stories.js
├── bootstrap-accordion-container.stories.js
├── bootstrap-accordion-container.vue
├── remove-button.stories.js
├── show-empty-component-at-bottom.stories.js
└── simple-array-with-container.stories.js
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwenaelp/vfg-field-array/24449cc133d721388e6813f2dc5e0ea4af78ff3a/.DS_Store
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["vue-app", {
4 | "modules": false
5 | }]
6 | ],
7 | "plugins": [
8 | ["module-resolver", {
9 | "extensions": [".js", ".vue", ".json"]
10 | }]
11 | ],
12 | "env": {
13 | "test": {
14 | "plugins": ["dynamic-import-node"]
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/.commitlintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "@commitlint/config-conventional"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 2
7 | indent_style = space
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | .github/
2 | _book/
3 | docs/
4 | coverage/
5 | dist/
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // https://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | root: true,
5 | parserOptions: {
6 | parser: 'babel-eslint',
7 | ecmaVersion: 2017,
8 | sourceType: 'module'
9 | },
10 | plugins: ['html', 'vue'],
11 | extends: [
12 | 'eslint:recommended',
13 | 'plugin:prettier/recommended',
14 | 'plugin:vue/recommended',
15 | 'plugin:import/errors',
16 | 'plugin:import/warnings'
17 | ],
18 | env: {
19 | browser: true,
20 | node: true,
21 | commonjs: true,
22 | es6: true,
23 | jest: true
24 | },
25 | rules: {
26 | // allow async-await
27 | 'generator-star-spacing': 'off',
28 | // don't require .vue extension when importing
29 | 'import/extensions': ['error', 'always', {
30 | 'js': 'never',
31 | 'vue': 'never'
32 | }],
33 | // disallow reassignment of function parameters
34 | // disallow parameter object manipulation except for specific exclusions
35 | 'no-param-reassign': ['error', {
36 | props: true,
37 | ignorePropertyModificationsFor: [
38 | 'state', // for vuex state
39 | 'acc', // for reduce accumulators
40 | 'e' // for e.returnvalue
41 | ]
42 | }],
43 | // allow optionalDependencies
44 | 'import/no-extraneous-dependencies': ['error', {
45 | optionalDependencies: ['test/index.js']
46 | }],
47 | // allow debugger during development
48 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
49 | },
50 | "settings": {
51 | // resolve using plugin babel module resolver
52 | "import/resolver": {
53 | "babel-module": {}
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at accounts@gwenp.fr. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
--------------------------------------------------------------------------------
/.github/COMMIT_CONVENTION.md:
--------------------------------------------------------------------------------
1 | ## Git Commit Message Convention
2 |
3 | > This is adapted from [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/blob/master/packages/conventional-changelog-angular/convention.md).
4 |
5 | #### Examples
6 |
7 | Appears under "Features" header, `compiler` subheader:
8 |
9 | ```
10 | feat(compiler): add 'comments' option
11 | ```
12 |
13 | Appears under "Bug Fixes" header, `v-model` subheader, with a link to issue #28:
14 |
15 | ```
16 | fix(v-model): handle events on blur
17 |
18 | close #28
19 | ```
20 |
21 | Appears under "Performance Improvements" header, and under "Breaking Changes" with the breaking change explanation:
22 |
23 | ```
24 | perf(core): improve vdom diffing by removing 'foo' option
25 |
26 | BREAKING CHANGE: The 'foo' option has been removed.
27 | ```
28 |
29 | The following commit and commit `667ecc1` do not appear in the changelog if they are under the same release. If not, the revert commit appears under the "Reverts" header.
30 |
31 | ```
32 | revert: feat(compiler): add 'comments' option
33 |
34 | This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
35 | ```
36 |
37 | ### Full Message Format
38 |
39 | A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
40 |
41 | ```
42 | ():
43 |
44 |
45 |
46 |
47 | ```
48 |
49 | The **header** is mandatory and the **scope** of the header is optional.
50 |
51 | ### Revert
52 |
53 | If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit .`, where the hash is the SHA of the commit being reverted.
54 |
55 | ### Type
56 |
57 | If the prefix is `feat`, `fix` or `perf`, it will appear in the changelog. However if there is any [BREAKING CHANGE](#footer), the commit will always appear in the changelog.
58 |
59 | Other prefixes are up to your discretion. Suggested prefixes are `docs`, `chore`, `style`, `refactor`, and `test` for non-changelog related tasks.
60 |
61 | ### Scope
62 |
63 | The scope could be anything specifying place of the commit change. For example `core`, `compiler`, `ssr`, `v-model`, `transition` etc...
64 |
65 | ### Subject
66 |
67 | The subject contains succinct description of the change:
68 |
69 | * use the imperative, present tense: "change" not "changed" nor "changes"
70 | * don't capitalize first letter
71 | * no dot (.) at the end
72 |
73 | ### Body
74 |
75 | Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
76 | The body should include the motivation for the change and contrast this with previous behavior.
77 |
78 | ### Footer
79 |
80 | The footer should contain any information about **Breaking Changes** and is also the place to
81 | reference GitHub issues that this commit **Closes**.
82 |
83 | **Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Usage
4 |
5 | ```bash
6 | # Install dependencies
7 | npm install
8 |
9 | # Build
10 | npm run build
11 |
12 | # Run development server with storybook
13 | npm run storybook
14 |
15 | # Run all tests with lint/jest
16 | npm run test
17 |
18 | # Run test in watch mode
19 | npm run test:watch
20 |
21 | # Update test snapshot
22 | npm run test:update
23 |
24 | # Run linter
25 | npm run lint
26 |
27 | # Run linter with auto fix
28 | npm run lint:fix
29 |
30 | # Commit files with commitizen (use this instead of git commit)
31 | npm run cz
32 | ```
33 | ## Workflow
34 |
35 | - Create a component in the src/components folder
36 | - Add tests in the src/components/\_\_tests\_\_ folder
37 | - Register this component in src/index.js
38 | - Write stories which use your component as a template in src/stories/index.stories.js
39 | - Run `npm run storybook` to author your components by having a development environment
40 | - Run lint and tests before commiting anything
41 | - Commit using [Commit Convention](.github/COMMIT_CONVENTION.md) and push to github
42 | - If deployment is setup correctly(see next section), your components will be available on npm and release on github :)
43 |
44 | ## CI, Deployment & Releases
45 |
46 | This repository is intended to be used with travisCI for deployment. [Semantic-release](https://github.com/semantic-release/semantic-release) is used and setup to auto-generate changelog, auto-publish to npm and auto-release to github based on commit messages structure. For it to work properly you have to follow this [Commit Convention](.github/COMMIT_CONVENTION.md).
47 |
48 | You may need to install [Semantic-release-cli](https://github.com/semantic-release/cli) to enable and pre-configure travisCI with npm and github tokens.
49 |
50 | So to enjoy an automated continuous deployment, it is required to have a NPM account, to have this project hosted on github, and to have previously login to travisCI.
51 |
52 | Warning: After setting up semantic-release, your library will be publish automatically to npm and release to github. If you are not ready to publish to npm or to release you can do this later.
53 |
54 | ```bash
55 | # Activate travisCI for your repository and launch semantic-release-cli
56 | cd yourProjectFolder
57 | npm install -g semantic-release-cli && semantic-release-cli setup
58 |
59 | # Fill your github and npm credentials, select travisCI and select to replace .travis.yml
60 | # TravisCI will be automatically enabled for your github repository
61 | # And tokens will be injected in your travisCI repository config as environment variables
62 | ```
63 |
64 | If everything is setup properly, every commit on master will automatically generate a release if needed. For the win.
65 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwenaelp/vfg-field-array/24449cc133d721388e6813f2dc5e0ea4af78ff3a/.github/ISSUE_TEMPLATE.md
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 | **What kind of change does this PR introduce?** (check at least one)
10 |
11 | - [ ] Bugfix
12 | - [ ] Feature
13 | - [ ] Code style update
14 | - [ ] Refactor
15 | - [ ] Build-related changes
16 | - [ ] Other, please describe:
17 |
18 | **Does this PR introduce a breaking change?** (check one)
19 |
20 | - [ ] Yes
21 | - [ ] No
22 |
23 | If yes, please describe the impact and migration path for existing applications:
24 |
25 | **The PR fulfills these requirements:**
26 |
27 | - [ ] It's submitted to the `develop` branch (or to a previous version branch), _not_ the `master` branch
28 | - [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
29 | - [ ] All tests are passing
30 | - [ ] New/updated tests are included
31 |
32 | If adding a **new feature**, the PR's description includes:
33 | - [ ] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)
34 |
35 | **Other information:**
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/node,visualstudiocode
2 |
3 | ### Node ###
4 | # Logs
5 | logs
6 | *.log
7 | npm-debug.log*
8 | yarn-debug.log*
9 | yarn-error.log*
10 |
11 | # Runtime data
12 | pids
13 | *.pid
14 | *.seed
15 | *.pid.lock
16 |
17 | # Directory for instrumented libs generated by jscoverage/JSCover
18 | lib-cov
19 |
20 | # Coverage directory used by tools like istanbul
21 | coverage
22 |
23 | # nyc test coverage
24 | .nyc_output
25 |
26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
27 | .grunt
28 |
29 | # Bower dependency directory (https://bower.io/)
30 | bower_components
31 |
32 | # node-waf configuration
33 | .lock-wscript
34 |
35 | # Compiled binary addons (http://nodejs.org/api/addons.html)
36 | build/Release
37 |
38 | # Dependency directories
39 | node_modules/
40 | jspm_packages/
41 |
42 | # Typescript v1 declaration files
43 | typings/
44 |
45 | # Optional npm cache directory
46 | .npm
47 |
48 | # Optional eslint cache
49 | .eslintcache
50 |
51 | # Optional REPL history
52 | .node_repl_history
53 |
54 | # Output of 'npm pack'
55 | *.tgz
56 |
57 | # Yarn Integrity file
58 | .yarn-integrity
59 |
60 | # dotenv environment variables file
61 | .env
62 |
63 | # Generated code
64 | dist/
65 |
66 | ### VisualStudioCode ###
67 | .vscode/*
68 | !.vscode/settings.json
69 | !.vscode/tasks.json
70 | !.vscode/launch.json
71 | !.vscode/extensions.json
72 |
73 | # End of https://www.gitignore.io/api/node,visualstudiocode
74 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .babelrc
2 | .commitlintrc
3 | .editorconfig
4 | .esdoc.json
5 | .travis.yml
6 | yarn.lock
7 | .github/
8 | .storybook/
9 | coverage/
10 | node_modules/
11 | src/
12 | stories/
13 | jest.*.js
14 | rollup.config.js
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 8.9.0
--------------------------------------------------------------------------------
/.storybook/config.js:
--------------------------------------------------------------------------------
1 | import { configure } from '@storybook/vue';
2 | import Vue from 'vue';
3 |
4 | // Import your custom components.
5 | import ModuleLibrary from '@/index';
6 |
7 | import VueFormGenerator from "vue-form-generator";
8 | import "vue-form-generator/dist/vfg.css";
9 |
10 | // Install this library
11 | Vue.use(ModuleLibrary);
12 |
13 | Vue.use(VueFormGenerator);
14 |
15 | // Install Vue plugins
16 | // ex: Vue.use(vuex)
17 |
18 | // Load stories
19 | const req = require.context("../stories", true, /\.stories\.js$/);
20 | function loadStories() {
21 | req.keys().forEach(filename => req(filename));
22 | }
23 | configure(loadStories, module);
24 |
--------------------------------------------------------------------------------
/.storybook/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | // load the default config generator.
4 | const genDefaultConfig = require('@storybook/vue/dist/server/config/defaults/webpack.config.js');
5 |
6 | module.exports = (baseConfig, env) => {
7 | const config = genDefaultConfig(baseConfig, env);
8 |
9 | // Add js, json and vue extension support
10 | config.resolve.extensions.push('.js', '.vue', '.json');
11 |
12 | // Add alias for @ pointing to src
13 | config.resolve.alias['@'] = path.resolve('src')
14 |
15 | return config;
16 | };
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright 2017 Gwenael Pluchon
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vfg-field-array
2 |
3 | 
4 | 
5 | 
6 | 
7 | 
8 | [](https://github.com/semantic-release/semantic-release)
9 | 
10 | [](https://travis-ci.org/gwenaelp/vfg-field-array)
11 |
12 | > A vue-form-generator field to handle arrays
13 |
14 | > Generated using [vue-cli-template-library](https://github.com/julon/vue-cli-template-library).
15 |
16 | ## Examples
17 |
18 | ### Simple array
19 |
20 | 
21 |
22 | ```javascript
23 | {
24 | model: {
25 | array: ["item1", "item2", "item3"]
26 | },
27 | schema: {
28 | fields: [
29 | {
30 | type: "array",
31 | label: "Array field",
32 | model: "array",
33 | showRemoveButton: true
34 | }
35 | ]
36 | }
37 | }
38 | ```
39 |
40 | ### With container component
41 |
42 | 
43 |
44 | ```javascript
45 | {
46 | model: {
47 | array: ["item1", "item2", "item3"]
48 | },
49 | schema: {
50 | fields: [
51 | {
52 | type: "array",
53 | label: "Array field",
54 | model: "array",
55 | itemContainerComponent: "Container"
56 | }
57 | ]
58 | }
59 | }
60 | ```
61 |
62 | #### Container.vue
63 |
64 | ```html
65 |
66 |
81 |
82 |
83 |
93 |
98 | ```
99 |
100 | ### With container component and object as array item
101 |
102 | 
103 |
104 | ```javascript
105 | {
106 | model: {
107 | columns: [{
108 | "label": "Name",
109 | "field": "full_name",
110 | "template": ""
111 | }, {
112 | "label": "URL",
113 | "field": "html_url",
114 | "template": ""
115 | }, {
116 | "label": "Date",
117 | "field": "date",
118 | "template": ""
119 | }],
120 | },
121 | schema: {
122 | type: 'array',
123 | label: 'Columns',
124 | model: 'columns',
125 | itemContainerComponent: 'WidgetListColumnEditorContainer',
126 | showRemoveButton: false,
127 | fieldClasses: 'arrayEditor',
128 | newElementButtonLabelClasses: "button is-primary",
129 | items: {
130 | type: 'object',
131 | default: {},
132 | schema: {
133 | fields: [{
134 | type: 'input',
135 | inputType: 'text',
136 | label: 'Label',
137 | model: 'label',
138 | },{
139 | type: 'input',
140 | inputType: 'text',
141 | label: 'Field',
142 | model: 'field',
143 | },{
144 | type: 'sourcecode',
145 | label: 'Template',
146 | model: 'template',
147 | }]
148 | }
149 | }
150 | }
151 | }
152 | ```
153 |
154 | ### With bootstrap 4 container component, object as array item, validation and inputName
155 |
156 | ```javascript
157 | model: {
158 | columns: {}
159 | },
160 | schema: {
161 | fields:[
162 | {
163 | type: "array",
164 | label: 'Columns',
165 | model: 'columns',
166 | inputName: "columns",
167 | showRemoveButton: false,
168 | newElementButtonLabelClasses: "btn btn-outline-dark mt-2",
169 | itemContainerComponent: "field-array-bootstrap-accordion-item",
170 | newElementButtonLabel: "+ Add Column",
171 | itemContainerHeader: function(model, schema, index) {
172 | return "Column " + (index + 1) + (model && model.label ? " (" + model.label + ")": "");
173 | },
174 | items:{
175 | type: 'object',
176 | schema: {
177 | fields: [
178 | {
179 | type: "input",
180 | inputType: "text",
181 | label: "Name",
182 | model: "name",
183 | inputName: "name",
184 | required: true,
185 | validator: "string",
186 | },
187 | {
188 | type: "input",
189 | inputType: "text",
190 | label: "Description",
191 | model: "description",
192 | inputName: "description",
193 | validator: "string"
194 | },
195 | {
196 | type: "select",
197 | label: "Field Type",
198 | model: "type",
199 | inputName: "type",
200 | required: true,
201 | validator: "string",
202 | values: [
203 | {id: "string", name: "Text Field"},
204 | {id: "text", name: "Text Area"},
205 | {id: "number", name: "Number"},
206 | {id: "date", name: "Date"},
207 | {id: "select", name: "Single selection"},
208 | {id: "multiselect", name: "Multiple Selection"},
209 | {id: "boolean", name: "True/False"}
210 | ]
211 | },
212 | {
213 | type: "array",
214 | label: "Values",
215 | model: "values",
216 | inputName: "values",
217 | validator: "array",
218 | showRemoveButton: true,
219 | showModeElementUpButton: true,
220 | showModeElementDownButton: true,
221 | itemFieldClasses: "form-control",
222 | itemContainerClasses: "input-group pb-2",
223 | newElementButtonLabelClasses: "btn btn-outline-dark",
224 | removeElementButtonClasses: "btn btn-danger input-group-append",
225 | moveElementUpButtonClasses: "btn btn-outline-dark input-group-append",
226 | moveElementDownButtonClasses: "btn btn-outline-dark input-group-append",
227 | newElementButtonLabel: "+ Add Value",
228 | visible: function(model) {
229 | return model && (model.type === "select" || model.type === "multiselect");
230 | },
231 | required: function(model) {
232 | return model && (model.type === "select" || model.type === "multiselect");
233 | }
234 | },
235 | {
236 | type: "input",
237 | inputType: "number",
238 | label: "Rows (optional)",
239 | model: "rows",
240 | inputName: "rows",
241 | validator: "integer",
242 | visible: function(model) {
243 | return model && model.type === "text";
244 | }
245 | }
246 | ]
247 | }
248 | }
249 | }
250 | ]
251 |
252 | },
253 | formOptions: {
254 | validateAfterChanged: true
255 | }
256 | ```
257 |
258 | ## Installation
259 |
260 | ```shell
261 | npm install vfg-field-array
262 | ```
263 |
264 | `vfg-field-array` can be used as a module in both CommonJS and ES modular environments.
265 |
266 | When in non-modular environment, vfg-field-array will register all the components to vue by itself.
267 |
268 | ### ES6
269 |
270 | ```javascript
271 | //
272 | // You can register a component manually
273 | //
274 | import { FieldArray } from 'vfg-field-array';
275 |
276 | export default {
277 | ...
278 | components: {
279 | FieldArray
280 | },
281 | ...
282 | };
283 |
284 | //
285 | // or register the whole module with vue
286 | //
287 | import ModuleLibrary from 'vfg-field-array';
288 |
289 | // Install this library
290 | Vue.use(ModuleLibrary);
291 | ```
292 |
293 | ### CommonJS
294 |
295 | ```javascript
296 | //
297 | // You can register a component manually
298 | //
299 | var Vue = require('vue');
300 | var ModuleLibrary = require('vfg-field-array');
301 |
302 | var YourComponent = Vue.extend({
303 | ...
304 | components: {
305 | 'field-array': ModuleLibrary.FieldArray
306 | },
307 | ...
308 | });
309 |
310 | //
311 | // or register the whole module with vue
312 | //
313 | var Vue = require('vue');
314 | var ModuleLibrary = require('vfg-field-array');
315 |
316 | // Install this library
317 | Vue.use(ModuleLibrary);
318 | ```
319 |
320 | ### Browser
321 |
322 | ```html
323 |
324 |
325 |
326 | ```
327 |
328 | ### After that, you can use it with Vue Form Generator:
329 |
330 | ```javascript
331 | schema: {
332 | fields: [
333 | {
334 | type: "array",
335 | label: "My array",
336 | model: "myArray"
337 | }
338 | ]
339 | }
340 | ```
341 |
342 |
343 | ## Changelog
344 |
345 | See the GitHub [release history](https://github.com/gwenaelp/vfg-field-array/releases).
346 |
347 | ## Contributing
348 |
349 | See [CONTRIBUTING.md](.github/CONTRIBUTING.md).
350 |
--------------------------------------------------------------------------------
/docs/preview-container-object.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwenaelp/vfg-field-array/24449cc133d721388e6813f2dc5e0ea4af78ff3a/docs/preview-container-object.png
--------------------------------------------------------------------------------
/docs/preview-container.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwenaelp/vfg-field-array/24449cc133d721388e6813f2dc5e0ea4af78ff3a/docs/preview-container.png
--------------------------------------------------------------------------------
/docs/preview-simple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwenaelp/vfg-field-array/24449cc133d721388e6813f2dc5e0ea4af78ff3a/docs/preview-simple.png
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | rootDir: path.resolve(__dirname, "./"),
5 | moduleFileExtensions: ["js", "json", "vue"],
6 | transform: {
7 | ".*\\.js$": "/node_modules/babel-jest",
8 | ".*\\.vue$": "/node_modules/vue-jest"
9 | },
10 | snapshotSerializers: ["/node_modules/jest-serializer-vue"],
11 | setupFiles: ["/jest.setup"],
12 | mapCoverage: true,
13 | coverageDirectory: "/coverage",
14 | collectCoverageFrom: [
15 | "src/**/*.{js,vue}",
16 | "!src/index.umd.js",
17 | "!**/node_modules/**"
18 | ]
19 | };
20 |
--------------------------------------------------------------------------------
/jest.setup.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 |
3 | Vue.config.productionTip = false;
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vfg-field-array",
3 | "description": "A vue-form-generator field to handle arrays",
4 | "version": "0.0.7",
5 | "license": "MIT",
6 | "main": "dist/vfg-field-array.common.js",
7 | "module": "dist/vfg-field-array.esm.js",
8 | "unpkg": "dist/vfg-field-array.js",
9 | "jsdelivr": "dist/vfg-field-array.js",
10 | "files": [
11 | "src",
12 | "dist/*.js"
13 | ],
14 | "author": "Gwenael Pluchon",
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/gwenaelp/vfg-field-array.git"
18 | },
19 | "bugs": {
20 | "url": "https://github.com/gwenaelp/vfg-field-array/issues"
21 | },
22 | "homepage": "https://github.com/gwenaelp/vfg-field-array#readme",
23 | "keywords": [
24 | "vue",
25 | "vue-component",
26 | "vue-library"
27 | ],
28 | "engines": {
29 | "node": ">=8.9.0"
30 | },
31 | "scripts": {
32 | "build": "npm run build:cjs && npm run build:es && npm run build:umd:dev && npm run build:umd:prod",
33 | "build:cjs": "rollup -c --environment TARGET:cjs",
34 | "build:es": "rollup -c --environment TARGET:esm",
35 | "build:umd:dev": "rollup -c --environment TARGET:umd-dev",
36 | "build:umd:prod": "rollup -c --environment TARGET:umd-prod",
37 | "storybook": "start-storybook -p 9001 -c .storybook",
38 | "storybook:build": "build-storybook -c .storybook -o docs/dist/stories",
39 | "precommit": "lint-staged",
40 | "cz": "git-cz",
41 | "commitmsg": "commitlint -e $GIT_PARAMS",
42 | "test": "npm run lint && jest --verbose --coverage",
43 | "test:update": "jest --verbose --updateSnapshot",
44 | "test:watch": "jest --verbose --watchAll --notify",
45 | "lint": "eslint --ext .js,.vue .",
46 | "lint:fix": "eslint --ext .js,.vue . --fix",
47 | "prepublishOnly": "npm run build",
48 | "semantic-release": "semantic-release"
49 | },
50 | "devDependencies": {
51 | "@commitlint/cli": "^6.0.2",
52 | "@commitlint/config-conventional": "^5.2.3",
53 | "@semantic-release/changelog": "^1.0.0",
54 | "@semantic-release/git": "^2.0.1",
55 | "@semantic-release/github": "^3.0.1",
56 | "@semantic-release/npm": "^2.6.1",
57 | "@storybook/vue": "^3.3.3",
58 | "babel-core": "^6.26.0",
59 | "babel-eslint": "^8.1.2",
60 | "babel-jest": "^22.0.4",
61 | "babel-plugin-dynamic-import-node": "^1.2.0",
62 | "babel-plugin-module-resolver": "^3.0.0",
63 | "babel-preset-vue-app": "^2.0.0",
64 | "commitizen": "^2.9.6",
65 | "cz-conventional-changelog": "^2.1.0",
66 | "eslint": "^4.14.0",
67 | "eslint-config-prettier": "^2.9.0",
68 | "eslint-import-resolver-babel-module": "^4.0.0",
69 | "eslint-plugin-html": "^4.0.1",
70 | "eslint-plugin-import": "^2.8.0",
71 | "eslint-plugin-jest": "^21.5.0",
72 | "eslint-plugin-prettier": "2.4.0",
73 | "eslint-plugin-vue": "^4.0.1",
74 | "husky": "^0.14.3",
75 | "jest": "^22.0.4",
76 | "jest-serializer-html": "^5.0.0",
77 | "jest-serializer-vue": "^0.3.0",
78 | "jest-vue-preprocessor": "^1.3.1",
79 | "lint-staged": "^6.0.0",
80 | "lodash": "^4.17.4",
81 | "prettier": "^1.9.2",
82 | "rollup": "^0.54.0",
83 | "rollup-plugin-babel": "^3.0.3",
84 | "rollup-plugin-commonjs": "^8.2.6",
85 | "rollup-plugin-filesize": "^1.5.0",
86 | "rollup-plugin-json": "^2.3.0",
87 | "rollup-plugin-license": "^0.5.0",
88 | "rollup-plugin-node-resolve": "^3.0.0",
89 | "rollup-plugin-replace": "^2.0.0",
90 | "rollup-plugin-uglify": "^2.0.1",
91 | "rollup-plugin-vue": "^3.0.0",
92 | "semantic-release": "^12.2.2",
93 | "stylus": "^0.54.5",
94 | "stylus-loader": "^3.0.1",
95 | "uglify-es": "^3.3.4",
96 | "vue": "^2.5.13",
97 | "vue-jest": "^1.4.0",
98 | "vue-loader": "^13.6.2",
99 | "vue-template-compiler": "^2.5.13"
100 | },
101 | "config": {
102 | "commitizen": {
103 | "path": "cz-conventional-changelog"
104 | }
105 | },
106 | "lint-staged": {
107 | "*.{js,vue}": [
108 | "eslint --fix",
109 | "git add"
110 | ]
111 | },
112 | "release": {
113 | "verifyConditions": [
114 | "@semantic-release/changelog",
115 | "@semantic-release/npm",
116 | "@semantic-release/git",
117 | "@semantic-release/github"
118 | ],
119 | "getLastRelease": "@semantic-release/npm",
120 | "publish": [
121 | "@semantic-release/changelog",
122 | "@semantic-release/npm",
123 | "@semantic-release/git",
124 | "@semantic-release/github"
125 | ]
126 | },
127 | "dependencies": {
128 | "lodash.clonedeep": "^4.5.0",
129 | "lodash.foreach": "^4.5.0",
130 | "lodash.isarray": "^4.0.0",
131 | "lodash.isfunction": "^3.0.9",
132 | "lodash.isstring": "^4.0.1",
133 | "vue-form-generator": "^2.2.1",
134 | "vuedraggable": "^2.24.3"
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { camelCase } from "lodash";
2 | import path from "path";
3 | import babel from "rollup-plugin-babel";
4 | import commonjs from "rollup-plugin-commonjs";
5 | import filesize from "rollup-plugin-filesize";
6 | import json from "rollup-plugin-json";
7 | import license from "rollup-plugin-license";
8 | import resolve from "rollup-plugin-node-resolve";
9 | import replace from "rollup-plugin-replace";
10 | import uglify from "rollup-plugin-uglify";
11 | import vue from "rollup-plugin-vue";
12 | import { minify } from "uglify-es";
13 |
14 | import pack from "./package.json";
15 |
16 | const projectName = "vfg-field-array";
17 |
18 | // compute globals from dependencies
19 | const globals =
20 | pack.dependencies &&
21 | Object.assign(
22 | {},
23 | ...Object.keys(pack.dependencies).map(key => ({
24 | [key]: camelCase(key)
25 | }))
26 | );
27 |
28 | const builds = {
29 | // (CommonJS). Used by bundlers e.g. Webpack & Browserify
30 | cjs: {
31 | entry: "src/index.js",
32 | ignore: [
33 | 'vue',
34 | 'vue-form-generator',
35 | 'lodash.foreach'
36 | ],
37 | dest: `dist/${projectName}.common.js`,
38 | format: "cjs"
39 | },
40 | // (ES Modules). Used by bundlers that support ES Modules,
41 | // e.g. Rollup & Webpack 2
42 | esm: {
43 | entry: "src/index.js",
44 | ignore: [
45 | 'vue',
46 | 'vue-form-generator',
47 | 'lodash.foreach'
48 | ],
49 | dest: `dist/${projectName}.esm.js`,
50 | format: "es"
51 | },
52 | // build (Browser)
53 | "umd-dev": {
54 | entry: "src/index.umd.js",
55 | dest: `dist/${projectName}.js`,
56 | ignore: [
57 | 'vue',
58 | 'vue-form-generator',
59 | 'lodash.foreach'
60 | ],
61 | format: "umd",
62 | env: "development"
63 | },
64 | // production build (Browser)
65 | "umd-prod": {
66 | entry: "src/index.umd.js",
67 | dest: `dist/${projectName}.min.js`,
68 | ignore: [
69 | 'vue',
70 | 'vue-form-generator',
71 | 'lodash.foreach'
72 | ],
73 | format: "umd",
74 | env: "production"
75 | }
76 | };
77 |
78 | function genConfig(name) {
79 | const opts = builds[name];
80 | const config = {
81 | input: opts.entry,
82 | external: ['vue', 'vue-form-generator', 'lodash.foreach'],
83 | globals: { vue: 'Vue' },
84 | plugins: [
85 | resolve({
86 | browser: true,
87 | jsnext: true,
88 | preferBuiltins: false,
89 | extensions: [".js", ".json", ".vue"]
90 | }),
91 | commonjs(),
92 | vue({ compileTemplate: true, css: true }),
93 | json(),
94 | babel({
95 | exclude: "node_modules/**",
96 | runtimeHelpers: true
97 | }),
98 | filesize()
99 | ].concat(opts.plugins || []),
100 | output: {
101 | exports: "named",
102 | file: opts.dest,
103 | format: opts.format,
104 | // define globals in window from external dependencies
105 | globals,
106 | name: opts.moduleName || projectName
107 | }
108 | };
109 |
110 | if (opts.env) {
111 | config.plugins.push(
112 | replace({
113 | "process.env.NODE_ENV": JSON.stringify(opts.env)
114 | })
115 | );
116 |
117 | // minify on production targets
118 | if (opts.env === "production") {
119 | config.plugins.push(uglify({}, minify));
120 | }
121 | }
122 |
123 | // output a license to builds
124 | config.plugins.push(
125 | license({
126 | sourceMap: true,
127 | banner: {
128 | file: path.resolve("LICENSE.md")
129 | }
130 | })
131 | );
132 |
133 | Object.defineProperty(config, "_name", {
134 | enumerable: false,
135 | value: name
136 | });
137 |
138 | return config;
139 | }
140 |
141 | const target = process.env.TARGET || "umd-prod";
142 | console.log(genConfig(target));
143 | module.exports = genConfig(target);
144 |
--------------------------------------------------------------------------------
/src/__tests__/index.js:
--------------------------------------------------------------------------------
1 | import ModuleLibrary from "../index";
2 |
3 | test("Should register all components when installed", () => {
4 | const component = jest.fn();
5 | const Vue = { component };
6 |
7 | ModuleLibrary.install(Vue);
8 |
9 | // Test if a particular component was registered
10 | expect(component).toBeCalledWith("field-array", expect.any(Object));
11 |
12 | // Test how many times component got registered
13 | const totalOfComponents = 1;
14 | expect(component).toHaveBeenCalledTimes(totalOfComponents);
15 | });
16 |
--------------------------------------------------------------------------------
/src/components/__tests__/__snapshots__/field-array.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`field-array.vue should match the snapshot 1`] = ``;
4 |
--------------------------------------------------------------------------------
/src/components/__tests__/field-array.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import FieldArray from "../field-array";
3 |
4 | describe("field-array.vue", () => {
5 | const Constructor = Vue.extend(FieldArray);
6 | const vm = new Constructor().$mount();
7 |
8 | test("should match the snapshot", () => {
9 | expect(vm.$el).toMatchSnapshot();
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/src/components/field-array.vue:
--------------------------------------------------------------------------------
1 |
2 |
89 |
90 |
91 |
290 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import FieldArray from "./components/field-array";
2 |
3 | const LibraryModule = {
4 | FieldArray,
5 |
6 | install(Vue) {
7 | // Register components with vue
8 | Vue.component("field-array", FieldArray);
9 | }
10 | };
11 |
12 | // Export library
13 | export default LibraryModule;
14 |
15 | // Export components
16 | export { FieldArray };
17 |
--------------------------------------------------------------------------------
/src/index.umd.js:
--------------------------------------------------------------------------------
1 | import LibraryModule, * as LibraryComponents from "./index";
2 |
3 | // Automatically register components if Vue is available globally
4 | if (typeof window !== "undefined" && window.Vue) {
5 | window.Vue.use(LibraryModule);
6 | }
7 |
8 | export default LibraryComponents;
9 |
--------------------------------------------------------------------------------
/stories/Container.vue:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
25 |
35 |
50 |
--------------------------------------------------------------------------------
/stories/_index.stories.js:
--------------------------------------------------------------------------------
1 | import { storiesOf } from "@storybook/vue";
2 |
3 | // Add more stories here to live develop your components
4 | storiesOf("1- FieldArray", module).add("Simple array", () => {
5 | return {
6 | data: function() {
7 | return {
8 | model: {
9 | array: []
10 | },
11 | schema: {
12 | fields: [
13 | {
14 | type: "array",
15 | label: "Array field",
16 | model: "array"
17 | }
18 | ]
19 | }
20 | };
21 | },
22 |
23 | template: `
24 |
25 |
Form
26 |
27 |
Generated Value
28 |
{{model.array}}
29 |
Schema
30 |
{{schema}}
31 |
32 | `
33 | };
34 | });
35 |
--------------------------------------------------------------------------------
/stories/bootstrap-accordion-container.stories.js:
--------------------------------------------------------------------------------
1 | import { storiesOf } from "@storybook/vue";
2 | import BootstrapAccordionContainer from "./bootstrap-accordion-container";
3 | import Vue from "vue";
4 |
5 | Vue.component(
6 | "field-array-bootstrap-accordion-item",
7 | BootstrapAccordionContainer
8 | );
9 |
10 | // Add more stories here to live develop your components
11 | storiesOf("2- Containers", module).add("Bootstrap accordion container", () => {
12 | return {
13 | data: function() {
14 | return {
15 | model: {
16 | array: []
17 | },
18 | schema: {
19 | fields: [
20 | {
21 | type: "array",
22 | label: "Columns",
23 | model: "columns",
24 | inputName: "columns",
25 | showRemoveButton: false,
26 | newElementButtonLabelClasses: "btn btn-outline-dark mt-2",
27 | itemContainerComponent: "field-array-bootstrap-accordion-item",
28 | newElementButtonLabel: "+ Add Column",
29 | itemContainerHeader: function(model, schema, index) {
30 | return (
31 | "Column " +
32 | (index + 1) +
33 | (model && model.label ? " (" + model.label + ")" : "")
34 | );
35 | },
36 | items: {
37 | type: "input",
38 | inputType: "text",
39 | label: "Description",
40 | model: "description",
41 | inputName: "description",
42 | validator: "string"
43 | }
44 | }
45 | ]
46 | }
47 | };
48 | },
49 |
50 | template: `
51 |
52 |
Warning
53 | This story is a work in progress and lacks some integration of jquery and bootstrap.
54 |
Form
55 |
56 |
Generated Value
57 |
{{model.array}}
58 |
Schema
59 |
{{schema}}
60 |
Container component
61 |
<template>
62 | <div>
63 | <div class="card">
64 | <div class="card-header" :id="'heading' + id" :class="{'bg-danger': hasError}">
65 | <h5 class="mb-0">
66 | <a data-toggle="collapse" :data-target="'#collapse' + id" aria-expanded="false" :aria-controls="'collapse' + id">
67 | {{ headerText }}
68 | </a>
69 | <input type="button" class="btn btn-danger btn-sm float-right" @click="$emit('removeItem')" :value="removeElementButtonLabel" />
70 | <input type="button" class="btn btn-outline-dark btn-sm float-right mr-1" @click="moveItem('moveItemDown')" :value="moveElementDownButtonLabel" />
71 | <input type="button" class="btn btn-outline-dark btn-sm float-right mr-1" @click="moveItem('moveItemUp')" :value="moveElementUpButtonLabel" />
72 | </h5>
73 | </div>
74 |
75 | <div :id="'collapse' + id" class="collapse" :aria-labelledby="'heading' + id" :data-parent="'#'+parentId" >
76 | <div class="card-body">
77 | <slot></slot>
78 | </div>
79 | </div>
80 | </div>
81 | </div>
82 | </template>
83 |
84 | <script>
85 | export default {
86 | name: "FieldArrayBootstrapAccordionItem",
87 | props: [
88 | "model",
89 | "index",
90 | "schema",
91 | "id",
92 | "parentId",
93 | "removeElementButtonLabel",
94 | "moveElementDownButtonLabel",
95 | "moveElementUpButtonLabel",
96 | "itemContainerHeader"
97 | ],
98 | data() {
99 | return {
100 | hasError: false
101 | };
102 | },
103 | computed: {
104 | headerText() {
105 | if (typeof this.itemContainerHeader === "function") {
106 | return this.itemContainerHeader(this.model, this.schema, this.index);
107 | } else if (typeof this.itemContainerHeader !== "undefined") {
108 | return typeof this.itemContainerHeader;
109 | }
110 |
111 | return "Field " + (this.index + 1);
112 | }
113 | },
114 | methods: {
115 | validate(calledParent) {
116 | if (this.$children.length < 1) {
117 | console.warn(
118 | "The accordion item of the array field seam to be empty. Could not validate"
119 | );
120 | return false;
121 | }
122 |
123 | return this.$children[0].validate(calledParent);
124 | },
125 | validated(isValid, errors) {
126 | this.hasError = !isValid;
127 | },
128 | moveItem(direction) {
129 | $("#collapse" + this.id).collapse("hide");
130 | this.$emit(direction);
131 | }
132 | },
133 | mounted() {
134 | if (
135 | !this.$slots.default ||
136 | typeof this.$slots.default[0] !== "object" ||
137 | typeof this.$slots.default[0].componentInstance !== "object" ||
138 | typeof this.$slots.default[0].componentInstance.$on !== "function"
139 | ) {
140 | return;
141 | }
142 |
143 | this.$slots.default[0].componentInstance.$on("validated", this.validated);
144 | }
145 | };
146 | </script>
147 |
148 | <style scoped>
149 |
150 | </style>
151 |
152 |
153 | `
154 | };
155 | });
156 |
--------------------------------------------------------------------------------
/stories/bootstrap-accordion-container.vue:
--------------------------------------------------------------------------------
1 |
2 |
22 |
23 |
24 |
87 |
88 |
91 |
--------------------------------------------------------------------------------
/stories/remove-button.stories.js:
--------------------------------------------------------------------------------
1 | import { storiesOf } from "@storybook/vue";
2 |
3 | // Add more stories here to live develop your components
4 | storiesOf("1- FieldArray", module).add("Remove button", () => {
5 | return {
6 | data: function() {
7 | return {
8 | model: {
9 | array: []
10 | },
11 | schema: {
12 | fields: [
13 | {
14 | type: "array",
15 | label: "Array field",
16 | model: "array",
17 | showRemoveButton: true
18 | }
19 | ]
20 | }
21 | };
22 | },
23 |
24 | template: `
25 |
26 |
Form
27 |
28 |
Generated Value
29 |
{{model.array}}
30 |
Schema
31 |
{{schema}}
32 |
33 | `
34 | };
35 | });
36 |
--------------------------------------------------------------------------------
/stories/show-empty-component-at-bottom.stories.js:
--------------------------------------------------------------------------------
1 | import { storiesOf } from "@storybook/vue";
2 | import Vue from "vue";
3 | import VueFormGenerator from "vue-form-generator";
4 |
5 | Vue.component("fieldCheck", {
6 | mixins: [VueFormGenerator.abstractField],
7 | template: `
8 |
9 | Please select one
10 | A
11 | B
12 | C
13 |
14 | `
15 | });
16 |
17 | // Add more stories here to live develop your components
18 | storiesOf("3- Advanced", module).add("showEmptyComponentAtBottom", () => {
19 | return {
20 | data: function() {
21 | return {
22 | model: {
23 | array: []
24 | },
25 | schema: {
26 | fields: [
27 | {
28 | type: "array",
29 | label: "Array field",
30 | model: "array",
31 | showEmptyComponentAtBottom: true,
32 | removeUndefinedValues: true,
33 | hideAddButton: true,
34 | items: {
35 | type: "check"
36 | }
37 | }
38 | ]
39 | }
40 | };
41 | },
42 |
43 | template: `
44 |
45 |
Form
46 |
47 |
Generated Value
48 |
{{model.array}}
49 |
Schema
50 |
{{schema}}
51 |
52 | `
53 | };
54 | });
55 |
--------------------------------------------------------------------------------
/stories/simple-array-with-container.stories.js:
--------------------------------------------------------------------------------
1 | import { storiesOf } from "@storybook/vue";
2 | import Container from "./Container";
3 | import Vue from "vue";
4 |
5 | Vue.component("Container", Container);
6 |
7 | // Add more stories here to live develop your components
8 | storiesOf("2- Containers", module).add("Simple array with container", () => {
9 | return {
10 | data: function() {
11 | return {
12 | model: {
13 | array: []
14 | },
15 | schema: {
16 | fields: [
17 | {
18 | type: "array",
19 | label: "Array field",
20 | model: "array",
21 | itemContainerComponent: "Container"
22 | }
23 | ]
24 | }
25 | };
26 | },
27 |
28 | template: `
29 |
30 |
Form
31 |
32 |
Generated Value
33 |
{{model.array}}
34 |
Schema
35 |
{{schema}}
36 |
37 | `
38 | };
39 | });
40 |
--------------------------------------------------------------------------------