diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 914a8836a..db7f431ac 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -526,18 +526,48 @@ importers:
'@mdx-js/react':
specifier: ^3.0.0
version: 3.1.0(@types/react@18.3.12)(react@18.3.1)
+ ajv:
+ specifier: 8.17.1
+ version: 8.17.1
+ antd:
+ specifier: 5.21.5
+ version: 5.21.5(date-fns@2.30.0)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ classnames:
+ specifier: ^2.0.0
+ version: 2.5.1
clsx:
specifier: ^2.0.0
version: 2.1.1
+ lodash:
+ specifier: ^4.0.0
+ version: 4.17.21
+ lz-string:
+ specifier: 1.5.0
+ version: 1.5.0
+ message-box:
+ specifier: 0.2.7
+ version: 0.2.7
prism-react-renderer:
specifier: ^2.3.0
version: 2.4.0(react@18.3.1)
+ raw-loader:
+ specifier: 4.0.2
+ version: 4.0.2(webpack@5.95.0)
react:
specifier: ^18.0.0
version: 18.3.1
react-dom:
specifier: ^18.0.0
version: 18.3.1(react@18.3.1)
+ react-feather:
+ specifier: 2.0.10
+ version: 2.0.10(react@18.3.1)
+ react-frame-component:
+ specifier: 5.2.7
+ version: 5.2.7(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ zod:
+ specifier: 3.23.8
+ version: 3.23.8
devDependencies:
'@docusaurus/module-type-aliases':
specifier: 3.5.2
@@ -551,9 +581,18 @@ importers:
'@docusaurus/types':
specifier: 3.5.2
version: 3.5.2(acorn@8.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@types/simpl-schema':
+ specifier: 1.12.8
+ version: 1.12.8(@aws-sdk/client-sso-oidc@3.679.0(@aws-sdk/client-sts@3.679.0))
+ simpl-schema:
+ specifier: 1.13.1
+ version: 1.13.1
typescript:
specifier: ~5.5.2
version: 5.5.4
+ uniforms:
+ specifier: workspace:*
+ version: link:../packages/uniforms
packages:
@@ -6946,6 +6985,12 @@ packages:
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
engines: {node: '>= 0.8'}
+ raw-loader@4.0.2:
+ resolution: {integrity: sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==}
+ engines: {node: '>= 10.13.0'}
+ peerDependencies:
+ webpack: ^4.0.0 || ^5.0.0
+
rc-cascader@3.28.2:
resolution: {integrity: sha512-8f+JgM83iLTvjgdkgU7GfI4qY8icXOBP0cGZjOdx2iJAkEe8ucobxDQAVE69UD/c3ehCxZlcgEHeD5hFmypbUw==}
peerDependencies:
@@ -7205,6 +7250,18 @@ packages:
react-fast-compare@3.2.2:
resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
+ react-feather@2.0.10:
+ resolution: {integrity: sha512-BLhukwJ+Z92Nmdcs+EMw6dy1Z/VLiJTzEQACDUEnWMClhYnFykJCGWQx+NmwP/qQHGX/5CzQ+TGi8ofg2+HzVQ==}
+ peerDependencies:
+ react: '>=16.8.6'
+
+ react-frame-component@5.2.7:
+ resolution: {integrity: sha512-ROjHtSLoSVYUBfTieazj/nL8jIX9rZFmHC0yXEU+dx6Y82OcBEGgU9o7VyHMrBFUN9FuQ849MtIPNNLsb4krbg==}
+ peerDependencies:
+ prop-types: ^15.5.9
+ react: '>= 16.3'
+ react-dom: '>= 16.3'
+
react-helmet-async@1.3.0:
resolution: {integrity: sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==}
peerDependencies:
@@ -11278,73 +11335,69 @@ snapshots:
'@open-draft/deferred-promise@2.2.0': {}
- '@parcel/bundler-default@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/bundler-default@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/diagnostic': 2.12.0
'@parcel/graph': 3.2.0
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/rust': 2.12.0
'@parcel/utils': 2.12.0
nullthrows: 1.1.1
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/cache@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/cache@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/core': 2.12.0(@swc/helpers@0.5.13)
'@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
'@parcel/logger': 2.12.0
'@parcel/utils': 2.12.0
lmdb: 2.8.5
- transitivePeerDependencies:
- - '@swc/helpers'
'@parcel/codeframe@2.12.0':
dependencies:
chalk: 4.1.2
- '@parcel/compressor-raw@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/compressor-raw@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
'@parcel/config-default@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)(postcss@8.4.47)(relateurl@0.2.7)(terser@5.36.0)(typescript@5.5.4)':
dependencies:
- '@parcel/bundler-default': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/compressor-raw': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/bundler-default': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/compressor-raw': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/core': 2.12.0(@swc/helpers@0.5.13)
- '@parcel/namer-default': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/optimizer-css': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/optimizer-htmlnano': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)(postcss@8.4.47)(relateurl@0.2.7)(terser@5.36.0)(typescript@5.5.4)
- '@parcel/optimizer-image': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/optimizer-svgo': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/namer-default': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/optimizer-css': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/optimizer-htmlnano': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(postcss@8.4.47)(relateurl@0.2.7)(terser@5.36.0)(typescript@5.5.4)
+ '@parcel/optimizer-image': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/optimizer-svgo': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/optimizer-swc': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/packager-css': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/packager-html': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/packager-js': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/packager-raw': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/packager-svg': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/packager-wasm': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/reporter-dev-server': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/resolver-default': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/runtime-browser-hmr': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/runtime-js': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/runtime-react-refresh': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/runtime-service-worker': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/transformer-babel': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/transformer-css': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/transformer-html': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/transformer-image': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/packager-css': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/packager-html': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/packager-js': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/packager-raw': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/packager-svg': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/packager-wasm': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/reporter-dev-server': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/resolver-default': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/runtime-browser-hmr': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/runtime-js': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/runtime-react-refresh': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/runtime-service-worker': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/transformer-babel': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/transformer-css': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/transformer-html': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/transformer-image': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/transformer-js': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
- '@parcel/transformer-json': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/transformer-postcss': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/transformer-posthtml': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/transformer-raw': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/transformer-react-refresh-wrap': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/transformer-svg': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/transformer-json': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/transformer-postcss': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/transformer-posthtml': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/transformer-raw': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/transformer-react-refresh-wrap': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/transformer-svg': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
transitivePeerDependencies:
- '@swc/helpers'
- cssnano
@@ -11359,20 +11412,20 @@ snapshots:
'@parcel/core@2.12.0(@swc/helpers@0.5.13)':
dependencies:
'@mischnic/json-sourcemap': 0.1.1
- '@parcel/cache': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/cache': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/diagnostic': 2.12.0
'@parcel/events': 2.12.0
'@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
'@parcel/graph': 3.2.0
'@parcel/logger': 2.12.0
'@parcel/package-manager': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/profiler': 2.12.0
'@parcel/rust': 2.12.0
'@parcel/source-map': 2.1.1
'@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
'@parcel/utils': 2.12.0
- '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
abortcontroller-polyfill: 1.7.5
base-x: 3.0.10
browserslist: 4.24.2
@@ -11400,7 +11453,7 @@ snapshots:
'@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
'@parcel/utils': 2.12.0
'@parcel/watcher': 2.4.1
- '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
transitivePeerDependencies:
- '@swc/helpers'
@@ -11417,14 +11470,13 @@ snapshots:
dependencies:
chalk: 4.1.2
- '@parcel/namer-default@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/namer-default@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/diagnostic': 2.12.0
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
nullthrows: 1.1.1
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
'@parcel/node-resolver-core@3.3.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
@@ -11438,10 +11490,10 @@ snapshots:
transitivePeerDependencies:
- '@parcel/core'
- '@parcel/optimizer-css@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/optimizer-css@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/diagnostic': 2.12.0
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/source-map': 2.1.1
'@parcel/utils': 2.12.0
browserslist: 4.24.2
@@ -11449,18 +11501,16 @@ snapshots:
nullthrows: 1.1.1
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/optimizer-htmlnano@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)(postcss@8.4.47)(relateurl@0.2.7)(terser@5.36.0)(typescript@5.5.4)':
+ '@parcel/optimizer-htmlnano@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(postcss@8.4.47)(relateurl@0.2.7)(terser@5.36.0)(typescript@5.5.4)':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
htmlnano: 2.1.1(postcss@8.4.47)(relateurl@0.2.7)(svgo@2.8.0)(terser@5.36.0)(typescript@5.5.4)
nullthrows: 1.1.1
posthtml: 0.16.6
svgo: 2.8.0
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- cssnano
- postcss
- purgecss
@@ -11470,31 +11520,28 @@ snapshots:
- typescript
- uncss
- '@parcel/optimizer-image@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/optimizer-image@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/core': 2.12.0(@swc/helpers@0.5.13)
'@parcel/diagnostic': 2.12.0
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/rust': 2.12.0
'@parcel/utils': 2.12.0
- '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- transitivePeerDependencies:
- - '@swc/helpers'
+ '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
- '@parcel/optimizer-svgo@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/optimizer-svgo@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/diagnostic': 2.12.0
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/utils': 2.12.0
svgo: 2.8.0
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
'@parcel/optimizer-swc@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
dependencies:
'@parcel/diagnostic': 2.12.0
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/source-map': 2.1.1
'@parcel/utils': 2.12.0
'@swc/core': 1.7.40(@swc/helpers@0.5.13)
@@ -11512,39 +11559,37 @@ snapshots:
'@parcel/node-resolver-core': 3.3.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
'@parcel/utils': 2.12.0
- '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@swc/core': 1.7.40(@swc/helpers@0.5.13)
semver: 7.6.3
transitivePeerDependencies:
- '@swc/helpers'
- '@parcel/packager-css@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/packager-css@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/diagnostic': 2.12.0
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/source-map': 2.1.1
'@parcel/utils': 2.12.0
lightningcss: 1.27.0
nullthrows: 1.1.1
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/packager-html@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/packager-html@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
'@parcel/utils': 2.12.0
nullthrows: 1.1.1
posthtml: 0.16.6
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/packager-js@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/packager-js@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/diagnostic': 2.12.0
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/rust': 2.12.0
'@parcel/source-map': 2.1.1
'@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
@@ -11553,38 +11598,33 @@ snapshots:
nullthrows: 1.1.1
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/packager-raw@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/packager-raw@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/packager-svg@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/packager-svg@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
'@parcel/utils': 2.12.0
posthtml: 0.16.6
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/packager-wasm@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/packager-wasm@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/plugin@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/plugin@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
'@parcel/profiler@2.12.0':
dependencies:
@@ -11592,79 +11632,71 @@ snapshots:
'@parcel/events': 2.12.0
chrome-trace-event: 1.0.4
- '@parcel/reporter-cli@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/reporter-cli@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
'@parcel/utils': 2.12.0
chalk: 4.1.2
term-size: 2.2.1
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/reporter-dev-server@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/reporter-dev-server@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/utils': 2.12.0
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/reporter-tracer@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/reporter-tracer@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/utils': 2.12.0
chrome-trace-event: 1.0.4
nullthrows: 1.1.1
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/resolver-default@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/resolver-default@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/node-resolver-core': 3.3.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/runtime-browser-hmr@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/runtime-browser-hmr@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/utils': 2.12.0
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/runtime-js@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/runtime-js@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/diagnostic': 2.12.0
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/utils': 2.12.0
nullthrows: 1.1.1
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/runtime-react-refresh@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/runtime-react-refresh@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/utils': 2.12.0
react-error-overlay: 6.0.9
react-refresh: 0.9.0
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/runtime-service-worker@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/runtime-service-worker@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/utils': 2.12.0
nullthrows: 1.1.1
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
'@parcel/rust@2.12.0': {}
@@ -11672,10 +11704,10 @@ snapshots:
dependencies:
detect-libc: 1.0.3
- '@parcel/transformer-babel@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/transformer-babel@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/diagnostic': 2.12.0
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/source-map': 2.1.1
'@parcel/utils': 2.12.0
browserslist: 4.24.2
@@ -11684,12 +11716,11 @@ snapshots:
semver: 7.6.3
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/transformer-css@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/transformer-css@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/diagnostic': 2.12.0
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/source-map': 2.1.1
'@parcel/utils': 2.12.0
browserslist: 4.24.2
@@ -11697,12 +11728,11 @@ snapshots:
nullthrows: 1.1.1
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/transformer-html@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/transformer-html@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/diagnostic': 2.12.0
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/rust': 2.12.0
nullthrows: 1.1.1
posthtml: 0.16.6
@@ -11712,45 +11742,41 @@ snapshots:
srcset: 4.0.0
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/transformer-image@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/transformer-image@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/core': 2.12.0(@swc/helpers@0.5.13)
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/utils': 2.12.0
- '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
nullthrows: 1.1.1
- transitivePeerDependencies:
- - '@swc/helpers'
'@parcel/transformer-js@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/core': 2.12.0(@swc/helpers@0.5.13)
'@parcel/diagnostic': 2.12.0
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/rust': 2.12.0
'@parcel/source-map': 2.1.1
'@parcel/utils': 2.12.0
- '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@swc/helpers': 0.5.13
browserslist: 4.24.2
nullthrows: 1.1.1
regenerator-runtime: 0.13.11
semver: 7.6.3
- '@parcel/transformer-json@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/transformer-json@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
json5: 2.2.3
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/transformer-postcss@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/transformer-postcss@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/diagnostic': 2.12.0
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/rust': 2.12.0
'@parcel/utils': 2.12.0
clone: 2.1.2
@@ -11759,11 +11785,10 @@ snapshots:
semver: 7.6.3
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/transformer-posthtml@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/transformer-posthtml@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/utils': 2.12.0
nullthrows: 1.1.1
posthtml: 0.16.6
@@ -11772,28 +11797,25 @@ snapshots:
semver: 7.6.3
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/transformer-raw@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/transformer-raw@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/transformer-react-refresh-wrap@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/transformer-react-refresh-wrap@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/utils': 2.12.0
react-refresh: 0.9.0
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
- '@parcel/transformer-svg@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/transformer-svg@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/diagnostic': 2.12.0
- '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/plugin': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/rust': 2.12.0
nullthrows: 1.1.1
posthtml: 0.16.6
@@ -11802,16 +11824,15 @@ snapshots:
semver: 7.6.3
transitivePeerDependencies:
- '@parcel/core'
- - '@swc/helpers'
'@parcel/types@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
dependencies:
- '@parcel/cache': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/cache': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/diagnostic': 2.12.0
'@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
'@parcel/package-manager': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
'@parcel/source-map': 2.1.1
- '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/workers': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
utility-types: 3.11.0
transitivePeerDependencies:
- '@parcel/core'
@@ -11884,7 +11905,7 @@ snapshots:
'@parcel/watcher-win32-ia32': 2.4.1
'@parcel/watcher-win32-x64': 2.4.1
- '@parcel/workers@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)':
+ '@parcel/workers@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))':
dependencies:
'@parcel/core': 2.12.0(@swc/helpers@0.5.13)
'@parcel/diagnostic': 2.12.0
@@ -11893,8 +11914,6 @@ snapshots:
'@parcel/types': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
'@parcel/utils': 2.12.0
nullthrows: 1.1.1
- transitivePeerDependencies:
- - '@swc/helpers'
'@pkgr/core@0.1.1': {}
@@ -17169,9 +17188,9 @@ snapshots:
'@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
'@parcel/logger': 2.12.0
'@parcel/package-manager': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/reporter-cli': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/reporter-dev-server': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
- '@parcel/reporter-tracer': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)
+ '@parcel/reporter-cli': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/reporter-dev-server': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
+ '@parcel/reporter-tracer': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.13))
'@parcel/utils': 2.12.0
chalk: 4.1.2
commander: 7.2.0
@@ -17613,6 +17632,12 @@ snapshots:
iconv-lite: 0.4.24
unpipe: 1.0.0
+ raw-loader@4.0.2(webpack@5.95.0):
+ dependencies:
+ loader-utils: 2.0.4
+ schema-utils: 3.3.0
+ webpack: 5.95.0
+
rc-cascader@3.28.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@babel/runtime': 7.25.9
@@ -17991,6 +18016,17 @@ snapshots:
react-fast-compare@3.2.2: {}
+ react-feather@2.0.10(react@18.3.1):
+ dependencies:
+ prop-types: 15.8.1
+ react: 18.3.1
+
+ react-frame-component@5.2.7(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ prop-types: 15.8.1
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+
react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@babel/runtime': 7.25.9
diff --git a/website/README.md b/website/README.md
index 0c6c2c27b..0a1b48113 100644
--- a/website/README.md
+++ b/website/README.md
@@ -4,22 +4,28 @@ This website is built using [Docusaurus](https://docusaurus.io/), a modern stati
### Installation
+In the root directory of the project, run:
+
```
-$ yarn
+$ pnpm install
```
### Local Development
+In the root directory of the project, run:
+
```
-$ yarn start
+$ pnpm -filter=website run start
```
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
### Build
+In the root directory of the project, run:
+
```
-$ yarn build
+$ pnpm -filter=website run build
```
This command generates static content into the `build` directory and can be served using any static contents hosting service.
diff --git a/website/docs/compare-matrix-landing.md b/website/docs/compare-matrix-landing.md
new file mode 100644
index 000000000..81cf036ec
--- /dev/null
+++ b/website/docs/compare-matrix-landing.md
@@ -0,0 +1,8 @@
+| Feature | [uniforms](https://github.com/vazco/uniforms) | [Formik](https://github.com/jaredpalmer/formik) | [redux-form](https://github.com/erikras/redux-form) | [React Final Form](https://github.com/final-form/react-final-form) | [react-hook-form](https://github.com/react-hook-form/react-hook-form) |
+| :----------------------------------------------------------------------------- | :---------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------: | ----------------------------------------------------------------------------------- |
+| Automatic form layout | ![supported](https://github.githubassets.com/images/icons/emoji/unicode/2714.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) |
+| Manual state management | ![supported](https://github.githubassets.com/images/icons/emoji/unicode/2714.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) | ![supported](https://github.githubassets.com/images/icons/emoji/unicode/2714.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) | ![supported](https://github.githubassets.com/images/icons/emoji/unicode/2714.png) |
+| Manually trigger validation | ![supported](https://github.githubassets.com/images/icons/emoji/unicode/2714.png) | ![supported](https://github.githubassets.com/images/icons/emoji/unicode/2714.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) | ![supported](https://github.githubassets.com/images/icons/emoji/unicode/2714.png) |
+| AntD, Bootstrap 3, Bootstrap 4, Bootstrap 5, Material, MUI, Semantic UI themes | ![supported](https://github.githubassets.com/images/icons/emoji/unicode/2714.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) |
+| JSON, GraphQL, SimpleSchema, Zod and custom schema support | ![supported](https://github.githubassets.com/images/icons/emoji/unicode/2714.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) |
+| Field level validation | ![unsupported](https://github.githubassets.com/images/icons/emoji/unicode/2716.png) | ![supported](https://github.githubassets.com/images/icons/emoji/unicode/2714.png) | ![supported](https://github.githubassets.com/images/icons/emoji/unicode/2714.png) | ![supported](https://github.githubassets.com/images/icons/emoji/unicode/2714.png) | ![supported](https://github.githubassets.com/images/icons/emoji/unicode/2714.png) |
diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts
index 8acacd11c..a5eeb659a 100644
--- a/website/docusaurus.config.ts
+++ b/website/docusaurus.config.ts
@@ -36,7 +36,49 @@ const config: Config = {
],
],
+ customFields: {
+ email: 'hello@uniforms.tools',
+ companies: [
+ { url: 'https://www.deskpro.com/', image: 'deskpro.png', alt: 'Deskpro' },
+ { url: 'https://www.nokia.com/', image: 'nokia.png', alt: 'Nokia' },
+ {
+ url: 'https://github.com/react-page/react-page/tree/master/packages/plugins/createPluginMaterialUi',
+ image: 'react-page.png',
+ alt: 'React Page',
+ },
+ {
+ url: 'https://graphback.dev',
+ image: 'graphback.png',
+ alt: 'Graphback',
+ },
+ {
+ url: 'https://www.onyx-one.com/',
+ image: 'onyx-one.png',
+ alt: 'Onyx one',
+ },
+ { url: 'https://aerogear.org', image: 'aerogear.png', alt: 'Aerogear' },
+ {
+ url: 'https://cleverbeagle.com/together',
+ image: 'cleverbeagle.png',
+ alt: 'Clever Beagle',
+ },
+ { url: 'http://www.orionjs.org', image: 'orionjs.png', alt: 'Orion.js' },
+ {
+ url: 'https://boulder.care',
+ image: 'boulder.svg',
+ alt: 'Boulder Care',
+ },
+ ],
+ },
+
themeConfig: {
+ algolia: {
+ apiKey: '9bab87682792c2bd77ec707a56669e29',
+ appId: 'WWWW16GKXU',
+ contextualSearch: false,
+ indexName: 'uniforms',
+ searchPagePath: false,
+ },
navbar: {
logo: { alt: 'uniforms logo', src: 'img/uniforms.svg' },
items: [
@@ -46,11 +88,27 @@ const config: Config = {
position: 'left',
label: 'Docs',
},
+ {
+ label: 'Tutorial',
+ to: '/docs/tutorials-basic-uniforms-usage',
+ position: 'left',
+ },
+ { label: 'Playground', to: '/playground', position: 'left' },
{
href: 'https://github.com/vazco/uniforms',
label: 'GitHub',
position: 'left',
},
+ {
+ label: 'Enterprise',
+ href: 'https://forminer.com/?utm_source=uniforms&utm_medium=Menu_CTA&utm_campaign=Forminer_uniforms_menu_CTA&utm_id=Forminer_uniforms_menu',
+ position: 'left',
+ },
+ {
+ label: 'Custom solutions',
+ href: 'https://www.vazco.eu/',
+ position: 'left',
+ },
],
},
prism: {
diff --git a/website/package.json b/website/package.json
index 24ff34ee8..8b61087ff 100644
--- a/website/package.json
+++ b/website/package.json
@@ -20,17 +20,30 @@
"@docusaurus/core": "3.5.2",
"@docusaurus/preset-classic": "3.5.2",
"@mdx-js/react": "^3.0.0",
+ "ajv": "8.17.1",
+ "antd": "5.21.5",
+ "classnames": "^2.0.0",
"clsx": "^2.0.0",
+ "lodash": "^4.0.0",
+ "lz-string": "1.5.0",
+ "message-box": "0.2.7",
"prism-react-renderer": "^2.3.0",
+ "raw-loader": "4.0.2",
"react": "^18.0.0",
- "react-dom": "^18.0.0"
+ "react-dom": "^18.0.0",
+ "react-feather": "2.0.10",
+ "react-frame-component": "5.2.7",
+ "zod": "3.23.8"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.5.2",
"@docusaurus/theme-classic": "3.5.2",
"@docusaurus/tsconfig": "3.5.2",
"@docusaurus/types": "3.5.2",
- "typescript": "~5.5.2"
+ "@types/simpl-schema": "1.12.8",
+ "simpl-schema": "1.13.1",
+ "typescript": "~5.5.2",
+ "uniforms": "workspace:*"
},
"browserslist": {
"production": [
diff --git a/website/src/components/Badge.tsx b/website/src/components/Badge.tsx
new file mode 100644
index 000000000..368033401
--- /dev/null
+++ b/website/src/components/Badge.tsx
@@ -0,0 +1,45 @@
+import Link from '@docusaurus/Link';
+import classNames from 'classnames';
+import React, { ComponentType } from 'react';
+
+import styles from '../css/index.module.css';
+import { Oval } from './Oval';
+
+export type BadgeProps = {
+ border: number;
+ color: string;
+ icon: ComponentType<{ color?: string }>;
+ number?: string;
+ text: string;
+ to: string;
+};
+
+export function Badge({
+ border,
+ color,
+ icon: Icon,
+ number,
+ text,
+ to,
+}: BadgeProps) {
+ return (
+
+
+ {Icon && (
+
+
+
+ )}
+
+
+ {number}
+
+
{text}
+
+
+ );
+}
diff --git a/website/src/components/Button.tsx b/website/src/components/Button.tsx
new file mode 100644
index 000000000..7c811a784
--- /dev/null
+++ b/website/src/components/Button.tsx
@@ -0,0 +1,24 @@
+import Link from '@docusaurus/Link';
+import classNames from 'classnames';
+import React from 'react';
+
+import styles from '../css/index.module.css';
+
+export type ButtonProps = JSX.IntrinsicElements['button'] & { to: string };
+
+export function Button({ children, className, to, ...props }: ButtonProps) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/website/src/components/CodeSection.tsx b/website/src/components/CodeSection.tsx
new file mode 100644
index 000000000..531b8d0dd
--- /dev/null
+++ b/website/src/components/CodeSection.tsx
@@ -0,0 +1,66 @@
+import components from '@theme/MDXComponents';
+import React from 'react';
+
+export type CodeSectionProps = {
+ language: string;
+ replace?: Record;
+ section?: string;
+ source: string | { default: string };
+};
+
+export function CodeSection({
+ language,
+ replace,
+ section,
+ source,
+}: CodeSectionProps) {
+ // Unwrap ES module.
+ if (typeof source === 'object' && 'default' in source) {
+ source = source.default;
+ }
+
+ // Cut out only desired section.
+ if (section) {
+ const pattern = new RegExp(
+ `// <${section}>\\s([\\s\\S]*?)\\s// ${section}>\\s`,
+ 'g',
+ );
+
+ source = source
+ .split(pattern)
+ .reduce(
+ (source, part, index) =>
+ index % 2 === 0 ? source : `${source}\n\n${part}`,
+ '',
+ );
+ }
+
+ // Remove remaining section tags.
+ source = source.replace(/\/\/ <.*?\n/g, '');
+
+ // Replace all mapped things.
+ if (replace) {
+ for (const [pattern, value] of Object.entries(replace)) {
+ source = source.replace(new RegExp(pattern, 'gs'), value);
+ }
+ }
+
+ // At least one newline is required for non-inline view.
+ source = source.trim();
+ if (!source.includes('\n')) {
+ source += '\n';
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/website/src/components/ExampleCustomizer.tsx b/website/src/components/ExampleCustomizer.tsx
new file mode 100644
index 000000000..1401e5381
--- /dev/null
+++ b/website/src/components/ExampleCustomizer.tsx
@@ -0,0 +1,63 @@
+import React, { ComponentType } from 'react';
+import { Box, Code, Database } from 'react-feather';
+
+import { themeContext } from '../lib/universal';
+import { CodeSection } from './CodeSection';
+import { FormWrapper } from './FormWrapper';
+import { TogglerTabs } from './TogglerTabs';
+
+const tabs = [
+ { name: 'Semantic', value: 'semantic' as const },
+ { name: 'MUI', value: 'mui' as const },
+ { name: 'Bootstrap4', value: 'bootstrap4' as const },
+ { name: 'Bootstrap5', value: 'bootstrap5' as const },
+ { name: 'AntD', value: 'antd' as const },
+ { name: 'Unstyled', value: 'unstyled' as const },
+];
+
+const toggles = [
+ { icon: , name: 'Example', tooltipText: 'Show example' },
+ { icon:
, name: 'Code', tooltipText: 'Show source code' },
+ { icon: , name: 'Schema', tooltipText: 'Show schema' },
+];
+
+export type ExampleCustomizerProps = {
+ code: { default: string };
+ example: ComponentType;
+ schema: { default: string };
+};
+
+export function ExampleCustomizer({
+ code: { default: code },
+ example: Example,
+ schema: { default: schema },
+}: ExampleCustomizerProps) {
+ return (
+
+ {({ tab: { value: theme }, toggle: { name } }) => {
+ switch (name) {
+ case 'Code':
+ return (
+
+ );
+ case 'Example':
+ return (
+
+
+
+
+
+ );
+ case 'Schema':
+ return ;
+ default:
+ return null;
+ }
+ }}
+
+ );
+}
diff --git a/website/src/components/FormWrapper.tsx b/website/src/components/FormWrapper.tsx
new file mode 100644
index 000000000..6ab731094
--- /dev/null
+++ b/website/src/components/FormWrapper.tsx
@@ -0,0 +1,21 @@
+import React, { ReactNode, useContext } from 'react';
+
+import styles from '../css/index.module.css';
+import { useFrameAutoResize } from '../lib/autoresize';
+import { themeContext } from '../lib/universal';
+import { PlaygroundWrap } from './Playground';
+
+export type FormWrapperProps = {
+ children: ReactNode;
+};
+
+export function FormWrapper(props: FormWrapperProps) {
+ const theme = useContext(themeContext);
+ const frameProps = useFrameAutoResize([props.children]);
+
+ return (
+
+ );
+}
diff --git a/website/src/components/Heading.tsx b/website/src/components/Heading.tsx
new file mode 100644
index 000000000..ab8b254f5
--- /dev/null
+++ b/website/src/components/Heading.tsx
@@ -0,0 +1,14 @@
+import classNames from 'classnames';
+import React from 'react';
+
+import styles from '../css/index.module.css';
+
+export type HeadingProps = JSX.IntrinsicElements['h1'];
+
+export function Heading({ children, className, ...props }: HeadingProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/website/src/components/IconLink.tsx b/website/src/components/IconLink.tsx
new file mode 100644
index 000000000..79b98c05a
--- /dev/null
+++ b/website/src/components/IconLink.tsx
@@ -0,0 +1,28 @@
+import Link from '@docusaurus/Link';
+import classNames from 'classnames';
+import React, { ComponentType } from 'react';
+
+import styles from '../css/index.module.css';
+
+export type IconLinkProps = JSX.IntrinsicElements['div'] & {
+ icon: ComponentType;
+ to: string;
+};
+
+export function IconLink({
+ className,
+ icon: Icon,
+ to,
+ ...props
+}: IconLinkProps) {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/website/src/components/Oval.tsx b/website/src/components/Oval.tsx
new file mode 100644
index 000000000..b7553478c
--- /dev/null
+++ b/website/src/components/Oval.tsx
@@ -0,0 +1,14 @@
+import classNames from 'classnames';
+import React from 'react';
+
+import styles from '../css/index.module.css';
+
+export type OvalProps = JSX.IntrinsicElements['span'];
+
+export function Oval({ children, className, ...props }: OvalProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/website/src/components/Playground.tsx b/website/src/components/Playground.tsx
new file mode 100644
index 000000000..8ea47b679
--- /dev/null
+++ b/website/src/components/Playground.tsx
@@ -0,0 +1,291 @@
+import ConfigProvider from 'antd/lib/config-provider';
+import classNames from 'classnames';
+import omit from 'lodash/omit';
+import React, { Component } from 'react';
+import Frame, { FrameContextConsumer } from 'react-frame-component';
+import { ValidatedForm, connectField, context, useForm } from 'uniforms';
+import type { Option } from 'uniforms-unstyled/src/types';
+
+import playgroundStyles from '../css/index.module.css';
+import presets from '../lib/presets';
+import { bridge, schema } from '../lib/schema';
+import styles from '../lib/styles';
+import { themes } from '../lib/universal';
+import { compress, parseQuery, updateQuery } from '../lib/utils';
+
+export class Playground extends Component {
+ static getDerivedStateFromError(error: Error) {
+ return { error };
+ }
+
+ constructor() {
+ // @ts-expect-error: Types.
+ super(...arguments);
+
+ const state = schema.clean(parseQuery());
+
+ try {
+ schema.validate(state);
+ // FIXME: Types.
+ } catch (error: any) {
+ (error.details as any[]).forEach(({ name }) => {
+ state[name] = schema.getDefinition(name).defaultValue;
+ });
+ }
+
+ this.state = state;
+
+ this.onChange = this.onChange.bind(this);
+ }
+
+ componentDidMount() {
+ updateQuery(this.state);
+ }
+
+ componentDidUpdate() {
+ updateQuery(this.state);
+ }
+
+ onChange(key: string, value: unknown) {
+ if (key === 'preset') {
+ // FIXME: Types.
+ this.setState((state: any) => ({
+ props: {
+ ...state.props,
+ schema: presets[value as keyof typeof presets],
+ },
+ }));
+ }
+
+ this.setState({ error: undefined, [key]: value });
+ }
+
+ render() {
+ return (
+ // @ts-expect-error: Types.
+
+
+
+ {this.state.error ? (
+
+ ) : (
+
+ )}
+
+ );
+ }
+}
+
+// FIXME: ValidatedForm is not derivable.
+class PlaygroundForm extends (ValidatedForm as any) {
+ getContextState() {
+ return {
+ ...super.getContextState(),
+ theme: this.props.model.theme,
+ };
+ }
+
+ render() {
+ return (
+
+
+
+ );
+ }
+}
+
+const PlaygroundModelDebug = () => {
+ const { model } = useForm();
+ return (
+ <>
+
+
+
+ {`const model = ${JSON.stringify(model, null, 2)};`}
+
+ >
+ );
+};
+
+class PlaygroundPreview extends Component {
+ constructor() {
+ // @ts-expect-error: Types.
+ super(...arguments);
+
+ this._schema = eval(`(${this.props.value.schema})`);
+ }
+
+ // eslint-disable-next-line camelcase
+ UNSAFE_componentWillReceiveProps(props: any) {
+ if (this.props.value.schema !== props.value.schema) {
+ this._schema = eval(`(${props.value.schema})`);
+ }
+ }
+
+ // TODO: Types.
+ _schema: any;
+
+ render() {
+ const { AutoFields, AutoForm, ErrorsField, SubmitField } =
+ themes[(this.props.theme as keyof typeof themes) || 'unstyled'];
+
+ const { asyncOnSubmit, asyncOnValidate, schema, ...props } =
+ this.props.value;
+
+ props.schema = this._schema;
+ if (asyncOnSubmit) {
+ props.onSubmit = () =>
+ new Promise(resolve => {
+ setTimeout(resolve, 1000);
+ });
+ }
+ if (asyncOnValidate) {
+ props.onValidate = (model: object, error: Error) =>
+ new Promise(resolve => {
+ setTimeout(() => {
+ resolve(error);
+ }, 1000);
+ });
+ }
+
+ return (
+
+ {this.props.errorMessage ? (
+
+ ) : (
+
+
+
+
+
+
+ )}
+
+ );
+ }
+}
+
+const PlaygroundPreviewField = connectField(PlaygroundPreview);
+
+class PlaygroundProps extends Component {
+ render() {
+ const { onChange, schema, theme, value } = this.props;
+
+ const isAntd = theme === 'antd';
+ const isBootstrap = theme === 'bootstrap4';
+ const isSemantic = theme === 'semantic';
+
+ // FIXME: theme is undefined during `docusaurus build`.
+ const { AutoForm, BoolField, ErrorsField, LongTextField, NumField } =
+ themes[(theme as keyof typeof themes) || 'unstyled'];
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+const PlaygroundPropsField = connectField(PlaygroundProps);
+
+class PlaygroundSelect extends Component {
+ render() {
+ // FIXME: allowedValues is undefined during `docusaurus build`.
+ const { options, onChange, value } = this.props;
+
+ return (
+ onChange(event.target.value)} value={value}>
+ {options.map((option: Option) => (
+
+ {option.label ?? option.value}
+
+ ))}
+
+ );
+ }
+}
+
+const PlaygroundSelectField = connectField(PlaygroundSelect);
+
+export class PlaygroundWrap extends Component {
+ render() {
+ const { children, frameProps, theme } = this.props;
+ const content = (
+
+ {children}
+ {styles[theme as keyof typeof styles]}
+
+ );
+
+ if (theme === 'mui') {
+ // Material-UI injects scoped CSS classes into head.
+ return (
+
+ );
+ }
+
+ let frameContent = content;
+ if (theme === 'antd') {
+ // Make AntD popups contained within the iframe.
+ frameContent = (
+
+ {(context: any) => (
+ context.document.body}>
+ {content}
+
+ )}
+
+ );
+ }
+
+ return (
+
+ );
+ }
+}
diff --git a/website/src/components/Subtitle.tsx b/website/src/components/Subtitle.tsx
new file mode 100644
index 000000000..1bed4a833
--- /dev/null
+++ b/website/src/components/Subtitle.tsx
@@ -0,0 +1,17 @@
+import classNames from 'classnames';
+import React from 'react';
+
+import styles from '../css/index.module.css';
+
+export type SubtitleProps = JSX.IntrinsicElements['p'];
+
+export function Subtitle({ children, className, ...props }: SubtitleProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/website/src/components/Tabs.tsx b/website/src/components/Tabs.tsx
new file mode 100644
index 000000000..06e96589c
--- /dev/null
+++ b/website/src/components/Tabs.tsx
@@ -0,0 +1,80 @@
+import classNames from 'classnames';
+import React, { ReactNode } from 'react';
+
+import styles from '../css/index.module.css';
+import { useTabs } from '../lib/tabs';
+
+export type TabsItem = {
+ name: string;
+};
+
+export type TabsHeaderProps = {
+ activeTab: number;
+ items: T[];
+ onTab: (tab: number) => void;
+};
+
+export function TabsHeader({
+ activeTab,
+ items,
+ onTab,
+}: TabsHeaderProps) {
+ return (
+
+ {items.map(({ name }, key) => (
+ onTab(key)}
+ >
+ {name}
+
+ ))}
+
+ );
+}
+
+export type TabsHeaderItemProps = {
+ active: boolean;
+ children: ReactNode;
+ onClick: () => void;
+};
+
+export function TabsHeaderItem({
+ active,
+ children,
+ onClick,
+}: TabsHeaderItemProps) {
+ return (
+ event.key === 'Enter' && onClick()}
+ role="button"
+ tabIndex={0}
+ >
+ {children}
+
+ );
+}
+
+export type TabsProps = {
+ children: (tab: T) => ReactNode;
+ group: string;
+ tabs: T[];
+};
+
+export function Tabs({
+ children,
+ group,
+ tabs,
+}: TabsProps) {
+ const { activeTab, onTab } = useTabs(group);
+
+ return (
+ <>
+
+ {children(tabs[activeTab])}
+ >
+ );
+}
diff --git a/website/src/components/Toggler.tsx b/website/src/components/Toggler.tsx
new file mode 100644
index 000000000..9daf9e797
--- /dev/null
+++ b/website/src/components/Toggler.tsx
@@ -0,0 +1,86 @@
+import classNames from 'classnames';
+import React, { ReactNode, useState } from 'react';
+
+import styles from '../css/index.module.css';
+
+export type TogglerItem = {
+ component?: ReactNode;
+ icon: ReactNode;
+ tooltipText: string;
+};
+
+export type TogglerHeaderItemProps = JSX.IntrinsicElements['span'] &
+ TogglerItem & { active: boolean };
+
+export function TogglerHeaderItem({
+ active,
+ icon,
+ tooltipText,
+ ...rest
+}: TogglerHeaderItemProps) {
+ return (
+
+ {tooltipText && (
+ {tooltipText}
+ )}
+ {icon}
+
+ );
+}
+
+export type TogglerHeaderProps = {
+ activeToggle: number;
+ items: T[];
+ onClick: (tab: number) => () => void;
+};
+
+export function TogglerHeader({
+ activeToggle,
+ items,
+ onClick,
+}: TogglerHeaderProps) {
+ return (
+
+ {items.map((item, key) => {
+ return (
+
+ );
+ })}
+
+ );
+}
+
+export type TogglerProps = {
+ items: T[];
+};
+
+export function Toggler({ items }: TogglerProps) {
+ const [activeToggle, setState] = useState(0);
+
+ const toggleClick = (toggle: number) => () => {
+ setState(toggle);
+ };
+
+ return (
+ <>
+
+ {items[activeToggle].component}
+ >
+ );
+}
diff --git a/website/src/components/TogglerTabs.tsx b/website/src/components/TogglerTabs.tsx
new file mode 100644
index 000000000..e33375497
--- /dev/null
+++ b/website/src/components/TogglerTabs.tsx
@@ -0,0 +1,44 @@
+import React, { ReactNode, useState } from 'react';
+
+import styles from '../css/index.module.css';
+import { useTabs } from '../lib/tabs';
+import { TabsHeader, TabsItem } from './Tabs';
+import { TogglerHeader, TogglerItem } from './Toggler';
+
+export type TogglerTabsProps = {
+ children: (args: { tab: T; toggle: U }) => ReactNode;
+ group: string;
+ tabsItems: T[];
+ togglerItems: U[];
+};
+
+export function TogglerTabs({
+ children,
+ group,
+ tabsItems,
+ togglerItems,
+}: TogglerTabsProps) {
+ const { activeTab, onTab } = useTabs(group);
+ const [activeToggle, setStateToggler] = useState(0);
+
+ const handleTogglerClick = (toggle: number) => () => {
+ setStateToggler(toggle);
+ };
+
+ return (
+
+
+ {children({
+ tab: tabsItems[activeTab],
+ toggle: togglerItems[activeToggle],
+ })}
+
+ );
+}
diff --git a/website/src/css/index.module.css b/website/src/css/index.module.css
new file mode 100644
index 000000000..99f6ab1ad
--- /dev/null
+++ b/website/src/css/index.module.css
@@ -0,0 +1,949 @@
+@import url('https://fonts.googleapis.com/css?family=Poppins:400,700&display=swap');
+@import url('https://fonts.googleapis.com/css?family=Muli:400,600&display=swap');
+
+@media (max-width: 996px) {
+ .center-if-sm {
+ text-align: center;
+ }
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ html {
+ scroll-behavior: smooth;
+ }
+}
+
+:root {
+ --black: #000;
+ --bright-cyan-50: rgba(63, 196, 254, 0.2);
+ --bright-cyan: #3fc4fe;
+ --dark-blue-grey: #211b50;
+ --dark-slate-blue: #2d2665;
+ --dodger-blue-10: rgba(63, 187, 254, 0.1);
+ --dodger-blue: #3fbbfe;
+ --dusk-blue: #412a94;
+ --greenblue-10: rgba(31, 216, 152, 0.1);
+ --greenblue: #1fd898;
+ --grey: #eee;
+ --greyish-brown: #474747;
+ --pale-grey: #e8e6f5;
+ --periwinkle-50: rgba(136, 89, 255, 0.2);
+ --periwinkle: #8759ff;
+ --sea-green-50: rgba(66, 255, 172, 0.2);
+ --sea-green: #42ffac;
+ --white: #fff;
+
+ --uni-background-color: var(--white);
+ --uni-heading-color: var(--dusk-blue);
+ --uni-heading-helper-color: var(--dusk-blue);
+ --uni-hero-bgcolor: var(--dark-blue-grey);
+ --uni-oval-background-color: var(--white);
+ --uni-oval-shadow-color: var(--pale-grey);
+ --uni-testimonial-text-color: var(--greyish-brown);
+ --uni-toolbar-bgcolor: var(--grey);
+
+ --ifm-button-color: var(--white);
+}
+
+:root[data-theme='dark'] {
+ --uni-background-color: var(--ifm-background-color);
+ --uni-heading-color: var(--ifm-font-color-base);
+ --uni-heading-helper-color: var(--ifm-font-color-base);
+ --uni-oval-shadow-color: var(--black);
+ --uni-testimonial-text-color: var(--ifm-font-color-base);
+ --uni-toolbar-bgcolor: var(--ifm-background-color);
+}
+
+.padding {
+ padding-top: 2em;
+}
+
+.landing table thead {
+ color: var(--black);
+ background-attachment: fixed;
+ background-image: linear-gradient(
+ to right,
+ var(--periwinkle),
+ var(--bright-cyan),
+ var(--sea-green)
+ );
+}
+
+.landing table thead tr th {
+ width: 15%;
+}
+
+.landing table thead tr th a {
+ color: inherit;
+}
+
+.landing table thead tr th:first-child {
+ width: 30%;
+}
+
+.landing table tr td {
+ background-color: var(--dark-slate-blue);
+ color: var(--white);
+}
+
+.landing table tr td:first-child,
+.landing table tr th:first-child {
+ padding-left: 4em;
+}
+
+.landing table tr td > img {
+ width: 1em;
+}
+
+.landing tr:nth-child(odd) td {
+ background-color: var(--dark-blue-grey);
+}
+
+.landing ul {
+ padding: 0;
+ opacity: 0.7;
+ font-family: Muli;
+ font-size: 1em;
+ font-weight: 600;
+ line-height: 1.33;
+}
+
+.landing ul li {
+ list-style: none;
+ position: relative;
+ padding-left: 1em;
+ margin-bottom: 1em;
+}
+
+.landing ul li::before {
+ content: '';
+ position: absolute;
+ top: 0.5em;
+ left: 0;
+ width: 4px;
+ height: 4px;
+ background-color: var(--ifm-font-color-base);
+}
+
+.landing :global .hero.hero--primary {
+ --ifm-hero-background-color: var(--dark-blue-grey);
+}
+
+.overflow-hidden {
+ overflow: hidden;
+}
+
+.landing .section-content :global .row {
+ position: relative;
+}
+
+@media (min-width: 997px) {
+ .landing :global .section {
+ margin-top: 4em;
+ }
+ .landing :global .container {
+ scroll-margin-top: 200px;
+ }
+}
+
+.landing :global .hero .container {
+ margin-top: 0em;
+ margin-bottom: 0em;
+}
+
+.landing :global .container {
+ margin-top: 4em;
+ margin-bottom: 4em;
+}
+
+@media (max-width: 996px) {
+ .landing :global .col {
+ margin-bottom: 2em;
+ }
+ .landing :global .col:last-of-type {
+ margin-bottom: 0;
+ }
+}
+
+.discussions {
+ margin-top: 0 !important;
+}
+
+.floating-example {
+ position: relative;
+}
+
+@media (max-width: 996px) {
+ .floating-example > *:first-child {
+ margin-bottom: 4em;
+ }
+ .floating-example .system-window {
+ margin-left: auto;
+ margin-right: auto;
+ }
+}
+
+@media (min-width: 997px) {
+ .floating-example > div:last-child {
+ position: absolute;
+ top: 12.5%;
+ left: 40%;
+ }
+}
+
+.system-window {
+ --ifm-leading: 0;
+ width: 95%;
+ padding: 0;
+ border-radius: 1em;
+ overflow: hidden;
+ background: rgb(33, 27, 80);
+}
+
+.system-window iframe {
+ border-radius: unset;
+}
+
+.system-top-bar {
+ background-image: linear-gradient(
+ to right,
+ var(--periwinkle-50),
+ var(--bright-cyan-50) 90%,
+ var(--sea-green-50)
+ );
+ padding: 0.25em 1em;
+}
+
+.system-top-bar-circle {
+ display: inline-block;
+ width: 0.5em;
+ height: 0.5em;
+ margin-left: 0.3em;
+ border-radius: 50%;
+ filter: brightness(100%);
+}
+
+.showcase-card {
+ padding: 1.5em;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 1em;
+}
+
+@media (min-width: 997px) {
+ .showcase-card {
+ flex-direction: column;
+ }
+}
+
+.showcase-card img {
+ height: 4em;
+ margin: 1em;
+}
+
+.showcase-card p {
+ display: block;
+ margin-left: 1em;
+}
+
+.footer li {
+ color: var(--white);
+}
+
+.footer .bullets {
+ color: var(--white);
+}
+
+.footer .bullets li::before {
+ background-color: var(--dodger-blue);
+}
+
+.footer .offset-column {
+ margin-left: 2em !important;
+}
+
+.simple-yet-powerful-text {
+ margin-left: -5em;
+ margin-top: -8em;
+}
+
+.fully-customizable-text {
+ margin-right: -5em;
+ margin-top: -8em;
+}
+
+.reverse-wrap {
+ flex-wrap: wrap-reverse;
+}
+
+.example + .example {
+ margin-bottom: 4em;
+}
+
+@media (min-width: 997px) {
+ .example {
+ margin: 16em 0 !important;
+ }
+ .example + .example {
+ margin-bottom: 0 !important;
+ }
+}
+
+.preview-border {
+ box-shadow: 0 6px 58px 0 var(--uni-box-shadow-color);
+ border: solid 1px var(--uni-border-color);
+}
+
+.preview {
+ position: relative;
+ background-color: var(--uni-background-color);
+ padding: 2em;
+}
+
+.no-padding {
+ padding: 0;
+}
+
+@media (max-width: 500px) {
+ .preview {
+ padding: 1em;
+ }
+}
+
+.solid-border-box {
+ border: solid 6px var(--uni-border-color);
+ padding: 4em;
+}
+
+@media (max-width: 996px) {
+ .solid-border-box {
+ margin: 4em 0;
+ }
+}
+
+@media (max-width: 400px) {
+ .solid-border-box {
+ padding: 4em 1em;
+ }
+}
+
+.green-accent {
+ --uni-border-color: var(--greenblue);
+ --uni-box-shadow-color: var(--greenblue-10);
+ --ifm-menu-color-active: var(--uni-border-color);
+}
+
+.blue-accent {
+ --uni-border-color: var(--dodger-blue);
+ --uni-box-shadow-color: var(--dodger-blue-10);
+ --ifm-menu-color-active: var(--uni-border-color);
+}
+
+.grid3x3 {
+ position: relative;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-evenly;
+ text-align: center;
+ height: 100%;
+ padding: 4em 0;
+}
+
+.item3x3 {
+ width: 33%;
+ align-self: center;
+ padding: 1.5em 1em;
+}
+
+.company {
+ width: 100%;
+ transition: 0.2s filter;
+}
+
+.company:hover {
+ filter: none;
+}
+
+.border-dashed {
+ border: 2px dashed var(--pale-grey);
+}
+
+.border-gradient {
+ border-style: solid;
+ border-width: 6px;
+ border-image-source: linear-gradient(
+ var(--uni-border-gradient-degrees),
+ var(--periwinkle),
+ var(--bright-cyan) 51%,
+ var(--sea-green)
+ );
+ border-image-slice: 1;
+}
+
+.border-disable-top-right {
+ --uni-border-gradient-degrees: 41deg;
+ border-top: 0;
+ border-right: 0;
+}
+
+.border-disable-bottom-left {
+ --uni-border-gradient-degrees: 221deg;
+ border-bottom: 0;
+ border-left: 0;
+}
+
+.arrow-button {
+ background-color: transparent;
+ border: none;
+ cursor: pointer;
+ transition: all 0.2s linear;
+}
+
+.left-arrow-button:hover {
+ transform: translateX(-5px);
+}
+
+.right-arrow-button:hover {
+ transform: translateX(5px);
+}
+
+.arrow {
+ height: 35px;
+ width: 35px;
+}
+
+.carousel-container {
+ overflow: hidden;
+ max-width: 968px;
+}
+
+.carousel {
+ transition: transform 0.3s;
+ display: flex;
+}
+
+.testimonials {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.testimonials-pair-active {
+ display: flex;
+ justify-content: center;
+ height: auto;
+}
+
+@media (max-width: 768px) {
+ .testimonials-pair-active {
+ flex-direction: column;
+ }
+}
+
+.testimonials-pair-inactive {
+ display: none;
+}
+
+@media (max-width: 768px) {
+ .testimonials-pair-inactive {
+ flex-direction: column;
+ }
+}
+
+.testimonial {
+ margin: 2em;
+ background: var(--ifm-background-surface-color);
+}
+
+.testimonial-content {
+ width: 420px;
+ height: 100%;
+ padding: 4em 2em;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.testimonial-links-wrapper {
+ width: 140px;
+ max-width: 140px;
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 30px;
+ color: var(--dusk-blue);
+}
+
+.testimonial-links-wrapper a {
+ color: var(--dusk-blue);
+}
+
+.testimonial-description::before {
+ content: '„';
+}
+
+.testimonial-description::after {
+ content: '”';
+}
+
+.testimonial-avatar {
+ width: 100px;
+ border-radius: 50%;
+ margin: 15px 0 10px;
+}
+
+.section-heading {
+ margin: 1.5em 0;
+}
+
+.section-bgwhite {
+ color: var(--ifm-font-color-base);
+}
+
+.section-bgwhite-block {
+ position: absolute;
+ width: 200%;
+ height: 100%;
+ top: 0;
+ left: 0;
+}
+
+.section-bgwhite-block-left {
+ background: var(--white);
+ margin-left: -100%;
+}
+
+.section-bgwhite-block-right {
+ background: var(--uni-background-color);
+ margin-right: -100%;
+}
+
+@media (min-width: 997px) {
+ .section-bgwhite-block-left,
+ .section-bgwhite-block-right {
+ height: 200%;
+ }
+}
+
+.section-content {
+ position: relative;
+ padding: 0 4em !important;
+}
+
+.section-content.logos {
+ padding-left: unset !important;
+}
+
+@media (max-width: 996px) {
+ .section-content,
+ .section-content.logos {
+ margin-top: 0;
+ padding: 4em !important;
+ }
+}
+
+@media (max-width: 400px) {
+ .section-content {
+ margin-top: 0;
+ padding: 0 1em !important;
+ }
+}
+
+.fluid {
+ padding: 8em 0;
+}
+
+.whyus {
+ --ifm-hero-background-color: var(--dusk-blue) !important;
+ color: black;
+}
+
+.oval {
+ display: inline-block;
+ width: 64px;
+ height: 64px;
+ padding: 20px;
+ border-radius: 50%;
+ box-shadow: 0 8px 23px 0 var(--uni-oval-shadow-color);
+ background-color: var(--uni-oval-background-color);
+}
+
+.link-icon-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 37px;
+ height: 37px;
+ border-radius: 50%;
+ box-shadow: 0 8px 23px 0 var(--uni-oval-shadow-color);
+ background-color: var(--uni-oval-background-color);
+}
+
+.link-icon-container svg:hover {
+ transition: filter 0.15s ease-in;
+ filter: brightness(1.6);
+}
+
+.oval.white {
+ background-color: var(--white);
+ width: 100px;
+ height: 100px;
+}
+
+.top-right-corner {
+ position: absolute;
+ right: 0;
+ transform: translate(45%, -45%);
+}
+
+.badges {
+ display: flex;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+.badge-number {
+ font-size: 3em;
+ font-weight: bold;
+}
+
+.badge {
+ position: relative;
+ height: 170px;
+ width: 260px;
+ margin: 2em;
+}
+
+.badge-image {
+ position: absolute;
+ display: block;
+}
+
+.badge-centered {
+ position: absolute;
+ top: 55%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+}
+
+.call-to-action {
+ --ifm-button-background-color: var(--periwinkle) !important;
+ --ifm-button-border-color: var(--periwinkle) !important;
+ --ifm-color-primary-darker: var(--bright-cyan) !important;
+ margin-top: 4em;
+ border-radius: 2em;
+ min-width: 240px;
+}
+
+.theme-icon {
+ width: 40px;
+ height: 40px;
+ margin-right: 25px;
+ display: inline-block;
+}
+
+.theme-icon:last-child {
+ margin-right: 0;
+}
+
+.section {
+ margin-bottom: 8em;
+}
+
+.flex-center {
+ display: flex;
+ justify-content: center;
+}
+
+.top-margin {
+ margin-top: 70px;
+}
+
+.long-button {
+ max-width: 100%;
+ white-space: pre-line;
+}
+
+.text {
+ font-family: Poppins, sans-serif;
+ font-stretch: normal;
+ font-style: normal;
+ letter-spacing: normal;
+ line-height: normal;
+ color: var(--ifm-font-color-base);
+}
+
+.text-big {
+ font-size: 1.5em;
+ font-weight: bold;
+}
+
+.text-huge {
+ font-size: 2.5em;
+ font-weight: bold;
+ line-height: 1.25;
+}
+
+.text.heading-helper {
+ color: var(--uni-heading-helper-color);
+ font-family: Muli;
+ font-size: 1em;
+ font-weight: 600;
+ letter-spacing: 1px;
+ opacity: 0.6;
+}
+
+.heading {
+ color: var(--uni-heading-color);
+ font-family: Poppins;
+ font-size: 2em;
+ font-weight: bold;
+ line-height: 1.31;
+ margin-bottom: 2em;
+}
+
+.heading-white {
+ --uni-heading-color: var(--white);
+}
+
+.text.emphasis {
+ color: var(--dodger-blue);
+ font-family: Poppins;
+ font-size: 24px;
+ font-weight: 600;
+ line-height: 1.42;
+}
+
+.text.paragraph {
+ color: var(--white);
+ font-family: Muli;
+ font-size: 1em;
+ line-height: 1.75;
+ margin-bottom: 0.8em;
+ opacity: 0.8;
+}
+
+.text.section-subtitle {
+ color: var(--white);
+ font-family: Muli;
+ font-size: 1em;
+ font-weight: 600;
+ letter-spacing: 1px;
+ opacity: 0.6;
+}
+
+.text.footer-heading {
+ color: var(--dodger-blue);
+ font-family: Muli;
+ font-size: 18px;
+ font-weight: 600;
+}
+
+.supported {
+ color: var(--white);
+ font-family: Muli;
+ font-size: 0.85em;
+ font-weight: 600;
+ line-height: 2;
+ margin-top: 3em;
+ opacity: 0.6;
+}
+
+.title {
+ color: var(--white);
+ display: block;
+}
+
+.description {
+ color: var(--bright-cyan);
+ display: block;
+ margin: 24px 0;
+}
+
+@supports (background-clip: text) {
+ .description {
+ background-image: linear-gradient(
+ 81deg,
+ var(--periwinkle),
+ var(--bright-cyan),
+ var(--sea-green)
+ );
+ background-clip: text;
+ -webkit-text-fill-color: transparent;
+ }
+}
+
+.text.testimonial-description {
+ color: var(--uni-testimonial-text-color);
+ font-family: Muli;
+ font-size: 1.2em;
+ line-height: 1.75;
+ opacity: 0.8;
+ text-align: center;
+}
+
+.bullets {
+ color: var(--white) !important;
+}
+
+.bullets li {
+ margin-bottom: 0;
+}
+
+.bullets li::before {
+ background-color: var(--periwinkle) !important;
+}
+
+.commercial-bullets > li {
+ font-weight: normal;
+}
+
+.commercial-box {
+ align-items: center;
+ display: flex;
+ justify-content: center;
+ padding: 0;
+}
+
+.vazco {
+ height: 2em;
+ margin-top: 2em;
+}
+
+.form-wrapper {
+ border-radius: 5px;
+ border: 1px solid var(--ifm-color-emphasis-alpha-30);
+}
+
+.playground {
+ height: calc(100vh - var(--ifm-navbar-height));
+}
+
+.playground-wrap {
+ background-color: #fff;
+ color: #0e0e0e;
+ display: block;
+ flex: 1;
+ overflow-y: auto;
+
+ border-radius: 5px;
+ border: none;
+ margin: 0;
+ padding: 0;
+ width: 100%;
+}
+
+.playground .playground-wrap {
+ border-radius: unset;
+}
+
+.playground,
+.playground-column,
+.playground-toolbar {
+ display: flex;
+}
+
+.playground iframe,
+.playground-toolbar select {
+ border: 0;
+}
+
+.playground-column {
+ flex-direction: column;
+}
+
+.playground iframe,
+.playground-column,
+.playground-toolbar select {
+ flex: 1;
+}
+
+.playground-toolbar {
+ background-color: var(--uni-toolbar-bgcolor);
+}
+
+.playground-toolbar select {
+ background-color: var(--uni-toolbar-bgcolor);
+ font-size: 1rem;
+ margin: 0.25em;
+ padding: 0.5em;
+}
+
+.tabs {
+ display: flex;
+ margin: 0 0 0.25em -0.2em;
+ font-weight: 700;
+ flex-wrap: wrap;
+}
+
+.tabs .item {
+ text-align: center;
+ font-size: 0.8em;
+ border: 1px solid var(--ifm-menu-color-active);
+ padding: 0.8em;
+ margin: 0.25em;
+ border-radius: 3px;
+ cursor: pointer;
+ flex: 1;
+}
+
+.tabs .item.active {
+ background-color: var(--ifm-menu-color-active);
+ color: white;
+}
+
+.toggler.header {
+ display: flex;
+ justify-content: flex-end;
+}
+
+.toggler.header .item {
+ margin: 0.5em 0 0.5em 1em;
+ cursor: pointer;
+}
+
+.toggler.header .item.active {
+ color: var(--ifm-menu-color-active);
+}
+
+.toggler .tooltip {
+ position: relative;
+ display: inline-block;
+}
+
+.toggler .tooltip .tooltip-text {
+ font-size: 0.7em;
+ visibility: hidden;
+ width: 120px;
+ background-color: black;
+ color: #fff;
+ text-align: center;
+ border-radius: 6px;
+ padding: 5px 0;
+ position: absolute;
+ z-index: 1;
+ bottom: 150%;
+ left: 50%;
+ margin-left: -60px;
+}
+
+.toggler .tooltip .tooltip-text::after {
+ content: '';
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 5px;
+ border-style: solid;
+ border-color: black transparent transparent transparent;
+}
+
+.toggler .tooltip:hover .tooltip-text {
+ visibility: visible;
+}
+
+.toggler-tabs .header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
diff --git a/website/src/lib/autoresize.ts b/website/src/lib/autoresize.ts
new file mode 100644
index 000000000..5769a50e5
--- /dev/null
+++ b/website/src/lib/autoresize.ts
@@ -0,0 +1,30 @@
+import get from 'lodash/get';
+import { RefObject, useCallback, useEffect, useRef, useState } from 'react';
+
+function handleResize(
+ ref: RefObject,
+ setHeight: (height: number) => void,
+) {
+ const scrollHeight = get(ref.current, 'contentDocument.body.scrollHeight', 0);
+
+ if (scrollHeight !== 0) {
+ setHeight(scrollHeight);
+ }
+}
+
+export function useFrameAutoResize(watch: unknown[]) {
+ const [height, setHeight] = useState(300);
+ const ref = useRef();
+ const onLoad = useCallback(
+ () => handleResize(ref, setHeight),
+ [ref, setHeight],
+ );
+
+ useEffect(() => {
+ const id = setInterval(onLoad, 1000);
+ return () => clearInterval(id);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [onLoad, ...watch]);
+
+ return { onLoad, ref, style: { height } };
+}
diff --git a/website/src/lib/presets.ts b/website/src/lib/presets.ts
new file mode 100644
index 000000000..642591fad
--- /dev/null
+++ b/website/src/lib/presets.ts
@@ -0,0 +1,145 @@
+const preset = (strings: TemplateStringsArray) =>
+ strings[0].slice(5, -3).replace(/([\r\n]+) {4}/g, '$1');
+
+const presets = {
+ 'Welcome!': preset`
+ new SimpleSchema2Bridge({
+ schema: new SimpleSchema({
+ date: { type: Date, defaultValue: new Date() },
+ adult: Boolean,
+ size: {
+ type: String,
+ defaultValue: 'm',
+ allowedValues: ['xs', 's', 'm', 'l', 'xl'],
+ },
+ rating: {
+ type: Number,
+ allowedValues: [1, 2, 3, 4, 5],
+ uniforms: { checkboxes: true },
+ },
+ friends: { type: Array, minCount: 1 },
+ 'friends.$': Object,
+ 'friends.$.name': { type: String, min: 3 },
+ 'friends.$.age': { type: Number, min: 0, max: 150 },
+ })
+ })
+ `,
+
+ 'Address (JSONSchema)': preset`
+ (() => {
+ const ajv = new Ajv({ allErrors: true, useDefaults: true, keywords: ["uniforms"] });
+ const schema = {
+ title: 'Address',
+ type: 'object',
+ properties: {
+ city: { type: 'string' },
+ state: { type: 'string' },
+ street: { type: 'string' },
+ zip: { type: 'string', pattern: '[0-9]{5}' },
+ },
+ required: ['street', 'zip', 'state'],
+ };
+
+ function createValidator(schema) {
+ const validator = ajv.compile(schema);
+
+ return (model) => {
+ validator(model);
+
+ if (validator.errors && validator.errors.length) {
+ return { details: validator.errors };
+ }
+ };
+ }
+
+ const validator = createValidator(schema);
+
+ return new JSONSchemaBridge({ schema, validator });
+ })()
+ `,
+
+ 'Address (SimpleSchema)': preset`
+ new SimpleSchema2Bridge({
+ schema: new SimpleSchema({
+ city: {
+ type: String,
+ optional: true,
+ max: 50,
+ },
+ state: String,
+ street: { type: String, max: 100 },
+ zip: {
+ type: String,
+ regEx: /^[0-9]{5}$/,
+ },
+ })
+ })
+ `,
+
+ 'Address (Zod)': preset`
+ new ZodBridge({
+ schema: z.object({
+ city: z.string().max(50).optional(),
+ state: z.string(),
+ street: z.string().max(100),
+ zip: z.string().regex(/^[0-9]{5}$/),
+ })
+ })
+ `,
+
+ 'All Fields (SimpleSchema)': preset`
+ new SimpleSchema2Bridge({
+ schema: new SimpleSchema({
+ text: { type: String },
+ num: { type: Number },
+ bool: { type: Boolean },
+ nested: { type: Object },
+ 'nested.text': { type: String },
+ date: { type: Date },
+ list: { type: Array },
+ 'list.$': {
+ type: String,
+ uniforms: { label: 'List Text', placeholder: 'List Text Placeholder' },
+ },
+ select: {
+ type: String,
+ uniforms: {
+ options: [
+ { label: 'Option A', value: 'a' },
+ { label: 'Option B', value: 'b' },
+ ],
+ },
+ },
+ radio: {
+ type: String,
+ uniforms: {
+ checkboxes: true,
+ options: [
+ { label: 'Option A', value: 'a' },
+ { label: 'Option B', value: 'b' },
+ ],
+ },
+ },
+ })
+ })
+ `,
+
+ 'All Fields (Zod)': preset`
+ new ZodBridge({
+ schema: z.object({
+ text: z.string(),
+ num: z.number(),
+ bool: z.boolean(),
+ nested: z.object({ text: z.string() }),
+ date: z.date(),
+ // TODO: Custom label and placeholder.
+ list: z.array(z.string()),
+ select: z.enum(['a', 'b']),
+ // TODO: Enums with custom props.
+ radio: z.enum(['a', 'b']),
+ })
+ })
+ `,
+};
+
+export default presets;
diff --git a/website/src/lib/schema.ts b/website/src/lib/schema.ts
new file mode 100644
index 000000000..8f0b292da
--- /dev/null
+++ b/website/src/lib/schema.ts
@@ -0,0 +1,110 @@
+import Ajv from 'ajv';
+import MessageBox from 'message-box';
+import SimpleSchema from 'simpl-schema';
+import { filterDOMProps } from 'uniforms';
+import { JSONSchemaBridge } from 'uniforms-bridge-json-schema';
+import { SimpleSchema2Bridge } from 'uniforms-bridge-simple-schema-2';
+import { ZodBridge } from 'uniforms-bridge-zod';
+import { z } from 'zod';
+
+import presets from './presets';
+import { themes } from './universal';
+
+// Hide custom props.
+declare module 'uniforms' {
+ interface FilterDOMProps {
+ const: never;
+ }
+}
+
+filterDOMProps.register('const');
+
+// FIXME: Make it extensible for globals.
+const scope: any = typeof window === 'undefined' ? global : window;
+
+// FIXME: This file is loading a different version of simpl-schema than the one
+// that uniforms-bridge-simple-schema-2 package.
+SimpleSchema.extendOptions(['uniforms']);
+
+// This is required for the eval.
+scope.Ajv = Ajv;
+scope.JSONSchemaBridge = JSONSchemaBridge;
+scope.SimpleSchema = SimpleSchema;
+scope.SimpleSchema2Bridge = SimpleSchema2Bridge;
+scope.ZodBridge = ZodBridge;
+scope.z = z;
+
+// Dynamic field error.
+MessageBox.defaults({ messages: { en: { syntax: '' } } });
+
+const propsSchema = new SimpleSchema({
+ autosave: { optional: true, type: Boolean },
+ autosaveDelay: { optional: true, type: SimpleSchema.Integer },
+ disabled: { optional: true, type: Boolean },
+ label: { optional: true, type: Boolean },
+ placeholder: { optional: true, type: Boolean },
+ readOnly: { optional: true, type: Boolean },
+ showInlineError: { optional: true, type: Boolean },
+ asyncOnSubmit: {
+ optional: true,
+ type: Boolean,
+ label: 'Async onSubmit (1 sec)',
+ },
+ asyncOnValidate: {
+ optional: true,
+ type: Boolean,
+ label: 'Async onValidate (1 sec)',
+ },
+
+ schema: {
+ optional: true,
+ type: String,
+ custom() {
+ try {
+ eval(`(${this.value ?? ''})`);
+ return undefined;
+ } catch (error) {
+ const message = error instanceof Error ? error.message : String(error);
+ MessageBox.defaults({ messages: { en: { syntax: message } } });
+ return 'syntax';
+ }
+ },
+ },
+});
+
+const propsBridge = new SimpleSchema2Bridge({ schema: propsSchema });
+
+export const schema = new SimpleSchema({
+ preset: {
+ type: String,
+ defaultValue: Object.keys(presets)[0],
+ allowedValues: Object.keys(presets),
+ },
+
+ props: {
+ type: Object,
+ blackbox: true,
+ defaultValue: {
+ autosave: false,
+ autosaveDelay: 100,
+ disabled: false,
+ label: true,
+ placeholder: false,
+ readOnly: false,
+ schema: presets[Object.keys(presets)[0] as keyof typeof presets],
+ showInlineError: false,
+ asyncOnSubmit: false,
+ asyncOnValidate: false,
+ },
+ uniforms: { schema: propsBridge },
+ },
+
+ theme: {
+ type: String,
+ uniforms: { transform: (theme: string) => `uniforms-${theme}` },
+ defaultValue: Object.keys(themes)[0],
+ allowedValues: Object.keys(themes),
+ },
+});
+
+export const bridge = new SimpleSchema2Bridge({ schema });
diff --git a/website/src/lib/stats.ts b/website/src/lib/stats.ts
new file mode 100644
index 000000000..a26ba7e50
--- /dev/null
+++ b/website/src/lib/stats.ts
@@ -0,0 +1,144 @@
+import { useEffect, useState } from 'react';
+
+function cacheKey(key: string) {
+ return `__cached-fetch-${key}`;
+}
+
+function cacheGet(key: string) {
+ try {
+ const stored = localStorage.getItem(cacheKey(key));
+ const { expires, data } = JSON.parse(stored ?? '');
+ return { expired: !expires || expires < Date.now(), data };
+ } catch (error) {
+ return { expired: true, data: null };
+ }
+}
+
+function cacheSet(key: string, data: unknown, expires: number) {
+ try {
+ localStorage.setItem(cacheKey(key), JSON.stringify({ data, expires }));
+ } catch (error) {
+ // Nothing.
+ }
+}
+
+function cached(key: string, fallback: () => Promise, expires = 0) {
+ const { expired, data } = cacheGet(key);
+
+ if (expired) {
+ return fallback()
+ .then(data => {
+ cacheSet(key, data, expires);
+ return data;
+ })
+ .catch(() => data || 0);
+ }
+
+ return Promise.resolve(data);
+}
+
+function formatDate(date: Date) {
+ return date.toISOString().slice(0, 10);
+}
+
+function formatNumber(number: number) {
+ return number.toLocaleString('en-US');
+}
+
+function getLastDayOfYear(date: Date) {
+ const newDate = new Date(date);
+ newDate.setFullYear(newDate.getFullYear() + 1);
+ newDate.setDate(newDate.getDate() - 1);
+ return newDate;
+}
+
+function getYearRange(date: Date) {
+ const lastDayOfMonth = getLastDayOfYear(date);
+ return `${formatDate(date)}:${formatDate(lastDayOfMonth)}`;
+}
+
+function dateRanges(from: Date, to: Date) {
+ const dates = [];
+ while (from < to) {
+ dates.push(getYearRange(from));
+ from.setFullYear(from.getFullYear() + 1);
+ }
+ return dates;
+}
+
+/**
+ * Queries are limited to at most 18 months of data. The earliest date for which data will be returned is January 10, 2015.
+ * {@link https://github.com/npm/registry/blob/master/docs/download-counts.md#limits See limits}
+ */
+async function fetchNPMDownloadsInRange(range: string) {
+ type NPMResponse = {
+ downloads: number;
+ start: string;
+ end: string;
+ package: string;
+ };
+
+ const response = await fetch(
+ `https://api.npmjs.org/downloads/point/${range}/uniforms`,
+ );
+ const { downloads }: NPMResponse = await response.json();
+ return downloads || 0;
+}
+
+function getNPMDownloadsInRange(range: string, expires?: number) {
+ return cached(`npm-${range}`, () => fetchNPMDownloadsInRange(range), expires);
+}
+
+function getNPMDownloads(from: Date, to: Date) {
+ const dates = dateRanges(from, to);
+ const lastRange = dates.pop()!;
+
+ const oneYear = 12 * 31 * 24 * 60 * 60 * 1000;
+ const oneDay = 1 * 24 * 60 * 60 * 1000;
+
+ return Promise.all(
+ dates
+ .map(range => getNPMDownloadsInRange(range, Date.now() + oneYear))
+ .concat(getNPMDownloadsInRange(lastRange, Date.now() + oneDay)),
+ ).then(sums => sums.reduce((a, b) => a + b, 0));
+}
+
+function getGitHubStats() {
+ const twoMinutes = 2 * 60 * 1000;
+ return cached(
+ 'github',
+ () =>
+ fetch('https://api.github.com/repos/vazco/uniforms')
+ .then(response => response.json())
+ .then(({ forks_count: forks, stargazers_count: stars }) => ({
+ forks,
+ stars,
+ })),
+ Date.now() + twoMinutes,
+ );
+}
+
+export function useStats() {
+ const [stars, setStars] = useState();
+ const [forks, setForks] = useState();
+ const [downloads, setDownloads] = useState();
+
+ useEffect(() => {
+ getGitHubStats().then(({ forks, stars }) => {
+ forks && setForks(formatNumber(forks));
+ stars && setStars(formatNumber(stars));
+ });
+ }, [stars, forks]);
+
+ useEffect(() => {
+ const DATE_RANGE_START = '2015-01-01';
+ const start = new Date(DATE_RANGE_START);
+ const today = new Date();
+
+ getNPMDownloads(start, today).then(downloads =>
+ setDownloads(formatNumber(downloads)),
+ );
+ }, [downloads]);
+
+ return { downloads, forks, stars };
+}
diff --git a/website/src/lib/styles.tsx b/website/src/lib/styles.tsx
new file mode 100644
index 000000000..b275db1a9
--- /dev/null
+++ b/website/src/lib/styles.tsx
@@ -0,0 +1,49 @@
+import React from 'react';
+
+const commonStyles = `
+ body {
+ margin: 0;
+ }
+
+ textarea[name="schema"] {
+ font-family: monospace !important;
+ min-height: 20em !important;
+ }
+
+ .frame-root {
+ padding: 1rem !important;
+ }
+`;
+
+const common = ;
+
+const style = (links: string[]) =>
+ links
+ .map((link, index) => )
+ .concat(common);
+
+const styles = {
+ antd: style([
+ 'https://cdnjs.cloudflare.com/ajax/libs/antd/4.4.0/antd.min.css',
+ ]),
+
+ bootstrap4: style([
+ 'https://cdnjs.cloudflare.com/ajax/libs/octicons/3.5.0/octicons.min.css',
+ 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css',
+ ]),
+
+ bootstrap5: style([
+ 'https://cdnjs.cloudflare.com/ajax/libs/octicons/3.5.0/octicons.min.css',
+ 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.2.3/css/bootstrap.min.css',
+ ]),
+
+ mui: style([]),
+
+ semantic: style([
+ 'https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.7/semantic.min.css',
+ ]),
+
+ unstyled: style([]),
+};
+
+export default styles;
diff --git a/website/src/lib/tabs.ts b/website/src/lib/tabs.ts
new file mode 100644
index 000000000..38cbace24
--- /dev/null
+++ b/website/src/lib/tabs.ts
@@ -0,0 +1,31 @@
+import { useCallback, useEffect, useState } from 'react';
+
+const activeTabs: Record = Object.create(null);
+const handlers: Record void)[]> = Object.create(null);
+
+function register(group: string, handler: (tab: number) => void) {
+ if (handlers[group] === undefined) {
+ activeTabs[group] = 0;
+ handlers[group] = [];
+ }
+
+ handlers[group].push(handler);
+
+ return () => {
+ handlers[group].splice(handlers[group].indexOf(handler), 1);
+ };
+}
+
+function trigger(group: string, activeTab: number) {
+ activeTabs[group] = activeTab;
+ handlers[group].forEach(handler => {
+ handler(activeTab);
+ });
+}
+
+export function useTabs(group: string) {
+ const [activeTab, setActiveTab] = useState(activeTabs[group] || 0);
+ const onTab = useCallback(activeTab => trigger(group, activeTab), [group]);
+ useEffect(() => register(group, setActiveTab), [group]);
+ return { activeTab, onTab };
+}
diff --git a/website/src/lib/universal.tsx b/website/src/lib/universal.tsx
new file mode 100644
index 000000000..1ea89f0d8
--- /dev/null
+++ b/website/src/lib/universal.tsx
@@ -0,0 +1,52 @@
+import React, { createContext, useContext } from 'react';
+import { UnknownObject } from 'uniforms';
+import * as antd from 'uniforms-antd';
+import * as bootstrap4 from 'uniforms-bootstrap4';
+import * as bootstrap5 from 'uniforms-bootstrap5';
+import * as mui from 'uniforms-mui';
+import * as semantic from 'uniforms-semantic';
+import * as unstyled from 'uniforms-unstyled';
+
+export const AutoFields = _createThemedComponent('AutoFields');
+export const AutoField = _createThemedComponent('AutoField');
+export const AutoForm = _createThemedComponent('AutoForm');
+export const BaseForm = _createThemedComponent('BaseForm');
+export const BoolField = _createThemedComponent('BoolField');
+export const DateField = _createThemedComponent('DateField');
+export const ErrorField = _createThemedComponent('ErrorField');
+export const ErrorsField = _createThemedComponent('ErrorsField');
+export const HiddenField = _createThemedComponent('HiddenField');
+export const ListAddField = _createThemedComponent('ListAddField');
+export const ListDelField = _createThemedComponent('ListDelField');
+export const ListField = _createThemedComponent('ListField');
+export const ListItemField = _createThemedComponent('ListItemField');
+export const LongTextField = _createThemedComponent('LongTextField');
+export const NestField = _createThemedComponent('NestField');
+export const NumField = _createThemedComponent('NumField');
+export const QuickForm = _createThemedComponent('QuickForm');
+export const RadioField = _createThemedComponent('RadioField');
+export const SelectField = _createThemedComponent('SelectField');
+export const SubmitField = _createThemedComponent('SubmitField');
+export const TextField = _createThemedComponent('TextField');
+export const ValidatedForm = _createThemedComponent('ValidatedForm');
+export const ValidatedQuickForm = _createThemedComponent('ValidatedQuickForm');
+
+export const themes = {
+ antd,
+ bootstrap4,
+ bootstrap5,
+ mui,
+ semantic,
+ unstyled,
+};
+
+export const themeContext = createContext('unstyled');
+
+function _createThemedComponent(component: keyof typeof unstyled) {
+ return function ThemedComponent(props: UnknownObject) {
+ const theme = useContext(themeContext);
+ // FIXME: Form prop errors due to `props` having no strict type here.
+ const Component: any = themes[theme][component];
+ return ;
+ };
+}
diff --git a/website/src/lib/utils.ts b/website/src/lib/utils.ts
new file mode 100644
index 000000000..30a642f9f
--- /dev/null
+++ b/website/src/lib/utils.ts
@@ -0,0 +1,39 @@
+import pick from 'lodash/pick';
+import LZString from 'lz-string';
+
+const URL_KEYS = ['preset', 'props', 'theme'];
+
+export const compress = (string: string) =>
+ LZString.compressToBase64(string)
+ .replace(/\+/g, '-') // Convert '+' to '-'.
+ .replace(/\//g, '_') // Convert '/' to '_'.
+ .replace(/=+$/, ''); // Remove ending '='.
+
+const decompress = (string: string) =>
+ LZString.decompressFromBase64(string.replace(/-/g, '+').replace(/_/g, '/'));
+
+export function updateQuery(state: object) {
+ try {
+ const query = pick(state, URL_KEYS);
+ const serialized = JSON.stringify(query);
+ const compressed = compress(serialized);
+ const encoded = encodeURIComponent(compressed);
+ const hash = '?' + encoded;
+ window.location.hash = hash;
+ } catch (_) {
+ // It's alright.
+ }
+}
+
+export function parseQuery() {
+ try {
+ const hash = document.location.hash.replace(/^#\?/, '');
+ const decoded = decodeURIComponent(hash);
+ const decompressed = decompress(decoded);
+ const deserialized = JSON.parse(decompressed ?? '');
+ const query = pick(deserialized, URL_KEYS);
+ return query;
+ } catch (_) {
+ return {};
+ }
+}
diff --git a/website/src/pages-parts/CommonForms/ShippingForm.tsx b/website/src/pages-parts/CommonForms/ShippingForm.tsx
new file mode 100644
index 000000000..c70e06a31
--- /dev/null
+++ b/website/src/pages-parts/CommonForms/ShippingForm.tsx
@@ -0,0 +1,80 @@
+import React from 'react';
+import {
+ AutoField,
+ AutoForm,
+ ErrorsField,
+ SubmitField,
+} from 'uniforms-semantic';
+
+import { bridge as schema } from './ShippingSchema';
+
+const model = {
+ firstName: 'John',
+ lastName: 'Doe',
+ addressLine: '7 Podwale st.',
+ city: 'Wroclaw',
+};
+
+export function ShippingForm() {
+ return (
+ alert(JSON.stringify(model, null, 2))}
+ model={model}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/website/src/pages-parts/CommonForms/ShippingSchema.ts b/website/src/pages-parts/CommonForms/ShippingSchema.ts
new file mode 100644
index 000000000..0c2ace0a9
--- /dev/null
+++ b/website/src/pages-parts/CommonForms/ShippingSchema.ts
@@ -0,0 +1,27 @@
+import SimpleSchema from 'simpl-schema';
+import { SimpleSchema2Bridge } from 'uniforms-bridge-simple-schema-2';
+
+//
+const schema = new SimpleSchema({
+ firstName: { type: String },
+ lastName: { type: String },
+ country: {
+ type: String,
+ allowedValues: ['Poland', 'England'],
+ defaultValue: 'Poland',
+ },
+ state: {
+ type: String,
+ optional: true,
+ },
+ useThisAddressForPaymentDetails: {
+ type: Boolean,
+ defaultValue: false,
+ },
+ addressLine: { type: String },
+ city: { type: String },
+ zip: { type: String },
+});
+
+export const bridge = new SimpleSchema2Bridge({ schema });
+//
diff --git a/website/src/pages-parts/CommonForms/SignUp.tsx b/website/src/pages-parts/CommonForms/SignUp.tsx
new file mode 100644
index 000000000..036932180
--- /dev/null
+++ b/website/src/pages-parts/CommonForms/SignUp.tsx
@@ -0,0 +1,13 @@
+import React from 'react';
+import { AutoForm } from '../../lib/universal';
+
+import { bridge as schema } from './SignUpSchema';
+
+export function SignUp() {
+ return (
+ alert(JSON.stringify(model, null, 2))}
+ />
+ );
+}
diff --git a/website/src/pages-parts/CommonForms/SignUpSchema.ts b/website/src/pages-parts/CommonForms/SignUpSchema.ts
new file mode 100644
index 000000000..ddbb924b1
--- /dev/null
+++ b/website/src/pages-parts/CommonForms/SignUpSchema.ts
@@ -0,0 +1,46 @@
+import Ajv from 'ajv';
+import { JSONSchemaBridge } from 'uniforms-bridge-json-schema';
+
+const ajv = new Ajv({ allErrors: true, useDefaults: true, $data: true });
+ajv.addKeyword('uniforms');
+
+const schema = {
+ title: 'Guest',
+ type: 'object',
+ properties: {
+ fullname: { type: 'string' },
+ email: { type: 'string' },
+ confirmEmail: { type: 'string', const: { $data: '1/email' } },
+ password: {
+ type: 'string',
+ uniforms: { type: 'password' },
+ },
+ confirmPassword: {
+ type: 'string',
+ const: { $data: '1/password' },
+ uniforms: { type: 'password' },
+ },
+ acceptTermsOfUse: { type: 'boolean', const: true },
+ },
+ required: [
+ 'fullname',
+ 'email',
+ 'confirmEmail',
+ 'password',
+ 'confirmPassword',
+ 'acceptTermsOfUse',
+ ],
+};
+
+function createValidator(schema: object) {
+ const validator = ajv.compile(schema);
+
+ return (model: object) => {
+ validator(model);
+ return validator.errors?.length ? { details: validator.errors } : null;
+ };
+}
+
+const validator = createValidator(schema);
+
+export const bridge = new JSONSchemaBridge({ schema, validator });
diff --git a/website/src/pages-parts/CustomFields/ImageField.tsx b/website/src/pages-parts/CustomFields/ImageField.tsx
new file mode 100644
index 000000000..997c0b7cc
--- /dev/null
+++ b/website/src/pages-parts/CustomFields/ImageField.tsx
@@ -0,0 +1,49 @@
+import React from 'react';
+import { AutoForm, SubmitField } from '../../lib/universal';
+import { HTMLFieldProps, connectField } from 'uniforms';
+
+import { bridge as schema } from './ImageFieldSchema';
+
+type ImageProps = HTMLFieldProps;
+
+function Image({ onChange, value }: ImageProps) {
+ return (
+
+
+ Choose your photo
+
+
+
{
+ if (files && files[0]) {
+ onChange(URL.createObjectURL(files[0]));
+ }
+ }}
+ style={{ display: 'none' }}
+ type="file"
+ />
+
+ );
+}
+
+const ImageField = connectField(Image);
+
+export function ImageFieldForm() {
+ return (
+ alert(JSON.stringify(model, null, 2))}
+ >
+
+
+
+
+
+ );
+}
diff --git a/website/src/pages-parts/CustomFields/ImageFieldSchema.ts b/website/src/pages-parts/CustomFields/ImageFieldSchema.ts
new file mode 100644
index 000000000..df9b34814
--- /dev/null
+++ b/website/src/pages-parts/CustomFields/ImageFieldSchema.ts
@@ -0,0 +1,25 @@
+import Ajv from 'ajv';
+import { JSONSchemaBridge } from 'uniforms-bridge-json-schema';
+
+const ajv = new Ajv({ allErrors: true, useDefaults: true });
+
+const schema = {
+ title: 'Guest',
+ type: 'object',
+ properties: {
+ pictureUrl: { type: 'string' },
+ },
+};
+
+function createValidator(schema: object) {
+ const validator = ajv.compile(schema);
+
+ return (model: object) => {
+ validator(model);
+ return validator.errors?.length ? { details: validator.errors } : null;
+ };
+}
+
+const validator = createValidator(schema);
+
+export const bridge = new JSONSchemaBridge({ schema, validator });
diff --git a/website/src/pages-parts/LandingPage/CommercialServices.tsx b/website/src/pages-parts/LandingPage/CommercialServices.tsx
new file mode 100644
index 000000000..8f5702536
--- /dev/null
+++ b/website/src/pages-parts/LandingPage/CommercialServices.tsx
@@ -0,0 +1,109 @@
+import classNames from 'classnames';
+import React from 'react';
+
+import { Button } from '../../components/Button';
+import { Heading } from '../../components/Heading';
+import styles from '../../css/index.module.css';
+
+const forminerLink =
+ 'https://forminer.com/?utm_source=uniforms&utm_medium=Section_CTA&utm_campaign=Forminer_uniforms_section_CTA&utm_id=Forminer_uniforms_section';
+
+export function CommercialServices() {
+ return (
+
+
+
+
+
+
+
+ Commercial support
+
+
Forminer
+
+ A commercial implementation of uniforms, which can save you up to
+ 1500 hours of work.
+
+
+
+ Ideal for building no-code tools - thanks to a
+ schema-first approach
+
+
+ Package - easy to integrate with your project and design
+ system
+
+
+ Powerful solution - custom fields, full state machine
+
+
+ Good level of support - based on our popular OS solution,
+ uniforms
+
+
+
+
+ Check Forminer in action
+
+
+
+
+
+
+ );
+}
diff --git a/website/src/pages-parts/LandingPage/Comparison.tsx b/website/src/pages-parts/LandingPage/Comparison.tsx
new file mode 100644
index 000000000..76a14aa80
--- /dev/null
+++ b/website/src/pages-parts/LandingPage/Comparison.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+
+import Matrix from '../../../docs/compare-matrix-landing.md';
+import { Button } from '../../components/Button';
+import { Heading } from '../../components/Heading';
+import styles from '../../css/index.module.css';
+
+export function Comparison() {
+ return (
+
+
+ Comparison
+
+ with similar libraries
+
+
+
+ );
+}
diff --git a/website/src/pages-parts/LandingPage/Discussions.tsx b/website/src/pages-parts/LandingPage/Discussions.tsx
new file mode 100644
index 000000000..2e961dc3b
--- /dev/null
+++ b/website/src/pages-parts/LandingPage/Discussions.tsx
@@ -0,0 +1,21 @@
+import React from 'react';
+
+import { Button } from '../../components/Button';
+import { Heading } from '../../components/Heading';
+import styles from '../../css/index.module.css';
+
+export function Discussions() {
+ return (
+
+
+ If You want to know more – join us at GitHub Discussions!
+
+
+ GitHub Discussions
+
+
+ );
+}
diff --git a/website/src/pages-parts/LandingPage/ExampleFullyCustomizable.tsx b/website/src/pages-parts/LandingPage/ExampleFullyCustomizable.tsx
new file mode 100644
index 000000000..d17b352ad
--- /dev/null
+++ b/website/src/pages-parts/LandingPage/ExampleFullyCustomizable.tsx
@@ -0,0 +1,54 @@
+import classNames from 'classnames';
+import React from 'react';
+
+import { ExampleCustomizer } from '../../components/ExampleCustomizer';
+import { Heading } from '../../components/Heading';
+import styles from '../../css/index.module.css';
+
+export function ExampleFullyCustomizable() {
+ return (
+
+
+
+
+
+ Fully
+
+ customizable
+
+
+ One-line helper for creating custom fields
+ Supports all types of objects
+
+ Freedom of choice when defining custom fields depending on the
+ abstraction level-schema or theme dependent approach
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/website/src/pages-parts/LandingPage/ExampleSimpleYetPowerful.tsx b/website/src/pages-parts/LandingPage/ExampleSimpleYetPowerful.tsx
new file mode 100644
index 000000000..d5b7bfe9c
--- /dev/null
+++ b/website/src/pages-parts/LandingPage/ExampleSimpleYetPowerful.tsx
@@ -0,0 +1,59 @@
+import classNames from 'classnames';
+import React from 'react';
+
+import { ExampleCustomizer } from '../../components/ExampleCustomizer';
+import { Heading } from '../../components/Heading';
+import styles from '../../css/index.module.css';
+
+export function ExampleSimpleYetPowerful() {
+ return (
+
+
+
+
+
+
+
+ Simple,
+
+ yet powerful
+
+
+ Abbreviates form code by 51%
+
+ Out-of-the box built-in fields capable of rendering every schema
+
+ Automatic state management
+ Inline and asynchronous form validation
+
+ Clean-looking components while keeping extensibility and
+ separation of concerns
+
+
+
+
+
+
+ );
+}
diff --git a/website/src/pages-parts/LandingPage/Footer.tsx b/website/src/pages-parts/LandingPage/Footer.tsx
new file mode 100644
index 000000000..b37b45553
--- /dev/null
+++ b/website/src/pages-parts/LandingPage/Footer.tsx
@@ -0,0 +1,87 @@
+import Link from '@docusaurus/Link';
+import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
+import classNames from 'classnames';
+import React from 'react';
+
+import styles from '../../css/index.module.css';
+import { ShowcaseCard } from './ShowcaseCard';
+
+export function Footer() {
+ const context = useDocusaurusContext();
+ const { email } = context.siteConfig.customFields;
+
+ const currentYear = new Date().getFullYear();
+
+ return (
+
+
+
+
+
+
+
+
+ Always free
+
+ and open source
+
+
+
+ Introduction
+
+
+ Examples
+
+
+ API Reference
+
+
+
+
+
+ Join the Community!
+
+
+
+ GitHub Discussions
+
+
+ GitHub
+
+
+
+
+
+ Start a conversation!
+
+
+
+ {email as string}
+
+
+
+
+ Meet the creators!
+
+
+
+
+
+
+
+ Copyright © 2016 - {currentYear} Vazco.
+ All Rights Reserved.
+
+
+
+
+
+ );
+}
diff --git a/website/src/pages-parts/LandingPage/Header.tsx b/website/src/pages-parts/LandingPage/Header.tsx
new file mode 100644
index 000000000..214ec231f
--- /dev/null
+++ b/website/src/pages-parts/LandingPage/Header.tsx
@@ -0,0 +1,140 @@
+import Link from '@docusaurus/Link';
+import classNames from 'classnames';
+import React from 'react';
+
+import { Button } from '../../components/Button';
+import { CodeSection } from '../../components/CodeSection';
+import { FormWrapper } from '../../components/FormWrapper';
+import styles from '../../css/index.module.css';
+import { themeContext } from '../../lib/universal';
+import { ShippingForm } from '../CommonForms/ShippingForm';
+
+type SystemWindowProps = JSX.IntrinsicElements['div'];
+
+function SystemWindow({ children, className, ...props }: SystemWindowProps) {
+ return (
+
+ );
+}
+
+function Showcase() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const themes = [
+ {
+ alt: 'Semantic UI',
+ src: 'themes/semantic.svg',
+ to: 'https://semantic-ui.com/',
+ },
+ {
+ alt: 'Ant Design',
+ src: 'themes/antd.png',
+ to: 'https://ant.design/',
+ },
+ {
+ alt: 'MUI',
+ src: 'themes/mui.png',
+ to: 'https://mui.com/',
+ },
+ {
+ alt: 'Bootstrap',
+ src: 'themes/bootstrap.svg',
+ to: 'https://getbootstrap.com/',
+ },
+];
+
+export function Header() {
+ return (
+
+
+
+
+
+ uniforms
+
+
+ A React library for building forms from any schema
+
+
+ support of all schemas and themes
+ instant prototyping
+ simplifies separation of concerns
+
+
+
+ Supported design libraries:
+
+ {themes.map(({ alt, src, to }) => (
+
+
+
+ ))}
+
+
+
+ Get Started
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/website/src/pages-parts/LandingPage/LandingPage.tsx b/website/src/pages-parts/LandingPage/LandingPage.tsx
new file mode 100644
index 000000000..2bf3120c8
--- /dev/null
+++ b/website/src/pages-parts/LandingPage/LandingPage.tsx
@@ -0,0 +1,43 @@
+import classNames from 'classnames';
+import React from 'react';
+
+import styles from '../../css/index.module.css';
+import { CommercialServices } from './CommercialServices';
+import { Comparison } from './Comparison';
+import { Discussions } from './Discussions';
+import { ExampleFullyCustomizable } from './ExampleFullyCustomizable';
+import { ExampleSimpleYetPowerful } from './ExampleSimpleYetPowerful';
+import { Footer } from './Footer';
+import { Header } from './Header';
+import { OpenSource } from './OpenSource';
+import { Testimonials } from './Testimonials';
+import { WhoUses } from './WhoUses';
+import { WhyUs } from './WhyUs';
+
+export function LandingPage() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/website/src/pages-parts/LandingPage/OpenSource.tsx b/website/src/pages-parts/LandingPage/OpenSource.tsx
new file mode 100644
index 000000000..e85a5dafb
--- /dev/null
+++ b/website/src/pages-parts/LandingPage/OpenSource.tsx
@@ -0,0 +1,54 @@
+import classNames from 'classnames';
+import React from 'react';
+import { Download, GitBranch, Star } from 'react-feather';
+
+import { Badge } from '../../components/Badge';
+import { Heading } from '../../components/Heading';
+import styles from '../../css/index.module.css';
+import { useStats } from '../../lib/stats';
+
+export function OpenSource() {
+ const { stars, forks, downloads } = useStats();
+ return (
+
+
+ Always Open Source.
+
+
+ React form library trusted by GitHub community
+
+
+
+
+
+
+
+ );
+}
diff --git a/website/src/pages-parts/LandingPage/ShowcaseCard.tsx b/website/src/pages-parts/LandingPage/ShowcaseCard.tsx
new file mode 100644
index 000000000..b64daaecb
--- /dev/null
+++ b/website/src/pages-parts/LandingPage/ShowcaseCard.tsx
@@ -0,0 +1,18 @@
+import classNames from 'classnames';
+import React from 'react';
+
+import styles from '../../css/index.module.css';
+
+export function ShowcaseCard() {
+ return (
+
+
+
+ A React library for building forms. Integrates with every schema and
+ wide range of themes.
+
+
+ );
+}
diff --git a/website/src/pages-parts/LandingPage/Testimonials.tsx b/website/src/pages-parts/LandingPage/Testimonials.tsx
new file mode 100644
index 000000000..59983d2f0
--- /dev/null
+++ b/website/src/pages-parts/LandingPage/Testimonials.tsx
@@ -0,0 +1,279 @@
+import classNames from 'classnames';
+import React, { useState } from 'react';
+import { GitHub, Linkedin } from 'react-feather';
+
+import { Heading } from '../../components/Heading';
+import { IconLink } from '../../components/IconLink';
+import styles from '../../css/index.module.css';
+
+type Testimonial = {
+ company: string;
+ description: JSX.Element;
+ position: string;
+ who: string;
+ avatar: string;
+ linkGithub: string;
+ linkLinkedin: string;
+};
+
+const testimonials: Testimonial[] = [
+ {
+ company: 'Resolve',
+ description: (
+ <>
+ uniforms is the backbone of our data-intensive web-applications. We have
+ about 200 different forms, from very simple ones, to ones that are
+ filled with complex data-loading conditional form components, which
+ create an incredible UX for our users. And if you really need to push
+ the limits of what you can do with forms, I would highly recommend{' '}
+
+
+ reaching out to Vazco
+
+ {' '}
+ themselves for expert advice.
+ >
+ ),
+ position: 'CTO and Co-Founder',
+ who: 'Florian Bienefelt',
+ avatar: 'avatar/florian-bienefelt.jpg',
+ linkGithub: 'https://github.com/Floriferous',
+ linkLinkedin: 'https://ch.linkedin.com/in/florianbienefelt',
+ },
+ {
+ company: 'Mindtree',
+ description: (
+ <>
+ Vazco's uniforms is a developer's go-to solution for
+ simplifying form creation and validation in JavaScript applications.
+ With its intuitive APIs and extensive feature set, uniforms streamlines
+ the process of building complex forms. It offers a wide range of input
+ types, validation options, and customization possibilities. Backed by an
+ active community, Vazco's uniforms ensures ongoing support and
+ updates, empowering developers to enhance productivity and deliver
+ polished, user-friendly applications.
+ >
+ ),
+ position: 'Senior Technical Specialist',
+ who: 'Lorant Vajda',
+ avatar: 'avatar/lorant-vajda.jpg',
+ linkGithub: 'https://github.com/lortschi',
+ linkLinkedin: 'https://www.linkedin.com/in/lorant-vajda-596372181/',
+ },
+
+ {
+ company: 'Simple Commerce',
+ description: (
+ <>
+ We were looking for a forms library that was compatible with React and
+ would help us build forms quicker with built-in logic without having to
+ create it from scratch. And the addition of a GraphQL schema bridge was
+ a no-brainer for us. We added a custom method to help validate required
+ fields based on our GraphQL schema which made it even more practical and
+ quick to use with the built-in features and extensibility. The support
+ for the library is also great, I have never had issues asking questions
+ and getting answers to point me in the right direction.
+ >
+ ),
+ position: 'Software Developer',
+ who: 'Kheang Hok Chin',
+ avatar: 'avatar/kheang-hok-chin.jpg',
+ linkGithub: 'https://github.com/simplecommerce',
+ linkLinkedin: 'https://www.linkedin.com/in/khokchin',
+ },
+ {
+ company: 'Okra',
+ description: (
+ <>
+ I used uniforms extensively during the height of Meteor.js as a
+ replacement for the dominant forms solution which hadn't caught up
+ with React. The flexibility to leverage different schema solutions
+ coupled with a thoughtfully architected API with a very clean source
+ implementation quickly made it my go-to solution for multiple
+ forms-heavy applications that probably shaved off significant
+ development time and directly boosted my productivity.
+ >
+ ),
+ position: 'Head of Engineering',
+ who: 'Serkan Durusoy',
+ avatar: 'avatar/serkan-durusoy.jpg',
+ linkGithub: 'https://github.com/serkandurusoy',
+ linkLinkedin: 'https://www.linkedin.com/in/serkandurusoy/',
+ },
+
+ {
+ company: 'Toptal',
+ description: (
+ <>
+ uniforms is my go-to solution for quite a while. Great holistic approach
+ to tackle forms. I especially love the approach to making custom form
+ layouts. Developer experience par-excellence.
+ >
+ ),
+ position: 'Front-end Platform Architect',
+ who: 'Viktor Bezdek',
+ avatar: 'avatar/viktor-bezdek.jpg',
+ linkGithub: 'https://github.com/viktorbezdek',
+ linkLinkedin: 'https://www.linkedin.com/in/viktorbezdek/',
+ },
+ {
+ company: 'MongoDB',
+ description: (
+ <>
+ Vazco's uniforms is one and the only library that allows you to
+ have greater flexibility on top of the React platform to building forms
+ you like.
+ >
+ ),
+ position: 'Software Engineer',
+ who: 'Wojciech Trocki',
+ avatar: 'avatar/wojciech-trocki.jpg',
+ linkGithub: 'https://github.com/wtrocki',
+ linkLinkedin: 'https://www.linkedin.com/in/wojciech-t-39574526/',
+ },
+];
+
+export type TestimonialProps = {
+ avatar: string;
+ company: string;
+ description: JSX.Element;
+ linkGithub: string;
+ linkLinkedin: string;
+ position: string;
+ who: string;
+};
+
+export function Testimonial({
+ testimonial,
+ mirror,
+}: {
+ testimonial: TestimonialProps;
+ mirror: boolean;
+}) {
+ const {
+ avatar,
+ company,
+ description,
+ linkGithub,
+ linkLinkedin,
+ position,
+ who,
+ } = testimonial;
+
+ return (
+
+
+
+
+ {who}
+
+ {position} at {company}
+
+
+
+
+
+
+
+ {description}
+
+
+
+ );
+}
+
+export function Testimonials() {
+ const [slide, setSlide] = useState(0);
+
+ const setNextSlide = () => {
+ setSlide(prevSlide =>
+ prevSlide === testimonials.length - 2 ? 0 : prevSlide + 1,
+ );
+ };
+
+ const setPrevSlide = () => {
+ setSlide(prevSlide =>
+ prevSlide === 0 ? testimonials.length - 2 : prevSlide - 1,
+ );
+ };
+
+ return (
+
+
+ They speak about us
+
+
Testimonials
+
+
+
+
+
+
+
+ {testimonials.map((testimonial, testimonialIdx) => (
+
+ ))}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/website/src/pages-parts/LandingPage/WhoUses.tsx b/website/src/pages-parts/LandingPage/WhoUses.tsx
new file mode 100644
index 000000000..8c291c1aa
--- /dev/null
+++ b/website/src/pages-parts/LandingPage/WhoUses.tsx
@@ -0,0 +1,69 @@
+import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
+import classNames from 'classnames';
+import React from 'react';
+
+import { Button } from '../../components/Button';
+import { Heading } from '../../components/Heading';
+import { Subtitle } from '../../components/Subtitle';
+import styles from '../../css/index.module.css';
+
+export function WhoUses() {
+ const context = useDocusaurusContext();
+ const { companies, email } = context.siteConfig.customFields;
+
+ return (
+
+
+
+
+
+
+
+ {/* FIXME: @docusaurus/* types. */}
+ {(companies as any[]).map(({ image, url, alt }) => (
+
+
+
+ ))}
+
+
+
+
References
+
+ Who uses uniforms
+
+
+ Our package has been used in several projects worldwide. From
+ small to the corporate business solutions and well-known
+ enterprises. Companies trusted us in the development of both
+ simple forms and sophisticated management systems.
+
+
+ Add your company
+
+
+
+
+
+
+ );
+}
diff --git a/website/src/pages-parts/LandingPage/WhyUs.tsx b/website/src/pages-parts/LandingPage/WhyUs.tsx
new file mode 100644
index 000000000..94efcb89f
--- /dev/null
+++ b/website/src/pages-parts/LandingPage/WhyUs.tsx
@@ -0,0 +1,103 @@
+import classNames from 'classnames';
+import React from 'react';
+
+import { Heading } from '../../components/Heading';
+import { Oval } from '../../components/Oval';
+import { Subtitle } from '../../components/Subtitle';
+import styles from '../../css/index.module.css';
+
+export function WhyUs() {
+ return (
+
+
+
+
+
+
Why choose us
+
+ Easy and ready
+
+ to implement solution
+
+
+ A set of open-source libraries capable of instantly generating
+ any given form in React.
+
+
+ Carried out by the global community of 50+ developers.
+
+
+ Focused on providing a ready-to-implement solution, and
+ effortless development experience. Keeps your code simple.
+
+
+
+
+
+
+
+
+
+
+ Integrations with various schemas:
+
+
+ JSON Schema
+ GraphQL
+ SimpleSchema
+ SimpleSchema2
+ Zod
+ And any other - only a small wrapper is needed!
+
+
+
+
+
+
+
+ Wide range of themes:
+
+
+ AntD theme
+ Bootstrap4
+ Bootstrap5
+ MUI
+ Semantic UI
+ Plain HTML
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/website/src/pages/index.tsx b/website/src/pages/index.tsx
index 169b25c7c..ff74e9277 100644
--- a/website/src/pages/index.tsx
+++ b/website/src/pages/index.tsx
@@ -1,5 +1,6 @@
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import Layout from '@theme/Layout';
+import { LandingPage } from '../pages-parts/LandingPage/LandingPage';
export default function Home(): JSX.Element {
const { siteConfig } = useDocusaurusContext();
@@ -7,6 +8,8 @@ export default function Home(): JSX.Element {
+ >
+
+
);
}
diff --git a/website/static/assets/companies/aerogear.png b/website/static/assets/companies/aerogear.png
new file mode 100644
index 000000000..2df726d3d
Binary files /dev/null and b/website/static/assets/companies/aerogear.png differ
diff --git a/website/static/assets/companies/boulder.svg b/website/static/assets/companies/boulder.svg
new file mode 100644
index 000000000..9d5e13397
--- /dev/null
+++ b/website/static/assets/companies/boulder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/static/assets/companies/cleverbeagle.png b/website/static/assets/companies/cleverbeagle.png
new file mode 100644
index 000000000..7f19166c9
Binary files /dev/null and b/website/static/assets/companies/cleverbeagle.png differ
diff --git a/website/static/assets/companies/deskpro.png b/website/static/assets/companies/deskpro.png
new file mode 100644
index 000000000..2715612d0
Binary files /dev/null and b/website/static/assets/companies/deskpro.png differ
diff --git a/website/static/assets/companies/graphback.png b/website/static/assets/companies/graphback.png
new file mode 100644
index 000000000..193cc473b
Binary files /dev/null and b/website/static/assets/companies/graphback.png differ
diff --git a/website/static/assets/companies/nokia.png b/website/static/assets/companies/nokia.png
new file mode 100644
index 000000000..1b9de7821
Binary files /dev/null and b/website/static/assets/companies/nokia.png differ
diff --git a/website/static/assets/companies/onyx-one.png b/website/static/assets/companies/onyx-one.png
new file mode 100644
index 000000000..355234dfb
Binary files /dev/null and b/website/static/assets/companies/onyx-one.png differ
diff --git a/website/static/assets/companies/orionjs.png b/website/static/assets/companies/orionjs.png
new file mode 100644
index 000000000..d3380d637
Binary files /dev/null and b/website/static/assets/companies/orionjs.png differ
diff --git a/website/static/assets/companies/react-page.png b/website/static/assets/companies/react-page.png
new file mode 100644
index 000000000..366520465
Binary files /dev/null and b/website/static/assets/companies/react-page.png differ
diff --git a/website/static/assets/themes/antd.png b/website/static/assets/themes/antd.png
new file mode 100644
index 000000000..004cb9940
Binary files /dev/null and b/website/static/assets/themes/antd.png differ
diff --git a/website/static/assets/themes/bootstrap.svg b/website/static/assets/themes/bootstrap.svg
new file mode 100644
index 000000000..91ded9e3e
--- /dev/null
+++ b/website/static/assets/themes/bootstrap.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/website/static/assets/themes/mui.png b/website/static/assets/themes/mui.png
new file mode 100644
index 000000000..56526f097
Binary files /dev/null and b/website/static/assets/themes/mui.png differ
diff --git a/website/static/assets/themes/semantic.svg b/website/static/assets/themes/semantic.svg
new file mode 100644
index 000000000..de73ced34
--- /dev/null
+++ b/website/static/assets/themes/semantic.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/website/static/avatar/florian-bienefelt.jpg b/website/static/avatar/florian-bienefelt.jpg
new file mode 100644
index 000000000..14ec01748
Binary files /dev/null and b/website/static/avatar/florian-bienefelt.jpg differ
diff --git a/website/static/avatar/kheang-hok-chin.jpg b/website/static/avatar/kheang-hok-chin.jpg
new file mode 100644
index 000000000..4d4e2f1b4
Binary files /dev/null and b/website/static/avatar/kheang-hok-chin.jpg differ
diff --git a/website/static/avatar/lorant-vajda.jpg b/website/static/avatar/lorant-vajda.jpg
new file mode 100644
index 000000000..db41f8160
Binary files /dev/null and b/website/static/avatar/lorant-vajda.jpg differ
diff --git a/website/static/avatar/serkan-durusoy.jpg b/website/static/avatar/serkan-durusoy.jpg
new file mode 100644
index 000000000..32677002e
Binary files /dev/null and b/website/static/avatar/serkan-durusoy.jpg differ
diff --git a/website/static/avatar/viktor-bezdek.jpg b/website/static/avatar/viktor-bezdek.jpg
new file mode 100644
index 000000000..5036eb4e0
Binary files /dev/null and b/website/static/avatar/viktor-bezdek.jpg differ
diff --git a/website/static/avatar/wojciech-trocki.jpg b/website/static/avatar/wojciech-trocki.jpg
new file mode 100644
index 000000000..6fec622e1
Binary files /dev/null and b/website/static/avatar/wojciech-trocki.jpg differ
diff --git a/website/static/img/arrow-left.svg b/website/static/img/arrow-left.svg
new file mode 100644
index 000000000..d7b68f076
--- /dev/null
+++ b/website/static/img/arrow-left.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/static/img/arrow-right.svg b/website/static/img/arrow-right.svg
new file mode 100644
index 000000000..ff4db8b18
--- /dev/null
+++ b/website/static/img/arrow-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/static/img/border-01.svg b/website/static/img/border-01.svg
new file mode 100644
index 000000000..6cfc42e3b
--- /dev/null
+++ b/website/static/img/border-01.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/website/static/img/border-02.svg b/website/static/img/border-02.svg
new file mode 100644
index 000000000..b38bc6145
--- /dev/null
+++ b/website/static/img/border-02.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/website/static/img/border-03.svg b/website/static/img/border-03.svg
new file mode 100644
index 000000000..cd9f5527a
--- /dev/null
+++ b/website/static/img/border-03.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/website/static/img/forminer-screenshot.png b/website/static/img/forminer-screenshot.png
new file mode 100644
index 000000000..1f687ca2a
Binary files /dev/null and b/website/static/img/forminer-screenshot.png differ
diff --git a/website/static/img/icon-01.svg b/website/static/img/icon-01.svg
new file mode 100644
index 000000000..845ec0525
--- /dev/null
+++ b/website/static/img/icon-01.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/website/static/img/icon-02.svg b/website/static/img/icon-02.svg
new file mode 100644
index 000000000..d302f683d
--- /dev/null
+++ b/website/static/img/icon-02.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/website/static/img/vazco.svg b/website/static/img/vazco.svg
new file mode 100644
index 000000000..aa5d98223
--- /dev/null
+++ b/website/static/img/vazco.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/types/docusaurus.d.ts b/website/types/docusaurus.d.ts
new file mode 100644
index 000000000..00b1685b2
--- /dev/null
+++ b/website/types/docusaurus.d.ts
@@ -0,0 +1,20 @@
+declare module '@docusaurus/*' {
+ const something: any;
+ export = something;
+}
+
+declare module '@theme/*' {
+ const something: any;
+ export = something;
+}
+
+declare module '*.md' {
+ import { ComponentType } from 'react';
+ const Component: ComponentType>;
+ export = Component;
+}
+
+declare module '*.module.css' {
+ const classes: Record;
+ export = classes;
+}
diff --git a/website/types/message-box.d.ts b/website/types/message-box.d.ts
new file mode 100644
index 000000000..aafc635ec
--- /dev/null
+++ b/website/types/message-box.d.ts
@@ -0,0 +1,3 @@
+declare module 'message-box' {
+ export function defaults(messages: unknown): void;
+}
diff --git a/website/types/simpl-schema.d.ts b/website/types/simpl-schema.d.ts
new file mode 100644
index 000000000..790c7ce20
--- /dev/null
+++ b/website/types/simpl-schema.d.ts
@@ -0,0 +1,8 @@
+import { ComponentType } from 'react';
+import { GuaranteedProps, UnknownObject } from 'uniforms';
+
+declare module 'simpl-schema' {
+ export interface SchemaDefinition {
+ uniforms?: ComponentType> | UnknownObject | string;
+ }
+}