@mui/utils 5.15.13 → 5.15.20
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 +348 -64
- package/deepmerge/deepmerge.js +2 -4
- package/index.js +1 -1
- package/legacy/deepmerge/deepmerge.js +2 -4
- package/legacy/index.js +1 -1
- package/legacy/useControlled/useControlled.js +1 -1
- package/legacy/useLocalStorageState/index.js +1 -0
- package/legacy/useLocalStorageState/useLocalStorageState.js +121 -0
- package/modern/deepmerge/deepmerge.js +2 -4
- package/modern/index.js +1 -1
- package/modern/useControlled/useControlled.js +1 -1
- package/modern/useLocalStorageState/index.js +1 -0
- package/modern/useLocalStorageState/useLocalStorageState.js +108 -0
- package/node/deepmerge/deepmerge.js +2 -4
- package/node/index.js +1 -1
- package/node/useControlled/useControlled.js +1 -1
- package/node/useLocalStorageState/index.js +13 -0
- package/node/useLocalStorageState/useLocalStorageState.js +117 -0
- package/package.json +1 -1
- package/useControlled/useControlled.js +1 -1
- package/useLocalStorageState/index.d.ts +1 -0
- package/useLocalStorageState/index.js +1 -0
- package/useLocalStorageState/package.json +6 -0
- package/useLocalStorageState/useLocalStorageState.d.ts +17 -0
- package/useLocalStorageState/useLocalStorageState.js +111 -0
package/deepmerge/deepmerge.js
CHANGED
|
@@ -23,11 +23,9 @@ export default function deepmerge(target, source, options = {
|
|
|
23
23
|
const output = options.clone ? _extends({}, target) : target;
|
|
24
24
|
if (isPlainObject(target) && isPlainObject(source)) {
|
|
25
25
|
Object.keys(source).forEach(key => {
|
|
26
|
+
if (isPlainObject(source[key]) &&
|
|
26
27
|
// Avoid prototype pollution
|
|
27
|
-
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
if (isPlainObject(source[key]) && key in target && isPlainObject(target[key])) {
|
|
28
|
+
Object.prototype.hasOwnProperty.call(target, key) && isPlainObject(target[key])) {
|
|
31
29
|
// Since `output` is a clone of `target` and we have narrowed `target` in this block we can cast to the same type.
|
|
32
30
|
output[key] = deepmerge(target[key], source[key], options);
|
|
33
31
|
} else if (options.clone) {
|
package/index.js
CHANGED
|
@@ -25,11 +25,9 @@ export default function deepmerge(target, source) {
|
|
|
25
25
|
var output = options.clone ? _extends({}, target) : target;
|
|
26
26
|
if (isPlainObject(target) && isPlainObject(source)) {
|
|
27
27
|
Object.keys(source).forEach(function (key) {
|
|
28
|
+
if (isPlainObject(source[key]) &&
|
|
28
29
|
// Avoid prototype pollution
|
|
29
|
-
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
if (isPlainObject(source[key]) && key in target && isPlainObject(target[key])) {
|
|
30
|
+
Object.prototype.hasOwnProperty.call(target, key) && isPlainObject(target[key])) {
|
|
33
31
|
// Since `output` is a clone of `target` and we have narrowed `target` in this block we can cast to the same type.
|
|
34
32
|
output[key] = deepmerge(target[key], source[key], options);
|
|
35
33
|
} else if (options.clone) {
|
package/legacy/index.js
CHANGED
|
@@ -24,7 +24,7 @@ export default function useControlled(_ref) {
|
|
|
24
24
|
var _React$useRef2 = React.useRef(defaultProp),
|
|
25
25
|
defaultValue = _React$useRef2.current;
|
|
26
26
|
React.useEffect(function () {
|
|
27
|
-
if (!isControlled && defaultValue
|
|
27
|
+
if (!isControlled && !Object.is(defaultValue, defaultProp)) {
|
|
28
28
|
console.error(["MUI: A component is changing the default ".concat(state, " state of an uncontrolled ").concat(name, " after being initialized. ") + "To suppress this warning opt to use a controlled ".concat(name, ".")].join('\n'));
|
|
29
29
|
}
|
|
30
30
|
}, [JSON.stringify(defaultProp)]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './useLocalStorageState';
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
// storage events only work across tabs, we'll use an event emitter to announce within the current tab
|
|
6
|
+
var currentTabChangeListeners = new Map();
|
|
7
|
+
function onCurrentTabStorageChange(key, handler) {
|
|
8
|
+
var listeners = currentTabChangeListeners.get(key);
|
|
9
|
+
if (!listeners) {
|
|
10
|
+
listeners = new Set();
|
|
11
|
+
currentTabChangeListeners.set(key, listeners);
|
|
12
|
+
}
|
|
13
|
+
listeners.add(handler);
|
|
14
|
+
}
|
|
15
|
+
function offCurrentTabStorageChange(key, handler) {
|
|
16
|
+
var listeners = currentTabChangeListeners.get(key);
|
|
17
|
+
if (!listeners) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
listeners.delete(handler);
|
|
21
|
+
if (listeners.size === 0) {
|
|
22
|
+
currentTabChangeListeners.delete(key);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function emitCurrentTabStorageChange(key) {
|
|
26
|
+
var listeners = currentTabChangeListeners.get(key);
|
|
27
|
+
if (listeners) {
|
|
28
|
+
listeners.forEach(function (listener) {
|
|
29
|
+
return listener();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function subscribe(area, key, callbark) {
|
|
34
|
+
if (!key) {
|
|
35
|
+
return function () {};
|
|
36
|
+
}
|
|
37
|
+
var storageHandler = function storageHandler(event) {
|
|
38
|
+
if (event.storageArea === area && event.key === key) {
|
|
39
|
+
callbark();
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
window.addEventListener('storage', storageHandler);
|
|
43
|
+
onCurrentTabStorageChange(key, callbark);
|
|
44
|
+
return function () {
|
|
45
|
+
window.removeEventListener('storage', storageHandler);
|
|
46
|
+
offCurrentTabStorageChange(key, callbark);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function getSnapshot(area, key) {
|
|
50
|
+
if (!key) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
return area.getItem(key);
|
|
55
|
+
} catch (_unused) {
|
|
56
|
+
// ignore
|
|
57
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#feature-detecting_localstorage
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function setValue(area, key, value) {
|
|
62
|
+
if (!key) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
if (value === null) {
|
|
67
|
+
area.removeItem(key);
|
|
68
|
+
} else {
|
|
69
|
+
area.setItem(key, String(value));
|
|
70
|
+
}
|
|
71
|
+
} catch (_unused2) {
|
|
72
|
+
// ignore
|
|
73
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#feature-detecting_localstorage
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
emitCurrentTabStorageChange(key);
|
|
77
|
+
}
|
|
78
|
+
var serverValue = [null, function () {}];
|
|
79
|
+
function useLocalStorageStateServer() {
|
|
80
|
+
return serverValue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Sync state to local storage so that it persists through a page refresh. Usage is
|
|
85
|
+
* similar to useState except we pass in a storage key so that we can default
|
|
86
|
+
* to that value on page load instead of the specified initial value.
|
|
87
|
+
*
|
|
88
|
+
* Since the storage API isn't available in server-rendering environments, we
|
|
89
|
+
* return null during SSR and hydration.
|
|
90
|
+
*/
|
|
91
|
+
function useLocalStorageStateBrowser(key) {
|
|
92
|
+
var initializer = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
93
|
+
var _React$useState = React.useState(initializer),
|
|
94
|
+
initialValue = _React$useState[0];
|
|
95
|
+
var area = window.localStorage;
|
|
96
|
+
var subscribeKey = React.useCallback(function (callbark) {
|
|
97
|
+
return subscribe(area, key, callbark);
|
|
98
|
+
}, [area, key]);
|
|
99
|
+
var getKeySnapshot = React.useCallback(function () {
|
|
100
|
+
var _getSnapshot;
|
|
101
|
+
return (_getSnapshot = getSnapshot(area, key)) != null ? _getSnapshot : initialValue;
|
|
102
|
+
}, [area, initialValue, key]);
|
|
103
|
+
|
|
104
|
+
// Start with null for the hydration, and then switch to the actual value.
|
|
105
|
+
var getKeyServerSnapshot = function getKeyServerSnapshot() {
|
|
106
|
+
return null;
|
|
107
|
+
};
|
|
108
|
+
var storedValue = React.useSyncExternalStore(subscribeKey, getKeySnapshot, getKeyServerSnapshot);
|
|
109
|
+
var setStoredValue = React.useCallback(function (value) {
|
|
110
|
+
var valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
111
|
+
setValue(area, key, valueToStore);
|
|
112
|
+
}, [area, key, storedValue]);
|
|
113
|
+
var _React$useState2 = React.useState(initialValue),
|
|
114
|
+
nonStoredValue = _React$useState2[0],
|
|
115
|
+
setNonStoredValue = _React$useState2[1];
|
|
116
|
+
if (!key) {
|
|
117
|
+
return [nonStoredValue, setNonStoredValue];
|
|
118
|
+
}
|
|
119
|
+
return [storedValue, setStoredValue];
|
|
120
|
+
}
|
|
121
|
+
export default typeof window === 'undefined' ? useLocalStorageStateServer : useLocalStorageStateBrowser;
|
|
@@ -23,11 +23,9 @@ export default function deepmerge(target, source, options = {
|
|
|
23
23
|
const output = options.clone ? _extends({}, target) : target;
|
|
24
24
|
if (isPlainObject(target) && isPlainObject(source)) {
|
|
25
25
|
Object.keys(source).forEach(key => {
|
|
26
|
+
if (isPlainObject(source[key]) &&
|
|
26
27
|
// Avoid prototype pollution
|
|
27
|
-
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
if (isPlainObject(source[key]) && key in target && isPlainObject(target[key])) {
|
|
28
|
+
Object.prototype.hasOwnProperty.call(target, key) && isPlainObject(target[key])) {
|
|
31
29
|
// Since `output` is a clone of `target` and we have narrowed `target` in this block we can cast to the same type.
|
|
32
30
|
output[key] = deepmerge(target[key], source[key], options);
|
|
33
31
|
} else if (options.clone) {
|
package/modern/index.js
CHANGED
|
@@ -24,7 +24,7 @@ export default function useControlled({
|
|
|
24
24
|
current: defaultValue
|
|
25
25
|
} = React.useRef(defaultProp);
|
|
26
26
|
React.useEffect(() => {
|
|
27
|
-
if (!isControlled && defaultValue
|
|
27
|
+
if (!isControlled && !Object.is(defaultValue, defaultProp)) {
|
|
28
28
|
console.error([`MUI: A component is changing the default ${state} state of an uncontrolled ${name} after being initialized. ` + `To suppress this warning opt to use a controlled ${name}.`].join('\n'));
|
|
29
29
|
}
|
|
30
30
|
}, [JSON.stringify(defaultProp)]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './useLocalStorageState';
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
// storage events only work across tabs, we'll use an event emitter to announce within the current tab
|
|
6
|
+
const currentTabChangeListeners = new Map();
|
|
7
|
+
function onCurrentTabStorageChange(key, handler) {
|
|
8
|
+
let listeners = currentTabChangeListeners.get(key);
|
|
9
|
+
if (!listeners) {
|
|
10
|
+
listeners = new Set();
|
|
11
|
+
currentTabChangeListeners.set(key, listeners);
|
|
12
|
+
}
|
|
13
|
+
listeners.add(handler);
|
|
14
|
+
}
|
|
15
|
+
function offCurrentTabStorageChange(key, handler) {
|
|
16
|
+
const listeners = currentTabChangeListeners.get(key);
|
|
17
|
+
if (!listeners) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
listeners.delete(handler);
|
|
21
|
+
if (listeners.size === 0) {
|
|
22
|
+
currentTabChangeListeners.delete(key);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function emitCurrentTabStorageChange(key) {
|
|
26
|
+
const listeners = currentTabChangeListeners.get(key);
|
|
27
|
+
if (listeners) {
|
|
28
|
+
listeners.forEach(listener => listener());
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function subscribe(area, key, callbark) {
|
|
32
|
+
if (!key) {
|
|
33
|
+
return () => {};
|
|
34
|
+
}
|
|
35
|
+
const storageHandler = event => {
|
|
36
|
+
if (event.storageArea === area && event.key === key) {
|
|
37
|
+
callbark();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
window.addEventListener('storage', storageHandler);
|
|
41
|
+
onCurrentTabStorageChange(key, callbark);
|
|
42
|
+
return () => {
|
|
43
|
+
window.removeEventListener('storage', storageHandler);
|
|
44
|
+
offCurrentTabStorageChange(key, callbark);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function getSnapshot(area, key) {
|
|
48
|
+
if (!key) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
return area.getItem(key);
|
|
53
|
+
} catch {
|
|
54
|
+
// ignore
|
|
55
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#feature-detecting_localstorage
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function setValue(area, key, value) {
|
|
60
|
+
if (!key) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
if (value === null) {
|
|
65
|
+
area.removeItem(key);
|
|
66
|
+
} else {
|
|
67
|
+
area.setItem(key, String(value));
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
// ignore
|
|
71
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#feature-detecting_localstorage
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
emitCurrentTabStorageChange(key);
|
|
75
|
+
}
|
|
76
|
+
const serverValue = [null, () => {}];
|
|
77
|
+
function useLocalStorageStateServer() {
|
|
78
|
+
return serverValue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Sync state to local storage so that it persists through a page refresh. Usage is
|
|
83
|
+
* similar to useState except we pass in a storage key so that we can default
|
|
84
|
+
* to that value on page load instead of the specified initial value.
|
|
85
|
+
*
|
|
86
|
+
* Since the storage API isn't available in server-rendering environments, we
|
|
87
|
+
* return null during SSR and hydration.
|
|
88
|
+
*/
|
|
89
|
+
function useLocalStorageStateBrowser(key, initializer = null) {
|
|
90
|
+
const [initialValue] = React.useState(initializer);
|
|
91
|
+
const area = window.localStorage;
|
|
92
|
+
const subscribeKey = React.useCallback(callbark => subscribe(area, key, callbark), [area, key]);
|
|
93
|
+
const getKeySnapshot = React.useCallback(() => getSnapshot(area, key) ?? initialValue, [area, initialValue, key]);
|
|
94
|
+
|
|
95
|
+
// Start with null for the hydration, and then switch to the actual value.
|
|
96
|
+
const getKeyServerSnapshot = () => null;
|
|
97
|
+
const storedValue = React.useSyncExternalStore(subscribeKey, getKeySnapshot, getKeyServerSnapshot);
|
|
98
|
+
const setStoredValue = React.useCallback(value => {
|
|
99
|
+
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
100
|
+
setValue(area, key, valueToStore);
|
|
101
|
+
}, [area, key, storedValue]);
|
|
102
|
+
const [nonStoredValue, setNonStoredValue] = React.useState(initialValue);
|
|
103
|
+
if (!key) {
|
|
104
|
+
return [nonStoredValue, setNonStoredValue];
|
|
105
|
+
}
|
|
106
|
+
return [storedValue, setStoredValue];
|
|
107
|
+
}
|
|
108
|
+
export default typeof window === 'undefined' ? useLocalStorageStateServer : useLocalStorageStateBrowser;
|
|
@@ -31,11 +31,9 @@ function deepmerge(target, source, options = {
|
|
|
31
31
|
const output = options.clone ? (0, _extends2.default)({}, target) : target;
|
|
32
32
|
if (isPlainObject(target) && isPlainObject(source)) {
|
|
33
33
|
Object.keys(source).forEach(key => {
|
|
34
|
+
if (isPlainObject(source[key]) &&
|
|
34
35
|
// Avoid prototype pollution
|
|
35
|
-
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
if (isPlainObject(source[key]) && key in target && isPlainObject(target[key])) {
|
|
36
|
+
Object.prototype.hasOwnProperty.call(target, key) && isPlainObject(target[key])) {
|
|
39
37
|
// Since `output` is a clone of `target` and we have narrowed `target` in this block we can cast to the same type.
|
|
40
38
|
output[key] = deepmerge(target[key], source[key], options);
|
|
41
39
|
} else if (options.clone) {
|
package/node/index.js
CHANGED
|
@@ -31,7 +31,7 @@ function useControlled({
|
|
|
31
31
|
current: defaultValue
|
|
32
32
|
} = React.useRef(defaultProp);
|
|
33
33
|
React.useEffect(() => {
|
|
34
|
-
if (!isControlled && defaultValue
|
|
34
|
+
if (!isControlled && !Object.is(defaultValue, defaultProp)) {
|
|
35
35
|
console.error([`MUI: A component is changing the default ${state} state of an uncontrolled ${name} after being initialized. ` + `To suppress this warning opt to use a controlled ${name}.`].join('\n'));
|
|
36
36
|
}
|
|
37
37
|
}, [JSON.stringify(defaultProp)]);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
Object.defineProperty(exports, "default", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () {
|
|
10
|
+
return _useLocalStorageState.default;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
var _useLocalStorageState = _interopRequireDefault(require("./useLocalStorageState"));
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
var React = _interopRequireWildcard(require("react"));
|
|
9
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
10
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
11
|
+
// storage events only work across tabs, we'll use an event emitter to announce within the current tab
|
|
12
|
+
const currentTabChangeListeners = new Map();
|
|
13
|
+
function onCurrentTabStorageChange(key, handler) {
|
|
14
|
+
let listeners = currentTabChangeListeners.get(key);
|
|
15
|
+
if (!listeners) {
|
|
16
|
+
listeners = new Set();
|
|
17
|
+
currentTabChangeListeners.set(key, listeners);
|
|
18
|
+
}
|
|
19
|
+
listeners.add(handler);
|
|
20
|
+
}
|
|
21
|
+
function offCurrentTabStorageChange(key, handler) {
|
|
22
|
+
const listeners = currentTabChangeListeners.get(key);
|
|
23
|
+
if (!listeners) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
listeners.delete(handler);
|
|
27
|
+
if (listeners.size === 0) {
|
|
28
|
+
currentTabChangeListeners.delete(key);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function emitCurrentTabStorageChange(key) {
|
|
32
|
+
const listeners = currentTabChangeListeners.get(key);
|
|
33
|
+
if (listeners) {
|
|
34
|
+
listeners.forEach(listener => listener());
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function subscribe(area, key, callbark) {
|
|
38
|
+
if (!key) {
|
|
39
|
+
return () => {};
|
|
40
|
+
}
|
|
41
|
+
const storageHandler = event => {
|
|
42
|
+
if (event.storageArea === area && event.key === key) {
|
|
43
|
+
callbark();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
window.addEventListener('storage', storageHandler);
|
|
47
|
+
onCurrentTabStorageChange(key, callbark);
|
|
48
|
+
return () => {
|
|
49
|
+
window.removeEventListener('storage', storageHandler);
|
|
50
|
+
offCurrentTabStorageChange(key, callbark);
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function getSnapshot(area, key) {
|
|
54
|
+
if (!key) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
return area.getItem(key);
|
|
59
|
+
} catch {
|
|
60
|
+
// ignore
|
|
61
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#feature-detecting_localstorage
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function setValue(area, key, value) {
|
|
66
|
+
if (!key) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
if (value === null) {
|
|
71
|
+
area.removeItem(key);
|
|
72
|
+
} else {
|
|
73
|
+
area.setItem(key, String(value));
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
// ignore
|
|
77
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#feature-detecting_localstorage
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
emitCurrentTabStorageChange(key);
|
|
81
|
+
}
|
|
82
|
+
const serverValue = [null, () => {}];
|
|
83
|
+
function useLocalStorageStateServer() {
|
|
84
|
+
return serverValue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Sync state to local storage so that it persists through a page refresh. Usage is
|
|
89
|
+
* similar to useState except we pass in a storage key so that we can default
|
|
90
|
+
* to that value on page load instead of the specified initial value.
|
|
91
|
+
*
|
|
92
|
+
* Since the storage API isn't available in server-rendering environments, we
|
|
93
|
+
* return null during SSR and hydration.
|
|
94
|
+
*/
|
|
95
|
+
function useLocalStorageStateBrowser(key, initializer = null) {
|
|
96
|
+
const [initialValue] = React.useState(initializer);
|
|
97
|
+
const area = window.localStorage;
|
|
98
|
+
const subscribeKey = React.useCallback(callbark => subscribe(area, key, callbark), [area, key]);
|
|
99
|
+
const getKeySnapshot = React.useCallback(() => {
|
|
100
|
+
var _getSnapshot;
|
|
101
|
+
return (_getSnapshot = getSnapshot(area, key)) != null ? _getSnapshot : initialValue;
|
|
102
|
+
}, [area, initialValue, key]);
|
|
103
|
+
|
|
104
|
+
// Start with null for the hydration, and then switch to the actual value.
|
|
105
|
+
const getKeyServerSnapshot = () => null;
|
|
106
|
+
const storedValue = React.useSyncExternalStore(subscribeKey, getKeySnapshot, getKeyServerSnapshot);
|
|
107
|
+
const setStoredValue = React.useCallback(value => {
|
|
108
|
+
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
109
|
+
setValue(area, key, valueToStore);
|
|
110
|
+
}, [area, key, storedValue]);
|
|
111
|
+
const [nonStoredValue, setNonStoredValue] = React.useState(initialValue);
|
|
112
|
+
if (!key) {
|
|
113
|
+
return [nonStoredValue, setNonStoredValue];
|
|
114
|
+
}
|
|
115
|
+
return [storedValue, setStoredValue];
|
|
116
|
+
}
|
|
117
|
+
var _default = exports.default = typeof window === 'undefined' ? useLocalStorageStateServer : useLocalStorageStateBrowser;
|
package/package.json
CHANGED
|
@@ -24,7 +24,7 @@ export default function useControlled({
|
|
|
24
24
|
current: defaultValue
|
|
25
25
|
} = React.useRef(defaultProp);
|
|
26
26
|
React.useEffect(() => {
|
|
27
|
-
if (!isControlled && defaultValue
|
|
27
|
+
if (!isControlled && !Object.is(defaultValue, defaultProp)) {
|
|
28
28
|
console.error([`MUI: A component is changing the default ${state} state of an uncontrolled ${name} after being initialized. ` + `To suppress this warning opt to use a controlled ${name}.`].join('\n'));
|
|
29
29
|
}
|
|
30
30
|
}, [JSON.stringify(defaultProp)]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './useLocalStorageState';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './useLocalStorageState';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
type Initializer = () => string | null;
|
|
3
|
+
type UseStorageStateHookResult = [
|
|
4
|
+
string | null,
|
|
5
|
+
React.Dispatch<React.SetStateAction<string | null>>
|
|
6
|
+
];
|
|
7
|
+
/**
|
|
8
|
+
* Sync state to local storage so that it persists through a page refresh. Usage is
|
|
9
|
+
* similar to useState except we pass in a storage key so that we can default
|
|
10
|
+
* to that value on page load instead of the specified initial value.
|
|
11
|
+
*
|
|
12
|
+
* Since the storage API isn't available in server-rendering environments, we
|
|
13
|
+
* return null during SSR and hydration.
|
|
14
|
+
*/
|
|
15
|
+
declare function useLocalStorageStateBrowser(key: string | null, initializer?: string | null | Initializer): UseStorageStateHookResult;
|
|
16
|
+
declare const _default: typeof useLocalStorageStateBrowser;
|
|
17
|
+
export default _default;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
// storage events only work across tabs, we'll use an event emitter to announce within the current tab
|
|
6
|
+
const currentTabChangeListeners = new Map();
|
|
7
|
+
function onCurrentTabStorageChange(key, handler) {
|
|
8
|
+
let listeners = currentTabChangeListeners.get(key);
|
|
9
|
+
if (!listeners) {
|
|
10
|
+
listeners = new Set();
|
|
11
|
+
currentTabChangeListeners.set(key, listeners);
|
|
12
|
+
}
|
|
13
|
+
listeners.add(handler);
|
|
14
|
+
}
|
|
15
|
+
function offCurrentTabStorageChange(key, handler) {
|
|
16
|
+
const listeners = currentTabChangeListeners.get(key);
|
|
17
|
+
if (!listeners) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
listeners.delete(handler);
|
|
21
|
+
if (listeners.size === 0) {
|
|
22
|
+
currentTabChangeListeners.delete(key);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function emitCurrentTabStorageChange(key) {
|
|
26
|
+
const listeners = currentTabChangeListeners.get(key);
|
|
27
|
+
if (listeners) {
|
|
28
|
+
listeners.forEach(listener => listener());
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function subscribe(area, key, callbark) {
|
|
32
|
+
if (!key) {
|
|
33
|
+
return () => {};
|
|
34
|
+
}
|
|
35
|
+
const storageHandler = event => {
|
|
36
|
+
if (event.storageArea === area && event.key === key) {
|
|
37
|
+
callbark();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
window.addEventListener('storage', storageHandler);
|
|
41
|
+
onCurrentTabStorageChange(key, callbark);
|
|
42
|
+
return () => {
|
|
43
|
+
window.removeEventListener('storage', storageHandler);
|
|
44
|
+
offCurrentTabStorageChange(key, callbark);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function getSnapshot(area, key) {
|
|
48
|
+
if (!key) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
return area.getItem(key);
|
|
53
|
+
} catch {
|
|
54
|
+
// ignore
|
|
55
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#feature-detecting_localstorage
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function setValue(area, key, value) {
|
|
60
|
+
if (!key) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
if (value === null) {
|
|
65
|
+
area.removeItem(key);
|
|
66
|
+
} else {
|
|
67
|
+
area.setItem(key, String(value));
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
// ignore
|
|
71
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#feature-detecting_localstorage
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
emitCurrentTabStorageChange(key);
|
|
75
|
+
}
|
|
76
|
+
const serverValue = [null, () => {}];
|
|
77
|
+
function useLocalStorageStateServer() {
|
|
78
|
+
return serverValue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Sync state to local storage so that it persists through a page refresh. Usage is
|
|
83
|
+
* similar to useState except we pass in a storage key so that we can default
|
|
84
|
+
* to that value on page load instead of the specified initial value.
|
|
85
|
+
*
|
|
86
|
+
* Since the storage API isn't available in server-rendering environments, we
|
|
87
|
+
* return null during SSR and hydration.
|
|
88
|
+
*/
|
|
89
|
+
function useLocalStorageStateBrowser(key, initializer = null) {
|
|
90
|
+
const [initialValue] = React.useState(initializer);
|
|
91
|
+
const area = window.localStorage;
|
|
92
|
+
const subscribeKey = React.useCallback(callbark => subscribe(area, key, callbark), [area, key]);
|
|
93
|
+
const getKeySnapshot = React.useCallback(() => {
|
|
94
|
+
var _getSnapshot;
|
|
95
|
+
return (_getSnapshot = getSnapshot(area, key)) != null ? _getSnapshot : initialValue;
|
|
96
|
+
}, [area, initialValue, key]);
|
|
97
|
+
|
|
98
|
+
// Start with null for the hydration, and then switch to the actual value.
|
|
99
|
+
const getKeyServerSnapshot = () => null;
|
|
100
|
+
const storedValue = React.useSyncExternalStore(subscribeKey, getKeySnapshot, getKeyServerSnapshot);
|
|
101
|
+
const setStoredValue = React.useCallback(value => {
|
|
102
|
+
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
103
|
+
setValue(area, key, valueToStore);
|
|
104
|
+
}, [area, key, storedValue]);
|
|
105
|
+
const [nonStoredValue, setNonStoredValue] = React.useState(initialValue);
|
|
106
|
+
if (!key) {
|
|
107
|
+
return [nonStoredValue, setNonStoredValue];
|
|
108
|
+
}
|
|
109
|
+
return [storedValue, setStoredValue];
|
|
110
|
+
}
|
|
111
|
+
export default typeof window === 'undefined' ? useLocalStorageStateServer : useLocalStorageStateBrowser;
|