@cdx-ui/utils 0.0.1-beta.9 → 0.0.1-beta.91
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/README.md +47 -23
- package/lib/commonjs/common/mergeRefs.js +39 -1
- package/lib/commonjs/common/mergeRefs.js.map +1 -1
- package/lib/commonjs/context/createContext.js +7 -3
- package/lib/commonjs/context/createContext.js.map +1 -1
- package/lib/commonjs/form-control/index.js +6 -0
- package/lib/commonjs/form-control/index.js.map +1 -1
- package/lib/commonjs/form-control/useFormControl.js +33 -0
- package/lib/commonjs/form-control/useFormControl.js.map +1 -1
- package/lib/commonjs/style/index.js +0 -11
- package/lib/commonjs/style/index.js.map +1 -1
- package/lib/module/common/mergeRefs.js +39 -1
- package/lib/module/common/mergeRefs.js.map +1 -1
- package/lib/module/context/createContext.js +7 -3
- package/lib/module/context/createContext.js.map +1 -1
- package/lib/module/form-control/index.js +1 -1
- package/lib/module/form-control/index.js.map +1 -1
- package/lib/module/form-control/useFormControl.js +33 -0
- package/lib/module/form-control/useFormControl.js.map +1 -1
- package/lib/module/style/index.js +0 -1
- package/lib/module/style/index.js.map +1 -1
- package/lib/typescript/common/mergeRefs.d.ts +8 -0
- package/lib/typescript/common/mergeRefs.d.ts.map +1 -1
- package/lib/typescript/context/createContext.d.ts +16 -1
- package/lib/typescript/context/createContext.d.ts.map +1 -1
- package/lib/typescript/form-control/index.d.ts +1 -1
- package/lib/typescript/form-control/index.d.ts.map +1 -1
- package/lib/typescript/form-control/useFormControl.d.ts +28 -3
- package/lib/typescript/form-control/useFormControl.d.ts.map +1 -1
- package/lib/typescript/style/index.d.ts +0 -1
- package/lib/typescript/style/index.d.ts.map +1 -1
- package/package.json +4 -2
- package/src/common/accessibilityUtils.test.ts +12 -0
- package/src/common/cn.test.ts +19 -0
- package/src/common/composeEventHandlers.test.ts +35 -0
- package/src/common/getSpacedChild.test.tsx +64 -0
- package/src/common/mergeRefs.test.ts +148 -0
- package/src/common/mergeRefs.ts +45 -2
- package/src/common/useControllableState.test.tsx +90 -0
- package/src/context/createContext.test.tsx +31 -0
- package/src/context/createContext.tsx +21 -4
- package/src/form-control/index.ts +2 -0
- package/src/form-control/useFormControl.test.tsx +214 -0
- package/src/form-control/useFormControl.tsx +48 -4
- package/src/style/context/index.test.tsx +31 -0
- package/src/style/index.tsx +0 -1
- package/lib/commonjs/style/withStyleContext/index.js +0 -33
- package/lib/commonjs/style/withStyleContext/index.js.map +0 -1
- package/lib/module/style/withStyleContext/index.js +0 -28
- package/lib/module/style/withStyleContext/index.js.map +0 -1
- package/lib/typescript/style/withStyleContext/index.d.ts +0 -6
- package/lib/typescript/style/withStyleContext/index.d.ts.map +0 -1
- package/src/style/withStyleContext/index.tsx +0 -36
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { Platform, type TextInput } from 'react-native';
|
|
3
|
+
import { act, renderHook } from '@testing-library/react-native';
|
|
4
|
+
import {
|
|
5
|
+
FormControlContext,
|
|
6
|
+
useFormControl,
|
|
7
|
+
useFormControlContext,
|
|
8
|
+
useFormControlRoot,
|
|
9
|
+
useReportFormControlLabelFocus,
|
|
10
|
+
} from './useFormControl';
|
|
11
|
+
|
|
12
|
+
function FormControlTestProvider({
|
|
13
|
+
children,
|
|
14
|
+
rootProps,
|
|
15
|
+
hasFeedbackText = false,
|
|
16
|
+
hasHelpText = false,
|
|
17
|
+
}: {
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
rootProps?: Parameters<typeof useFormControlRoot>[0];
|
|
20
|
+
hasFeedbackText?: boolean;
|
|
21
|
+
hasHelpText?: boolean;
|
|
22
|
+
}) {
|
|
23
|
+
const root = useFormControlRoot(rootProps ?? { id: 'email' });
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<FormControlContext.Provider value={{ ...root, hasFeedbackText, hasHelpText }}>
|
|
27
|
+
{children}
|
|
28
|
+
</FormControlContext.Provider>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe('useFormControlRoot', () => {
|
|
33
|
+
it('uses provided id and coerces boolean state flags', () => {
|
|
34
|
+
const { result } = renderHook(() =>
|
|
35
|
+
useFormControlRoot({
|
|
36
|
+
id: 'custom-id',
|
|
37
|
+
isRequired: true,
|
|
38
|
+
isInvalid: true,
|
|
39
|
+
isDisabled: false,
|
|
40
|
+
isReadOnly: false,
|
|
41
|
+
name: 'email',
|
|
42
|
+
'data-test': 'root',
|
|
43
|
+
}),
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
expect(result.current.id).toBe('custom-id');
|
|
47
|
+
expect(result.current.labelId).toBe('custom-id-label');
|
|
48
|
+
expect(result.current.isRequired).toBe(true);
|
|
49
|
+
expect(result.current.isInvalid).toBe(true);
|
|
50
|
+
expect(result.current.isDisabled).toBe(false);
|
|
51
|
+
expect(result.current.isReadOnly).toBe(false);
|
|
52
|
+
expect(result.current.name).toBe('email');
|
|
53
|
+
expect(result.current.htmlProps).toEqual({ 'data-test': 'root' });
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('generates an id when none is provided', () => {
|
|
57
|
+
const { result } = renderHook(() => useFormControlRoot({}));
|
|
58
|
+
|
|
59
|
+
expect(result.current.id).toMatch(/^field-/);
|
|
60
|
+
expect(result.current.feedbackId).toBe(`${result.current.id}-feedback`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('ignores empty string id and generates a fallback', () => {
|
|
64
|
+
const { result } = renderHook(() => useFormControlRoot({ id: '' }));
|
|
65
|
+
|
|
66
|
+
expect(result.current.id).toMatch(/^field-/);
|
|
67
|
+
expect(result.current.id).not.toBe('');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('exposes native focus helpers by default', () => {
|
|
71
|
+
const { result } = renderHook(() => useFormControlRoot({}));
|
|
72
|
+
|
|
73
|
+
expect(result.current.inputRef).toBeDefined();
|
|
74
|
+
expect(result.current.focusInput).toBeDefined();
|
|
75
|
+
|
|
76
|
+
const { inputRef, focusInput } = result.current;
|
|
77
|
+
if (!inputRef || !focusInput) {
|
|
78
|
+
throw new Error('expected native focus helpers');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const focus = jest.fn();
|
|
82
|
+
act(() => {
|
|
83
|
+
inputRef.current = { focus } as TextInput;
|
|
84
|
+
focusInput();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(focus).toHaveBeenCalled();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('no-ops focusInput when ref is empty or focus is unavailable', () => {
|
|
91
|
+
const { result } = renderHook(() => useFormControlRoot({}));
|
|
92
|
+
const { focusInput, inputRef } = result.current;
|
|
93
|
+
|
|
94
|
+
if (!focusInput || !inputRef) {
|
|
95
|
+
throw new Error('expected native focus helpers');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
expect(() => {
|
|
99
|
+
focusInput();
|
|
100
|
+
}).not.toThrow();
|
|
101
|
+
|
|
102
|
+
act(() => {
|
|
103
|
+
inputRef.current = {} as TextInput;
|
|
104
|
+
focusInput();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
expect(inputRef.current).toEqual({});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('omits native focus helpers on web', () => {
|
|
111
|
+
const originalDescriptor = Object.getOwnPropertyDescriptor(Platform, 'OS');
|
|
112
|
+
Object.defineProperty(Platform, 'OS', { configurable: true, get: () => 'web' });
|
|
113
|
+
|
|
114
|
+
const { result } = renderHook(() => useFormControlRoot({}));
|
|
115
|
+
|
|
116
|
+
expect(result.current.inputRef).toBeUndefined();
|
|
117
|
+
expect(result.current.focusInput).toBeUndefined();
|
|
118
|
+
|
|
119
|
+
if (originalDescriptor) {
|
|
120
|
+
Object.defineProperty(Platform, 'OS', originalDescriptor);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('useFormControl', () => {
|
|
126
|
+
it('merges local props with context and composes aria-describedby', () => {
|
|
127
|
+
const { result } = renderHook(() => useFormControl({ id: 'override', isDisabled: true }), {
|
|
128
|
+
wrapper: ({ children }) => (
|
|
129
|
+
<FormControlTestProvider
|
|
130
|
+
rootProps={{
|
|
131
|
+
id: 'email',
|
|
132
|
+
name: 'email',
|
|
133
|
+
isRequired: true,
|
|
134
|
+
isInvalid: true,
|
|
135
|
+
isDisabled: false,
|
|
136
|
+
isReadOnly: false,
|
|
137
|
+
}}
|
|
138
|
+
hasFeedbackText
|
|
139
|
+
hasHelpText
|
|
140
|
+
>
|
|
141
|
+
{children}
|
|
142
|
+
</FormControlTestProvider>
|
|
143
|
+
),
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
expect(result.current.id).toBe('override');
|
|
147
|
+
expect(result.current.name).toBe('email');
|
|
148
|
+
expect(result.current.disabled).toBe(true);
|
|
149
|
+
expect(result.current.required).toBe(true);
|
|
150
|
+
expect(result.current['aria-invalid']).toBe(true);
|
|
151
|
+
expect(result.current['aria-labelledby']).toBe('email-label');
|
|
152
|
+
expect(result.current['aria-describedby']).toBe('email-feedback email-helptext');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('returns undefined aria-describedby when no help or feedback is present', () => {
|
|
156
|
+
const { result } = renderHook(() => useFormControl({}), {
|
|
157
|
+
wrapper: ({ children }) => <FormControlTestProvider>{children}</FormControlTestProvider>,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
expect(result.current['aria-describedby']).toBeUndefined();
|
|
161
|
+
expect(result.current.id).toBe('email-input');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('composes aria-describedby from feedback or help text alone', () => {
|
|
165
|
+
const feedbackOnly = renderHook(() => useFormControl({}), {
|
|
166
|
+
wrapper: ({ children }) => (
|
|
167
|
+
<FormControlTestProvider hasFeedbackText>{children}</FormControlTestProvider>
|
|
168
|
+
),
|
|
169
|
+
}).result.current['aria-describedby'];
|
|
170
|
+
|
|
171
|
+
const helpOnly = renderHook(() => useFormControl({}), {
|
|
172
|
+
wrapper: ({ children }) => (
|
|
173
|
+
<FormControlTestProvider hasHelpText>{children}</FormControlTestProvider>
|
|
174
|
+
),
|
|
175
|
+
}).result.current['aria-describedby'];
|
|
176
|
+
|
|
177
|
+
expect(feedbackOnly).toBe('email-feedback');
|
|
178
|
+
expect(helpOnly).toBe('email-helptext');
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('merges readOnly from local props and omits id without field context', () => {
|
|
182
|
+
const { result } = renderHook(() => useFormControl({ isReadOnly: true }));
|
|
183
|
+
|
|
184
|
+
expect(result.current.readOnly).toBe(true);
|
|
185
|
+
expect(result.current.id).toBeUndefined();
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('useReportFormControlLabelFocus', () => {
|
|
190
|
+
it('reports active state to the form control context', () => {
|
|
191
|
+
const { result, rerender } = renderHook(
|
|
192
|
+
({ active }: { active: boolean }) => {
|
|
193
|
+
useReportFormControlLabelFocus(active);
|
|
194
|
+
return useFormControlContext().isLabelFocused;
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
wrapper: ({ children }) => <FormControlTestProvider>{children}</FormControlTestProvider>,
|
|
198
|
+
initialProps: { active: true },
|
|
199
|
+
},
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
expect(result.current).toBe(true);
|
|
203
|
+
|
|
204
|
+
rerender({ active: false });
|
|
205
|
+
|
|
206
|
+
expect(result.current).toBe(false);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('no-ops when setIsLabelFocused is unavailable', () => {
|
|
210
|
+
expect(() => {
|
|
211
|
+
renderHook(() => useReportFormControlLabelFocus(true));
|
|
212
|
+
}).not.toThrow();
|
|
213
|
+
});
|
|
214
|
+
});
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Platform
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
3
|
import { ariaAttr } from '../common/accessibilityUtils';
|
|
4
4
|
|
|
5
5
|
type HTMLProps = Record<string, unknown>;
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Minimal contract for any control registered on `FormControlContextValue.inputRef`.
|
|
9
|
+
*
|
|
10
|
+
* `Field.Label` / `focusInput()` only need to call `.focus()` on the registered node;
|
|
11
|
+
* narrower control-specific shapes (`TextInput`, `HTMLInputElement`, custom focus
|
|
12
|
+
* bridges like `Select.Trigger`'s) all satisfy this contract. Consumers that need
|
|
13
|
+
* more (e.g. reading `.value`) should cast locally — keeping the context type
|
|
14
|
+
* minimal avoids a typed lie when a non-input bridge is registered.
|
|
15
|
+
*/
|
|
16
|
+
export interface Focusable {
|
|
17
|
+
focus(): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
7
20
|
export interface FormControlState {
|
|
8
21
|
isRequired: boolean;
|
|
9
22
|
isInvalid: boolean;
|
|
@@ -25,9 +38,16 @@ export interface FormControlContextValue extends FormControlState {
|
|
|
25
38
|
/**
|
|
26
39
|
* @platform native — set by `Input.Field` / `Select.Trigger` for label press-to-focus.
|
|
27
40
|
* On web, use `<label htmlFor>` with a labelable control (`input`, `button`, `select`, …).
|
|
41
|
+
*
|
|
42
|
+
* Typed as `Focusable` so any control (TextInput, HTMLInputElement, custom focus
|
|
43
|
+
* bridge) can register without a cast at the assignment site. Consumers that need
|
|
44
|
+
* the narrower shape cast locally.
|
|
28
45
|
*/
|
|
29
|
-
inputRef?: React.RefObject<
|
|
46
|
+
inputRef?: React.RefObject<Focusable | null>;
|
|
30
47
|
focusInput?: () => void;
|
|
48
|
+
/** True when the primary child control is focused or (for Select) open — drives `Field.Label` styling. */
|
|
49
|
+
isLabelFocused?: boolean;
|
|
50
|
+
setIsLabelFocused?: React.Dispatch<React.SetStateAction<boolean>>;
|
|
31
51
|
}
|
|
32
52
|
|
|
33
53
|
export const FormControlContext = React.createContext<Partial<FormControlContextValue>>({});
|
|
@@ -57,8 +77,10 @@ export interface FormControlRootReturn {
|
|
|
57
77
|
feedbackId: string;
|
|
58
78
|
helpTextId: string;
|
|
59
79
|
htmlProps: HTMLProps;
|
|
60
|
-
inputRef?: React.RefObject<
|
|
80
|
+
inputRef?: React.RefObject<Focusable | null>;
|
|
61
81
|
focusInput?: () => void;
|
|
82
|
+
isLabelFocused: boolean;
|
|
83
|
+
setIsLabelFocused: React.Dispatch<React.SetStateAction<boolean>>;
|
|
62
84
|
}
|
|
63
85
|
|
|
64
86
|
/**
|
|
@@ -87,7 +109,9 @@ export function useFormControlRoot(props: FormControlRootProps): FormControlRoot
|
|
|
87
109
|
*/
|
|
88
110
|
const [hasHelpText, setHasHelpText] = React.useState(false);
|
|
89
111
|
|
|
90
|
-
const
|
|
112
|
+
const [isLabelFocused, setIsLabelFocused] = React.useState(false);
|
|
113
|
+
|
|
114
|
+
const inputRef = React.useRef<Focusable | null>(null);
|
|
91
115
|
|
|
92
116
|
const focusInput = React.useCallback(() => {
|
|
93
117
|
const node = inputRef.current;
|
|
@@ -111,12 +135,32 @@ export function useFormControlRoot(props: FormControlRootProps): FormControlRoot
|
|
|
111
135
|
feedbackId,
|
|
112
136
|
helpTextId,
|
|
113
137
|
htmlProps,
|
|
138
|
+
isLabelFocused,
|
|
139
|
+
setIsLabelFocused,
|
|
114
140
|
...(Platform.OS !== 'web' ? { inputRef, focusInput } : {}),
|
|
115
141
|
};
|
|
116
142
|
|
|
117
143
|
return context;
|
|
118
144
|
}
|
|
119
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Reports focus/active state from `Input` / `Select.Trigger` to the parent `Field` for label styling.
|
|
148
|
+
* No-ops outside a `Field` (no `setIsLabelFocused` on context).
|
|
149
|
+
*/
|
|
150
|
+
export function useReportFormControlLabelFocus(isActive: boolean) {
|
|
151
|
+
const { setIsLabelFocused } = useFormControlContext();
|
|
152
|
+
|
|
153
|
+
React.useLayoutEffect(() => {
|
|
154
|
+
if (typeof setIsLabelFocused !== 'function') {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
setIsLabelFocused(isActive);
|
|
158
|
+
return () => {
|
|
159
|
+
setIsLabelFocused(false);
|
|
160
|
+
};
|
|
161
|
+
}, [isActive, setIsLabelFocused]);
|
|
162
|
+
}
|
|
163
|
+
|
|
120
164
|
/**
|
|
121
165
|
* Hook that merges local field props with the nearest form field context
|
|
122
166
|
* and returns normalised HTML / ARIA attributes for input elements.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { renderHook } from '@testing-library/react-native';
|
|
3
|
+
import { ParentContext, useParentContext, useStyleContext } from './index';
|
|
4
|
+
|
|
5
|
+
describe('style context hooks', () => {
|
|
6
|
+
it('reads parent context values', () => {
|
|
7
|
+
const wrapper = ({ children }: { children: ReactNode }) => (
|
|
8
|
+
<ParentContext.Provider value={{ Button: { size: 'lg' }, Card: { padded: true } }}>
|
|
9
|
+
{children}
|
|
10
|
+
</ParentContext.Provider>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const { result: parentResult } = renderHook(() => useParentContext(), { wrapper });
|
|
14
|
+
const { result: styleResult } = renderHook(() => useStyleContext('Button'), { wrapper });
|
|
15
|
+
|
|
16
|
+
expect(parentResult.current).toEqual({ Button: { size: 'lg' }, Card: { padded: true } });
|
|
17
|
+
expect(styleResult.current).toEqual({ size: 'lg' });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('defaults to Global scope', () => {
|
|
21
|
+
const wrapper = ({ children }: { children: ReactNode }) => (
|
|
22
|
+
<ParentContext.Provider value={{ Global: { theme: 'dark' } }}>
|
|
23
|
+
{children}
|
|
24
|
+
</ParentContext.Provider>
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const { result } = renderHook(() => useStyleContext(), { wrapper });
|
|
28
|
+
|
|
29
|
+
expect(result.current).toEqual({ theme: 'dark' });
|
|
30
|
+
});
|
|
31
|
+
});
|
package/src/style/index.tsx
CHANGED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.withStyleContext = void 0;
|
|
7
|
-
var _react = require("react");
|
|
8
|
-
var _context = require("../context");
|
|
9
|
-
var _jsxRuntime = require("react/jsx-runtime");
|
|
10
|
-
const withStyleContext = (Component, scope = 'Global') => {
|
|
11
|
-
const Wrapped = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
12
|
-
context,
|
|
13
|
-
...props
|
|
14
|
-
}, ref) => {
|
|
15
|
-
const parentContextValues = (0, _context.useParentContext)();
|
|
16
|
-
const contextValues = (0, _react.useMemo)(() => {
|
|
17
|
-
return {
|
|
18
|
-
...parentContextValues,
|
|
19
|
-
[scope]: context
|
|
20
|
-
};
|
|
21
|
-
}, [parentContextValues, context]);
|
|
22
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_context.ParentContext.Provider, {
|
|
23
|
-
value: contextValues,
|
|
24
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(Component, {
|
|
25
|
-
...props,
|
|
26
|
-
ref: ref
|
|
27
|
-
})
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
return Wrapped;
|
|
31
|
-
};
|
|
32
|
-
exports.withStyleContext = withStyleContext;
|
|
33
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["_react","require","_context","_jsxRuntime","withStyleContext","Component","scope","Wrapped","forwardRef","context","props","ref","parentContextValues","useParentContext","contextValues","useMemo","jsx","ParentContext","Provider","value","children","exports"],"sourceRoot":"../../../../src","sources":["style/withStyleContext/index.tsx"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AASA,IAAAC,QAAA,GAAAD,OAAA;AAAmF,IAAAE,WAAA,GAAAF,OAAA;AAM5E,MAAMG,gBAAgB,GAAGA,CAAwBC,SAAY,EAAEC,KAAK,GAAG,QAAQ,KAAK;EAIzF,MAAMC,OAAO,gBAAG,IAAAC,iBAAU,EAAa,CAAC;IAAEC,OAAO;IAAE,GAAGC;EAAM,CAAC,EAAEC,GAAG,KAAK;IACrE,MAAMC,mBAAmB,GAAG,IAAAC,yBAAgB,EAAC,CAAC;IAE9C,MAAMC,aAA8B,GAAG,IAAAC,cAAO,EAAC,MAAM;MACnD,OAAO;QAAE,GAAGH,mBAAmB;QAAE,CAACN,KAAK,GAAGG;MAAQ,CAAC;IACrD,CAAC,EAAE,CAACG,mBAAmB,EAAEH,OAAO,CAAC,CAAC;IAElC,oBACE,IAAAN,WAAA,CAAAa,GAAA,EAACd,QAAA,CAAAe,aAAa,CAACC,QAAQ;MAACC,KAAK,EAAEL,aAAc;MAAAM,QAAA,eAE3C,IAAAjB,WAAA,CAAAa,GAAA,EAACX,SAAS;QAAA,GAAKK,KAAK;QAAEC,GAAG,EAAEA;MAAI,CAAE;IAAC,CACZ,CAAC;EAE7B,CAAC,CAAC;EAEF,OAAOJ,OAAO;AAChB,CAAC;AAACc,OAAA,CAAAjB,gBAAA,GAAAA,gBAAA","ignoreList":[]}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { forwardRef, useMemo } from 'react';
|
|
4
|
-
import { ParentContext, useParentContext } from '../context';
|
|
5
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
6
|
-
export const withStyleContext = (Component, scope = 'Global') => {
|
|
7
|
-
const Wrapped = /*#__PURE__*/forwardRef(({
|
|
8
|
-
context,
|
|
9
|
-
...props
|
|
10
|
-
}, ref) => {
|
|
11
|
-
const parentContextValues = useParentContext();
|
|
12
|
-
const contextValues = useMemo(() => {
|
|
13
|
-
return {
|
|
14
|
-
...parentContextValues,
|
|
15
|
-
[scope]: context
|
|
16
|
-
};
|
|
17
|
-
}, [parentContextValues, context]);
|
|
18
|
-
return /*#__PURE__*/_jsx(ParentContext.Provider, {
|
|
19
|
-
value: contextValues,
|
|
20
|
-
children: /*#__PURE__*/_jsx(Component, {
|
|
21
|
-
...props,
|
|
22
|
-
ref: ref
|
|
23
|
-
})
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
return Wrapped;
|
|
27
|
-
};
|
|
28
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["forwardRef","useMemo","ParentContext","useParentContext","jsx","_jsx","withStyleContext","Component","scope","Wrapped","context","props","ref","parentContextValues","contextValues","Provider","value","children"],"sourceRoot":"../../../../src","sources":["style/withStyleContext/index.tsx"],"mappings":";;AAAA,SAMEA,UAAU,EACVC,OAAO,QACF,OAAO;AACd,SAASC,aAAa,EAAwBC,gBAAgB,QAAQ,YAAY;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAMnF,OAAO,MAAMC,gBAAgB,GAAGA,CAAwBC,SAAY,EAAEC,KAAK,GAAG,QAAQ,KAAK;EAIzF,MAAMC,OAAO,gBAAGT,UAAU,CAAa,CAAC;IAAEU,OAAO;IAAE,GAAGC;EAAM,CAAC,EAAEC,GAAG,KAAK;IACrE,MAAMC,mBAAmB,GAAGV,gBAAgB,CAAC,CAAC;IAE9C,MAAMW,aAA8B,GAAGb,OAAO,CAAC,MAAM;MACnD,OAAO;QAAE,GAAGY,mBAAmB;QAAE,CAACL,KAAK,GAAGE;MAAQ,CAAC;IACrD,CAAC,EAAE,CAACG,mBAAmB,EAAEH,OAAO,CAAC,CAAC;IAElC,oBACEL,IAAA,CAACH,aAAa,CAACa,QAAQ;MAACC,KAAK,EAAEF,aAAc;MAAAG,QAAA,eAE3CZ,IAAA,CAACE,SAAS;QAAA,GAAKI,KAAK;QAAEC,GAAG,EAAEA;MAAI,CAAE;IAAC,CACZ,CAAC;EAE7B,CAAC,CAAC;EAEF,OAAOH,OAAO;AAChB,CAAC","ignoreList":[]}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { type ComponentPropsWithoutRef, type ComponentRef, type ElementType, type ForwardRefExoticComponent, type RefAttributes } from 'react';
|
|
2
|
-
export interface WithStyleContextProps {
|
|
3
|
-
context?: Record<string, unknown>;
|
|
4
|
-
}
|
|
5
|
-
export declare const withStyleContext: <T extends ElementType>(Component: T, scope?: string) => ForwardRefExoticComponent<(ComponentPropsWithoutRef<T> & WithStyleContextProps) & RefAttributes<ComponentRef<T>>>;
|
|
6
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/style/withStyleContext/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,yBAAyB,EAC9B,KAAK,aAAa,EAGnB,MAAM,OAAO,CAAC;AAGf,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,WAAW,EAAE,WAAW,CAAC,EAAE,cAAgB,KAmBlE,yBAAyB,CAAC,wDAAQ,aAAa,iBAAK,CACvE,CAAC"}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type ComponentPropsWithoutRef,
|
|
3
|
-
type ComponentRef,
|
|
4
|
-
type ElementType,
|
|
5
|
-
type ForwardRefExoticComponent,
|
|
6
|
-
type RefAttributes,
|
|
7
|
-
forwardRef,
|
|
8
|
-
useMemo,
|
|
9
|
-
} from 'react';
|
|
10
|
-
import { ParentContext, type StyleContextMap, useParentContext } from '../context';
|
|
11
|
-
|
|
12
|
-
export interface WithStyleContextProps {
|
|
13
|
-
context?: Record<string, unknown>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const withStyleContext = <T extends ElementType>(Component: T, scope = 'Global') => {
|
|
17
|
-
type Ref = ComponentRef<T>;
|
|
18
|
-
type Props = ComponentPropsWithoutRef<T> & WithStyleContextProps;
|
|
19
|
-
|
|
20
|
-
const Wrapped = forwardRef<Ref, Props>(({ context, ...props }, ref) => {
|
|
21
|
-
const parentContextValues = useParentContext();
|
|
22
|
-
|
|
23
|
-
const contextValues: StyleContextMap = useMemo(() => {
|
|
24
|
-
return { ...parentContextValues, [scope]: context };
|
|
25
|
-
}, [parentContextValues, context]);
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<ParentContext.Provider value={contextValues}>
|
|
29
|
-
{/* @ts-expect-error -- generic component spread is not fully inferrable */}
|
|
30
|
-
<Component {...props} ref={ref} />
|
|
31
|
-
</ParentContext.Provider>
|
|
32
|
-
);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
return Wrapped as ForwardRefExoticComponent<Props & RefAttributes<Ref>>;
|
|
36
|
-
};
|