[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.md]\ntrim_trailing_whitespace = false"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\ndist/\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) EGOIST <0x142857@gmail.com> (https://egoist.moe)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# vue-final-form\n\n<!-- hide-on-docup-start -->\n\n[![NPM version](https://img.shields.io/npm/v/vue-final-form.svg?style=flat)](https://npmjs.com/package/vue-final-form) [![NPM downloads](https://img.shields.io/npm/dm/vue-final-form.svg?style=flat)](https://npmjs.com/package/vue-final-form) [![CircleCI](https://circleci.com/gh/egoist/vue-final-form/tree/master.svg?style=shield)](https://circleci.com/gh/egoist/vue-final-form/tree/master) [![donate](https://img.shields.io/badge/$-donate-ff69b4.svg?maxAge=2592000&style=flat)](https://github.com/egoist/donate) [![chat](https://img.shields.io/badge/chat-on%20discord-7289DA.svg?style=flat)](https://chat.egoist.moe)\n\n<!-- hide-on-docup-stop -->\n\n## Introduction\n\n🏁 High performance subscription-based form state management for Vue.js.\n\n## Install\n\n```bash\nyarn add final-form vue-final-form\n```\n\n## Usage\n\n[![Edit example](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/egoist/vue-final-form/tree/master/example)\n\nThis library exports two components:\n\n```js\nimport { FinalForm, FinalField } from \"vue-final-form\";\n```\n\nThe first component you'll need is the `FinalForm` component:\n\n```vue\n<FinalForm :submit=\"submit\">\n  <!-- ignore the children for now -->\n</FinalForm>\n```\n\nThe only required prop is `submit`, it defines how to submit the form data, maybe simply log it:\n\n```js\nfunction submit(values) {\n  console.log(values);\n}\n```\n\nThe rendered output is:\n\n```html\n<div></div>\n```\n\nAs you can see it does nothing for now, you need to feed it a `<form>`:\n\n```vue\n<FinalForm :submit=\"submit\">\n  <form v-slot=\"props\" @submit=\"props.handleSubmit\">\n    <!-- ignore the children for now -->\n  </form>\n</FinalForm>\n```\n\nNow it renders:\n\n```html\n<div><form></form></div>\n```\n\nHere it uses the [`scoped slots`](https://vuejs.org/v2/guide/components.html#Scoped-Slots) feature from Vue.js (>=2.1.0), `props.handleSubmit` is the actual method it will run to submit data.\n\nNext let's define a form field with `<FinalField>` component:\n\n```vue\n<FinalForm :submit=\"submit\">\n  <form v-slot=\"props\" @submit=\"props.handleSubmit\">\n    <FinalField\n      name=\"username\"\n      :validate=\"v => v ? null : 'this is required'\">\n      <div v-slot=\"props\">\n        <input v-on=\"props.events\" :name=\"props.name\">\n        <span v-if=\"props.meta.error && props.meta.touched\">\n          {{ props.meta.error }}\n        </span>\n      </div>\n    </FinalField>\n  </form>\n</FinalForm>\n```\n\nThings got a bit more complex, but it's easy if you try to understand:\n\nThe only prop that is required by `FinalField` is `name`, it tells the field where to store the field data in the form state, here we stores it as `state.username`.\n\nThe `validate` prop is used to validate the field data, it could be a function that returns an error message or `null`, `undefined` when it's considered valid.\n\nThe data that `FinalField` passed to its children contains `props.events` which is required to be bound with the `input` element in order to properly track events. And `props.name` is the `name` you gave `FinalField`, `props.meta` is some infomation about this field.\n\n## API\n\n### `<FinalForm>`\n\n#### Props\n\n##### submit\n\nType: `function`<br>\nDefault: `() => {}`\n\nHere we allow `submit` to be optional since you may not need it when you just use `vue-final-form` as a form validation tool.\n\nSee [onSubmit](https://github.com/final-form/final-form#onsubmit-values-object-form-formapi-callback-errors-object--void--object--promiseobject--void).\n\n##### validate\n\nType: `function` `Array<function>`\n\nA whole-record validation function that takes all the values of the form and returns any validation errors.\n\nSee [validate](https://github.com/final-form/final-form#validate-values-object--object--promiseobject).\n\n##### initialValues\n\nType: `object`\n\nSee [initialValues](https://github.com/final-form/final-form#initialvalues-object).\n\n##### subscription\n\nType: `FormSubscription`<br>\nDefault: All\n\nSee [FormSubscription](https://github.com/final-form/final-form#formsubscription--string-boolean-).\n\n#### Events\n\n##### change\n\nParams:\n\n- `formState`: https://github.com/final-form/final-form#formstate\n\n#### Scoped slot props\n\nIt basically exposes everything in [FormState](https://github.com/final-form/final-form#formstate) plus follwoings:\n\n##### handleSubmit\n\nType: `function`\n\nThe function that you will invoke to submit the form data, you may use it as the `:submit` event handler on your `<form>`.\n\n##### reset\n\nType: `function`\n\nSee [FormApi.reset](https://github.com/final-form/final-form#reset---void).\n\n##### mutators\n\nType: `?{ [string]: Function }`\n\nSee [FormApi.mutators](https://github.com/final-form/final-form#mutators--string-function-).\n\n##### batch\n\nType: `function`\n\nSee [FormApi.batch](https://github.com/final-form/final-form#batch-fn---void--void).\n\n##### blur\n\nType: `function`\n\nSee [FormApi.blur](https://github.com/final-form/final-form#blur-name-string--void).\n\n##### change\n\nType: `function`\n\nSee [FormApi.change](https://github.com/final-form/final-form#change-name-string-value-any--void).\n\n##### focus\n\nType: `function`\n\nSee [FormApi.focus](https://github.com/final-form/final-form#focus-name-string--void)\n\n##### initialize\n\nType: `function`\n\nSee [FormApi.initialize](https://github.com/final-form/final-form#initialize-values-object--void).\n\n### `<FinalField>`\n\n#### Props\n\n##### name\n\nType: `string`<br>\nRequired: `true`\n\nThe name of this field.\n\nSee [name](https://github.com/final-form/final-form#name-string-1).\n\n##### validate\n\nType: `function` `Array<function>`\n\nA field-level validation function to validate a single field value. Returns an error if the value is not valid, or undefined if the value is valid.\n\nSee [validate](https://github.com/final-form/final-form#validate-value-any-allvalues-object--any).\n\n##### subscription\n\nType: `FieldSubscription`<br>\nDefault: All\n\nSee [FieldSubcription](https://github.com/final-form/final-form#fieldsubscription--string-boolean-).\n\n#### Events\n\n##### change\n\nParams:\n\n- `fieldState`: https://github.com/final-form/final-form#fieldstate\n\n#### Scoped slot props\n\nIt basically exposes [FieldState](https://github.com/final-form/final-form#fieldstate).\n\n##### name\n\nType: `string`\n\nThe name of this field.\n\nSee [`FieldState.name`](https://github.com/final-form/final-form#name-string)\n\n##### change\n\nType: `function`\n\nSee [`FieldState.change`](https://github.com/final-form/final-form#change-value-any--void)\n\n##### value\n\nType: `any`.\n\nThe current value of this field. You should probably bind it to `:value` of `input` or `textarea` if you have set initial value for the field.\n\n##### events\n\nType: `{ input: Function, focus: Function, blur: Function }`\n\nBind these event handlers to your `input` `textarea` element.\n\nSee [FieldState.change](https://github.com/final-form/final-form#change-value-any--void), [FieldState.focus](https://github.com/final-form/final-form#focus---void), [FieldState.blur](https://github.com/final-form/final-form#blur---void).\n\n##### meta\n\nType: `object`\n\nEverything in [FieldState](https://github.com/final-form/final-form#fieldstate) except for `blur` `change` `focus` `name`\n\n## Contributing\n\n1. Fork it!\n2. Create your feature branch: `git checkout -b my-new-feature`\n3. Commit your changes: `git commit -am 'Add some feature'`\n4. Push to the branch: `git push origin my-new-feature`\n5. Submit a pull request :D\n\n## Author\n\n**vue-final-form** © [EGOIST](https://github.com/egoist), Released under the [MIT](https://github.com/egoist/vue-final-form/blob/master/LICENSE) License.<br>\nAuthored and maintained by EGOIST with help from contributors ([list](https://github.com/egoist/vue-final-form/contributors)).\n\n> [egoist.moe](https://egoist.moe) · GitHub [@EGOIST](https://github.com/egoist) · Twitter [@\\_egoistlily](https://twitter.com/_egoistlily)\n"
  },
  {
    "path": "circle.yml",
    "content": "version: 2\njobs:\n  build:\n    working_directory: ~/repo\n    docker:\n      - image: circleci/node:latest\n    branches:\n      ignore:\n        - gh-pages # list of branches to ignore\n        - /release\\/.*/ # or ignore regexes\n    steps:\n      - checkout\n      - restore_cache:\n          key: dependency-cache-{{ checksum \"yarn.lock\" }}\n      - run:\n          name: install dependences\n          command: yarn\n      - save_cache:\n          key: dependency-cache-{{ checksum \"yarn.lock\" }}\n          paths:\n            - ./node_modules\n      - run:\n          name: test\n          command: yarn test\n"
  },
  {
    "path": "example/App.vue",
    "content": "<template>\n  <div id=\"app\">\n    <select\n      class=\"component-list\"\n      v-model=\"current\">\n      <option value=\"\">Select an example</option>\n      <option\n        class=\"component-item\"\n        v-for=\"item in list\"\n        :value=\"item\"\n        :key=\"item\">\n        {{ item }}\n      </option>\n    </select>\n    <hr>\n    <component\n      v-if=\"$route.query.component\"\n      :is=\"$route.query.component\" />\n  </div>\n</template>\n\n<script>\nimport Simple from './examples/Simple.vue'\nimport Composition from './examples/Composition.vue'\n\nexport default {\n  data() {\n    return {\n      list: [\n        'Simple',\n        'Composition',\n      ],\n      current: this.$route.query.component || ''\n    }\n  },\n\n  watch: {\n    current() {\n      this.$router.push({\n        query: {\n          component: this.current\n        }\n      })\n    }\n  },\n\n  components: {\n    Simple,\n    Composition,\n  }\n}\n</script>\n\n<style>\n.error {\n  color: red;\n}\n</style>\n\n<style scoped>\n.component-item {\n  cursor: pointer;\n}\n</style>\n"
  },
  {
    "path": "example/examples/Composition.vue",
    "content": "<template>\n  <div>\n    <form @submit=\"finalForm.handleSubmit\">\n      <div>\n        <input\n          :name=\"emailField.name\"\n          :value=\"emailField.value\"\n          v-on=\"emailField.events\"\n          type=\"email\"\n        />\n        <span\n          class=\"error\"\n          v-if=\"emailField.touched && emailField.error\">\n          {{ emailField.error }}\n        </span>\n      </div>\n      <div>\n        <input\n          :name=\"passwordField.name\"\n          :value=\"passwordField.value\"\n          v-on=\"passwordField.events\"\n          type=\"password\"\n        />\n        <span\n          class=\"error\"\n          v-if=\"passwordField.touched && passwordField.error\">\n          {{ passwordField.error }}\n        </span>\n      </div>\n      <div>\n        <input\n          :name=\"confirmPasswordField.name\"\n          :value=\"confirmPasswordField.value\"\n          v-on=\"confirmPasswordField.events\"\n          type=\"password\"\n        />\n        <span\n          class=\"error\"\n          v-if=\"confirmPasswordField.touched && confirmPasswordField.error\">\n          {{ confirmPasswordField.error }}\n        </span>\n      </div>\n\n      <button type=\"submit\" :disabled=\"formState.submitting || null\">\n        {{ formState.submitting ? 'Submitting' : 'Submit' }}\n      </button>\n    </form>\n\n    <pre v-if=\"formState\"><code>form state:<br><br>{{ JSON.stringify(formState, null, 2) }}</code></pre>\n  </div>\n</template>\n\n<script setup>\nimport { useForm, useField } from 'vue-final-form'\n\nfunction sleep(timeout) {\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve()\n    }, timeout)\n  })\n}\n\nconst required = v =>\n  v ? null : 'This field is required!'\n\nconst matchedPassword = (value, values) =>\n  value === values.password ? null : 'Mismatched password!'\n\nconst range = (min, max) =>\n  value =>\n    value && value.length >= min && value.length <= max ? null : `Password should be between length ${min} and ${max}`\n\nconst noSpecialChars = (v) =>\n  /[!@#$%^&*()]/.test(v) ? 'Please do not use special chars' : null\n\nconst initialValues = {\n  email: 'egoist@boring.com',\n}\n\nconst handleSubmit = async state => {\n  await sleep(2000)\n  console.log(state)\n}\n\nconst { finalForm, formState } = useForm({\n  initialValues,\n  onSubmit: handleSubmit,\n})\n\nconst { fieldState: emailField } = useField({\n  finalForm,\n  name: 'email',\n  validate: required,\n})\n\nconst { fieldState: passwordField } = useField({\n  finalForm,\n  name: 'password',\n  validate: [range(6, 20), noSpecialChars],\n})\n\nconst { fieldState: confirmPasswordField } = useField({\n  finalForm,\n  name: 'confirmPassword',\n  validate: matchedPassword,\n})\n</script>\n"
  },
  {
    "path": "example/examples/Simple.vue",
    "content": "<template>\n  <div>\n    <FinalForm\n      :submit=\"handleSubmit\"\n      @change=\"updateState\"\n      :initialValues=\"initialValues\">\n      <template v-slot=\"props\">\n        <form @submit=\"props.handleSubmit\">\n          <FinalField name=\"email\" :validate=\"required\">\n            <template v-slot=\"props\">\n              <div>\n                <input\n                  v-on=\"props.events\"\n                  :name=\"props.name\"\n                  :value=\"props.value\"\n                  type=\"email\"\n                />\n                <span\n                  class=\"error\"\n                  v-if=\"props.meta.touched && props.meta.error\">\n                  {{ props.meta.error }}\n                </span>\n              </div>\n            </template>\n          </FinalField>\n          <FinalField name=\"password\" :validate=\"[range(6, 20), noSpecialChars]\">\n            <template v-slot=\"props\">\n              <div>\n                <input\n                  v-on=\"props.events\"\n                  :name=\"props.name\"\n                  :value=\"props.value\"\n                  type=\"password\"\n                />\n                <span\n                  class=\"error\"\n                  v-if=\"props.meta.touched && props.meta.error\">\n                  {{ props.meta.error }}\n                </span>\n              </div>\n            </template>\n          </FinalField>\n          <FinalField name=\"confirmPassword\" :validate=\"matchedPassword\">\n            <template v-slot=\"props\">\n              <div>\n                <input\n                  v-on=\"props.events\"\n                  :name=\"props.name\"\n                  :value=\"props.value\"\n                  type=\"password\"\n                />\n                <span\n                  class=\"error\"\n                  v-if=\"props.meta.touched && props.meta.error\">\n                  {{ props.meta.error }}\n                </span>\n              </div>\n            </template>\n          </FinalField>\n          <button type=\"submit\" :disabled=\"props.submitting || null\">\n            {{ props.submitting ? 'Submitting' : 'Submit' }}\n          </button>\n        </form>\n      </template>\n    </FinalForm>\n\n    <pre v-if=\"formState\"><code>form state:<br><br>{{ JSON.stringify(formState, null, 2) }}</code></pre>\n  </div>\n</template>\n\n<script>\nimport { FinalForm, FinalField } from 'vue-final-form'\n\nfunction sleep(timeout) {\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve()\n    }, timeout);\n  })\n}\n\nexport default {\n  components: {\n    FinalForm,\n    FinalField\n  },\n\n  data() {\n    return {\n      formState: null,\n      initialValues: {\n        email: 'egoist@boring.com'\n      }\n    }\n  },\n\n  methods: {\n    async handleSubmit(state) {\n      await sleep(2000)\n      console.log(state)\n    },\n\n    updateState(state) {\n      this.formState = state\n    },\n\n    required(v) {\n      return v ? null : 'This field is required!'\n    },\n\n    matchedPassword(value, values) {\n      return value === values.password ? null : 'Mismatched password!'\n    },\n\n    range(min, max) {\n      return value => {\n        return value && value.length >= min && value.length <= max ? null : `Password should be between length ${min} and ${max}`\n      }\n    },\n\n    noSpecialChars(v) {\n      return /[!@#$%^&*()]/.test(v) ? 'Please do not use specfial chars' : null\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "example/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\n  <title>Vue-Final-Form</title>\n</head>\n<body>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"/index.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "example/index.js",
    "content": "import { createApp } from 'vue'\nimport App from './App.vue'\nimport router from './router'\n\nconst app = createApp(App)\n\napp.use(router)\n\napp.mount('#app')\n"
  },
  {
    "path": "example/package.json",
    "content": "{\n  \"name\": \"example\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"final-form\": \"^4.0.0\",\n    \"vue\": \"^3.0.0\",\n    \"vue-router\": \"^4.0.0\",\n    \"vue-final-form\": \"latest\"\n  }\n}\n"
  },
  {
    "path": "example/router.js",
    "content": "import { h } from 'vue'\nimport { createRouter, createWebHistory } from 'vue-router'\nimport App from './App.vue'\n\nexport default createRouter({\n  history: createWebHistory(),\n  routes: [{\n    path: '/',\n    component: {\n      name: 'Home',\n      render: () => h(App)\n    }\n  }]\n})\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\n  <title>Vue-Final-Form</title>\n  <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@egojump/docup/dist/docup.css\">\n</head>\n<body>\n    <a href=\"https://github.com/egoist/vue-final-form\" class=\"github-corner\" aria-label=\"View source on Github\"><svg width=\"80\" height=\"80\" viewBox=\"0 0 250 250\" style=\"fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;\" aria-hidden=\"true\"><path d=\"M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z\"></path><path d=\"M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2\" fill=\"currentColor\" style=\"transform-origin: 130px 106px;\" class=\"octo-arm\"></path><path d=\"M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z\" fill=\"currentColor\" class=\"octo-body\"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>\n  <div id=\"app\"></div>\n  <script src=\"https://cdn.jsdelivr.net/npm/@egojump/docup/dist/docup.js\"></script>\n  <script>\n    var doc = new Docup()\n    doc.start('#app')\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"vue-final-form\",\n  \"version\": \"4.0.1\",\n  \"description\": \"Subscription-based form state management for Vue.js\",\n  \"repository\": {\n    \"url\": \"egoist/vue-final-form\",\n    \"type\": \"git\"\n  },\n  \"main\": \"dist/index.js\",\n  \"module\": \"dist/index.esm.js\",\n  \"unpkg\": \"dist/index.umd.js\",\n  \"jsdelivr\": \"dist/index.umd.js\",\n  \"files\": [\n    \"dist\"\n  ],\n  \"scripts\": {\n    \"test\": \"npm run lint && echo 'no tests!'\",\n    \"lint\": \"xo\",\n    \"prepublishOnly\": \"npm run build\",\n    \"build\": \"bili --format esm,cjs,umd,umd-min --module-name VueFinalForm --global.final-form final-form --global.vue vue\",\n    \"example\": \"vite\",\n    \"build:example\": \"vite build\",\n    \"gh\": \"gh-pages -d example/dist\",\n    \"deploy\": \"npm run build:example && npm run gh\"\n  },\n  \"author\": \"egoist <0x142857@gmail.com>\",\n  \"license\": \"MIT\",\n  \"devDependencies\": {\n    \"@vitejs/plugin-vue\": \"^1.10.0\",\n    \"bili\": \"^5.0.5\",\n    \"eslint-config-rem\": \"^4.0.0\",\n    \"final-form\": \"^4.0.0\",\n    \"gh-pages\": \"^1.0.0\",\n    \"vite\": \"^2.0.0\",\n    \"vue\": \"^3.0.0\",\n    \"vue-router\": \"^4.0.0\",\n    \"xo\": \"^0.47.0\"\n  },\n  \"xo\": {\n    \"extends\": \"rem\",\n    \"envs\": [\n      \"browser\"\n    ],\n    \"ignores\": [\n      \"example/**\"\n    ],\n    \"rules\": {\n      \"unicorn/filename-case\": 0,\n      \"unicorn/prefer-export-from\": 0,\n      \"import/prefer-default-export\": 0\n    }\n  },\n  \"peerDependencies\": {\n    \"final-form\": \"^4.0.0\"\n  }\n}\n"
  },
  {
    "path": "src/Field.js",
    "content": "import { getChildren } from './utils.js'\nimport useField from './useField.js'\n\nconst FinalField = {\n  name: 'final-field',\n\n  props: {\n    name: {\n      required: true,\n      type: String,\n    },\n    validate: [Function, Array],\n    subscription: Object,\n  },\n\n  setup(props) {\n    const { finalForm, fieldState } = useField({\n      name: props.name,\n      subscription: props.subscription,\n      validate: props.validate,\n    })\n\n    return {\n      finalForm,\n      fieldState,\n    }\n  },\n\n  watch: {\n    fieldState(state) {\n      this.$emit('change', state)\n    },\n  },\n\n  render() {\n    const {\n      blur,\n      change,\n      focus,\n      value,\n      name,\n      ...meta\n    } = this.fieldState\n\n    const children = this.$slots.default({\n      events: this.fieldState.events,\n      change,\n      value,\n      name,\n      meta,\n    })\n\n    return getChildren(children)[0]\n  },\n}\n\nexport default FinalField\n"
  },
  {
    "path": "src/Form.js",
    "content": "import { h } from 'vue'\nimport { getChildren } from './utils.js'\nimport useForm from './useForm.js'\n\nconst FinalForm = {\n  name: 'final-form',\n\n  props: {\n    initialValues: Object,\n    submit: {\n      type: Function,\n      default: () => {},\n    },\n    subscription: Object,\n    validate: [Function, Array],\n  },\n\n  setup(props) {\n    const { finalForm, formState } = useForm({\n      initialValues: props.initialValues,\n      validate: props.validate,\n      subscription: props.subscription,\n      onSubmit: props.submit,\n    })\n\n    return {\n      finalForm,\n      formState,\n    }\n  },\n\n  methods: {\n    handleSubmit(event) {\n      event && event.preventDefault()\n      this.finalForm.submit()\n    },\n  },\n\n  watch: {\n    formState(state) {\n      this.$emit('change', state)\n    },\n  },\n\n  render() {\n    const children = this.$slots.default\n      ? this.$slots.default({\n        ...this.formState,\n        ...this.finalForm,\n      })\n      : this.$slots.default\n\n    return h('div', null, getChildren(children))\n  },\n}\n\nexport default FinalForm\n"
  },
  {
    "path": "src/index.js",
    "content": "import FinalForm from './Form.js'\nimport FinalField from './Field.js'\nimport useForm from './useForm.js'\nimport useField from './useField.js'\n\nexport {\n  FinalForm,\n  FinalField,\n  useForm,\n  useField,\n}\n"
  },
  {
    "path": "src/useCurrentForm.js",
    "content": "import { inject } from 'vue'\n\nconst useCurrentForm = () => {\n  const finalForm = inject('finalForm')\n\n  if (!finalForm) {\n    throw new Error('Form was\\'t found. Please provide it using `<FinalForm>` component or `useForm` hook.')\n  }\n\n  return finalForm\n}\n\nexport default useCurrentForm\n"
  },
  {
    "path": "src/useField.js",
    "content": "import { fieldSubscriptionItems } from 'final-form'\nimport { ref, onUnmounted, computed } from 'vue'\nimport useCurrentForm from './useCurrentForm.js'\nimport { composeFieldValidators, makeSubscriptionObject } from './utils.js'\n\nconst defaultSubscription = makeSubscriptionObject(fieldSubscriptionItems)\n\nconst useField = config => {\n  const finalForm = config.finalForm || useCurrentForm()\n\n  const fieldState = ref({})\n\n  const unregister = finalForm.value.registerField(config.name, state => {\n    fieldState.value = state\n  }, config.subscription || defaultSubscription, {\n    getValidator: Array.isArray(config.validate) ? composeFieldValidators(config.validate) : () => config.validate,\n  })\n\n  onUnmounted(unregister)\n\n  return {\n    fieldState: computed(() => ({\n      ...fieldState.value,\n      events: {\n        input: event => fieldState.value.change(event.target.value),\n        focus: () => fieldState.value.focus(),\n        blur: () => fieldState.value.blur(),\n      },\n    })),\n    unregister,\n  }\n}\n\nexport default useField\n"
  },
  {
    "path": "src/useForm.js",
    "content": "import { provide, ref, onUnmounted } from 'vue'\nimport { createForm, formSubscriptionItems } from 'final-form'\nimport { composeFormValidators, makeSubscriptionObject } from './utils.js'\n\nconst defaultSubscription = makeSubscriptionObject(formSubscriptionItems)\n\nconst useForm = config => {\n  const formApi = createForm({\n    initialValues: config.initialValues,\n    validate: Array.isArray(config.validate) ? composeFormValidators(config.validate) : config.validate,\n    onSubmit: config.onSubmit,\n  })\n\n  const finalForm = ref({\n    ...formApi,\n    handleSubmit: event => {\n      event && event.preventDefault()\n      formApi.submit()\n    },\n  })\n\n  const formState = ref()\n\n  const unsubscribe = finalForm.value.subscribe(state => {\n    formState.value = state\n  }, config.subscription || defaultSubscription)\n\n  onUnmounted(unsubscribe)\n\n  provide('finalForm', finalForm)\n\n  return {\n    finalForm,\n    formState,\n    unsubscribe,\n  }\n}\n\nexport default useForm\n"
  },
  {
    "path": "src/utils.js",
    "content": "export const getChildren = children =>\n  Array.isArray(children) ? children : [children]\n\nconst composeValidators = (validators, ...args) =>\n  // eslint-disable-next-line unicorn/no-array-reduce\n  validators.reduce((error, validator) => error || validator(...args), undefined)\n\nexport const composeFormValidators = validators => (...args) =>\n  composeValidators(validators, ...args)\n\nexport const composeFieldValidators = validators => () => (...args) =>\n  composeValidators(validators, ...args)\n\nexport const makeSubscriptionObject = subscriptionItems =>\n  // eslint-disable-next-line no-use-extend-native/no-use-extend-native\n  Object.fromEntries(subscriptionItems.map(key => [key, true]))\n"
  },
  {
    "path": "vite.config.js",
    "content": "import path from 'node:path'\nimport { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  root: 'example',\n  resolve: {\n    alias: {\n      'vue-final-form': path.resolve('./src/index.js'),\n    },\n  },\n  plugins: [vue()],\n})\n"
  }
]