├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── SECURITY.md ├── SUMMARY.md ├── code_of_conduct.md ├── contributing.md ├── package.json ├── packages ├── example │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .prettierrc │ ├── index.html │ ├── package.json │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.tsx │ │ ├── Forms │ │ │ ├── BasicFieldsScheme │ │ │ │ ├── BasicFieldsScheme.tsx │ │ │ │ ├── basic.json │ │ │ │ └── index.tsx │ │ │ ├── BasicFieldsValues │ │ │ │ ├── BasicFieldsValues.tsx │ │ │ │ ├── basic.json │ │ │ │ └── index.tsx │ │ │ ├── BasicWizard │ │ │ │ ├── BasicWizard.tsx │ │ │ │ └── index.tsx │ │ │ ├── Builder │ │ │ │ ├── Builder.tsx │ │ │ │ ├── config.json │ │ │ │ └── index.tsx │ │ │ ├── ConfigProvider │ │ │ │ ├── ConfigProvider.tsx │ │ │ │ └── index.tsx │ │ │ ├── ControlledForm │ │ │ │ ├── ControlledForm.tsx │ │ │ │ └── index.tsx │ │ │ ├── DeepNested │ │ │ │ ├── DeepNested.tsx │ │ │ │ ├── config.json │ │ │ │ └── index.tsx │ │ │ ├── DesignSystem │ │ │ │ ├── DesignSystem.tsx │ │ │ │ ├── basic.json │ │ │ │ └── index.tsx │ │ │ ├── DynamicState │ │ │ │ ├── DynamicState.tsx │ │ │ │ ├── basic.json │ │ │ │ └── index.tsx │ │ │ ├── FieldLogicRender │ │ │ │ ├── FieldLogicRender.tsx │ │ │ │ ├── basic.json │ │ │ │ └── index.tsx │ │ │ ├── FieldValidations │ │ │ │ ├── FieldValidations.tsx │ │ │ │ ├── basic.json │ │ │ │ └── index.tsx │ │ │ ├── FormMeta │ │ │ │ ├── FormMeta.tsx │ │ │ │ └── index.tsx │ │ │ ├── FormWizard │ │ │ │ ├── FormWizard.tsx │ │ │ │ ├── basic.json │ │ │ │ └── index.tsx │ │ │ ├── InitialFields │ │ │ │ ├── InitialFields.tsx │ │ │ │ ├── basic.json │ │ │ │ └── index.ts │ │ │ ├── LayoutConfig │ │ │ │ ├── LayoutConfig.tsx │ │ │ │ ├── basic.json │ │ │ │ └── index.tsx │ │ │ ├── MultiNested │ │ │ │ ├── MultiNested.tsx │ │ │ │ └── index.tsx │ │ │ ├── SchemaServe │ │ │ │ ├── SchemaServe.tsx │ │ │ │ └── index.tsx │ │ │ ├── SimpleForm │ │ │ │ ├── SimpleForm.tsx │ │ │ │ └── index.tsx │ │ │ ├── TutimForm │ │ │ │ ├── TutimForm.tsx │ │ │ │ ├── basic.json │ │ │ │ └── index.tsx │ │ │ └── index.ts │ │ ├── ShadcnApp.tsx │ │ ├── Wizards │ │ │ ├── AllOfExamples.tsx │ │ │ └── HeadlessWizard.tsx │ │ ├── basic.json │ │ ├── composition.json │ │ ├── main.tsx │ │ ├── utils │ │ │ └── index.ts │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── fields │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .prettierrc │ ├── .storybook │ │ ├── main.js │ │ ├── package.json │ │ └── preview.js │ ├── package.json │ ├── post-build.js │ ├── pre-build.js │ ├── src │ │ ├── Buttons │ │ │ ├── ActionButtons.tsx │ │ │ ├── Button.tsx │ │ │ ├── SubmitButton.tsx │ │ │ └── index.ts │ │ ├── Fields │ │ │ ├── CheckboxField.tsx │ │ │ ├── DateField.tsx │ │ │ ├── DefaultFields.tsx │ │ │ ├── JsonField.tsx │ │ │ ├── MultiChecboxField.tsx │ │ │ ├── MultiSelectField.tsx │ │ │ ├── MultiTextField.tsx │ │ │ ├── NumberField.tsx │ │ │ ├── PasswordField.tsx │ │ │ ├── RadioField.tsx │ │ │ ├── SelectField.tsx │ │ │ ├── SpecialFields │ │ │ │ ├── Collapse.tsx │ │ │ │ ├── FieldArray.tsx │ │ │ │ └── index.ts │ │ │ ├── SwitchField.tsx │ │ │ ├── TextAreaField.tsx │ │ │ ├── TextField.tsx │ │ │ ├── index.ts │ │ │ └── utils │ │ │ │ ├── FieldWrapper.tsx │ │ │ │ ├── Label.tsx │ │ │ │ └── index.tsx │ │ ├── Forms │ │ │ ├── FieldGroup.tsx │ │ │ ├── Form.tsx │ │ │ ├── FormElement.tsx │ │ │ ├── FormGrid.tsx │ │ │ ├── getGroupFields.tsx │ │ │ └── index.ts │ │ ├── NativeFields │ │ │ ├── DefaultFields.tsx │ │ │ ├── SelectField.tsx │ │ │ ├── TextField.tsx │ │ │ └── index.ts │ │ ├── Tabs │ │ │ ├── Tabs.tsx │ │ │ └── index.ts │ │ ├── Wizards │ │ │ ├── Footer.tsx │ │ │ ├── Header.tsx │ │ │ ├── MultiStepWizard.tsx │ │ │ ├── WizardStep.tsx │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── stories │ │ │ ├── CheckboxField.stories.tsx │ │ │ ├── JsonField.stories.tsx │ │ │ ├── MultiTextField.stories.tsx │ │ │ ├── RadioField.stories.tsx │ │ │ ├── SelectField.stories.tsx │ │ │ ├── SwitchField.stories.tsx │ │ │ └── TextField.stories.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── headless │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .prettierrc │ ├── package.json │ ├── post-build.js │ ├── pre-build.js │ ├── src │ │ ├── context │ │ │ ├── form.tsx │ │ │ ├── index.ts │ │ │ └── useRemoteSchemas.tsx │ │ ├── form │ │ │ ├── Field.tsx │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useForm.tsx │ │ │ │ ├── useFormConfig.tsx │ │ │ │ ├── useFormFields.tsx │ │ │ │ ├── useFormFieldsInternalState.tsx │ │ │ │ ├── useFormInit.tsx │ │ │ │ ├── useFormLayout.tsx │ │ │ │ ├── useMultiField.tsx │ │ │ │ └── useRenderLogic.tsx │ │ │ ├── index.ts │ │ │ └── utils.tsx │ │ ├── index.ts │ │ ├── utils │ │ │ └── index.ts │ │ ├── vite-env.d.ts │ │ └── wizard │ │ │ ├── components │ │ │ ├── TutimWizard.tsx │ │ │ └── index.ts │ │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── use-step.tsx │ │ │ └── use-wizard.tsx │ │ │ └── index.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── shadcn-ui │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .prettierrc │ ├── .storybook │ │ ├── main.ts │ │ └── preview.ts │ ├── LICENCSE.md │ ├── README.md │ ├── index.html │ ├── jest.config.ts │ ├── jest.setup.ts │ ├── package.json │ ├── postcss.config.js │ ├── src │ │ ├── components │ │ │ ├── analytics.tsx │ │ │ ├── callout.tsx │ │ │ ├── code-block-wrapper.tsx │ │ │ ├── command-menu.tsx │ │ │ ├── component-card.tsx │ │ │ ├── component-example.tsx │ │ │ ├── component-source.tsx │ │ │ ├── copy-button.tsx │ │ │ ├── examples-index.tsx │ │ │ ├── examples-nav.tsx │ │ │ ├── examples │ │ │ │ ├── .eslintrc.json │ │ │ │ ├── accordion │ │ │ │ │ └── demo.tsx │ │ │ │ ├── alert-dialog │ │ │ │ │ └── demo.tsx │ │ │ │ ├── alert │ │ │ │ │ ├── demo.tsx │ │ │ │ │ └── destructive.tsx │ │ │ │ ├── aspect-ratio │ │ │ │ │ └── demo.tsx │ │ │ │ ├── avatar │ │ │ │ │ └── demo.tsx │ │ │ │ ├── badge │ │ │ │ │ ├── demo.tsx │ │ │ │ │ ├── destructive.tsx │ │ │ │ │ ├── outline.tsx │ │ │ │ │ └── secondary.tsx │ │ │ │ ├── box │ │ │ │ │ └── demo.tsx │ │ │ │ ├── button │ │ │ │ │ ├── demo.tsx │ │ │ │ │ ├── destructive.tsx │ │ │ │ │ ├── ghost.tsx │ │ │ │ │ ├── link.tsx │ │ │ │ │ ├── loading.tsx │ │ │ │ │ ├── outline.tsx │ │ │ │ │ ├── secondary.tsx │ │ │ │ │ └── with-icon.tsx │ │ │ │ ├── calendar │ │ │ │ │ ├── date-picker.tsx │ │ │ │ │ ├── date-range-picker.tsx │ │ │ │ │ ├── demo.tsx │ │ │ │ │ └── with-presets.tsx │ │ │ │ ├── card │ │ │ │ │ ├── demo.tsx │ │ │ │ │ └── with-form.tsx │ │ │ │ ├── charts │ │ │ │ │ └── demo.tsx │ │ │ │ ├── checkbox │ │ │ │ │ ├── demo.tsx │ │ │ │ │ ├── disabled.tsx │ │ │ │ │ └── with-text.tsx │ │ │ │ ├── collapsible │ │ │ │ │ └── demo.tsx │ │ │ │ ├── command │ │ │ │ │ ├── combobox.tsx │ │ │ │ │ ├── demo.tsx │ │ │ │ │ ├── dialog.tsx │ │ │ │ │ ├── dropdown-menu.tsx │ │ │ │ │ └── popover.tsx │ │ │ │ ├── context-menu │ │ │ │ │ └── demo.tsx │ │ │ │ ├── dialog │ │ │ │ │ └── demo.tsx │ │ │ │ ├── dropdown-menu │ │ │ │ │ ├── checkboxes.tsx │ │ │ │ │ ├── demo.tsx │ │ │ │ │ └── radio-group.tsx │ │ │ │ ├── hover-card │ │ │ │ │ └── demo.tsx │ │ │ │ ├── icons │ │ │ │ │ └── demo.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── input │ │ │ │ │ ├── demo.tsx │ │ │ │ │ ├── disabled.tsx │ │ │ │ │ ├── file.tsx │ │ │ │ │ ├── with-button.tsx │ │ │ │ │ ├── with-label.tsx │ │ │ │ │ └── with-text.tsx │ │ │ │ ├── json-skeleton │ │ │ │ │ ├── demo.tsx │ │ │ │ │ └── example-json.json │ │ │ │ ├── label │ │ │ │ │ └── demo.tsx │ │ │ │ ├── menubar │ │ │ │ │ └── demo.tsx │ │ │ │ ├── navigation-menu │ │ │ │ │ └── demo.tsx │ │ │ │ ├── navlink │ │ │ │ │ └── demo.tsx │ │ │ │ ├── popover │ │ │ │ │ └── demo.tsx │ │ │ │ ├── progress │ │ │ │ │ └── demo.tsx │ │ │ │ ├── radio-group │ │ │ │ │ └── demo.tsx │ │ │ │ ├── scroll-area │ │ │ │ │ └── demo.tsx │ │ │ │ ├── select │ │ │ │ │ └── demo.tsx │ │ │ │ ├── separator │ │ │ │ │ └── demo.tsx │ │ │ │ ├── sheet │ │ │ │ │ ├── demo.tsx │ │ │ │ │ ├── position.tsx │ │ │ │ │ └── size.tsx │ │ │ │ ├── skeleton │ │ │ │ │ └── demo.tsx │ │ │ │ ├── slider │ │ │ │ │ └── demo.tsx │ │ │ │ ├── switch │ │ │ │ │ └── demo.tsx │ │ │ │ ├── tabs │ │ │ │ │ └── demo.tsx │ │ │ │ ├── textarea │ │ │ │ │ ├── demo.tsx │ │ │ │ │ ├── disabled.tsx │ │ │ │ │ ├── with-button.tsx │ │ │ │ │ ├── with-label.tsx │ │ │ │ │ └── with-text.tsx │ │ │ │ ├── toast │ │ │ │ │ ├── demo.tsx │ │ │ │ │ ├── destructive.tsx │ │ │ │ │ ├── simple.tsx │ │ │ │ │ ├── with-action.tsx │ │ │ │ │ └── with-title.tsx │ │ │ │ ├── toggle │ │ │ │ │ ├── demo.tsx │ │ │ │ │ ├── disabled.tsx │ │ │ │ │ ├── lg.tsx │ │ │ │ │ ├── outline.tsx │ │ │ │ │ ├── sm.tsx │ │ │ │ │ └── with-text.tsx │ │ │ │ ├── tooltip │ │ │ │ │ └── demo.tsx │ │ │ │ └── typography │ │ │ │ │ ├── all-demo.tsx │ │ │ │ │ ├── blockquote.tsx │ │ │ │ │ ├── demo.tsx │ │ │ │ │ ├── h1.tsx │ │ │ │ │ ├── h2.tsx │ │ │ │ │ ├── h3.tsx │ │ │ │ │ ├── h4.tsx │ │ │ │ │ ├── inline-code.tsx │ │ │ │ │ ├── large.tsx │ │ │ │ │ ├── lead.tsx │ │ │ │ │ ├── list.tsx │ │ │ │ │ ├── muted.tsx │ │ │ │ │ ├── p.tsx │ │ │ │ │ ├── small.tsx │ │ │ │ │ └── table.tsx │ │ │ ├── icons.tsx │ │ │ ├── index.ts │ │ │ ├── main-nav.tsx │ │ │ ├── mdx-components.tsx │ │ │ ├── mobile-nav.tsx │ │ │ ├── mode-toggle.tsx │ │ │ ├── page-header.tsx │ │ │ ├── pager.tsx │ │ │ ├── promo-video.tsx │ │ │ ├── sidebar-nav.tsx │ │ │ ├── site-footer.tsx │ │ │ ├── site-header.tsx │ │ │ ├── style-switcher.tsx │ │ │ ├── tailwind-indicator.tsx │ │ │ ├── theme-provider.tsx │ │ │ ├── toc.tsx │ │ │ └── ui │ │ │ │ ├── accordion.tsx │ │ │ │ ├── alert-dialog.tsx │ │ │ │ ├── alert.tsx │ │ │ │ ├── aspect-ratio.tsx │ │ │ │ ├── avatar.tsx │ │ │ │ ├── badge.tsx │ │ │ │ ├── box.tsx │ │ │ │ ├── button.tsx │ │ │ │ ├── calendar.tsx │ │ │ │ ├── card.tsx │ │ │ │ ├── charts.tsx │ │ │ │ ├── checkbox.tsx │ │ │ │ ├── collapsible.tsx │ │ │ │ ├── command.tsx │ │ │ │ ├── context-menu.tsx │ │ │ │ ├── dialog.tsx │ │ │ │ ├── dropdown-menu.tsx │ │ │ │ ├── hover-card.tsx │ │ │ │ ├── icons.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── input.tsx │ │ │ │ ├── label.tsx │ │ │ │ ├── menubar.tsx │ │ │ │ ├── navigation-menu.tsx │ │ │ │ ├── navlink.tsx │ │ │ │ ├── popover.tsx │ │ │ │ ├── progress.tsx │ │ │ │ ├── radio-group.tsx │ │ │ │ ├── scroll-area.tsx │ │ │ │ ├── select.tsx │ │ │ │ ├── separator.tsx │ │ │ │ ├── sheet.tsx │ │ │ │ ├── skeleton.tsx │ │ │ │ ├── slider.tsx │ │ │ │ ├── switch.tsx │ │ │ │ ├── tabs.tsx │ │ │ │ ├── textarea.tsx │ │ │ │ ├── toast.tsx │ │ │ │ ├── toaster.tsx │ │ │ │ ├── toggle.tsx │ │ │ │ ├── tooltip.tsx │ │ │ │ ├── typography.tsx │ │ │ │ └── use-toast.ts │ │ ├── config │ │ │ ├── components.ts │ │ │ ├── docs.ts │ │ │ └── site.ts │ │ ├── exports │ │ │ ├── Buttons │ │ │ │ ├── ActionButtons.tsx │ │ │ │ ├── SimpleButton.tsx │ │ │ │ ├── SubmitButton.tsx │ │ │ │ └── index.ts │ │ │ ├── DefaultFields.tsx │ │ │ ├── Fields │ │ │ │ ├── CheckboxField.tsx │ │ │ │ ├── DateField.tsx │ │ │ │ ├── FieldArray.tsx │ │ │ │ ├── FieldWrapper.tsx │ │ │ │ ├── JsonField.tsx │ │ │ │ ├── MultiChecboxField.tsx │ │ │ │ ├── MultiSelectField.tsx │ │ │ │ ├── MultiTextField.tsx │ │ │ │ ├── NumberField.tsx │ │ │ │ ├── PasswordField.tsx │ │ │ │ ├── RadioField.tsx │ │ │ │ ├── SelectField.tsx │ │ │ │ ├── SwitchField.tsx │ │ │ │ ├── TextAreaField.tsx │ │ │ │ ├── TextField.tsx │ │ │ │ └── index.ts │ │ │ ├── Forms │ │ │ │ ├── Collapse.tsx │ │ │ │ ├── FieldGroup.tsx │ │ │ │ ├── Form.tsx │ │ │ │ ├── FormElement.tsx │ │ │ │ ├── FormGrid.tsx │ │ │ │ ├── getGroupFields.tsx │ │ │ │ └── index.ts │ │ │ ├── Wizards │ │ │ │ ├── Footer.tsx │ │ │ │ ├── Header.tsx │ │ │ │ ├── MultiStepWizard.tsx │ │ │ │ ├── WizardStep.tsx │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ ├── hooks │ │ │ ├── use-lock-body.ts │ │ │ ├── use-mounted.ts │ │ │ └── use-mutation-observer.ts │ │ ├── index.ts │ │ ├── input.css │ │ ├── lib │ │ │ ├── events.ts │ │ │ ├── themes │ │ │ │ ├── dark.json │ │ │ │ └── light.json │ │ │ ├── toc.ts │ │ │ ├── utils.ts │ │ │ └── validations │ │ │ │ ├── log.ts │ │ │ │ └── og.ts │ │ ├── types │ │ │ ├── nav.ts │ │ │ └── unist.ts │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts └── types │ ├── .gitignore │ ├── package.json │ ├── post-build.js │ ├── pre-build.js │ ├── src │ ├── Field.ts │ ├── FieldConfig.ts │ ├── FormConfig.ts │ ├── FormLayout.ts │ ├── TutimFormReturn.ts │ ├── TutimOptions.ts │ ├── form.ts │ └── index.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts └── yarn.lock /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: Tutim Community Support 4 | url: https://discord.tutim.io 5 | about: Feel free to ask questions here for even faster reply. -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ### What change does this PR introduce? 7 | 8 | 9 | 10 | ### Reference? 11 | 12 | 13 | 14 | ### Other information 15 | 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | node_modules 3 | .npmrc -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "trailingComma": "es5", 4 | "singleQuote": true, 5 | "semi": true, 6 | "jsxSingleQuote": false, 7 | "tabWidth": 2, 8 | "quoteProps": "as-needed", 9 | "arrowParens": "always" 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Tutim Data Tools LTD 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 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | 4 | ## Reporting a Vulnerability 5 | 6 | Send a DM to an admin on our [Discord server](https://discord.tutim.io), or send an email to support@tutim.io 7 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [🍓 README](README.md) 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tutim/root", 3 | "version": "0.1.4", 4 | "description": "form infrastructure for web applications", 5 | "author": "Tutim ", 6 | "homepage": "https://tutim.io", 7 | "license": "MIT", 8 | "repository": "https://github.com/tutim-io/tutim", 9 | "private": true, 10 | "workspaces": [ 11 | "packages/*" 12 | ], 13 | "scripts": { 14 | "dev": "yarn workspace @tutim/example dev", 15 | "build": "yarn workspace @tutim/headless build && yarn workspace @tutim/fields build && yarn workspace @tutim/types build", 16 | "storybook": "yarn workspace @tutim/fields storybook", 17 | "pkg:patch": "yarn workspace @tutim/types version --patch --no-git-tag-version && yarn workspace @tutim/headless version --patch --no-git-tag-version && yarn workspace @tutim/fields version --patch --no-git-tag-version && yarn version --patch --no-git-tag-version", 18 | "pkg:tag": "git add . && git commit -m v${npm_package_version} && git push && git tag v${npm_package_version} && git push --tags", 19 | "pkg:deploy": "yarn workspace @tutim/types pkg && yarn workspace @tutim/headless pkg && yarn workspace @tutim/fields pkg", 20 | "pkg": "yarn pkg:patch && yarn pkg:tag && yarn pkg:deploy" 21 | }, 22 | "keywords": [ 23 | "form", 24 | "form-builder", 25 | "form-infrastructure", 26 | "tutim", 27 | "headless-forms", 28 | "hook-form", 29 | "react" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /packages/example/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:react/recommended", 9 | "plugin:@typescript-eslint/recommended" 10 | ], 11 | "parser": "@typescript-eslint/parser", 12 | "parserOptions": { 13 | "ecmaFeatures": { 14 | "jsx": true 15 | }, 16 | "ecmaVersion": 12, 17 | "sourceType": "module" 18 | }, 19 | "plugins": [ 20 | "react", 21 | "@typescript-eslint" 22 | ], 23 | "rules": { 24 | "react/prop-types": "off", 25 | "react/react-in-jsx-scope": "off" 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /packages/example/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /packages/example/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "trailingComma": "es5", 4 | "singleQuote": true, 5 | "semi": true, 6 | "jsxSingleQuote": false, 7 | "tabWidth": 2, 8 | "quoteProps": "as-needed", 9 | "arrowParens": "always" 10 | } 11 | -------------------------------------------------------------------------------- /packages/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tutim Examples 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tutim/example", 3 | "version": "0.0.0", 4 | "description": "form infrastructure for web applications", 5 | "author": "Tutim ", 6 | "homepage": "https://tutim.io", 7 | "license": "MIT", 8 | "repository": "https://github.com/tutim-io/tutim", 9 | "type": "module", 10 | "scripts": { 11 | "dev": "vite", 12 | "build": "tsc && vite build", 13 | "preview": "vite preview" 14 | }, 15 | "dependencies": { 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0", 18 | "react-router-dom": "^6.13.0" 19 | }, 20 | "devDependencies": { 21 | "@types/react": "^18.0.26", 22 | "@types/react-dom": "^18.0.9", 23 | "@typescript-eslint/eslint-plugin": "^5.47.0", 24 | "@typescript-eslint/parser": "^5.47.0", 25 | "@vitejs/plugin-react": "^3.0.0", 26 | "eslint": "^8.30.0", 27 | "eslint-config-airbnb": "^19.0.4", 28 | "eslint-plugin-import": "^2.26.0", 29 | "eslint-plugin-jsx-a11y": "^6.6.1", 30 | "eslint-plugin-react": "^7.31.11", 31 | "eslint-plugin-react-hooks": "^4.6.0", 32 | "typescript": "^4.9.4", 33 | "vite": "^4.0.3" 34 | }, 35 | "keywords": [ 36 | "form", 37 | "form-builder", 38 | "form-infrastructure", 39 | "tutim", 40 | "headless-forms", 41 | "hook-form", 42 | "react" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /packages/example/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/example/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { defaultFields, SelectField } from '@tutim/fields'; //material-ui 2 | import { TutimProvider } from '@tutim/headless'; 3 | import React from 'react'; 4 | 5 | import formConfig from './basic.json'; 6 | import { HeadlessWizard } from './Wizards/HeadlessWizard'; 7 | import { AllOfExamples } from './Wizards/AllOfExamples'; 8 | 9 | const contextOptions = { 10 | clientId: '2', 11 | forms: { ['form-config-1337']: formConfig }, 12 | }; 13 | 14 | const examples: Record JSX.Element> = { 15 | HeadlessWizard, 16 | AllOfExamples, 17 | }; 18 | 19 | const options = Object.keys(examples).map((key, ix) => ({ value: key, label: `${ix}) => ${key}` })); 20 | 21 | function App(): React.ReactNode { 22 | const [exampleKey, setExample] = React.useState(options[options.length - 1].value); 23 | const Example = examples[exampleKey]; 24 | 25 | return ( 26 |
27 |
28 |

Pick any form example - Material UI

29 | setExample(e.target.value), 34 | }} 35 | /> 36 |
37 | 38 | {} 39 | 40 |
41 | ); 42 | } 43 | 44 | export default App; 45 | -------------------------------------------------------------------------------- /packages/example/src/Forms/BasicFieldsScheme/BasicFieldsScheme.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useForm } from '@tutim/headless'; 3 | import config from './basic.json'; 4 | 5 | export const BasicFieldsScheme = (): JSX.Element => { 6 | const { fields, nativeSubmit } = useForm(config); 7 | 8 | const onSubmit = (data: any) => { 9 | alert(JSON.stringify(data)); 10 | }; 11 | 12 | return ( 13 |
14 | {fields} 15 | 16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /packages/example/src/Forms/BasicFieldsScheme/basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": [ 3 | { 4 | "key": "firstName", 5 | "label": "Name", 6 | "isDisabled": false, 7 | "type": "text", 8 | "isRequired": true 9 | }, 10 | { 11 | "key": "lastName", 12 | "label": "Last Name", 13 | "isDisabled": false, 14 | "type": "text", 15 | "isRequired": true 16 | }, 17 | { 18 | "key": "role", 19 | "label": "Role", 20 | "isDisabled": false, 21 | "type": "select", 22 | "options": [ 23 | { "value": "admin", "label": "Administrator" }, 24 | { "value": "viewer", "label": "Viewer" }, 25 | { "value": "editor", "label": "Editor" } 26 | ], 27 | "isRequired": true 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /packages/example/src/Forms/BasicFieldsScheme/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './BasicFieldsScheme'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/BasicFieldsValues/BasicFieldsValues.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useForm } from '@tutim/headless'; 3 | import config from './basic.json'; 4 | 5 | export const BasicFieldsValues = (): JSX.Element => { 6 | const { fields, handleSubmit } = useForm(config); 7 | 8 | const onSubmit = (data: any) => { 9 | alert(JSON.stringify(data)); 10 | }; 11 | 12 | return ( 13 |
14 | {fields} 15 | 16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /packages/example/src/Forms/BasicFieldsValues/basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": [ 3 | { 4 | "key": "firstName", 5 | "label": "Name", 6 | "isDisabled": false, 7 | "type": "text", 8 | "isRequired": true, 9 | "defaultValue": "defaultName" 10 | }, 11 | { 12 | "key": "lastName", 13 | "label": "Last Name", 14 | "isDisabled": false, 15 | "type": "text", 16 | "isRequired": true 17 | }, 18 | { 19 | "key": "role", 20 | "label": "Role", 21 | "isDisabled": false, 22 | "type": "select", 23 | "options": [ 24 | { "value": "admin", "label": "Administrator" }, 25 | { "value": "viewer", "label": "Viewer" }, 26 | { "value": "editor", "label": "Editor" } 27 | ], 28 | "isRequired": true, 29 | "defaultValue": "editor" 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /packages/example/src/Forms/BasicFieldsValues/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './BasicFieldsValues'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/BasicWizard/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './BasicWizard'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/Builder/Builder.tsx: -------------------------------------------------------------------------------- 1 | import { TutimWizard } from '@tutim/fields'; 2 | import config from './config.json'; 3 | 4 | const onSubmit = ({ data, schema }: any) => { 5 | alert(JSON.stringify(data)); 6 | console.log(data); 7 | }; 8 | 9 | export const Builder = (): JSX.Element => { 10 | return ; 11 | }; 12 | -------------------------------------------------------------------------------- /packages/example/src/Forms/Builder/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Builder'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/ConfigProvider/ConfigProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { TutimWizard } from '@tutim/fields'; 3 | import { useFormConfig, useTutimOptions } from '@tutim/headless'; 4 | import { getDataAsync } from '../../utils'; 5 | 6 | export const BasicConfigProvider = ({ formId }: { formId: string }): JSX.Element => { 7 | const config = useFormConfig(formId); 8 | const onSubmit = (data: any) => alert(JSON.stringify(data)); 9 | 10 | return ; 11 | }; 12 | 13 | export const ConfigProvider = (): JSX.Element => { 14 | const [formId, setFormId] = React.useState('form-config-1337'); 15 | const { setOptions } = useTutimOptions(); 16 | 17 | React.useEffect(() => { 18 | getDataAsync().then((data) => { 19 | const config = { 20 | fields: [ 21 | { 22 | key: 'name', 23 | label: data.firstName, 24 | type: 'text', 25 | }, 26 | ], 27 | }; 28 | setOptions({ forms: { ['form-config-async-1337']: config } }); 29 | }); 30 | }, []); 31 | 32 | return ( 33 |
34 | 35 | 38 |
39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /packages/example/src/Forms/ConfigProvider/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './ConfigProvider'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/ControlledForm/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './ControlledForm'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/DeepNested/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './DeepNested'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/DesignSystem/DesignSystem.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useForm } from '@tutim/headless'; 3 | import { FormElement } from '@tutim/fields'; 4 | import config from './basic.json'; 5 | 6 | export const DesignSystem = (): JSX.Element => { 7 | const { handleSubmit, fieldsByKey } = useForm(config); 8 | 9 | const onSubmit = (data: any) => { 10 | alert(JSON.stringify(data)); 11 | }; 12 | 13 | return ; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/example/src/Forms/DesignSystem/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './DesignSystem'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/DynamicState/DynamicState.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useForm } from '@tutim/headless'; 3 | import config from './basic.json'; 4 | import { getSelectOptionsAsync } from '../../utils'; 5 | 6 | const additionalField = { 7 | key: 'additional', 8 | label: 'Additional Name', 9 | isDisabled: false, 10 | type: 'text', 11 | isRequired: true, 12 | }; 13 | 14 | const overrideField = { 15 | key: 'lastName', 16 | label: 'Last Name Changed', 17 | isDisabled: true, 18 | type: 'text', 19 | isRequired: true, 20 | }; 21 | 22 | export const DynamicState = (): JSX.Element => { 23 | const [stateConfig, setStateConfig] = React.useState([]); 24 | const { fields, handleSubmit } = useForm(config, { fields: stateConfig }); 25 | 26 | React.useEffect(() => { 27 | getSelectOptionsAsync().then((options) => { 28 | setStateConfig([{ key: 'role', options }]); 29 | }); 30 | }, []); 31 | 32 | const onSubmit = (data: any) => { 33 | alert(JSON.stringify(data)); 34 | }; 35 | 36 | const onButtonClick = () => { 37 | setStateConfig((Math.random() > 0.5 ? [additionalField, overrideField] : []) as any); 38 | }; 39 | 40 | return ( 41 |
42 | {fields} 43 | 44 | 47 |
48 | ); 49 | }; 50 | -------------------------------------------------------------------------------- /packages/example/src/Forms/DynamicState/basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": [ 3 | { 4 | "key": "firstName", 5 | "label": "Name", 6 | "isDisabled": false, 7 | "type": "text", 8 | "isRequired": true 9 | }, 10 | { 11 | "key": "lastName", 12 | "label": "Last Name", 13 | "isDisabled": false, 14 | "type": "text", 15 | "isRequired": true 16 | }, 17 | { 18 | "key": "role", 19 | "label": "Role", 20 | "isDisabled": false, 21 | "type": "select", 22 | "options": [ 23 | { "value": "admin", "label": "Administrator" }, 24 | { "value": "viewer", "label": "Viewer" }, 25 | { "value": "editor", "label": "Editor" } 26 | ], 27 | "isRequired": true 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /packages/example/src/Forms/DynamicState/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './DynamicState'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/FieldLogicRender/FieldLogicRender.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useForm } from '@tutim/headless'; 3 | import config from './basic.json'; 4 | 5 | export const FieldLogicRender = (): JSX.Element => { 6 | const { fields, handleSubmit } = useForm(config); 7 | 8 | const onSubmit = (data: any) => { 9 | alert(JSON.stringify(data)); 10 | }; 11 | 12 | return ( 13 |
14 | {fields} 15 | 16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /packages/example/src/Forms/FieldLogicRender/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './FieldLogicRender'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/FieldValidations/FieldValidations.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useForm } from '@tutim/headless'; 3 | import config from './basic.json'; 4 | 5 | export const FieldValidations = (): JSX.Element => { 6 | const { fields, handleSubmit } = useForm(config); 7 | 8 | const onSubmit = (data: any) => { 9 | alert(JSON.stringify(data)); 10 | }; 11 | 12 | return ( 13 |
14 | {fields} 15 | 16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /packages/example/src/Forms/FieldValidations/basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": [ 3 | { 4 | "key": "firstName", 5 | "label": "Name", 6 | "isDisabled": false, 7 | "type": "text", 8 | "isRequired": true, 9 | "validations": { 10 | "minLength": { "value": 5, "message": "Minimum 5 letters" }, 11 | "pattern": { "value": "^(john)", "message": "Has to start with john" } 12 | } 13 | }, 14 | { 15 | "key": "lastName", 16 | "label": "Last Name", 17 | "isDisabled": false, 18 | "type": "text", 19 | "isRequired": true, 20 | "validations": { 21 | "maxLength": { "value": 1, "message": "Maximum 1 letter" } 22 | } 23 | }, 24 | { 25 | "key": "role", 26 | "label": "Role", 27 | "isDisabled": false, 28 | "type": "select", 29 | "options": [ 30 | { "value": "admin", "label": "Administrator" }, 31 | { "value": "viewer", "label": "Viewer" }, 32 | { "value": "editor", "label": "Editor" } 33 | ] 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /packages/example/src/Forms/FieldValidations/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './FieldValidations'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/FormMeta/FormMeta.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { FormConfig } from '@tutim/types'; 3 | import { TutimWizard } from '@tutim/fields'; 4 | 5 | const config: FormConfig = { 6 | meta: { title: 'My Form' }, 7 | 8 | fields: [ 9 | { 10 | key: 'firstName', 11 | label: 'First Name', 12 | type: 'text', 13 | }, 14 | { 15 | key: 'lastName', 16 | label: 'Last Name', 17 | type: 'text', 18 | }, 19 | ], 20 | }; 21 | 22 | export const FormMeta = (): JSX.Element => { 23 | return ; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/example/src/Forms/FormMeta/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './FormMeta'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/FormWizard/FormWizard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { TutimWizard } from '@tutim/fields'; 3 | import config from './basic.json'; 4 | import { FieldsPerRow } from '@tutim/types'; 5 | 6 | const layout = { 7 | wizard: {}, 8 | groupConfigs: { 9 | groups: [ 10 | { 11 | key: 'name', 12 | title: 'Name', 13 | fields: ['firstName', 'lastName'], 14 | layout: { fieldsPerRow: FieldsPerRow.Two }, 15 | }, 16 | { key: 'role', title: 'Role', fields: ['role'], layout: { fieldsPerRow: FieldsPerRow.One } }, 17 | { 18 | key: 'settings', 19 | title: 'Settings', 20 | fields: ['hosting', 'agree', 'enable'], 21 | layout: { fieldsPerRow: FieldsPerRow.Three }, 22 | }, 23 | ], 24 | }, 25 | } as any; 26 | 27 | export const FormWizard = (): JSX.Element => { 28 | const onSubmit = (data: any) => alert(JSON.stringify(data)); 29 | 30 | return ; 31 | }; 32 | -------------------------------------------------------------------------------- /packages/example/src/Forms/FormWizard/basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "layout": {}, 3 | "fields": [ 4 | { 5 | "key": "firstName", 6 | "label": "First Name", 7 | "isDisabled": false, 8 | "type": "text", 9 | "isRequired": true 10 | }, 11 | { 12 | "key": "lastName", 13 | "label": "Last Name", 14 | "isDisabled": false, 15 | "type": "text", 16 | "isRequired": true 17 | }, 18 | { 19 | "key": "role", 20 | "label": "Role", 21 | "isDisabled": false, 22 | "type": "select", 23 | "options": [ 24 | { "value": "admin", "label": "Administrator" }, 25 | { "value": "viewer", "label": "Viewer" }, 26 | { "value": "editor", "label": "Editor" } 27 | ], 28 | "isRequired": true, 29 | "defaultValue": "editor" 30 | }, 31 | { 32 | "key": "hosting", 33 | "label": "Hosting", 34 | "isDisabled": false, 35 | "type": "radio", 36 | "options": [ 37 | { "value": "self", "label": "Self-Host" }, 38 | { "value": "cloud", "label": "Cloud" } 39 | ], 40 | "isRequired": true, 41 | "defaultValue": "self" 42 | }, 43 | { 44 | "key": "agree", 45 | "label": "Agree to our terms and conditions", 46 | "isDisabled": false, 47 | "type": "checkbox", 48 | "isRequired": true 49 | }, 50 | { 51 | "key": "enable", 52 | "label": "Enable this feature", 53 | "isDisabled": false, 54 | "type": "switch", 55 | "isRequired": true 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /packages/example/src/Forms/FormWizard/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './FormWizard'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/InitialFields/InitialFields.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useForm } from '@tutim/headless'; 3 | import config from './basic.json'; 4 | import { getDataAsync } from '../../utils'; 5 | 6 | export const InitialFields = (): JSX.Element => { 7 | const { fields, handleSubmit, useFormInit } = useForm(config); 8 | const isInitializing = useFormInit(getDataAsync); 9 | 10 | const onSubmit = (data: any) => { 11 | alert(JSON.stringify(data)); 12 | }; 13 | 14 | if (!isInitializing) return
Loading
; 15 | 16 | return ( 17 |
18 | {fields} 19 | 20 |
21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /packages/example/src/Forms/InitialFields/basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": [ 3 | { 4 | "key": "firstName", 5 | "label": "Name", 6 | "isDisabled": false, 7 | "type": "text", 8 | "isRequired": true, 9 | "defaultValue": "defaultName" 10 | }, 11 | { 12 | "key": "lastName", 13 | "label": "Last Name", 14 | "isDisabled": true, 15 | "type": "text", 16 | "isRequired": true 17 | }, 18 | { 19 | "key": "role", 20 | "label": "Role", 21 | "isDisabled": false, 22 | "type": "select", 23 | "options": [ 24 | { "value": "admin", "label": "Administrator" }, 25 | { "value": "viewer", "label": "Viewer" }, 26 | { "value": "editor", "label": "Editor" } 27 | ], 28 | "isRequired": true, 29 | "defaultValue": "editor" 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /packages/example/src/Forms/InitialFields/index.ts: -------------------------------------------------------------------------------- 1 | export * from './InitialFields'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/LayoutConfig/LayoutConfig.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { TutimWizard } from '@tutim/fields'; 3 | import config from './basic.json'; 4 | import { FieldsPerRow } from '@tutim/types'; 5 | 6 | const layout = { 7 | style: { color: 'green' }, 8 | groupConfigs: { 9 | groups: [ 10 | { 11 | key: 'name', 12 | title: 'Name', 13 | fields: ['firstName', 'lastName'], 14 | layout: { fieldsPerRow: FieldsPerRow.Two }, 15 | }, 16 | { key: 'role', title: 'Role', fields: ['role'], layout: { fieldsPerRow: FieldsPerRow.One } }, 17 | { 18 | key: 'settings', 19 | title: 'Settings', 20 | fields: ['hosting', 'agree', 'enable'], 21 | layout: { fieldsPerRow: FieldsPerRow.Three }, 22 | }, 23 | ], 24 | }, 25 | } as any; 26 | 27 | export const LayoutConfig = (): JSX.Element => { 28 | const onSubmit = (data: any) => alert(JSON.stringify(data)); 29 | 30 | return ; 31 | }; 32 | -------------------------------------------------------------------------------- /packages/example/src/Forms/LayoutConfig/basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "layout": {}, 3 | "fields": [ 4 | { 5 | "key": "firstName", 6 | "label": "First Name", 7 | "isDisabled": false, 8 | "type": "text", 9 | "isRequired": true 10 | }, 11 | { 12 | "key": "lastName", 13 | "label": "Last Name", 14 | "isDisabled": false, 15 | "type": "text", 16 | "isRequired": true 17 | }, 18 | { 19 | "key": "role", 20 | "label": "Role", 21 | "isDisabled": false, 22 | "type": "select", 23 | "options": [ 24 | { "value": "admin", "label": "Administrator" }, 25 | { "value": "viewer", "label": "Viewer" }, 26 | { "value": "editor", "label": "Editor" } 27 | ], 28 | "isRequired": true, 29 | "defaultValue": "editor" 30 | }, 31 | { 32 | "key": "hosting", 33 | "label": "Hosting", 34 | "isDisabled": false, 35 | "type": "radio", 36 | "options": [ 37 | { "value": "self", "label": "Self-Host" }, 38 | { "value": "cloud", "label": "Cloud" } 39 | ], 40 | "isRequired": true, 41 | "defaultValue": "self" 42 | }, 43 | { 44 | "key": "agree", 45 | "label": "Agree to our terms and conditions", 46 | "isDisabled": false, 47 | "type": "checkbox", 48 | "isRequired": true 49 | }, 50 | { 51 | "key": "enable", 52 | "label": "Enable this feature", 53 | "isDisabled": false, 54 | "type": "switch", 55 | "isRequired": true 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /packages/example/src/Forms/LayoutConfig/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './LayoutConfig'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/MultiNested/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './MultiNested'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/SchemaServe/SchemaServe.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { TutimWizard } from '@tutim/fields'; 3 | 4 | export const SchemaServe = (): JSX.Element => { 5 | return ; 6 | }; 7 | -------------------------------------------------------------------------------- /packages/example/src/Forms/SchemaServe/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './SchemaServe'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/SimpleForm/SimpleForm.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { TutimWizard } from '@tutim/fields'; 3 | 4 | const config = { 5 | fields: [ 6 | { 7 | key: 'firstName', 8 | label: 'First Name', 9 | type: 'text', 10 | }, 11 | { 12 | key: 'lastName', 13 | label: 'Last Name', 14 | type: 'text', 15 | }, 16 | ], 17 | }; 18 | 19 | export const SimpleForm = (): JSX.Element => { 20 | return ; 21 | }; 22 | -------------------------------------------------------------------------------- /packages/example/src/Forms/SimpleForm/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './SimpleForm'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/TutimForm/TutimForm.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { TutimWizard } from '@tutim/fields'; 3 | import configBase from './basic.json'; 4 | import { Field } from '@tutim/types'; 5 | 6 | const CustomField: Field = ({ inputProps }) => { 7 | const { value, onChange } = inputProps; 8 | const onClick = () => onChange(value + 2); 9 | return ( 10 | 13 | ); 14 | }; 15 | 16 | const customField = { 17 | key: 'clicker', 18 | label: 'Click Me', 19 | type: 'custom', 20 | defaultValue: 0, 21 | Field: CustomField, 22 | }; 23 | 24 | const config = { ...configBase, fields: [...configBase.fields, customField] }; 25 | 26 | const initialValues = { 27 | firstName: 'sami', 28 | agree: true, 29 | enable: true, 30 | hosting: 'cloud', 31 | }; 32 | 33 | export const TutimForm = (): JSX.Element => { 34 | const onSubmit = (data: any) => { 35 | alert(JSON.stringify(data)); 36 | }; 37 | 38 | return ; 39 | }; 40 | -------------------------------------------------------------------------------- /packages/example/src/Forms/TutimForm/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './TutimForm'; 2 | -------------------------------------------------------------------------------- /packages/example/src/Forms/index.ts: -------------------------------------------------------------------------------- 1 | export * from './BasicFieldsScheme'; 2 | export * from './BasicFieldsValues'; 3 | export * from './InitialFields'; 4 | export * from './FieldValidations'; 5 | export * from './DynamicState'; 6 | export * from './FieldLogicRender'; 7 | export * from './DesignSystem'; 8 | export * from './TutimForm'; 9 | export * from './LayoutConfig'; 10 | export * from './FormWizard'; 11 | export * from './ConfigProvider'; 12 | export * from './SimpleForm'; 13 | export * from './MultiNested'; 14 | export * from './DeepNested'; 15 | export * from './Builder'; 16 | export * from './ControlledForm'; 17 | export * from './SchemaServe'; 18 | export * from './FormMeta'; 19 | export * from './BasicWizard'; 20 | export * from './LogicWizard'; 21 | -------------------------------------------------------------------------------- /packages/example/src/basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "layout": {}, 3 | "fields": [ 4 | { 5 | "key": "firstName", 6 | "label": "First Name (Global)", 7 | "isDisabled": false, 8 | "type": "text", 9 | "isRequired": true 10 | }, 11 | { 12 | "key": "lastName", 13 | "label": "Last Name", 14 | "isDisabled": false, 15 | "type": "text", 16 | "isRequired": true 17 | }, 18 | { 19 | "key": "role", 20 | "label": "Role", 21 | "isDisabled": false, 22 | "type": "select", 23 | "options": [ 24 | { "value": "admin", "label": "Administrator" }, 25 | { "value": "viewer", "label": "Viewer" }, 26 | { "value": "editor", "label": "Editor" } 27 | ], 28 | "isRequired": true, 29 | "defaultValue": "editor" 30 | }, 31 | { 32 | "key": "hosting", 33 | "label": "Hosting", 34 | "isDisabled": false, 35 | "type": "radio", 36 | "options": [ 37 | { "value": "self", "label": "Self-Host" }, 38 | { "value": "cloud", "label": "Cloud" } 39 | ], 40 | "isRequired": true, 41 | "defaultValue": "self" 42 | }, 43 | { 44 | "key": "agree", 45 | "label": "Agree to our terms and conditions", 46 | "isDisabled": false, 47 | "type": "checkbox", 48 | "isRequired": true 49 | }, 50 | { 51 | "key": "enable", 52 | "label": "Enable this feature", 53 | "isDisabled": false, 54 | "type": "switch", 55 | "isRequired": true 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /packages/example/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | // import App from './App'; 4 | import App from './ShadcnApp'; 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(); 7 | -------------------------------------------------------------------------------- /packages/example/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export const getDataAsync = (): Promise> => { 2 | console.log('requesting data asynchronously'); 3 | return new Promise((resolve, reject) => { 4 | setTimeout(() => { 5 | resolve({ 6 | firstName: 'John' + Math.floor(Math.random() * 100), 7 | lastName: 'Doe', 8 | age: 30, 9 | }); 10 | }, 1000); 11 | }); 12 | }; 13 | 14 | export const getSelectOptionsAsync = (): Promise> => { 15 | return new Promise((resolve, reject) => { 16 | setTimeout(() => { 17 | resolve([ 18 | { value: 'option1', label: 'Option 1' }, 19 | { value: 'option2', label: 'Option 2' }, 20 | ]); 21 | }, 1000); 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /packages/example/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /packages/example/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/example/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /packages/fields/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:react/recommended", 9 | "plugin:@typescript-eslint/recommended" 10 | ], 11 | "parser": "@typescript-eslint/parser", 12 | "parserOptions": { 13 | "ecmaFeatures": { 14 | "jsx": true 15 | }, 16 | "ecmaVersion": 12, 17 | "sourceType": "module" 18 | }, 19 | "plugins": [ 20 | "react", 21 | "@typescript-eslint" 22 | ], 23 | "rules": { 24 | "react/prop-types": "off", 25 | "react/react-in-jsx-scope": "off" 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /packages/fields/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /packages/fields/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "trailingComma": "es5", 4 | "singleQuote": true, 5 | "semi": true, 6 | "jsxSingleQuote": false, 7 | "tabWidth": 2, 8 | "quoteProps": "as-needed", 9 | "arrowParens": "always" 10 | } 11 | -------------------------------------------------------------------------------- /packages/fields/.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "stories": [ 3 | "../src/**/*.stories.mdx", 4 | "../src/**/*.stories.@(js|jsx|ts|tsx)" 5 | ], 6 | "addons": [ 7 | "@storybook/addon-links", 8 | "@storybook/addon-essentials" 9 | ] 10 | } -------------------------------------------------------------------------------- /packages/fields/.storybook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "commonjs" 3 | } 4 | -------------------------------------------------------------------------------- /packages/fields/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | 2 | export const parameters = { 3 | actions: { argTypesRegex: "^on[A-Z].*" }, 4 | } -------------------------------------------------------------------------------- /packages/fields/post-build.js: -------------------------------------------------------------------------------- 1 | import fs from "fs/promises"; 2 | 3 | 4 | const pkgPath = "package.json"; 5 | 6 | fs.readFile(pkgPath, 'utf-8').then(async (f) => { 7 | const json = JSON.parse(f); 8 | const pkgJson = { 9 | ...json, dependencies: undefined, devDependencies: undefined, 10 | scripts: undefined, main: undefined, module: undefined, 11 | files: undefined, types: undefined, 12 | } 13 | await fs.writeFile(`dist/${pkgPath}`, JSON.stringify(pkgJson, null, 2)) 14 | delete json.exports; 15 | delete json.types; 16 | await fs.writeFile(pkgPath, JSON.stringify(json, null, 2) + '\n') 17 | }); 18 | -------------------------------------------------------------------------------- /packages/fields/pre-build.js: -------------------------------------------------------------------------------- 1 | import fs from "fs/promises"; 2 | 3 | 4 | const pkgPath = "package.json"; 5 | 6 | fs.readFile(pkgPath, 'utf-8').then(f => { 7 | const pkgJson = JSON.parse(f); 8 | pkgJson.exports = { 9 | ".": { 10 | "import": "./fields.es.js", 11 | "require": "./fields.umd.js" 12 | } 13 | } 14 | pkgJson.types = "./dist/index.d.ts" 15 | 16 | fs.writeFile(pkgPath, JSON.stringify(pkgJson, null, 2)) 17 | }); 18 | -------------------------------------------------------------------------------- /packages/fields/src/Buttons/ActionButtons.tsx: -------------------------------------------------------------------------------- 1 | import AddIcon from '@mui/icons-material/Add'; 2 | import DeleteIcon from '@mui/icons-material/Delete'; 3 | import RemoveIcon from '@mui/icons-material/Remove'; 4 | import EditIcon from '@mui/icons-material/Edit'; 5 | import IconButton from '@mui/material/IconButton'; 6 | import Tooltip from '@mui/material/Tooltip'; 7 | 8 | export const AddButton = ({ onClick }) => { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | }; 17 | 18 | export const RemoveButton = ({ onClick }) => { 19 | return ( 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | }; 27 | 28 | export const DeleteButton = ({ onClick }) => { 29 | return ( 30 | 31 | 32 | 33 | 34 | 35 | ); 36 | }; 37 | 38 | export const EditButton = ({ onClick }) => { 39 | return ( 40 | 41 | 42 | 43 | 44 | 45 | ); 46 | }; 47 | -------------------------------------------------------------------------------- /packages/fields/src/Buttons/Button.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MuiButton from '@mui/material/Button'; 3 | 4 | export const Button = ({ label, onClick }: { label: string; onClick: () => void }) => { 5 | return ( 6 | 7 | {label} 8 | 9 | ); 10 | }; 11 | -------------------------------------------------------------------------------- /packages/fields/src/Buttons/SubmitButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box } from '@mui/material'; 3 | import Button from '@mui/material/Button'; 4 | import { FormLayout } from '@tutim/types'; 5 | 6 | export const SubmitButton = ({ label = 'Submit', display = true }: FormLayout['submit'] = {}) => { 7 | if (!display) return null; 8 | return ( 9 | 10 | 13 | 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /packages/fields/src/Buttons/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SubmitButton'; 2 | export * from './Button'; 3 | export * from './ActionButtons'; 4 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/CheckboxField.tsx: -------------------------------------------------------------------------------- 1 | import { Field } from '@tutim/types'; 2 | import { Checkbox, FormControlLabel } from '@mui/material'; 3 | import { FieldWrapper } from './utils'; 4 | 5 | export const CheckboxField: Field = ({ fieldConfig, inputProps: { value = false, onChange }, fieldState }) => { 6 | const { key, helperText } = fieldConfig; 7 | 8 | return ( 9 | 10 | } /> 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/DateField.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Field } from '@tutim/types'; 3 | import { TextField as MuiTextField } from '@mui/material'; 4 | import { FieldWrapper } from './utils'; 5 | 6 | export const DateField: Field = ({ fieldConfig, inputProps: { value, onChange }, fieldState }) => { 7 | const { key, isRequired, isDisabled, placeholder } = fieldConfig; 8 | 9 | const onInputChange = (event: React.ChangeEvent) => { 10 | onChange(event.currentTarget.value); 11 | }; 12 | 13 | return ( 14 | 15 | 29 | 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/JsonField.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Field } from '@tutim/types'; 3 | import ReactJson from 'react-json-view'; 4 | import { FieldWrapper } from './utils'; 5 | 6 | export const JsonField: Field = ({ fieldConfig, inputProps: { value, onChange }, fieldState }) => { 7 | const onEdit = onChange ? ({ updated_src }: any) => onChange(updated_src) : undefined; 8 | 9 | return ( 10 | 11 | 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/MultiSelectField.tsx: -------------------------------------------------------------------------------- 1 | import { Field } from '@tutim/types'; 2 | import { TextField, Autocomplete } from '@mui/material'; 3 | import { FieldWrapper } from './utils'; 4 | 5 | export const MultiSelectField: Field = ({ fieldConfig, inputProps: { value = [], onChange }, fieldState }) => { 6 | const { isDisabled, options = [] } = fieldConfig; 7 | 8 | const handleChange = (event, newValue) => { 9 | onChange(newValue.map((option) => option.value)); 10 | }; 11 | 12 | return ( 13 | 14 | value.includes(option.value))} 18 | onChange={handleChange} 19 | getOptionLabel={(option) => option.label} 20 | renderInput={(params) => ( 21 | 22 | )} 23 | fullWidth 24 | size="small" 25 | disabled={isDisabled} 26 | /> 27 | 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/MultiTextField.tsx: -------------------------------------------------------------------------------- 1 | import { Field } from '@tutim/types'; 2 | import { TextField as MuiTextField, Autocomplete } from '@mui/material'; 3 | import { FieldWrapper } from './utils'; 4 | 5 | export const MultiTextField: Field = ({ fieldConfig, inputProps: { value = [], onChange }, fieldState }) => { 6 | const { isDisabled } = fieldConfig; 7 | 8 | return ( 9 | 10 | onChange(value)} 12 | value={value} 13 | multiple 14 | options={[]} 15 | freeSolo 16 | renderInput={(params) => } 17 | fullWidth 18 | size="small" 19 | disabled={isDisabled} 20 | /> 21 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/NumberField.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Field } from '@tutim/types'; 3 | import { TextField as MuiTextField } from '@mui/material'; 4 | import { FieldWrapper } from './utils'; 5 | 6 | export const NumberField: Field = ({ fieldConfig, inputProps: { value, onChange }, fieldState }) => { 7 | const { key, isRequired, isDisabled, placeholder } = fieldConfig; 8 | 9 | const onInputChange = (event: React.ChangeEvent) => { 10 | onChange(event.currentTarget.valueAsNumber); 11 | }; 12 | 13 | return ( 14 | 15 | 29 | 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/PasswordField.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Field } from '@tutim/types'; 3 | import { TextField as MuiTextField } from '@mui/material'; 4 | import { FieldWrapper } from './utils'; 5 | 6 | export const PasswordField: Field = ({ fieldConfig, inputProps: { value, onChange }, fieldState }) => { 7 | const { key, isRequired, isDisabled, placeholder } = fieldConfig; 8 | 9 | const onInputChange = (event: React.ChangeEvent) => { 10 | onChange(event.currentTarget.value); 11 | }; 12 | 13 | return ( 14 | 15 | 29 | 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/RadioField.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Radio, RadioGroup as MuiRadioGroup, FormControlLabel, Grid } from '@mui/material'; 3 | import { Option, Field } from '@tutim/types'; 4 | import { FieldWrapper } from './utils'; 5 | 6 | export const RadioField: Field = (props) => { 7 | const { fieldConfig, inputProps, fieldState } = props; 8 | const { value, onChange } = inputProps; 9 | const { options = [], custom } = fieldConfig; 10 | 11 | const isHorizontal = custom?.orientation === 'vertical' ? false : true; 12 | 13 | const childOptions = options.map((option: Option) => ( 14 | 15 | } label={option.label} /> 16 | 17 | )); 18 | 19 | return ( 20 | 21 | 22 | 23 | {childOptions} 24 | 25 | 26 | 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/SelectField.tsx: -------------------------------------------------------------------------------- 1 | import { Option, Field } from '@tutim/types'; 2 | import { TextField as MuiTextField, MenuItem as MuiMenuItem } from '@mui/material'; 3 | import { FieldWrapper } from './utils'; 4 | 5 | export const SelectField: Field = ({ fieldConfig, inputProps: { value, onChange }, fieldState }) => { 6 | const { key, isRequired, isDisabled, options = [], placeholder } = fieldConfig; 7 | 8 | const childOptions = options.map((option: Option) => ( 9 | 10 | {option.label} 11 | 12 | )); 13 | 14 | return ( 15 | 16 | 31 | {childOptions} 32 | 33 | 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/SpecialFields/index.ts: -------------------------------------------------------------------------------- 1 | export * from './FieldArray'; 2 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/SwitchField.tsx: -------------------------------------------------------------------------------- 1 | import { Field } from '@tutim/types'; 2 | import { Switch, FormControlLabel } from '@mui/material'; 3 | import { FieldWrapper } from './utils'; 4 | 5 | export const SwitchField: Field = ({ fieldConfig, inputProps: { value = false, onChange }, fieldState }) => { 6 | const { key, label } = fieldConfig; 7 | 8 | return ( 9 | 10 | } /> 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/TextAreaField.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Field } from '@tutim/types'; 3 | import { TextField as MuiTextField } from '@mui/material'; 4 | import { FieldWrapper } from './utils'; 5 | 6 | export const TextAreaField: Field = ({ fieldConfig, inputProps: { value, onChange }, fieldState }) => { 7 | const { key, isRequired, isDisabled, placeholder } = fieldConfig; 8 | 9 | const onInputChange = (event: React.ChangeEvent) => { 10 | onChange(event.currentTarget.value); 11 | }; 12 | 13 | return ( 14 | 15 | 29 | 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/TextField.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Field } from '@tutim/types'; 3 | import { TextField as MuiTextField } from '@mui/material'; 4 | import { FieldWrapper } from './utils'; 5 | 6 | export const TextField: Field = ({ fieldConfig, inputProps: { value, onChange }, fieldState }) => { 7 | const { key, isRequired, isDisabled, placeholder } = fieldConfig; 8 | 9 | const onInputChange = (event: React.ChangeEvent) => { 10 | onChange(event.currentTarget.value); 11 | }; 12 | 13 | return ( 14 | 15 | 28 | 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DefaultFields'; 2 | export * from './TextField'; 3 | export * from './SelectField'; 4 | export * from './RadioField'; 5 | export * from './CheckboxField'; 6 | export * from './SwitchField'; 7 | export * from './JsonField'; 8 | export * from './MultiTextField'; 9 | export * from './MultiSelectField'; 10 | export * from './DateField'; 11 | export * from './NumberField'; 12 | export * from './PasswordField'; 13 | export * from './TextAreaField'; 14 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/utils/FieldWrapper.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { FormControl, FormHelperText } from '@mui/material'; 3 | import { Label } from './Label'; 4 | 5 | export const FieldWrapper: React.FC<{ fieldConfig: any; fieldState: any; children: any }> = ({ 6 | fieldConfig, 7 | fieldState, 8 | children, 9 | }) => { 10 | const { key, label, isRequired, isDisabled, tooltip, helperText } = fieldConfig; 11 | const { error = { message: '' } } = fieldState || {}; 12 | const showError = !!error.message; 13 | 14 | return ( 15 | 16 | 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/utils/Label.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { FormLabel, Tooltip, IconButton } from '@mui/material'; 3 | import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; 4 | 5 | interface LabelProps { 6 | label?: string; 7 | tooltip?: string; 8 | } 9 | export const Label: React.FC = ({ label, tooltip }) => { 10 | return ( 11 | 12 | {label} 13 | {tooltip && ( 14 | 15 | 16 | 17 | 18 | 19 | )} 20 | 21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /packages/fields/src/Fields/utils/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Label'; 2 | export * from './FieldWrapper'; 3 | -------------------------------------------------------------------------------- /packages/fields/src/Forms/FieldGroup.tsx: -------------------------------------------------------------------------------- 1 | import { FormLayout } from '@tutim/types'; 2 | import React from 'react'; 3 | import { FormGrid } from './FormGrid'; 4 | 5 | interface FieldGroupProps { 6 | children: React.ReactNode[]; 7 | layout?: FormLayout; 8 | } 9 | 10 | export const FieldGroup = ({ children, layout = {} }: FieldGroupProps) => { 11 | return {children}; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/fields/src/Forms/FormGrid.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MuiGrid from '@mui/material/Grid'; 3 | import { FieldsPerRow } from '@tutim/types'; 4 | 5 | interface GridProps { 6 | children: React.ReactNode[]; 7 | fieldsPerRow?: FieldsPerRow; 8 | } 9 | 10 | export const ROW_SIZE = { 11 | [FieldsPerRow.One]: 12, 12 | [FieldsPerRow.Two]: 6, 13 | [FieldsPerRow.Three]: 4, 14 | }; 15 | 16 | export const FormGrid = ({ children, fieldsPerRow = FieldsPerRow.One }: GridProps): JSX.Element => { 17 | return ( 18 | 25 | {children.map((child, index) => ( 26 | 27 | {child} 28 | 29 | ))} 30 | 31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /packages/fields/src/Forms/index.ts: -------------------------------------------------------------------------------- 1 | export * from './FormElement'; 2 | export * from './FormGrid'; 3 | export * from './FieldGroup'; 4 | export * from './Form'; 5 | -------------------------------------------------------------------------------- /packages/fields/src/NativeFields/DefaultFields.tsx: -------------------------------------------------------------------------------- 1 | import { FieldComponents, InputType } from '@tutim/types'; 2 | import { SelectField } from './SelectField'; 3 | import { TextField } from './TextField'; 4 | 5 | export const defaultFields: FieldComponents = { 6 | [InputType.Select]: SelectField, 7 | [InputType.Text]: TextField, 8 | }; 9 | -------------------------------------------------------------------------------- /packages/fields/src/NativeFields/SelectField.tsx: -------------------------------------------------------------------------------- 1 | import { Option, Field } from '@tutim/types'; 2 | 3 | export const SelectField: Field = ({ fieldConfig, inputProps: { value, onChange }, fieldState }) => { 4 | const { key, label, options } = fieldConfig; 5 | const { error = { message: '' } } = fieldState || {}; 6 | 7 | const onSelectChange: React.ChangeEventHandler = (event) => { 8 | onChange(event.target.value); 9 | }; 10 | 11 | const renderOption = (option: Option) => ( 12 | 15 | ); 16 | 17 | return ( 18 |
19 | 20 | {options && ( 21 | 24 | )} 25 | {error.message && {error.message}} 26 |
27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /packages/fields/src/NativeFields/TextField.tsx: -------------------------------------------------------------------------------- 1 | import { Field } from '@tutim/types'; 2 | 3 | export const TextField: Field = ({ fieldConfig, inputProps: { value, onChange }, fieldState }) => { 4 | const { key, label, isDisabled, isRequired } = fieldConfig; 5 | const { error = { message: '' } } = fieldState || {}; 6 | 7 | const onInputChange = (event: React.ChangeEvent) => { 8 | onChange(event.currentTarget.value); 9 | }; 10 | 11 | return ( 12 |
13 | 14 | 15 | {error.message && {error.message}} 16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /packages/fields/src/NativeFields/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SelectField'; 2 | export * from './TextField'; 3 | export * from './DefaultFields'; 4 | -------------------------------------------------------------------------------- /packages/fields/src/Tabs/Tabs.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Box from '@mui/material/Box'; 3 | import Tabs from '@mui/material/Tabs'; 4 | import Tab from '@mui/material/Tab'; 5 | 6 | interface FormTabsProps { 7 | labels: string[]; 8 | children: React.ReactNode[]; 9 | } 10 | 11 | export const FormTabs = ({ labels, children }: FormTabsProps) => { 12 | const [value, setValue] = React.useState(0); 13 | 14 | const handleChange = (event: React.SyntheticEvent, newValue: number) => { 15 | setValue(newValue); 16 | }; 17 | 18 | return ( 19 | 20 | 21 | {labels.map((label) => ( 22 | 23 | ))} 24 | 25 | {children[value]} 26 | 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /packages/fields/src/Tabs/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Tabs'; 2 | -------------------------------------------------------------------------------- /packages/fields/src/Wizards/Footer.tsx: -------------------------------------------------------------------------------- 1 | import Button from '@mui/material/Button'; 2 | 3 | const Footer = ({ step }) => { 4 | const { goBack, goNext, form, isFirstStep, isLastStep } = step; 5 | 6 | return ( 7 |
8 |
9 | 12 |
13 | {!isLastStep && ( 14 | 17 | )} 18 | {isLastStep && ( 19 | 22 | )} 23 |
24 |
25 |
26 | ); 27 | }; 28 | 29 | export { Footer }; 30 | -------------------------------------------------------------------------------- /packages/fields/src/Wizards/Header.tsx: -------------------------------------------------------------------------------- 1 | import Stepper from '@mui/material/Stepper'; 2 | import Step from '@mui/material/Step'; 3 | import StepButton from '@mui/material/StepButton'; 4 | import { PartialFormConfig } from '@tutim/types'; 5 | import { useWizardContext } from '@tutim/headless'; 6 | 7 | export const Header = ({ config }: { config: PartialFormConfig }) => { 8 | const { activeStep, goToStep } = useWizardContext(); 9 | if (!config.wizard) return null; 10 | const isVertical = config.wizard.orientation === 'vertical'; 11 | const styling = isVertical 12 | ? { overflow: 'auto', height: '100%', minWidth: '100px', maxWidth: '250px', padding: '10px' } 13 | : { overflow: 'auto', width: '100%', height: '80px', padding: '10px' }; 14 | 15 | return ( 16 | 17 | {Object.values(config.wizard.steps).map(({ label }, index) => ( 18 | 19 | goToStep(index)} 23 | > 24 | {label} 25 | 26 | 27 | ))} 28 | 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /packages/fields/src/Wizards/MultiStepWizard.tsx: -------------------------------------------------------------------------------- 1 | import { Header } from './Header'; 2 | import { WizardStep } from './WizardStep'; 3 | import { Typography } from '@mui/material'; 4 | import { OnSubmit, PartialFormConfig } from '@tutim/types'; 5 | import { WizardProvider, useWizard } from '@tutim/headless'; 6 | 7 | interface WizardProps { 8 | onSubmit: OnSubmit; 9 | config: PartialFormConfig; 10 | initialValues?: Record; 11 | wizardContext?: any; 12 | } 13 | 14 | export const MuiTutimWizard = ({ onSubmit, config, initialValues = {}, wizardContext }: WizardProps) => { 15 | if (!config.wizard) throw new Error('Wizard config is missing'); 16 | 17 | const wizardContextMerged = useWizard({ initialValues, onSubmit, config, wizardContext }); 18 | const title = config.meta?.title && {config.meta.title}; 19 | const isVertical = config.wizard.orientation === 'vertical'; 20 | 21 | return ( 22 |
23 | {title} 24 |
25 | 26 |
27 | 28 | 29 |
30 |
31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /packages/fields/src/Wizards/WizardStep.tsx: -------------------------------------------------------------------------------- 1 | import { useStep, useWizardContext } from '@tutim/headless'; 2 | import { Footer } from './Footer'; 3 | import { Box } from '@mui/system'; 4 | import { FormView } from '../Forms'; 5 | 6 | export const WizardStep = () => { 7 | const context = useWizardContext(); 8 | const step = useStep(); 9 | const stepKey = `page${context.activeStep}`; 10 | 11 | return ( 12 | 13 | null} /> 14 |