{"id":23172014,"url":"https://github.com/choonkeat/tiny-form-fields","last_synced_at":"2025-07-05T17:06:14.916Z","repository":{"id":243468093,"uuid":"812145359","full_name":"choonkeat/tiny-form-fields","owner":"choonkeat","description":"Tiny form field builder and renderer for embedding into web apps","archived":false,"fork":false,"pushed_at":"2025-06-22T13:58:00.000Z","size":2035,"stargazers_count":1,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-28T09:12:43.546Z","etag":null,"topics":["form","formbuilder"],"latest_commit_sha":null,"homepage":"https://tiny-form-fields.netlify.app","language":"Elm","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/choonkeat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-06-08T04:43:33.000Z","updated_at":"2025-06-22T13:58:00.000Z","dependencies_parsed_at":"2024-06-13T11:13:05.329Z","dependency_job_id":"e11283b9-e837-450c-8135-77510f0a3449","html_url":"https://github.com/choonkeat/tiny-form-fields","commit_stats":null,"previous_names":["choonkeat/tiny-form-fields"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/choonkeat/tiny-form-fields","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/choonkeat%2Ftiny-form-fields","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/choonkeat%2Ftiny-form-fields/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/choonkeat%2Ftiny-form-fields/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/choonkeat%2Ftiny-form-fields/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/choonkeat","download_url":"https://codeload.github.com/choonkeat/tiny-form-fields/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/choonkeat%2Ftiny-form-fields/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263776670,"owners_count":23509770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["form","formbuilder"],"created_at":"2024-12-18T04:20:41.815Z","updated_at":"2025-07-05T17:06:14.909Z","avatar_url":"https://github.com/choonkeat.png","language":"Elm","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tiny-form-fields\n\nA lightweight, customizable form builder and renderer written in Elm. Create dynamic forms with various field types, drag-and-drop reordering, and validation support.\n\n👉 [Try the live demo](https://tiny-form-fields.netlify.app/)\n\n## Features\n\n- Rich form field types including:\n  - Single-line text\n    - Optional multiple value support\n  - Multi-line text\n  - Dropdown menus\n  - Radio buttons\n  - Checkboxes\n  - Email (single and multiple)\n  - Phone numbers\n  - URLs\n  - Custom elements with validation\n  - Rich text editor (via custom field integration)\n- Drag-and-drop field reordering\n- Field validation\n- Conditional visibility rules:\n  - Show/Hide fields based on other field values\n  - Multiple conditions with AND/OR logic\n  - Real-time updates in CollectData mode\n  - Support for various comparison types (equals, contains, ends with, greater than)\n  - Visual logic indicators showing which fields:\n    - Contain logic (blue pill with \"Contains logic\" text)\n    - Affect other fields' logic (gray pill with \"Affects logic\" text)\n    - Both contain and affect logic (blue pill with \"Contains \u0026 affects logic\" text)\n- Dynamic choice filtering:\n  - Filter dropdown, radio button, or checkbox choices based on values from other fields\n  - Support for \"starts with\" and \"contains\" filtering modes\n  - Real-time filtering as users type in source fields\n  - Fields are hidden when filter field is empty or no options match the filter\n- Responsive design\n- Two modes: Editor (for building forms) and CollectData (for end users)\n- JSON import/export of form definitions\n- Cross-browser compatibility:\n  - Tested on Chrome, Firefox, Safari, and Edge\n  - Consistent behavior for form controls across browsers\n  - Optimized dropdown handling for Edge on Windows\n  - Built-in protection against browser extensions:\n    - Grammarly is automatically disabled for form fields\n    - Support for disabling Google Translate and Dark Reader\n\n## Tasks\n\nSee the [tasks](tasks/) directory for planned features and improvements. Each task is documented with requirements and implementation details.\n\n## Installation\n\n1. Add the compiled assets to your project:\n   ```html\n   \u003cscript src=\"./dist/tiny-form-fields.js\"\u003e\u003c/script\u003e\n   \u003clink rel=\"stylesheet\" href=\"./dist/tiny-form-fields.min.css\"\u003e\n   ```\n\n2. Add meta tags to prevent browser extensions from interfering with form fields:\n   ```html\n   \u003c!-- Disable Google Translate --\u003e\n   \u003cmeta name=\"google\" content=\"notranslate\"\u003e\n   \u003c!-- Disable Dark Reader --\u003e\n   \u003cmeta name=\"darkreader-lock\"\u003e\n   ```\n\n3. Initialize the form builder in your HTML:\n   ```html\n   \u003c!-- Editor mode --\u003e\n   \u003cdiv id=\"editor\"\u003e\u003c/div\u003e\n   \u003cscript\u003e\n     var app = Elm.Main.init({\n       node: document.getElementById('editor'),\n       flags: {\n         viewMode: \"Editor\",\n         formFields: [], // your initial form fields\n         formValues: {}, // initial values\n         shortTextTypeList: [] // custom field types\n       }\n     });\n   \u003c/script\u003e\n   ```\n   See [FLAGS.md](FLAGS.md) for detailed documentation of all available configuration options.\n\n## Config Validation\n\ntiny-form-fields provides JSON Schema validation for configuration objects to help catch errors early and provide IDE support.\n\n### JSON Schema File\n\nThe project automatically generates `dist/config.schema.json` during the build process. This schema validates the structure of configuration objects passed to the `flags` parameter.\n\n### IDE Integration\n\nFor IDE autocompletion and validation, add the schema reference to your JSON config files:\n\n```json\n{\n  \"$schema\": \"https://tiny-form-fields.netlify.app/dist/config.schema.json\",\n  \"viewMode\": \"Editor\",\n  \"formFields\": [],\n  \"formValues\": {},\n  \"shortTextTypeList\": []\n}\n```\n\n### CLI Validation\n\nValidate config files from the command line:\n\n```bash\n# Validate a config file\nmake validate-config CONFIG=my-config.json\n\n# Or use the validation script directly\nnode validate-config.js my-config.json\n```\n\n### Requirements\n\nConfig validation requires the `ajv` package:\n\n```bash\nnpm install --save-dev ajv\n```\n\nThe schema is automatically regenerated whenever you run `make build` or `make schema`, ensuring it stays in sync with code changes.\n\n## Development\n\n### Prerequisites\n\n- Node.js and npm\n- Elm (for compilation)\n- Make (for build scripts)\n\n### Setup\n\n1. Clone the repository\n2. Install dependencies:\n   ```bash\n   npm install\n   ```\n\n### Available Commands\n\n- `make dist/tiny-form-fields.esm.js` - Build the production JS and CSS files\n- `make css` - Build just the CSS\n- `make run` - Start development server with hot reloading\n- `make test` - Run Elm tests\n- `make test-playwright` - Run end-to-end tests\n- `make test-playwright-ui` - Run end-to-end tests with UI\n- `make elm-review` - Run elm-review with auto-fix\n- `make schema` - Generate JSON schema for config validation\n- `make validate-config CONFIG=path/to/config.json` - Validate a config file\n\n### Project Structure\n\n- `src/Main.elm` - Main Elm source code\n- `src/ConfigSchema.elm` - JSON schema definition for config validation\n- `src/GenerateSchema.elm` - Schema generation utilities\n- `input.css` - Source CSS file (processed by Tailwind)\n- `tests/` - Elm unit tests\n- `e2e/` - Playwright end-to-end tests\n- `dist/` - Compiled assets\n  - `dist/config.schema.json` - Auto-generated JSON schema for config validation\n\n## Usage Example\n\n```html\n\u003c!-- Include the required assets --\u003e\n\u003cscript src=\"./dist/tiny-form-fields.js\"\u003e\u003c/script\u003e\n\u003clink rel=\"stylesheet\" href=\"./dist/tiny-form-fields.min.css\"\u003e\n\n\u003c!-- Create a container for the form --\u003e\n\u003cdiv id=\"myform\"\u003e\u003c/div\u003e\n\n\u003cscript\u003e\n  // Initialize in Editor mode\n  var app = Elm.Main.init({\n    node: document.getElementById('myform'),\n    flags: {\n      viewMode: \"Editor\",\n      formFields: [],\n      formValues: {},\n      shortTextTypeList: [\n        {\n          \"Text\": {\n            \"type\": \"text\",\n            \"maxlength\": \"100\"\n          }\n        }\n      ]\n    }\n  });\n\n  // Listen for form changes\n  app.ports.outgoing.subscribe(function(data) {\n    console.log('Form data:', data);\n    // Handle form data changes here\n  });\n\u003c/script\u003e\n```\n\n## Rich Text Editor Integration\n\nYou can integrate a rich text editor as a custom field type. Here's how:\n\n1. Choose a Rich Text Editor library (e.g., TinyMCE, CKEditor, Quill)\n\n2. Create a custom field type:\n   ```javascript\n   var app = Elm.Main.init({\n     node: document.getElementById('editor'),\n     flags: {\n       viewMode: \"Editor\",\n       formFields: [],\n       formValues: {},\n       shortTextTypeList: [{\n         type: \"richtext\",\n         label: \"Rich Text Editor\"\n       }]\n     }\n   });\n   ```\n\n3. Subscribe to field render events:\n   ```javascript\n   app.ports.renderCustomField.subscribe(function(data) {\n     if (data.type === \"richtext\") {\n       // Initialize your rich text editor\n       const editor = new RichTextEditor({\n         element: document.getElementById(data.id),\n         value: data.value || ''\n       });\n       \n       // Send value back to Elm\n       editor.onChange((newValue) =\u003e {\n         app.ports.onCustomFieldChange.send({\n           id: data.id,\n           value: newValue\n         });\n       });\n     }\n   });\n   ```\n\n4. Add necessary CSS to style your editor:\n   ```html\n   \u003clink href=\"rich-text-editor.css\" rel=\"stylesheet\"\u003e\n   ```\n\n### Example with TinyMCE\n\n```javascript\n// Initialize TinyMCE\napp.ports.renderCustomField.subscribe(function(data) {\n  if (data.type === \"richtext\") {\n    tinymce.init({\n      selector: '#' + data.id,\n      height: 300,\n      menubar: false,\n      plugins: [\n        'advlist', 'autolink', 'lists', 'link', 'image', 'charmap',\n        'preview', 'anchor', 'searchreplace', 'visualblocks', 'code',\n        'insertdatetime', 'media', 'table', 'help', 'wordcount'\n      ],\n      toolbar: 'undo redo | formatselect | bold italic | ' +\n        'alignleft aligncenter alignright alignjustify | ' +\n        'bullist numlist outdent indent | help',\n      setup: function(editor) {\n        editor.on('change', function() {\n          app.ports.onCustomFieldChange.send({\n            id: data.id,\n            value: editor.getContent()\n          });\n        });\n      }\n    });\n  }\n});\n```\n\nNote: Rich text editors can be heavy dependencies. Consider lazy-loading the editor library only when needed to optimize initial page load.\n\n## Custom Elements\n\ntiny-form-fields supports custom form field elements through web components. If you need to create your own custom form field types, please refer to our [Custom Elements Guide](CUSTOM_ELEMENT.md) for detailed instructions.\n\n## Configuring Form Fields and Custom Types\n\nThe library accepts two main configuration objects: `formFields` for defining form fields, and `shortTextTypeList` for defining custom input types.\n\n**Note:** For choice-based fields (dropdowns, radio buttons, checkboxes), all choice values are automatically trimmed of leading and trailing whitespace to ensure consistent matching between form definitions and submitted values.\n\n#### Form Fields Format\n\nForm fields should be defined with this structure:\n\n```javascript\nformFields: [\n    {\n        \"label\": \"Field Label\",\n        \"type\": {\n            \"type\": \"ShortText\",      // Field type (ShortText, LongText, ChooseOne, ChooseMultiple)\n            \"inputType\": \"text\"       // HTML input type or custom type\n        },\n        \"required\": true,             // or false\n        \"description\": \"Help text\",   // or null\n        \"name\": null                  // Optional field name\n    }\n]\n```\n\n#### Custom Field Types (shortTextTypeList)\n\nThe `shortTextTypeList` parameter allows you to define custom input types:\n\n```javascript\nshortTextTypeList: [\n    {\n        \"Field Type Name\": {           // Display name in the form builder\n            \"type\": \"text\",            // Base HTML input type\n            \"maxlength\": \"10\",         // Optional: Additional HTML attributes\n            \"multiple\": \"true\"         // Optional: Support multiple values\n        }\n    }\n]\n```\n\nExample configurations:\n\n```javascript\n// Basic text input\n{\n    \"Text\": {\n        \"type\": \"text\"\n    }\n}\n\n// Email input with multiple values\n{\n    \"Emails\": {\n        \"type\": \"email\",\n        \"multiple\": \"true\"\n    }\n}\n\n// Custom validated input\n{\n    \"NRIC\": {\n        \"type\": \"text\",\n        \"pattern\": \"^[STGM][0-9]{7}[ABCDEFGHIZJ]$\"\n    }\n}\n\n// Rich text editor\n{\n    \"Rich Text\": {\n        \"type\": \"text\",\n        \"class\": \"richtext\"\n    }\n}\n```\n\nFor custom elements using Web Components:\n1. Define your custom element class extending `BaseCustomField`\n2. Register it using `customElements.define`\n3. Include it in `shortTextTypeList` with the appropriate configuration\n\n## Developer Guidelines\n\n### Event Handling Architecture in CollectData Mode\n\nWhen implementing new features that depend on user input in CollectData mode, follow these important guidelines:\n\n1. **CollectData Mode Default Behavior**:\n   - By default, form fields in CollectData mode DO NOT wire up Elm event handling\n   - Users can change form values without triggering Elm events or model updates\n   - This maximizes compatibility with browser extensions\n\n2. **When Event Handlers Are Required**:\n   - Features that need to respond to user input must explicitly wire up event handlers\n   - Currently, this is controlled by these conditions:\n     - `needsFormLogic`: For fields with visibility rules\n     - `isAnyChooseManyUsingMinMax`: For checkboxes with min/max constraints\n     - `isUsingFilter`: For fields with filtering capability\n\n3. **Adding Event-Dependent Features**:\n   - Create a helper function to detect your feature (e.g., `isUsingFeatureX`)\n   - Update the appropriate event handler conditions in `viewFormPreview`:\n     ```elm\n     , onChooseMany =\n         if needsFormLogic || isAnyChooseManyUsingMinMax || isUsingFeatureX then\n             onChooseManyAttrs\n         else\n             \\_ _ -\u003e []\n     ```\n   - Add similar checks for all affected event types (`onInput`, `onChange`, etc.)\n\n4. **Common Mistakes to Avoid**:\n   - Not checking if your new feature requires event handling in CollectData mode\n   - Not updating the event handler conditions for all relevant event types\n   - Testing only in Editor mode and not in CollectData mode\n\n5. **Testing Your Feature**:\n   - Always test your feature in CollectData mode\n   - Verify that event handlers are properly wired up\n   - Create an E2E test that specifically validates the feature in CollectData mode\n\nFollowing these guidelines prevents bugs where features appear to work in UI but don't actually update the model or trigger expected behaviors.\n\n## Contributing\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Make your changes\n4. Run tests (`make test \u0026\u0026 make test-playwright`)\n5. Commit your changes (`git commit -m 'Add amazing feature'`)\n6. Push to the branch (`git push origin feature/amazing-feature`)\n7. Open a Pull Request\n\n## License\n\nThis project is licensed under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchoonkeat%2Ftiny-form-fields","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchoonkeat%2Ftiny-form-fields","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchoonkeat%2Ftiny-form-fields/lists"}