@aws-amplify/ui-react-core 3.0.24 → 3.0.26
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 +2 -2
- package/dist/esm/elements.mjs +3 -0
- package/dist/esm/hooks/useDropZone.mjs +65 -0
- package/dist/esm/hooks/useTimeout.mjs +2 -2
- package/dist/esm/index.mjs +1 -0
- package/dist/esm/utils/filterAllowedFiles.mjs +34 -0
- package/dist/index.js +120 -29
- package/dist/types/hooks/index.d.ts +1 -0
- package/dist/types/hooks/useDropZone.d.ts +25 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/utils/filterAllowedFiles.d.ts +10 -0
- package/elements/package.json +1 -1
- package/package.json +5 -5
- package/src/components/FormCore/useControlledField.ts +3 -3
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useDropZone.ts +110 -0
- package/src/hooks/useTimeout.ts +2 -2
- package/src/index.ts +2 -0
- package/src/utils/filterAllowedFiles.ts +50 -0
- package/dist/esm/elements/elements.mjs +0 -3
package/dist/elements.js
CHANGED
|
@@ -4,9 +4,9 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var React = require('react');
|
|
6
6
|
|
|
7
|
-
function
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
8
|
|
|
9
|
-
var React__default = /*#__PURE__*/
|
|
9
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
10
10
|
|
|
11
11
|
const ElementsContext = React__default["default"].createContext(undefined);
|
|
12
12
|
/**
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { isFunction } from '@aws-amplify/ui';
|
|
3
|
+
import { filterAllowedFiles } from '../utils/filterAllowedFiles.mjs';
|
|
4
|
+
|
|
5
|
+
function useDropZone({ onDropComplete, onDragEnter: _onDragEnter, onDragLeave: _onDragLeave, onDragOver: _onDragOver, onDragStart: _onDragStart, onDrop: _onDrop, acceptedFileTypes = [], }) {
|
|
6
|
+
const [dragState, setDragState] = useState('inactive');
|
|
7
|
+
const onDragStart = (event) => {
|
|
8
|
+
event.dataTransfer.clearData();
|
|
9
|
+
if (isFunction(_onDragStart)) {
|
|
10
|
+
_onDragStart(event);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const onDragEnter = (event) => {
|
|
14
|
+
event.preventDefault();
|
|
15
|
+
event.stopPropagation();
|
|
16
|
+
if (isFunction(_onDragEnter)) {
|
|
17
|
+
_onDragEnter(event);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const onDragLeave = (event) => {
|
|
21
|
+
event.preventDefault();
|
|
22
|
+
event.stopPropagation();
|
|
23
|
+
setDragState('inactive');
|
|
24
|
+
if (isFunction(_onDragLeave)) {
|
|
25
|
+
_onDragLeave(event);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const onDragOver = (event) => {
|
|
29
|
+
event.preventDefault();
|
|
30
|
+
event.stopPropagation();
|
|
31
|
+
event.dataTransfer.dropEffect = 'copy';
|
|
32
|
+
if (isFunction(_onDragOver)) {
|
|
33
|
+
_onDragOver(event);
|
|
34
|
+
}
|
|
35
|
+
const files = Array.from(event.dataTransfer.items).map(({ kind, type }) => ({
|
|
36
|
+
kind,
|
|
37
|
+
type,
|
|
38
|
+
}));
|
|
39
|
+
const { rejectedFiles } = filterAllowedFiles(files, acceptedFileTypes);
|
|
40
|
+
setDragState(rejectedFiles.length > 0 ? 'reject' : 'accept');
|
|
41
|
+
};
|
|
42
|
+
const onDrop = (event) => {
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
event.stopPropagation();
|
|
45
|
+
setDragState('inactive');
|
|
46
|
+
const files = Array.from(event.dataTransfer.files);
|
|
47
|
+
const { acceptedFiles, rejectedFiles } = filterAllowedFiles(files, acceptedFileTypes);
|
|
48
|
+
if (isFunction(_onDrop)) {
|
|
49
|
+
_onDrop(event);
|
|
50
|
+
}
|
|
51
|
+
if (isFunction(onDropComplete)) {
|
|
52
|
+
onDropComplete({ acceptedFiles, rejectedFiles });
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
return {
|
|
56
|
+
onDragStart,
|
|
57
|
+
onDragEnter,
|
|
58
|
+
onDragLeave,
|
|
59
|
+
onDragOver,
|
|
60
|
+
onDrop,
|
|
61
|
+
dragState,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { useDropZone as default };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { isFunction } from '@aws-amplify/ui';
|
|
3
3
|
|
|
4
4
|
function useTimeout({ callback, delay, }) {
|
|
5
5
|
const storedCallback = React__default.useRef(callback);
|
|
@@ -7,7 +7,7 @@ function useTimeout({ callback, delay, }) {
|
|
|
7
7
|
storedCallback.current = callback;
|
|
8
8
|
}, [callback]);
|
|
9
9
|
React__default.useEffect(() => {
|
|
10
|
-
if (!
|
|
10
|
+
if (!isFunction(storedCallback.current) || !delay) {
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
13
|
const timeoutId = setTimeout(() => {
|
package/dist/esm/index.mjs
CHANGED
|
@@ -16,4 +16,5 @@ export { default as useHasValueUpdated } from './hooks/useHasValueUpdated.mjs';
|
|
|
16
16
|
export { default as usePreviousValue } from './hooks/usePreviousValue.mjs';
|
|
17
17
|
export { default as useSetUserAgent } from './hooks/useSetUserAgent.mjs';
|
|
18
18
|
export { default as useTimeout } from './hooks/useTimeout.mjs';
|
|
19
|
+
export { default as useDropZone } from './hooks/useDropZone.mjs';
|
|
19
20
|
export { default as createContextUtilities } from './utils/createContextUtilities.mjs';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
function filterAllowedFiles(files, acceptedFileTypes) {
|
|
2
|
+
// Allow any files if acceptedFileTypes is undefined, empty array, or contains '*'
|
|
3
|
+
if (!acceptedFileTypes ||
|
|
4
|
+
acceptedFileTypes.length === 0 ||
|
|
5
|
+
acceptedFileTypes.includes('*')) {
|
|
6
|
+
return { acceptedFiles: files, rejectedFiles: [] };
|
|
7
|
+
}
|
|
8
|
+
const acceptedFiles = [];
|
|
9
|
+
const rejectedFiles = [];
|
|
10
|
+
function filterFile(file) {
|
|
11
|
+
const { type = '', name = '' } = file;
|
|
12
|
+
const mimeType = type.toLowerCase();
|
|
13
|
+
const baseMimeType = mimeType.split('/')[0];
|
|
14
|
+
return acceptedFileTypes.some((type) => {
|
|
15
|
+
const validType = type.trim().toLowerCase();
|
|
16
|
+
// if the accepted file type is a file extension
|
|
17
|
+
// it will start with '.', check against the file name
|
|
18
|
+
if (validType.charAt(0) === '.') {
|
|
19
|
+
return name.toLowerCase().endsWith(validType);
|
|
20
|
+
}
|
|
21
|
+
// This is something like a image/* mime type
|
|
22
|
+
if (validType.endsWith('/*')) {
|
|
23
|
+
return baseMimeType === validType.split('/')[0];
|
|
24
|
+
}
|
|
25
|
+
return mimeType === validType;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
files.forEach((file) => {
|
|
29
|
+
(filterFile(file) ? acceptedFiles : rejectedFiles).push(file);
|
|
30
|
+
});
|
|
31
|
+
return { acceptedFiles, rejectedFiles };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { filterAllowedFiles };
|
package/dist/index.js
CHANGED
|
@@ -9,8 +9,6 @@ var ui = require('@aws-amplify/ui');
|
|
|
9
9
|
var reactHookForm = require('react-hook-form');
|
|
10
10
|
var storage = require('aws-amplify/storage');
|
|
11
11
|
|
|
12
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
13
|
-
|
|
14
12
|
function _interopNamespace(e) {
|
|
15
13
|
if (e && e.__esModule) return e;
|
|
16
14
|
var n = Object.create(null);
|
|
@@ -29,7 +27,6 @@ function _interopNamespace(e) {
|
|
|
29
27
|
return Object.freeze(n);
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
33
30
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
34
31
|
|
|
35
32
|
/**
|
|
@@ -37,7 +34,7 @@ var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
|
37
34
|
*
|
|
38
35
|
* https://xstate.js.org/docs/recipes/react.html#context-provider
|
|
39
36
|
*/
|
|
40
|
-
const AuthenticatorContext =
|
|
37
|
+
const AuthenticatorContext = React__namespace["default"].createContext(null);
|
|
41
38
|
|
|
42
39
|
const createHubHandler = (options) => (data, service) => {
|
|
43
40
|
ui.defaultAuthHubHandler(data, service, options);
|
|
@@ -47,9 +44,9 @@ function AuthenticatorProvider({ children, }) {
|
|
|
47
44
|
// state machine as the machine only updates on `Authenticator` initiated events, which
|
|
48
45
|
// leads to scenarios where the state machine `authStatus` gets "stuck". For exmample,
|
|
49
46
|
// if a user was to sign in using `Auth.signIn` directly rather than using `Authenticator`
|
|
50
|
-
const [authStatus, setAuthStatus] =
|
|
47
|
+
const [authStatus, setAuthStatus] = React__namespace["default"].useState('configuring');
|
|
51
48
|
// only run on first render
|
|
52
|
-
|
|
49
|
+
React__namespace["default"].useEffect(() => {
|
|
53
50
|
auth.getCurrentUser()
|
|
54
51
|
.then(() => {
|
|
55
52
|
setAuthStatus('authenticated');
|
|
@@ -79,7 +76,7 @@ function AuthenticatorProvider({ children, }) {
|
|
|
79
76
|
const unsubscribe = ui.listenToAuthHub(activeService, createHubHandler({ onSignIn, onSignOut }));
|
|
80
77
|
return unsubscribe;
|
|
81
78
|
}, [activeService]);
|
|
82
|
-
return (
|
|
79
|
+
return (React__namespace["default"].createElement(AuthenticatorContext.Provider, { value: value }, children));
|
|
83
80
|
}
|
|
84
81
|
|
|
85
82
|
const USE_AUTHENTICATOR_ERROR = '`useAuthenticator` must be used inside an `Authenticator.Provider`.';
|
|
@@ -185,7 +182,7 @@ const getMachineFields = (route, state, unverifiedUserAttributes) => {
|
|
|
185
182
|
* [📖 Docs](https://ui.docs.amplify.aws/react/connected-components/authenticator/headless#useauthenticator-hook)
|
|
186
183
|
*/
|
|
187
184
|
function useAuthenticator(selector) {
|
|
188
|
-
const context =
|
|
185
|
+
const context = React__namespace["default"].useContext(AuthenticatorContext);
|
|
189
186
|
if (!context) {
|
|
190
187
|
throw new Error(USE_AUTHENTICATOR_ERROR);
|
|
191
188
|
}
|
|
@@ -243,13 +240,13 @@ function useForm(options = {}) {
|
|
|
243
240
|
};
|
|
244
241
|
// memoize `registerField` and `setFormValue` together,
|
|
245
242
|
// `register` and `setValue` maintain stable references
|
|
246
|
-
const { registerField, setFormValue } =
|
|
243
|
+
const { registerField, setFormValue } = React__namespace["default"].useMemo(() => {
|
|
247
244
|
return {
|
|
248
245
|
registerField: ({ name, ...options }) => register(name, options),
|
|
249
246
|
setFormValue: ({ name, value, ...options }) => setValue(name, value, options),
|
|
250
247
|
};
|
|
251
248
|
}, [register, setValue]);
|
|
252
|
-
const onSubmit =
|
|
249
|
+
const onSubmit = React__namespace["default"].useCallback((event) => {
|
|
253
250
|
const handler = _onSubmit ? handleSubmit(_onSubmit) : ui.noop;
|
|
254
251
|
handler(event);
|
|
255
252
|
}, [_onSubmit, handleSubmit]);
|
|
@@ -282,14 +279,14 @@ function useField(params) {
|
|
|
282
279
|
}
|
|
283
280
|
|
|
284
281
|
const DEFAULT_MODE = 'onTouched';
|
|
285
|
-
const FormProvider =
|
|
282
|
+
const FormProvider = React__namespace["default"].forwardRef(function FormProvider({ children, defaultValues, mode = DEFAULT_MODE }, ref) {
|
|
286
283
|
const formProviderProps = reactHookForm.useForm({
|
|
287
284
|
defaultValues,
|
|
288
285
|
mode,
|
|
289
286
|
});
|
|
290
287
|
const { getValues, reset } = formProviderProps;
|
|
291
|
-
|
|
292
|
-
return (
|
|
288
|
+
React__namespace["default"].useImperativeHandle(ref, () => ({ getValues, reset: () => reset(defaultValues) }), [defaultValues, getValues, reset]);
|
|
289
|
+
return (React__namespace["default"].createElement(reactHookForm.FormProvider, { ...formProviderProps }, children));
|
|
293
290
|
});
|
|
294
291
|
|
|
295
292
|
/**
|
|
@@ -297,9 +294,9 @@ const FormProvider = React__default["default"].forwardRef(function FormProvider(
|
|
|
297
294
|
* @returns Composed `Form` component exposing `FormContext` values to descendents
|
|
298
295
|
*/
|
|
299
296
|
function withFormProvider(Child) {
|
|
300
|
-
return
|
|
301
|
-
return (
|
|
302
|
-
|
|
297
|
+
return React__namespace["default"].forwardRef(function Form({ defaultValues, mode, ...props }, ref) {
|
|
298
|
+
return (React__namespace["default"].createElement(FormProvider, { defaultValues: defaultValues, mode: mode, ref: ref },
|
|
299
|
+
React__namespace["default"].createElement(Child, { ...props })));
|
|
303
300
|
});
|
|
304
301
|
}
|
|
305
302
|
|
|
@@ -549,8 +546,8 @@ function useAuthenticatorRoute({ components, }) {
|
|
|
549
546
|
const routeSelector = ({ route }) => [route];
|
|
550
547
|
function useAuthenticatorInitMachine(data) {
|
|
551
548
|
const { route, initializeMachine } = useAuthenticator(routeSelector);
|
|
552
|
-
const hasInitialized =
|
|
553
|
-
|
|
549
|
+
const hasInitialized = React__namespace["default"].useRef(false);
|
|
550
|
+
React__namespace["default"].useEffect(() => {
|
|
554
551
|
if (!hasInitialized.current && route === 'setup') {
|
|
555
552
|
initializeMachine(data);
|
|
556
553
|
hasInitialized.current = true;
|
|
@@ -567,12 +564,12 @@ const resolveMaybeAsync = async (value) => {
|
|
|
567
564
|
return awaited;
|
|
568
565
|
};
|
|
569
566
|
function useDataState(action, initialData) {
|
|
570
|
-
const [dataState, setDataState] =
|
|
567
|
+
const [dataState, setDataState] = React__namespace["default"].useState(() => ({
|
|
571
568
|
...INITIAL_STATE,
|
|
572
569
|
data: initialData,
|
|
573
570
|
}));
|
|
574
|
-
const prevData =
|
|
575
|
-
const handleAction =
|
|
571
|
+
const prevData = React__namespace["default"].useRef(initialData);
|
|
572
|
+
const handleAction = React__namespace["default"].useCallback((...input) => {
|
|
576
573
|
setDataState(({ data }) => ({ ...LOADING_STATE, data }));
|
|
577
574
|
resolveMaybeAsync(action(prevData.current, ...input))
|
|
578
575
|
.then((data) => {
|
|
@@ -679,12 +676,12 @@ function useSetUserAgent({ componentName, packageName, version, }) {
|
|
|
679
676
|
}
|
|
680
677
|
|
|
681
678
|
function useTimeout({ callback, delay, }) {
|
|
682
|
-
const storedCallback =
|
|
683
|
-
|
|
679
|
+
const storedCallback = React__namespace["default"].useRef(callback);
|
|
680
|
+
React__namespace["default"].useLayoutEffect(() => {
|
|
684
681
|
storedCallback.current = callback;
|
|
685
682
|
}, [callback]);
|
|
686
|
-
|
|
687
|
-
if (!ui.
|
|
683
|
+
React__namespace["default"].useEffect(() => {
|
|
684
|
+
if (!ui.isFunction(storedCallback.current) || !delay) {
|
|
688
685
|
return;
|
|
689
686
|
}
|
|
690
687
|
const timeoutId = setTimeout(() => {
|
|
@@ -696,6 +693,99 @@ function useTimeout({ callback, delay, }) {
|
|
|
696
693
|
}, [delay]);
|
|
697
694
|
}
|
|
698
695
|
|
|
696
|
+
function filterAllowedFiles(files, acceptedFileTypes) {
|
|
697
|
+
// Allow any files if acceptedFileTypes is undefined, empty array, or contains '*'
|
|
698
|
+
if (!acceptedFileTypes ||
|
|
699
|
+
acceptedFileTypes.length === 0 ||
|
|
700
|
+
acceptedFileTypes.includes('*')) {
|
|
701
|
+
return { acceptedFiles: files, rejectedFiles: [] };
|
|
702
|
+
}
|
|
703
|
+
const acceptedFiles = [];
|
|
704
|
+
const rejectedFiles = [];
|
|
705
|
+
function filterFile(file) {
|
|
706
|
+
const { type = '', name = '' } = file;
|
|
707
|
+
const mimeType = type.toLowerCase();
|
|
708
|
+
const baseMimeType = mimeType.split('/')[0];
|
|
709
|
+
return acceptedFileTypes.some((type) => {
|
|
710
|
+
const validType = type.trim().toLowerCase();
|
|
711
|
+
// if the accepted file type is a file extension
|
|
712
|
+
// it will start with '.', check against the file name
|
|
713
|
+
if (validType.charAt(0) === '.') {
|
|
714
|
+
return name.toLowerCase().endsWith(validType);
|
|
715
|
+
}
|
|
716
|
+
// This is something like a image/* mime type
|
|
717
|
+
if (validType.endsWith('/*')) {
|
|
718
|
+
return baseMimeType === validType.split('/')[0];
|
|
719
|
+
}
|
|
720
|
+
return mimeType === validType;
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
files.forEach((file) => {
|
|
724
|
+
(filterFile(file) ? acceptedFiles : rejectedFiles).push(file);
|
|
725
|
+
});
|
|
726
|
+
return { acceptedFiles, rejectedFiles };
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function useDropZone({ onDropComplete, onDragEnter: _onDragEnter, onDragLeave: _onDragLeave, onDragOver: _onDragOver, onDragStart: _onDragStart, onDrop: _onDrop, acceptedFileTypes = [], }) {
|
|
730
|
+
const [dragState, setDragState] = React.useState('inactive');
|
|
731
|
+
const onDragStart = (event) => {
|
|
732
|
+
event.dataTransfer.clearData();
|
|
733
|
+
if (ui.isFunction(_onDragStart)) {
|
|
734
|
+
_onDragStart(event);
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
const onDragEnter = (event) => {
|
|
738
|
+
event.preventDefault();
|
|
739
|
+
event.stopPropagation();
|
|
740
|
+
if (ui.isFunction(_onDragEnter)) {
|
|
741
|
+
_onDragEnter(event);
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
const onDragLeave = (event) => {
|
|
745
|
+
event.preventDefault();
|
|
746
|
+
event.stopPropagation();
|
|
747
|
+
setDragState('inactive');
|
|
748
|
+
if (ui.isFunction(_onDragLeave)) {
|
|
749
|
+
_onDragLeave(event);
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
const onDragOver = (event) => {
|
|
753
|
+
event.preventDefault();
|
|
754
|
+
event.stopPropagation();
|
|
755
|
+
event.dataTransfer.dropEffect = 'copy';
|
|
756
|
+
if (ui.isFunction(_onDragOver)) {
|
|
757
|
+
_onDragOver(event);
|
|
758
|
+
}
|
|
759
|
+
const files = Array.from(event.dataTransfer.items).map(({ kind, type }) => ({
|
|
760
|
+
kind,
|
|
761
|
+
type,
|
|
762
|
+
}));
|
|
763
|
+
const { rejectedFiles } = filterAllowedFiles(files, acceptedFileTypes);
|
|
764
|
+
setDragState(rejectedFiles.length > 0 ? 'reject' : 'accept');
|
|
765
|
+
};
|
|
766
|
+
const onDrop = (event) => {
|
|
767
|
+
event.preventDefault();
|
|
768
|
+
event.stopPropagation();
|
|
769
|
+
setDragState('inactive');
|
|
770
|
+
const files = Array.from(event.dataTransfer.files);
|
|
771
|
+
const { acceptedFiles, rejectedFiles } = filterAllowedFiles(files, acceptedFileTypes);
|
|
772
|
+
if (ui.isFunction(_onDrop)) {
|
|
773
|
+
_onDrop(event);
|
|
774
|
+
}
|
|
775
|
+
if (ui.isFunction(onDropComplete)) {
|
|
776
|
+
onDropComplete({ acceptedFiles, rejectedFiles });
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
return {
|
|
780
|
+
onDragStart,
|
|
781
|
+
onDragEnter,
|
|
782
|
+
onDragLeave,
|
|
783
|
+
onDragOver,
|
|
784
|
+
onDrop,
|
|
785
|
+
dragState,
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
|
|
699
789
|
const INVALID_OPTIONS_MESSAGE = 'an `errorMessage` or a `defaultValue` must be provided in `options`';
|
|
700
790
|
/**
|
|
701
791
|
* Uses `ContextType`/`Name` generics and `options` to create:
|
|
@@ -748,20 +838,20 @@ function createContextUtilities(options) {
|
|
|
748
838
|
if (ui.isUndefined(defaultValue) && !ui.isString(errorMessage)) {
|
|
749
839
|
throw new Error(INVALID_OPTIONS_MESSAGE);
|
|
750
840
|
}
|
|
751
|
-
const Context =
|
|
841
|
+
const Context = React__namespace["default"].createContext(defaultValue);
|
|
752
842
|
function Provider(props) {
|
|
753
843
|
const { children, ...context } = props;
|
|
754
|
-
const value =
|
|
844
|
+
const value = React__namespace["default"].useMemo(() => context,
|
|
755
845
|
// Unpack `context` for the dep array; using `[context]` results in
|
|
756
846
|
// evaluation on every render
|
|
757
847
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
758
848
|
Object.values(context));
|
|
759
|
-
return
|
|
849
|
+
return React__namespace["default"].createElement(Context.Provider, { value: value }, children);
|
|
760
850
|
}
|
|
761
851
|
Provider.displayName = `${contextName}Provider`;
|
|
762
852
|
return {
|
|
763
853
|
[`use${contextName}`]: function (params) {
|
|
764
|
-
const context =
|
|
854
|
+
const context = React__namespace["default"].useContext(Context);
|
|
765
855
|
if (ui.isUndefined(context)) {
|
|
766
856
|
throw new Error(params?.errorMessage ?? errorMessage);
|
|
767
857
|
}
|
|
@@ -783,6 +873,7 @@ exports.useAuthenticatorInitMachine = useAuthenticatorInitMachine;
|
|
|
783
873
|
exports.useAuthenticatorRoute = useAuthenticatorRoute;
|
|
784
874
|
exports.useDataState = useDataState;
|
|
785
875
|
exports.useDeprecationWarning = useDeprecationWarning;
|
|
876
|
+
exports.useDropZone = useDropZone;
|
|
786
877
|
exports.useField = useField;
|
|
787
878
|
exports.useForm = useForm;
|
|
788
879
|
exports.useGetUrl = useGetUrl;
|
|
@@ -5,3 +5,4 @@ export { default as useHasValueUpdated } from './useHasValueUpdated';
|
|
|
5
5
|
export { default as usePreviousValue } from './usePreviousValue';
|
|
6
6
|
export { default as useSetUserAgent } from './useSetUserAgent';
|
|
7
7
|
export { default as useTimeout } from './useTimeout';
|
|
8
|
+
export { default as useDropZone, UseDropZoneParams } from './useDropZone';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
interface DragEvents {
|
|
3
|
+
onDragStart: (event: React.DragEvent<HTMLDivElement>) => void;
|
|
4
|
+
onDragEnter: (event: React.DragEvent<HTMLDivElement>) => void;
|
|
5
|
+
onDragLeave: (event: React.DragEvent<HTMLDivElement>) => void;
|
|
6
|
+
onDragOver: (event: React.DragEvent<HTMLDivElement>) => void;
|
|
7
|
+
onDrop: (event: React.DragEvent<HTMLDivElement>) => void;
|
|
8
|
+
}
|
|
9
|
+
export interface UseDropZoneParams extends Partial<DragEvents> {
|
|
10
|
+
onDropComplete?: (props: {
|
|
11
|
+
acceptedFiles: File[];
|
|
12
|
+
rejectedFiles: File[];
|
|
13
|
+
}) => void;
|
|
14
|
+
/**
|
|
15
|
+
* List of accepted File types, values of `['*']` or undefined allow any files
|
|
16
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept
|
|
17
|
+
*/
|
|
18
|
+
acceptedFileTypes?: string[];
|
|
19
|
+
}
|
|
20
|
+
type DragState = 'accept' | 'reject' | 'inactive';
|
|
21
|
+
interface UseDropZoneReturn extends DragEvents {
|
|
22
|
+
dragState: DragState;
|
|
23
|
+
}
|
|
24
|
+
export default function useDropZone({ onDropComplete, onDragEnter: _onDragEnter, onDragLeave: _onDragLeave, onDragOver: _onDragOver, onDragStart: _onDragStart, onDrop: _onDrop, acceptedFileTypes, }: UseDropZoneParams): UseDropZoneReturn;
|
|
25
|
+
export {};
|
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, } from './hooks';
|
|
3
|
+
export { 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';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type DragFile = {
|
|
2
|
+
kind: string;
|
|
3
|
+
type: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
} | File;
|
|
6
|
+
export declare function filterAllowedFiles<FileType extends DragFile = DragFile>(files: FileType[], acceptedFileTypes: string[]): {
|
|
7
|
+
acceptedFiles: FileType[];
|
|
8
|
+
rejectedFiles: FileType[];
|
|
9
|
+
};
|
|
10
|
+
export {};
|
package/elements/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws-amplify/ui-react-core",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.26",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"module": "dist/esm/index.mjs",
|
|
6
6
|
"react-native": "src/index.ts",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"types": "./dist/types/index.d.ts"
|
|
12
12
|
},
|
|
13
13
|
"./elements": {
|
|
14
|
-
"import": "./dist/esm/elements
|
|
14
|
+
"import": "./dist/esm/elements.mjs",
|
|
15
15
|
"require": "./dist/elements.js",
|
|
16
16
|
"react-native": "./src/elements/index.ts",
|
|
17
17
|
"types": "./dist/types/elements/index.d.ts"
|
|
@@ -33,21 +33,21 @@
|
|
|
33
33
|
"build": "rollup --config",
|
|
34
34
|
"check:esm": "node --input-type=module --eval 'import \"@aws-amplify/ui-react-core\"'",
|
|
35
35
|
"dev": "yarn build --watch",
|
|
36
|
-
"lint": "yarn typecheck && eslint
|
|
36
|
+
"lint": "yarn typecheck && eslint .",
|
|
37
37
|
"prebuild": "rimraf dist",
|
|
38
38
|
"test": "jest",
|
|
39
39
|
"test:watch": "yarn test --watch",
|
|
40
40
|
"typecheck": "tsc --noEmit"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@aws-amplify/ui": "6.6.
|
|
43
|
+
"@aws-amplify/ui": "6.6.2",
|
|
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.6.0",
|
|
51
51
|
"react": "^16.14.0 || ^17.0 || ^18.0"
|
|
52
52
|
},
|
|
53
53
|
"sideEffects": false
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
UseControlledField,
|
|
7
7
|
UseControlledFieldParams,
|
|
8
8
|
} from './types';
|
|
9
|
-
import {
|
|
9
|
+
import { isFunction } from '@aws-amplify/ui';
|
|
10
10
|
|
|
11
11
|
export const DEFAULT_ERROR_MESSAGE =
|
|
12
12
|
'`useControlledField` must be used within a `FormProvider`';
|
|
@@ -44,7 +44,7 @@ export default function useControlledField<OnBlur extends FocusHandler>({
|
|
|
44
44
|
const hasError = !!errorMessage;
|
|
45
45
|
|
|
46
46
|
const handleBlur = (event: Parameters<OnBlur>[0]) => {
|
|
47
|
-
if (
|
|
47
|
+
if (isFunction(_onBlur)) {
|
|
48
48
|
_onBlur(event);
|
|
49
49
|
}
|
|
50
50
|
// `useController.onBlur` does not receive params
|
|
@@ -52,7 +52,7 @@ export default function useControlledField<OnBlur extends FocusHandler>({
|
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
const handleChangeText = (event: string) => {
|
|
55
|
-
if (
|
|
55
|
+
if (isFunction(_onChangeText)) {
|
|
56
56
|
_onChangeText(event);
|
|
57
57
|
}
|
|
58
58
|
onChangeText(event);
|
package/src/hooks/index.ts
CHANGED
|
@@ -9,3 +9,4 @@ export { default as useHasValueUpdated } from './useHasValueUpdated';
|
|
|
9
9
|
export { default as usePreviousValue } from './usePreviousValue';
|
|
10
10
|
export { default as useSetUserAgent } from './useSetUserAgent';
|
|
11
11
|
export { default as useTimeout } from './useTimeout';
|
|
12
|
+
export { default as useDropZone, UseDropZoneParams } from './useDropZone';
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { isFunction } from '@aws-amplify/ui';
|
|
3
|
+
import { filterAllowedFiles } from '../utils/filterAllowedFiles';
|
|
4
|
+
|
|
5
|
+
interface DragEvents {
|
|
6
|
+
onDragStart: (event: React.DragEvent<HTMLDivElement>) => void;
|
|
7
|
+
onDragEnter: (event: React.DragEvent<HTMLDivElement>) => void;
|
|
8
|
+
onDragLeave: (event: React.DragEvent<HTMLDivElement>) => void;
|
|
9
|
+
onDragOver: (event: React.DragEvent<HTMLDivElement>) => void;
|
|
10
|
+
onDrop: (event: React.DragEvent<HTMLDivElement>) => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface UseDropZoneParams extends Partial<DragEvents> {
|
|
14
|
+
onDropComplete?: (props: {
|
|
15
|
+
acceptedFiles: File[];
|
|
16
|
+
rejectedFiles: File[];
|
|
17
|
+
}) => void;
|
|
18
|
+
/**
|
|
19
|
+
* List of accepted File types, values of `['*']` or undefined allow any files
|
|
20
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept
|
|
21
|
+
*/
|
|
22
|
+
acceptedFileTypes?: string[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type DragState = 'accept' | 'reject' | 'inactive';
|
|
26
|
+
|
|
27
|
+
interface UseDropZoneReturn extends DragEvents {
|
|
28
|
+
dragState: DragState;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default function useDropZone({
|
|
32
|
+
onDropComplete,
|
|
33
|
+
onDragEnter: _onDragEnter,
|
|
34
|
+
onDragLeave: _onDragLeave,
|
|
35
|
+
onDragOver: _onDragOver,
|
|
36
|
+
onDragStart: _onDragStart,
|
|
37
|
+
onDrop: _onDrop,
|
|
38
|
+
acceptedFileTypes = [],
|
|
39
|
+
}: UseDropZoneParams): UseDropZoneReturn {
|
|
40
|
+
const [dragState, setDragState] = useState<DragState>('inactive');
|
|
41
|
+
|
|
42
|
+
const onDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
|
43
|
+
event.dataTransfer.clearData();
|
|
44
|
+
if (isFunction(_onDragStart)) {
|
|
45
|
+
_onDragStart(event);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const onDragEnter = (event: React.DragEvent<HTMLDivElement>) => {
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
event.stopPropagation();
|
|
52
|
+
if (isFunction(_onDragEnter)) {
|
|
53
|
+
_onDragEnter(event);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const onDragLeave = (event: React.DragEvent<HTMLDivElement>) => {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
event.stopPropagation();
|
|
60
|
+
setDragState('inactive');
|
|
61
|
+
if (isFunction(_onDragLeave)) {
|
|
62
|
+
_onDragLeave(event);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const onDragOver = (event: React.DragEvent<HTMLDivElement>) => {
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
event.stopPropagation();
|
|
69
|
+
event.dataTransfer.dropEffect = 'copy';
|
|
70
|
+
if (isFunction(_onDragOver)) {
|
|
71
|
+
_onDragOver(event);
|
|
72
|
+
}
|
|
73
|
+
const files = Array.from(event.dataTransfer.items).map(
|
|
74
|
+
({ kind, type }) => ({
|
|
75
|
+
kind,
|
|
76
|
+
type,
|
|
77
|
+
})
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const { rejectedFiles } = filterAllowedFiles(files, acceptedFileTypes);
|
|
81
|
+
setDragState(rejectedFiles.length > 0 ? 'reject' : 'accept');
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const onDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
event.stopPropagation();
|
|
87
|
+
setDragState('inactive');
|
|
88
|
+
const files = Array.from(event.dataTransfer.files);
|
|
89
|
+
const { acceptedFiles, rejectedFiles } = filterAllowedFiles<File>(
|
|
90
|
+
files,
|
|
91
|
+
acceptedFileTypes
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (isFunction(_onDrop)) {
|
|
95
|
+
_onDrop(event);
|
|
96
|
+
}
|
|
97
|
+
if (isFunction(onDropComplete)) {
|
|
98
|
+
onDropComplete({ acceptedFiles, rejectedFiles });
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
onDragStart,
|
|
104
|
+
onDragEnter,
|
|
105
|
+
onDragLeave,
|
|
106
|
+
onDragOver,
|
|
107
|
+
onDrop,
|
|
108
|
+
dragState,
|
|
109
|
+
};
|
|
110
|
+
}
|
package/src/hooks/useTimeout.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { isFunction } from '@aws-amplify/ui';
|
|
3
3
|
|
|
4
4
|
export default function useTimeout({
|
|
5
5
|
callback,
|
|
@@ -15,7 +15,7 @@ export default function useTimeout({
|
|
|
15
15
|
}, [callback]);
|
|
16
16
|
|
|
17
17
|
React.useEffect(() => {
|
|
18
|
-
if (!
|
|
18
|
+
if (!isFunction(storedCallback.current) || !delay) {
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
21
|
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Drag event file shape is different than the drop event fileshape
|
|
2
|
+
type DragFile =
|
|
3
|
+
| {
|
|
4
|
+
kind: string;
|
|
5
|
+
type: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
}
|
|
8
|
+
| File;
|
|
9
|
+
|
|
10
|
+
export function filterAllowedFiles<FileType extends DragFile = DragFile>(
|
|
11
|
+
files: FileType[],
|
|
12
|
+
acceptedFileTypes: string[]
|
|
13
|
+
): { acceptedFiles: FileType[]; rejectedFiles: FileType[] } {
|
|
14
|
+
// Allow any files if acceptedFileTypes is undefined, empty array, or contains '*'
|
|
15
|
+
if (
|
|
16
|
+
!acceptedFileTypes ||
|
|
17
|
+
acceptedFileTypes.length === 0 ||
|
|
18
|
+
acceptedFileTypes.includes('*')
|
|
19
|
+
) {
|
|
20
|
+
return { acceptedFiles: files, rejectedFiles: [] };
|
|
21
|
+
}
|
|
22
|
+
const acceptedFiles: FileType[] = [];
|
|
23
|
+
const rejectedFiles: FileType[] = [];
|
|
24
|
+
|
|
25
|
+
function filterFile(file: DragFile) {
|
|
26
|
+
const { type = '', name = '' } = file;
|
|
27
|
+
const mimeType = type.toLowerCase();
|
|
28
|
+
const baseMimeType = mimeType.split('/')[0];
|
|
29
|
+
|
|
30
|
+
return acceptedFileTypes.some((type) => {
|
|
31
|
+
const validType = type.trim().toLowerCase();
|
|
32
|
+
// if the accepted file type is a file extension
|
|
33
|
+
// it will start with '.', check against the file name
|
|
34
|
+
if (validType.charAt(0) === '.') {
|
|
35
|
+
return name.toLowerCase().endsWith(validType);
|
|
36
|
+
}
|
|
37
|
+
// This is something like a image/* mime type
|
|
38
|
+
if (validType.endsWith('/*')) {
|
|
39
|
+
return baseMimeType === validType.split('/')[0];
|
|
40
|
+
}
|
|
41
|
+
return mimeType === validType;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
files.forEach((file) => {
|
|
46
|
+
(filterFile(file) ? acceptedFiles : rejectedFiles).push(file);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return { acceptedFiles, rejectedFiles };
|
|
50
|
+
}
|