├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .npmrc ├── .storybook ├── fastform-background.png ├── favicon.ico ├── main.cjs ├── manager-head.html ├── manager.js ├── preview-head.html ├── preview.cjs └── theme.js ├── LICENSE ├── README.md ├── fastform-demo.gif ├── package-lock.json ├── package.json ├── src ├── app.html ├── lib │ ├── Checkbox │ │ ├── Checkbox.stories.svelte │ │ └── Checkbox.svelte │ ├── FastForm │ │ ├── FastForm.stories.mdx │ │ ├── FastForm.stories.svelte │ │ └── FastForm.svelte │ ├── Field │ │ ├── Field.stories.mdx │ │ └── Field.svelte │ ├── Input │ │ ├── Input.stories.svelte │ │ └── Input.svelte │ ├── Multiselect │ │ ├── Multiselect.stories.svelte │ │ └── Multiselect.svelte │ ├── Radio │ │ ├── Radio.stories.svelte │ │ └── Radio.svelte │ ├── Select │ │ ├── Select.stories.svelte │ │ └── Select.svelte │ ├── constants │ │ └── inputType.ts │ ├── index.ts │ ├── store.ts │ ├── types.ts │ └── validators │ │ ├── index.ts │ │ ├── isAlpha │ │ └── isAlpha.ts │ │ ├── isAlphaNumeric │ │ └── isAlphaNumeric.ts │ │ ├── isBase64 │ │ └── isBase64.ts │ │ ├── isCreditCard │ │ └── isCreditCard.ts │ │ ├── isDate │ │ └── isDate.ts │ │ ├── isFileSize │ │ └── isFileSize.ts │ │ ├── isInRange │ │ └── isInRange.ts │ │ ├── isJSON │ │ └── isJSON.ts │ │ ├── isMimeType │ │ └── isMimeType.ts │ │ ├── isNumber │ │ └── isNumber.ts │ │ ├── isPhoneNumberNA │ │ └── isPhoneNumberNA.ts │ │ ├── isStrongPassword │ │ └── isStrongPassword.ts │ │ ├── isTime │ │ └── isTime.ts │ │ ├── isURL │ │ └── isURL.ts │ │ ├── matchesPattern │ │ └── matchesPattern.ts │ │ ├── maxNumOptions │ │ ├── maxNumOptions.stories.svelte │ │ └── maxNumOptions.ts │ │ ├── minNumOptions │ │ ├── minNumOptions.stories.svelte │ │ └── minNumOptions.ts │ │ ├── mustMatch │ │ ├── mustMatch.stories.svelte │ │ └── mustMatch.ts │ │ ├── required │ │ ├── required.stories.svelte │ │ └── required.ts │ │ ├── validators.stories.mdx │ │ └── verifyEmail │ │ ├── verifyEmail.stories.svelte │ │ └── verifyEmail.ts ├── routes │ └── index.svelte └── stories │ ├── Getting Started │ └── GettingStarted.stories.mdx │ └── More Information │ ├── Changelog.stories.mdx │ └── Contributors.stories.mdx ├── svelte.config.js ├── tsconfig.json └── vite.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | storybook-static 10 | 11 | # Ignore files for PNPM, NPM and YARN 12 | pnpm-lock.yaml 13 | package-lock.json 14 | yarn.lock 15 | 16 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | plugins: ['svelte3', '@typescript-eslint'], 4 | overrides: [{ 5 | files: ['*.svelte', '*.stories.svelte'], 6 | processor: 'svelte3/svelte3' 7 | }], 8 | rules: { 9 | 'indent': ['warn', 2], 10 | 'no-unused-vars': ['off', { 11 | 'vars': 'local' 12 | }], 13 | 'prefer-const': 'warn', 14 | 'quotes': ['warn', 'single'], 15 | 'semi': ['warn', 'never'], 16 | 'space-infix-ops': 'warn' 17 | }, 18 | settings: { 19 | 'svelte3/typescript': () => require('typescript') 20 | }, 21 | extends: ['plugin:storybook/recommended'] 22 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | storybook-static -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /.storybook/fastform-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/FastForm/40980c900a54065a6f937c9649a0b11de87a98ea/.storybook/fastform-background.png -------------------------------------------------------------------------------- /.storybook/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/FastForm/40980c900a54065a6f937c9649a0b11de87a98ea/.storybook/favicon.ico -------------------------------------------------------------------------------- /.storybook/main.cjs: -------------------------------------------------------------------------------- 1 | const sveltePreprocess = require('svelte-preprocess'); 2 | 3 | module.exports = { 4 | svelteOptions: { 5 | preprocess: sveltePreprocess(), 6 | }, 7 | "stories": [ 8 | "../src/stories/Introduction/GettingStarted.stories.mdx", 9 | "../src/**/*.stories.mdx", 10 | "../src/lib/**/*.stories.svelte", 11 | ], 12 | "addons": [ 13 | "@storybook/addon-links",{ 14 | name: "@storybook/addon-essentials", 15 | options: { 16 | viewport: false, 17 | measure: false, 18 | backgrounds: false, 19 | outline: false 20 | } 21 | }, 22 | "@storybook/addon-svelte-csf" 23 | ], 24 | "framework": "@storybook/svelte", 25 | "core": { 26 | "builder": "@storybook/builder-vite" 27 | }, 28 | "features": { 29 | "storyStoreV7": false 30 | } 31 | } -------------------------------------------------------------------------------- /.storybook/manager-head.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.storybook/manager.js: -------------------------------------------------------------------------------- 1 | // .storybook/manager.js 2 | 3 | import { addons } from '@storybook/addons'; 4 | import theme from './theme'; 5 | 6 | addons.setConfig({ 7 | enableShortcuts: false, 8 | theme: theme, 9 | toolbar: { 10 | zoom: { hidden: true }, 11 | eject: { hidden: true }, 12 | grid: { hidden: true }, 13 | }, 14 | }); -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.storybook/preview.cjs: -------------------------------------------------------------------------------- 1 | export const parameters = { 2 | actions: { argTypesRegex: "^on[A-Z].*" }, 3 | controls: { 4 | matchers: { 5 | color: /(background|color)$/i, 6 | date: /Date$/, 7 | }, 8 | }, 9 | options: { 10 | storySort: { 11 | method: 'alphabetical', 12 | order: ['Introduction', 'FastForm', ['FastForm'], 'Field', ['Field'], 'Validators', ['Validators'], 'More Information'], 13 | locals: 'en-US' 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /.storybook/theme.js: -------------------------------------------------------------------------------- 1 | import { create } from '@storybook/theming'; 2 | import logo from './fastform-background.png' 3 | 4 | export default create({ 5 | base: 'light', 6 | 7 | colorPrimary: 'red', 8 | colorSecondary: '#5ca6e5', 9 | 10 | // UI 11 | appBg: '#efefef', 12 | appContentBg: 'white', 13 | appBorderColor: 'grey', 14 | appBorderRadius: 4, 15 | 16 | // Typography 17 | fontBase: '"Open Sans", sans-serif', 18 | fontCode: 'monospace', 19 | 20 | // Text colors 21 | textColor: 'black', 22 | textInverseColor: 'rgba(255,255,255,0.9)', 23 | 24 | // Toolbar default and active colors 25 | barTextColor: 'silver', 26 | barSelectedColor: 'black', 27 | barBg: '#f1f1f1', 28 | 29 | // Form colors 30 | inputBg: 'white', 31 | inputBorder: 'silver', 32 | inputTextColor: 'black', 33 | inputBorderRadius: 4, 34 | 35 | brandTitle: 'FastForm', 36 | brandUrl: 'https://github.com/oslabs-beta/FastForm', 37 | brandImage: logo, 38 | brandTarget: '_blank', 39 | }); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 OSLabs Beta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 5 | 6 | [![NPM Version](https://img.shields.io/npm/v/@fastform/form)](https://www.npmjs.com/package/@fastform/form) 7 | [![MIT License](https://img.shields.io/github/license/oslabs-beta/fastform)](https://github.com/oslabs-beta/FastForm/blob/dev/LICENSE) 8 | [![Downloads](https://img.shields.io/npm/dt/@fastform/form?color=skyblue)](https://www.npmjs.com/package/@fastform/form) 9 | [![Forks](https://img.shields.io/github/forks/oslabs-beta/fastform)](https://github.com/oslabs-beta/FastForm) 10 | [![Stars](https://img.shields.io/github/stars/oslabs-beta/fastform)](https://github.com/oslabs-beta/FastForm) 11 | 12 | 13 | 14 |
15 | 16 | # FastForm 17 | 18 | FastForm is a form component library designed for Svelte that includes different base components and various validator logic. FastForm's components come with no styling to allow developers more customization over the look of their applications. 19 | 20 | [🐍 Getting Started](https://getfastform.io/) | [📕 Documentation](https://getfastform.io) | [📰 Medium](https://medium.com/@steven.yuan91/30838499b1d6) 21 | 22 | ## Key Features 23 | * **Ease of Use:** Getting started with FastForm is simple. Visit our documentation website for a guide on the different components and form logic used. 24 | * **Default Field Components:** Ready to use components that will take in validator functions. 25 | * **Validators:** FastForm includes validator function examples but also gives developers the ability to create their own custom validator functions to pass onto components. 26 | * **Static Typing:** FastForm is written in TypeScript to establish static typing and improve the overall developer experience. 27 | * **Customizable Components:** FastForm does not come with component styling. The developer is free to style any components to their own liking. 28 | 29 | ## How To Install 30 | 31 | FastForm is available as an npm package. You can install it by running the following command in the terminal within your project directory: 32 | 33 | ```bash 34 | npm install @fastform/form 35 | ``` 36 | 37 | ## Contributors 38 | 39 | * Angel Cortes Gildo - [LinkedIn](https://www.linkedin.com/in/angel-cortes-gildo-708972243/) / [Github](https://github.com/angcortes) 40 | * Griffin Barlow - [LinkedIn](https://www.linkedin.com/in/griffinbrlw/) / [Github](https://github.com/griffin104) 41 | * Ilija Bibic - [LinkedIn](https://www.linkedin.com/in/ilija-bibic/) / [Github](https://github.com/ibibic) 42 | * Steven Yuan - [LinkedIn](https://www.linkedin.com/in/steven-yuann/) / [Github](https://github.com/steven-yuann) 43 | 44 | ## How To Contribute 45 | 46 | FastForm is a new open source project that has just begun development. There are many areas that have room for growth. We welcome all feedback and developer contributions. Below is a list of features and improvements that any open source developer can contribute to. If you have any additional concepts, feel free to discuss and implement them as well. 47 | 48 | * Asynchronous validation 49 | * Additional validator functions to be passed onto components 50 | * Larger component library 51 | * Custom developer error handling on validator functions 52 | 53 | 54 | ## License 55 | 56 | FastForm is developed under the [MIT license](https://github.com/oslabs-beta/FastForm/blob/dev/LICENSE). 57 | -------------------------------------------------------------------------------- /fastform-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/FastForm/40980c900a54065a6f937c9649a0b11de87a98ea/fastform-demo.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fastform/form", 3 | "version": "0.3.2", 4 | "scripts": { 5 | "dev": "vite dev", 6 | "build": "vite build", 7 | "package": "svelte-kit package", 8 | "preview": "vite preview", 9 | "prepare": "svelte-kit sync", 10 | "lint": "eslint . --fix", 11 | "storybook": "start-storybook -p 6006", 12 | "build-storybook": "build-storybook" 13 | }, 14 | "package": "svelte-kit package", 15 | "devDependencies": { 16 | "@babel/core": "^7.18.6", 17 | "@storybook/addon-actions": "^6.5.9", 18 | "@storybook/addon-essentials": "^6.5.9", 19 | "@storybook/addon-interactions": "^6.5.9", 20 | "@storybook/addon-links": "^6.5.9", 21 | "@storybook/addon-svelte-csf": "^2.0.5", 22 | "@storybook/builder-vite": "^0.1.39", 23 | "@storybook/svelte": "^6.5.9", 24 | "@storybook/testing-library": "^0.0.13", 25 | "@sveltejs/adapter-auto": "^1.0.0-next.5", 26 | "@sveltejs/kit": "next", 27 | "@typescript-eslint/eslint-plugin": "^5.31.0", 28 | "@typescript-eslint/parser": "^5.31.0", 29 | "babel-loader": "^8.2.5", 30 | "eslint": "^8.20.0", 31 | "eslint-config-standard": "^17.0.0", 32 | "eslint-plugin-import": "^2.26.0", 33 | "eslint-plugin-n": "^15.2.4", 34 | "eslint-plugin-promise": "^6.0.0", 35 | "eslint-plugin-storybook": "^0.6.2", 36 | "eslint-plugin-svelte3": "^4.0.0", 37 | "svelte": "^3.49.0", 38 | "svelte-check": "^2.8.0", 39 | "svelte-loader": "^3.1.3", 40 | "svelte-preprocess": "^4.10.7", 41 | "svelte2tsx": "^0.5.11", 42 | "tslib": "^2.4.0", 43 | "typescript": "^4.7.4", 44 | "vite": "^2.9.13" 45 | }, 46 | "exports": { 47 | ".": "./index.js", 48 | "./package.json": "./package.json", 49 | "./validators": "./validators/index.js", 50 | "./store.js": "./store.js", 51 | "./FastForm.svelte": "./FastForm/FastForm.svelte", 52 | "./Field.svelte": "./Field/Field.svelte" 53 | }, 54 | "type": "module" 55 | } 56 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/lib/Checkbox/Checkbox.stories.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 19 | 20 | 21 | 35 | 36 | 37 | 44 | -------------------------------------------------------------------------------- /src/lib/Checkbox/Checkbox.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | {#each values as value} 11 | 12 | 13 | {/each} -------------------------------------------------------------------------------- /src/lib/FastForm/FastForm.stories.mdx: -------------------------------------------------------------------------------- 1 | import { Canvas, Meta, Story } from '@storybook/addon-docs'; 2 | import FastForm from './FastForm.svelte' 3 | 4 | 5 | 6 | 7 | # FastForm 8 | 9 | `` is a necessary component that eases the process of creating a form. The event handlers are passed down as props within the `FastForm` component. 10 | 11 | ### Getting Started/Using FastForm Component 12 | 13 | 1.Declare your `` component within your markup section of your current component
14 | 15 | 2.Add initValues, handleSubmit, and validators as functions within the props of ``
16 | (You can declare the functions within the script tag or inline) 17 | 18 | ### Example 19 | ```jsx 20 | 37 | 38 | // Setting your FastForm component in the markup section with children 39 | 40 | // The children of FastForm would be placed here 41 | 42 | ``` 43 | 44 | 45 | ### Props 46 | 47 | | Name | Required| Type | Default | Description | 48 | |------|:-------:|------| :-----: |-------------| 49 | |initValues| No |Object | - | Object that declares the inital values of the Fields as [name:value] pairs 50 | |handleSubmit| No | Function| - | Function that defines what the form will do onSubmit. It takes one input which will be form state. Form state is a object which contains two objects labeled: values and errors. 51 | |handleChange| No | Function| - | Function that defines what the form will do onChange. It takes one input which will be form state. Form state is a object which contains two objects labeled: values and errors. 52 | |validate| No | Function | - | [Validation page](?path=/docs/validators-validators--page) -------------------------------------------------------------------------------- /src/lib/FastForm/FastForm.stories.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 18 | 19 | 20 | 44 | 45 | 46 | 62 | -------------------------------------------------------------------------------- /src/lib/FastForm/FastForm.svelte: -------------------------------------------------------------------------------- 1 | 34 | 35 |
36 | 37 |
38 | -------------------------------------------------------------------------------- /src/lib/Field/Field.stories.mdx: -------------------------------------------------------------------------------- 1 | import { Canvas, Meta, Story } from '@storybook/addon-docs'; 2 | 3 | import Field from './Field.svelte' 4 | import FastForm from '../FastForm/FastForm.svelte' 5 | 6 | 7 | 8 | # Field 9 | 10 | 11 | 12 | ## Before we begin 13 | `` is meant for selecting input types. The type of input must be specified, and you really need to wrap this within `` in order for us to manage your state. 14 | 15 | You have been warned! 16 | Please refer to the following documentations that may help you write the `` component. 17 | - [FastForm documentation](?path=/story/fastform-fastform--page) 18 | - [How to write validations](?path=/story/validators-validators--page) 19 | 20 | ## How to use `` 21 | 22 | `` component is like an `` field, it can take in any props, (class, id etc). In addition, table specifies the required and other fields that's unique to `` component 23 | 24 | | Name | Required | Type | Default | Description | 25 | | --- | --- | --- | --- | --- | 26 | | **name** | **Yes** | String | - | Name for the is specific field | 27 | | **type** | **Yes** | String | - | Specify the type of input field | 28 | | **values** | **Yes*** | Array | - | Values required for specific components. *See Types of Field Table for special types 29 | | **handleBlur** | No | Function | - | Instructions to be run onBlur, this function takes in an object that contains *`values`*, and *`errors`* object, and will trigger after validate function | 30 | | **handleChange** | No | Function | - | Instructions to be run OnChange, this function takes in an object that contains *`values`*, and *`errors`* object, and will trigger after validate function| 31 | | **validate** | No | Function | - | Any validators that is required to validate the *``* component. Refer to validator docs on how to write your own validations| 32 | | **validateOnBlur** | No | Boolean | `true` | Passed in validate function will run onBlur| 33 | | **validateOnChange** | No | Boolean | `!validateOnBlur` | Passed in validate function will run onChange | 34 | 35 | 36 | ## Types of Field 37 | 38 | The types of the input field needs to be passed in to the `type` prop. You can pass it in like how you would specify in an HTML `` field. But there are some special cases. 39 | 40 | | Types | Values Type | Example | 41 | | --- | --- | --- | 42 | | Regular types | - | ``| 43 | | [multiselect](?path=/story/field-multi-select--multi-select)** | Array | `` | 44 | | [radio](?path=/story/field-radio--radio)** | Array | `` | 45 | | [select](?path=/story/field-select--select)** | Array | `` | 46 | | [checkbox](?path=/story/field-checkbox--checkbox)** | Array | `` | 47 | ** Special input type that also require `values` prop to be passed in. 48 | ## Examples 49 | 50 | Below is an example of a creating a checkbox component using vanilla Svelte. 51 | ```jsx 52 | 59 | 60 | {#each iceCreamSelection as option} 61 | 62 | 63 | {/each} 64 | 65 | ``` 66 | 67 | In comparison, below is an example if you were to write this using FastForm. 68 | ```jsx 69 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | ``` 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/lib/Field/Field.svelte: -------------------------------------------------------------------------------- 1 | 64 | 65 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/lib/Input/Input.stories.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 19 | 20 | 21 | 35 | 36 | 37 | 43 | -------------------------------------------------------------------------------- /src/lib/Input/Input.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/lib/Multiselect/Multiselect.stories.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 19 | 20 | 21 | 35 | 36 | 37 | 44 | -------------------------------------------------------------------------------- /src/lib/Multiselect/Multiselect.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | -------------------------------------------------------------------------------- /src/lib/Radio/Radio.stories.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 19 | 20 | 21 | 35 | 36 | 37 | 44 | -------------------------------------------------------------------------------- /src/lib/Radio/Radio.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | {#each values as value} 13 | 14 | 15 | {/each} -------------------------------------------------------------------------------- /src/lib/Select/Select.stories.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 19 | 20 | 21 | 35 | 36 | 37 | 44 | -------------------------------------------------------------------------------- /src/lib/Select/Select.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /src/lib/constants/inputType.ts: -------------------------------------------------------------------------------- 1 | import Input from '../Input/Input.svelte' 2 | import Radio from '../Radio/Radio.svelte' 3 | import Checkbox from '../Checkbox/Checkbox.svelte' 4 | import Select from '../Select/Select.svelte' 5 | import Multiselect from '../Multiselect/Multiselect.svelte' 6 | 7 | export const typeSelect = { 8 | text: Input, 9 | email: Input, 10 | color: Input, 11 | number: Input, 12 | password: Input, //minlength option 13 | tel: Input, //pattern option 14 | button: Input, //value required 15 | date: Input, //min/max option 16 | 'datetime-local': Input, //min/max option 17 | file: Input, //accept types option 18 | hidden: Input, 19 | image: Input, 20 | month: Input, //min option 21 | reset: Input, 22 | search: Input, 23 | submit: Input, 24 | time: Input, 25 | url: Input, 26 | week: Input, 27 | radio: Radio, 28 | checkbox: Checkbox, 29 | select: Select, 30 | multiselect: Multiselect, 31 | range: Input 32 | } -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | //Import all rendering components 2 | import FastForm from './FastForm/FastForm.svelte' 3 | import Field from './Field/Field.svelte' 4 | 5 | //Export all rendering components 6 | export { FastForm, Field } -------------------------------------------------------------------------------- /src/lib/store.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store' 2 | 3 | import { required, 4 | mustMatch, 5 | minNumOptions, 6 | maxNumOptions, 7 | verifyEmail, 8 | isNumber, 9 | isDate, 10 | isInRange, 11 | isPhoneNumberNA, 12 | isAlpha, 13 | isURL, 14 | isJSON, 15 | isCreditCard, 16 | isAlphaNumeric, 17 | isStrongPassword, 18 | isTime, 19 | isBase64, 20 | isMimeType, 21 | matchesPattern, 22 | } from './validators' 23 | import type { formStoreType, formStoreValueType } from './types' 24 | 25 | const initStoreValues:formStoreValueType = { 26 | values: {}, 27 | errors: {} 28 | } 29 | 30 | function createFormStore() : formStoreType { 31 | const { subscribe, set, update } = writable(initStoreValues) 32 | 33 | return { 34 | subscribe, 35 | set, 36 | update, 37 | required: (field) => update(store => required(field, store)), 38 | mustMatch: (field, fieldToMatch) => update(store => mustMatch(field, fieldToMatch, store)), 39 | minNumOptions: (field, min) => update(store => minNumOptions(field, min, store)), 40 | maxNumOptions: (field, max) => update(store => maxNumOptions(field, max, store)), 41 | customValidator: (func) => update(store => func(store)), 42 | verifyEmail: (field) => update(store=>verifyEmail(field, store)), 43 | isNumber: (field) => update(store=>isNumber(field, store)), 44 | isDate: (field) => update(store=>isDate(field, store)), 45 | isInRange: (field, min, max) => update(store=>isInRange(field, min, max, store)), 46 | isPhoneNumberNA: (field) => update(store=>isPhoneNumberNA(field, store)), 47 | isAlpha: (field) => update(store=>isAlpha(field, store)), 48 | isURL: (field) => update(store=>isURL(field, store)), 49 | isJSON: (field) => update(store=>isJSON(field, store)), 50 | isCreditCard: (field) => update(store=>isCreditCard(field, store)), 51 | isAlphaNumeric: (field) => update(store=>isAlphaNumeric(field, store)), 52 | isStrongPassword: (field) => update(store=>isStrongPassword(field, store)), 53 | isTime: (field) => update(store=>isTime(field, store)), 54 | isBase64: (field) => update(store=>isBase64(field, store)), 55 | isMimeType: (field) => update(store=>isMimeType(field, store)), 56 | matchesPattern: (field, regex) => update(store=>matchesPattern(field, regex, store)), 57 | isFileSize: (field, maxSize) => update(store=>matchesPattern(field, maxSize, store)), 58 | } 59 | } 60 | 61 | export const formStore = createFormStore() 62 | -------------------------------------------------------------------------------- /src/lib/types.ts: -------------------------------------------------------------------------------- 1 | import type { Writable } from 'svelte/store' 2 | 3 | export type formStoreValueType = { 4 | values: { 5 | [index: string]: any 6 | } 7 | errors: { 8 | [index: string] : { 9 | [index : string] : string 10 | } 11 | } 12 | } 13 | 14 | export type formStoreType = { 15 | subscribe: Writable['subscribe'] 16 | set: any 17 | update: any 18 | required: ( 19 | field : string 20 | ) => void 21 | mustMatch: ( 22 | field: string, 23 | fieldToMatch: string 24 | ) => void 25 | maxNumOptions: ( 26 | field: string, 27 | max: number 28 | ) => void 29 | minNumOptions: ( 30 | field: string, 31 | min: number 32 | ) => void 33 | customValidator: ( 34 | func: (store: formStoreValueType) => formStoreValueType 35 | ) => void 36 | isAlphaNumeric: ( 37 | field: string, 38 | ) => void 39 | isBase64: ( 40 | field: string, 41 | ) => void 42 | isCreditCard: ( 43 | field: string, 44 | ) => void 45 | isDate: ( 46 | field: string, 47 | ) => void 48 | isInRange: ( 49 | field: number, 50 | min: number, 51 | max: number 52 | ) => void 53 | isJSON: ( 54 | field: string, 55 | ) => void 56 | isNumber: ( 57 | field: number, 58 | ) => void 59 | isPhoneNumberNA: ( 60 | field: string, 61 | ) => void 62 | isStrongPassword: ( 63 | field: string, 64 | ) => void 65 | isTime: ( 66 | field: string, 67 | ) => void 68 | isURL: ( 69 | field: string, 70 | ) => void 71 | verifyEmail: ( 72 | field: string, 73 | ) => void 74 | isMimeType: ( 75 | field: string, 76 | ) => void 77 | isAlpha: ( 78 | field: string, 79 | ) => void 80 | matchesPattern: ( 81 | field: string, 82 | regex: string 83 | ) => void 84 | isFileSize: ( 85 | field: string, 86 | maxSize: string 87 | ) => void 88 | } 89 | 90 | export type validateType = { 91 | required: (field: string) => void, 92 | mustMatch: (field: string, fieldToMatch: string) => void, 93 | minNumOptions: (field: string, min: number) => void, 94 | maxNumOptions: (field: string, min: number) => void, 95 | customValidator: (func: (store: formStoreValueType) => formStoreValueType) => void, 96 | isAlphaNumeric: (field: string) => void, 97 | isAlpha: (field: string) => void, 98 | isBase64: (field: string) => void, 99 | isCreditCard: (field: string) => void, 100 | isDate: (field: string) => void, 101 | isInRange: (field: number, min: number, max: number) => void, 102 | isJSON: (field: string) => void, 103 | isNumber: (field: number) => void, 104 | isPhoneNumberNA: (field: string) => void, 105 | isStrongPassword: (field: string) => void, 106 | isTime: (field: string) => void, 107 | isURL: (field: string) => void, 108 | verifyEmail: (field: string) => void, 109 | isMimeType: (field: string) => void, 110 | matchesPattern: (field: string, regex: string) => void, 111 | isFileSize: (field: string, maxSize: string) => void, 112 | 113 | } 114 | //FastForm.svelte variable types 115 | export type validate = (input: validateType) => void; 116 | export type eventHandler = (userInput: formStoreValueType) => void; 117 | export type initValue = { 118 | [key: string]: any 119 | } -------------------------------------------------------------------------------- /src/lib/validators/index.ts: -------------------------------------------------------------------------------- 1 | import required from './required/required' 2 | import mustMatch from './mustMatch/mustMatch' 3 | import minNumOptions from './minNumOptions/minNumOptions' 4 | import maxNumOptions from './maxNumOptions/maxNumOptions' 5 | import verifyEmail from './verifyEmail/verifyEmail' 6 | import isNumber from './isNumber/isNumber' 7 | import isDate from './isDate/isDate' 8 | import isInRange from './isInRange/isInRange' 9 | import isPhoneNumberNA from './isPhoneNumberNA/isPhoneNumberNA' 10 | import isAlpha from './isAlpha/isAlpha' 11 | import isURL from './isURL/isURL' 12 | import isJSON from './isJSON/isJSON' 13 | import isCreditCard from './isCreditCard/isCreditCard' 14 | import isAlphaNumeric from './isAlphaNumeric/isAlphaNumeric' 15 | import isStrongPassword from './isStrongPassword/isStrongPassword' 16 | import isTime from './isTime/isTime' 17 | import isBase64 from './isBase64/isBase64' 18 | import isMimeType from './isMimeType/isMimeType' 19 | import matchesPattern from './matchesPattern/matchesPattern' 20 | import isFileSize from './isFileSize/isFileSize' 21 | 22 | export { 23 | required, 24 | mustMatch, 25 | minNumOptions, 26 | maxNumOptions, 27 | verifyEmail, 28 | isNumber, 29 | isDate, 30 | isInRange, 31 | isPhoneNumberNA, 32 | isAlpha, 33 | isURL, 34 | isJSON, 35 | isCreditCard, 36 | isAlphaNumeric, 37 | isStrongPassword, 38 | isTime, 39 | isBase64, 40 | isMimeType, 41 | matchesPattern, 42 | isFileSize, 43 | } 44 | -------------------------------------------------------------------------------- /src/lib/validators/isAlpha/isAlpha.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function isAlpha(field : string, store: formStoreValueType):formStoreValueType { 4 | const alphaRegex = /^[A-Za-z]+$/; 5 | if (alphaRegex.test(field)) { 6 | store.errors[field]['isAlpha'] = `Error: Please enter all letters` 7 | } 8 | return store 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/validators/isAlphaNumeric/isAlphaNumeric.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function isAlphaNumeric(field : string, store: formStoreValueType):formStoreValueType { 4 | const alphanumericRegex = /^[a-z0-9]+$/i; 5 | if (alphanumericRegex.test(field)) { 6 | store.errors[field]['isAlphaNumeric'] = `Error: invalid input, must be alpha-numeric` 7 | } 8 | return store 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/validators/isBase64/isBase64.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function isBase64(field : string, store: formStoreValueType):formStoreValueType { 4 | const base64Regex = /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/; 5 | if (base64Regex.test(field)) { 6 | store.errors[field]['isBase64'] = `Error: invalid base64 format` 7 | } 8 | return store 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/validators/isCreditCard/isCreditCard.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function isCreditCard(field : string, store: formStoreValueType):formStoreValueType { 4 | // Remove all non-digit characters 5 | const sanitized = field.replace(/\D/g, ''); 6 | // Check if the sanitized card number has the correct length 7 | if (sanitized.length < 13 || sanitized.length > 19) { 8 | store.errors[field]['isCreditCard'] = `Error: invalid Credit Card Number` 9 | } else { 10 | let sum = 0; 11 | let shouldDouble = false; 12 | 13 | for (let i = sanitized.length - 1; i >= 0; i--) { 14 | let digit = parseInt(sanitized.charAt(i), 10); 15 | 16 | if (shouldDouble) { 17 | digit *= 2; 18 | if (digit > 9) { 19 | digit -= 9; 20 | } 21 | } 22 | sum += digit; 23 | shouldDouble = !shouldDouble; 24 | } 25 | // If the sum is a multiple of 10, the card number is valid 26 | if ((sum % 10) !== 0) { 27 | store.errors[field]['isCreditCard'] = `Error: invalid Credit Card Number` 28 | } 29 | } 30 | return store; 31 | } 32 | -------------------------------------------------------------------------------- /src/lib/validators/isDate/isDate.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function isDate(field : string, store: formStoreValueType):formStoreValueType { 4 | const date = new Date(field) 5 | if (!isNaN(date.getTime())) { 6 | store.errors[field]['isDate'] = `Error: invalid Date input` 7 | } 8 | return store 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/validators/isFileSize/isFileSize.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function isFileSize(field : number, maxSize: number = 5000000, store: formStoreValueType):formStoreValueType { 4 | //file size default at 5MB 5 | if (field>maxSize) { 6 | store.errors[field]['isFileSize'] = `Error: filesize is too large` 7 | } 8 | return store 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/validators/isInRange/isInRange.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function isInRange(field : number, min : number, max: number, store: formStoreValueType):formStoreValueType { 4 | 5 | if (field < min || field > max) { 6 | store.errors[field]['outOfRange'] = `Error: Number must be within range` 7 | } 8 | return store 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/validators/isJSON/isJSON.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function isJSON(field : string, store: formStoreValueType):formStoreValueType { 4 | try { 5 | JSON.parse(field) 6 | } catch (e) { 7 | store.errors[field]['isJSON'] = `Error: Not a valid JSON format` 8 | } 9 | return store 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/validators/isMimeType/isMimeType.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function isMimeType(field : string, store: formStoreValueType):formStoreValueType { 4 | const urlRegex = /^(https?:\/\/)?((([a-zA-Z0-9\-]+\.)+[a-zA-Z]{2,})|(([0-9]{1,3}\.){3}[0-9]{1,3}))(:\d+)?(\/[^\s]*)?$/; 5 | if (urlRegex.test(field)) { 6 | store.errors[field]['isMimeType'] = `Error: invalid URL link` 7 | } 8 | return store 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/validators/isNumber/isNumber.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function isNumber(field : string, store: formStoreValueType):formStoreValueType { 4 | if (!isNaN(Number(store.values[field]))) { 5 | store.errors[field]['isNumber'] = `Error: invalid number input` 6 | } 7 | return store 8 | } 9 | -------------------------------------------------------------------------------- /src/lib/validators/isPhoneNumberNA/isPhoneNumberNA.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function isPhoneNumberNA(field : string, store: formStoreValueType):formStoreValueType { 4 | // This is only valid for North American phone numbers 5 | const phoneRegex = /^(\+\d{1,3}[- ]?)?\d{10}$/; 6 | if (phoneRegex.test(field)) { 7 | store.errors[field]['isPhoneNumber'] = `Error: invalid phone number` 8 | } 9 | return store 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/validators/isStrongPassword/isStrongPassword.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function isStrongPassword(field : string, store: formStoreValueType):formStoreValueType { 4 | 5 | const minLength = 8; 6 | const hasUpperCase = /[A-Z]/.test(field); 7 | const hasLowerCase = /[a-z]/.test(field); 8 | const hasDigit = /[0-9]/.test(field); 9 | const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(field); 10 | 11 | if (field.length < minLength) { 12 | store.errors[field]['passwordLength'] = `Error: Please enter password longer than 8 characters` 13 | } 14 | 15 | if (!hasUpperCase) { 16 | store.errors[field]['passwordUppercase'] = `Error: Please include an uppercase letter` 17 | } 18 | 19 | if (!hasLowerCase) { 20 | store.errors[field]['passwordLowercase'] = `Error: Please include a lowercase letter` 21 | } 22 | 23 | if (!hasDigit) { 24 | store.errors[field]['passwordHasDigit'] = `Error: Please include a number` 25 | } 26 | 27 | if (!hasSpecialChar) { 28 | store.errors[field]['passwordSpecialChar'] = `Error: Please include a special Character !@#$%^&*()` 29 | } 30 | 31 | return store 32 | } 33 | -------------------------------------------------------------------------------- /src/lib/validators/isTime/isTime.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function isTime(field : string, store: formStoreValueType):formStoreValueType { 4 | //this verifies HH:MM format in a 24-hour clock 5 | const timeRegex = /^(?:[01]\d|2[0-3]):[0-5]\d$/; 6 | if (timeRegex.test(field)) { 7 | store.errors[field]['isTime'] = `Error: invalid time format` 8 | } 9 | return store 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/validators/isURL/isURL.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function isURL(field : string, store: formStoreValueType):formStoreValueType { 4 | const urlRegex = /^(https?:\/\/)?((([a-zA-Z0-9\-]+\.)+[a-zA-Z]{2,})|(([0-9]{1,3}\.){3}[0-9]{1,3}))(:\d+)?(\/[^\s]*)?$/; 5 | if (urlRegex.test(field)) { 6 | store.errors[field]['isURL'] = `Error: invalid URL link` 7 | } 8 | return store 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/validators/matchesPattern/matchesPattern.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function matchesPattern(field : string, pattern: string, store: formStoreValueType):formStoreValueType { 4 | const regex = new RegExp(pattern) 5 | if (regex.test(field)) { 6 | store.errors[field]['matchesPattern'] = `Error: input does not match regex pattern: ${pattern}` 7 | } 8 | return store 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/validators/maxNumOptions/maxNumOptions.stories.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | 33 | 34 | 35 | 41 | -------------------------------------------------------------------------------- /src/lib/validators/maxNumOptions/maxNumOptions.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function maxNumOptions(field : string, max : number, store : formStoreValueType ) : formStoreValueType { 4 | if (Array.isArray(store.values[field]) === false) { 5 | throw new Error('Error: maxNumOptions is being used with a non-array type.') 6 | } 7 | if (store.values[field].length > max) { 8 | store.errors[field] ??= {} 9 | store.errors[field]['maxNumOptions'] = 'Error: Maximum number of options has exceeded.' 10 | } 11 | return store 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/validators/minNumOptions/minNumOptions.stories.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | 33 | 34 | 35 | 41 | -------------------------------------------------------------------------------- /src/lib/validators/minNumOptions/minNumOptions.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function minNumOptions(field : string, min : number, store : formStoreValueType ) : formStoreValueType { 4 | if (Array.isArray(store.values[field]) === false) { 5 | throw new Error('Error: minNumOptions is being used with a non-array type.') 6 | } 7 | if (store.values[field].length < min) { 8 | store.errors[field] ??= {} 9 | store.errors[field]['minNumOptions'] = `Error: Minimum of ${min} items must be selected.` 10 | } 11 | return store 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/validators/mustMatch/mustMatch.stories.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | 18 | { 20 | myValues = values 21 | myErrors = errors 22 | }} 23 | validate={({mustMatch}) => { 24 | mustMatch('email', 'confirmEmail') 25 | }}> 26 | 27 | 28 | 29 | 30 |
31 |
{JSON.stringify(myValues, null, 2)}
32 |
{JSON.stringify(myErrors, null, 2)}
33 |
34 | -------------------------------------------------------------------------------- /src/lib/validators/mustMatch/mustMatch.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function mustMatch(field : string, fieldToMatch : string, store: formStoreValueType):formStoreValueType { 4 | if (store.values[field] !== store.values[fieldToMatch]) { 5 | store.errors[field] ??= {} 6 | store.errors[field]['mustMatch'] = `Error: '${field}' must match '${fieldToMatch}'.` 7 | } 8 | return store 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/validators/required/required.stories.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | 18 | { 20 | myValues = values 21 | myErrors = errors 22 | }} 23 | validate={({required}) => { 24 | required('name') 25 | }}> 26 | 27 | 28 | 29 |
30 |
{JSON.stringify(myValues, null, 2)}
31 |
{JSON.stringify(myErrors, null, 2)}
32 |
33 | -------------------------------------------------------------------------------- /src/lib/validators/required/required.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function required(field : string, store: formStoreValueType) : formStoreValueType { 4 | if (!store.values[field]) { 5 | store.errors[field] ??= {} 6 | store.errors[field].required = `Error: '${field}' is a required field.` 7 | } 8 | return store 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/validators/validators.stories.mdx: -------------------------------------------------------------------------------- 1 | import { Canvas, Meta, Story } from '@storybook/addon-docs'; 2 | 3 | 4 | 5 | # Validators 6 | Validation can happen at both the form-level and field-level. Both our `` and `` components can take in a `validate` prop. 7 | - Form-level - This type of validation runs from the `validate` prop in the `` component. It will trigger directly before `handleSubmit` 8 | - Field-level - This type of validation runs from the `validate` prop in the `` component. It can trigger `onChange`, `onBlur`, or both! 9 | 10 | 11 | ### [`required(name: string)`](?path=/story/validators-required--required) 12 | Indicates that the given field is required. 13 | - `name`: Name of the required field. 14 | 15 | ```jsx 16 | 23 | 24 | 25 | 26 | 27 | 28 | ``` 29 | 30 | ### [`mustMatch(name: string, nameToMatch: string)`](?path=/story/validators-mustmatch--must-match) 31 | Indicates that two fields must have the same value. 32 | - `name`: Name of one of the fields (The error will be listed under this label). 33 | - `nametoMatch`: Name of the other field. 34 | ```jsx 35 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | ``` 49 | 50 | ### [`minNumOptions(name: string, min: number)`](?path=/story/validators-minnumoptions--min-num-options) 51 | For fields that can have multiple selected values (i.e. checkboxes), this validator determines the minimum number of options that the user must fill out. 52 | - `name`: Name of the field. 53 | - `min`: Minimum number of selected options. 54 | 55 | ```jsx 56 | 63 | 64 | 65 | 66 | 67 | ``` 68 | ### [`maxNumOptions(name: string, max: number)`](?path=/story/validators-maxnumoptions--max-num-options) 69 | For fields that can have multiple selected values (i.e. checkboxes), this validator determines the maximum number of options that the user can fill out. 70 | - `name`: Name of the field. 71 | - `max`: Maximum number of selected options. 72 | 73 | ```jsx 74 | 81 | 82 | 83 | 84 | 85 | ``` 86 | ### `customValidator(validator: ({values, errors}) => {values, errors})` 87 | The custom validator is for any validation that cannnot be handled by the prewritten validation functions. 88 | - `validator`: A function that will have an object with `values` and `errors` as properties be passed in as an argument. These objects can be mutated during the function to add errors to the `errors` object. The function must return these objects. 89 | 90 | ```jsx 91 | 103 | 104 | 105 | 106 | 107 | ``` -------------------------------------------------------------------------------- /src/lib/validators/verifyEmail/verifyEmail.stories.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | 33 | 34 | 35 | 41 | -------------------------------------------------------------------------------- /src/lib/validators/verifyEmail/verifyEmail.ts: -------------------------------------------------------------------------------- 1 | import type { formStoreValueType } from '../../types' 2 | 3 | export default function verifyEmail(field : string, store : formStoreValueType ) : formStoreValueType { 4 | if (isValidEmail(store.values[field]) === false) { 5 | throw new Error('Error: invalid e-mail format') 6 | } 7 | return store 8 | } 9 | 10 | function isValidEmail(email: string): boolean { 11 | const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 12 | return re.test(email); 13 | } 14 | -------------------------------------------------------------------------------- /src/routes/index.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | { 22 | mustMatch('name', 'name2') 23 | maxNumOptions('icecream', 2) 24 | minNumOptions('icecream', 1) 25 | customValidator(({values, errors}) => { 26 | if (errors.name) { 27 | errors.custom ??= {} 28 | errors.custom.custom = 'Some message' 29 | } 30 | return { 31 | values, 32 | errors 33 | } 34 | }) 35 | required('name') 36 | }}> 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |

{JSON.stringify($formStore.values, null, 2)}

45 |

{JSON.stringify($formStore.errors, null, 2)}

46 | -------------------------------------------------------------------------------- /src/stories/Getting Started/GettingStarted.stories.mdx: -------------------------------------------------------------------------------- 1 | import { Canvas, Meta, Story } from '@storybook/addon-docs'; 2 | import gif from '../../../fastform-demo.gif' 3 | 4 | 5 | 6 | 7 | # Getting Started 8 | 9 | FastForm is a form component library designed specifically for Svelte that includes different base components and various validator logic. FastForm's components come with no styling to allow developers more customization over the look of their applications. 10 | 11 | FastForm is available as an [npm package](https://www.npmjs.com/package/@fastform/form). You can install it by running the following command in the terminal within your project directory: 12 | 13 | ## Installation and Imports 14 | You can import FastForm onto your application using the following command. 15 | ```bash 16 | npm install @fastform/form 17 | ``` 18 | Example for the [Field](?path=/docs/field-field--page) component: 19 | ```jsx 20 | import { FastForm, Field } from '@fastform/form'; 21 | ``` 22 | 23 | ## How It Works 24 | 25 | FastForm comes with a store along with predefined validators. Developers have the ability to create their own custom validator functions to pass onto components. Refer to the [Validators](?path=/docs/validators-validators--page) section for more details on each validator. 26 | 27 | Here is how FastForm works: 28 | 29 | 30 | In this demo, both the value and errors object is displayed below the fields. We can see the following occurred: 31 | 32 | * The first name field was left blank, which triggered an error for the ['required'](?path=/story/validators-required--required) validator. 33 | * The first name field did not match our other name field with name 'John Doe', triggering an error for the ['mustMatch'](?path=/story/validators-mustmatch--must-match) validator. 34 | * No options were checked for the ice cream field, triggering an error for the ['minNumOptions'](?path=/story/validators-minnumoptions--min-num-options) validator. 35 | 36 | Once each validator's conditions were met, the errors were cleared. 37 | 38 | In the code snippet below, our [Field](?path=/story/field-field--page) components for both names and icecream are within our [FastForm](?path=/story/fastform-fastform--page) component. FastForm takes in initial values, a handleSubmit function, and validator functions. 39 | 40 | We can see the mustMatch validator is being passed onto both name fields, the minNumOptions validator on the icecream field, and the required validator on the first name field. More details on the components and validators can be found in the examples. 41 | 42 | ```jsx 43 | { 47 | mustMatch('name', 'name2') 48 | minNumOptions('icecream', 1) 49 | required('name') 50 | }}> 51 | 52 | 53 | 54 | 55 | 56 | ``` -------------------------------------------------------------------------------- /src/stories/More Information/Changelog.stories.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from '@storybook/addon-docs'; 2 | 3 | 4 | 5 | # v0.3.2 - Form-level onChange 6 | #### August 5, 2022 7 | --- 8 | - Implemented a `handleChange` prop to FastForm 9 | 10 |
11 | 12 | # v0.3.0 - Release! 13 | #### August 4, 2022 14 | --- 15 | This version marks the release of FastForm!
16 | Information about FastForm can be found [here](https://www.npmjs.com/package/@fastform/form)! 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/stories/More Information/Contributors.stories.mdx: -------------------------------------------------------------------------------- 1 | import { Canvas, Meta, Story } from '@storybook/addon-docs'; 2 | 3 | 4 | 5 | # Contributors 6 | 7 | The following individuals are contributors to FastForm. For any future updates, please add additional contributors. 8 | 9 | * Angel Cortes Gildo - [LinkedIn](https://www.linkedin.com/in/angel-cortes-gildo-708972243/) / [Github](https://github.com/angcortes) 10 | * Griffin Barlow - [LinkedIn](https://www.linkedin.com/in/griffinbrlw/) / [Github](https://github.com/griffin104) 11 | * Ilija Bibic - [LinkedIn](https://www.linkedin.com/in/ilija-bibic/) / [Github](https://github.com/ibibic) 12 | * Steven Yuan - [LinkedIn](https://www.linkedin.com/in/steven-yuann/) / [Github](https://github.com/steven-yuann) 13 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-auto' 2 | import preprocess from 'svelte-preprocess' 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | preprocess: preprocess(), 7 | kit: { 8 | package: { 9 | exports: () => false 10 | }, 11 | adapter: adapter() 12 | } 13 | } 14 | 15 | export default config 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "noImplicitAny": true, 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "moduleResolution": "node", 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | }, 13 | "include": ["src/**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite' 2 | 3 | /** @type {import('vite').UserConfig} */ 4 | const config = { 5 | plugins: [sveltekit()] 6 | } 7 | 8 | export default config 9 | --------------------------------------------------------------------------------