@docusaurus/theme-common 2.1.0 → 2.3.0
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/lib/hooks/useHideableNavbar.d.ts.map +1 -1
- package/lib/hooks/useHideableNavbar.js +6 -1
- package/lib/hooks/useHideableNavbar.js.map +1 -1
- package/lib/hooks/useSearchPage.d.ts.map +1 -1
- package/lib/hooks/useSearchPage.js +4 -2
- package/lib/hooks/useSearchPage.js.map +1 -1
- package/lib/index.d.ts +3 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/internal.d.ts +3 -3
- package/lib/internal.d.ts.map +1 -1
- package/lib/internal.js +2 -3
- package/lib/internal.js.map +1 -1
- package/lib/utils/admonitionUtils.d.ts +12 -0
- package/lib/utils/admonitionUtils.d.ts.map +1 -0
- package/lib/utils/admonitionUtils.js +33 -0
- package/lib/utils/admonitionUtils.js.map +1 -0
- package/lib/utils/historyUtils.d.ts +12 -1
- package/lib/utils/historyUtils.d.ts.map +1 -1
- package/lib/utils/historyUtils.js +23 -0
- package/lib/utils/historyUtils.js.map +1 -1
- package/lib/utils/skipToContentUtils.d.ts +17 -0
- package/lib/utils/skipToContentUtils.d.ts.map +1 -0
- package/lib/utils/skipToContentUtils.js +71 -0
- package/lib/utils/skipToContentUtils.js.map +1 -0
- package/lib/utils/storageUtils.d.ts +4 -0
- package/lib/utils/storageUtils.d.ts.map +1 -1
- package/lib/utils/storageUtils.js +70 -7
- package/lib/utils/storageUtils.js.map +1 -1
- package/lib/utils/tabsUtils.d.ts +46 -0
- package/lib/utils/tabsUtils.d.ts.map +1 -0
- package/lib/utils/tabsUtils.js +151 -0
- package/lib/utils/tabsUtils.js.map +1 -0
- package/package.json +11 -10
- package/src/hooks/useHideableNavbar.ts +7 -1
- package/src/hooks/useSearchPage.ts +10 -5
- package/src/index.ts +12 -1
- package/src/internal.ts +7 -6
- package/src/utils/admonitionUtils.tsx +43 -0
- package/src/utils/historyUtils.ts +29 -1
- package/src/utils/skipToContentUtils.tsx +103 -0
- package/src/utils/storageUtils.ts +108 -7
- package/src/utils/tabsUtils.tsx +266 -0
- package/lib/contexts/tabGroupChoice.d.ts +0 -21
- package/lib/contexts/tabGroupChoice.d.ts.map +0 -1
- package/lib/contexts/tabGroupChoice.js +0 -49
- package/lib/contexts/tabGroupChoice.js.map +0 -1
- package/lib/hooks/useSkipToContent.d.ts +0 -25
- package/lib/hooks/useSkipToContent.d.ts.map +0 -1
- package/lib/hooks/useSkipToContent.js +0 -35
- package/lib/hooks/useSkipToContent.js.map +0 -1
- package/src/contexts/tabGroupChoice.tsx +0 -85
- package/src/hooks/useSkipToContent.ts +0 -58
|
@@ -5,12 +5,46 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import {useCallback, useRef} from 'react';
|
|
9
|
+
// @ts-expect-error: TODO temp error until React 18 upgrade
|
|
10
|
+
import {useSyncExternalStore} from 'use-sync-external-store/shim';
|
|
11
|
+
|
|
8
12
|
const StorageTypes = ['localStorage', 'sessionStorage', 'none'] as const;
|
|
9
13
|
|
|
10
14
|
export type StorageType = typeof StorageTypes[number];
|
|
11
15
|
|
|
12
16
|
const DefaultStorageType: StorageType = 'localStorage';
|
|
13
17
|
|
|
18
|
+
// window.addEventListener('storage') only works for different windows...
|
|
19
|
+
// so for current window we have to dispatch the event manually
|
|
20
|
+
// Now we can listen for both cross-window / current-window storage changes!
|
|
21
|
+
// see https://stackoverflow.com/a/71177640/82609
|
|
22
|
+
// see https://stackoverflow.com/questions/26974084/listen-for-changes-with-localstorage-on-the-same-window
|
|
23
|
+
function dispatchChangeEvent({
|
|
24
|
+
key,
|
|
25
|
+
oldValue,
|
|
26
|
+
newValue,
|
|
27
|
+
storage,
|
|
28
|
+
}: {
|
|
29
|
+
key: string;
|
|
30
|
+
oldValue: string | null;
|
|
31
|
+
newValue: string | null;
|
|
32
|
+
storage: Storage;
|
|
33
|
+
}) {
|
|
34
|
+
const event = document.createEvent('StorageEvent');
|
|
35
|
+
event.initStorageEvent(
|
|
36
|
+
'storage',
|
|
37
|
+
false,
|
|
38
|
+
false,
|
|
39
|
+
key,
|
|
40
|
+
oldValue,
|
|
41
|
+
newValue,
|
|
42
|
+
window.location.href,
|
|
43
|
+
storage,
|
|
44
|
+
);
|
|
45
|
+
window.dispatchEvent(event);
|
|
46
|
+
}
|
|
47
|
+
|
|
14
48
|
/**
|
|
15
49
|
* Will return `null` if browser storage is unavailable (like running Docusaurus
|
|
16
50
|
* in an iframe). This should NOT be called in SSR.
|
|
@@ -58,12 +92,14 @@ export type StorageSlot = {
|
|
|
58
92
|
get: () => string | null;
|
|
59
93
|
set: (value: string) => void;
|
|
60
94
|
del: () => void;
|
|
95
|
+
listen: (onChange: (event: StorageEvent) => void) => () => void;
|
|
61
96
|
};
|
|
62
97
|
|
|
63
98
|
const NoopStorageSlot: StorageSlot = {
|
|
64
99
|
get: () => null,
|
|
65
100
|
set: () => {},
|
|
66
101
|
del: () => {},
|
|
102
|
+
listen: () => () => {},
|
|
67
103
|
};
|
|
68
104
|
|
|
69
105
|
// Fail-fast, as storage APIs should not be used during the SSR process
|
|
@@ -78,6 +114,7 @@ Please only call storage APIs in effects and event handlers.`);
|
|
|
78
114
|
get: throwError,
|
|
79
115
|
set: throwError,
|
|
80
116
|
del: throwError,
|
|
117
|
+
listen: throwError,
|
|
81
118
|
};
|
|
82
119
|
}
|
|
83
120
|
|
|
@@ -98,39 +135,103 @@ export function createStorageSlot(
|
|
|
98
135
|
if (typeof window === 'undefined') {
|
|
99
136
|
return createServerStorageSlot(key);
|
|
100
137
|
}
|
|
101
|
-
const
|
|
102
|
-
if (
|
|
138
|
+
const storage = getBrowserStorage(options?.persistence);
|
|
139
|
+
if (storage === null) {
|
|
103
140
|
return NoopStorageSlot;
|
|
104
141
|
}
|
|
105
142
|
return {
|
|
106
143
|
get: () => {
|
|
107
144
|
try {
|
|
108
|
-
return
|
|
145
|
+
return storage.getItem(key);
|
|
109
146
|
} catch (err) {
|
|
110
147
|
console.error(`Docusaurus storage error, can't get key=${key}`, err);
|
|
111
148
|
return null;
|
|
112
149
|
}
|
|
113
150
|
},
|
|
114
|
-
set: (
|
|
151
|
+
set: (newValue) => {
|
|
115
152
|
try {
|
|
116
|
-
|
|
153
|
+
const oldValue = storage.getItem(key);
|
|
154
|
+
storage.setItem(key, newValue);
|
|
155
|
+
dispatchChangeEvent({
|
|
156
|
+
key,
|
|
157
|
+
oldValue,
|
|
158
|
+
newValue,
|
|
159
|
+
storage,
|
|
160
|
+
});
|
|
117
161
|
} catch (err) {
|
|
118
162
|
console.error(
|
|
119
|
-
`Docusaurus storage error, can't set ${key}=${
|
|
163
|
+
`Docusaurus storage error, can't set ${key}=${newValue}`,
|
|
120
164
|
err,
|
|
121
165
|
);
|
|
122
166
|
}
|
|
123
167
|
},
|
|
124
168
|
del: () => {
|
|
125
169
|
try {
|
|
126
|
-
|
|
170
|
+
const oldValue = storage.getItem(key);
|
|
171
|
+
storage.removeItem(key);
|
|
172
|
+
dispatchChangeEvent({key, oldValue, newValue: null, storage});
|
|
127
173
|
} catch (err) {
|
|
128
174
|
console.error(`Docusaurus storage error, can't delete key=${key}`, err);
|
|
129
175
|
}
|
|
130
176
|
},
|
|
177
|
+
listen: (onChange) => {
|
|
178
|
+
try {
|
|
179
|
+
const listener = (event: StorageEvent) => {
|
|
180
|
+
if (event.storageArea === storage && event.key === key) {
|
|
181
|
+
onChange(event);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
window.addEventListener('storage', listener);
|
|
185
|
+
return () => window.removeEventListener('storage', listener);
|
|
186
|
+
} catch (err) {
|
|
187
|
+
console.error(
|
|
188
|
+
`Docusaurus storage error, can't listen for changes of key=${key}`,
|
|
189
|
+
err,
|
|
190
|
+
);
|
|
191
|
+
return () => {};
|
|
192
|
+
}
|
|
193
|
+
},
|
|
131
194
|
};
|
|
132
195
|
}
|
|
133
196
|
|
|
197
|
+
export function useStorageSlot(
|
|
198
|
+
key: string | null,
|
|
199
|
+
options?: {persistence?: StorageType},
|
|
200
|
+
): [string | null, StorageSlot] {
|
|
201
|
+
// Not ideal but good enough: assumes storage slot config is constant
|
|
202
|
+
const storageSlot = useRef(() => {
|
|
203
|
+
if (key === null) {
|
|
204
|
+
return NoopStorageSlot;
|
|
205
|
+
}
|
|
206
|
+
return createStorageSlot(key, options);
|
|
207
|
+
}).current();
|
|
208
|
+
|
|
209
|
+
const listen: StorageSlot['listen'] = useCallback(
|
|
210
|
+
(onChange) => {
|
|
211
|
+
// Do not try to add a listener during SSR
|
|
212
|
+
if (typeof window === 'undefined') {
|
|
213
|
+
return () => {};
|
|
214
|
+
}
|
|
215
|
+
return storageSlot.listen(onChange);
|
|
216
|
+
},
|
|
217
|
+
[storageSlot],
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
const currentValue = useSyncExternalStore(
|
|
221
|
+
listen,
|
|
222
|
+
() => {
|
|
223
|
+
// TODO this check should be useless after React 18
|
|
224
|
+
if (typeof window === 'undefined') {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
return storageSlot.get();
|
|
228
|
+
},
|
|
229
|
+
() => null,
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
return [currentValue, storageSlot];
|
|
233
|
+
}
|
|
234
|
+
|
|
134
235
|
/**
|
|
135
236
|
* Returns a list of all the keys currently stored in browser storage,
|
|
136
237
|
* or an empty list if browser storage can't be accessed.
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, {
|
|
9
|
+
isValidElement,
|
|
10
|
+
useCallback,
|
|
11
|
+
useEffect,
|
|
12
|
+
useState,
|
|
13
|
+
useMemo,
|
|
14
|
+
type ReactNode,
|
|
15
|
+
type ReactElement,
|
|
16
|
+
} from 'react';
|
|
17
|
+
import {useHistory} from '@docusaurus/router';
|
|
18
|
+
import {useQueryStringValue} from '@docusaurus/theme-common/internal';
|
|
19
|
+
import {duplicates, useStorageSlot} from '../index';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* TabValue is the "config" of a given Tab
|
|
23
|
+
* Provided through <Tabs> "values" prop or through the children <TabItem> props
|
|
24
|
+
*/
|
|
25
|
+
export interface TabValue {
|
|
26
|
+
readonly value: string;
|
|
27
|
+
readonly label?: string;
|
|
28
|
+
readonly attributes?: {[key: string]: unknown};
|
|
29
|
+
readonly default?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface TabsProps {
|
|
33
|
+
readonly lazy?: boolean;
|
|
34
|
+
readonly block?: boolean;
|
|
35
|
+
readonly children: readonly ReactElement<TabItemProps>[];
|
|
36
|
+
readonly defaultValue?: string | null;
|
|
37
|
+
readonly values?: readonly TabValue[];
|
|
38
|
+
readonly groupId?: string;
|
|
39
|
+
readonly className?: string;
|
|
40
|
+
readonly queryString?: string | boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface TabItemProps {
|
|
44
|
+
readonly children: ReactNode;
|
|
45
|
+
readonly value: string;
|
|
46
|
+
readonly default?: boolean;
|
|
47
|
+
readonly label?: string;
|
|
48
|
+
readonly hidden?: boolean;
|
|
49
|
+
readonly className?: string;
|
|
50
|
+
readonly attributes?: {[key: string]: unknown};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// A very rough duck type, but good enough to guard against mistakes while
|
|
54
|
+
// allowing customization
|
|
55
|
+
function isTabItem(
|
|
56
|
+
comp: ReactElement<object>,
|
|
57
|
+
): comp is ReactElement<TabItemProps> {
|
|
58
|
+
return 'value' in comp.props;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function ensureValidChildren(children: TabsProps['children']) {
|
|
62
|
+
return React.Children.map(children, (child) => {
|
|
63
|
+
if (isValidElement(child) && isTabItem(child)) {
|
|
64
|
+
return child;
|
|
65
|
+
}
|
|
66
|
+
// child.type.name will give non-sensical values in prod because of
|
|
67
|
+
// minification, but we assume it won't throw in prod.
|
|
68
|
+
throw new Error(
|
|
69
|
+
`Docusaurus error: Bad <Tabs> child <${
|
|
70
|
+
// @ts-expect-error: guarding against unexpected cases
|
|
71
|
+
typeof child.type === 'string' ? child.type : child.type.name
|
|
72
|
+
}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.`,
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function extractChildrenTabValues(children: TabsProps['children']): TabValue[] {
|
|
78
|
+
return ensureValidChildren(children).map(
|
|
79
|
+
({props: {value, label, attributes, default: isDefault}}) => ({
|
|
80
|
+
value,
|
|
81
|
+
label,
|
|
82
|
+
attributes,
|
|
83
|
+
default: isDefault,
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function ensureNoDuplicateValue(values: readonly TabValue[]) {
|
|
89
|
+
const dup = duplicates(values, (a, b) => a.value === b.value);
|
|
90
|
+
if (dup.length > 0) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Docusaurus error: Duplicate values "${dup
|
|
93
|
+
.map((a) => a.value)
|
|
94
|
+
.join(', ')}" found in <Tabs>. Every value needs to be unique.`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function useTabValues(
|
|
100
|
+
props: Pick<TabsProps, 'values' | 'children'>,
|
|
101
|
+
): readonly TabValue[] {
|
|
102
|
+
const {values: valuesProp, children} = props;
|
|
103
|
+
return useMemo(() => {
|
|
104
|
+
const values = valuesProp ?? extractChildrenTabValues(children);
|
|
105
|
+
ensureNoDuplicateValue(values);
|
|
106
|
+
return values;
|
|
107
|
+
}, [valuesProp, children]);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isValidValue({
|
|
111
|
+
value,
|
|
112
|
+
tabValues,
|
|
113
|
+
}: {
|
|
114
|
+
value: string | null | undefined;
|
|
115
|
+
tabValues: readonly TabValue[];
|
|
116
|
+
}) {
|
|
117
|
+
return tabValues.some((a) => a.value === value);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function getInitialStateValue({
|
|
121
|
+
defaultValue,
|
|
122
|
+
tabValues,
|
|
123
|
+
}: {
|
|
124
|
+
defaultValue: TabsProps['defaultValue'];
|
|
125
|
+
tabValues: readonly TabValue[];
|
|
126
|
+
}): string {
|
|
127
|
+
if (tabValues.length === 0) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
'Docusaurus error: the <Tabs> component requires at least one <TabItem> children component',
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
if (defaultValue) {
|
|
133
|
+
// Warn user about passing incorrect defaultValue as prop.
|
|
134
|
+
if (!isValidValue({value: defaultValue, tabValues})) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
`Docusaurus error: The <Tabs> has a defaultValue "${defaultValue}" but none of its children has the corresponding value. Available values are: ${tabValues
|
|
137
|
+
.map((a) => a.value)
|
|
138
|
+
.join(
|
|
139
|
+
', ',
|
|
140
|
+
)}. If you intend to show no default tab, use defaultValue={null} instead.`,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
return defaultValue;
|
|
144
|
+
}
|
|
145
|
+
const defaultTabValue =
|
|
146
|
+
tabValues.find((tabValue) => tabValue.default) ?? tabValues[0];
|
|
147
|
+
if (!defaultTabValue) {
|
|
148
|
+
throw new Error('Unexpected error: 0 tabValues');
|
|
149
|
+
}
|
|
150
|
+
return defaultTabValue.value;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function getStorageKey(groupId: string | undefined) {
|
|
154
|
+
if (!groupId) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
return `docusaurus.tab.${groupId}`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function getQueryStringKey({
|
|
161
|
+
queryString = false,
|
|
162
|
+
groupId,
|
|
163
|
+
}: Pick<TabsProps, 'queryString' | 'groupId'>) {
|
|
164
|
+
if (typeof queryString === 'string') {
|
|
165
|
+
return queryString;
|
|
166
|
+
}
|
|
167
|
+
if (queryString === false) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
if (queryString === true && !groupId) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
`Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
return groupId ?? null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function useTabQueryString({
|
|
179
|
+
queryString = false,
|
|
180
|
+
groupId,
|
|
181
|
+
}: Pick<TabsProps, 'queryString' | 'groupId'>) {
|
|
182
|
+
const history = useHistory();
|
|
183
|
+
const key = getQueryStringKey({queryString, groupId});
|
|
184
|
+
const value = useQueryStringValue(key);
|
|
185
|
+
|
|
186
|
+
const setValue = useCallback(
|
|
187
|
+
(newValue: string) => {
|
|
188
|
+
if (!key) {
|
|
189
|
+
return; // no-op
|
|
190
|
+
}
|
|
191
|
+
const searchParams = new URLSearchParams(history.location.search);
|
|
192
|
+
searchParams.set(key, newValue);
|
|
193
|
+
history.replace({...history.location, search: searchParams.toString()});
|
|
194
|
+
},
|
|
195
|
+
[key, history],
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
return [value, setValue] as const;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function useTabStorage({groupId}: Pick<TabsProps, 'groupId'>) {
|
|
202
|
+
const key = getStorageKey(groupId);
|
|
203
|
+
const [value, storageSlot] = useStorageSlot(key);
|
|
204
|
+
|
|
205
|
+
const setValue = useCallback(
|
|
206
|
+
(newValue: string) => {
|
|
207
|
+
if (!key) {
|
|
208
|
+
return; // no-op
|
|
209
|
+
}
|
|
210
|
+
storageSlot.set(newValue);
|
|
211
|
+
},
|
|
212
|
+
[key, storageSlot],
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
return [value, setValue] as const;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function useTabs(props: TabsProps): {
|
|
219
|
+
selectedValue: string;
|
|
220
|
+
selectValue: (value: string) => void;
|
|
221
|
+
tabValues: readonly TabValue[];
|
|
222
|
+
} {
|
|
223
|
+
const {defaultValue, queryString = false, groupId} = props;
|
|
224
|
+
const tabValues = useTabValues(props);
|
|
225
|
+
|
|
226
|
+
const [selectedValue, setSelectedValue] = useState(() =>
|
|
227
|
+
getInitialStateValue({defaultValue, tabValues}),
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
const [queryStringValue, setQueryString] = useTabQueryString({
|
|
231
|
+
queryString,
|
|
232
|
+
groupId,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const [storageValue, setStorageValue] = useTabStorage({
|
|
236
|
+
groupId,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// We sync valid querystring/storage value to state on change + hydration
|
|
240
|
+
const valueToSync = (() => {
|
|
241
|
+
const value = queryStringValue ?? storageValue;
|
|
242
|
+
if (!isValidValue({value, tabValues})) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
return value;
|
|
246
|
+
})();
|
|
247
|
+
useEffect(() => {
|
|
248
|
+
if (valueToSync) {
|
|
249
|
+
setSelectedValue(valueToSync);
|
|
250
|
+
}
|
|
251
|
+
}, [valueToSync]);
|
|
252
|
+
|
|
253
|
+
const selectValue = useCallback(
|
|
254
|
+
(newValue: string) => {
|
|
255
|
+
if (!isValidValue({value: newValue, tabValues})) {
|
|
256
|
+
throw new Error(`Can't select invalid tab value=${newValue}`);
|
|
257
|
+
}
|
|
258
|
+
setSelectedValue(newValue);
|
|
259
|
+
setQueryString(newValue);
|
|
260
|
+
setStorageValue(newValue);
|
|
261
|
+
},
|
|
262
|
+
[setQueryString, setStorageValue, tabValues],
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
return {selectedValue, selectValue, tabValues};
|
|
266
|
+
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*/
|
|
7
|
-
import { type ReactNode } from 'react';
|
|
8
|
-
declare type ContextValue = {
|
|
9
|
-
/** A map from `groupId` to the `value` of the saved choice. */
|
|
10
|
-
readonly tabGroupChoices: {
|
|
11
|
-
readonly [groupId: string]: string;
|
|
12
|
-
};
|
|
13
|
-
/** Set the new choice value of a group. */
|
|
14
|
-
readonly setTabGroupChoices: (groupId: string, newChoice: string) => void;
|
|
15
|
-
};
|
|
16
|
-
export declare function TabGroupChoiceProvider({ children, }: {
|
|
17
|
-
children: ReactNode;
|
|
18
|
-
}): JSX.Element;
|
|
19
|
-
export declare function useTabGroupChoice(): ContextValue;
|
|
20
|
-
export {};
|
|
21
|
-
//# sourceMappingURL=tabGroupChoice.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tabGroupChoice.d.ts","sourceRoot":"","sources":["../../src/contexts/tabGroupChoice.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAc,EAMZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAMf,aAAK,YAAY,GAAG;IAClB,+DAA+D;IAC/D,QAAQ,CAAC,eAAe,EAAE;QAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAAC;IAC/D,2CAA2C;IAC3C,QAAQ,CAAC,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3E,CAAC;AA4CF,wBAAgB,sBAAsB,CAAC,EACrC,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;CACrB,GAAG,GAAG,CAAC,OAAO,CAGd;AAED,wBAAgB,iBAAiB,IAAI,YAAY,CAMhD"}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*/
|
|
7
|
-
import React, { useState, useCallback, useEffect, useMemo, useContext, } from 'react';
|
|
8
|
-
import { createStorageSlot, listStorageKeys } from '../utils/storageUtils';
|
|
9
|
-
import { ReactContextError } from '../utils/reactUtils';
|
|
10
|
-
const TAB_CHOICE_PREFIX = 'docusaurus.tab.';
|
|
11
|
-
const Context = React.createContext(undefined);
|
|
12
|
-
function useContextValue() {
|
|
13
|
-
const [tabGroupChoices, setChoices] = useState({});
|
|
14
|
-
const setChoiceSyncWithLocalStorage = useCallback((groupId, newChoice) => {
|
|
15
|
-
createStorageSlot(`${TAB_CHOICE_PREFIX}${groupId}`).set(newChoice);
|
|
16
|
-
}, []);
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
try {
|
|
19
|
-
const localStorageChoices = {};
|
|
20
|
-
listStorageKeys().forEach((storageKey) => {
|
|
21
|
-
if (storageKey.startsWith(TAB_CHOICE_PREFIX)) {
|
|
22
|
-
const groupId = storageKey.substring(TAB_CHOICE_PREFIX.length);
|
|
23
|
-
localStorageChoices[groupId] = createStorageSlot(storageKey).get();
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
setChoices(localStorageChoices);
|
|
27
|
-
}
|
|
28
|
-
catch (err) {
|
|
29
|
-
console.error(err);
|
|
30
|
-
}
|
|
31
|
-
}, []);
|
|
32
|
-
const setTabGroupChoices = useCallback((groupId, newChoice) => {
|
|
33
|
-
setChoices((oldChoices) => ({ ...oldChoices, [groupId]: newChoice }));
|
|
34
|
-
setChoiceSyncWithLocalStorage(groupId, newChoice);
|
|
35
|
-
}, [setChoiceSyncWithLocalStorage]);
|
|
36
|
-
return useMemo(() => ({ tabGroupChoices, setTabGroupChoices }), [tabGroupChoices, setTabGroupChoices]);
|
|
37
|
-
}
|
|
38
|
-
export function TabGroupChoiceProvider({ children, }) {
|
|
39
|
-
const value = useContextValue();
|
|
40
|
-
return <Context.Provider value={value}>{children}</Context.Provider>;
|
|
41
|
-
}
|
|
42
|
-
export function useTabGroupChoice() {
|
|
43
|
-
const context = useContext(Context);
|
|
44
|
-
if (context == null) {
|
|
45
|
-
throw new ReactContextError('TabGroupChoiceProvider');
|
|
46
|
-
}
|
|
47
|
-
return context;
|
|
48
|
-
}
|
|
49
|
-
//# sourceMappingURL=tabGroupChoice.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tabGroupChoice.js","sourceRoot":"","sources":["../../src/contexts/tabGroupChoice.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,EACZ,QAAQ,EACR,WAAW,EACX,SAAS,EACT,OAAO,EACP,UAAU,GAEX,MAAM,OAAO,CAAC;AACf,OAAO,EAAC,iBAAiB,EAAE,eAAe,EAAC,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AAEtD,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;AAS5C,MAAM,OAAO,GAAG,KAAK,CAAC,aAAa,CAA2B,SAAS,CAAC,CAAC;AAEzE,SAAS,eAAe;IACtB,MAAM,CAAC,eAAe,EAAE,UAAU,CAAC,GAAG,QAAQ,CAE3C,EAAE,CAAC,CAAC;IACP,MAAM,6BAA6B,GAAG,WAAW,CAC/C,CAAC,OAAe,EAAE,SAAiB,EAAE,EAAE;QACrC,iBAAiB,CAAC,GAAG,iBAAiB,GAAG,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACrE,CAAC,EACD,EAAE,CACH,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI;YACF,MAAM,mBAAmB,GAAgC,EAAE,CAAC;YAC5D,eAAe,EAAE,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE;gBACvC,IAAI,UAAU,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE;oBAC5C,MAAM,OAAO,GAAG,UAAU,CAAC,SAAS,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;oBAC/D,mBAAmB,CAAC,OAAO,CAAC,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC,GAAG,EAAG,CAAC;iBACrE;YACH,CAAC,CAAC,CAAC;YACH,UAAU,CAAC,mBAAmB,CAAC,CAAC;SACjC;QAAC,OAAO,GAAG,EAAE;YACZ,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;SACpB;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,kBAAkB,GAAG,WAAW,CACpC,CAAC,OAAe,EAAE,SAAiB,EAAE,EAAE;QACrC,UAAU,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,EAAC,GAAG,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,SAAS,EAAC,CAAC,CAAC,CAAC;QACpE,6BAA6B,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACpD,CAAC,EACD,CAAC,6BAA6B,CAAC,CAChC,CAAC;IAEF,OAAO,OAAO,CACZ,GAAG,EAAE,CAAC,CAAC,EAAC,eAAe,EAAE,kBAAkB,EAAC,CAAC,EAC7C,CAAC,eAAe,EAAE,kBAAkB,CAAC,CACtC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,EACrC,QAAQ,GAGT;IACC,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAChC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IACpC,IAAI,OAAO,IAAI,IAAI,EAAE;QACnB,MAAM,IAAI,iBAAiB,CAAC,wBAAwB,CAAC,CAAC;KACvD;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*/
|
|
7
|
-
import type React from 'react';
|
|
8
|
-
/** This hook wires the logic for a skip-to-content link. */
|
|
9
|
-
export declare function useSkipToContent(): {
|
|
10
|
-
/**
|
|
11
|
-
* The ref to the container. On page transition, the container will be focused
|
|
12
|
-
* so that keyboard navigators can instantly interact with the link and jump
|
|
13
|
-
* to content. **Note:** the type is `RefObject<HTMLDivElement>` only because
|
|
14
|
-
* the typing for refs don't reflect that the `ref` prop is contravariant, so
|
|
15
|
-
* using `HTMLElement` causes type-checking to fail. You can plug the ref into
|
|
16
|
-
* any HTML element, as long as it can be focused.
|
|
17
|
-
*/
|
|
18
|
-
containerRef: React.RefObject<HTMLDivElement>;
|
|
19
|
-
/**
|
|
20
|
-
* Callback fired when the skip to content link has been interacted with. It
|
|
21
|
-
* will programmatically focus the main content.
|
|
22
|
-
*/
|
|
23
|
-
handleSkip: (e: React.MouseEvent<HTMLAnchorElement>) => void;
|
|
24
|
-
};
|
|
25
|
-
//# sourceMappingURL=useSkipToContent.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useSkipToContent.d.ts","sourceRoot":"","sources":["../../src/hooks/useSkipToContent.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAY/B,4DAA4D;AAC5D,wBAAgB,gBAAgB,IAAI;IAClC;;;;;;;OAOG;IACH,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAC9C;;;OAGG;IACH,UAAU,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAC;CAC9D,CAsBA"}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*/
|
|
7
|
-
import { useCallback, useRef } from 'react';
|
|
8
|
-
import { useHistory } from '@docusaurus/router';
|
|
9
|
-
import { useLocationChange } from '../utils/useLocationChange';
|
|
10
|
-
import { ThemeClassNames } from '../utils/ThemeClassNames';
|
|
11
|
-
function programmaticFocus(el) {
|
|
12
|
-
el.setAttribute('tabindex', '-1');
|
|
13
|
-
el.focus();
|
|
14
|
-
el.removeAttribute('tabindex');
|
|
15
|
-
}
|
|
16
|
-
/** This hook wires the logic for a skip-to-content link. */
|
|
17
|
-
export function useSkipToContent() {
|
|
18
|
-
const containerRef = useRef(null);
|
|
19
|
-
const { action } = useHistory();
|
|
20
|
-
const handleSkip = useCallback((e) => {
|
|
21
|
-
e.preventDefault();
|
|
22
|
-
const targetElement = document.querySelector('main:first-of-type') ??
|
|
23
|
-
document.querySelector(`.${ThemeClassNames.wrapper.main}`);
|
|
24
|
-
if (targetElement) {
|
|
25
|
-
programmaticFocus(targetElement);
|
|
26
|
-
}
|
|
27
|
-
}, []);
|
|
28
|
-
useLocationChange(({ location }) => {
|
|
29
|
-
if (containerRef.current && !location.hash && action === 'PUSH') {
|
|
30
|
-
programmaticFocus(containerRef.current);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
return { containerRef, handleSkip };
|
|
34
|
-
}
|
|
35
|
-
//# sourceMappingURL=useSkipToContent.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useSkipToContent.js","sourceRoot":"","sources":["../../src/hooks/useSkipToContent.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAC,WAAW,EAAE,MAAM,EAAC,MAAM,OAAO,CAAC;AAC1C,OAAO,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAC,iBAAiB,EAAC,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAC,eAAe,EAAC,MAAM,0BAA0B,CAAC;AAEzD,SAAS,iBAAiB,CAAC,EAAe;IACxC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAClC,EAAE,CAAC,KAAK,EAAE,CAAC;IACX,EAAE,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;AACjC,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,gBAAgB;IAgB9B,MAAM,YAAY,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAClD,MAAM,EAAC,MAAM,EAAC,GAAG,UAAU,EAAE,CAAC;IAC9B,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAsC,EAAE,EAAE;QACxE,CAAC,CAAC,cAAc,EAAE,CAAC;QAEnB,MAAM,aAAa,GACjB,QAAQ,CAAC,aAAa,CAAC,oBAAoB,CAAC;YAC5C,QAAQ,CAAC,aAAa,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QAE7D,IAAI,aAAa,EAAE;YACjB,iBAAiB,CAAC,aAAa,CAAC,CAAC;SAClC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,iBAAiB,CAAC,CAAC,EAAC,QAAQ,EAAC,EAAE,EAAE;QAC/B,IAAI,YAAY,CAAC,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,MAAM,KAAK,MAAM,EAAE;YAC/D,iBAAiB,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;SACzC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,EAAC,YAAY,EAAE,UAAU,EAAC,CAAC;AACpC,CAAC"}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import React, {
|
|
9
|
-
useState,
|
|
10
|
-
useCallback,
|
|
11
|
-
useEffect,
|
|
12
|
-
useMemo,
|
|
13
|
-
useContext,
|
|
14
|
-
type ReactNode,
|
|
15
|
-
} from 'react';
|
|
16
|
-
import {createStorageSlot, listStorageKeys} from '../utils/storageUtils';
|
|
17
|
-
import {ReactContextError} from '../utils/reactUtils';
|
|
18
|
-
|
|
19
|
-
const TAB_CHOICE_PREFIX = 'docusaurus.tab.';
|
|
20
|
-
|
|
21
|
-
type ContextValue = {
|
|
22
|
-
/** A map from `groupId` to the `value` of the saved choice. */
|
|
23
|
-
readonly tabGroupChoices: {readonly [groupId: string]: string};
|
|
24
|
-
/** Set the new choice value of a group. */
|
|
25
|
-
readonly setTabGroupChoices: (groupId: string, newChoice: string) => void;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const Context = React.createContext<ContextValue | undefined>(undefined);
|
|
29
|
-
|
|
30
|
-
function useContextValue(): ContextValue {
|
|
31
|
-
const [tabGroupChoices, setChoices] = useState<{
|
|
32
|
-
readonly [groupId: string]: string;
|
|
33
|
-
}>({});
|
|
34
|
-
const setChoiceSyncWithLocalStorage = useCallback(
|
|
35
|
-
(groupId: string, newChoice: string) => {
|
|
36
|
-
createStorageSlot(`${TAB_CHOICE_PREFIX}${groupId}`).set(newChoice);
|
|
37
|
-
},
|
|
38
|
-
[],
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
try {
|
|
43
|
-
const localStorageChoices: {[groupId: string]: string} = {};
|
|
44
|
-
listStorageKeys().forEach((storageKey) => {
|
|
45
|
-
if (storageKey.startsWith(TAB_CHOICE_PREFIX)) {
|
|
46
|
-
const groupId = storageKey.substring(TAB_CHOICE_PREFIX.length);
|
|
47
|
-
localStorageChoices[groupId] = createStorageSlot(storageKey).get()!;
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
setChoices(localStorageChoices);
|
|
51
|
-
} catch (err) {
|
|
52
|
-
console.error(err);
|
|
53
|
-
}
|
|
54
|
-
}, []);
|
|
55
|
-
|
|
56
|
-
const setTabGroupChoices = useCallback(
|
|
57
|
-
(groupId: string, newChoice: string) => {
|
|
58
|
-
setChoices((oldChoices) => ({...oldChoices, [groupId]: newChoice}));
|
|
59
|
-
setChoiceSyncWithLocalStorage(groupId, newChoice);
|
|
60
|
-
},
|
|
61
|
-
[setChoiceSyncWithLocalStorage],
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
return useMemo(
|
|
65
|
-
() => ({tabGroupChoices, setTabGroupChoices}),
|
|
66
|
-
[tabGroupChoices, setTabGroupChoices],
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function TabGroupChoiceProvider({
|
|
71
|
-
children,
|
|
72
|
-
}: {
|
|
73
|
-
children: ReactNode;
|
|
74
|
-
}): JSX.Element {
|
|
75
|
-
const value = useContextValue();
|
|
76
|
-
return <Context.Provider value={value}>{children}</Context.Provider>;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function useTabGroupChoice(): ContextValue {
|
|
80
|
-
const context = useContext(Context);
|
|
81
|
-
if (context == null) {
|
|
82
|
-
throw new ReactContextError('TabGroupChoiceProvider');
|
|
83
|
-
}
|
|
84
|
-
return context;
|
|
85
|
-
}
|