docusaurus-theme-openapi-docs 5.0.1 → 5.1.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.
Files changed (137) hide show
  1. package/lib/markdown/schema.js +38 -15
  2. package/lib/markdown/schema.test.d.ts +1 -0
  3. package/lib/markdown/schema.test.js +86 -0
  4. package/lib/theme/ApiExplorer/ApiCodeBlock/Container/index.js +4 -2
  5. package/lib/theme/ApiExplorer/ApiCodeBlock/Content/String.js +9 -6
  6. package/lib/theme/ApiExplorer/ApiCodeBlock/Line/index.d.ts +1 -1
  7. package/lib/theme/ApiExplorer/ApiCodeBlock/index.d.ts +1 -1
  8. package/lib/theme/ApiExplorer/Authorization/index.js +9 -10
  9. package/lib/theme/ApiExplorer/Body/index.js +4 -5
  10. package/lib/theme/ApiExplorer/CodeSnippets/index.js +96 -61
  11. package/lib/theme/ApiExplorer/CodeSnippets/languages.js +12 -1
  12. package/lib/theme/ApiExplorer/CodeSnippets/languages.test.d.ts +1 -0
  13. package/lib/theme/ApiExplorer/CodeSnippets/languages.test.js +102 -0
  14. package/lib/theme/ApiExplorer/CodeTabs/index.d.ts +1 -1
  15. package/lib/theme/ApiExplorer/CodeTabs/index.js +6 -5
  16. package/lib/theme/ApiExplorer/Export/index.js +9 -2
  17. package/lib/theme/ApiExplorer/FormFileUpload/index.js +1 -2
  18. package/lib/theme/ApiExplorer/FormLabel/index.js +1 -2
  19. package/lib/theme/ApiExplorer/FormTextInput/index.js +1 -2
  20. package/lib/theme/ApiExplorer/LiveEditor/index.js +1 -2
  21. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.js +5 -3
  22. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.js +75 -4
  23. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.js +1 -2
  24. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.js +67 -4
  25. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.js +65 -1
  26. package/lib/theme/ApiExplorer/ParamOptions/index.js +2 -3
  27. package/lib/theme/ApiExplorer/Request/index.js +17 -18
  28. package/lib/theme/ApiExplorer/Response/index.js +54 -12
  29. package/lib/theme/ApiExplorer/SecuritySchemes/index.js +57 -50
  30. package/lib/theme/ApiExplorer/Server/index.js +2 -3
  31. package/lib/theme/ApiItem/index.js +59 -33
  32. package/lib/theme/ApiTabs/index.d.ts +1 -1
  33. package/lib/theme/ApiTabs/index.js +7 -7
  34. package/lib/theme/DiscriminatorTabs/index.d.ts +1 -1
  35. package/lib/theme/DiscriminatorTabs/index.js +6 -5
  36. package/lib/theme/Example/index.js +3 -4
  37. package/lib/theme/MimeTabs/index.d.ts +1 -1
  38. package/lib/theme/MimeTabs/index.js +6 -5
  39. package/lib/theme/OperationTabs/index.d.ts +1 -1
  40. package/lib/theme/OperationTabs/index.js +6 -5
  41. package/lib/theme/ParamsDetails/index.js +1 -2
  42. package/lib/theme/ParamsItem/index.js +7 -8
  43. package/lib/theme/RequestSchema/index.js +57 -57
  44. package/lib/theme/ResponseExamples/index.js +3 -4
  45. package/lib/theme/ResponseSchema/index.js +26 -24
  46. package/lib/theme/Schema/index.js +148 -27
  47. package/lib/theme/SchemaExpansion/_SchemaExpansion.scss +113 -0
  48. package/lib/theme/SchemaExpansion/context.d.ts +24 -0
  49. package/lib/theme/SchemaExpansion/context.js +187 -0
  50. package/lib/theme/SchemaExpansion/index.d.ts +4 -0
  51. package/lib/theme/SchemaExpansion/index.js +314 -0
  52. package/lib/theme/SchemaItem/index.js +9 -10
  53. package/lib/theme/SchemaTabs/index.d.ts +1 -1
  54. package/lib/theme/SchemaTabs/index.js +6 -5
  55. package/lib/theme/StatusCodes/index.js +2 -4
  56. package/lib/theme/TabItem/index.d.ts +5 -0
  57. package/lib/theme/TabItem/index.js +51 -0
  58. package/lib/theme/TabItem/styles.module.css +3 -0
  59. package/lib/theme/Tabs/index.d.ts +5 -0
  60. package/lib/theme/Tabs/index.js +148 -0
  61. package/lib/theme/Tabs/styles.module.css +7 -0
  62. package/lib/theme/styles.scss +1 -0
  63. package/lib/theme/translationIds.d.ts +1 -93
  64. package/lib/theme/translationIds.js +0 -109
  65. package/lib/theme/utils/codeBlockUtils.d.ts +28 -0
  66. package/lib/theme/utils/codeBlockUtils.js +223 -0
  67. package/lib/theme/utils/reactUtils.d.ts +1 -0
  68. package/lib/theme/utils/reactUtils.js +23 -0
  69. package/lib/theme/utils/scrollUtils.d.ts +7 -0
  70. package/lib/theme/utils/scrollUtils.js +175 -0
  71. package/lib/theme/utils/tabsUtils.d.ts +47 -0
  72. package/lib/theme/utils/tabsUtils.js +299 -0
  73. package/lib/theme/utils/useCodeWordWrap.d.ts +8 -0
  74. package/lib/theme/utils/useCodeWordWrap.js +84 -0
  75. package/lib/theme/utils/useMutationObserver.d.ts +3 -0
  76. package/lib/theme/utils/useMutationObserver.js +34 -0
  77. package/package.json +4 -4
  78. package/src/markdown/schema.test.ts +102 -0
  79. package/src/markdown/schema.ts +42 -15
  80. package/src/theme/ApiExplorer/ApiCodeBlock/Container/index.tsx +2 -1
  81. package/src/theme/ApiExplorer/ApiCodeBlock/Content/String.tsx +8 -7
  82. package/src/theme/ApiExplorer/ApiCodeBlock/Line/index.tsx +1 -1
  83. package/src/theme/ApiExplorer/ApiCodeBlock/index.tsx +1 -1
  84. package/src/theme/ApiExplorer/Authorization/index.tsx +9 -10
  85. package/src/theme/ApiExplorer/Body/index.tsx +7 -5
  86. package/src/theme/ApiExplorer/CodeSnippets/index.tsx +103 -59
  87. package/src/theme/ApiExplorer/CodeSnippets/languages.test.ts +109 -0
  88. package/src/theme/ApiExplorer/CodeSnippets/languages.ts +13 -1
  89. package/src/theme/ApiExplorer/CodeTabs/index.tsx +5 -5
  90. package/src/theme/ApiExplorer/Export/index.tsx +6 -2
  91. package/src/theme/ApiExplorer/FormFileUpload/index.tsx +1 -2
  92. package/src/theme/ApiExplorer/FormLabel/index.tsx +1 -2
  93. package/src/theme/ApiExplorer/FormTextInput/index.tsx +1 -2
  94. package/src/theme/ApiExplorer/LiveEditor/index.tsx +1 -2
  95. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.tsx +5 -3
  96. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.tsx +20 -4
  97. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.tsx +1 -2
  98. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.tsx +15 -4
  99. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.tsx +11 -1
  100. package/src/theme/ApiExplorer/ParamOptions/index.tsx +2 -3
  101. package/src/theme/ApiExplorer/Request/index.tsx +23 -18
  102. package/src/theme/ApiExplorer/Response/index.tsx +63 -9
  103. package/src/theme/ApiExplorer/SecuritySchemes/index.tsx +60 -52
  104. package/src/theme/ApiExplorer/Server/index.tsx +8 -3
  105. package/src/theme/ApiItem/index.tsx +43 -21
  106. package/src/theme/ApiTabs/index.tsx +8 -8
  107. package/src/theme/DiscriminatorTabs/index.tsx +6 -5
  108. package/src/theme/Example/index.tsx +3 -4
  109. package/src/theme/MimeTabs/index.tsx +9 -8
  110. package/src/theme/OperationTabs/index.tsx +5 -4
  111. package/src/theme/ParamsDetails/index.tsx +1 -2
  112. package/src/theme/ParamsItem/index.tsx +13 -8
  113. package/src/theme/RequestSchema/index.tsx +38 -40
  114. package/src/theme/ResponseExamples/index.tsx +3 -4
  115. package/src/theme/ResponseSchema/index.tsx +16 -17
  116. package/src/theme/Schema/index.tsx +156 -27
  117. package/src/theme/SchemaExpansion/_SchemaExpansion.scss +113 -0
  118. package/src/theme/SchemaExpansion/context.tsx +154 -0
  119. package/src/theme/SchemaExpansion/index.tsx +236 -0
  120. package/src/theme/SchemaItem/index.tsx +18 -10
  121. package/src/theme/SchemaTabs/index.tsx +6 -5
  122. package/src/theme/StatusCodes/index.tsx +2 -3
  123. package/src/theme/TabItem/index.tsx +61 -0
  124. package/src/theme/TabItem/styles.module.css +3 -0
  125. package/src/theme/Tabs/index.tsx +164 -0
  126. package/src/theme/Tabs/styles.module.css +7 -0
  127. package/src/theme/styles.scss +1 -0
  128. package/src/theme/translationIds.ts +37 -106
  129. package/src/theme/utils/codeBlockUtils.ts +296 -0
  130. package/src/theme/utils/reactUtils.ts +22 -0
  131. package/src/theme/utils/scrollUtils.tsx +153 -0
  132. package/src/theme/utils/tabsUtils.tsx +329 -0
  133. package/src/theme/utils/useCodeWordWrap.ts +110 -0
  134. package/src/theme/utils/useMutationObserver.ts +43 -0
  135. package/src/theme-classic.d.ts +0 -96
  136. package/src/types.d.ts +27 -0
  137. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,113 @@
1
+ /* ============================================================================
2
+ * Copyright (c) Palo Alto Networks
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
+ .openapi-schema-expansion {
9
+ position: relative;
10
+ display: inline-flex;
11
+ align-items: center;
12
+ margin-left: auto;
13
+ }
14
+
15
+ .openapi-schema-expansion__trigger {
16
+ appearance: none;
17
+ background: transparent;
18
+ border: none;
19
+ border-radius: var(--ifm-global-radius);
20
+ padding: 0;
21
+ width: 22px;
22
+ height: 22px;
23
+ flex: 0 0 22px;
24
+ display: inline-flex;
25
+ align-items: center;
26
+ justify-content: center;
27
+ color: var(--ifm-color-emphasis-600);
28
+ line-height: 0;
29
+ opacity: 0.7;
30
+ transition:
31
+ opacity 0.12s ease,
32
+ background-color 0.12s ease,
33
+ color 0.12s ease;
34
+
35
+ &:hover {
36
+ opacity: 1;
37
+ background-color: var(--ifm-color-emphasis-200);
38
+ color: var(--ifm-color-emphasis-900);
39
+ }
40
+
41
+ &:focus-visible {
42
+ outline: 2px solid var(--ifm-color-primary);
43
+ outline-offset: 2px;
44
+ opacity: 1;
45
+ }
46
+
47
+ &[aria-expanded="true"] {
48
+ opacity: 1;
49
+ background-color: var(--ifm-color-emphasis-200);
50
+ color: var(--ifm-color-emphasis-900);
51
+ }
52
+ }
53
+
54
+ .openapi-schema-expansion__popover {
55
+ position: fixed;
56
+ z-index: 1000;
57
+ display: inline-flex;
58
+ align-items: center;
59
+ gap: 2px;
60
+ padding: 3px;
61
+ background-color: var(--ifm-background-surface-color);
62
+ border: 1px solid var(--ifm-color-emphasis-300);
63
+ border-radius: var(--ifm-global-radius);
64
+ box-shadow: var(--ifm-global-shadow-md);
65
+ white-space: nowrap;
66
+ }
67
+
68
+ .openapi-schema-expansion__option {
69
+ appearance: none;
70
+ background: transparent;
71
+ border: none;
72
+ border-radius: calc(var(--ifm-global-radius) - 2px);
73
+ padding: 2px 8px;
74
+ font-size: 0.8rem;
75
+ line-height: 1.4;
76
+ color: var(--ifm-color-emphasis-800);
77
+ transition:
78
+ background-color 0.12s ease,
79
+ color 0.12s ease;
80
+
81
+ &:hover {
82
+ background-color: var(--ifm-color-emphasis-200);
83
+ }
84
+
85
+ &:focus-visible {
86
+ outline: 2px solid var(--ifm-color-primary);
87
+ outline-offset: 1px;
88
+ }
89
+ }
90
+
91
+ .openapi-schema-expansion__option--active {
92
+ background-color: var(--ifm-color-primary);
93
+ color: var(--ifm-color-white);
94
+
95
+ &:hover {
96
+ background-color: var(--ifm-color-primary-dark);
97
+ }
98
+ }
99
+
100
+ .openapi-markdown__details-summary--with-control {
101
+ display: flex !important;
102
+ align-items: center;
103
+ gap: 0.5rem;
104
+
105
+ > h3,
106
+ > strong {
107
+ margin-bottom: 0;
108
+ display: inline-flex;
109
+ align-items: center;
110
+ flex-wrap: wrap;
111
+ gap: 0.4rem;
112
+ }
113
+ }
@@ -0,0 +1,154 @@
1
+ /* ============================================================================
2
+ * Copyright (c) Palo Alto Networks
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
+ createContext,
10
+ useCallback,
11
+ useContext,
12
+ useEffect,
13
+ useMemo,
14
+ useState,
15
+ } from "react";
16
+
17
+ import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
18
+ import type { ThemeConfig } from "docusaurus-theme-openapi-docs/src/types";
19
+
20
+ export const SCHEMA_EXPANSION_STORAGE_KEY =
21
+ "docusaurus-openapi-schema-expansion-level";
22
+
23
+ export interface SchemaExpansionConfig {
24
+ enabled: boolean;
25
+ defaultLevel: number;
26
+ max: number;
27
+ persist: boolean;
28
+ }
29
+
30
+ interface SchemaExpansionContextValue {
31
+ config: SchemaExpansionConfig;
32
+ level: number;
33
+ setLevel: (next: number) => void;
34
+ }
35
+
36
+ const DEFAULT_CONFIG: SchemaExpansionConfig = {
37
+ enabled: false,
38
+ defaultLevel: 0,
39
+ max: 4,
40
+ persist: true,
41
+ };
42
+
43
+ const SchemaExpansionContext = createContext<SchemaExpansionContextValue>({
44
+ config: DEFAULT_CONFIG,
45
+ level: 0,
46
+ setLevel: () => {},
47
+ });
48
+
49
+ const SchemaDepthContext = createContext<number>(0);
50
+
51
+ export function normalizeLevel(value: number | "all" | undefined): number {
52
+ if (value === "all") return Infinity;
53
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
54
+ return Math.floor(value);
55
+ }
56
+ return 0;
57
+ }
58
+
59
+ function readConfig(
60
+ themeConfig: ThemeConfig | undefined
61
+ ): SchemaExpansionConfig {
62
+ const raw = themeConfig?.api?.schemaExpansion;
63
+ if (!raw) return DEFAULT_CONFIG;
64
+ const enabled = raw.enabled ?? false;
65
+ return {
66
+ enabled,
67
+ defaultLevel: normalizeLevel(raw.default),
68
+ max: typeof raw.max === "number" && raw.max > 0 ? Math.floor(raw.max) : 4,
69
+ // Persistence only matters when the reader can change the level via the
70
+ // UI control. When the control is hidden, fall back to the configured
71
+ // default on every visit so it isn't shadowed by a stale localStorage
72
+ // value from a session where the control used to be enabled.
73
+ persist: enabled ? (raw.persist ?? true) : false,
74
+ };
75
+ }
76
+
77
+ function readPersistedLevel(): number | undefined {
78
+ if (typeof window === "undefined") return undefined;
79
+ try {
80
+ const stored = window.localStorage.getItem(SCHEMA_EXPANSION_STORAGE_KEY);
81
+ if (stored === null) return undefined;
82
+ if (stored === "all") return Infinity;
83
+ const parsed = parseInt(stored, 10);
84
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : undefined;
85
+ } catch {
86
+ return undefined;
87
+ }
88
+ }
89
+
90
+ function writePersistedLevel(level: number): void {
91
+ if (typeof window === "undefined") return;
92
+ try {
93
+ const value = level === Infinity ? "all" : String(level);
94
+ window.localStorage.setItem(SCHEMA_EXPANSION_STORAGE_KEY, value);
95
+ } catch {
96
+ // ignore quota / disabled storage
97
+ }
98
+ }
99
+
100
+ export const SchemaExpansionProvider: React.FC<{
101
+ children: React.ReactNode;
102
+ }> = ({ children }) => {
103
+ const { siteConfig } = useDocusaurusContext();
104
+ const themeConfig = siteConfig.themeConfig as ThemeConfig | undefined;
105
+ const config = useMemo(() => readConfig(themeConfig), [themeConfig]);
106
+
107
+ const [level, setLevelState] = useState<number>(config.defaultLevel);
108
+
109
+ useEffect(() => {
110
+ if (!config.persist) return;
111
+ const persisted = readPersistedLevel();
112
+ if (persisted !== undefined) {
113
+ setLevelState(persisted);
114
+ }
115
+ }, [config.persist]);
116
+
117
+ const setLevel = useCallback(
118
+ (next: number) => {
119
+ setLevelState(next);
120
+ if (config.persist) {
121
+ writePersistedLevel(next);
122
+ }
123
+ },
124
+ [config.persist]
125
+ );
126
+
127
+ const value = useMemo(
128
+ () => ({ config, level, setLevel }),
129
+ [config, level, setLevel]
130
+ );
131
+
132
+ return (
133
+ <SchemaExpansionContext.Provider value={value}>
134
+ {children}
135
+ </SchemaExpansionContext.Provider>
136
+ );
137
+ };
138
+
139
+ export const SchemaDepthProvider: React.FC<{
140
+ depth: number;
141
+ children: React.ReactNode;
142
+ }> = ({ depth, children }) => (
143
+ <SchemaDepthContext.Provider value={depth}>
144
+ {children}
145
+ </SchemaDepthContext.Provider>
146
+ );
147
+
148
+ export function useSchemaExpansion(): SchemaExpansionContextValue {
149
+ return useContext(SchemaExpansionContext);
150
+ }
151
+
152
+ export function useSchemaDepth(): number {
153
+ return useContext(SchemaDepthContext);
154
+ }
@@ -0,0 +1,236 @@
1
+ /* ============================================================================
2
+ * Copyright (c) Palo Alto Networks
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
+ useCallback,
10
+ useEffect,
11
+ useId,
12
+ useLayoutEffect,
13
+ useMemo,
14
+ useRef,
15
+ useState,
16
+ } from "react";
17
+
18
+ import { translate } from "@docusaurus/Translate";
19
+ import clsx from "clsx";
20
+
21
+ import { useSchemaExpansion } from "./context";
22
+
23
+ export {
24
+ SchemaExpansionProvider,
25
+ SchemaDepthProvider,
26
+ useSchemaExpansion,
27
+ useSchemaDepth,
28
+ normalizeLevel,
29
+ SCHEMA_EXPANSION_STORAGE_KEY,
30
+ } from "./context";
31
+
32
+ const ALL_VALUE = Number.POSITIVE_INFINITY;
33
+
34
+ const ExpandIcon: React.FC = () => (
35
+ <svg
36
+ aria-hidden="true"
37
+ focusable="false"
38
+ width="14"
39
+ height="14"
40
+ viewBox="0 0 16 16"
41
+ fill="none"
42
+ stroke="currentColor"
43
+ strokeWidth="1.6"
44
+ strokeLinecap="round"
45
+ strokeLinejoin="round"
46
+ >
47
+ <polyline points="4 6 8 2 12 6" />
48
+ <polyline points="4 10 8 14 12 10" />
49
+ </svg>
50
+ );
51
+
52
+ const SchemaExpansionControl: React.FC = () => {
53
+ const { config, level, setLevel } = useSchemaExpansion();
54
+ const [open, setOpen] = useState(false);
55
+ const [coords, setCoords] = useState<{ top: number; right: number } | null>(
56
+ null
57
+ );
58
+ const buttonRef = useRef<HTMLButtonElement | null>(null);
59
+ const popoverRef = useRef<HTMLDivElement | null>(null);
60
+ const optionRefs = useRef<Array<HTMLButtonElement | null>>([]);
61
+ const popoverId = useId();
62
+
63
+ const options = useMemo(() => {
64
+ const numbers = Array.from({ length: config.max + 1 }, (_, i) => i);
65
+ return [...numbers, ALL_VALUE];
66
+ }, [config.max]);
67
+
68
+ const activeIndex = useMemo(() => {
69
+ const idx = options.indexOf(level);
70
+ return idx >= 0 ? idx : 0;
71
+ }, [options, level]);
72
+
73
+ const updatePosition = useCallback(() => {
74
+ if (!buttonRef.current || typeof window === "undefined") return;
75
+ const rect = buttonRef.current.getBoundingClientRect();
76
+ setCoords({
77
+ top: rect.bottom + 4,
78
+ right: window.innerWidth - rect.right,
79
+ });
80
+ }, []);
81
+
82
+ useLayoutEffect(() => {
83
+ if (!open) return;
84
+ updatePosition();
85
+ const handleScroll = () => setOpen(false);
86
+ window.addEventListener("scroll", handleScroll, true);
87
+ window.addEventListener("resize", updatePosition);
88
+ return () => {
89
+ window.removeEventListener("scroll", handleScroll, true);
90
+ window.removeEventListener("resize", updatePosition);
91
+ };
92
+ }, [open, updatePosition]);
93
+
94
+ useEffect(() => {
95
+ if (!open) return;
96
+ optionRefs.current[activeIndex]?.focus();
97
+ }, [open, activeIndex]);
98
+
99
+ useEffect(() => {
100
+ if (!open) return;
101
+ const handlePointer = (event: MouseEvent) => {
102
+ const target = event.target as Node;
103
+ if (buttonRef.current?.contains(target)) return;
104
+ if (popoverRef.current?.contains(target)) return;
105
+ setOpen(false);
106
+ };
107
+ const handleKey = (event: KeyboardEvent) => {
108
+ if (event.key === "Escape") {
109
+ event.stopPropagation();
110
+ setOpen(false);
111
+ buttonRef.current?.focus();
112
+ }
113
+ };
114
+ document.addEventListener("mousedown", handlePointer);
115
+ document.addEventListener("keydown", handleKey);
116
+ return () => {
117
+ document.removeEventListener("mousedown", handlePointer);
118
+ document.removeEventListener("keydown", handleKey);
119
+ };
120
+ }, [open]);
121
+
122
+ const choose = useCallback(
123
+ (next: number) => {
124
+ setLevel(next);
125
+ setOpen(false);
126
+ buttonRef.current?.focus();
127
+ },
128
+ [setLevel]
129
+ );
130
+
131
+ const onMenuKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
132
+ if (event.key !== "ArrowRight" && event.key !== "ArrowLeft") return;
133
+ event.preventDefault();
134
+ const current = optionRefs.current.findIndex(
135
+ (el) => el === document.activeElement
136
+ );
137
+ if (current < 0) return;
138
+ const next =
139
+ event.key === "ArrowRight"
140
+ ? (current + 1) % options.length
141
+ : (current - 1 + options.length) % options.length;
142
+ optionRefs.current[next]?.focus();
143
+ };
144
+
145
+ if (!config.enabled) return null;
146
+
147
+ const buttonLabel = translate({
148
+ id: "theme.openapi.schema.expansion.button",
149
+ message: "Schema expansion depth",
150
+ description: "Aria/title tooltip for the schema expansion icon button",
151
+ });
152
+ const menuLabel = translate({
153
+ id: "theme.openapi.schema.expansion.menu",
154
+ message: "Schema expansion depth options",
155
+ description: "Accessible label for the expansion options menu",
156
+ });
157
+ const allLabel = translate({
158
+ id: "theme.openapi.schema.expansion.all",
159
+ message: "All",
160
+ description: "Label for the expand-all option",
161
+ });
162
+
163
+ return (
164
+ <span className="openapi-schema-expansion">
165
+ <button
166
+ ref={buttonRef}
167
+ type="button"
168
+ className="openapi-schema-expansion__trigger"
169
+ aria-haspopup="menu"
170
+ aria-expanded={open}
171
+ aria-controls={open ? popoverId : undefined}
172
+ aria-label={buttonLabel}
173
+ title={buttonLabel}
174
+ onClick={(event) => {
175
+ event.preventDefault();
176
+ event.stopPropagation();
177
+ setOpen((prev) => !prev);
178
+ }}
179
+ >
180
+ <ExpandIcon />
181
+ </button>
182
+ {open && coords && (
183
+ <div
184
+ ref={popoverRef}
185
+ id={popoverId}
186
+ role="menu"
187
+ aria-label={menuLabel}
188
+ className="openapi-schema-expansion__popover"
189
+ style={{ top: coords.top, right: coords.right }}
190
+ onKeyDown={onMenuKeyDown}
191
+ onClick={(event) => {
192
+ event.preventDefault();
193
+ event.stopPropagation();
194
+ }}
195
+ >
196
+ {options.map((value, index) => {
197
+ const isAll = value === ALL_VALUE;
198
+ const label = isAll ? allLabel : String(value);
199
+ const optionAriaLabel = isAll
200
+ ? allLabel
201
+ : translate(
202
+ {
203
+ id: "theme.openapi.schema.expansion.depthOption",
204
+ message: "Expand to depth {depth}",
205
+ description: "Accessible label for a depth option",
206
+ },
207
+ { depth: value }
208
+ );
209
+ const isActive = level === value;
210
+ return (
211
+ <button
212
+ key={isAll ? "all" : value}
213
+ ref={(el) => {
214
+ optionRefs.current[index] = el;
215
+ }}
216
+ type="button"
217
+ role="menuitemradio"
218
+ aria-checked={isActive}
219
+ aria-label={optionAriaLabel}
220
+ tabIndex={index === activeIndex ? 0 : -1}
221
+ className={clsx("openapi-schema-expansion__option", {
222
+ "openapi-schema-expansion__option--active": isActive,
223
+ })}
224
+ onClick={() => choose(value)}
225
+ >
226
+ {label}
227
+ </button>
228
+ );
229
+ })}
230
+ </div>
231
+ )}
232
+ </span>
233
+ );
234
+ };
235
+
236
+ export default SchemaExpansionControl;
@@ -10,7 +10,6 @@ import React, { ReactNode } from "react";
10
10
  import { translate } from "@docusaurus/Translate";
11
11
  import { Example } from "@theme/Example";
12
12
  import Markdown from "@theme/Markdown";
13
- import { OPENAPI_SCHEMA_ITEM } from "@theme/translationIds";
14
13
  import clsx from "clsx";
15
14
 
16
15
  import { getQualifierMessage } from "../../markdown/schema";
@@ -41,11 +40,11 @@ const transformEnumDescriptions = (
41
40
  const getEnumDescriptionMarkdown = (enumDescriptions?: [string, string][]) => {
42
41
  if (enumDescriptions?.length) {
43
42
  const enumValue = translate({
44
- id: OPENAPI_SCHEMA_ITEM.ENUM_VALUE,
43
+ id: "theme.openapi.schemaItem.enumValue",
45
44
  message: "Enum Value",
46
45
  });
47
46
  const description = translate({
48
- id: OPENAPI_SCHEMA_ITEM.ENUM_DESCRIPTION,
47
+ id: "theme.openapi.schemaItem.enumDescription",
49
48
  message: "Description",
50
49
  });
51
50
  return `| ${enumValue} | ${description} |
@@ -97,20 +96,29 @@ export default function SchemaItem(props: Props) {
97
96
  Array.isArray(required) ? required.includes(name) : required,
98
97
  () => (
99
98
  <span className="openapi-schema__required">
100
- {translate({ id: OPENAPI_SCHEMA_ITEM.REQUIRED, message: "required" })}
99
+ {translate({
100
+ id: "theme.openapi.schemaItem.required",
101
+ message: "required",
102
+ })}
101
103
  </span>
102
104
  )
103
105
  );
104
106
 
105
107
  const renderDeprecated = guard(deprecated, () => (
106
108
  <span className="openapi-schema__deprecated">
107
- {translate({ id: OPENAPI_SCHEMA_ITEM.DEPRECATED, message: "deprecated" })}
109
+ {translate({
110
+ id: "theme.openapi.schemaItem.deprecated",
111
+ message: "deprecated",
112
+ })}
108
113
  </span>
109
114
  ));
110
115
 
111
116
  const renderNullable = guard(nullable, () => (
112
117
  <span className="openapi-schema__nullable">
113
- {translate({ id: OPENAPI_SCHEMA_ITEM.NULLABLE, message: "nullable" })}
118
+ {translate({
119
+ id: "theme.openapi.schemaItem.nullable",
120
+ message: "nullable",
121
+ })}
114
122
  </span>
115
123
  ));
116
124
 
@@ -148,7 +156,7 @@ export default function SchemaItem(props: Props) {
148
156
  <div>
149
157
  <strong>
150
158
  {translate({
151
- id: OPENAPI_SCHEMA_ITEM.DEFAULT_VALUE,
159
+ id: "theme.openapi.schemaItem.defaultValue",
152
160
  message: "Default value:",
153
161
  })}{" "}
154
162
  </strong>
@@ -162,7 +170,7 @@ export default function SchemaItem(props: Props) {
162
170
  <div>
163
171
  <strong>
164
172
  {translate({
165
- id: OPENAPI_SCHEMA_ITEM.DEFAULT_VALUE,
173
+ id: "theme.openapi.schemaItem.defaultValue",
166
174
  message: "Default value:",
167
175
  })}{" "}
168
176
  </strong>
@@ -182,7 +190,7 @@ export default function SchemaItem(props: Props) {
182
190
  <div>
183
191
  <strong>
184
192
  {translate({
185
- id: OPENAPI_SCHEMA_ITEM.CONSTANT_VALUE,
193
+ id: "theme.openapi.schemaItem.constantValue",
186
194
  message: "Constant value:",
187
195
  })}{" "}
188
196
  </strong>
@@ -196,7 +204,7 @@ export default function SchemaItem(props: Props) {
196
204
  <div>
197
205
  <strong>
198
206
  {translate({
199
- id: OPENAPI_SCHEMA_ITEM.CONSTANT_VALUE,
207
+ id: "theme.openapi.schemaItem.constantValue",
200
208
  message: "Constant value:",
201
209
  })}{" "}
202
210
  </strong>
@@ -14,17 +14,18 @@ import React, {
14
14
  LegacyRef,
15
15
  } from "react";
16
16
 
17
+ import useIsBrowser from "@docusaurus/useIsBrowser";
18
+ import clsx from "clsx";
19
+ import flatten from "lodash/flatten";
20
+
21
+ import { useScrollPositionBlocker } from "@theme/utils/scrollUtils";
17
22
  import {
18
23
  sanitizeTabsChildren,
19
24
  type TabItemProps,
20
25
  TabProps,
21
26
  TabsProvider,
22
- useScrollPositionBlocker,
23
27
  useTabsContextValue,
24
- } from "@docusaurus/theme-common/internal";
25
- import useIsBrowser from "@docusaurus/useIsBrowser";
26
- import clsx from "clsx";
27
- import flatten from "lodash/flatten";
28
+ } from "@theme/utils/tabsUtils";
28
29
 
29
30
  export interface SchemaTabsProps extends TabProps {
30
31
  /**
@@ -14,7 +14,6 @@ import Markdown from "@theme/Markdown";
14
14
  import ResponseHeaders from "@theme/ResponseHeaders";
15
15
  import ResponseSchema from "@theme/ResponseSchema";
16
16
  import TabItem from "@theme/TabItem";
17
- import { OPENAPI_STATUS_CODES } from "@theme/translationIds";
18
17
  import type { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
19
18
 
20
19
  interface Props {
@@ -55,7 +54,7 @@ const StatusCodes: React.FC<Props> = ({ label, id, responses }: any) => {
55
54
  <summary>
56
55
  <strong>
57
56
  {translate({
58
- id: OPENAPI_STATUS_CODES.RESPONSE_HEADERS,
57
+ id: "theme.openapi.statusCodes.responseHeaders",
59
58
  message: "Response Headers",
60
59
  })}
61
60
  </strong>
@@ -67,7 +66,7 @@ const StatusCodes: React.FC<Props> = ({ label, id, responses }: any) => {
67
66
  )}
68
67
  <ResponseSchema
69
68
  title={translate({
70
- id: OPENAPI_STATUS_CODES.SCHEMA_TITLE,
69
+ id: "theme.openapi.statusCodes.schemaTitle",
71
70
  message: "Schema",
72
71
  })}
73
72
  body={{ content: response.content }}
@@ -0,0 +1,61 @@
1
+ /* ============================================================================
2
+ * Portions Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ * Portions Copyright (c) Palo Alto Networks
4
+ *
5
+ * Swizzled from @docusaurus/theme-classic/src/theme/TabItem/index.tsx (MIT).
6
+ * Re-points useTabs to our vendored tabsUtils so that <TabItem> reads the same
7
+ * context our swizzled <Tabs> and OpenAPI tab variants (ApiTabs, MimeTabs,
8
+ * SchemaTabs, etc.) provide. See:
9
+ * https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/issues/1140
10
+ *
11
+ * This source code is licensed under the MIT license found in the
12
+ * LICENSE file in the root directory of this source tree.
13
+ * ========================================================================== */
14
+
15
+ import React, { type ReactNode } from "react";
16
+
17
+ import clsx from "clsx";
18
+
19
+ import { type TabItemProps, useTabs } from "@theme/utils/tabsUtils";
20
+
21
+ type Props = TabItemProps;
22
+ import styles from "./styles.module.css";
23
+
24
+ function TabItemPanel({
25
+ children,
26
+ className,
27
+ hidden,
28
+ }: {
29
+ children: ReactNode;
30
+ className?: string;
31
+ hidden?: boolean;
32
+ }) {
33
+ return (
34
+ <div
35
+ role="tabpanel"
36
+ className={clsx(styles.tabItem, className)}
37
+ {...{ hidden }}
38
+ >
39
+ {children}
40
+ </div>
41
+ );
42
+ }
43
+
44
+ export default function TabItem({
45
+ children,
46
+ className,
47
+ value,
48
+ }: Props): ReactNode {
49
+ const { selectedValue, lazy } = useTabs();
50
+ const isSelected = value === selectedValue;
51
+
52
+ if (!isSelected && lazy) {
53
+ return null;
54
+ }
55
+
56
+ return (
57
+ <TabItemPanel className={className} hidden={!isSelected}>
58
+ {children}
59
+ </TabItemPanel>
60
+ );
61
+ }