{{ value }}
+ {{ value }}
+ {{ value }}
+ {{ value }}
+ {{ value }}
+ {{ value }}
+ {{ value }}
+
+ This form cannot be successfully submitted because
+ the form fields do not match the provided schema.
+ This is done to illustrate hydration of form-level errors.
+ Do not actually do this. :)
+
{{ value }}
+ createInput().
::
-::ExampleCard
----
-href: "https://formkit.link/68e51abc7431687edc8f9ff008477b3d"
-title: "A currency input with currency selector"
----
-A simple custom input that allows users to select a currency and enter a value. Made using createInput().
-::
-
::ExampleCard
---
href: "https://formkit.link/de8e6530b80717f5229813258e739e7b"
diff --git a/getting-started/community.md b/getting-started/community.md
index 4f31b57b..5194bd87 100644
--- a/getting-started/community.md
+++ b/getting-started/community.md
@@ -39,25 +39,7 @@ Community involvement and contributions are one of the most important aspects of
/>
-### Silver sponsors
-
-
-
+
+
### Backers
[uscreen](https://uscreen.de), [gfenn08](https://github.com/gfenn08), [Ryan E](https://github.com/VikingDadMedic), [JoΓ£o Bondim](https://github.com/JesterIruka)
diff --git a/inputs/autocomplete.md b/inputs/autocomplete.md
index 771c9365..57616adb 100644
--- a/inputs/autocomplete.md
+++ b/inputs/autocomplete.md
@@ -353,6 +353,21 @@ file: [
---
::
+## Option groups
+
+If you would like the listitems in the listbox to be grouped, pass the `options` prop an array of objects and include the property `group`:
+
+::Example
+---
+name: "Autocomplete"
+min-height: 550
+file: [
+ "_content/_examples/autocomplete-pro/autocomplete-option-groups.vue",
+ "_content/_examples/_data/citiesByState.js"
+]
+---
+::
+
## Full example
@@ -838,14 +853,12 @@ schema: [
---
::
-### Inner listbox structure
-
-Below is the inner options list (`listbox`) structure from the diagrams above:
+### Listbox structure
::FormKitInputDiagram
---
hide-on-small: true
-class: "input-diagram--autocomplete"
+class: "input-diagram--dropdown"
schema: [
{
name: "dropdownWrapper",
@@ -865,20 +878,108 @@ schema: [
]
},
{
- name: "listitem",
- class: "flex flex-grow",
- position: "right",
+ name: "listitems",
+ children: [{
+ name: 'listitem',
+ class: "flex flex-grow",
+ position: "right",
+ children: [
+ {
+ name: "selectedIcon",
+ content: "βοΈ"
+ },
+ {
+ name: "option",
+ content: "Gray",
+ position: "right",
+ class: "flex flex-grow"
+ },
+ ]
+ }]
+ },
+ {
+ name: "loadMore",
children: [
{
- name: "selectedIcon",
- content: "βοΈ"
+ name: "loadMoreInner",
+ children: [
+ {
+ name: "loaderIcon",
+ content: "β³",
+ class: "text-center"
+ }
+ ]
},
+ ]
+ },
+ ]
+ },
+ ]
+ }
+]
+---
+::
+
+### Grouped Listbox Structure
+
+::FormKitInputDiagram
+---
+hide-on-small: true
+class: "input-diagram--dropdown"
+schema: [
+ {
+ name: "dropdownWrapper",
+ position: "right",
+ children: [
+ {
+ name: "listbox",
+ position: "right",
+ children: [
+ {
+ name: "emptyMessage",
+ children: [
{
- name: "option",
- content: "Serena",
- position: "right",
- class: "flex flex-grow"
- },
+ name: "emptyMessageInner",
+ content: "No options to display.",
+ }
+ ]
+ },
+ {
+ name: "listitems",
+ children: [
+ {
+ name: 'listitemGroup',
+ children: [
+ {
+ name: 'groupLabel'
+ },
+ {
+ name: 'groupList',
+ children: [
+ {
+ name: 'innerListitems',
+ children: [{
+ name: 'listitem',
+ class: "flex flex-grow",
+ position: "right",
+ children: [
+ {
+ name: "selectedIcon",
+ content: "βοΈ"
+ },
+ {
+ name: "option",
+ content: "Gray",
+ position: "right",
+ class: "flex flex-grow"
+ },
+ ]
+ }]
+ }
+ ]
+ }
+ ]
+ }
]
},
{
diff --git a/inputs/colorpicker.md b/inputs/colorpicker.md
index 92e06939..4805ef12 100644
--- a/inputs/colorpicker.md
+++ b/inputs/colorpicker.md
@@ -51,6 +51,18 @@ There may be times where you wish to show one format to users as a default but r
---
::
+### Disabling the alpha channel
+
+You can disable the alpha channel in the `colorpicker` by setting the `alpha` prop to `false`. This will enforce full opacity on values. Any values with an alpha channel will be converted to full opacity.
+
+::Example
+---
+ name: "Color input"
+ file: "_content/_examples/colorpicker/alpha.vue"
+ minHeight: 400
+---
+::
+
## Swatches
Swatches are provided using the `options` prop and use the same `options` API as the `select`, `dropdown`, `autocomplete` and `taglist` inputs from FormKit.
@@ -138,6 +150,12 @@ data: [
default: "hex",
description: "The format that the colorpicker input should show in the input preview and be the default setting for the picker panel input. Can be set to hex, hsla or rgba.",
},
+ {
+ prop: "alpha",
+ type: "boolean",
+ default: "true",
+ description: "When set to false the alpha channel will be disabled in the color picker.",
+ },
{
prop: "value-format",
type: "string",
diff --git a/inputs/currency.md b/inputs/currency.md
new file mode 100644
index 00000000..c9011c33
--- /dev/null
+++ b/inputs/currency.md
@@ -0,0 +1,155 @@
+---
+title: Currency
+description: The currency input is used to create locale/currency specific parsed content
+---
+
+::InputPageHero
+---
+type: "Currency"
+---
+::
+
+:PageToc
+
+:ProInstallSnippet
+
+## Basic example
+
+The `currency` input allows for restricted input of values based on the provided currency and locale.
+
+::Example
+---
+name: "Currency input defaults"
+file: "_content/_examples/currency/currency-default.vue"
+---
+::
+
+## Props
+
+### Currency
+
+The `currency` prop is the three letter currency code and sets the proper formatting for the specified currency. For a full list of supported currency types check: https://en.wikipedia.org/wiki/ISO_4217#List_of_ISO_4217_currency_codes
+
+### Locale
+
+By default, the locale used will be that of what is set in your `formkit.config.ts`. This can be overriden with the `display-locale` prop. For example, `en`, `de`, `en-IN`, `zh-CN`, etc, etc.
+
+
+::Example
+---
+name: "Currency and Locale settings"
+file: "_content/_examples/currency/currency-simple-locale.vue"
+---
+::
+
+## Currency Additional Props
+
+### Decimals
+
+If you would like to prevent decimals from being displayed, set `decimals` to `false` or `0`.
+
+You can also choose to set a minimum number of decimals with the `min-decimals` prop. For example, setting the prop `min-decimals="2"` will always show 2 decimal points.
+
+### Minimum and Maximum Values
+
+Setting the props `min` or `max` will prevent entry of a higher or lower value.
+
+### Step
+
+Value scan be incremented and decremented by use of the up/down keys. The degree by which the value changes is determined by the `step` prop.
+
+::Example
+---
+name: "Currecy Prop Exploration"
+file: "_content/_examples/currency/currency-props-explore.vue"
+---
+::
+
+## Props & Attributes
+
+::ReferenceTable
+---
+input: "currency"
+data: [
+ {
+ prop: "currency",
+ type: "string",
+ default: "USD",
+ description: "Set the specified currency to use for this input"
+ },
+ {
+ prop: "displayLocale",
+ type: "string",
+ default: "en-US",
+ description: "Set the desired display locale to use for this input"
+ },
+ {
+ prop: "decimals",
+ type: "boolean|number",
+ default: "null",
+ description: "Choose to either completely disallow decimals or override the maximum number of decimals for the input"
+ },
+ {
+ prop: "minDecimals",
+ type: "number",
+ default: "null",
+ description: "Choose to show a minimum number of decimals should your input require this"
+ },
+ {
+ prop: "min",
+ type: "number",
+ default: "null",
+ description: "Minimum numeric value allowed. If zero or above, negatives will not be allowed"
+ },
+ {
+ prop: "max",
+ type: "number",
+ default: "null",
+ description: "Maximum numeric value allowed for this input"
+ },
+ {
+ prop: "step",
+ type: "number",
+ default: "1",
+ description: "When using the up/down keys, how much to modify the current value."
+ },
+ {
+ prop: "valueFormat",
+ type: "string",
+ default: "number",
+ description: "Choose between number and string whether you want a numeric value or a parsed string value"
+ },
+]
+---
+::
+
+
+## Sections
+
+:SectionKeysIntro
+
+### Currency input diagram
+
+::FormKitInputDiagram
+---
+prefix-icon-content: ""
+suffix-icon-content: ""
+label-content: "Currency Input"
+input-content: "$1,234.56"
+help-content: "How much do you want to donate?"
+message-content: "That's a lot of money"
+---
+::
+### Keyboard Interactions
+
+::KeyboardEventsTable
+---
+data: [
+ {
+ event: ["up", "down"],
+ separator: '',
+ description: "Increments through input value by current step amount.",
+ },
+]
+---
+::
\ No newline at end of file
diff --git a/inputs/dropdown.md b/inputs/dropdown.md
index 53a92ea5..ebc026f2 100644
--- a/inputs/dropdown.md
+++ b/inputs/dropdown.md
@@ -306,6 +306,22 @@ file: [
---
::
+## Option groups
+
+If you would like the listitems in the listbox to be grouped, pass the `options` prop an array of objects and include the property `group`:
+
+::Example
+---
+name: "Dropdown"
+min-height: 550
+file: [
+ "_content/_examples/dropdown/dropdown-option-groups.vue",
+ "_content/_examples/_data/citiesByState.js"
+]
+---
+::
+
+
## Props & Attributes
::ReferenceTable
@@ -538,20 +554,108 @@ schema: [
]
},
{
- name: "listitem",
- class: "flex flex-grow",
- position: "right",
+ name: "listitems",
+ children: [{
+ name: 'listitem',
+ class: "flex flex-grow",
+ position: "right",
+ children: [
+ {
+ name: "selectedIcon",
+ content: "βοΈ"
+ },
+ {
+ name: "option",
+ content: "Gray",
+ position: "right",
+ class: "flex flex-grow"
+ },
+ ]
+ }]
+ },
+ {
+ name: "loadMore",
children: [
{
- name: "selectedIcon",
- content: "βοΈ"
+ name: "loadMoreInner",
+ children: [
+ {
+ name: "loaderIcon",
+ content: "β³",
+ class: "text-center"
+ }
+ ]
},
+ ]
+ },
+ ]
+ },
+ ]
+ }
+]
+---
+::
+
+### Grouped Listbox Structure
+
+::FormKitInputDiagram
+---
+hide-on-small: true
+class: "input-diagram--dropdown"
+schema: [
+ {
+ name: "dropdownWrapper",
+ position: "right",
+ children: [
+ {
+ name: "listbox",
+ position: "right",
+ children: [
+ {
+ name: "emptyMessage",
+ children: [
{
- name: "option",
- content: "Gray",
- position: "right",
- class: "flex flex-grow"
- },
+ name: "emptyMessageInner",
+ content: "No options to display.",
+ }
+ ]
+ },
+ {
+ name: "listitems",
+ children: [
+ {
+ name: 'listitemGroup',
+ children: [
+ {
+ name: 'groupLabel'
+ },
+ {
+ name: 'groupList',
+ children: [
+ {
+ name: 'innerListitems',
+ children: [{
+ name: 'listitem',
+ class: "flex flex-grow",
+ position: "right",
+ children: [
+ {
+ name: "selectedIcon",
+ content: "βοΈ"
+ },
+ {
+ name: "option",
+ content: "Gray",
+ position: "right",
+ class: "flex flex-grow"
+ },
+ ]
+ }]
+ }
+ ]
+ }
+ ]
+ }
]
},
{
diff --git a/inputs/mask.md b/inputs/mask.md
index 71fbee8b..fa1c433c 100644
--- a/inputs/mask.md
+++ b/inputs/mask.md
@@ -319,24 +319,6 @@ label: "Values can't match the mask"
Your prefix and suffix content can't match the mask. For instance, if your mask has a digit token #, your prefix/suffix can't contain numbers.
::
-## Running the mask in reverse
-
-In specific circumstances, you may want to run your mask in reverse. The mask will test if user input fulfills the mask from right to left. This is common in currency-type inputs and can be applied by adding the `reverse` prop:
-
-::Example
----
-name: "Mask input"
-file: "_content/_examples/mask/reverse.vue"
----
-::
-
-::Callout
----
-type: "warning"
-label: "Shift mode requirement"
----
-Running a mask in reverse only works in shift mode.
-::
## Mask values
diff --git a/inputs/taglist.md b/inputs/taglist.md
index 1aa4afda..9cb4df61 100644
--- a/inputs/taglist.md
+++ b/inputs/taglist.md
@@ -266,6 +266,22 @@ file: [
---
::
+## Option groups
+
+If you would like the listitems in the listbox to be grouped, pass the `options` prop an array of objects and include the property `group`:
+
+::Example
+---
+name: "Taglist"
+min-height: 550
+file: [
+ "_content/_examples/taglist/taglist-option-groups.vue",
+ "_content/_examples/_data/citiesByState.js"
+]
+---
+::
+
+
## Full example
@@ -469,6 +485,229 @@ data: [
---
::
+## Sections
+
+:SectionKeysIntro
+
+### Listbox structure
+
+::FormKitInputDiagram
+---
+hide-on-small: true
+class: "input-diagram--dropdown"
+schema: [
+ {
+ name: "dropdownWrapper",
+ position: "right",
+ children: [
+ {
+ name: "listbox",
+ position: "right",
+ children: [
+ {
+ name: "emptyMessage",
+ children: [
+ {
+ name: "emptyMessageInner",
+ content: "No options to display.",
+ }
+ ]
+ },
+ {
+ name: "listitems",
+ children: [{
+ name: 'listitem',
+ class: "flex flex-grow",
+ position: "right",
+ children: [
+ {
+ name: "selectedIcon",
+ content: "βοΈ"
+ },
+ {
+ name: "option",
+ content: "Gray",
+ position: "right",
+ class: "flex flex-grow"
+ },
+ ]
+ }]
+ },
+ {
+ name: "loadMore",
+ children: [
+ {
+ name: "loadMoreInner",
+ children: [
+ {
+ name: "loaderIcon",
+ content: "β³",
+ class: "text-center"
+ }
+ ]
+ },
+ ]
+ },
+ ]
+ },
+ ]
+ }
+]
+---
+::
+
+### Grouped Listbox Structure
+
+::FormKitInputDiagram
+---
+hide-on-small: true
+class: "input-diagram--dropdown"
+schema: [
+ {
+ name: "dropdownWrapper",
+ position: "right",
+ children: [
+ {
+ name: "listbox",
+ position: "right",
+ children: [
+ {
+ name: "emptyMessage",
+ children: [
+ {
+ name: "emptyMessageInner",
+ content: "No options to display.",
+ }
+ ]
+ },
+ {
+ name: "listitems",
+ children: [
+ {
+ name: 'listitemGroup',
+ children: [
+ {
+ name: 'groupLabel'
+ },
+ {
+ name: 'groupList',
+ children: [
+ {
+ name: 'innerListitems',
+ children: [{
+ name: 'listitem',
+ class: "flex flex-grow",
+ position: "right",
+ children: [
+ {
+ name: "selectedIcon",
+ content: "βοΈ"
+ },
+ {
+ name: "option",
+ content: "Gray",
+ position: "right",
+ class: "flex flex-grow"
+ },
+ ]
+ }]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ name: "loadMore",
+ children: [
+ {
+ name: "loadMoreInner",
+ children: [
+ {
+ name: "loaderIcon",
+ content: "β³",
+ class: "text-center"
+ }
+ ]
+ },
+ ]
+ },
+ ]
+ },
+ ]
+ }
+]
+---
+::
+
+#### Taglist selections
+
+::FormKitInputDiagram
+---
+hide-on-small: true
+class: "input-diagram--dropdown-outer"
+schema: [
+ {
+ name: "selector",
+ class: "flex flex-grow",
+ children: [
+ {
+ name: "tagsWrapper",
+ class: "flex flex-grow",
+ children: [
+ {
+ name: "tags",
+ class: "flex flex-grow border-solid",
+ children: [
+ {
+ name: "tagWrapper",
+ children: [
+ {
+ name: "tag",
+ class: 'flex',
+ children: [
+ {
+ name: "tagLabel",
+ content: 'Gray'
+ },
+ {
+ name: "removeSelection",
+ content: 'Γ'
+ }
+ ]
+ },
+ ]
+ },
+ {
+ name: "tagWrapper",
+ children: [
+ {
+ name: "tag",
+ class: 'flex',
+ children: [
+ {
+ name: "tagLabel",
+ content: 'Blue'
+ },
+ {
+ name: "removeSelection",
+ content: 'Γ'
+ }
+ ]
+ },
+ ]
+ }
+ ],
+ },
+ ]
+ },
+ ]
+ }
+]
+---
+::
+
## Accessibility
All FormKit inputs are designed with the following accessibility considerations in mind. Help us continually improve accessibility for all by filing accessibility issues [here](https://github.com/formkit/formkit/issues/new?assignees=&labels=%F0%9F%90%9B+bug-report%2C%E2%9B%91+Needs+triage&projects=&template=bug-report.yml):
diff --git a/plugins/typebox.md b/plugins/typebox.md
new file mode 100644
index 00000000..5f159236
--- /dev/null
+++ b/plugins/typebox.md
@@ -0,0 +1,88 @@
+---
+title: Typebox Plugin
+description: Use your Typebox schema to validate your FormKit forms.
+---
+
+# Typebox Plugin
+
+:PageToc
+
+With the `@formkit/typebox` package you can easily enable validation of your FormKit forms with your Typebox schema. This provides a convenient way to have isomorphic types and ensure that your front-end and back-end are using the same validation rules.
+
+When validating against a Typebox schema all validation errors will be mapped to their corresponding inputs, show or hide based on your form / input's `validation-visibility` prop, and prevent submission when form data does not pass validation with Typebox.
+
+## Installation
+
+To use this plugin with FormKit, install `@formkit/typebox`:
+
+```bash
+yarn add @formkit/typebox
+```
+
+Once you've installed the `@formkit/typebox` package, you'll need to register the plugin on a per-form basis and each form that requires validation with a Typebox schema will create a new instance of the plugin using the `createTypeboxPlugin` function.
+
+## Usage
+
+To use the Typebox plugin we need to import the `createTypeboxPlugin` function from `@formkit/typebox`, call the `createTypeboxPlugin` function to create receive our `typeboxPlugin` and `submitHandler`, and then add them both to our FormKit form.
+
+The `createTypeboxPlugin` function takes two arguments:
+
+- `typeboxSchema`: The Typebox schema that you would like to use to validate against the form.
+- `submitCallback`: a function you would like to run once validation has succeeded on your form βΒ this is where you would handle posting your data to your backend or other submit-related tasks. You form data will be provided with full TypeScript support based on your Typebox schema.
+
+The `createTypeboxPlugin` will return a tuple of:
+
+- `typeboxPlugin`: The plugin that should be applied to your target form's `plugins` prop.
+- `submitHandler`: The submit handler that should be attached to your form's `@submit` action. When the form data passes validation of your provided Typebox schema your `submitCallback` will fire.
+
+### For form validation
+
+Here is an example of using a Typebox schema to validate a FormKit form. It's important that your FormKit input `name`s match the expected values for your Typebox schema.
+
+::Example
+---
+name: 'Typebox Validation'
+file: [
+'/\_content/_examples/typebox/validation.vue',
+]
+import-map: '/\_content/_examples/typebox/importMap.json'
+---
+::
+
+Now your FormKit form will use your Typebox Schema for validation βΒ and all messages will adjacent to each matching FormKit just live native FormKit validation!
+
+### In addition to FormKit validation
+
+Using Typebox to validate your form doesn't mean you have to forgo using FormKit's built-in validation messages. If you add FormKit validation to your FormKit inputs then Typebox validation errors will only show if all FormKit validations have been satisfied and there are remaining unsatisfied Typebox validations.
+
+This has a few benefits:
+
+- You can use FormKit's built-in rules such as `confirm` which don't have easy-to-use equivalents within Typebox.
+- Your messages can be translated to one of the many existing languges in `@formkit/i18n` without any additional effort on your part.
+- The built-in FormKit validation messages are written to be contextually aware of your input names and knowing that they will be attached directly to their corresponding inputs β so they are more precise and easier to understand than their generic Typebox counterparts.
+
+Here's the same form as before, but now using FormKit validation messages in addition to Typebox schema validation.
+
+::Example
+---
+name: 'Typebox Validation with FormKit Validation'
+file: [
+'/\_content/_examples/typebox/with-formkit-validation.vue',
+]
+import-map: '/\_content/_examples/typebox/importMap.json'
+---
+::
+
+### For setting form errors
+
+If you need to set errors on your form you can do so with the `node.setTypeboxErrors` function that is made available by the `typeboxPlugin`. The `node.setTypeboxErrors` function accepts a Typebox `ValueErrorIterator` and will map the errors to each input. Any non-matching errors will be shown as form-level errors.
+
+::Example
+---
+name: 'Typebox Errors'
+file: [
+'/\_content/_examples/typebox/errors.vue',
+]
+import-map: '/\_content/_examples/typebox/importMap.json'
+---
+::