Repository: egoist/vue-final-form
Branch: master
Commit: 0be8ed488152
Files: 23
Total size: 25.9 KB
Directory structure:
gitextract_6qdo1odu/
├── .editorconfig
├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── circle.yml
├── example/
│ ├── App.vue
│ ├── examples/
│ │ ├── Composition.vue
│ │ └── Simple.vue
│ ├── index.html
│ ├── index.js
│ ├── package.json
│ └── router.js
├── index.html
├── package.json
├── src/
│ ├── Field.js
│ ├── Form.js
│ ├── index.js
│ ├── useCurrentForm.js
│ ├── useField.js
│ ├── useForm.js
│ └── utils.js
└── vite.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
================================================
FILE: .gitattributes
================================================
* text=auto
================================================
FILE: .gitignore
================================================
node_modules
dist/
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) EGOIST <0x142857@gmail.com> (https://egoist.moe)
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:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
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.
================================================
FILE: README.md
================================================
# vue-final-form
<!-- hide-on-docup-start -->
[](https://npmjs.com/package/vue-final-form) [](https://npmjs.com/package/vue-final-form) [](https://circleci.com/gh/egoist/vue-final-form/tree/master) [](https://github.com/egoist/donate) [](https://chat.egoist.moe)
<!-- hide-on-docup-stop -->
## Introduction
🏁 High performance subscription-based form state management for Vue.js.
## Install
```bash
yarn add final-form vue-final-form
```
## Usage
[](https://codesandbox.io/s/github/egoist/vue-final-form/tree/master/example)
This library exports two components:
```js
import { FinalForm, FinalField } from "vue-final-form";
```
The first component you'll need is the `FinalForm` component:
```vue
<FinalForm :submit="submit">
<!-- ignore the children for now -->
</FinalForm>
```
The only required prop is `submit`, it defines how to submit the form data, maybe simply log it:
```js
function submit(values) {
console.log(values);
}
```
The rendered output is:
```html
<div></div>
```
As you can see it does nothing for now, you need to feed it a `<form>`:
```vue
<FinalForm :submit="submit">
<form v-slot="props" @submit="props.handleSubmit">
<!-- ignore the children for now -->
</form>
</FinalForm>
```
Now it renders:
```html
<div><form></form></div>
```
Here 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.
Next let's define a form field with `<FinalField>` component:
```vue
<FinalForm :submit="submit">
<form v-slot="props" @submit="props.handleSubmit">
<FinalField
name="username"
:validate="v => v ? null : 'this is required'">
<div v-slot="props">
<input v-on="props.events" :name="props.name">
<span v-if="props.meta.error && props.meta.touched">
{{ props.meta.error }}
</span>
</div>
</FinalField>
</form>
</FinalForm>
```
Things got a bit more complex, but it's easy if you try to understand:
The 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`.
The `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.
The 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.
## API
### `<FinalForm>`
#### Props
##### submit
Type: `function`<br>
Default: `() => {}`
Here we allow `submit` to be optional since you may not need it when you just use `vue-final-form` as a form validation tool.
See [onSubmit](https://github.com/final-form/final-form#onsubmit-values-object-form-formapi-callback-errors-object--void--object--promiseobject--void).
##### validate
Type: `function` `Array<function>`
A whole-record validation function that takes all the values of the form and returns any validation errors.
See [validate](https://github.com/final-form/final-form#validate-values-object--object--promiseobject).
##### initialValues
Type: `object`
See [initialValues](https://github.com/final-form/final-form#initialvalues-object).
##### subscription
Type: `FormSubscription`<br>
Default: All
See [FormSubscription](https://github.com/final-form/final-form#formsubscription--string-boolean-).
#### Events
##### change
Params:
- `formState`: https://github.com/final-form/final-form#formstate
#### Scoped slot props
It basically exposes everything in [FormState](https://github.com/final-form/final-form#formstate) plus follwoings:
##### handleSubmit
Type: `function`
The function that you will invoke to submit the form data, you may use it as the `:submit` event handler on your `<form>`.
##### reset
Type: `function`
See [FormApi.reset](https://github.com/final-form/final-form#reset---void).
##### mutators
Type: `?{ [string]: Function }`
See [FormApi.mutators](https://github.com/final-form/final-form#mutators--string-function-).
##### batch
Type: `function`
See [FormApi.batch](https://github.com/final-form/final-form#batch-fn---void--void).
##### blur
Type: `function`
See [FormApi.blur](https://github.com/final-form/final-form#blur-name-string--void).
##### change
Type: `function`
See [FormApi.change](https://github.com/final-form/final-form#change-name-string-value-any--void).
##### focus
Type: `function`
See [FormApi.focus](https://github.com/final-form/final-form#focus-name-string--void)
##### initialize
Type: `function`
See [FormApi.initialize](https://github.com/final-form/final-form#initialize-values-object--void).
### `<FinalField>`
#### Props
##### name
Type: `string`<br>
Required: `true`
The name of this field.
See [name](https://github.com/final-form/final-form#name-string-1).
##### validate
Type: `function` `Array<function>`
A 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.
See [validate](https://github.com/final-form/final-form#validate-value-any-allvalues-object--any).
##### subscription
Type: `FieldSubscription`<br>
Default: All
See [FieldSubcription](https://github.com/final-form/final-form#fieldsubscription--string-boolean-).
#### Events
##### change
Params:
- `fieldState`: https://github.com/final-form/final-form#fieldstate
#### Scoped slot props
It basically exposes [FieldState](https://github.com/final-form/final-form#fieldstate).
##### name
Type: `string`
The name of this field.
See [`FieldState.name`](https://github.com/final-form/final-form#name-string)
##### change
Type: `function`
See [`FieldState.change`](https://github.com/final-form/final-form#change-value-any--void)
##### value
Type: `any`.
The 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.
##### events
Type: `{ input: Function, focus: Function, blur: Function }`
Bind these event handlers to your `input` `textarea` element.
See [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).
##### meta
Type: `object`
Everything in [FieldState](https://github.com/final-form/final-form#fieldstate) except for `blur` `change` `focus` `name`
## Contributing
1. Fork it!
2. Create your feature branch: `git checkout -b my-new-feature`
3. Commit your changes: `git commit -am 'Add some feature'`
4. Push to the branch: `git push origin my-new-feature`
5. Submit a pull request :D
## Author
**vue-final-form** © [EGOIST](https://github.com/egoist), Released under the [MIT](https://github.com/egoist/vue-final-form/blob/master/LICENSE) License.<br>
Authored and maintained by EGOIST with help from contributors ([list](https://github.com/egoist/vue-final-form/contributors)).
> [egoist.moe](https://egoist.moe) · GitHub [@EGOIST](https://github.com/egoist) · Twitter [@\_egoistlily](https://twitter.com/_egoistlily)
================================================
FILE: circle.yml
================================================
version: 2
jobs:
build:
working_directory: ~/repo
docker:
- image: circleci/node:latest
branches:
ignore:
- gh-pages # list of branches to ignore
- /release\/.*/ # or ignore regexes
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "yarn.lock" }}
- run:
name: install dependences
command: yarn
- save_cache:
key: dependency-cache-{{ checksum "yarn.lock" }}
paths:
- ./node_modules
- run:
name: test
command: yarn test
================================================
FILE: example/App.vue
================================================
<template>
<div id="app">
<select
class="component-list"
v-model="current">
<option value="">Select an example</option>
<option
class="component-item"
v-for="item in list"
:value="item"
:key="item">
{{ item }}
</option>
</select>
<hr>
<component
v-if="$route.query.component"
:is="$route.query.component" />
</div>
</template>
<script>
import Simple from './examples/Simple.vue'
import Composition from './examples/Composition.vue'
export default {
data() {
return {
list: [
'Simple',
'Composition',
],
current: this.$route.query.component || ''
}
},
watch: {
current() {
this.$router.push({
query: {
component: this.current
}
})
}
},
components: {
Simple,
Composition,
}
}
</script>
<style>
.error {
color: red;
}
</style>
<style scoped>
.component-item {
cursor: pointer;
}
</style>
================================================
FILE: example/examples/Composition.vue
================================================
<template>
<div>
<form @submit="finalForm.handleSubmit">
<div>
<input
:name="emailField.name"
:value="emailField.value"
v-on="emailField.events"
type="email"
/>
<span
class="error"
v-if="emailField.touched && emailField.error">
{{ emailField.error }}
</span>
</div>
<div>
<input
:name="passwordField.name"
:value="passwordField.value"
v-on="passwordField.events"
type="password"
/>
<span
class="error"
v-if="passwordField.touched && passwordField.error">
{{ passwordField.error }}
</span>
</div>
<div>
<input
:name="confirmPasswordField.name"
:value="confirmPasswordField.value"
v-on="confirmPasswordField.events"
type="password"
/>
<span
class="error"
v-if="confirmPasswordField.touched && confirmPasswordField.error">
{{ confirmPasswordField.error }}
</span>
</div>
<button type="submit" :disabled="formState.submitting || null">
{{ formState.submitting ? 'Submitting' : 'Submit' }}
</button>
</form>
<pre v-if="formState"><code>form state:<br><br>{{ JSON.stringify(formState, null, 2) }}</code></pre>
</div>
</template>
<script setup>
import { useForm, useField } from 'vue-final-form'
function sleep(timeout) {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, timeout)
})
}
const required = v =>
v ? null : 'This field is required!'
const matchedPassword = (value, values) =>
value === values.password ? null : 'Mismatched password!'
const range = (min, max) =>
value =>
value && value.length >= min && value.length <= max ? null : `Password should be between length ${min} and ${max}`
const noSpecialChars = (v) =>
/[!@#$%^&*()]/.test(v) ? 'Please do not use special chars' : null
const initialValues = {
email: 'egoist@boring.com',
}
const handleSubmit = async state => {
await sleep(2000)
console.log(state)
}
const { finalForm, formState } = useForm({
initialValues,
onSubmit: handleSubmit,
})
const { fieldState: emailField } = useField({
finalForm,
name: 'email',
validate: required,
})
const { fieldState: passwordField } = useField({
finalForm,
name: 'password',
validate: [range(6, 20), noSpecialChars],
})
const { fieldState: confirmPasswordField } = useField({
finalForm,
name: 'confirmPassword',
validate: matchedPassword,
})
</script>
================================================
FILE: example/examples/Simple.vue
================================================
<template>
<div>
<FinalForm
:submit="handleSubmit"
@change="updateState"
:initialValues="initialValues">
<template v-slot="props">
<form @submit="props.handleSubmit">
<FinalField name="email" :validate="required">
<template v-slot="props">
<div>
<input
v-on="props.events"
:name="props.name"
:value="props.value"
type="email"
/>
<span
class="error"
v-if="props.meta.touched && props.meta.error">
{{ props.meta.error }}
</span>
</div>
</template>
</FinalField>
<FinalField name="password" :validate="[range(6, 20), noSpecialChars]">
<template v-slot="props">
<div>
<input
v-on="props.events"
:name="props.name"
:value="props.value"
type="password"
/>
<span
class="error"
v-if="props.meta.touched && props.meta.error">
{{ props.meta.error }}
</span>
</div>
</template>
</FinalField>
<FinalField name="confirmPassword" :validate="matchedPassword">
<template v-slot="props">
<div>
<input
v-on="props.events"
:name="props.name"
:value="props.value"
type="password"
/>
<span
class="error"
v-if="props.meta.touched && props.meta.error">
{{ props.meta.error }}
</span>
</div>
</template>
</FinalField>
<button type="submit" :disabled="props.submitting || null">
{{ props.submitting ? 'Submitting' : 'Submit' }}
</button>
</form>
</template>
</FinalForm>
<pre v-if="formState"><code>form state:<br><br>{{ JSON.stringify(formState, null, 2) }}</code></pre>
</div>
</template>
<script>
import { FinalForm, FinalField } from 'vue-final-form'
function sleep(timeout) {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, timeout);
})
}
export default {
components: {
FinalForm,
FinalField
},
data() {
return {
formState: null,
initialValues: {
email: 'egoist@boring.com'
}
}
},
methods: {
async handleSubmit(state) {
await sleep(2000)
console.log(state)
},
updateState(state) {
this.formState = state
},
required(v) {
return v ? null : 'This field is required!'
},
matchedPassword(value, values) {
return value === values.password ? null : 'Mismatched password!'
},
range(min, max) {
return value => {
return value && value.length >= min && value.length <= max ? null : `Password should be between length ${min} and ${max}`
}
},
noSpecialChars(v) {
return /[!@#$%^&*()]/.test(v) ? 'Please do not use specfial chars' : null
}
}
}
</script>
================================================
FILE: example/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue-Final-Form</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/index.js"></script>
</body>
</html>
================================================
FILE: example/index.js
================================================
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
================================================
FILE: example/package.json
================================================
{
"name": "example",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"final-form": "^4.0.0",
"vue": "^3.0.0",
"vue-router": "^4.0.0",
"vue-final-form": "latest"
}
}
================================================
FILE: example/router.js
================================================
import { h } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import App from './App.vue'
export default createRouter({
history: createWebHistory(),
routes: [{
path: '/',
component: {
name: 'Home',
render: () => h(App)
}
}]
})
================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue-Final-Form</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@egojump/docup/dist/docup.css">
</head>
<body>
<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>
<div id="app"></div>
<script src="https://cdn.jsdelivr.net/npm/@egojump/docup/dist/docup.js"></script>
<script>
var doc = new Docup()
doc.start('#app')
</script>
</body>
</html>
================================================
FILE: package.json
================================================
{
"name": "vue-final-form",
"version": "4.0.1",
"description": "Subscription-based form state management for Vue.js",
"repository": {
"url": "egoist/vue-final-form",
"type": "git"
},
"main": "dist/index.js",
"module": "dist/index.esm.js",
"unpkg": "dist/index.umd.js",
"jsdelivr": "dist/index.umd.js",
"files": [
"dist"
],
"scripts": {
"test": "npm run lint && echo 'no tests!'",
"lint": "xo",
"prepublishOnly": "npm run build",
"build": "bili --format esm,cjs,umd,umd-min --module-name VueFinalForm --global.final-form final-form --global.vue vue",
"example": "vite",
"build:example": "vite build",
"gh": "gh-pages -d example/dist",
"deploy": "npm run build:example && npm run gh"
},
"author": "egoist <0x142857@gmail.com>",
"license": "MIT",
"devDependencies": {
"@vitejs/plugin-vue": "^1.10.0",
"bili": "^5.0.5",
"eslint-config-rem": "^4.0.0",
"final-form": "^4.0.0",
"gh-pages": "^1.0.0",
"vite": "^2.0.0",
"vue": "^3.0.0",
"vue-router": "^4.0.0",
"xo": "^0.47.0"
},
"xo": {
"extends": "rem",
"envs": [
"browser"
],
"ignores": [
"example/**"
],
"rules": {
"unicorn/filename-case": 0,
"unicorn/prefer-export-from": 0,
"import/prefer-default-export": 0
}
},
"peerDependencies": {
"final-form": "^4.0.0"
}
}
================================================
FILE: src/Field.js
================================================
import { getChildren } from './utils.js'
import useField from './useField.js'
const FinalField = {
name: 'final-field',
props: {
name: {
required: true,
type: String,
},
validate: [Function, Array],
subscription: Object,
},
setup(props) {
const { finalForm, fieldState } = useField({
name: props.name,
subscription: props.subscription,
validate: props.validate,
})
return {
finalForm,
fieldState,
}
},
watch: {
fieldState(state) {
this.$emit('change', state)
},
},
render() {
const {
blur,
change,
focus,
value,
name,
...meta
} = this.fieldState
const children = this.$slots.default({
events: this.fieldState.events,
change,
value,
name,
meta,
})
return getChildren(children)[0]
},
}
export default FinalField
================================================
FILE: src/Form.js
================================================
import { h } from 'vue'
import { getChildren } from './utils.js'
import useForm from './useForm.js'
const FinalForm = {
name: 'final-form',
props: {
initialValues: Object,
submit: {
type: Function,
default: () => {},
},
subscription: Object,
validate: [Function, Array],
},
setup(props) {
const { finalForm, formState } = useForm({
initialValues: props.initialValues,
validate: props.validate,
subscription: props.subscription,
onSubmit: props.submit,
})
return {
finalForm,
formState,
}
},
methods: {
handleSubmit(event) {
event && event.preventDefault()
this.finalForm.submit()
},
},
watch: {
formState(state) {
this.$emit('change', state)
},
},
render() {
const children = this.$slots.default
? this.$slots.default({
...this.formState,
...this.finalForm,
})
: this.$slots.default
return h('div', null, getChildren(children))
},
}
export default FinalForm
================================================
FILE: src/index.js
================================================
import FinalForm from './Form.js'
import FinalField from './Field.js'
import useForm from './useForm.js'
import useField from './useField.js'
export {
FinalForm,
FinalField,
useForm,
useField,
}
================================================
FILE: src/useCurrentForm.js
================================================
import { inject } from 'vue'
const useCurrentForm = () => {
const finalForm = inject('finalForm')
if (!finalForm) {
throw new Error('Form was\'t found. Please provide it using `<FinalForm>` component or `useForm` hook.')
}
return finalForm
}
export default useCurrentForm
================================================
FILE: src/useField.js
================================================
import { fieldSubscriptionItems } from 'final-form'
import { ref, onUnmounted, computed } from 'vue'
import useCurrentForm from './useCurrentForm.js'
import { composeFieldValidators, makeSubscriptionObject } from './utils.js'
const defaultSubscription = makeSubscriptionObject(fieldSubscriptionItems)
const useField = config => {
const finalForm = config.finalForm || useCurrentForm()
const fieldState = ref({})
const unregister = finalForm.value.registerField(config.name, state => {
fieldState.value = state
}, config.subscription || defaultSubscription, {
getValidator: Array.isArray(config.validate) ? composeFieldValidators(config.validate) : () => config.validate,
})
onUnmounted(unregister)
return {
fieldState: computed(() => ({
...fieldState.value,
events: {
input: event => fieldState.value.change(event.target.value),
focus: () => fieldState.value.focus(),
blur: () => fieldState.value.blur(),
},
})),
unregister,
}
}
export default useField
================================================
FILE: src/useForm.js
================================================
import { provide, ref, onUnmounted } from 'vue'
import { createForm, formSubscriptionItems } from 'final-form'
import { composeFormValidators, makeSubscriptionObject } from './utils.js'
const defaultSubscription = makeSubscriptionObject(formSubscriptionItems)
const useForm = config => {
const formApi = createForm({
initialValues: config.initialValues,
validate: Array.isArray(config.validate) ? composeFormValidators(config.validate) : config.validate,
onSubmit: config.onSubmit,
})
const finalForm = ref({
...formApi,
handleSubmit: event => {
event && event.preventDefault()
formApi.submit()
},
})
const formState = ref()
const unsubscribe = finalForm.value.subscribe(state => {
formState.value = state
}, config.subscription || defaultSubscription)
onUnmounted(unsubscribe)
provide('finalForm', finalForm)
return {
finalForm,
formState,
unsubscribe,
}
}
export default useForm
================================================
FILE: src/utils.js
================================================
export const getChildren = children =>
Array.isArray(children) ? children : [children]
const composeValidators = (validators, ...args) =>
// eslint-disable-next-line unicorn/no-array-reduce
validators.reduce((error, validator) => error || validator(...args), undefined)
export const composeFormValidators = validators => (...args) =>
composeValidators(validators, ...args)
export const composeFieldValidators = validators => () => (...args) =>
composeValidators(validators, ...args)
export const makeSubscriptionObject = subscriptionItems =>
// eslint-disable-next-line no-use-extend-native/no-use-extend-native
Object.fromEntries(subscriptionItems.map(key => [key, true]))
================================================
FILE: vite.config.js
================================================
import path from 'node:path'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
root: 'example',
resolve: {
alias: {
'vue-final-form': path.resolve('./src/index.js'),
},
},
plugins: [vue()],
})
gitextract_6qdo1odu/ ├── .editorconfig ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── circle.yml ├── example/ │ ├── App.vue │ ├── examples/ │ │ ├── Composition.vue │ │ └── Simple.vue │ ├── index.html │ ├── index.js │ ├── package.json │ └── router.js ├── index.html ├── package.json ├── src/ │ ├── Field.js │ ├── Form.js │ ├── index.js │ ├── useCurrentForm.js │ ├── useField.js │ ├── useForm.js │ └── utils.js └── vite.config.js
SYMBOL INDEX (7 symbols across 2 files)
FILE: src/Field.js
method setup (line 16) | setup(props) {
method fieldState (line 30) | fieldState(state) {
method render (line 35) | render() {
FILE: src/Form.js
method setup (line 18) | setup(props) {
method handleSubmit (line 33) | handleSubmit(event) {
method formState (line 40) | formState(state) {
method render (line 45) | render() {
Condensed preview — 23 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (29K chars).
[
{
"path": ".editorconfig",
"chars": 187,
"preview": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ni"
},
{
"path": ".gitattributes",
"chars": 12,
"preview": "* text=auto\n"
},
{
"path": ".gitignore",
"chars": 19,
"preview": "node_modules\ndist/\n"
},
{
"path": "LICENSE",
"chars": 1110,
"preview": "The MIT License (MIT)\n\nCopyright (c) EGOIST <0x142857@gmail.com> (https://egoist.moe)\n\nPermission is hereby granted, fre"
},
{
"path": "README.md",
"chars": 7812,
"preview": "# vue-final-form\n\n<!-- hide-on-docup-start -->\n\n[\n\na"
},
{
"path": "example/package.json",
"chars": 221,
"preview": "{\n \"name\": \"example\",\n \"version\": \"1.0.0\",\n \"main\": \"index.js\",\n \"license\": \"MIT\",\n \"dependencies\": {\n \"final-fo"
},
{
"path": "example/router.js",
"chars": 280,
"preview": "import { h } from 'vue'\nimport { createRouter, createWebHistory } from 'vue-router'\nimport App from './App.vue'\n\nexport "
},
{
"path": "index.html",
"chars": 2077,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, in"
},
{
"path": "package.json",
"chars": 1393,
"preview": "{\n \"name\": \"vue-final-form\",\n \"version\": \"4.0.1\",\n \"description\": \"Subscription-based form state management for Vue.j"
},
{
"path": "src/Field.js",
"chars": 911,
"preview": "import { getChildren } from './utils.js'\nimport useField from './useField.js'\n\nconst FinalField = {\n name: 'final-field"
},
{
"path": "src/Form.js",
"chars": 1049,
"preview": "import { h } from 'vue'\nimport { getChildren } from './utils.js'\nimport useForm from './useForm.js'\n\nconst FinalForm = {"
},
{
"path": "src/index.js",
"chars": 204,
"preview": "import FinalForm from './Form.js'\nimport FinalField from './Field.js'\nimport useForm from './useForm.js'\nimport useField"
},
{
"path": "src/useCurrentForm.js",
"chars": 288,
"preview": "import { inject } from 'vue'\n\nconst useCurrentForm = () => {\n const finalForm = inject('finalForm')\n\n if (!finalForm) "
},
{
"path": "src/useField.js",
"chars": 1038,
"preview": "import { fieldSubscriptionItems } from 'final-form'\nimport { ref, onUnmounted, computed } from 'vue'\nimport useCurrentFo"
},
{
"path": "src/useForm.js",
"chars": 964,
"preview": "import { provide, ref, onUnmounted } from 'vue'\nimport { createForm, formSubscriptionItems } from 'final-form'\nimport { "
},
{
"path": "src/utils.js",
"chars": 692,
"preview": "export const getChildren = children =>\n Array.isArray(children) ? children : [children]\n\nconst composeValidators = (val"
},
{
"path": "vite.config.js",
"chars": 299,
"preview": "import path from 'node:path'\nimport { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\n\n// https://vitejs"
}
]
About this extraction
This page contains the full source code of the egoist/vue-final-form GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 23 files (25.9 KB), approximately 7.5k tokens, and a symbol index with 7 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.