@aws-amplify/ui-react-core 3.0.30 → 3.1.1
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/dist/elements.js +116 -3
- package/dist/esm/elements/ControlsContext.mjs +71 -0
- package/dist/esm/elements/ElementsContext.mjs +3 -2
- package/dist/esm/elements/defineBaseElement.mjs +30 -1
- package/dist/esm/elements/utils.mjs +11 -0
- package/dist/esm/elements/withBaseElementProps.mjs +1 -1
- package/dist/esm/elements.mjs +3 -1
- package/dist/esm/hooks/useDataState.mjs +10 -4
- package/dist/esm/utils/createContextUtilities.mjs +6 -3
- package/dist/index.js +15 -7
- package/dist/types/elements/ControlsContext.d.ts +63 -0
- package/dist/types/elements/ElementsContext.d.ts +3 -2
- package/dist/types/elements/defineBaseElement.d.ts +19 -2
- package/dist/types/elements/index.d.ts +4 -1
- package/dist/types/elements/types.d.ts +88 -6
- package/dist/types/elements/utils.d.ts +3 -0
- package/dist/types/elements/withBaseElementProps.d.ts +3 -3
- package/dist/types/hooks/index.d.ts +1 -1
- package/dist/types/hooks/useDataState.d.ts +6 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/utils/createContextUtilities.d.ts +1 -1
- package/package.json +3 -3
- package/src/elements/ControlsContext.tsx +89 -0
- package/src/elements/ElementsContext.tsx +3 -2
- package/src/elements/defineBaseElement.tsx +50 -2
- package/src/elements/index.ts +7 -1
- package/src/elements/types.ts +114 -6
- package/src/elements/utils.ts +20 -0
- package/src/elements/withBaseElementProps.tsx +3 -3
- package/src/hooks/index.ts +6 -1
- package/src/hooks/useDataState.ts +25 -7
- package/src/index.ts +2 -0
- package/src/utils/createContextUtilities.tsx +9 -6
|
@@ -12,17 +12,27 @@ import React from 'react';
|
|
|
12
12
|
* are always optional at the interface level, allowing for additional `props`
|
|
13
13
|
* to be added to existing `BaseElement` interfaces as needed.
|
|
14
14
|
*/
|
|
15
|
-
export type BaseElement<T = {}
|
|
15
|
+
export type BaseElement<T = {}> = (props: T) => React.JSX.Element;
|
|
16
|
+
/**
|
|
17
|
+
* @internal @unstable
|
|
18
|
+
*
|
|
19
|
+
* see @type {BaseElement}
|
|
20
|
+
*
|
|
21
|
+
* `BaseElement` with a `ref` corresponding to the `element` type
|
|
22
|
+
*/
|
|
23
|
+
export type BaseElementWithRef<T = {}, K = {}> = React.ForwardRefExoticComponent<React.PropsWithoutRef<T> & React.RefAttributes<K>>;
|
|
16
24
|
type ListElementSubType = 'Ordered' | 'Unordered';
|
|
17
|
-
type ListElementDisplayName =
|
|
18
|
-
type TableElementSubType = 'Body' | '
|
|
25
|
+
type ListElementDisplayName = `${ListElementSubType}List`;
|
|
26
|
+
type TableElementSubType = 'Body' | 'DataCell' | 'Row' | 'Head' | 'Header';
|
|
19
27
|
type TableElementDisplayName = 'Table' | `Table${TableElementSubType}`;
|
|
28
|
+
type DescriptionElementSubType = 'Details' | 'List' | 'Term';
|
|
29
|
+
type DescriptionElementDisplayName = `Description${DescriptionElementSubType}`;
|
|
20
30
|
/**
|
|
21
31
|
* @internal @unstable
|
|
22
32
|
*
|
|
23
33
|
* allowed values of `displayName` of `BaseElement` and `ElemebtsContext` keys
|
|
24
34
|
*/
|
|
25
|
-
export type ElementDisplayName = 'Button' | '
|
|
35
|
+
export type ElementDisplayName = 'Button' | 'Heading' | 'Icon' | 'Image' | 'Input' | 'Label' | 'ListItem' | 'Nav' | 'ProgressBar' | 'Span' | 'Text' | 'TextArea' | 'Title' | 'View' | DescriptionElementDisplayName | ListElementDisplayName | TableElementDisplayName;
|
|
26
36
|
/**
|
|
27
37
|
* @internal @unstable
|
|
28
38
|
*/
|
|
@@ -36,7 +46,7 @@ export type ReactElementType = keyof React.JSX.IntrinsicElements;
|
|
|
36
46
|
/**
|
|
37
47
|
* @internal @unstable
|
|
38
48
|
*/
|
|
39
|
-
export type ReactElementProps<T extends ReactElementType> = React.
|
|
49
|
+
export type ReactElementProps<T extends ReactElementType> = React.ComponentProps<T>;
|
|
40
50
|
/**
|
|
41
51
|
* @internal @unstable
|
|
42
52
|
*
|
|
@@ -46,8 +56,80 @@ type ElementPropKey<T> = T | 'children' | 'className' | 'style';
|
|
|
46
56
|
/**
|
|
47
57
|
* @internal @unstable
|
|
48
58
|
*/
|
|
49
|
-
export type BaseElementProps<T extends keyof K, V = string, K extends Record<ElementPropKey<keyof K>, any> = Record<string, any>> = React.AriaAttributes & React.
|
|
59
|
+
export type BaseElementProps<T extends keyof K, V = string, K extends Record<ElementPropKey<keyof K>, any> = Record<string, any>> = React.AriaAttributes & React.Attributes & Pick<K, ElementPropKey<T>> & {
|
|
50
60
|
testId?: string;
|
|
51
61
|
variant?: V;
|
|
52
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* @internal @unstable
|
|
65
|
+
*/
|
|
66
|
+
export type BaseElementWithRefProps<T extends keyof K, V = string, K extends Record<ElementPropKey<keyof K>, any> = Record<string, any>> = BaseElementProps<T, V, K> & React.RefAttributes<ElementRefType<K>>;
|
|
67
|
+
/**
|
|
68
|
+
* @internal @unstable
|
|
69
|
+
*/
|
|
70
|
+
export type ElementWithAndWithoutRef<T extends ReactElementType, K extends React.ComponentType<React.ComponentProps<T>> = React.ComponentType<React.ComponentProps<T>>> = K extends React.ComponentType<infer U> ? React.ForwardRefExoticComponent<U> : never;
|
|
71
|
+
/**
|
|
72
|
+
* @internal @unstable
|
|
73
|
+
*
|
|
74
|
+
* Merge `BaseElement` defintions with `elements` types provided by
|
|
75
|
+
* consumers, for use with top level connected component function
|
|
76
|
+
* signatures.
|
|
77
|
+
*
|
|
78
|
+
* Example:
|
|
79
|
+
*
|
|
80
|
+
* ```tsx
|
|
81
|
+
* export function createStorageBrowser<
|
|
82
|
+
* T extends Partial<StorageBrowserElements>,
|
|
83
|
+
* >({ elements }: CreateStorageBrowserInput<T> = {}): {
|
|
84
|
+
* StorageBrowser: StorageBrowser<MergeElements<StorageBrowserElements, T>>
|
|
85
|
+
* } {
|
|
86
|
+
* // ...do create stuff
|
|
87
|
+
* };
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export type MergeBaseElements<T, K extends Partial<T>> = {
|
|
91
|
+
[U in keyof T]: K[U] extends T[U] ? K[U] : T[U];
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* @internal @unstable
|
|
95
|
+
*
|
|
96
|
+
* Extend the defintion of a `BaseElement` with additional `props`.
|
|
97
|
+
*
|
|
98
|
+
* Use cases are restricted to scenarios where additional `props`
|
|
99
|
+
* are required for a `ControlElement` interface, for example:
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```tsx
|
|
103
|
+
* const FieldInput = defineBaseElementWithRef({
|
|
104
|
+
* type: 'input',
|
|
105
|
+
* displayName: 'Input'
|
|
106
|
+
* });
|
|
107
|
+
*
|
|
108
|
+
* type InputWithSearchCallback =
|
|
109
|
+
* ExtendBaseElement<
|
|
110
|
+
* typeof FieldInput,
|
|
111
|
+
* { onSearch?: (event: { value: string }) => void }
|
|
112
|
+
* >
|
|
113
|
+
*
|
|
114
|
+
* const SearchInput = React.forwardRef((
|
|
115
|
+
* { onSearch, ...props }
|
|
116
|
+
* ref
|
|
117
|
+
* ) => {
|
|
118
|
+
* // ...do something with onSearch
|
|
119
|
+
*
|
|
120
|
+
* return <FieldInput {...props} ref={ref} />;
|
|
121
|
+
* });
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* Caveats:
|
|
125
|
+
* - additional `props` should not be passed directly to
|
|
126
|
+
* `BaseElement` components, the outputted interface should be
|
|
127
|
+
* applied to a wrapping element that handles the additional `props`
|
|
128
|
+
*
|
|
129
|
+
* - additional `props` that share a key with existing `props`
|
|
130
|
+
* are omitted from the outputted interface to adhere to `BaseElement`
|
|
131
|
+
* type contracts
|
|
132
|
+
*
|
|
133
|
+
*/
|
|
134
|
+
export type ExtendBaseElement<T extends React.ComponentType, K = {}, U extends React.ComponentPropsWithRef<T> = React.ComponentPropsWithRef<T>> = BaseElementWithRef<U & Omit<K, keyof U>, U>;
|
|
53
135
|
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export declare function isComponent<T>(component?: React.ComponentType<T> | React.ForwardRefExoticComponent<T>): component is React.ComponentType<T>;
|
|
3
|
+
export declare function isForwardRefExoticComponent<T>(component: React.ComponentType<T> | React.ForwardRefExoticComponent<T>): component is React.ForwardRefExoticComponent<T>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { BaseElementWithRef, ElementRefType } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* @internal @unstable
|
|
5
5
|
*
|
|
@@ -15,7 +15,7 @@ import { BaseElement, ElementRefType } from './types';
|
|
|
15
15
|
* type InputElementPropKey = 'onChange' | 'type';
|
|
16
16
|
*
|
|
17
17
|
* // create `InputElement` base with `type` generic and extended `props` key
|
|
18
|
-
* export const InputElement =
|
|
18
|
+
* export const InputElement = defineBaseElementWithRef<"input", InputElementPropKey>({
|
|
19
19
|
* type: "input",
|
|
20
20
|
* displayName: "Input",
|
|
21
21
|
* });
|
|
@@ -31,4 +31,4 @@ import { BaseElement, ElementRefType } from './types';
|
|
|
31
31
|
* @param defaultProps `defaultProps` to apply to `Target`, accepts object or callback
|
|
32
32
|
* @returns extended `BaseElement` with `defaultProps`
|
|
33
33
|
*/
|
|
34
|
-
export default function withBaseElementProps<T, K extends T | ((input: T) => T)>(Target: React.ForwardRefExoticComponent<T>, defaultProps: K):
|
|
34
|
+
export default function withBaseElementProps<T, K extends T | ((input: T) => T)>(Target: React.ForwardRefExoticComponent<T>, defaultProps: K): BaseElementWithRef<T, ElementRefType<T>>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { default as useDataState, DataState } from './useDataState';
|
|
1
|
+
export { default as useDataState, AsyncDataAction, DataAction, DataState, } from './useDataState';
|
|
2
2
|
export { default as useDeprecationWarning, UseDeprecationWarning, } from './useDeprecationWarning';
|
|
3
3
|
export { default as useGetUrl } from './useGetUrl';
|
|
4
4
|
export { default as useHasValueUpdated } from './useHasValueUpdated';
|
|
@@ -4,4 +4,9 @@ export interface DataState<T> {
|
|
|
4
4
|
isLoading: boolean;
|
|
5
5
|
message: string | undefined;
|
|
6
6
|
}
|
|
7
|
-
export
|
|
7
|
+
export type DataAction<T = any, K = any> = (prevData: T, input: K) => T;
|
|
8
|
+
export type AsyncDataAction<T = any, K = any> = (prevData: T, input: K) => Promise<T>;
|
|
9
|
+
export default function useDataState<T, K>(action: DataAction<T, K> | AsyncDataAction<T, K>, initialData: T, options?: {
|
|
10
|
+
onSuccess?: (data: T) => void;
|
|
11
|
+
onError?: (message: string) => void;
|
|
12
|
+
}): [state: DataState<T>, handleAction: (input: K) => void];
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { AuthenticatorComponentDefaults, AuthenticatorComponentDefaultProps, AuthenticatorComponentOverrides, AuthenticatorFooterComponent, AuthenticatorFormFieldsComponent, AuthenticatorHeaderComponent, AuthenticatorLegacyField, AuthenticatorMachineContext, AuthenticatorProvider, AuthenticatorRouteComponentKey, AuthenticatorRouteComponentName, isAuthenticatorComponentRouteKey, resolveAuthenticatorComponents, useAuthenticator, useAuthenticatorRoute, UseAuthenticator, useAuthenticatorInitMachine, UseAuthenticatorRoute, } from './Authenticator';
|
|
2
2
|
export { FormProvider, FormProviderProps, RenderNothing, FormValues, FormHandle, useField, useForm, UseForm, Validate, Validator, withFormProvider, } from './components';
|
|
3
|
-
export { useDeprecationWarning, UseDeprecationWarning, useGetUrl, useHasValueUpdated, usePreviousValue, useSetUserAgent, useTimeout, useDataState, DataState, useDropZone, UseDropZoneParams, } from './hooks';
|
|
3
|
+
export { AsyncDataAction, DataAction, useDeprecationWarning, UseDeprecationWarning, useGetUrl, useHasValueUpdated, usePreviousValue, useSetUserAgent, useTimeout, useDataState, DataState, useDropZone, UseDropZoneParams, } from './hooks';
|
|
4
4
|
export { MergeProps } from './types';
|
|
5
5
|
export { createContextUtilities } from './utils';
|
|
@@ -16,7 +16,7 @@ type HookParams = {
|
|
|
16
16
|
};
|
|
17
17
|
type UtilityKey<ContextName extends string> = `${ContextName}Provider` | `use${ContextName}` | `${ContextName}Context`;
|
|
18
18
|
type CreateContextUtilitiesReturn<ContextType, ContextName extends string> = {
|
|
19
|
-
[Key in UtilityKey<ContextName>]: Key extends `${string}Provider` ? React.ComponentType<React.PropsWithChildren<ContextType>> : Key extends `use${string}` ? (params?: HookParams) => ContextType : Key extends `${string}Context` ? React.Context<ContextType
|
|
19
|
+
[Key in UtilityKey<ContextName>]: Key extends `${string}Provider` ? React.ComponentType<React.PropsWithChildren<ContextType>> : Key extends `use${string}` ? (params?: HookParams) => ContextType : Key extends `${string}Context` ? React.Context<ContextType> : never;
|
|
20
20
|
};
|
|
21
21
|
/**
|
|
22
22
|
* Uses `ContextType`/`Name` generics and `options` to create:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws-amplify/ui-react-core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"module": "dist/esm/index.mjs",
|
|
6
6
|
"react-native": "src/index.ts",
|
|
@@ -40,14 +40,14 @@
|
|
|
40
40
|
"typecheck": "tsc --noEmit"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@aws-amplify/ui": "6.
|
|
43
|
+
"@aws-amplify/ui": "6.7.1",
|
|
44
44
|
"@xstate/react": "^3.2.2",
|
|
45
45
|
"lodash": "4.17.21",
|
|
46
46
|
"react-hook-form": "^7.43.5",
|
|
47
47
|
"xstate": "^4.33.6"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"aws-amplify": "^6.
|
|
50
|
+
"aws-amplify": "^6.9.0",
|
|
51
51
|
"react": "^16.14.0 || ^17.0 || ^18.0"
|
|
52
52
|
},
|
|
53
53
|
"sideEffects": false
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { isComponent } from './utils';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @internal @unstable
|
|
6
|
+
*/
|
|
7
|
+
export interface Controls
|
|
8
|
+
extends Partial<Record<string, React.ComponentType>> {}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @internal @unstable
|
|
12
|
+
*/
|
|
13
|
+
export const ControlsContext = React.createContext<Controls | undefined>(
|
|
14
|
+
undefined
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @internal @unstable
|
|
19
|
+
*
|
|
20
|
+
* `ControlsProvider` provides the values contained in `ControlsContext`
|
|
21
|
+
* to consumers. `ControlsContext` lookup is handled directly
|
|
22
|
+
* by `Control` components returned by `withControls`.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
*
|
|
26
|
+
* Add `ControlsContext` aware `Controls` components to a Connected
|
|
27
|
+
* Component:
|
|
28
|
+
*
|
|
29
|
+
* ```tsx
|
|
30
|
+
* const DataList = withControls(function DataList<T>(data: T[]) {
|
|
31
|
+
* return <ScrollView>data.map(ListItem)</ScrollView>;
|
|
32
|
+
* }, 'DataList');
|
|
33
|
+
*
|
|
34
|
+
* const DataListControl = () => {
|
|
35
|
+
* const data = useData();
|
|
36
|
+
* return <DataList data={data} />;
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
* interface ComponentControls {
|
|
40
|
+
* DataList: typeof DataListControl;
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* function Component<T extends ComponentControls>(
|
|
44
|
+
* controls?: T
|
|
45
|
+
* ) {
|
|
46
|
+
* function ConnectedComponent({
|
|
47
|
+
* children,
|
|
48
|
+
* }: { children?: React.ReactNode }) {
|
|
49
|
+
* return (
|
|
50
|
+
* <ControlsProvider controls={controls}>
|
|
51
|
+
* {children}
|
|
52
|
+
* </ControlsProvider>
|
|
53
|
+
* );
|
|
54
|
+
* }
|
|
55
|
+
*
|
|
56
|
+
* return ConnectedComponent;
|
|
57
|
+
* }
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export function ControlsProvider<T extends Controls>({
|
|
61
|
+
controls,
|
|
62
|
+
...props
|
|
63
|
+
}: {
|
|
64
|
+
children?: React.ReactNode;
|
|
65
|
+
controls?: T;
|
|
66
|
+
}): React.JSX.Element {
|
|
67
|
+
return <ControlsContext.Provider {...props} value={controls} />;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @internal @unstable
|
|
72
|
+
*
|
|
73
|
+
* @note reference `ControlsProvider` for example usage
|
|
74
|
+
*/
|
|
75
|
+
export function withControls<
|
|
76
|
+
T extends React.ComponentType<any>,
|
|
77
|
+
K extends keyof Controls,
|
|
78
|
+
>(Default: T, name: K): (props: React.ComponentProps<T>) => React.JSX.Element {
|
|
79
|
+
const Component = (props: React.ComponentProps<T>) => {
|
|
80
|
+
const Override = React.useContext(ControlsContext)?.[name];
|
|
81
|
+
if (isComponent(Override)) {
|
|
82
|
+
return <Override {...props} />;
|
|
83
|
+
}
|
|
84
|
+
return <Default {...props} />;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
Component.displayName = name;
|
|
88
|
+
return Component;
|
|
89
|
+
}
|
|
@@ -16,7 +16,8 @@ export const ElementsContext = React.createContext<Elements | undefined>(
|
|
|
16
16
|
*
|
|
17
17
|
* `ElementsProvider` provides the values contained in `ElementsContext`
|
|
18
18
|
* to its `children`. `ElementsContext` lookup is handled directly
|
|
19
|
-
* by `BaseElement`components returned by `defineBaseElement
|
|
19
|
+
* by `BaseElement`components returned by `defineBaseElement` and
|
|
20
|
+
* `defineBaseElementWithRef`.
|
|
20
21
|
*
|
|
21
22
|
* @example
|
|
22
23
|
*
|
|
@@ -25,7 +26,7 @@ export const ElementsContext = React.createContext<Elements | undefined>(
|
|
|
25
26
|
*
|
|
26
27
|
* ```tsx
|
|
27
28
|
* // `BaseElement`, renders custom or default element defintion
|
|
28
|
-
* const ViewElement =
|
|
29
|
+
* const ViewElement = defineBaseElementWithRef({
|
|
29
30
|
* displayName: "View",
|
|
30
31
|
* type: "div",
|
|
31
32
|
* });
|
|
@@ -3,6 +3,8 @@ import { ElementsContext } from './ElementsContext';
|
|
|
3
3
|
import {
|
|
4
4
|
BaseElement,
|
|
5
5
|
BaseElementProps,
|
|
6
|
+
BaseElementWithRef,
|
|
7
|
+
BaseElementWithRefProps,
|
|
6
8
|
ElementDisplayName,
|
|
7
9
|
ElementRefType,
|
|
8
10
|
ReactElementProps,
|
|
@@ -40,7 +42,7 @@ export interface DefineBaseElementInput<T> {
|
|
|
40
42
|
* @param {DefineBaseElementInput} input `BaseElement` parameters
|
|
41
43
|
* @returns {BaseElement} `ElementsContext` aware UI component
|
|
42
44
|
*/
|
|
43
|
-
export
|
|
45
|
+
export function defineBaseElement<
|
|
44
46
|
// element type
|
|
45
47
|
T extends ReactElementType,
|
|
46
48
|
// string union of base element props to include
|
|
@@ -51,7 +53,53 @@ export default function defineBaseElement<
|
|
|
51
53
|
U extends ReactElementProps<T> = ReactElementProps<T>,
|
|
52
54
|
// control element props
|
|
53
55
|
P extends BaseElementProps<K, V, U> = BaseElementProps<K, V, U>,
|
|
54
|
-
>(input: DefineBaseElementInput<T>): BaseElement<P
|
|
56
|
+
>(input: DefineBaseElementInput<T>): BaseElement<P> {
|
|
57
|
+
const { displayName, type } = input;
|
|
58
|
+
|
|
59
|
+
const Element = ({ variant, ...props }: Omit<P, 'ref'>) => {
|
|
60
|
+
const Element = React.useContext(ElementsContext)?.[displayName];
|
|
61
|
+
|
|
62
|
+
if (Element) {
|
|
63
|
+
// only pass `variant` to provided `Element` values
|
|
64
|
+
return <Element {...{ ...props, variant }} />;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return React.createElement(type, props);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
Element.displayName = displayName;
|
|
71
|
+
|
|
72
|
+
return Element;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @internal @unstable
|
|
77
|
+
*
|
|
78
|
+
* Defines a `ElementsContext` aware `BaseElement` UI component of the
|
|
79
|
+
* provided `type` with an assigned `displayName` and element `ref`.
|
|
80
|
+
*
|
|
81
|
+
* If `BaseElement` is used as a child of an `ElementsProvider`, returns the
|
|
82
|
+
* `BaseElement` value of the provided `displayName` of `ElementsContext`.
|
|
83
|
+
*
|
|
84
|
+
* When used outside of a parent `ElementsProvider` or no `BaseElement`
|
|
85
|
+
* of `displayName` is found in the `ElementsContext`, returns a stateless,
|
|
86
|
+
* unstyled HTML element of the provided `type`.
|
|
87
|
+
*
|
|
88
|
+
* @param {DefineBaseElementInput} input `BaseElement` parameters
|
|
89
|
+
* @returns {BaseElementWithRefProps} `ElementsContext` aware UI component
|
|
90
|
+
*/
|
|
91
|
+
export function defineBaseElementWithRef<
|
|
92
|
+
// element type
|
|
93
|
+
T extends ReactElementType,
|
|
94
|
+
// string union of base element props to include
|
|
95
|
+
K extends keyof U = never,
|
|
96
|
+
// variant string union
|
|
97
|
+
V = string,
|
|
98
|
+
// available props of base element type
|
|
99
|
+
U extends ReactElementProps<T> = ReactElementProps<T>,
|
|
100
|
+
// control element props
|
|
101
|
+
P extends BaseElementWithRefProps<K, V, U> = BaseElementWithRefProps<K, V, U>,
|
|
102
|
+
>(input: DefineBaseElementInput<T>): BaseElementWithRef<P, ElementRefType<P>> {
|
|
55
103
|
const { displayName, type } = input;
|
|
56
104
|
|
|
57
105
|
const Element = React.forwardRef<ElementRefType<P>, P>(
|
package/src/elements/index.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './ControlsContext';
|
|
2
|
+
export {
|
|
3
|
+
defineBaseElement,
|
|
4
|
+
defineBaseElementWithRef,
|
|
5
|
+
} from './defineBaseElement';
|
|
2
6
|
export { default as withBaseElementProps } from './withBaseElementProps';
|
|
3
7
|
export { ElementsProvider } from './ElementsContext';
|
|
8
|
+
export { isComponent, isForwardRefExoticComponent } from './utils';
|
|
9
|
+
export * from './types';
|
package/src/elements/types.ts
CHANGED
|
@@ -13,16 +13,31 @@ import React from 'react';
|
|
|
13
13
|
* are always optional at the interface level, allowing for additional `props`
|
|
14
14
|
* to be added to existing `BaseElement` interfaces as needed.
|
|
15
15
|
*/
|
|
16
|
-
export type BaseElement<T = {}
|
|
16
|
+
export type BaseElement<T = {}> = (props: T) => React.JSX.Element;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @internal @unstable
|
|
20
|
+
*
|
|
21
|
+
* see @type {BaseElement}
|
|
22
|
+
*
|
|
23
|
+
* `BaseElement` with a `ref` corresponding to the `element` type
|
|
24
|
+
*/
|
|
25
|
+
export type BaseElementWithRef<
|
|
26
|
+
T = {},
|
|
27
|
+
K = {},
|
|
28
|
+
> = React.ForwardRefExoticComponent<
|
|
17
29
|
React.PropsWithoutRef<T> & React.RefAttributes<K>
|
|
18
30
|
>;
|
|
19
31
|
|
|
20
32
|
type ListElementSubType = 'Ordered' | 'Unordered';
|
|
21
|
-
type ListElementDisplayName =
|
|
33
|
+
type ListElementDisplayName = `${ListElementSubType}List`;
|
|
22
34
|
|
|
23
|
-
type TableElementSubType = 'Body' | '
|
|
35
|
+
type TableElementSubType = 'Body' | 'DataCell' | 'Row' | 'Head' | 'Header';
|
|
24
36
|
type TableElementDisplayName = 'Table' | `Table${TableElementSubType}`;
|
|
25
37
|
|
|
38
|
+
type DescriptionElementSubType = 'Details' | 'List' | 'Term';
|
|
39
|
+
type DescriptionElementDisplayName = `Description${DescriptionElementSubType}`;
|
|
40
|
+
|
|
26
41
|
/**
|
|
27
42
|
* @internal @unstable
|
|
28
43
|
*
|
|
@@ -30,7 +45,6 @@ type TableElementDisplayName = 'Table' | `Table${TableElementSubType}`;
|
|
|
30
45
|
*/
|
|
31
46
|
export type ElementDisplayName =
|
|
32
47
|
| 'Button'
|
|
33
|
-
| 'Divider'
|
|
34
48
|
| 'Heading' // h1, h2, etc
|
|
35
49
|
| 'Icon'
|
|
36
50
|
| 'Image'
|
|
@@ -44,6 +58,7 @@ export type ElementDisplayName =
|
|
|
44
58
|
| 'TextArea'
|
|
45
59
|
| 'Title'
|
|
46
60
|
| 'View'
|
|
61
|
+
| DescriptionElementDisplayName
|
|
47
62
|
| ListElementDisplayName
|
|
48
63
|
| TableElementDisplayName;
|
|
49
64
|
|
|
@@ -68,7 +83,7 @@ export type ReactElementType = keyof React.JSX.IntrinsicElements;
|
|
|
68
83
|
* @internal @unstable
|
|
69
84
|
*/
|
|
70
85
|
export type ReactElementProps<T extends ReactElementType> =
|
|
71
|
-
React.
|
|
86
|
+
React.ComponentProps<T>;
|
|
72
87
|
|
|
73
88
|
/**
|
|
74
89
|
* @internal @unstable
|
|
@@ -85,5 +100,98 @@ export type BaseElementProps<
|
|
|
85
100
|
V = string,
|
|
86
101
|
K extends Record<ElementPropKey<keyof K>, any> = Record<string, any>,
|
|
87
102
|
> = React.AriaAttributes &
|
|
88
|
-
React.
|
|
103
|
+
React.Attributes &
|
|
89
104
|
Pick<K, ElementPropKey<T>> & { testId?: string; variant?: V };
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @internal @unstable
|
|
108
|
+
*/
|
|
109
|
+
export type BaseElementWithRefProps<
|
|
110
|
+
T extends keyof K,
|
|
111
|
+
V = string,
|
|
112
|
+
K extends Record<ElementPropKey<keyof K>, any> = Record<string, any>,
|
|
113
|
+
> = BaseElementProps<T, V, K> & React.RefAttributes<ElementRefType<K>>;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @internal @unstable
|
|
117
|
+
*/
|
|
118
|
+
export type ElementWithAndWithoutRef<
|
|
119
|
+
T extends ReactElementType,
|
|
120
|
+
K extends React.ComponentType<React.ComponentProps<T>> = React.ComponentType<
|
|
121
|
+
React.ComponentProps<T>
|
|
122
|
+
>,
|
|
123
|
+
> = K extends React.ComponentType<infer U>
|
|
124
|
+
? React.ForwardRefExoticComponent<U>
|
|
125
|
+
: never;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @internal @unstable
|
|
129
|
+
*
|
|
130
|
+
* Merge `BaseElement` defintions with `elements` types provided by
|
|
131
|
+
* consumers, for use with top level connected component function
|
|
132
|
+
* signatures.
|
|
133
|
+
*
|
|
134
|
+
* Example:
|
|
135
|
+
*
|
|
136
|
+
* ```tsx
|
|
137
|
+
* export function createStorageBrowser<
|
|
138
|
+
* T extends Partial<StorageBrowserElements>,
|
|
139
|
+
* >({ elements }: CreateStorageBrowserInput<T> = {}): {
|
|
140
|
+
* StorageBrowser: StorageBrowser<MergeElements<StorageBrowserElements, T>>
|
|
141
|
+
* } {
|
|
142
|
+
* // ...do create stuff
|
|
143
|
+
* };
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export type MergeBaseElements<T, K extends Partial<T>> = {
|
|
147
|
+
[U in keyof T]: K[U] extends T[U] ? K[U] : T[U];
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @internal @unstable
|
|
152
|
+
*
|
|
153
|
+
* Extend the defintion of a `BaseElement` with additional `props`.
|
|
154
|
+
*
|
|
155
|
+
* Use cases are restricted to scenarios where additional `props`
|
|
156
|
+
* are required for a `ControlElement` interface, for example:
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```tsx
|
|
160
|
+
* const FieldInput = defineBaseElementWithRef({
|
|
161
|
+
* type: 'input',
|
|
162
|
+
* displayName: 'Input'
|
|
163
|
+
* });
|
|
164
|
+
*
|
|
165
|
+
* type InputWithSearchCallback =
|
|
166
|
+
* ExtendBaseElement<
|
|
167
|
+
* typeof FieldInput,
|
|
168
|
+
* { onSearch?: (event: { value: string }) => void }
|
|
169
|
+
* >
|
|
170
|
+
*
|
|
171
|
+
* const SearchInput = React.forwardRef((
|
|
172
|
+
* { onSearch, ...props }
|
|
173
|
+
* ref
|
|
174
|
+
* ) => {
|
|
175
|
+
* // ...do something with onSearch
|
|
176
|
+
*
|
|
177
|
+
* return <FieldInput {...props} ref={ref} />;
|
|
178
|
+
* });
|
|
179
|
+
* ```
|
|
180
|
+
*
|
|
181
|
+
* Caveats:
|
|
182
|
+
* - additional `props` should not be passed directly to
|
|
183
|
+
* `BaseElement` components, the outputted interface should be
|
|
184
|
+
* applied to a wrapping element that handles the additional `props`
|
|
185
|
+
*
|
|
186
|
+
* - additional `props` that share a key with existing `props`
|
|
187
|
+
* are omitted from the outputted interface to adhere to `BaseElement`
|
|
188
|
+
* type contracts
|
|
189
|
+
*
|
|
190
|
+
*/
|
|
191
|
+
export type ExtendBaseElement<
|
|
192
|
+
// `BaseElement` to extend
|
|
193
|
+
T extends React.ComponentType,
|
|
194
|
+
// additional `props`
|
|
195
|
+
K = {},
|
|
196
|
+
U extends React.ComponentPropsWithRef<T> = React.ComponentPropsWithRef<T>,
|
|
197
|
+
> = BaseElementWithRef<U & Omit<K, keyof U>, U>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export function isComponent<T>(
|
|
4
|
+
component?: React.ComponentType<T> | React.ForwardRefExoticComponent<T>
|
|
5
|
+
): component is React.ComponentType<T> {
|
|
6
|
+
return typeof component === 'function';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function isForwardRefExoticComponent<T>(
|
|
10
|
+
component: React.ComponentType<T> | React.ForwardRefExoticComponent<T>
|
|
11
|
+
): component is React.ForwardRefExoticComponent<T> {
|
|
12
|
+
return (
|
|
13
|
+
typeof component === 'object' &&
|
|
14
|
+
typeof (component as React.ForwardRefExoticComponent<T>).$$typeof ===
|
|
15
|
+
'symbol' &&
|
|
16
|
+
['react.memo', 'react.forward_ref'].includes(
|
|
17
|
+
(component as React.ForwardRefExoticComponent<T>).$$typeof.description!
|
|
18
|
+
)
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { BaseElementWithRef, ElementRefType } from './types';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @internal @unstable
|
|
@@ -16,7 +16,7 @@ import { BaseElement, ElementRefType } from './types';
|
|
|
16
16
|
* type InputElementPropKey = 'onChange' | 'type';
|
|
17
17
|
*
|
|
18
18
|
* // create `InputElement` base with `type` generic and extended `props` key
|
|
19
|
-
* export const InputElement =
|
|
19
|
+
* export const InputElement = defineBaseElementWithRef<"input", InputElementPropKey>({
|
|
20
20
|
* type: "input",
|
|
21
21
|
* displayName: "Input",
|
|
22
22
|
* });
|
|
@@ -38,7 +38,7 @@ export default function withBaseElementProps<
|
|
|
38
38
|
>(
|
|
39
39
|
Target: React.ForwardRefExoticComponent<T>,
|
|
40
40
|
defaultProps: K
|
|
41
|
-
):
|
|
41
|
+
): BaseElementWithRef<T, ElementRefType<T>> {
|
|
42
42
|
const Component = React.forwardRef<ElementRefType<T>, T>((props, ref) => (
|
|
43
43
|
<Target
|
|
44
44
|
{...{
|
package/src/hooks/index.ts
CHANGED