@docusaurus/theme-common 3.7.0 → 3.8.0-canary-6327

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.
Files changed (70) hide show
  1. package/lib/components/Collapsible/index.d.ts +0 -6
  2. package/lib/components/Collapsible/index.d.ts.map +1 -1
  3. package/lib/components/Collapsible/index.js +3 -17
  4. package/lib/components/Collapsible/index.js.map +1 -1
  5. package/lib/components/Details/index.d.ts.map +1 -1
  6. package/lib/components/Details/index.js +1 -2
  7. package/lib/components/Details/index.js.map +1 -1
  8. package/lib/contexts/colorMode.d.ts +6 -7
  9. package/lib/contexts/colorMode.d.ts.map +1 -1
  10. package/lib/contexts/colorMode.js +111 -67
  11. package/lib/contexts/colorMode.js.map +1 -1
  12. package/lib/contexts/navbarMobileSidebar.d.ts.map +1 -1
  13. package/lib/contexts/navbarMobileSidebar.js +21 -12
  14. package/lib/contexts/navbarMobileSidebar.js.map +1 -1
  15. package/lib/hooks/useCodeWordWrap.d.ts +2 -1
  16. package/lib/hooks/useCodeWordWrap.d.ts.map +1 -1
  17. package/lib/hooks/useCodeWordWrap.js.map +1 -1
  18. package/lib/index.js +1 -1
  19. package/lib/index.js.map +1 -1
  20. package/lib/internal.d.ts +2 -3
  21. package/lib/internal.d.ts.map +1 -1
  22. package/lib/internal.js +5 -3
  23. package/lib/internal.js.map +1 -1
  24. package/lib/utils/ThemeClassNames.d.ts +21 -1
  25. package/lib/utils/ThemeClassNames.d.ts.map +1 -1
  26. package/lib/utils/ThemeClassNames.js +21 -2
  27. package/lib/utils/ThemeClassNames.js.map +1 -1
  28. package/lib/utils/codeBlockUtils.d.ts +66 -34
  29. package/lib/utils/codeBlockUtils.d.ts.map +1 -1
  30. package/lib/utils/codeBlockUtils.js +136 -30
  31. package/lib/utils/codeBlockUtils.js.map +1 -1
  32. package/lib/utils/metadataUtils.d.ts.map +1 -1
  33. package/lib/utils/metadataUtils.js +37 -20
  34. package/lib/utils/metadataUtils.js.map +1 -1
  35. package/lib/utils/reactUtils.d.ts.map +1 -1
  36. package/lib/utils/reactUtils.js +4 -1
  37. package/lib/utils/reactUtils.js.map +1 -1
  38. package/lib/utils/scrollUtils.d.ts.map +1 -1
  39. package/lib/utils/scrollUtils.js +1 -0
  40. package/lib/utils/scrollUtils.js.map +1 -1
  41. package/lib/utils/storageUtils.js +3 -3
  42. package/lib/utils/storageUtils.js.map +1 -1
  43. package/lib/utils/titleFormatterUtils.d.ts +59 -0
  44. package/lib/utils/titleFormatterUtils.d.ts.map +1 -0
  45. package/lib/utils/titleFormatterUtils.js +51 -0
  46. package/lib/utils/titleFormatterUtils.js.map +1 -0
  47. package/lib/utils/useThemeConfig.d.ts +3 -2
  48. package/lib/utils/useThemeConfig.d.ts.map +1 -1
  49. package/lib/utils/useThemeConfig.js.map +1 -1
  50. package/package.json +9 -10
  51. package/src/components/Collapsible/index.tsx +1 -29
  52. package/src/components/Details/index.tsx +0 -1
  53. package/src/contexts/colorMode.tsx +164 -83
  54. package/src/contexts/navbarMobileSidebar.tsx +33 -13
  55. package/src/hooks/useCodeWordWrap.ts +4 -2
  56. package/src/index.ts +1 -1
  57. package/src/internal.ts +14 -3
  58. package/src/utils/ThemeClassNames.ts +25 -2
  59. package/src/utils/{codeBlockUtils.ts → codeBlockUtils.tsx} +255 -57
  60. package/src/utils/metadataUtils.tsx +57 -27
  61. package/src/utils/reactUtils.tsx +4 -1
  62. package/src/utils/scrollUtils.tsx +1 -0
  63. package/src/utils/storageUtils.ts +3 -3
  64. package/src/utils/titleFormatterUtils.tsx +122 -0
  65. package/src/utils/useThemeConfig.ts +4 -2
  66. package/lib/utils/generalUtils.d.ts +0 -11
  67. package/lib/utils/generalUtils.d.ts.map +0 -1
  68. package/lib/utils/generalUtils.js +0 -18
  69. package/lib/utils/generalUtils.js.map +0 -1
  70. package/src/utils/generalUtils.ts +0 -19
@@ -11,21 +11,52 @@ import React, {
11
11
  useEffect,
12
12
  useContext,
13
13
  useMemo,
14
- useRef,
15
14
  type ReactNode,
16
15
  } from 'react';
17
- import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
18
16
  import {ReactContextError} from '../utils/reactUtils';
19
17
  import {createStorageSlot} from '../utils/storageUtils';
20
18
  import {useThemeConfig} from '../utils/useThemeConfig';
21
19
 
20
+ // The "effective" color mode
21
+ export type ColorMode = 'light' | 'dark';
22
+
23
+ // The color mode explicitly chosen by the user
24
+ // null => no choice has been made, or the choice has been reverted to OS value
25
+ export type ColorModeChoice = ColorMode | null;
26
+
27
+ function getSystemColorMode(): ColorMode {
28
+ return window.matchMedia('(prefers-color-scheme: dark)').matches
29
+ ? 'dark'
30
+ : 'light';
31
+ }
32
+
33
+ function subscribeToMedia(
34
+ query: string,
35
+ listener: (event: MediaQueryListEvent) => void,
36
+ ): () => void {
37
+ const mql = window.matchMedia(query);
38
+ mql.addEventListener('change', listener);
39
+ return () => mql.removeEventListener('change', listener);
40
+ }
41
+
42
+ function subscribeToSystemColorModeChange(
43
+ onChange: (newSystemColorMode: ColorMode) => void,
44
+ ): () => void {
45
+ return subscribeToMedia('(prefers-color-scheme: dark)', () =>
46
+ onChange(getSystemColorMode()),
47
+ );
48
+ }
49
+
22
50
  type ContextValue = {
23
- /** Current color mode. */
51
+ /** The effective color mode. */
24
52
  readonly colorMode: ColorMode;
53
+ /** The explicitly chosen color mode */
54
+ readonly colorModeChoice: ColorModeChoice;
25
55
  /** Set new color mode. */
26
- readonly setColorMode: (colorMode: ColorMode) => void;
56
+ readonly setColorMode: (colorMode: ColorModeChoice) => void;
27
57
 
28
- // TODO legacy APIs kept for retro-compatibility: deprecate them
58
+ // TODO Docusaurus v4
59
+ // legacy APIs kept for retro-compatibility: deprecate them
29
60
  readonly isDarkTheme: boolean;
30
61
  readonly setLightTheme: () => void;
31
62
  readonly setDarkTheme: () => void;
@@ -36,33 +67,91 @@ const Context = React.createContext<ContextValue | undefined>(undefined);
36
67
  const ColorModeStorageKey = 'theme';
37
68
  const ColorModeStorage = createStorageSlot(ColorModeStorageKey);
38
69
 
39
- const ColorModes = {
40
- light: 'light',
41
- dark: 'dark',
42
- } as const;
43
-
44
- export type ColorMode = (typeof ColorModes)[keyof typeof ColorModes];
70
+ // We use data-theme-choice="system", not an absent attribute
71
+ // This is easier to handle for users with CSS
72
+ const SystemAttribute = 'system';
45
73
 
46
74
  // Ensure to always return a valid colorMode even if input is invalid
47
- const coerceToColorMode = (colorMode?: string | null): ColorMode =>
48
- colorMode === ColorModes.dark ? ColorModes.dark : ColorModes.light;
75
+ const coerceToColorMode = (colorMode: string | null): ColorMode =>
76
+ colorMode === 'dark' ? 'dark' : 'light';
77
+ const coerceToColorModeChoice = (colorMode: string | null): ColorModeChoice =>
78
+ colorMode === null || colorMode === SystemAttribute
79
+ ? null
80
+ : coerceToColorMode(colorMode);
81
+
82
+ const ColorModeAttribute = {
83
+ get: () => {
84
+ return coerceToColorMode(
85
+ document.documentElement.getAttribute('data-theme'),
86
+ );
87
+ },
88
+ set: (colorMode: ColorMode) => {
89
+ document.documentElement.setAttribute(
90
+ 'data-theme',
91
+ coerceToColorMode(colorMode),
92
+ );
93
+ },
94
+ };
49
95
 
50
- const getInitialColorMode = (defaultMode: ColorMode | undefined): ColorMode =>
51
- ExecutionEnvironment.canUseDOM
52
- ? coerceToColorMode(document.documentElement.getAttribute('data-theme'))
53
- : coerceToColorMode(defaultMode);
96
+ const ColorModeChoiceAttribute = {
97
+ get: () => {
98
+ return coerceToColorModeChoice(
99
+ document.documentElement.getAttribute('data-theme-choice'),
100
+ );
101
+ },
102
+ set: (colorMode: ColorModeChoice) => {
103
+ document.documentElement.setAttribute(
104
+ 'data-theme-choice',
105
+ coerceToColorModeChoice(colorMode) ?? SystemAttribute,
106
+ );
107
+ },
108
+ };
54
109
 
55
- const storeColorMode = (newColorMode: ColorMode) => {
56
- ColorModeStorage.set(coerceToColorMode(newColorMode));
110
+ const persistColorModeChoice = (newColorMode: ColorModeChoice) => {
111
+ if (newColorMode === null) {
112
+ ColorModeStorage.del();
113
+ } else {
114
+ ColorModeStorage.set(coerceToColorMode(newColorMode));
115
+ }
57
116
  };
58
117
 
118
+ // The color mode state is initialized in useEffect on purpose
119
+ // to avoid a React hydration mismatch errors
120
+ // The useColorMode() hook value lags behind on purpose
121
+ // This helps users avoid hydration mismatch errors in their code
122
+ // See also https://github.com/facebook/docusaurus/issues/7986
123
+ function useColorModeState() {
124
+ const {
125
+ colorMode: {defaultMode},
126
+ } = useThemeConfig();
127
+
128
+ const [colorMode, setColorModeState] = useState<ColorMode>(defaultMode);
129
+ const [colorModeChoice, setColorModeChoiceState] =
130
+ useState<ColorModeChoice>(null);
131
+
132
+ useEffect(() => {
133
+ setColorModeState(ColorModeAttribute.get());
134
+ setColorModeChoiceState(ColorModeChoiceAttribute.get());
135
+ }, []);
136
+
137
+ return {
138
+ colorMode,
139
+ setColorModeState,
140
+ colorModeChoice,
141
+ setColorModeChoiceState,
142
+ } as const;
143
+ }
144
+
59
145
  function useContextValue(): ContextValue {
60
146
  const {
61
147
  colorMode: {defaultMode, disableSwitch, respectPrefersColorScheme},
62
148
  } = useThemeConfig();
63
- const [colorMode, setColorModeState] = useState(
64
- getInitialColorMode(defaultMode),
65
- );
149
+ const {
150
+ colorMode,
151
+ setColorModeState,
152
+ colorModeChoice,
153
+ setColorModeChoiceState,
154
+ } = useColorModeState();
66
155
 
67
156
  useEffect(() => {
68
157
  // A site is deployed without disableSwitch
@@ -75,78 +164,70 @@ function useContextValue(): ContextValue {
75
164
  }, [disableSwitch]);
76
165
 
77
166
  const setColorMode = useCallback(
78
- (newColorMode: ColorMode | null, options: {persist?: boolean} = {}) => {
167
+ (
168
+ newColorModeChoice: ColorModeChoice,
169
+ options: {persist?: boolean} = {},
170
+ ) => {
79
171
  const {persist = true} = options;
80
- if (newColorMode) {
172
+
173
+ // Reset to system/default color mode
174
+ if (newColorModeChoice === null) {
175
+ // Set the effective color
176
+ const newColorMode = respectPrefersColorScheme
177
+ ? getSystemColorMode()
178
+ : defaultMode;
179
+ ColorModeAttribute.set(newColorMode);
81
180
  setColorModeState(newColorMode);
82
- if (persist) {
83
- storeColorMode(newColorMode);
84
- }
85
- } else {
86
- if (respectPrefersColorScheme) {
87
- setColorModeState(
88
- window.matchMedia('(prefers-color-scheme: dark)').matches
89
- ? ColorModes.dark
90
- : ColorModes.light,
91
- );
92
- } else {
93
- setColorModeState(defaultMode);
94
- }
95
- ColorModeStorage.del();
181
+ // Set the chosen color
182
+ ColorModeChoiceAttribute.set(null);
183
+ setColorModeChoiceState(null);
184
+ }
185
+ // Happy case, when an explicit color is provided
186
+ else {
187
+ ColorModeAttribute.set(newColorModeChoice);
188
+ ColorModeChoiceAttribute.set(newColorModeChoice);
189
+ setColorModeState(newColorModeChoice);
190
+ setColorModeChoiceState(newColorModeChoice);
191
+ }
192
+
193
+ if (persist) {
194
+ persistColorModeChoice(newColorModeChoice);
96
195
  }
97
196
  },
98
- [respectPrefersColorScheme, defaultMode],
197
+ [
198
+ setColorModeState,
199
+ setColorModeChoiceState,
200
+ respectPrefersColorScheme,
201
+ defaultMode,
202
+ ],
99
203
  );
100
204
 
205
+ // Synchronize theme color/choice mode with browser storage
101
206
  useEffect(() => {
102
- document.documentElement.setAttribute(
103
- 'data-theme',
104
- coerceToColorMode(colorMode),
105
- );
106
- }, [colorMode]);
207
+ return ColorModeStorage.listen((e) => {
208
+ setColorMode(coerceToColorModeChoice(e.newValue));
209
+ });
210
+ }, [setColorMode]);
107
211
 
212
+ // Synchronize theme color with system color
108
213
  useEffect(() => {
109
- if (disableSwitch) {
214
+ if (colorModeChoice !== null || !respectPrefersColorScheme) {
110
215
  return undefined;
111
216
  }
112
- const onChange = (e: StorageEvent) => {
113
- if (e.key !== ColorModeStorageKey) {
114
- return;
115
- }
116
- const storedColorMode = ColorModeStorage.get();
117
- if (storedColorMode !== null) {
118
- setColorMode(coerceToColorMode(storedColorMode));
119
- }
120
- };
121
- window.addEventListener('storage', onChange);
122
- return () => window.removeEventListener('storage', onChange);
123
- }, [disableSwitch, setColorMode]);
124
-
125
- // PCS is coerced to light mode when printing, which causes the color mode to
126
- // be reset to dark when exiting print mode, disregarding user settings. When
127
- // the listener fires only because of a print/screen switch, we don't change
128
- // color mode. See https://github.com/facebook/docusaurus/pull/6490
129
- const previousMediaIsPrint = useRef(false);
130
-
131
- useEffect(() => {
132
- if (disableSwitch && !respectPrefersColorScheme) {
133
- return undefined;
134
- }
135
- const mql = window.matchMedia('(prefers-color-scheme: dark)');
136
- const onChange = () => {
137
- if (window.matchMedia('print').matches || previousMediaIsPrint.current) {
138
- previousMediaIsPrint.current = window.matchMedia('print').matches;
139
- return;
140
- }
141
- setColorMode(null);
142
- };
143
- mql.addListener(onChange);
144
- return () => mql.removeListener(onChange);
145
- }, [setColorMode, disableSwitch, respectPrefersColorScheme]);
217
+ return subscribeToSystemColorModeChange((newSystemColorMode) => {
218
+ // Note: we don't use "setColorMode" on purpose
219
+ // The system changes should never be considered an explicit theme choice
220
+ // They only affect the "effective" color, and should never be persisted
221
+ // Note: this listener also fire when printing, see https://github.com/facebook/docusaurus/pull/6490
222
+ setColorModeState(newSystemColorMode);
223
+ ColorModeAttribute.set(newSystemColorMode);
224
+ });
225
+ }, [respectPrefersColorScheme, colorModeChoice, setColorModeState]);
146
226
 
147
227
  return useMemo(
148
228
  () => ({
149
229
  colorMode,
230
+ colorModeChoice,
150
231
  setColorMode,
151
232
  get isDarkTheme() {
152
233
  if (process.env.NODE_ENV === 'development') {
@@ -154,7 +235,7 @@ function useContextValue(): ContextValue {
154
235
  '`useColorMode().isDarkTheme` is deprecated. Please use `useColorMode().colorMode === "dark"` instead.',
155
236
  );
156
237
  }
157
- return colorMode === ColorModes.dark;
238
+ return colorMode === 'dark';
158
239
  },
159
240
  setLightTheme() {
160
241
  if (process.env.NODE_ENV === 'development') {
@@ -162,7 +243,7 @@ function useContextValue(): ContextValue {
162
243
  '`useColorMode().setLightTheme` is deprecated. Please use `useColorMode().setColorMode("light")` instead.',
163
244
  );
164
245
  }
165
- setColorMode(ColorModes.light);
246
+ setColorMode('light');
166
247
  },
167
248
  setDarkTheme() {
168
249
  if (process.env.NODE_ENV === 'development') {
@@ -170,10 +251,10 @@ function useContextValue(): ContextValue {
170
251
  '`useColorMode().setDarkTheme` is deprecated. Please use `useColorMode().setColorMode("dark")` instead.',
171
252
  );
172
253
  }
173
- setColorMode(ColorModes.dark);
254
+ setColorMode('dark');
174
255
  },
175
256
  }),
176
- [colorMode, setColorMode],
257
+ [colorMode, colorModeChoice, setColorMode],
177
258
  );
178
259
  }
179
260
 
@@ -53,18 +53,6 @@ function useContextValue(): ContextValue {
53
53
 
54
54
  const [shown, setShown] = useState(false);
55
55
 
56
- // Close mobile sidebar on navigation pop
57
- // Most likely firing when using the Android back button (but not only)
58
- useHistoryPopHandler(() => {
59
- if (shown) {
60
- setShown(false);
61
- // Prevent pop navigation; seems desirable enough
62
- // See https://github.com/facebook/docusaurus/pull/5462#issuecomment-911699846
63
- return false;
64
- }
65
- return undefined;
66
- });
67
-
68
56
  const toggle = useCallback(() => {
69
57
  setShown((s) => !s);
70
58
  }, []);
@@ -81,13 +69,45 @@ function useContextValue(): ContextValue {
81
69
  );
82
70
  }
83
71
 
72
+ // A component hook wrapper enables conditional rendering
73
+ // See reason here: https://github.com/facebook/docusaurus/issues/10988
74
+ function OnHistoryPop({
75
+ handler,
76
+ }: {
77
+ handler: Parameters<typeof useHistoryPopHandler>[0];
78
+ }) {
79
+ useHistoryPopHandler(handler);
80
+ return null;
81
+ }
82
+
84
83
  export function NavbarMobileSidebarProvider({
85
84
  children,
86
85
  }: {
87
86
  children: ReactNode;
88
87
  }): ReactNode {
89
88
  const value = useContextValue();
90
- return <Context.Provider value={value}>{children}</Context.Provider>;
89
+ return (
90
+ <>
91
+ {
92
+ // Close mobile sidebar on navigation pop
93
+ // Most likely firing when using the Android back button (but not only)
94
+ // Important: we can only have a single history blocker at a time
95
+ // That's why this needs to be rendered conditionally
96
+ // See bug report https://github.com/facebook/docusaurus/issues/10988
97
+ value.shown && (
98
+ <OnHistoryPop
99
+ handler={() => {
100
+ value.toggle();
101
+ // Prevent pop navigation; seems desirable enough
102
+ // See https://github.com/facebook/docusaurus/pull/5462#issuecomment-911699846
103
+ return false;
104
+ }}
105
+ />
106
+ )
107
+ }
108
+ <Context.Provider value={value}>{children}</Context.Provider>
109
+ </>
110
+ );
91
111
  }
92
112
 
93
113
  export function useNavbarMobileSidebar(): ContextValue {
@@ -52,12 +52,14 @@ function useTabBecameVisibleCallback(
52
52
  );
53
53
  }
54
54
 
55
- export function useCodeWordWrap(): {
55
+ export type WordWrap = {
56
56
  readonly codeBlockRef: RefObject<HTMLPreElement>;
57
57
  readonly isEnabled: boolean;
58
58
  readonly isCodeScrollable: boolean;
59
59
  readonly toggle: () => void;
60
- } {
60
+ };
61
+
62
+ export function useCodeWordWrap(): WordWrap {
61
63
  const [isEnabled, setIsEnabled] = useState(false);
62
64
  const [isCodeScrollable, setIsCodeScrollable] = useState<boolean>(false);
63
65
  const codeBlockRef = useRef<HTMLPreElement>(null);
package/src/index.ts CHANGED
@@ -33,7 +33,7 @@ export function useDocsPreferredVersion(...args: unknown[]): unknown {
33
33
  export function useContextualSearchFilters() {
34
34
  const {i18n} = useDocusaurusContext();
35
35
  const docsTags =
36
- // eslint-disable-next-line @typescript-eslint/no-var-requires
36
+ // eslint-disable-next-line @typescript-eslint/no-var-requires, react-compiler/react-compiler
37
37
  require('@docusaurus/plugin-content-docs/client').useDocsContextualSearchTags();
38
38
  const tags = [DEFAULT_SEARCH_TAG, ...docsTags];
39
39
  return {locale: i18n.currentLocale, tags};
package/src/internal.ts CHANGED
@@ -34,15 +34,27 @@ export {ColorModeProvider} from './contexts/colorMode';
34
34
  export {useAlternatePageUtils} from './utils/useAlternatePageUtils';
35
35
 
36
36
  export {
37
+ type CodeBlockMetadata,
38
+ createCodeBlockMetadata,
39
+ getPrismCssVariables,
40
+ CodeBlockContextProvider,
41
+ useCodeBlockContext,
42
+
43
+ // TODO Docusaurus v4: remove, only kept for internal retro-compatibility
44
+ // See https://github.com/facebook/docusaurus/pull/11153
37
45
  parseCodeBlockTitle,
38
- parseLanguage,
46
+ parseClassNameLanguage as parseLanguage,
39
47
  parseLines,
48
+ getLineNumbersStart,
40
49
  containsLineNumbers,
41
50
  } from './utils/codeBlockUtils';
42
51
 
43
52
  export {DEFAULT_SEARCH_TAG} from './utils/searchUtils';
44
53
 
45
- export {useTitleFormatter} from './utils/generalUtils';
54
+ export {
55
+ TitleFormatterProvider,
56
+ useTitleFormatter,
57
+ } from './utils/titleFormatterUtils';
46
58
 
47
59
  export {useLocationChange} from './utils/useLocationChange';
48
60
 
@@ -88,7 +100,6 @@ export {
88
100
  } from './hooks/useKeyboardNavigation';
89
101
  export {useLockBodyScroll} from './hooks/useLockBodyScroll';
90
102
  export {useCodeWordWrap} from './hooks/useCodeWordWrap';
91
- export {getPrismCssVariables} from './utils/codeBlockUtils';
92
103
  export {useBackToTopButton} from './hooks/useBackToTopButton';
93
104
 
94
105
  export {
@@ -27,8 +27,10 @@ export const ThemeClassNames = {
27
27
 
28
28
  mdxPage: 'mdx-page',
29
29
  },
30
+
31
+ // TODO Docusaurus v4: remove old classes?
30
32
  wrapper: {
31
- main: 'main-wrapper',
33
+ main: 'main-wrapper', // replaced by theme-layout-main
32
34
  // TODO these wrapper class names are now quite useless
33
35
  // TODO do breaking change later in 3.0
34
36
  // we already add plugin name/id class on <html>: that's enough
@@ -36,6 +38,7 @@ export const ThemeClassNames = {
36
38
  docsPages: 'docs-wrapper',
37
39
  mdxPages: 'mdx-wrapper',
38
40
  },
41
+
39
42
  common: {
40
43
  editThisPage: 'theme-edit-this-page',
41
44
  lastUpdated: 'theme-last-updated',
@@ -47,8 +50,28 @@ export const ThemeClassNames = {
47
50
 
48
51
  admonitionType: (type: string) => `theme-admonition-${type}`,
49
52
  },
53
+
54
+ announcementBar: {
55
+ container: 'theme-announcement-bar',
56
+ },
57
+
50
58
  layout: {
51
- // TODO add other stable classNames here
59
+ navbar: {
60
+ container: 'theme-layout-navbar',
61
+ containerLeft: 'theme-layout-navbar-left',
62
+ containerRight: 'theme-layout-navbar-right',
63
+ mobileSidebar: {
64
+ container: 'theme-layout-navbar-sidebar',
65
+ panel: 'theme-layout-navbar-sidebar-panel',
66
+ },
67
+ },
68
+ main: {
69
+ container: 'theme-layout-main',
70
+ },
71
+ footer: {
72
+ container: 'theme-layout-footer',
73
+ column: 'theme-layout-footer-column',
74
+ },
52
75
  },
53
76
 
54
77
  /**