From e06fddf29e236b112815bcd94043fcfad34cd555 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Fri, 24 Nov 2023 10:28:20 +0000 Subject: [PATCH] Bootstrap (#5) --- .eslintrc.cjs | 2 +- package-lock.json | 228 +++++++++++++++++- packages/fastui-bootstrap/package.json | 1 + packages/fastui-bootstrap/src/index.tsx | 85 ++++++- packages/fastui-bootstrap/src/modal.tsx | 25 ++ packages/fastui-bootstrap/src/navbar.tsx | 43 ++++ packages/fastui/src/components/FormField.tsx | 101 +++++--- packages/fastui/src/components/Json.tsx | 3 + packages/fastui/src/components/LinkList.tsx | 18 ++ packages/fastui/src/components/PageTitle.tsx | 16 ++ packages/fastui/src/components/ServerLoad.tsx | 8 +- packages/fastui/src/components/button.tsx | 10 +- packages/fastui/src/components/display.tsx | 2 +- packages/fastui/src/components/div.tsx | 37 +-- packages/fastui/src/components/form.tsx | 24 +- packages/fastui/src/components/heading.tsx | 8 +- packages/fastui/src/components/index.tsx | 89 +++++-- packages/fastui/src/components/link.tsx | 20 +- packages/fastui/src/components/modal.css | 49 ---- packages/fastui/src/components/modal.tsx | 41 ++-- packages/fastui/src/components/navbar.tsx | 45 ++++ packages/fastui/src/components/paragraph.tsx | 15 ++ packages/fastui/src/components/table.tsx | 12 +- packages/fastui/src/hooks/className.ts | 52 +++- .../fastui/src/hooks/{event.ts => events.ts} | 13 +- packages/fastui/src/hooks/locationContext.tsx | 22 ++ packages/fastui/src/index.tsx | 12 +- packages/vanilla/index.html | 2 +- .../src/main.scss | 0 packages/vanilla/src/main.tsx | 2 +- python/demo/main.py | 179 +++++++++----- python/fastui/__init__.py | 2 +- python/fastui/components/__init__.py | 99 +++++--- python/fastui/components/extra.py | 2 +- python/fastui/components/forms.py | 4 +- python/fastui/components/tables.py | 2 +- python/fastui/events.py | 6 +- python/fastui/forms.py | 2 +- python/fastui/json_schema.py | 5 + 39 files changed, 953 insertions(+), 333 deletions(-) create mode 100644 packages/fastui-bootstrap/src/modal.tsx create mode 100644 packages/fastui-bootstrap/src/navbar.tsx create mode 100644 packages/fastui/src/components/LinkList.tsx create mode 100644 packages/fastui/src/components/PageTitle.tsx delete mode 100644 packages/fastui/src/components/modal.css create mode 100644 packages/fastui/src/components/navbar.tsx create mode 100644 packages/fastui/src/components/paragraph.tsx rename packages/fastui/src/hooks/{event.ts => events.ts} (81%) rename packages/{fastui-bootstrap => vanilla}/src/main.scss (100%) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 7e8c5a4..a5d6722 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -12,7 +12,7 @@ module.exports = { parser: '@typescript-eslint/parser', plugins: ['react', '@typescript-eslint', 'react-refresh', 'simple-import-sort'], rules: { - 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], + 'react-refresh/only-export-components': 'off', // how much effect does this have? '@typescript-eslint/no-explicit-any': 'off', 'no-use-before-define': 'off', 'import/order': [ diff --git a/package-lock.json b/package-lock.json index 4db3285..033e0a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,17 @@ "node": ">=0.10.0" } }, + "node_modules/@babel/runtime": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.4.tgz", + "integrity": "sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@esbuild/android-arm": { "version": "0.19.5", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz", @@ -513,12 +524,64 @@ "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" } }, + "node_modules/@react-aria/ssr": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.0.tgz", + "integrity": "sha512-Bz6BqP6ZorCme9tSWHZVmmY+s7AU8l6Vl2NUYmBzezD//fVHHfFo4lFBn5tBuAaJEm3AuCLaJQ6H2qhxNSb7zg==", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, + "node_modules/@restart/hooks": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.11.tgz", + "integrity": "sha512-Ft/ncTULZN6ldGHiF/k5qt72O8JyRMOeg0tApvCni8LkoiEahO+z3TNxfXIVGy890YtWVDvJAl662dVJSJXvMw==", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.6.tgz", + "integrity": "sha512-eC3puKuWE1SRYbojWHXnvCNHGgf3uzHCb6JOhnF4OXPibOIPEkR1sqDSkL643ydigxwh+ruCa1CmYHlzk7ikKA==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@popperjs/core": "^2.11.6", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.4.9", + "@types/warning": "^3.0.0", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "peerDependencies": { + "react": ">=16.14.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.5.0.tgz", @@ -867,6 +930,14 @@ "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==" }, + "node_modules/@swc/helpers": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz", + "integrity": "sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@swc/types": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", @@ -897,14 +968,12 @@ "node_modules/@types/prop-types": { "version": "15.7.10", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.10.tgz", - "integrity": "sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A==", - "dev": true + "integrity": "sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A==" }, "node_modules/@types/react": { "version": "18.2.37", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.37.tgz", "integrity": "sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw==", - "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -920,11 +989,18 @@ "@types/react": "*" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.9.tgz", + "integrity": "sha512-ZVNmWumUIh5NhH8aMD9CR2hdW0fNuYInlocZHaZ+dgk/1K49j1w/HoAuK1ki+pgscQrOFRTlXeoURtuzEkV3dg==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.6", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.6.tgz", - "integrity": "sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA==", - "dev": true + "integrity": "sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA==" }, "node_modules/@types/semver": { "version": "7.5.5", @@ -932,6 +1008,11 @@ "integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==", "dev": true }, + "node_modules/@types/warning": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", + "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.11.0.tgz", @@ -1521,6 +1602,11 @@ "node": ">= 6" } }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1562,8 +1648,7 @@ "node_modules/csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "dev": true + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "node_modules/debug": { "version": "4.3.4", @@ -1619,6 +1704,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1643,6 +1736,15 @@ "node": ">=6.0.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/es-abstract": { "version": "1.22.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", @@ -2744,6 +2846,14 @@ "node": ">= 0.4" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -3321,7 +3431,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3617,13 +3726,24 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3664,6 +3784,35 @@ "node": ">=0.10.0" } }, + "node_modules/react-bootstrap": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.9.1.tgz", + "integrity": "sha512-ezgmh/ARCYp18LbZEqPp0ppvy+ytCmycDORqc8vXSKYV3cer4VH7OReV8uMOoKXmYzivJTxgzGHalGrHamryHA==", + "dependencies": { + "@babel/runtime": "^7.22.5", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.6.6", + "@types/react-transition-group": "^4.4.6", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -3679,8 +3828,27 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } }, "node_modules/readdirp": { "version": "3.6.0", @@ -3713,6 +3881,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", @@ -4160,6 +4333,11 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4277,6 +4455,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -4351,6 +4543,14 @@ } } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4478,8 +4678,12 @@ "dependencies": { "bootstrap": "^5.3.2", "react": "^18.2.0", + "react-bootstrap": "^2.9.1", "react-dom": "^18.2.0", "sass": "^1.69.5" + }, + "peerDependencies": { + "fastui": "0.0.0" } }, "packages/vanilla": { diff --git a/packages/fastui-bootstrap/package.json b/packages/fastui-bootstrap/package.json index e94afbe..66ff159 100644 --- a/packages/fastui-bootstrap/package.json +++ b/packages/fastui-bootstrap/package.json @@ -9,6 +9,7 @@ "dependencies": { "bootstrap": "^5.3.2", "react": "^18.2.0", + "react-bootstrap": "^2.9.1", "react-dom": "^18.2.0", "sass": "^1.69.5" }, diff --git a/packages/fastui-bootstrap/src/index.tsx b/packages/fastui-bootstrap/src/index.tsx index 6f13c97..8584454 100644 --- a/packages/fastui-bootstrap/src/index.tsx +++ b/packages/fastui-bootstrap/src/index.tsx @@ -1,27 +1,90 @@ -import { ClassNameGenerator, CustomRender } from 'fastui' +import { pathMatch } from 'fastui' + +import type { components, ClassNameGenerator, CustomRender, ClassName } from 'fastui' + +import { Modal } from './modal' +import { Navbar } from './navbar' export const customRender: CustomRender = (props) => { const { type } = props - if (type === 'DisplayPrimitive') { - const { value } = props - if (typeof value === 'boolean') { - return () => <>{value ? '👍' : '👎'} - } + switch (type) { + case 'DisplayPrimitive': + return displayPrimitiveRender(props) + case 'Navbar': + return () => + case 'Modal': + return () => } } -export const classNameGenerator: ClassNameGenerator = (props) => { +function displayPrimitiveRender(props: components.DisplayPrimitiveProps) { + const { value } = props + if (typeof value === 'boolean') { + return () => <>{value ? '👍' : '👎'} + } +} + +export const classNameGenerator: ClassNameGenerator = ({ props, fullPath, subElement }) => { const { type } = props switch (type) { case 'Page': return 'container py-4' - case 'Row': - return 'row' - case 'Col': - return 'col' case 'Button': return 'btn btn-primary' case 'Table': return 'table table-striped' + case 'Form': + case 'ModelForm': + return formClassName(subElement) + case 'FormFieldInput': + case 'FormFieldCheckbox': + case 'FormFieldSelect': + case 'FormFieldFile': + return formFieldClassName(props, subElement) + case 'Navbar': + return navbarClassName(subElement) + case 'Link': + return linkClassName(props, fullPath) } } + +function formFieldClassName(props: components.FormFieldProps, subElement?: string): ClassName { + switch (subElement) { + case 'input': + return props.error ? 'is-invalid form-control' : 'form-control' + case 'select': + return 'form-select' + case 'label': + return { 'form-label': true, 'fw-bold': props.required } + case 'error': + return 'invalid-feedback' + case 'description': + return 'form-text' + default: + return 'mb-3' + } +} + +function formClassName(subElement?: string): ClassName { + switch (subElement) { + case 'form-container': + return 'row justify-content-center' + default: + return 'col-md-4' + } +} + +function navbarClassName(subElement?: string): ClassName { + switch (subElement) { + case 'contents': + return 'container' + case 'title': + return 'navbar-brand' + default: + return 'navbar navbar-expand-lg bg-body-tertiary' + } +} + +function linkClassName(props: components.LinkProps, fullPath: string): ClassName { + return { active: pathMatch(props.active, fullPath), 'nav-link': props.mode === 'navbar' } +} diff --git a/packages/fastui-bootstrap/src/modal.tsx b/packages/fastui-bootstrap/src/modal.tsx new file mode 100644 index 0000000..79f13a1 --- /dev/null +++ b/packages/fastui-bootstrap/src/modal.tsx @@ -0,0 +1,25 @@ +import { FC } from 'react' +import { components, events, renderClassName } from 'fastui' +import BootstrapModal from 'react-bootstrap/Modal' + +export const Modal: FC = (props) => { + const { className, title, body, footer, openTrigger } = props + + const [open, toggle] = events.useEventListenerToggle(openTrigger, props.open) + + return ( + + + {title} + + + + + {footer && ( + + + + )} + + ) +} diff --git a/packages/fastui-bootstrap/src/navbar.tsx b/packages/fastui-bootstrap/src/navbar.tsx new file mode 100644 index 0000000..30a48b7 --- /dev/null +++ b/packages/fastui-bootstrap/src/navbar.tsx @@ -0,0 +1,43 @@ +import { FC } from 'react' +import { components, useClassName } from 'fastui' +import BootstrapNavbar from 'react-bootstrap/Navbar' + +export const Navbar: FC = (props) => { + const links = props.links.map((link) => { + link.mode = link.mode || 'navbar' + return link + }) + return ( + +
+ + + +
    + {links.map((link, i) => ( +
  • + +
  • + ))} +
+
+
+
+ ) +} + +const NavbarTitle = (props: components.NavbarProps) => { + const { title, titleEvent } = props + const className = useClassName(props, { el: 'title' }) + if (title) { + if (titleEvent) { + return ( + + {title} + + ) + } else { + return {title} + } + } +} diff --git a/packages/fastui/src/components/FormField.tsx b/packages/fastui/src/components/FormField.tsx index f21e33f..94c9eeb 100644 --- a/packages/fastui/src/components/FormField.tsx +++ b/packages/fastui/src/components/FormField.tsx @@ -1,6 +1,6 @@ import { FC, useState } from 'react' -import { ClassName, useClassNameGenerator } from '../hooks/className' +import { ClassName, useClassName } from '../hooks/className' interface BaseFormFieldProps { name: string @@ -8,6 +8,7 @@ interface BaseFormFieldProps { required: boolean locked: boolean error?: string + description?: string className?: ClassName } @@ -15,29 +16,31 @@ export type FormFieldProps = FormFieldInputProps | FormFieldCheckboxProps | Form interface FormFieldInputProps extends BaseFormFieldProps { type: 'FormFieldInput' - htmlType?: 'text' | 'date' | 'datetime-local' | 'time' | 'email' | 'url' | 'file' | 'number' + htmlType?: 'text' | 'date' | 'datetime-local' | 'time' | 'email' | 'url' | 'number' | 'password' initial?: string | number + placeholder?: string } export const FormFieldInputComp: FC = (props) => { - const { className, name, title, required, htmlType, locked } = props + const { name, placeholder, required, htmlType, locked } = props const [value, setValue] = useState(props.initial ?? '') - // TODO placeholder return ( -
-