diff --git a/README.md b/README.md index 0ac6207f2..a32db5dd8 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,6 @@ npm install @remoteoss/remote-flows ```tsx import { RemoteFlows, CostCalculator } from '@remoteoss/remote-flows'; -import { defaultComponents } from '@remoteoss/remote-flows/default-components'; import '@remoteoss/remote-flows/styles.css'; function App() { diff --git a/docs/COMPONENT_CUSTOMIZATION.md b/docs/COMPONENT_CUSTOMIZATION.md index d126459fd..f50a28e73 100644 --- a/docs/COMPONENT_CUSTOMIZATION.md +++ b/docs/COMPONENT_CUSTOMIZATION.md @@ -86,7 +86,7 @@ function App() { ### Supported Field Types -For a complete list of all component types you can override, see the [default components registry](../src/default-components.ts). This file shows all the default implementations used internally by Remote Flows and serves as a reference for building custom components. +For a complete list of all component types you can override. This file shows all the default implementations used internally by Remote Flows and serves as a reference for building custom components. Available component types include: @@ -121,8 +121,9 @@ and their typescript definitions - `DatePickerComponentProps`: for datepicker components - `WorkScheduleComponentProps`: for workschedule components - `PDFPreviewComponentProps`: for the pdf viewer component +- `TableComponentProps`: for the table component -> **Tip:** Check [src/default-components.ts](../src/default-components.ts) to see the default implementations. You can use these as a starting point or reference when building your own custom components. +> **Tip:** Check [example/src/DefaultComponents.ts](../example/src/DefaultComponents.tsx) to see the default implementations. You can use these as a starting point or reference when building your own custom components. ### Important Notes diff --git a/example/package-lock.json b/example/package-lock.json index 7096cef0f..87e275e12 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -9,16 +9,30 @@ "version": "0.0.0", "hasInstallScript": true, "dependencies": { + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-collapsible": "^1.1.12", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-popover": "^1.1.15", + "@radix-ui/react-radio-group": "^1.3.8", + "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-tabs": "^1.1.13", "@remoteoss/remote-flows": "file://..", "axios": "^1.8.3", + "class-variance-authority": "^0.7.1", + "cmdk": "^1.1.1", + "date-fns": "^3.6.0", "dotenv": "^17.0.0", "express": "^4.21.2", "jsonwebtoken": "^9.0.2", + "lucide-react": "0.477.0", "react": "^18.3.1", + "react-day-picker": "^8.10.1", "react-dom": "^18.3.1", "react-flagpack": "^2.0.6", "react-syntax-highlighter": "^15.6.1", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "vaul": "^1.1.2" }, "devDependencies": { "@eslint/js": "^9.21.0", @@ -37,23 +51,14 @@ }, "..": { "name": "@remoteoss/remote-flows", - "version": "1.17.0", + "version": "1.18.0", "dependencies": { "@hookform/resolvers": "^4.1.3", - "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-checkbox": "^1.3.3", - "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-dropdown-menu": "^2.1.16", - "@radix-ui/react-label": "^2.1.8", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-radio-group": "^1.3.8", - "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-select": "^2.2.6", - "@radix-ui/react-separator": "^1.1.8", - "@radix-ui/react-slot": "^1.2.4", - "@radix-ui/react-tabs": "^1.1.13", - "@radix-ui/react-tooltip": "^1.2.8", "@remoteoss/remote-json-schema-form-kit": "github:remoteoss/remote-json-schema-form-kit#v0.0.5", "@tailwindcss/cli": "^4.1.17", "@tailwindcss/postcss": "^4.1.17", @@ -666,102 +671,917 @@ "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==", "dev": true, "dependencies": { - "@eslint/core": "^0.15.1", - "levn": "^0.4.1" + "@eslint/core": "^0.15.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", + "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.6" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT" + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@playwright/test": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", + "integrity": "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.52.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", + "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", + "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", + "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz", + "integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz", + "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", + "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", - "dev": true, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.15" + "@radix-ui/react-use-layout-effect": "1.1.1" }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "engines": { - "node": ">=18.18.0" + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "engines": { - "node": ">=18.18.0" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "engines": { - "node": ">=18.18" + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "engines": { - "node": ">=18.18" + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@playwright/test": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", - "integrity": "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "license": "MIT", "dependencies": { - "playwright": "1.52.0" + "@radix-ui/react-primitive": "2.1.3" }, - "bin": { - "playwright": "cli.js" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "engines": { - "node": ">=18" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, "node_modules/@remoteoss/remote-flows": { "resolved": "..", "link": true @@ -1278,7 +2098,7 @@ "version": "19.1.8", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", - "dev": true, + "devOptional": true, "dependencies": { "csstype": "^3.0.2" } @@ -1287,7 +2107,7 @@ "version": "19.1.6", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", - "dev": true, + "devOptional": true, "peerDependencies": { "@types/react": "^19.0.0" } @@ -1402,6 +2222,18 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -1627,6 +2459,43 @@ "node": ">= 6" } }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1721,7 +2590,17 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "devOptional": true + }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } }, "node_modules/debug": { "version": "4.4.1", @@ -1771,6 +2650,12 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/dotenv": { "version": "17.0.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.0.1.tgz", @@ -2385,6 +3270,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -3147,6 +4041,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/lucide-react": { + "version": "0.477.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.477.0.tgz", + "integrity": "sha512-yCf7aYxerFZAbd8jHJxjwe1j7jEMPptjnaOqdYeirFnEy85cNR3/L+o0I875CYFYya+eEVzZSbNuRk8BZPDpVw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -3657,6 +4560,20 @@ "node": ">=0.10.0" } }, + "node_modules/react-day-picker": { + "version": "8.10.1", + "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-8.10.1.tgz", + "integrity": "sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/gpbl" + }, + "peerDependencies": { + "date-fns": "^2.28.0 || ^3.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -3696,6 +4613,75 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-syntax-highlighter": { "version": "15.6.1", "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.6.1.tgz", @@ -4122,6 +5108,12 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4195,6 +5187,49 @@ "punycode": "^2.1.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -4211,6 +5246,19 @@ "node": ">= 0.8" } }, + "node_modules/vaul": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", + "integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-dialog": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/vite": { "version": "6.3.5", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", diff --git a/example/package.json b/example/package.json index 3a204d7cc..98a6e76a4 100644 --- a/example/package.json +++ b/example/package.json @@ -13,16 +13,30 @@ "test:e2e:ui": "playwright test --ui" }, "dependencies": { + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-collapsible": "^1.1.12", + "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-tabs": "^1.1.13", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-popover": "^1.1.15", + "@radix-ui/react-radio-group": "^1.3.8", + "@radix-ui/react-select": "^2.2.6", "@remoteoss/remote-flows": "file://..", "axios": "^1.8.3", + "class-variance-authority": "^0.7.1", + "cmdk": "^1.1.1", + "date-fns": "^3.6.0", "dotenv": "^17.0.0", "express": "^4.21.2", "jsonwebtoken": "^9.0.2", + "lucide-react": "0.477.0", "react": "^18.3.1", + "react-day-picker": "^8.10.1", "react-dom": "^18.3.1", "react-flagpack": "^2.0.6", "react-syntax-highlighter": "^15.6.1", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "vaul": "^1.1.2" }, "devDependencies": { "@eslint/js": "^9.21.0", diff --git a/example/src/App.tsx b/example/src/App.tsx index 770ad5048..7a134fc89 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -4,17 +4,15 @@ import { CardDescription, CardHeader, CardTitle, - Tabs, - TabsContent, - TabsList, - TabsTrigger, + cn, +} from '@remoteoss/remote-flows/internals'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from './components/ui/tabs'; +import { Collapsible, CollapsibleContent, CollapsibleTrigger, - Button, - cn, -} from '@remoteoss/remote-flows/internals'; - +} from './components/ui/collapsible'; +import { Button } from '@remoteoss/remote-flows/internals'; import { Check, Copy, ChevronRight, ChevronDown } from 'lucide-react'; import { useState } from 'react'; diff --git a/example/src/BasicCostCalculator.tsx b/example/src/BasicCostCalculator.tsx index f1474b633..f9c0a0a13 100644 --- a/example/src/BasicCostCalculator.tsx +++ b/example/src/BasicCostCalculator.tsx @@ -6,6 +6,7 @@ import { } from '@remoteoss/remote-flows'; import { RemoteFlows } from './RemoteFlows'; import './css/main.css'; +import { defaultComponents } from './DefaultComponents'; const estimationOptions = { title: 'Estimate for a new company', @@ -20,7 +21,7 @@ export function BasicCostCalculator() { }; return ( - + { diff --git a/example/src/BasicCostCalculatorDefaultValues.tsx b/example/src/BasicCostCalculatorDefaultValues.tsx index dae96bbf3..832de1080 100644 --- a/example/src/BasicCostCalculatorDefaultValues.tsx +++ b/example/src/BasicCostCalculatorDefaultValues.tsx @@ -5,6 +5,7 @@ import { CostCalculatorResetButton, } from '@remoteoss/remote-flows'; import { RemoteFlows } from './RemoteFlows'; +import { defaultComponents } from './DefaultComponents'; import './css/main.css'; const estimationOptions = { @@ -15,7 +16,7 @@ const estimationOptions = { export function BasicCostCalculatorWithDefaultValues() { return ( - + + { diff --git a/example/src/ContractAmendment.tsx b/example/src/ContractAmendment.tsx index d0bf0dd6a..e8270cc9e 100644 --- a/example/src/ContractAmendment.tsx +++ b/example/src/ContractAmendment.tsx @@ -5,6 +5,7 @@ import { } from '@remoteoss/remote-flows'; import { useState } from 'react'; import { RemoteFlows } from './RemoteFlows'; +import { defaultComponents } from './DefaultComponents'; import './css/main.css'; function AmendmentFlow({ @@ -113,7 +114,10 @@ function AmendmentFlow({ export function ContractAmendment() { const EMPLOYMENT_ID = import.meta.env.VITE_CONTRACT_AMENDMENT_EMPLOYMENT_ID; // set another employment ID here as it will probably fail for you return ( - +
diff --git a/example/src/Components.tsx b/example/src/CostCalculatorComponents.tsx similarity index 98% rename from example/src/Components.tsx rename to example/src/CostCalculatorComponents.tsx index b087ae4e0..d5f8cdd00 100644 --- a/example/src/Components.tsx +++ b/example/src/CostCalculatorComponents.tsx @@ -6,7 +6,8 @@ import type { FileComponentProps, PDFPreviewComponentProps, } from '@remoteoss/remote-flows'; -import { FileUploader } from '@remoteoss/remote-flows/internals'; +import { FileUploader } from './components/ui/file-uploader'; +import { defaultComponents } from '@remoteoss/remote-flows/internals'; //import { ZendeskDialog } from './ZendeskDialog'; // you can define HTML button attributes or event props that exist in your Button like variant, size, etc. @@ -333,6 +334,7 @@ const PDFPreview = ({ base64Data }: PDFPreviewComponentProps) => { }; export const components: Components = { + ...defaultComponents, button: Button, text: Input, select: Select, diff --git a/example/src/CostCalculatorWithExportPdf.tsx b/example/src/CostCalculatorWithExportPdf.tsx index 6e7d442fd..c3df8a376 100644 --- a/example/src/CostCalculatorWithExportPdf.tsx +++ b/example/src/CostCalculatorWithExportPdf.tsx @@ -14,6 +14,8 @@ import './css/main.css'; import { useState } from 'react'; import { RemoteFlows } from './RemoteFlows'; import { downloadFile } from './utils'; +import { defaultComponents } from './DefaultComponents'; +import './css/main.css'; const estimationOptions = { title: 'Estimate for a new company', @@ -87,7 +89,7 @@ function CostCalculatorFormDemo() { export function CostCalculatorWithExportPdf() { return ( - + ); diff --git a/example/src/CostCalculatorWithPremiumBenefits.tsx b/example/src/CostCalculatorWithPremiumBenefits.tsx index ab4da181b..e97611815 100644 --- a/example/src/CostCalculatorWithPremiumBenefits.tsx +++ b/example/src/CostCalculatorWithPremiumBenefits.tsx @@ -20,18 +20,17 @@ import { convertFromCents, ZendeskTriggerButton, } from '@remoteoss/remote-flows'; +import { Card, cn } from '@remoteoss/remote-flows/internals'; import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger, - Card, - cn, -} from '@remoteoss/remote-flows/internals'; +} from './components/ui/drawer'; import { ButtonHTMLAttributes, useState, isValidElement } from 'react'; import { RemoteFlows } from './RemoteFlows'; -import { components } from './Components'; +import { components } from './CostCalculatorComponents'; import { downloadFile } from './utils'; import { AlertError } from './AlertError'; import 'react-flagpack/dist/style.css'; diff --git a/example/src/CostCalculatorWithReplaceableComponents.tsx b/example/src/CostCalculatorWithReplaceableComponents.tsx index c8c11eba1..5a563bb41 100644 --- a/example/src/CostCalculatorWithReplaceableComponents.tsx +++ b/example/src/CostCalculatorWithReplaceableComponents.tsx @@ -4,7 +4,7 @@ import { CostCalculatorSubmitButton, CostCalculatorResetButton, } from '@remoteoss/remote-flows'; -import { components } from './Components'; +import { components } from './CostCalculatorComponents'; import { RemoteFlows } from './RemoteFlows'; import './css/main.css'; diff --git a/example/src/CostCalculatorWithResults.tsx b/example/src/CostCalculatorWithResults.tsx index 85412fb20..53a8c8307 100644 --- a/example/src/CostCalculatorWithResults.tsx +++ b/example/src/CostCalculatorWithResults.tsx @@ -12,6 +12,7 @@ import type { import Flag from 'react-flagpack'; import { useState } from 'react'; import { RemoteFlows } from './RemoteFlows'; +import { defaultComponents } from './DefaultComponents'; import './css/main.css'; import 'react-flagpack/dist/style.css'; @@ -26,7 +27,7 @@ export function CostCalculatorWithResults() { useState(null); return ( - + { diff --git a/example/src/CreateCompany.tsx b/example/src/CreateCompany.tsx index 8b84da076..4bba5202e 100644 --- a/example/src/CreateCompany.tsx +++ b/example/src/CreateCompany.tsx @@ -9,6 +9,7 @@ import { Card } from '@remoteoss/remote-flows/internals'; import React, { useState, useCallback } from 'react'; import { RemoteFlows } from './RemoteFlows'; import { AlertError } from './AlertError'; +import { defaultComponents } from './DefaultComponents'; import './css/main.css'; import './css/contractor-onboarding.css'; @@ -178,6 +179,7 @@ export const CreateCompanyWithProps = ({ return (
diff --git a/example/src/DefaultComponents.tsx b/example/src/DefaultComponents.tsx new file mode 100644 index 000000000..8b6c79d82 --- /dev/null +++ b/example/src/DefaultComponents.tsx @@ -0,0 +1,42 @@ +import { Components } from '@remoteoss/remote-flows'; +import { CountryFieldDefault } from './components/defaults/CountryFieldDefault'; +import { MultiSelectFieldDefault } from './components/defaults/MultiSelectFieldDefault'; +import { TextFieldDefault } from './components/defaults/TextFieldDefault'; +import { TextAreaFieldDefault } from './components/defaults/TextAreaFieldDefault'; +import { SelectFieldDefault } from './components/defaults/SelectFieldDefault'; +import { CheckboxFieldDefault } from './components/defaults/CheckboxFieldDefault'; +import { RadioGroupFieldDefault } from './components/defaults/RadioGroupFieldDefault'; +import { DatePickerFieldDefault } from './components/defaults/DatePickerFieldDefault'; +import { ButtonDefault } from './components/defaults/ButtonDefault'; +import { EmailFieldDefault } from './components/defaults/EmailFieldDefault'; +import { NumberFieldDefault } from './components/defaults/NumberFieldDefault'; +import { StatementDefault } from './components/defaults/StatementDefault'; +import { PDFPreviewDefault } from './components/defaults/PDFPreviewDefault'; +import { FileUploadFieldDefault } from './components/defaults/FileUploadFieldDefault'; +import { DrawerDefault } from './components/defaults/DrawerDefault'; +import { TableFieldDefault } from './components/defaults/TableFieldDefault'; +import { ZendeskDrawerDefault } from './components/defaults/ZendeskDrawerDefault'; +import { FieldsetToggleButtonDefault } from './components/defaults/FieldsetToggleButtonDefault'; +import { WorkScheduleFieldDefault } from './components/defaults/WorkScheduleFieldDefault'; + +export const defaultComponents: Components = { + text: TextFieldDefault, + textarea: TextAreaFieldDefault, + select: SelectFieldDefault, + checkbox: CheckboxFieldDefault, + radio: RadioGroupFieldDefault, + date: DatePickerFieldDefault, + button: ButtonDefault, + email: EmailFieldDefault, + number: NumberFieldDefault, + statement: StatementDefault, + countries: CountryFieldDefault, + 'multi-select': MultiSelectFieldDefault, + pdfViewer: PDFPreviewDefault, + file: FileUploadFieldDefault, + drawer: DrawerDefault, + table: TableFieldDefault, + zendeskDrawer: ZendeskDrawerDefault, + fieldsetToggle: FieldsetToggleButtonDefault, + 'work-schedule': WorkScheduleFieldDefault, +}; diff --git a/example/src/OffboardingRequestModal.tsx b/example/src/OffboardingRequestModal.tsx index 114fcbfb6..239752e3e 100644 --- a/example/src/OffboardingRequestModal.tsx +++ b/example/src/OffboardingRequestModal.tsx @@ -1,13 +1,13 @@ import { TerminationDialogInfoContent } from '@remoteoss/remote-flows'; +import { Button } from '@remoteoss/remote-flows/internals'; import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, - Button, - ScrollArea, -} from '@remoteoss/remote-flows/internals'; +} from './components/ui/dialog'; +import { ScrollArea } from './components/ui/scroll-area'; export function OffboardingRequestModal({ employee, diff --git a/example/src/Onboarding.tsx b/example/src/Onboarding.tsx index 8c7b3e96c..3a15e35ac 100644 --- a/example/src/Onboarding.tsx +++ b/example/src/Onboarding.tsx @@ -17,6 +17,7 @@ import { OnboardingAlertStatuses } from './OnboardingAlertStatuses'; import { RemoteFlows } from './RemoteFlows'; import { AlertError } from './AlertError'; import './css/main.css'; +import { defaultComponents } from './DefaultComponents'; export const InviteSection = ({ title, @@ -256,7 +257,7 @@ const OnboardingWithProps = ({ employmentId, externalId, }: OnboardingFormData) => ( - + { return ( - + ( - + { return fetch('/api/fetch-refresh-token') @@ -53,6 +53,7 @@ export const RemoteFlows = ({ children, isClientToken, authType, + components = defaultComponents, ...props }: RemoteFlowsProps) => { const auth = useMemo(() => { @@ -70,8 +71,8 @@ export const RemoteFlows = ({ { const proxyURL = window.location.origin; return ( - + { const { 'data-type': dataType, ...buttonProps } = props; diff --git a/src/components/form/fields/default/CheckboxFieldDefault.tsx b/example/src/components/defaults/CheckboxFieldDefault.tsx similarity index 83% rename from src/components/form/fields/default/CheckboxFieldDefault.tsx rename to example/src/components/defaults/CheckboxFieldDefault.tsx index f463567ef..ecf7755a6 100644 --- a/src/components/form/fields/default/CheckboxFieldDefault.tsx +++ b/example/src/components/defaults/CheckboxFieldDefault.tsx @@ -1,14 +1,13 @@ -import { Checkbox } from '@/src/components/ui/checkbox'; +import { Checkbox } from '../ui/checkbox'; import { FormControl, FormDescription, FormItem, FormLabel, FormMessage, -} from '@/src/components/ui/form'; -import { cn } from '@/src/lib/utils'; -import { FieldComponentProps } from '@/src/types/fields'; -import { CheckedState } from '@radix-ui/react-checkbox'; +} from '../ui/form'; +import { cn } from '@remoteoss/remote-flows/internals'; +import { FieldComponentProps } from '@remoteoss/remote-flows'; export const CheckboxFieldDefault = ({ field, @@ -31,7 +30,7 @@ export const CheckboxFieldDefault = ({
{ + onCheckedChange={(checked: boolean | 'indeterminate') => { field.onChange(checked === true, option.value); }} checked={field.value?.includes(option.value)} @@ -49,7 +48,7 @@ export const CheckboxFieldDefault = ({
{ + onCheckedChange={(checked: boolean | 'indeterminate') => { field.onChange(checked === true, null); }} checked={field.value} diff --git a/src/components/form/fields/default/CountryFieldDefault.tsx b/example/src/components/defaults/CountryFieldDefault.tsx similarity index 70% rename from src/components/form/fields/default/CountryFieldDefault.tsx rename to example/src/components/defaults/CountryFieldDefault.tsx index 5f15d2eb4..f4b1caeec 100644 --- a/src/components/form/fields/default/CountryFieldDefault.tsx +++ b/example/src/components/defaults/CountryFieldDefault.tsx @@ -2,41 +2,39 @@ import { useEffect, useState } from 'react'; import { FormControl, FormDescription, + FormItem, FormLabel, FormMessage, -} from '@/src/components/ui/form'; - -import { FormItem } from '@/src/components/ui/form'; -import { MultiSelect } from '@/src/components/ui/multi-select'; -import { $TSFixMe } from '@/src/types/remoteFlows'; -import { CountryComponentProps } from '@/src/types/fields'; -import { HelpCenter } from '@/src/components/shared/zendesk-drawer/HelpCenter'; +} from '../ui/form'; +import { MultiSelect, type Option } from '../ui/multi-select'; +import { CountryComponentProps } from '@remoteoss/remote-flows'; export function CountryFieldDefault({ field, fieldState, fieldData, }: CountryComponentProps) { - const [selected, setSelected] = useState<$TSFixMe[]>([]); - const handleChange = (rawValues: $TSFixMe[]) => { - const values = rawValues.map(({ value }) => value); - field.onChange(values); - setSelected(rawValues); - }; + const [selected, setSelected] = useState([]); useEffect(() => { if (field.value && fieldData.options) { setSelected( field.value.map( - (value: $TSFixMe) => + (value: string) => fieldData?.options?.find( (option) => option.value === value, - ) as $TSFixMe, + ) as Option, ), ); } }, [field.value, fieldData.options]); + const handleChange = (rawValues: Option[]) => { + const values = rawValues.map(({ value }) => value); + field.onChange(values); + setSelected(rawValues); + }; + const countryOptions = [ ...Object.entries(fieldData.$meta?.regions || {}).map(([key, value]) => ({ value, @@ -57,6 +55,7 @@ export function CountryFieldDefault({ category: 'Countries', })) || []), ]; + return ( {fieldData.description && ( - - {fieldData.description}{' '} - - + {fieldData.description} )} {fieldState.error && } diff --git a/src/components/form/fields/default/DatePickerFieldDefault.tsx b/example/src/components/defaults/DatePickerFieldDefault.tsx similarity index 87% rename from src/components/form/fields/default/DatePickerFieldDefault.tsx rename to example/src/components/defaults/DatePickerFieldDefault.tsx index e9032fe81..e116d0070 100644 --- a/src/components/form/fields/default/DatePickerFieldDefault.tsx +++ b/example/src/components/defaults/DatePickerFieldDefault.tsx @@ -1,12 +1,12 @@ -import { Button } from '@/src/components/ui/button'; +import { Button } from '@remoteoss/remote-flows/internals'; import { FormControl, FormDescription, FormItem, FormLabel, FormMessage, -} from '@/src/components/ui/form'; -import { DatePickerComponentProps } from '@/src/types/fields'; +} from '../ui/form'; +import { DatePickerComponentProps, HelpCenter } from '@remoteoss/remote-flows'; import { CalendarIcon } from 'lucide-react'; import { format } from 'date-fns'; import { @@ -14,10 +14,9 @@ import { PopoverClose, PopoverContent, PopoverTrigger, -} from '@/src/components/ui/popover'; -import { cn } from '@/src/lib/utils'; -import { Calendar } from '@/src/components/ui/calendar'; -import { HelpCenter } from '@/src/components/shared/zendesk-drawer/HelpCenter'; +} from '../ui/popover'; +import { cn } from '@remoteoss/remote-flows/internals'; +import { Calendar } from '../ui/calendar'; export function DatePickerFieldDefault({ field, diff --git a/src/components/shared/drawer/DrawerDefault.tsx b/example/src/components/defaults/DrawerDefault.tsx similarity index 69% rename from src/components/shared/drawer/DrawerDefault.tsx rename to example/src/components/defaults/DrawerDefault.tsx index f736291b3..bd11dd400 100644 --- a/src/components/shared/drawer/DrawerDefault.tsx +++ b/example/src/components/defaults/DrawerDefault.tsx @@ -1,12 +1,12 @@ -import { DrawerComponentProps } from '@/src/types/remoteFlows'; import { - Drawer as DrawerPrimitive, - DrawerTrigger, + Drawer, DrawerContent, DrawerHeader, DrawerTitle, -} from '@/src/components/ui/drawer'; -import { cn } from '@/src/lib/utils'; + DrawerTrigger, +} from '../ui/drawer'; +import { cn } from '@remoteoss/remote-flows/internals'; +import { DrawerComponentProps } from '@remoteoss/remote-flows'; export function DrawerDefault({ open, @@ -18,11 +18,7 @@ export function DrawerDefault({ children, }: DrawerComponentProps) { return ( - + {trigger} {children}
- + ); } diff --git a/example/src/components/defaults/EmailFieldDefault.tsx b/example/src/components/defaults/EmailFieldDefault.tsx new file mode 100644 index 000000000..10953884f --- /dev/null +++ b/example/src/components/defaults/EmailFieldDefault.tsx @@ -0,0 +1,44 @@ +import { FormDescription, FormMessage } from '../ui/form'; +import { FormControl, FormItem, FormLabel } from '../ui/form'; +import { Input } from '../ui/input'; +import { TextFieldComponentProps } from '@remoteoss/remote-flows'; + +export function EmailFieldDefault({ + field, + fieldState, + fieldData, +}: TextFieldComponentProps) { + const { name, label, description, maxLength, includeErrorMessage } = + fieldData; + return ( + + {label && ( + {label} + )} + + ) => { + field.onChange(event); + }} + className='RemoteFlows__TextField__Input' + placeholder={label} + maxLength={maxLength} + /> + + {description && ( + + {description} + + )} + {includeErrorMessage && fieldState.error && ( + + )} + + ); +} diff --git a/example/src/components/defaults/FieldsetToggleButtonDefault.tsx b/example/src/components/defaults/FieldsetToggleButtonDefault.tsx new file mode 100644 index 000000000..6b07b2704 --- /dev/null +++ b/example/src/components/defaults/FieldsetToggleButtonDefault.tsx @@ -0,0 +1,25 @@ +import { Button } from '@remoteoss/remote-flows/internals'; +import { cn } from '@remoteoss/remote-flows/internals'; +import { FieldSetToggleComponentProps } from '@remoteoss/remote-flows'; + +export const FieldsetToggleButtonDefault = ({ + isExpanded, + onToggle, + className, + ...props +}: FieldSetToggleComponentProps) => { + return ( + + ); +}; diff --git a/src/components/form/fields/default/FileUploadFieldDefault.tsx b/example/src/components/defaults/FileUploadFieldDefault.tsx similarity index 63% rename from src/components/form/fields/default/FileUploadFieldDefault.tsx rename to example/src/components/defaults/FileUploadFieldDefault.tsx index 1068fbfa1..1b0000318 100644 --- a/src/components/form/fields/default/FileUploadFieldDefault.tsx +++ b/example/src/components/defaults/FileUploadFieldDefault.tsx @@ -4,10 +4,9 @@ import { FormItem, FormLabel, FormMessage, -} from '@/src/components/ui/form'; -import { FileUploader } from '@/src/components/ui/file-uploader'; -import { cn } from '@/src/lib/utils'; -import { FileComponentProps } from '@/src/types/fields'; +} from '../ui/form'; +import { FileUploader } from '../ui/file-uploader'; +import { FileComponentProps } from '@remoteoss/remote-flows'; export function FileUploadFieldDefault({ field, @@ -26,17 +25,14 @@ export function FileUploadFieldDefault({ {description && ( -
- - {description} - -
+ + {description} + )} {fieldState.error && ( diff --git a/src/components/form/fields/default/MultiSelectFieldDefault.tsx b/example/src/components/defaults/MultiSelectFieldDefault.tsx similarity index 70% rename from src/components/form/fields/default/MultiSelectFieldDefault.tsx rename to example/src/components/defaults/MultiSelectFieldDefault.tsx index d63f616c9..a38d06040 100644 --- a/src/components/form/fields/default/MultiSelectFieldDefault.tsx +++ b/example/src/components/defaults/MultiSelectFieldDefault.tsx @@ -5,26 +5,30 @@ import { FormItem, FormLabel, FormMessage, -} from '@/src/components/ui/form'; -import { MultiSelect, Option } from '@/src/components/ui/multi-select'; -import { FieldComponentProps } from '@/src/types/fields'; +} from '../ui/form'; +import { MultiSelect, type Option } from '../ui/multi-select'; +import { FieldComponentProps } from '@remoteoss/remote-flows'; -export const MultiSelectFieldDefault = ({ +export function MultiSelectFieldDefault({ field, fieldState, fieldData, -}: FieldComponentProps) => { +}: FieldComponentProps) { const [selected, setSelected] = useState([]); + + useEffect(() => { + setSelected( + fieldData.options?.filter((option) => + field.value?.includes(option.value), + ) || [], + ); + }, [field.value, fieldData.options]); + const { name, label, description, options } = fieldData; const selectedOptions = selected || options?.filter((option) => field.value?.includes(option.value)); - useEffect(() => { - setSelected( - options?.filter((option) => field.value?.includes(option.value)) || [], - ); - }, [field.value, options]); return ( { const values = rawValues.map(({ value }) => value); - field.onChange(values); // This triggers the wrapped onChange from MultiSelectField + field.onChange(values); setSelected(rawValues); }} /> @@ -46,4 +50,4 @@ export const MultiSelectFieldDefault = ({ {fieldState.error && } ); -}; +} diff --git a/example/src/components/defaults/NumberFieldDefault.tsx b/example/src/components/defaults/NumberFieldDefault.tsx new file mode 100644 index 000000000..0bb9427c8 --- /dev/null +++ b/example/src/components/defaults/NumberFieldDefault.tsx @@ -0,0 +1,46 @@ +import { FormDescription, FormMessage } from '../ui/form'; +import { FormControl, FormItem, FormLabel } from '../ui/form'; +import { Input } from '../ui/input'; +import { TextFieldComponentProps } from '@remoteoss/remote-flows'; + +export function NumberFieldDefault({ + field, + fieldState, + fieldData, +}: TextFieldComponentProps) { + const { name, label, description, maxLength, includeErrorMessage } = + fieldData; + return ( + + {label && ( + {label} + )} + + ) => { + field.onChange(event); + }} + className='RemoteFlows__TextField__Input' + placeholder={label} + maxLength={maxLength} + /> + + {description && ( + + {description} + + )} + {includeErrorMessage && fieldState.error && ( + + )} + + ); +} diff --git a/src/components/shared/pdf-preview/PDFPreviewDefault.tsx b/example/src/components/defaults/PDFPreviewDefault.tsx similarity index 92% rename from src/components/shared/pdf-preview/PDFPreviewDefault.tsx rename to example/src/components/defaults/PDFPreviewDefault.tsx index ea7b0e45e..59167501c 100644 --- a/src/components/shared/pdf-preview/PDFPreviewDefault.tsx +++ b/example/src/components/defaults/PDFPreviewDefault.tsx @@ -1,4 +1,4 @@ -import { PDFPreviewComponentProps } from '@/src/types/remoteFlows'; +import { PDFPreviewComponentProps } from '@remoteoss/remote-flows'; export function PDFPreviewDefault({ base64Data, diff --git a/src/components/form/fields/default/RadioGroupFieldDefault.tsx b/example/src/components/defaults/RadioGroupFieldDefault.tsx similarity index 88% rename from src/components/form/fields/default/RadioGroupFieldDefault.tsx rename to example/src/components/defaults/RadioGroupFieldDefault.tsx index aaf34d1d6..c98bbc784 100644 --- a/src/components/form/fields/default/RadioGroupFieldDefault.tsx +++ b/example/src/components/defaults/RadioGroupFieldDefault.tsx @@ -5,11 +5,10 @@ import { FormItem, FormLabel, FormMessage, -} from '@/src/components/ui/form'; -import { RadioGroup, RadioGroupItem } from '@/src/components/ui/radio-group'; -import { cn } from '@/src/lib/utils'; -import { FieldComponentProps } from '@/src/types/fields'; -import { HelpCenter } from '@/src/components/shared/zendesk-drawer/HelpCenter'; +} from '../ui/form'; +import { RadioGroup, RadioGroupItem } from '../ui/radio-group'; +import { cn } from '@remoteoss/remote-flows/internals'; +import { FieldComponentProps, HelpCenter } from '@remoteoss/remote-flows'; export const RadioGroupFieldDefault = ({ field, diff --git a/src/components/form/fields/default/SelectFieldDefault.tsx b/example/src/components/defaults/SelectFieldDefault.tsx similarity index 93% rename from src/components/form/fields/default/SelectFieldDefault.tsx rename to example/src/components/defaults/SelectFieldDefault.tsx index e58cf398d..5fa7810cf 100644 --- a/src/components/form/fields/default/SelectFieldDefault.tsx +++ b/example/src/components/defaults/SelectFieldDefault.tsx @@ -4,8 +4,8 @@ import { FormItem, FormLabel, FormMessage, -} from '@/src/components/ui/form'; -import { FieldComponentProps } from '@/src/types/fields'; +} from '../ui/form'; +import { FieldComponentProps } from '@remoteoss/remote-flows'; import { Select, SelectGroup, @@ -13,7 +13,7 @@ import { SelectTrigger, SelectContent, SelectItem, -} from '@/src/components/ui/select'; +} from '../ui/select'; export function SelectFieldDefault({ field, diff --git a/src/components/form/fields/default/StatementDefault.tsx b/example/src/components/defaults/StatementDefault.tsx similarity index 82% rename from src/components/form/fields/default/StatementDefault.tsx rename to example/src/components/defaults/StatementDefault.tsx index b95e386b1..fb2e85117 100644 --- a/src/components/form/fields/default/StatementDefault.tsx +++ b/example/src/components/defaults/StatementDefault.tsx @@ -1,5 +1,9 @@ -import { Alert, AlertDescription, AlertTitle } from '@/src/components/ui/alert'; -import { StatementComponentProps } from '@/src/types/fields'; +import { + Alert, + AlertDescription, + AlertTitle, +} from '@remoteoss/remote-flows/internals'; +import { StatementComponentProps } from '@remoteoss/remote-flows'; import { AlertCircle } from 'lucide-react'; export function StatementDefault({ data }: StatementComponentProps) { diff --git a/src/components/shared/table/TableFieldDefault.tsx b/example/src/components/defaults/TableFieldDefault.tsx similarity index 74% rename from src/components/shared/table/TableFieldDefault.tsx rename to example/src/components/defaults/TableFieldDefault.tsx index c3751e23f..7b1deac44 100644 --- a/src/components/shared/table/TableFieldDefault.tsx +++ b/example/src/components/defaults/TableFieldDefault.tsx @@ -1,13 +1,13 @@ import { - Table as TablePrimitive, + Table, + TableHeader, TableBody, - TableCell, TableHead, - TableHeader, TableRow, -} from '@/src/components/ui/table'; -import { cn } from '@/src/lib/utils'; -import { $TSFixMe, TableComponentProps } from '@/src/types/remoteFlows'; + TableCell, +} from '../ui/table'; +import { cn } from '@remoteoss/remote-flows/internals'; +import { TableComponentProps } from '@remoteoss/remote-flows'; export const TableFieldDefault = ({ data, @@ -16,7 +16,7 @@ export const TableFieldDefault = ({ ref, }: TableComponentProps) => { return ( - + {columns.map((column) => ( @@ -31,7 +31,7 @@ export const TableFieldDefault = ({ {data?.map((row, rowIndex) => ( - + {columns.map((column) => ( ))} - +
); }; - -function getRowKey(row: $TSFixMe, index: number): string { - return String(row.id ?? index); -} diff --git a/src/components/form/fields/default/TextAreaFieldDefault.tsx b/example/src/components/defaults/TextAreaFieldDefault.tsx similarity index 89% rename from src/components/form/fields/default/TextAreaFieldDefault.tsx rename to example/src/components/defaults/TextAreaFieldDefault.tsx index 45cc7d4d3..9a8924530 100644 --- a/src/components/form/fields/default/TextAreaFieldDefault.tsx +++ b/example/src/components/defaults/TextAreaFieldDefault.tsx @@ -4,10 +4,10 @@ import { FormItem, FormLabel, FormMessage, -} from '@/src/components/ui/form'; -import { Textarea } from '@/src/components/ui/textarea'; -import { cn } from '@/src/lib/utils'; -import { FieldComponentProps } from '@/src/types/fields'; +} from '../ui/form'; +import { Textarea } from '../ui/textarea'; +import { cn } from '@remoteoss/remote-flows/internals'; +import { FieldComponentProps } from '@remoteoss/remote-flows'; export function TextAreaFieldDefault({ field, diff --git a/src/components/form/fields/default/TextFieldDefault.tsx b/example/src/components/defaults/TextFieldDefault.tsx similarity index 80% rename from src/components/form/fields/default/TextFieldDefault.tsx rename to example/src/components/defaults/TextFieldDefault.tsx index 1f2ea530a..9c0784b58 100644 --- a/src/components/form/fields/default/TextFieldDefault.tsx +++ b/example/src/components/defaults/TextFieldDefault.tsx @@ -1,7 +1,7 @@ -import { FormDescription, FormMessage } from '@/src/components/ui/form'; -import { FormControl, FormItem, FormLabel } from '@/src/components/ui/form'; -import { Input } from '@/src/components/ui/input'; -import { TextFieldComponentProps } from '@/src/types/fields'; +import { FormDescription, FormMessage } from '../ui/form'; +import { FormControl, FormItem, FormLabel } from '../ui/form'; +import { Input } from '../ui/input'; +import { TextFieldComponentProps } from '@remoteoss/remote-flows'; export function TextFieldDefault({ field, diff --git a/example/src/components/defaults/WorkScheduleFieldDefault.tsx b/example/src/components/defaults/WorkScheduleFieldDefault.tsx new file mode 100644 index 000000000..525236bc8 --- /dev/null +++ b/example/src/components/defaults/WorkScheduleFieldDefault.tsx @@ -0,0 +1,228 @@ +import { Fragment, useState } from 'react'; +import { useFieldArray, useForm, Controller } from 'react-hook-form'; +import { Button } from '@remoteoss/remote-flows/internals'; +import { Input } from '../ui/input'; +import { Checkbox } from '../ui/checkbox'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '../ui/dialog'; +import { + calculateHours, + DailySchedule, + DAYS_OF_THE_WEEK, + getShortWeekday, + WorkScheduleComponentProps, +} from '@remoteoss/remote-flows'; + +type WorkScheduleFormData = { + schedule: DailySchedule[]; +}; + +type WorkScheduleSelectionProps = { + defaultSchedule: DailySchedule[]; + onSubmit: (data: DailySchedule[]) => void; +}; + +function WorkScheduleSelectionForm({ + defaultSchedule, + onSubmit, +}: WorkScheduleSelectionProps) { + const [openDialog, setOpenDialog] = useState(false); + + const transformedSchedule = DAYS_OF_THE_WEEK.map((day) => { + const existingSchedule = defaultSchedule.find( + (schedule) => + schedule.day.toLowerCase() === getShortWeekday(day).toLowerCase() || + schedule.day.toLowerCase() === day.toLowerCase(), + ); + + if (existingSchedule) { + return { ...existingSchedule, day: getShortWeekday(day), checked: true }; + } + + return { + ...defaultSchedule[0], + checked: false, + day: getShortWeekday(day), + }; + }); + + const { handleSubmit, watch, reset, control, register, formState } = + useForm({ + defaultValues: { schedule: transformedSchedule }, + }); + + const { fields } = useFieldArray({ name: 'schedule', control }); + const watchedSchedule = watch('schedule'); + + function handleSubmitWorkingHours(data: WorkScheduleFormData) { + const schedule = data.schedule + .filter(({ checked }) => checked) + .map((day) => ({ ...day, hours: calculateHours(day) })); + + onSubmit(schedule); + setOpenDialog(false); + } + + function handleCancel() { + reset(); + setOpenDialog(false); + } + + return ( +
+ + + + + + + + Edit employee working hours + + + +
+
+

+ The times displayed are in the employee's time zone in the + 24-hour format. +

+ +
+
+
START
+
+
END
+
HOURS
+
+ +
+ {fields.map((field, index) => { + const currentDay = watchedSchedule[index]; + const calculatedHours = calculateHours(currentDay); + + return ( + +
+
+ ( +
+ + +
+ )} + /> +
+
+ +
+
+ to +
+
+ +
+
+ {isNaN(calculatedHours) + ? '-' + : `${calculatedHours} hours`} +
+
+ +
+
Break
+
+ +
+
minutes
+
+
+
+ ); + })} +
+
+ + {Object.keys(formState.errors).length > 0 && ( +

+ Invalid time format (HH:mm) +

+ )} + +
+ + +
+
+
+
+
+ ); +} + +export const WorkScheduleFieldDefault = ({ + fieldData, +}: WorkScheduleComponentProps) => { + const { defaultFormattedValue, currentSchedule, onSubmit } = fieldData; + const { workHoursSummary, breakSummary, totalWorkHours } = + defaultFormattedValue; + + return ( +
+

+ Work hours +

+
+

+

+ {breakSummary.join()} +

+

+ Total of {totalWorkHours} hours per week +

+ +
+
+ ); +}; diff --git a/src/components/shared/zendesk-drawer/ZendeskDrawerDefault.tsx b/example/src/components/defaults/ZendeskDrawerDefault.tsx similarity index 91% rename from src/components/shared/zendesk-drawer/ZendeskDrawerDefault.tsx rename to example/src/components/defaults/ZendeskDrawerDefault.tsx index 21b7652b4..11c615c93 100644 --- a/src/components/shared/zendesk-drawer/ZendeskDrawerDefault.tsx +++ b/example/src/components/defaults/ZendeskDrawerDefault.tsx @@ -5,8 +5,8 @@ import { DrawerHeader, DrawerTitle, DrawerTrigger, -} from '@/src/components/ui/drawer'; -import { ZendeskDrawerComponentProps } from '@/src/types/remoteFlows'; +} from '../ui/drawer'; +import { ZendeskDrawerComponentProps } from '@remoteoss/remote-flows'; export function ZendeskDrawerDefault({ open, @@ -46,7 +46,7 @@ export function ZendeskDrawerDefault({ dangerouslySetInnerHTML={{ __html: data?.body || '', }} - >
+ />
diff --git a/src/components/ui/calendar.tsx b/example/src/components/ui/calendar.tsx similarity index 96% rename from src/components/ui/calendar.tsx rename to example/src/components/ui/calendar.tsx index e95343bac..0c1076944 100644 --- a/src/components/ui/calendar.tsx +++ b/example/src/components/ui/calendar.tsx @@ -2,8 +2,7 @@ import * as React from 'react'; import { ChevronLeft, ChevronRight } from 'lucide-react'; import { DayPicker } from 'react-day-picker'; -import { cn } from '@/src/lib/utils'; -import { buttonVariants } from '@/src/components/ui/button'; +import { cn, buttonVariants } from '@remoteoss/remote-flows/internals'; function Calendar({ className, diff --git a/src/components/ui/checkbox.tsx b/example/src/components/ui/checkbox.tsx similarity index 94% rename from src/components/ui/checkbox.tsx rename to example/src/components/ui/checkbox.tsx index 7fab8fddb..c0b9a0713 100644 --- a/src/components/ui/checkbox.tsx +++ b/example/src/components/ui/checkbox.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; import { Check } from 'lucide-react'; -import { cn } from '@/src/lib/utils'; +import { cn } from '@remoteoss/remote-flows/internals'; const Checkbox = React.forwardRef< React.ComponentRef, diff --git a/src/components/ui/collapsible.tsx b/example/src/components/ui/collapsible.tsx similarity index 100% rename from src/components/ui/collapsible.tsx rename to example/src/components/ui/collapsible.tsx diff --git a/src/components/ui/command.tsx b/example/src/components/ui/command.tsx similarity index 71% rename from src/components/ui/command.tsx rename to example/src/components/ui/command.tsx index 88e730e84..719479906 100644 --- a/src/components/ui/command.tsx +++ b/example/src/components/ui/command.tsx @@ -1,15 +1,13 @@ import * as React from 'react'; import { Command as CommandPrimitive } from 'cmdk'; -import { SearchIcon } from 'lucide-react'; - -import { cn } from '@/src/lib/utils'; +import { cn } from '@remoteoss/remote-flows/internals'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, -} from '@/src/components/ui/dialog'; +} from './dialog'; function Command({ className, @@ -17,7 +15,6 @@ function Command({ }: React.ComponentProps) { return ( ) { - return ( -
- - -
- ); -} - function CommandList({ className, ...props }: React.ComponentProps) { return ( ) { return ( - + ); } @@ -107,7 +77,6 @@ function CommandGroup({ }: React.ComponentProps) { return ( ) { return ( @@ -136,7 +104,6 @@ function CommandItem({ }: React.ComponentProps) { return ( ) { - return ( - - ); -} - export { Command, CommandDialog, - CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, - CommandShortcut, CommandSeparator, }; diff --git a/src/components/ui/dialog.tsx b/example/src/components/ui/dialog.tsx similarity index 83% rename from src/components/ui/dialog.tsx rename to example/src/components/ui/dialog.tsx index f4cba00c9..78112d216 100644 --- a/src/components/ui/dialog.tsx +++ b/example/src/components/ui/dialog.tsx @@ -1,33 +1,30 @@ -'use client'; - import * as React from 'react'; import * as DialogPrimitive from '@radix-ui/react-dialog'; import { XIcon } from 'lucide-react'; - -import { cn } from '@/src/lib/utils'; +import { cn } from '@remoteoss/remote-flows/internals'; function Dialog({ ...props }: React.ComponentProps) { - return ; + return ; } function DialogTrigger({ ...props }: React.ComponentProps) { - return ; + return ; } function DialogPortal({ ...props }: React.ComponentProps) { - return ; + return ; } function DialogClose({ ...props }: React.ComponentProps) { - return ; + return ; } function DialogOverlay({ @@ -36,7 +33,6 @@ function DialogOverlay({ }: React.ComponentProps) { return ( ) { return ( - + {children} - + Close @@ -75,7 +70,6 @@ function DialogContent({ function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) { return (
@@ -85,7 +79,6 @@ function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) { function DialogFooter({ className, ...props }: React.ComponentProps<'div'>) { return (
) { return ( @@ -114,7 +106,6 @@ function DialogDescription({ }: React.ComponentProps) { return ( diff --git a/src/components/ui/drawer.tsx b/example/src/components/ui/drawer.tsx similarity index 98% rename from src/components/ui/drawer.tsx rename to example/src/components/ui/drawer.tsx index 6e9ce7c87..2bf6fb1b8 100644 --- a/src/components/ui/drawer.tsx +++ b/example/src/components/ui/drawer.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { Drawer as DrawerPrimitive } from 'vaul'; -import { cn } from '@/src/lib/utils'; +import { cn } from '@remoteoss/remote-flows/internals'; const Drawer = ({ shouldScaleBackground = true, diff --git a/src/components/ui/file-uploader.tsx b/example/src/components/ui/file-uploader.tsx similarity index 96% rename from src/components/ui/file-uploader.tsx rename to example/src/components/ui/file-uploader.tsx index 2a2b2360b..90c41f91a 100644 --- a/src/components/ui/file-uploader.tsx +++ b/example/src/components/ui/file-uploader.tsx @@ -1,7 +1,7 @@ import React, { useState, useRef, useEffect } from 'react'; -import { Button } from '@/src/components/ui/button'; +import { Button } from '@remoteoss/remote-flows/internals'; import { Upload, X } from 'lucide-react'; -import { cn } from '@/src/lib/utils'; +import { cn } from '@remoteoss/remote-flows/internals'; // Convert accept string to readable format (e.g., ".pdf, .doc" -> "PDF, DOC") const getAcceptedFormats = (accept?: string) => { diff --git a/example/src/components/ui/form.tsx b/example/src/components/ui/form.tsx new file mode 100644 index 000000000..eb35b6722 --- /dev/null +++ b/example/src/components/ui/form.tsx @@ -0,0 +1,91 @@ +import * as React from 'react'; + +import { + FormItemContext, + useFormField, + FormDescription, +} from '@remoteoss/remote-flows'; +import { cn } from '@remoteoss/remote-flows/internals'; +import { Label } from './label'; + +function FormItem({ className, ...props }: React.ComponentProps<'div'>) { + const id = React.useId(); + + return ( + +
+ + ); +} + +function FormLabel({ className, ...props }: React.ComponentProps<'label'>) { + const { error, formItemId } = useFormField(); + + return ( +
); +import { defaultComponents } from '@/src/tests/defaultComponents'; + vi.mock('@/src/context', () => ({ useFormFields: vi.fn(() => ({ components: { statement: MockStatement, + fieldsetToggle: defaultComponents.fieldsetToggle, }, })), })); diff --git a/src/components/shared/drawer/tests/Drawer.test.tsx b/src/components/shared/drawer/tests/Drawer.test.tsx index 3619e6198..1f6f26f62 100644 --- a/src/components/shared/drawer/tests/Drawer.test.tsx +++ b/src/components/shared/drawer/tests/Drawer.test.tsx @@ -3,9 +3,11 @@ import userEvent from '@testing-library/user-event'; import { Drawer } from '@/src/components/shared/drawer/Drawer'; import { FormFieldsContext } from '@/src/context'; import { ReactNode } from 'react'; -import { DrawerDefault } from '@/src/components/shared/drawer/DrawerDefault'; +import { defaultComponents } from '@/src/tests/defaultComponents'; -const mockComponents = { components: { drawer: DrawerDefault } }; +const mockComponents = { + components: { drawer: defaultComponents.drawer }, +}; const FormFieldsProvider = ({ children }: { children: ReactNode }) => ( @@ -93,7 +95,9 @@ describe('Drawer', () => { it('should use custom drawer component from components prop', () => { const CustomDrawer = vi.fn(() =>
Custom Drawer
); - const customComponents = { components: { drawer: CustomDrawer } }; + const customComponents = { + components: { drawer: CustomDrawer }, + }; render( diff --git a/src/components/shared/table/tests/Table.test.tsx b/src/components/shared/table/tests/Table.test.tsx index a2c594d38..78712e083 100644 --- a/src/components/shared/table/tests/Table.test.tsx +++ b/src/components/shared/table/tests/Table.test.tsx @@ -2,13 +2,13 @@ import { render, screen } from '@testing-library/react'; import { Table, ColumnDef } from '@/src/components/shared/table/Table'; import * as FormFieldsContext from '@/src/context'; import { $TSFixMe } from '@/src/types/remoteFlows'; -import { TableFieldDefault } from '@/src/components/shared/table/TableFieldDefault'; +import { defaultComponents } from '@/src/tests/defaultComponents'; // Mock the context vi.mock('@/src/context', () => ({ useFormFields: vi.fn(() => ({ components: { - table: TableFieldDefault, + table: defaultComponents.table, }, })), })); diff --git a/src/components/shared/zendesk-drawer/tests/ZendeskDrawer.test.tsx b/src/components/shared/zendesk-drawer/tests/ZendeskDrawer.test.tsx index 9f25ce715..6887b5737 100644 --- a/src/components/shared/zendesk-drawer/tests/ZendeskDrawer.test.tsx +++ b/src/components/shared/zendesk-drawer/tests/ZendeskDrawer.test.tsx @@ -112,27 +112,6 @@ describe('ZendeskDrawer', () => { ); }); - it('renders custom zendesk dialog component when provided', () => { - const CustomComponent = vi.fn(() => null); - const customWrapper = ({ children }: PropsWithChildren) => ( - - {children} - - ); - - render(, { wrapper: customWrapper }); - - expect(CustomComponent).toHaveBeenCalledWith( - expect.objectContaining({ - open: defaultProps.open, - onClose: expect.any(Function), - zendeskURL: expect.stringContaining(String(defaultProps.zendeskId)), - Trigger: defaultProps.Trigger, - }), - expect.any(Object), - ); - }); - it('only fetches article data when drawer is open', async () => { const fetchSpy = vi.fn(); server.use( diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx index ef9f598d6..72cacd134 100644 --- a/src/components/ui/accordion.tsx +++ b/src/components/ui/accordion.tsx @@ -1,48 +1,185 @@ import * as React from 'react'; -import * as AccordionPrimitive from '@radix-ui/react-accordion'; import { ChevronDownIcon } from 'lucide-react'; import { cn } from '@/src/lib/utils'; +type AccordionContextValue = { + openItem: string | undefined; + toggleItem: (value: string) => void; + collapsible: boolean; +}; + +const AccordionContext = React.createContext( + null, +); + +const useAccordion = () => { + const context = React.useContext(AccordionContext); + if (!context) { + throw new Error('Accordion components must be used within Accordion'); + } + return context; +}; + +type AccordionProps = { + type?: 'single'; + collapsible?: boolean; + defaultValue?: string; + value?: string; + onValueChange?: (value: string | undefined) => void; + className?: string; + children: React.ReactNode; +}; + function Accordion({ + collapsible = false, + defaultValue, + value: controlledValue, + onValueChange, + className, + children, ...props -}: React.ComponentProps) { - return ; +}: AccordionProps) { + const [uncontrolledValue, setUncontrolledValue] = React.useState< + string | undefined + >(defaultValue); + + const isControlled = controlledValue !== undefined; + const openItem = isControlled ? controlledValue : uncontrolledValue; + + const toggleItem = React.useCallback( + (value: string) => { + let newValue: string | undefined; + + if (openItem === value) { + // Clicking the open item + newValue = collapsible ? undefined : value; + } else { + // Clicking a different item + newValue = value; + } + + if (!isControlled) { + setUncontrolledValue(newValue); + } + + if (onValueChange) { + onValueChange(newValue); + } + }, + [openItem, collapsible, isControlled, onValueChange], + ); + + const contextValue = React.useMemo( + () => ({ openItem, toggleItem, collapsible }), + [openItem, toggleItem, collapsible], + ); + + return ( + +
+ {children} +
+
+ ); } +type AccordionItemContextValue = { + value: string; + isOpen: boolean; +}; + +const AccordionItemContext = + React.createContext(null); + +const useAccordionItem = () => { + const context = React.useContext(AccordionItemContext); + if (!context) { + throw new Error( + 'AccordionItem components must be used within AccordionItem', + ); + } + return context; +}; + +type AccordionItemProps = { + value: string; + className?: string; + children: React.ReactNode; +}; + function AccordionItem({ + value, className, + children, ...props -}: React.ComponentProps) { +}: AccordionItemProps) { + const { openItem } = useAccordion(); + const isOpen = openItem === value; + + const contextValue = React.useMemo( + () => ({ value, isOpen }), + [value, isOpen], + ); + return ( - + +
+ {children} +
+
); } -type AccordionTriggerProps = React.ComponentProps< - typeof AccordionPrimitive.Trigger -> & { +type AccordionTriggerProps = { + className?: string; iconClassName?: string; -}; + children: React.ReactNode; +} & React.ButtonHTMLAttributes; function AccordionTrigger({ className, iconClassName, children, + onClick, ...props }: AccordionTriggerProps) { + const { toggleItem } = useAccordion(); + const { value, isOpen } = useAccordionItem(); + const triggerId = React.useId(); + const contentId = React.useId(); + + const handleClick = (event: React.MouseEvent) => { + toggleItem(value); + onClick?.(event); + }; + + const handleKeyDown = (event: React.KeyboardEvent) => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + toggleItem(value); + } + }; + return ( - - + +
); } +type AccordionContentProps = { + className?: string; + children: React.ReactNode; +}; + function AccordionContent({ className, children, ...props -}: React.ComponentProps) { +}: AccordionContentProps): React.ReactElement | null { + const { isOpen } = useAccordionItem(); + const contentId = React.useId(); + const [shouldRender, setShouldRender] = React.useState(isOpen); + + React.useEffect(() => { + if (isOpen) { + setShouldRender(true); + return; + } + + const timer = setTimeout(() => setShouldRender(false), 200); + return () => clearTimeout(timer); + }, [isOpen]); + + if (!shouldRender) return null; + return ( -
{children}
-
+
); } diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx deleted file mode 100644 index f9389b570..000000000 --- a/src/components/ui/badge.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import * as React from 'react'; -import { Slot } from '@radix-ui/react-slot'; -import { cva, type VariantProps } from 'class-variance-authority'; - -import { cn } from '@/src/lib/utils'; - -const badgeVariants = cva( - 'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden', - { - variants: { - variant: { - default: - 'border-transparent bg-badge text-badge-foreground rounded-full [a&]:hover:bg-primary/90', - secondary: - 'border-transparent bg-secondary text-secondary-foreground rounded-full [a&]:hover:bg-secondary/90', - destructive: - 'border-transparent bg-destructive text-white rounded-full [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40', - outline: - 'text-foreground rounded-full [a&]:hover:bg-accent [a&]:hover:text-accent-foreground', - }, - }, - defaultVariants: { - variant: 'default', - }, - }, -); - -function Badge({ - className, - variant, - asChild = false, - ...props -}: React.ComponentProps<'span'> & - VariantProps & { asChild?: boolean }) { - const Comp = asChild ? Slot : 'span'; - - return ( - - ); -} - -export { Badge, badgeVariants }; diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index c20af6def..77e5f0469 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,4 +1,3 @@ -import { Slot } from '@radix-ui/react-slot'; import { cva, type VariantProps } from 'class-variance-authority'; import * as React from 'react'; @@ -36,20 +35,13 @@ const buttonVariants = cva( }, ); -function Button({ - className, - variant, - size, - asChild = false, - ...props -}: React.ComponentProps<'button'> & - VariantProps & { - asChild?: boolean; - }) { - const Comp = asChild ? Slot : 'button'; - +const Button = React.forwardRef< + HTMLButtonElement, + React.ComponentProps<'button'> & VariantProps +>(({ className, variant, size, ...props }, ref) => { return ( - ); -} +}); +Button.displayName = 'Button'; export { Button, buttonVariants }; diff --git a/src/components/ui/form.tsx b/src/components/ui/form.tsx index 9fc18c405..f7661ab16 100644 --- a/src/components/ui/form.tsx +++ b/src/components/ui/form.tsx @@ -1,5 +1,3 @@ -import * as LabelPrimitive from '@radix-ui/react-label'; -import { Slot } from '@radix-ui/react-slot'; import * as React from 'react'; import { Controller, @@ -11,7 +9,6 @@ import { useFormState, } from 'react-hook-form'; -import { Label } from '@/src/components/ui/label'; import { cn, sanitizeHtml } from '@/src/lib/utils'; const Form = FormProvider; @@ -71,65 +68,6 @@ const FormItemContext = React.createContext( {} as FormItemContextValue, ); -function FormItem({ className, ...props }: React.ComponentProps<'div'>) { - const id = React.useId(); - - return ( - -
- - ); -} - -function FormLabel({ - className, - ...props -}: React.ComponentProps) { - const { error, formItemId } = useFormField(); - - return ( -