@dev-fastn-ai/react-core 2.4.4 → 2.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +0 -7
- package/README.md +191 -5
- package/dist/core/use-connectors.d.ts +1 -1
- package/dist/core/use-field-options.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,13 +5,6 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [Unreleased]
|
|
9
|
-
|
|
10
|
-
### Added
|
|
11
|
-
- `useFieldOptions` hook now supports passing context data to `getOptions` and `loadMore` methods
|
|
12
|
-
- Context parameter allows passing form data or additional parameters for dynamic option filtering
|
|
13
|
-
- Support for cascading dropdowns and context-dependent option loading
|
|
14
|
-
|
|
15
8
|
## [1.0.0] - 2024-01-XX
|
|
16
9
|
|
|
17
10
|
### Added
|
package/README.md
CHANGED
|
@@ -603,7 +603,8 @@ For fields of type `select` or `multi-select`, use the `useFieldOptions` hook to
|
|
|
603
603
|
```tsx
|
|
604
604
|
import { useFieldOptions } from "@fastn-ai/react-core";
|
|
605
605
|
|
|
606
|
-
function SelectField({ field, value, onChange, isMulti = false }) {
|
|
606
|
+
function SelectField({ field, value, onChange, isMulti = false, context = {} }) {
|
|
607
|
+
// context contains all form values and is used to fetch dependent options
|
|
607
608
|
const {
|
|
608
609
|
options,
|
|
609
610
|
loading,
|
|
@@ -613,7 +614,7 @@ function SelectField({ field, value, onChange, isMulti = false }) {
|
|
|
613
614
|
error,
|
|
614
615
|
search,
|
|
615
616
|
totalLoadedOptions,
|
|
616
|
-
} = useFieldOptions(field,context);
|
|
617
|
+
} = useFieldOptions(field, context);
|
|
617
618
|
|
|
618
619
|
function handleInputChange(e) {
|
|
619
620
|
search(e.target.value);
|
|
@@ -715,10 +716,10 @@ function SelectField({ field, value, onChange, isMulti = false }) {
|
|
|
715
716
|
|
|
716
717
|
### **Google Drive Picker Fields**
|
|
717
718
|
|
|
718
|
-
For Google Drive file picker fields, handle the file selection flow. These fields also work with `{ label, value }` objects:
|
|
719
|
+
For Google Drive file picker fields, handle the file selection flow. These fields also work with `{ label, value }` objects and support file type filtering:
|
|
719
720
|
|
|
720
721
|
```tsx
|
|
721
|
-
function GoogleFilesPickerField({ field, value, onChange, isMulti = false }) {
|
|
722
|
+
function GoogleFilesPickerField({ field, value, onChange, isMulti = false, context = {} }) {
|
|
722
723
|
async function handlePickFiles() {
|
|
723
724
|
if (field.optionsSource?.openGoogleFilesPicker) {
|
|
724
725
|
await field.optionsSource.openGoogleFilesPicker({
|
|
@@ -743,6 +744,7 @@ function GoogleFilesPickerField({ field, value, onChange, isMulti = false }) {
|
|
|
743
744
|
console.error("Google Files Picker error:", pickerError);
|
|
744
745
|
alert("Failed to pick files: " + pickerError);
|
|
745
746
|
},
|
|
747
|
+
fileTypes: field?.optionsSource?.fileTypes,
|
|
746
748
|
});
|
|
747
749
|
}
|
|
748
750
|
}
|
|
@@ -786,7 +788,7 @@ function GoogleFilesPickerField({ field, value, onChange, isMulti = false }) {
|
|
|
786
788
|
Create a reusable component that handles different field types with proper value handling:
|
|
787
789
|
|
|
788
790
|
```tsx
|
|
789
|
-
function FormField({ field, value, onChange }) {
|
|
791
|
+
function FormField({ field, value, onChange, context = {} }) {
|
|
790
792
|
switch (field.type) {
|
|
791
793
|
case "text":
|
|
792
794
|
case "email":
|
|
@@ -839,6 +841,7 @@ function FormField({ field, value, onChange }) {
|
|
|
839
841
|
value={value}
|
|
840
842
|
onChange={onChange}
|
|
841
843
|
isMulti={false}
|
|
844
|
+
context={context}
|
|
842
845
|
/>
|
|
843
846
|
);
|
|
844
847
|
|
|
@@ -849,6 +852,7 @@ function FormField({ field, value, onChange }) {
|
|
|
849
852
|
value={value}
|
|
850
853
|
onChange={onChange}
|
|
851
854
|
isMulti={true}
|
|
855
|
+
context={context}
|
|
852
856
|
/>
|
|
853
857
|
);
|
|
854
858
|
|
|
@@ -859,6 +863,7 @@ function FormField({ field, value, onChange }) {
|
|
|
859
863
|
value={value}
|
|
860
864
|
onChange={onChange}
|
|
861
865
|
isMulti={false}
|
|
866
|
+
context={context}
|
|
862
867
|
/>
|
|
863
868
|
);
|
|
864
869
|
|
|
@@ -869,6 +874,7 @@ function FormField({ field, value, onChange }) {
|
|
|
869
874
|
value={value}
|
|
870
875
|
onChange={onChange}
|
|
871
876
|
isMulti={true}
|
|
877
|
+
context={context}
|
|
872
878
|
/>
|
|
873
879
|
);
|
|
874
880
|
|
|
@@ -886,6 +892,186 @@ function FormField({ field, value, onChange }) {
|
|
|
886
892
|
|
|
887
893
|
---
|
|
888
894
|
|
|
895
|
+
## 🔗 Field Context Passing
|
|
896
|
+
|
|
897
|
+
The React Core library supports **dynamic field dependencies** where form fields can access and respond to values from other fields in real-time. This enables complex form workflows where the options or behavior of one field depends on the selection made in another field.
|
|
898
|
+
|
|
899
|
+
### **How Context Passing Works**
|
|
900
|
+
|
|
901
|
+
When a user selects a value in one field, that value becomes available to all other fields through the `context` parameter. This allows dependent fields to:
|
|
902
|
+
|
|
903
|
+
- Load different options based on the parent field's selection
|
|
904
|
+
- Show/hide fields based on other field values
|
|
905
|
+
- Update their behavior dynamically
|
|
906
|
+
|
|
907
|
+
### **Implementation Example**
|
|
908
|
+
|
|
909
|
+
Here's how to implement context passing in your form components:
|
|
910
|
+
|
|
911
|
+
```tsx
|
|
912
|
+
import { useConfigurationForm } from "@fastn-ai/react-core";
|
|
913
|
+
|
|
914
|
+
function ConfigurationForm({ configurationId, connectorId, configuration }) {
|
|
915
|
+
const { data: configurationForm, isLoading, error } = useConfigurationForm({
|
|
916
|
+
configurationId,
|
|
917
|
+
connectorId,
|
|
918
|
+
configuration,
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
return (
|
|
922
|
+
<Formik
|
|
923
|
+
initialValues={initialValues}
|
|
924
|
+
onSubmit={handleSubmit}
|
|
925
|
+
>
|
|
926
|
+
{({ values, setFieldValue, setFieldTouched, errors, touched }) => (
|
|
927
|
+
<Form>
|
|
928
|
+
{configurationForm.fields
|
|
929
|
+
.filter((field) => !field.hidden)
|
|
930
|
+
.map((field) => (
|
|
931
|
+
<FormField
|
|
932
|
+
key={field.name}
|
|
933
|
+
field={field}
|
|
934
|
+
form={{ values, errors, touched }}
|
|
935
|
+
setFieldValue={setFieldValue}
|
|
936
|
+
setFieldTouched={setFieldTouched}
|
|
937
|
+
context={values} // Pass all form values as context
|
|
938
|
+
/>
|
|
939
|
+
))}
|
|
940
|
+
</Form>
|
|
941
|
+
)}
|
|
942
|
+
</Formik>
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// FormField component that passes context to individual fields
|
|
947
|
+
function FormField({ field, form, setFieldValue, setFieldTouched, context }) {
|
|
948
|
+
const handleChange = useCallback(
|
|
949
|
+
(value) => {
|
|
950
|
+
setFieldValue(field.name, value);
|
|
951
|
+
setFieldTouched(field.name, true);
|
|
952
|
+
},
|
|
953
|
+
[field.name, setFieldValue, setFieldTouched]
|
|
954
|
+
);
|
|
955
|
+
|
|
956
|
+
switch (field.type) {
|
|
957
|
+
case "select":
|
|
958
|
+
case "multi-select":
|
|
959
|
+
return (
|
|
960
|
+
<SelectField
|
|
961
|
+
field={field}
|
|
962
|
+
onChange={handleChange}
|
|
963
|
+
value={form.values[field.name]}
|
|
964
|
+
isMulti={field.type === "multi-select"}
|
|
965
|
+
context={context} // Pass context to SelectField
|
|
966
|
+
/>
|
|
967
|
+
);
|
|
968
|
+
// ... other field types
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// SelectField component that uses context
|
|
973
|
+
function SelectField({ field, onChange, value, isMulti, context }) {
|
|
974
|
+
// The useFieldOptions hook receives the context and can use it
|
|
975
|
+
// to fetch options based on other field values
|
|
976
|
+
const {
|
|
977
|
+
options,
|
|
978
|
+
loading,
|
|
979
|
+
loadingMore,
|
|
980
|
+
hasNext,
|
|
981
|
+
loadMore,
|
|
982
|
+
error,
|
|
983
|
+
search,
|
|
984
|
+
refresh,
|
|
985
|
+
totalLoadedOptions,
|
|
986
|
+
} = useFieldOptions(field, context);
|
|
987
|
+
|
|
988
|
+
const handleChange = useCallback(
|
|
989
|
+
(selected) => {
|
|
990
|
+
onChange(selected);
|
|
991
|
+
// When this field changes, the context is automatically updated
|
|
992
|
+
// and passed to all other fields
|
|
993
|
+
},
|
|
994
|
+
[onChange]
|
|
995
|
+
);
|
|
996
|
+
|
|
997
|
+
// ... rest of component implementation
|
|
998
|
+
}
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
### **Context Structure**
|
|
1002
|
+
|
|
1003
|
+
The `context` parameter contains all current form values in the following structure:
|
|
1004
|
+
|
|
1005
|
+
```tsx
|
|
1006
|
+
// Example context object
|
|
1007
|
+
const context = {
|
|
1008
|
+
workspace: { label: "My Workspace", value: "ws_123" },
|
|
1009
|
+
channel: { label: "General", value: "ch_456" },
|
|
1010
|
+
webhookUrl: "https://hooks.slack.com/...",
|
|
1011
|
+
enableNotifications: true,
|
|
1012
|
+
// ... other field values
|
|
1013
|
+
};
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
### **Use Cases**
|
|
1017
|
+
|
|
1018
|
+
1. **Workspace → Channel Selection**: When a user selects a workspace, the channel field loads only channels from that workspace.
|
|
1019
|
+
|
|
1020
|
+
2. **Database → Table Selection**: When a database is selected, the table field shows only tables from that database.
|
|
1021
|
+
|
|
1022
|
+
3. **Conditional Field Display**: Show/hide fields based on other field values.
|
|
1023
|
+
|
|
1024
|
+
4. **Dynamic Option Loading**: Load different options based on parent field selections.
|
|
1025
|
+
|
|
1026
|
+
### **Best Practices**
|
|
1027
|
+
|
|
1028
|
+
1. **Always pass context**: Make sure to pass the `context` parameter to all field components that need access to other field values.
|
|
1029
|
+
|
|
1030
|
+
2. **Handle loading states**: When context changes, dependent fields may need to reload their options, so handle loading states appropriately.
|
|
1031
|
+
|
|
1032
|
+
3. **Optimize re-renders**: Use `useCallback` and `useMemo` to prevent unnecessary re-renders when context changes.
|
|
1033
|
+
|
|
1034
|
+
4. **Error handling**: Handle cases where context-dependent operations fail gracefully.
|
|
1035
|
+
|
|
1036
|
+
### **Advanced Example: Multi-level Dependencies**
|
|
1037
|
+
|
|
1038
|
+
```tsx
|
|
1039
|
+
// Example: Workspace → Channel → User selection
|
|
1040
|
+
function MultiLevelSelectForm() {
|
|
1041
|
+
const [formValues, setFormValues] = useState({});
|
|
1042
|
+
|
|
1043
|
+
return (
|
|
1044
|
+
<div>
|
|
1045
|
+
{/* Workspace selection */}
|
|
1046
|
+
<SelectField
|
|
1047
|
+
field={workspaceField}
|
|
1048
|
+
value={formValues.workspace}
|
|
1049
|
+
onChange={(value) => setFormValues(prev => ({ ...prev, workspace: value }))}
|
|
1050
|
+
context={formValues}
|
|
1051
|
+
/>
|
|
1052
|
+
|
|
1053
|
+
{/* Channel selection - depends on workspace */}
|
|
1054
|
+
<SelectField
|
|
1055
|
+
field={channelField}
|
|
1056
|
+
value={formValues.channel}
|
|
1057
|
+
onChange={(value) => setFormValues(prev => ({ ...prev, channel: value }))}
|
|
1058
|
+
context={formValues} // Has access to workspace value
|
|
1059
|
+
/>
|
|
1060
|
+
|
|
1061
|
+
{/* User selection - depends on both workspace and channel */}
|
|
1062
|
+
<SelectField
|
|
1063
|
+
field={userField}
|
|
1064
|
+
value={formValues.user}
|
|
1065
|
+
onChange={(value) => setFormValues(prev => ({ ...prev, user: value }))}
|
|
1066
|
+
context={formValues} // Has access to both workspace and channel values
|
|
1067
|
+
/>
|
|
1068
|
+
</div>
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
```
|
|
1072
|
+
|
|
1073
|
+
---
|
|
1074
|
+
|
|
889
1075
|
### **Error Handling and Loading States**
|
|
890
1076
|
|
|
891
1077
|
```tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const useConnectors: () => import("@tanstack/react-query").UseQueryResult<import("@
|
|
1
|
+
export declare const useConnectors: () => import("@tanstack/react-query").UseQueryResult<import("@fastn-ai/core").Connector[], Error>;
|
|
@@ -23,7 +23,7 @@ import type { ConnectorField, SelectOption, FormData } from "@fastn-ai/core";
|
|
|
23
23
|
* // Pagination is managed with infinite query
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
|
-
export declare function useFieldOptions(field: ConnectorField, context?: FormData): {
|
|
26
|
+
export declare function useFieldOptions(field: ConnectorField, context?: FormData | object): {
|
|
27
27
|
options: SelectOption[];
|
|
28
28
|
loading: boolean;
|
|
29
29
|
loadingMore: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ declare const useConnectors: () => _tanstack_react_query.UseQueryResult<_fastn_a
|
|
|
41
41
|
* // Pagination is managed with infinite query
|
|
42
42
|
* ```
|
|
43
43
|
*/
|
|
44
|
-
declare function useFieldOptions(field: ConnectorField, context?: FormData): {
|
|
44
|
+
declare function useFieldOptions(field: ConnectorField, context?: FormData | object): {
|
|
45
45
|
options: SelectOption[];
|
|
46
46
|
loading: boolean;
|
|
47
47
|
loadingMore: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dev-fastn-ai/react-core",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.6",
|
|
4
4
|
"description": "React hooks and components for integrating Fastn AI connector marketplace into your applications. Built on top of @fastn-ai/core with React Query for optimal performance.",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -52,15 +52,15 @@
|
|
|
52
52
|
"license": "MIT",
|
|
53
53
|
"repository": {
|
|
54
54
|
"type": "git",
|
|
55
|
-
"url": "https://github.com/fastn-ai/react-core.git"
|
|
55
|
+
"url": "https://github.com/dev-fastn-ai/react-core.git"
|
|
56
56
|
},
|
|
57
57
|
"bugs": {
|
|
58
|
-
"url": "https://github.com/fastn-ai/react-core/issues"
|
|
58
|
+
"url": "https://github.com/dev-fastn-ai/react-core/issues"
|
|
59
59
|
},
|
|
60
60
|
"homepage": "https://docs.fastn.ai",
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@dev-fastn-ai/core": "^2.0.7",
|
|
63
|
-
"@fastn-ai/core": "^1.0.
|
|
63
|
+
"@fastn-ai/core": "^1.0.5",
|
|
64
64
|
"@types/lodash": "^4.17.20",
|
|
65
65
|
"lodash": "^4.17.21"
|
|
66
66
|
},
|
|
@@ -89,4 +89,4 @@
|
|
|
89
89
|
"publishConfig": {
|
|
90
90
|
"access": "public"
|
|
91
91
|
}
|
|
92
|
-
}
|
|
92
|
+
}
|