├── .eslintrc.js
├── .github
└── CODEOWNERS
├── .gitignore
├── .prettierignore
├── .prettierrc
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── bin
├── commands.sh
└── postbuild.js
├── gatsby-browser.js
├── gatsby-config.js
├── gatsby-node.js
├── gatsby-ssr.js
├── package-lock.json
├── package.json
├── src
├── components
│ ├── accessible-svg.js
│ ├── architecture-diagram.js
│ ├── breadcrumbs.js
│ ├── browser-resize-tracking.js
│ ├── cloudflare-logo.js
│ ├── code-block.js
│ ├── docs-code-examples-overview.js
│ ├── docs-footer.js
│ ├── docs-mobile-title-header.js
│ ├── docs-nav-logo-lockup.js
│ ├── docs-page.js
│ ├── docs-product-logo.js
│ ├── docs-search.js
│ ├── docs-sidebar-more-dropdown.js
│ ├── docs-sidebar-nav-data.js
│ ├── docs-sidebar-nav-item.js
│ ├── docs-sidebar-nav-section.js
│ ├── docs-sidebar-nav.js
│ ├── docs-sidebar-title-section.js
│ ├── docs-sidebar.js
│ ├── docs-table-of-contents.js
│ ├── docs-title.js
│ ├── docs-toolbar.js
│ ├── docs-tutorials.js
│ ├── dropdown.js
│ ├── handle-mobile-page-navigations.js
│ ├── icons
│ │ ├── base.js
│ │ ├── external-link.js
│ │ └── nav-menu.js
│ ├── mdx-custom-renderer.js
│ ├── mdx
│ │ ├── anchor-link.js
│ │ ├── aside.js
│ │ ├── button-group.js
│ │ ├── button.js
│ │ ├── code-block.js
│ │ ├── code.js
│ │ ├── content-column.js
│ │ ├── custom-syntax-highlighting.js
│ │ ├── definitions.js
│ │ ├── demo.js
│ │ ├── directory-listing.js
│ │ ├── example.js
│ │ ├── headers.js
│ │ ├── inline-code.js
│ │ ├── param-type.js
│ │ ├── prop-meta.js
│ │ ├── root.js
│ │ ├── stream-video.js
│ │ ├── table-wrap.js
│ │ ├── type-link.js
│ │ ├── type.js
│ │ └── youtube.js
│ ├── network-map.js
│ ├── scrollbars-with-scroll-shadows.js
│ ├── seo.js
│ ├── smooth-scroll-hash-changes.js
│ ├── theme-toggle.js
│ └── worker-starter.js
├── constants
│ └── sidebar-collapse-transition-duration.js
├── css
│ └── docs
│ │ └── components
│ │ ├── architecture-diagram.css
│ │ ├── docs-body.css
│ │ ├── docs-code-examples-overview.css
│ │ ├── docs-content.css
│ │ ├── docs-footer.css
│ │ ├── docs-markdown.css
│ │ ├── docs-mobile-header.css
│ │ ├── docs-mobile-nav-backdrop.css
│ │ ├── docs-mobile-title-header.css
│ │ ├── docs-nav-logo-lockup.css
│ │ ├── docs-noscript.css
│ │ ├── docs-page.css
│ │ ├── docs-search.css
│ │ ├── docs-sidebar.css
│ │ ├── docs-table-of-contents.css
│ │ ├── docs-toolbar.css
│ │ ├── docs-tutorials.css
│ │ ├── skip-nav-link.css
│ │ ├── tags-filter.css
│ │ └── worker-starter.css
├── html.js
├── images
│ └── cloudflare-icon.png
├── pages
│ └── 404.js
└── utils
│ ├── animate.js
│ ├── generate-nav-tree.js
│ ├── get-breadcrumbs.js
│ ├── get-cloudflare-docs-config.js
│ ├── get-normalized-path.js
│ ├── get-order.js
│ ├── get-page-by-path.js
│ ├── get-page-title.js
│ ├── get-page-type.js
│ ├── get-parent-path.js
│ ├── get-path-prefix.js
│ ├── get-table-of-contents.js
│ ├── get-unique-readable-id.js
│ ├── google-analytics.js
│ ├── has-breadcrumbs.js
│ ├── is-mobile.js
│ ├── mobile-sidebar-manipulation.js
│ └── user-prefers-reduced-motion.js
└── workers-site
├── .cargo-ok
├── .gitignore
├── index.js
├── package-lock.json
└── package.json
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | globals: {
3 | __PATH_PREFIX__: true,
4 | },
5 | extends: `react-app`,
6 | "rules": {
7 | /* Don’t require
for proxy elements in https://github.com/nfl/react-helmet */
8 | "jsx-a11y/html-has-lang": 0
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | src/ @adamschwartz
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # TODO
2 | TODO
3 |
4 | # Logs
5 | logs
6 | *.log
7 | npm-debug.log*
8 | yarn-debug.log*
9 | yarn-error.log*
10 |
11 | # Runtime data
12 | pids
13 | *.pid
14 | *.seed
15 | *.pid.lock
16 |
17 | # Directory for instrumented libs generated by jscoverage/JSCover
18 | lib-cov
19 |
20 | # Coverage directory used by tools like istanbul
21 | coverage
22 |
23 | # nyc test coverage
24 | .nyc_output
25 |
26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
27 | .grunt
28 |
29 | # Bower dependency directory (https://bower.io/)
30 | bower_components
31 |
32 | # node-waf configuration
33 | .lock-wscript
34 |
35 | # Compiled binary addons (http://nodejs.org/api/addons.html)
36 | build/Release
37 |
38 | # Dependency directories
39 | node_modules/
40 | jspm_packages/
41 |
42 | # TypeScript v1 declaration files
43 | typings/
44 |
45 | # Optional npm cache directory
46 | .npm
47 |
48 | # Optional eslint cache
49 | .eslintcache
50 |
51 | # Optional REPL history
52 | .node_repl_history
53 |
54 | # Output of 'npm pack'
55 | *.tgz
56 |
57 | # dotenv environment variable files
58 | .env*
59 |
60 | # gatsby files
61 | .cache/
62 | public
63 |
64 | # Mac files
65 | .DS_Store
66 |
67 | # Yarn
68 | yarn-error.log
69 | .pnp/
70 | .pnp.js
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # Workers dist
76 | dist/
77 |
78 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .cache
2 | package.json
3 | package-lock.json
4 | public
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "lf",
3 | "semi": false,
4 | "singleQuote": false,
5 | "tabWidth": 2,
6 | "trailingComma": "es5"
7 | }
8 |
--------------------------------------------------------------------------------
/LICENSE-APACHE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Cloudflare
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cloudflare Docs Engine
2 |
3 | Cloudflare’s open-source tool for building documentation.
4 |
5 | https://developers.cloudflare.com/docs-engine
6 |
7 | ## Known issues
8 |
9 | - [@gatsbyjs/gatsby#17506](https://github.com/gatsbyjs/gatsby/issues/17506) Console warning about `lazy=load` images missing dimensions. This is a known issue in Gatsby and the [recommendation as of Sept, 2019](https://github.com/gatsbyjs/gatsby/issues/17506#issuecomment-529904482) is to ignore it.
10 | - Hard page loads with hashes don’t start scrolled when developing locally (e.g. `http://localhost:8000/#docs-content`).
11 |
--------------------------------------------------------------------------------
/bin/commands.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | realpath() {
5 | [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
6 | }
7 |
8 |
9 | docs_engine_path=$(dirname $(dirname $(realpath "$0")))
10 | parent_path=$(dirname $docs_engine_path)
11 | parent_folder_name=$(basename $parent_path)
12 |
13 | if [ "$parent_folder_name" != "node_modules" ]; then
14 | echo "Failed: expected the Cloudflare docs enginee to be inside node_modules"
15 | exit
16 | fi
17 |
18 | project_path=$(dirname $(dirname $docs_engine_path))
19 |
20 |
21 | if [ "$1" = "ghactionsbootstrap" ]; then
22 | cd $project_path
23 |
24 | echo "Deleting .docs"
25 | rm -rf .docs
26 |
27 | echo "Creating .docs directory"
28 | mkdir .docs
29 |
30 | echo "Moving cloudflare-docs-engine files into .docs"
31 | cp -r node_modules/cloudflare-docs-engine/* .docs
32 |
33 | echo "Entering .docs"
34 | cd .docs
35 |
36 | echo "Running npm install inside .docs"
37 | npm install
38 | fi
39 |
40 |
41 | if [ "$1" = "bootstrap" ]; then
42 | cd $project_path
43 |
44 | echo "Deleting .docs"
45 | rm -rf .docs
46 |
47 | echo "Moving cloudflare-docs-engine files into .docs"
48 | cp -r node_modules/cloudflare-docs-engine/ .docs
49 |
50 | echo "Entering .docs"
51 | cd .docs
52 |
53 | echo "Removing existing node_modules (local npm link case)"
54 | rm -rf node_modules/
55 |
56 | echo "Running npm install inside .docs"
57 | npm install
58 | fi
59 |
60 |
61 | copysrc() {
62 | cd $project_path
63 |
64 | echo "Entering .docs"
65 | cd .docs
66 |
67 | echo "Copying docs-config.js into .docs"
68 | cp -r ../docs-config.js ./
69 |
70 | if [ -e ../static ]; then
71 | echo "Copying static into .docs"
72 | cp -r ../static ./
73 | fi
74 |
75 | echo "Entering .docs/src/"
76 | cd src
77 |
78 | echo "Copying content into .docs/src/"
79 | cp -r ../../src/content ./
80 | }
81 |
82 |
83 | if [ "$1" = "develop" ]; then
84 | copysrc
85 |
86 | cd $project_path
87 |
88 | echo "Entering .docs"
89 | cd .docs
90 |
91 | echo "Running npm run clean"
92 | npm run clean
93 |
94 | echo "Running npm run develop"
95 | npm run develop -- "${@:2}"
96 | fi
97 |
98 |
99 | if [ "$1" = "build" ]; then
100 | copysrc
101 |
102 | cd $project_path
103 |
104 | echo "Entering .docs"
105 | cd .docs
106 |
107 | echo "Running npm run build"
108 | npm run build -- "${@:2}"
109 |
110 | # We must run from inside `.docs/`
111 | echo "Running bin/postbuild.js"
112 | node bin/postbuild.js
113 | fi
114 |
115 |
116 | if [ "$1" = "serve" ]; then
117 | cd $project_path
118 |
119 | echo "Entering .docs"
120 | cd .docs
121 |
122 | echo "Running npm run serve"
123 | npm run serve -- "${@:2}"
124 | fi
125 |
126 |
127 | if [ "$1" = "savechanges" ]; then
128 | cd $project_path
129 |
130 | cp -r .docs/src/content/ src/content/
131 | fi
132 |
--------------------------------------------------------------------------------
/bin/postbuild.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs")
2 | const { exec } = require("child_process")
3 | const { pathPrefix } = require("../docs-config.js")
4 |
5 | console.log("Running postbuild.js")
6 |
7 | if (!pathPrefix && pathPrefix !== "") {
8 | console.error(`docs-config.js must have a pathPrefix set`)
9 | }
10 |
11 | if (pathPrefix.length && !pathPrefix.startsWith("/")) {
12 | console.error(`docs-config.js pathPrefix must start with a forward slash "/"`)
13 | return
14 | }
15 |
16 | const handleExec = (completed) => {
17 | return (error, stdout, stderr) => {
18 | if (error) {
19 | console.error(error)
20 |
21 | } else {
22 | const trimmedStdout = stdout.trim()
23 | if (trimmedStdout) console.log(trimmedStdout)
24 |
25 | const trimmedStderr = stderr.trim()
26 | if (trimmedStderr) console.log(trimmedStderr)
27 |
28 | completed()
29 | }
30 | }
31 | }
32 |
33 | const robots = `User-agent: *\nDisallow: /`
34 |
35 | if (pathPrefix !== "") {
36 | // We are running from inside `.docs/`
37 | const dir = "$PWD/"
38 | const folderName = pathPrefix.substr(1)
39 |
40 | exec(`mv "${dir}public" "${dir}${folderName}"`, handleExec(() => {
41 | exec(`mkdir "${dir}public"`, handleExec(() => {
42 | exec(`mv "${dir}${folderName}" "${dir}public/"`, handleExec(() => {
43 | console.log("Completed postbuild")
44 | if (process.env.WORKERS_ENV && process.env.WORKERS_ENV === "development") {
45 | console.log("Building robots.txt for development environment")
46 | fs.writeFile(`${process.env.PWD}/public/robots.txt`, robots, err => err && console.error(err))
47 | }
48 | }))
49 | }))
50 | }))
51 | }
52 |
--------------------------------------------------------------------------------
/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | // See: https://www.gatsbyjs.org/docs/browser-apis/
2 |
3 | // Required until wider support
4 | // https://caniuse.com/#search=focus-visible
5 | import "focus-visible-polyfill"
6 |
7 | import "@cloudflare/cloudflare-brand-assets/css/global/box-sizing.css"
8 | import "@cloudflare/cloudflare-brand-assets/css/global/element-normalization.css"
9 | import "@cloudflare/cloudflare-brand-assets/css/global/sizes-variables.css"
10 | import "@cloudflare/cloudflare-brand-assets/css/global/font-variables.css"
11 | import "@cloudflare/cloudflare-brand-assets/css/global/brand-color-variables.css"
12 | import "@cloudflare/cloudflare-brand-assets/css/global/theme-color-variables.css"
13 | import "@cloudflare/cloudflare-brand-assets/css/global/theme-helpers.css"
14 | import "@cloudflare/cloudflare-brand-assets/css/global/selection-color.css"
15 | import "@cloudflare/cloudflare-brand-assets/css/global/html.css"
16 |
17 | import "@cloudflare/cloudflare-brand-assets/css/helpers/desktop-and-mobile-only.css"
18 | import "@cloudflare/cloudflare-brand-assets/css/helpers/is-smooth-scrolling.css"
19 | import "@cloudflare/cloudflare-brand-assets/css/helpers/is-visually-hidden.css"
20 | import "@cloudflare/cloudflare-brand-assets/css/helpers/with-styled-webkit-scrollbars.css"
21 |
22 | // TODO: should these be imported by their respective components?
23 | import "@cloudflare/cloudflare-brand-assets/css/components/aspect-ratio.css"
24 | import "@cloudflare/cloudflare-brand-assets/css/components/breadcrumbs.css"
25 | import "@cloudflare/cloudflare-brand-assets/css/components/button.css"
26 | import "@cloudflare/cloudflare-brand-assets/css/components/cloudflare-logo.css"
27 | import "@cloudflare/cloudflare-brand-assets/css/components/cloudflare-workers-logo.css"
28 | import "@cloudflare/cloudflare-brand-assets/css/components/code-block.css"
29 | import "@cloudflare/cloudflare-brand-assets/css/components/theme-toggle.css"
30 | import "@cloudflare/cloudflare-brand-assets/css/components/dropdown.css"
31 | import "@cloudflare/cloudflare-brand-assets/css/components/inline-code.css"
32 | import "@cloudflare/cloudflare-brand-assets/css/components/link.css"
33 | import "@cloudflare/cloudflare-brand-assets/css/components/network-map.css"
34 | import "@cloudflare/cloudflare-brand-assets/css/components/scrollbars.css"
35 | import "@cloudflare/cloudflare-brand-assets/css/components/stream-video.css"
36 | import "@cloudflare/cloudflare-brand-assets/css/components/superscript.css"
37 | import "@cloudflare/cloudflare-brand-assets/css/components/tooltip.css"
38 |
39 | // TODO: should these be imported by their respective components?
40 | import "./src/css/docs/components/skip-nav-link.css"
41 | import "./src/css/docs/components/tags-filter.css"
42 | import "./src/css/docs/components/worker-starter.css"
43 | import "./src/css/docs/components/architecture-diagram.css"
44 | import "./src/css/docs/components/docs-noscript.css"
45 | import "./src/css/docs/components/docs-nav-logo-lockup.css"
46 | import "./src/css/docs/components/docs-page.css"
47 | import "./src/css/docs/components/docs-sidebar.css"
48 | import "./src/css/docs/components/docs-body.css"
49 | import "./src/css/docs/components/docs-table-of-contents.css"
50 | import "./src/css/docs/components/docs-content.css"
51 | import "./src/css/docs/components/docs-markdown.css"
52 | import "./src/css/docs/components/docs-toolbar.css"
53 | import "./src/css/docs/components/docs-search.css"
54 | import "./src/css/docs/components/docs-mobile-title-header.css"
55 | import "./src/css/docs/components/docs-mobile-nav-backdrop.css"
56 | import "./src/css/docs/components/docs-footer.css"
57 | import "./src/css/docs/components/docs-code-examples-overview.css"
58 | import "./src/css/docs/components/docs-tutorials.css"
59 |
--------------------------------------------------------------------------------
/gatsby-config.js:
--------------------------------------------------------------------------------
1 | const docsConfig = require("./docs-config.js")
2 |
3 | const isProduction = process.env.NODE_ENV === "production"
4 |
5 | const getProduct = (name) => {
6 | const repo = "@cloudflare/cloudflare-brand-assets"
7 | const dir = "resources/product-icons/"
8 | return `./node_modules/${repo}/${dir}/${name}.js`
9 | }
10 |
11 | const products = [
12 | "1.1.1.1",
13 | "access",
14 | "analytics",
15 | "api",
16 | "automatic-platform-optimization",
17 | "argo-tunnel",
18 | "bots",
19 | "byoip",
20 | "cache",
21 | "client-ip-geolocation",
22 | "cloudflare-for-teams",
23 | "cloudflare-one",
24 | "distributed-web",
25 | "docs-engine",
26 | "events",
27 | "firewall",
28 | "fundamentals",
29 | "gateway",
30 | "http3",
31 | "images",
32 | "load-balancing",
33 | "logs",
34 | "magic-transit",
35 | "magic-wan",
36 | "mobile-sdk",
37 | "network-interconnect",
38 | "page-shield",
39 | "railgun",
40 | "randomness-beacon",
41 | "registrar",
42 | "rules",
43 | "spectrum",
44 | "ssl",
45 | "stream",
46 | "tenant",
47 | "terraform",
48 | "time-services",
49 | "waf",
50 | "waiting-room",
51 | "warp-client",
52 | "workers",
53 | ]
54 |
55 | const productIcons = {}
56 | products.forEach(name => {
57 | productIcons[name] = require(getProduct(name)).pathD
58 | })
59 |
60 | if (docsConfig.productLogoPathD && docsConfig.productIconKey) {
61 | return Error("Set either `productLogoPathD` or `productIconKey` in docs-config.js, not both")
62 | }
63 |
64 | if (docsConfig.productIconKey) {
65 | docsConfig.productLogoPathD = productIcons[docsConfig.productIconKey]
66 | }
67 |
68 | const siteMetadata = docsConfig.siteMetadata
69 | siteMetadata.cloudflareDocs = {}
70 | Object.keys(docsConfig).forEach(prop => {
71 | if (prop === "siteMetadata") return
72 | siteMetadata.cloudflareDocs[prop] = docsConfig[prop]
73 | })
74 |
75 | siteMetadata.cloudflareDocs.productIcons = productIcons
76 |
77 | // We exposed friendlier siteMetadata.url to Docs consumers but
78 | // gatsby-plugin-sitemap requires `siteUrl` https://git.io/JUUxW
79 | siteMetadata.siteUrl = siteMetadata.url
80 | delete siteMetadata.url
81 |
82 | module.exports = {
83 | // Deploy production site to the docs config pathPrefix
84 | // but keep local development done at the root due to:
85 | // https://github.com/gatsbyjs/gatsby/issues/16040
86 | pathPrefix: isProduction ? docsConfig.pathPrefix : "",
87 | siteMetadata: siteMetadata,
88 |
89 | plugins: [
90 | "gatsby-plugin-eslint",
91 | "gatsby-plugin-no-sourcemaps",
92 | "gatsby-plugin-react-helmet",
93 | "gatsby-transformer-sharp",
94 | "gatsby-plugin-sharp",
95 | "gatsby-plugin-remove-trailing-slashes",
96 |
97 | // Sets page.updatedAt to the author time of last commit (https://git.io/JfPCj)
98 | "saber-plugin-git-modification-time",
99 |
100 | // Custom sitemap configuration that fixes prefix issues
101 | {
102 | resolve: `gatsby-plugin-sitemap`,
103 | options: {
104 | resolveSiteUrl: () => new URL(siteMetadata.siteUrl).origin,
105 | },
106 | },
107 |
108 | // Prevent nav from (un)mounting on page navigations (https://git.io/JfOKn)
109 | {
110 | resolve: "gatsby-plugin-layout",
111 | options: {
112 | component: require.resolve("./src/components/docs-page.js")
113 | }
114 | },
115 | {
116 | resolve: "gatsby-source-filesystem",
117 | options: {
118 | path: `${__dirname}/src/content/`
119 | }
120 | },
121 | {
122 | resolve: "gatsby-plugin-mdx",
123 | options: {
124 | extensions: [".mdx", ".md"],
125 | gatsbyRemarkPlugins: [
126 | {
127 | resolve: "gatsby-remark-images",
128 | options: {
129 | maxWidth: 1382,
130 | disableBgImageOnAlpha: true
131 | },
132 | },
133 | // Copies linked files from Markdown to public directory (ie for gifs)
134 | `gatsby-remark-copy-linked-files`,
135 | ],
136 | remarkPlugins: [require("remark-slug")]
137 | }
138 | },
139 | {
140 | resolve: "gatsby-plugin-material-ui",
141 | options: {
142 | stylesProvider: {
143 | disableGeneration: true
144 | },
145 | },
146 | },
147 | {
148 | resolve: "gatsby-plugin-manifest",
149 | options: {
150 | name: "Cloudflare docs",
151 | short_name: "Docs",
152 | start_url: "/",
153 | background_color: "#f38020",
154 | theme_color: "#f38020",
155 | display: "minimal-ui"
156 | // icon: "src/images/cloudflare-icon.png"
157 | }
158 | },
159 | // Consider enabling for PWA + offline functionality
160 | // https://gatsby.dev/offline
161 | // 'gatsby-plugin-offline',
162 | ],
163 | }
164 |
--------------------------------------------------------------------------------
/gatsby-node.js:
--------------------------------------------------------------------------------
1 | // See: https://www.gatsbyjs.org/docs/node-apis/
2 |
3 | // https://www.gatsbyjs.org/docs/add-custom-webpack-config/
4 | exports.onCreateWebpackConfig = ({
5 | getConfig,
6 | actions,
7 | plugins,
8 | }) => {
9 | const config = getConfig()
10 |
11 | // Hides "[HMR] ..." logs in devtools
12 | if (config.entry.commons) {
13 | config.entry.commons = config.entry.commons.map(path => (
14 | // Add query param to entry added by Gatsby CLI https://git.io/JvAC5
15 | path.indexOf('/webpack-hot-middleware/client.js?') > -1 ?
16 | path + '&quiet=true' : path
17 | ))
18 | }
19 |
20 | actions.replaceWebpackConfig(config)
21 |
22 | actions.setWebpackConfig({
23 | plugins: [
24 | // Hides React Devtools advertisement in devtools
25 | // https://tinyurl.com/hide-react-devtools-advert
26 | plugins.define({
27 | __REACT_DEVTOOLS_GLOBAL_HOOK__: "({ isDisabled: true })"
28 | })
29 | ]
30 | })
31 | }
32 |
33 |
34 | const { createFilePath } = require("gatsby-source-filesystem")
35 |
36 | exports.onCreateNode = ({ node, actions, getNode }) => {
37 | const { createNodeField } = actions
38 |
39 | if (node.internal.type.toLowerCase() === "mdx") {
40 | const value = createFilePath({ node, getNode }).replace(/(.+)(\/)$/, "$1")
41 |
42 | createNodeField({
43 | name: "slug",
44 | node,
45 | value
46 | })
47 | }
48 | }
49 |
50 |
51 | const path = require("path")
52 |
53 | exports.createPages = async ({ graphql, actions, reporter }) => {
54 | const { createPage } = actions
55 |
56 | const result = await graphql(`
57 | query {
58 | allMdx {
59 | edges {
60 | node {
61 | id
62 | fields {
63 | slug
64 | }
65 | frontmatter {
66 | title
67 | type
68 | order
69 | hidden
70 | hideChildren
71 | breadcrumbs
72 | }
73 | headings(depth: h1) {
74 | value
75 | depth
76 | }
77 | tableOfContents
78 | parent {
79 | ... on File {
80 | modifiedTime(formatString: "YYYY-MM-DD")
81 | relativePath
82 | }
83 | }
84 | }
85 | }
86 | }
87 | }
88 | `)
89 |
90 | if (result.errors) {
91 | reporter.panicOnBuild('ERROR: Loading "createPages" query')
92 | }
93 |
94 | const pages = result.data.allMdx.edges
95 |
96 | pages.forEach(({ node }) => {
97 | createPage({
98 | path: node.fields.slug,
99 | component: path.resolve("./src/components/mdx-custom-renderer.js"),
100 | context: node
101 | })
102 | })
103 | }
104 |
105 | // We implement type definitions in order to prevent
106 | // Gatsby from erroring that it can’t infer the type
107 | // of a GraphQL-queried frontmatter property when
108 | // there are no md(x) files currently using it.
109 | // https://www.gatsbyjs.org/docs/schema-customization/#creating-type-definitions
110 | exports.createSchemaCustomization = ({ actions }) => {
111 | const { createTypes } = actions
112 |
113 | const typeDefs = `
114 | type Mdx implements Node {
115 | frontmatter: Frontmatter
116 | }
117 |
118 | type Frontmatter {
119 | demo: String
120 | breadcrumbs: Boolean
121 | difficulty: String
122 | hidden: Boolean
123 | hideChildren: Boolean
124 | order: Int
125 | summary: String
126 | tags: [String]
127 | title: String
128 | type: String
129 | updated: Date @dateformat
130 | }
131 |
132 | type Site {
133 | pathPrefix: String
134 | }
135 | `
136 |
137 | createTypes(typeDefs)
138 | }
139 |
--------------------------------------------------------------------------------
/gatsby-ssr.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Implement Gatsby's SSR (Server Side Rendering) APIs in this file.
3 | *
4 | * See: https://www.gatsbyjs.org/docs/ssr-apis/
5 | */
6 |
7 | // You can delete this file if you're not using it
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cloudflare-docs-engine",
3 | "private": true,
4 | "description": "Cloudflare Workers documentation engine",
5 | "version": "0.1.0",
6 | "author": "Cloudflare ",
7 | "dependencies": {
8 | "@adaptivelink/pops": "0.2.10",
9 | "@cloudflare/cloudflare-brand-assets": "^4.7.7",
10 | "@material-ui/core": "4.11.0",
11 | "@mdx-js/mdx": "1.6.6",
12 | "@mdx-js/react": "1.6.6",
13 | "@reach/skip-nav": "0.10.5",
14 | "animejs": "3.2.0",
15 | "focus-group": "0.3.1",
16 | "focus-visible-polyfill": "1.0.0",
17 | "gatsby": "2.24.7",
18 | "gatsby-image": "2.4.13",
19 | "gatsby-plugin-layout": "1.3.10",
20 | "gatsby-plugin-manifest": "2.4.18",
21 | "gatsby-plugin-material-ui": "2.1.9",
22 | "gatsby-plugin-mdx": "1.2.25",
23 | "gatsby-plugin-no-sourcemaps": "2.2.0",
24 | "gatsby-plugin-offline": "3.2.17",
25 | "gatsby-plugin-react-helmet": "3.3.10",
26 | "gatsby-plugin-sharp": "2.6.19",
27 | "gatsby-plugin-sitemap": "2.4.11",
28 | "gatsby-remark-copy-linked-files": "2.3.9",
29 | "gatsby-remark-images": "3.3.18",
30 | "gatsby-source-filesystem": "2.3.19",
31 | "gatsby-transformer-remark": "2.8.22",
32 | "gatsby-transformer-sharp": "2.5.11",
33 | "graphql": "14.7.0",
34 | "gray-matter": "4.0.2",
35 | "prism-react-renderer": "1.1.1",
36 | "prismjs": "1.21.0",
37 | "prop-types": "15.7.2",
38 | "react": "16.13.1",
39 | "react-copy-to-clipboard": "5.0.2",
40 | "react-custom-scrollbars": "4.2.1",
41 | "react-dom": "16.13.1",
42 | "react-helmet": "6.1.0",
43 | "react-spring": "8.0.27",
44 | "react-timeago": "4.4.0",
45 | "react-transition-group": "4.4.1",
46 | "remark-slug": "6.0.0",
47 | "saber-plugin-git-modification-time": "0.1.3",
48 | "set-interval-visible": "2.0.0"
49 | },
50 | "engines": {
51 | "node": ">=12.0.0"
52 | },
53 | "devDependencies": {
54 | "eslint-config-react-app": "5.2.1",
55 | "gatsby-plugin-eslint": "2.0.8",
56 | "gatsby-plugin-remove-trailing-slashes": "2.3.11",
57 | "prettier": "1.19.1"
58 | },
59 | "license": "Apache-2.0 OR MIT",
60 | "scripts": {
61 | "build": "npm run clean && gatsby build --prefix-paths",
62 | "build:incremental": "GATSBY_EXPERIMENTAL_PAGE_BUILD_ON_DATA_CHANGES=true gatsby build --log-pages --prefix-paths",
63 | "clean": "gatsby clean",
64 | "develop": "gatsby develop -H 0.0.0.0",
65 | "format": "prettier --write \"**/*.{js,jsx,json,md}\"",
66 | "publish": "npm run build && wrangler publish",
67 | "serve": "gatsby serve",
68 | "start": "npm run develop"
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/components/accessible-svg.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import PropTypes from "prop-types"
3 |
4 | import getUniqueReadableID from "../utils/get-unique-readable-id"
5 |
6 | const AccessibleSVG = props => {
7 | const titleID = getUniqueReadableID("title")
8 | const { title, children, ...svgProps } = props
9 |
10 | return (
11 |
12 | {title}
13 | {children}
14 |
15 | )
16 | }
17 |
18 | AccessibleSVG.defaultProps = {
19 | fill: "currentColor",
20 | role: "img"
21 | }
22 |
23 | AccessibleSVG.propTypes = {
24 | title: PropTypes.string.isRequired
25 | }
26 |
27 | export default AccessibleSVG
28 |
--------------------------------------------------------------------------------
/src/components/architecture-diagram.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | const ArchitectureDiagram = (props) => {
4 | return (
5 | <>
6 |
7 |
8 |
9 |
13 |
17 |
21 |
25 |
29 |
33 |
37 |
41 |
45 |
46 |
47 |
Traditional architecture
48 |
49 |
50 |
51 |
52 |
56 |
60 |
64 |
68 |
72 |
76 |
80 |
84 |
88 |
89 |
90 |
Workers V8 isolates
91 |
92 |
93 |
94 |
95 |
99 |
100 |
103 |
Process overhead
104 |
105 |
106 |
107 |
108 | >
109 | )
110 | }
111 |
112 | export default ArchitectureDiagram
113 |
--------------------------------------------------------------------------------
/src/components/breadcrumbs.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { Link, useStaticQuery, graphql } from "gatsby"
3 |
4 | import getBreadcrumbs from "../utils/get-breadcrumbs"
5 |
6 | const Item = ({ item }) => (
7 |
8 |
9 | {item.title}
10 |
11 |
12 | {Array.isArray(item.items) && (
13 |
14 | {item.items.map(item => (
15 | -
16 | ))}
17 |
18 | )}
19 |
20 | )
21 |
22 | const Breadcrumbs = props => {
23 | const query = useStaticQuery(graphql`
24 | query {
25 | allMdx {
26 | edges {
27 | node {
28 | id
29 | parent {
30 | id
31 | }
32 | fields {
33 | slug
34 | }
35 | frontmatter {
36 | title
37 | }
38 | headings(depth: h1) {
39 | value
40 | depth
41 | }
42 | }
43 | }
44 | }
45 | }
46 | `)
47 |
48 | const pages = query.allMdx.edges
49 | .map(({ node }) => node)
50 |
51 | const { className, location } = props
52 | const breadcrumbs = getBreadcrumbs(pages, location)
53 |
54 | return breadcrumbs.length ? (
55 |
56 |
57 | {breadcrumbs.map(item => (
58 | -
59 | ))}
60 |
61 |
62 | ) : null
63 | }
64 |
65 | Breadcrumbs.defaultProps = {
66 | className: "Breadcrumbs---wrapper"
67 | }
68 |
69 | export default Breadcrumbs
70 |
--------------------------------------------------------------------------------
/src/components/browser-resize-tracking.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | const attr = "is-resizing"
4 |
5 | class SmoothBrowserResizing extends React.Component {
6 |
7 | constructor(props) {
8 | super(props)
9 |
10 | this.handleWindowResize = this.handleWindowResize.bind(this)
11 | }
12 |
13 | componentDidMount() {
14 | window.addEventListener("resize", this.handleWindowResize)
15 | }
16 |
17 | componentWillUnmount() {
18 | window.removeEventListener("resize", this.handleWindowResize)
19 | }
20 |
21 | handleWindowResize() {
22 | clearTimeout(this.resizeTimeout)
23 |
24 | document.documentElement.setAttribute(attr, "")
25 |
26 | this.resizeTimeout = setTimeout(() => {
27 | document.documentElement.removeAttribute(attr)
28 | }, this.props.debounce)
29 | }
30 |
31 | render() {
32 | return null
33 | }
34 | }
35 |
36 | SmoothBrowserResizing.defaultProps = {
37 | debounce: 400
38 | }
39 |
40 | export default SmoothBrowserResizing
41 |
--------------------------------------------------------------------------------
/src/components/cloudflare-logo.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import AccessibleSVG from "./accessible-svg"
3 |
4 | const CloudflareLogo = () => (
5 |
6 |
7 |
8 | )
9 |
10 | export default CloudflareLogo
11 |
--------------------------------------------------------------------------------
/src/components/code-block.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import MDXCodeBlock from "./mdx/code-block"
4 |
5 | const CodeBlock = props => {
6 | return (
7 |
8 |
9 | {props.children}
10 |
11 |
12 | )
13 | }
14 |
15 | export default CodeBlock
16 |
--------------------------------------------------------------------------------
/src/components/docs-code-examples-overview.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { Link, useStaticQuery, graphql } from "gatsby"
3 |
4 | import getPageTitle from "../utils/get-page-title"
5 | import CodeBlock from "./code-block"
6 |
7 | const getCodeBlockFromMDXAST = ast => {
8 | for (let i = 0; i < ast.children.length; i += 1) {
9 | if (ast.children[i].type === "code") {
10 | return ast.children[i]
11 | }
12 | }
13 | }
14 |
15 | const DocsExamplesData = props => {
16 | const query = useStaticQuery(graphql`
17 | query {
18 | allMdx {
19 | edges {
20 | node {
21 | fields {
22 | slug
23 | }
24 | frontmatter {
25 | order
26 | title
27 | summary
28 | demo
29 | tags
30 | }
31 | headings(depth: h1) {
32 | value
33 | }
34 | parent {
35 | ... on File {
36 | modifiedTime(formatString: "YYYY-MM-DD")
37 | }
38 | }
39 | mdxAST
40 | }
41 | }
42 | }
43 | }
44 | `)
45 |
46 | const examples = query.allMdx.edges
47 | .map(({ node }) => node)
48 | .filter(page => page.fields.slug.match(/^\/examples\/.+/))
49 | .map(page => ({
50 | order: page.frontmatter.order,
51 | title: getPageTitle(page),
52 | url: page.fields.slug,
53 | summary: page.frontmatter.summary,
54 | code: getCodeBlockFromMDXAST(page.mdxAST),
55 | tags: page.frontmatter.tags,
56 | demo: page.frontmatter.demo,
57 | updated: page.parent.modifiedTime,
58 | }))
59 | .sort((a, b) => +new Date(b.updated) - +new Date(a.updated))
60 | .sort((a, b) => a.order - b.order)
61 |
62 | const tagsSet = new Set()
63 | examples.forEach(ex => {
64 | ex.tags.forEach(tag => tagsSet.add(tag))
65 | })
66 |
67 | const tags = Array.from(tagsSet)
68 | tags.unshift("All examples")
69 |
70 | return props.children({ examples, tags })
71 | }
72 |
73 | class DocsCodeExamplesOverview extends React.Component {
74 |
75 | constructor(props) {
76 | super(props)
77 |
78 | this.state = {
79 | activeTag: "All examples"
80 | }
81 | }
82 |
83 | render() {
84 | const activeTag = this.state.activeTag
85 |
86 | return (
87 |
88 | {({ examples, tags }) => (
89 | <>
90 |
91 | {tags.map(tag => (
92 | {
101 | this.setState({
102 | activeTag: tag
103 | })
104 | }}
105 | children={tag}/>
106 | ))}
107 |
108 |
109 |
110 | {examples
111 | .filter(ex =>
112 | activeTag === "All examples" ?
113 | true :
114 | ex.tags.indexOf(activeTag) >= 0
115 | )
116 | .map((example, i) => (
117 |
118 |
119 |
120 | {example.title}
121 |
122 |
123 |
124 |
125 |
{example.summary}
126 |
127 |
128 |
129 |
130 |
131 |
132 | ))}
133 |
134 | >
135 | )}
136 |
137 | )
138 | }
139 | }
140 |
141 | export default DocsCodeExamplesOverview
142 |
--------------------------------------------------------------------------------
/src/components/docs-footer.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import TimeAgo from "react-timeago"
3 |
4 | import AnchorLink from "./mdx/anchor-link"
5 | import getCloudflareDocsConfig from "../utils/get-cloudflare-docs-config"
6 |
7 | export default ({ page }) => {
8 | if (!page || !page.parent) return null
9 |
10 | const { modifiedTime, relativePath } = page.parent
11 |
12 | const { contentRepo, contentRepoFolder } = getCloudflareDocsConfig()
13 | const filePathPrefix = contentRepoFolder ? `${contentRepoFolder}/` : ""
14 | const pathToFile = `${filePathPrefix}src/content/${relativePath}`
15 | const editOnGithubURL = `https://github.com/${contentRepo}/blob/production/${pathToFile}`
16 |
17 | return (
18 |
19 |
20 |
21 |
22 | Edit on GitHub
23 |
24 |
25 |
26 | {" "} · {" "}
27 |
28 |
29 |
30 | Updated{" "}
31 |
32 |
33 |
34 |
35 |
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/docs-mobile-title-header.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { Link } from "gatsby"
3 |
4 | import { sidebarToggle } from "../utils/mobile-sidebar-manipulation"
5 | import IconNavMenu from "./icons/nav-menu"
6 |
7 | import DocsTitle from "./docs-title"
8 | import DocsProductLogo from "./docs-product-logo"
9 | import DocsNavLogoLockup from "./docs-nav-logo-lockup"
10 |
11 | const DocsMobileTitleHeader = () => (
12 |
13 |
14 | }
17 | scaleTextClassName="DocsMobileTitleHeader--text-scaler"
18 | textLength={DocsTitle().length}
19 | text={ }
20 | />
21 |
22 |
23 | sidebarToggle()}>
24 |
25 |
26 |
27 | )
28 |
29 | export default DocsMobileTitleHeader
30 |
--------------------------------------------------------------------------------
/src/components/docs-nav-logo-lockup.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | const DocsNavLogoLockup = ({ logo, text, small, scaleTextClassName, textLength }) => {
4 | const wrappedText = (scaleTextClassName && textLength) ? (
5 |
6 | {text}
7 |
8 | ) : (
9 | <>{text}>
10 | )
11 |
12 | return (
13 |
17 |
18 | {logo}
19 |
20 |
21 | {wrappedText}
22 |
23 |
24 | )
25 | }
26 |
27 | DocsNavLogoLockup.defaultProps = {
28 | small: false
29 | }
30 |
31 | export default DocsNavLogoLockup
32 |
--------------------------------------------------------------------------------
/src/components/docs-page.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { SkipNavLink, SkipNavContent } from "@reach/skip-nav"
3 | // These are overridden by our SkipNavLink styles
4 | // but included here to quiet console warnings
5 | import "@reach/skip-nav/styles.css"
6 |
7 | import Helmet from "react-helmet"
8 | import SEO from "./seo"
9 |
10 | import HandleMobilePageNavigations from "./handle-mobile-page-navigations"
11 | import BrowserResizeTracking from "./browser-resize-tracking"
12 | import SmoothScrollHashChanges from "./smooth-scroll-hash-changes"
13 | import Breadcrumbs from "./breadcrumbs"
14 |
15 | import DocsMobileTitleHeader from "./docs-mobile-title-header"
16 | import DocsSidebar from "./docs-sidebar"
17 | import DocsToolbar from "./docs-toolbar"
18 | import DocsTableOfContents from "./docs-table-of-contents"
19 | import { className as docsMarkdownClassName } from "./mdx/root"
20 | import DocsFooter from "./docs-footer"
21 |
22 | import getCloudflareDocsConfig from "../utils/get-cloudflare-docs-config"
23 | import getPageTitle from "../utils/get-page-title"
24 | import getPageType from "../utils/get-page-type"
25 | import getTableOfContents from "../utils/get-table-of-contents"
26 | import hasBreadcrumbs from "../utils/has-breadcrumbs"
27 |
28 | const DocsPage = ({ pageContext: page, children, location }) => {
29 | const title = getPageTitle(page, true)
30 | const pageType = getPageType(page)
31 | const tableOfContents = getTableOfContents(page)
32 |
33 | const { search } = getCloudflareDocsConfig()
34 | const enableSearch = search.apiKey && search.indexName && search.algoliaOptions
35 | const disableSearchProps = enableSearch ? {} : { "search-disabled": "" }
36 |
37 | return (
38 | <>
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | {/*
*/}
53 |
54 |
55 |
56 |
57 |
58 | {pageType === "document" && tableOfContents && (
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | )}
68 |
69 |
70 |
71 |
72 |
73 |
74 | {hasBreadcrumbs(page) && (
75 |
76 | )}
77 |
78 |
79 | {children}
80 |
81 |
82 |
83 |
84 |
85 |
86 | >
87 | )
88 | }
89 |
90 | export default DocsPage
91 |
--------------------------------------------------------------------------------
/src/components/docs-product-logo.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import getCloudflareDocsConfig from "../utils/get-cloudflare-docs-config"
3 | import AccessibleSVG from "./accessible-svg"
4 |
5 | export default () => {
6 | const { product, productLogoPathD } = getCloudflareDocsConfig()
7 |
8 | return (
9 |
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/docs-search.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react"
2 | import { navigate } from "@reach/router"
3 | import getPathPrefix from "../utils/get-path-prefix"
4 | import getCloudflareDocsConfig from "../utils/get-cloudflare-docs-config"
5 |
6 | import Helmet from "react-helmet"
7 |
8 | import DocsTitle from "./docs-title"
9 | import AccessibleSVG from "./accessible-svg"
10 |
11 | const DocsSearch = () => {
12 | const pathPrefix = getPathPrefix()
13 |
14 | const {
15 | pathPrefix: productionPathPrefix,
16 | search: {
17 | indexName,
18 | apiKey,
19 | algoliaOptions
20 | }
21 | } = getCloudflareDocsConfig()
22 |
23 | const enableSearch = indexName && apiKey && algoliaOptions
24 |
25 | // Adjust search result URL pathname to work with local development
26 | // See https://github.com/cloudflare/cloudflare-docs-engine/issues/196
27 | const fixSearchResultPathname = (pathname) => {
28 | // When the pathPrefix we get from getPathPrefix() matches the
29 | // productionPathPrefix we get from getCloudflareDocsConfig()
30 | // then we should not strip the prefix from the pathname. This
31 | // is a more reliable check than location.hostname !== "locahost"
32 | // because both `npm run serve` and `npm run develop` serve to
33 | // localhost but only the latter needs the pathPrefix removed.
34 | if (productionPathPrefix === pathPrefix) return pathname
35 |
36 | // The crawled search results should end up including the
37 | // productionPathPrefix (if one exists) so we need to remove
38 | // that for local development (where getPathPrefix() === "").
39 | if (pathname.startsWith(`${productionPathPrefix}/`))
40 | return pathname.substr(productionPathPrefix.length)
41 |
42 | // If for some reason the results are missing the pathPrefix,
43 | // then this is likely due to a crawling issue. But we return
44 | // the pathname as it is in this case which should cause a
45 | // 404 when we try to navigate(pathname) below.
46 | return pathname
47 | }
48 |
49 | useEffect(() => {
50 | let frames = 0
51 | const init = () => {
52 | frames += 1
53 |
54 | // Sadly this is needed because of the way Helmet works in local development
55 | if (!window.docsearch && frames < 60) {
56 | return requestAnimationFrame(() => init())
57 | }
58 |
59 | const search = window.docsearch({
60 | indexName,
61 | apiKey,
62 | algoliaOptions,
63 |
64 | // TODO: pass DOM in with Reacth.createRef?
65 | inputSelector: "#DocsSearch--input",
66 |
67 | autocompleteOptions: {
68 | // https://github.com/algolia/autocomplete.js#global-options
69 | autoselect: true,
70 | openOnFocus: true,
71 | clearOnSelected: false,
72 | tabAutocomplete: false,
73 |
74 | appendTo: ".DocsSearch--input-wrapper",
75 | hint: false,
76 |
77 | autoselectOnBlur: matchMedia("(pointer: course)").matches
78 | },
79 |
80 | // https://docsearch.algolia.com/docs/behavior
81 | handleSelected: (input, event, suggestion, datasetNumber, context) => {
82 | // Adjust search result URL pathname to work with local development
83 | // See https://github.com/cloudflare/cloudflare-docs-engine/issues/196
84 | const url = new URL(suggestion.url)
85 | const pathname = fixSearchResultPathname(url.pathname)
86 |
87 | search.input.autocomplete.setVal("")
88 | search.input[0].blur()
89 |
90 | // Don’t scroll to hash when it’s just the h1.
91 | if (suggestion.isLvl0) {
92 | navigate(pathname)
93 |
94 | } else {
95 | navigate(pathname + url.hash)
96 |
97 | // Then focus the anchor in the header anchor
98 | // corresponding to the hash if it exists (it
99 | // should if Algolia’s crawl is up-to-date).
100 | const headerAnchor = document.querySelector(`${url.hash} a`)
101 | if (headerAnchor) headerAnchor.focus()
102 | }
103 | },
104 |
105 | transformData: function(hits) {
106 | // Remove empty results
107 | for (let i = hits.length - 1; i >= 0; i -= 1) {
108 | if (!hits[i].hierarchy.lvl0 && !hits[i].hierarchy.lvl1) {
109 | hits.splice(i, 1)
110 | }
111 | }
112 | }
113 | })
114 |
115 | const autocompleteWrapper = search.autocomplete.autocomplete.getWrapper()
116 |
117 | search.autocomplete.on("autocomplete:shown", event => {
118 | autocompleteWrapper.setAttribute("data-expanded", true)
119 | })
120 |
121 | search.autocomplete.on("autocomplete:closed", event => {
122 | autocompleteWrapper.setAttribute("data-expanded", false)
123 | })
124 |
125 | const input = search.input[0]
126 | const wrapper = input.closest(".DocsSearch")
127 |
128 | input.addEventListener("focus", () => {
129 | wrapper.setAttribute("is-focused", "")
130 | })
131 |
132 | input.addEventListener("blur", () => {
133 | wrapper.removeAttribute("is-focused")
134 | })
135 |
136 | document.addEventListener("keydown", event => {
137 | if (event.target === input) return
138 |
139 | const slashShortcut = event.key === "/"
140 | const commandShortcut = event.key === "S" && event.shiftKey
141 |
142 | if (slashShortcut || commandShortcut) {
143 | event.preventDefault()
144 | window.scrollTo(0, 0)
145 | input.focus()
146 | }
147 | })
148 | }
149 |
150 | if (enableSearch) {
151 | init()
152 | }
153 |
154 | // eslint-disable-next-line react-hooks/exhaustive-deps
155 | }, [])
156 |
157 | if (!enableSearch) {
158 | return
159 | }
160 |
161 | return (
162 | <>
163 |
164 |
165 |
166 |
167 |
178 | >
179 | )
180 | }
181 |
182 | export default DocsSearch
183 |
--------------------------------------------------------------------------------
/src/components/docs-sidebar-more-dropdown.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import getCloudflareDocsConfig from "../utils/get-cloudflare-docs-config"
4 |
5 | import DocsTitle from "./docs-title"
6 | import AccessibleSVG from "./accessible-svg"
7 | import Dropdown from "./dropdown"
8 |
9 | const ExternalLinks = ({ children }) => {
10 | const { externalLinks } = getCloudflareDocsConfig()
11 | return children(externalLinks)
12 | }
13 |
14 | const DocsSidebarMoreDropdown = () => {
15 | const className = "DocsSidebar--section-more"
16 | const buttonClassName = className + "-button"
17 | const buttonIconClassName = buttonClassName + "-icon"
18 | const props = { className, buttonClassName }
19 |
20 | props.buttonChildren = (
21 |
22 |
23 |
24 |
25 |
menu
26 |
27 | )
28 |
29 | return (
30 |
31 | {({ expanded }) => (
32 |
45 | )}
46 |
47 | )
48 | }
49 |
50 | export default DocsSidebarMoreDropdown
51 |
--------------------------------------------------------------------------------
/src/components/docs-sidebar-nav-data.js:
--------------------------------------------------------------------------------
1 | import { useStaticQuery, graphql } from "gatsby"
2 |
3 | import generateNavTree from "../utils/generate-nav-tree"
4 |
5 | let navTree
6 | const getCachedNavTree = pages => {
7 | if (navTree) return navTree
8 | navTree = generateNavTree(pages)
9 | return navTree
10 | }
11 |
12 | const DocsSidebarNavData = props => {
13 | const query = useStaticQuery(graphql`
14 | query {
15 | allMdx {
16 | edges {
17 | node {
18 | id
19 | fields {
20 | slug
21 | }
22 | frontmatter {
23 | title
24 | type
25 | order
26 | hidden
27 | hideChildren
28 | breadcrumbs
29 | }
30 | headings(depth: h1) {
31 | value
32 | depth
33 | }
34 | }
35 | }
36 | }
37 | }
38 | `)
39 |
40 | const pages = query.allMdx.edges
41 | .map(({ node }) => node)
42 | .filter(page => !page.frontmatter.hidden)
43 |
44 | const data = getCachedNavTree(pages)
45 |
46 | return props.children({ data })
47 | }
48 |
49 | export default DocsSidebarNavData
50 |
--------------------------------------------------------------------------------
/src/components/docs-sidebar-nav-item.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { Link } from "gatsby"
3 |
4 | import Collapse from "@material-ui/core/Collapse"
5 |
6 | import sidebarCollapseTransitionDuration from "../constants/sidebar-collapse-transition-duration"
7 |
8 | import getPathPrefix from "../utils/get-path-prefix"
9 | import getNormalizedPath from "../utils/get-normalized-path"
10 | import userPrefersReducedMotion from "../utils/user-prefers-reduced-motion"
11 |
12 | const collapseClassesBase = "DocsSidebar--nav-item-collapse-"
13 |
14 | const DocsSidebarCollapse = ({ expanded, children }) => {
15 | const base = collapseClassesBase
16 | const collapseClasses = {
17 | container: `${base}container`,
18 | entered: `${base}entered`,
19 | hidden: `${base}hidden`,
20 | wrapper: `${base}wrapper`,
21 | wrapperInner: `${base}wrapperInner`
22 | }
23 |
24 | if (userPrefersReducedMotion()) {
25 | let className = collapseClasses.container + " "
26 | className += expanded ? collapseClasses.entered : collapseClasses.hidden
27 |
28 | return (
29 |
30 | )
31 | }
32 |
33 | return (
34 |
{
39 | // Get the height Collapse wants to expand to
40 | let height = parseInt(node.style.height, 10)
41 |
42 | // Find all child Collapses which are simultaneously expanding
43 | node.querySelectorAll(`.${collapseClasses.container}`).forEach(node => {
44 | if (!node.hasAttribute("style")) return
45 |
46 | const childNodeHeight = parseInt(node.style.height, 10)
47 |
48 | // And augment the parent Collapse height by the height
49 | // of the simultaneously expanding child Collapses
50 | if (!isNaN(childNodeHeight)) {
51 | height += parseInt(node.style.height, 10)
52 | }
53 | })
54 |
55 | node.style.height = `${height}px`
56 | }}
57 | children={children}/>
58 | )
59 | }
60 |
61 | const pathPrefix = getPathPrefix()
62 |
63 | class DocsSidebarNavItem extends React.Component {
64 |
65 | constructor(props) {
66 | super(props)
67 |
68 | this.expandCollapseEl = React.createRef()
69 |
70 | this.state = {
71 | expanded: this.isExpanded()
72 | }
73 |
74 | this.onExpandCollapseClick = this.onExpandCollapseClick.bind(this)
75 | }
76 |
77 | showChildren() {
78 | const { node } = this.props
79 | return Array.isArray(node.children) && !node.frontmatter.hideChildren
80 | }
81 |
82 | isActive() {
83 | const { node, location } = this.props
84 |
85 | const href = pathPrefix ? pathPrefix + node.href : node.href
86 | const isActive = getNormalizedPath(href) === getNormalizedPath(location.pathname)
87 | const isActiveDueToChild = !this.showChildren() && this.isActiveRoot()
88 |
89 | return isActive || isActiveDueToChild
90 | }
91 |
92 | isActiveRoot() {
93 | const { node, location } = this.props
94 |
95 | const href = node => pathPrefix ? pathPrefix + node.href : node.href
96 | const isActive = node => getNormalizedPath(href(node)) === getNormalizedPath(location.pathname)
97 | const hasActiveChild = node => !node.children ? false : node.children.some(
98 | node => isActive(node) || hasActiveChild(node)
99 | )
100 |
101 | return hasActiveChild(node)
102 | }
103 |
104 | isHidden() {
105 | if (typeof this.props.isParentExpanded === "undefined") return false
106 | return !this.props.isParentExpanded
107 | }
108 |
109 | isExpanded() {
110 | const active = this.isActive()
111 | const activeRoot = this.isActiveRoot()
112 | return !!(this.props.node.children && (active || activeRoot))
113 | }
114 |
115 | componentDidMount() {
116 | const el = this.expandCollapseEl.current
117 | const expanded = this.state.expanded
118 |
119 | if (!el || !expanded) return
120 |
121 | el.style.height = el.firstElementChild.clientHeight + 'px'
122 | }
123 |
124 | componentDidUpdate(prevProps) {
125 | if (getNormalizedPath(this.props.location.pathname) !== getNormalizedPath(prevProps.location.pathname)) {
126 | this.setState({ expanded: this.isExpanded() })
127 | }
128 | }
129 |
130 | onExpandCollapseClick() {
131 | this.setState(state => ({
132 | expanded: !state.expanded
133 | }))
134 | }
135 |
136 | render() {
137 | const { expanded } = this.state
138 | const { node, location } = this.props
139 | const depth = this.props.depth + 1
140 |
141 | const props = {}
142 | if (expanded) props['is-expanded'] = ''
143 | if (this.isActive()) props['is-active'] = ''
144 | if (this.isActiveRoot()) props['is-active-root'] = ''
145 |
146 | const linkProps = {}
147 | if (this.isHidden()) linkProps.tabIndex = -1
148 | if (this.isHidden()) linkProps["aria-hidden"] = true
149 | if (this.isActive()) linkProps['is-active'] = ''
150 |
151 | const buttonProps = {}
152 | if (this.isHidden()) buttonProps.tabIndex = -1
153 | if (this.isHidden()) buttonProps["aria-hidden"] = true
154 |
155 | return (
156 |
161 | {this.showChildren() && (
162 |
163 |
164 | {expanded ? "Collapse" : "Expand"}: {node.title}
165 |
166 | )}
167 |
168 |
169 |
170 | {node.title}
171 |
172 |
173 | {this.showChildren() && (
174 |
175 |
176 |
177 | {node.children.map(node => (
178 |
185 | ))}
186 |
187 |
188 |
189 | )}
190 |
191 | )
192 | }
193 | }
194 |
195 | export default DocsSidebarNavItem
196 |
--------------------------------------------------------------------------------
/src/components/docs-sidebar-nav-section.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { Location } from "@reach/router"
3 |
4 | import sidebarCollapseTransitionDuration
5 | from "../constants/sidebar-collapse-transition-duration"
6 |
7 | import animate from "../utils/animate"
8 | import isMobile from "../utils/is-mobile"
9 | import userPrefersReducedMotion from "../utils/user-prefers-reduced-motion"
10 | import ScrollbarsWithScrollShadow from "./scrollbars-with-scroll-shadows"
11 |
12 | import DocsSidebarNav from "./docs-sidebar-nav"
13 |
14 | class DocsSidebarNavSectionContent extends React.Component {
15 |
16 | componentDidMount() {
17 | this.scrollToActiveNavItemIfHidden()
18 | }
19 |
20 | componentDidUpdate(prevProps) {
21 | if (this.props.location.pathname === prevProps.location.pathname) return
22 | if (this.scrollToActiveTimeout) clearTimeout(this.scrollToActiveTimeout)
23 |
24 | if (userPrefersReducedMotion() || isMobile()) {
25 | // Wait one frame to allow the sidebar nav items
26 | // to expand/collapse as needed before determining
27 | // the scroll position
28 | requestAnimationFrame(() => {
29 | this.scrollToActiveNavItemIfHidden()
30 | })
31 | return
32 | }
33 |
34 | // Wait the sidebar nav transition duration plus one
35 | // frame before determining the scroll position
36 | this.scrollToActiveTimeout = setTimeout(() => {
37 | // TODO:
38 | // Investigate whether it’s possible to do this
39 | // with a transitionend event instead of setTimeout
40 | // without reaching into the child components
41 | requestAnimationFrame(() => {
42 | this.scrollToActiveNavItemIfHidden(true)
43 | })
44 | }, sidebarCollapseTransitionDuration)
45 | }
46 |
47 | scrollToActiveNavItemIfHidden(shouldAnimate) {
48 | if (!this.refs || !this.refs.scrollbars || !this.refs.scrollbars.refs) return
49 |
50 | const { scrollbars } = this.refs.scrollbars.refs
51 | const { container } = scrollbars
52 |
53 | const activeLink = container.querySelector("a[href][is-active]")
54 | if (!activeLink) return
55 |
56 | const activeRect = activeLink.getBoundingClientRect()
57 | const containerRect = container.getBoundingClientRect()
58 |
59 | const activeNavItemAboveFold = activeRect.top < containerRect.top
60 | const activeNavItemBelowFold = activeRect.bottom > containerRect.bottom
61 |
62 | if (activeNavItemAboveFold || activeNavItemBelowFold) {
63 | const scrollTop = scrollbars.getScrollTop()
64 |
65 | const delta = (
66 | activeRect.top - containerRect.top
67 | - ((containerRect.height - activeRect.height) / 2)
68 | )
69 |
70 | const scrollHeight = scrollbars.getScrollHeight()
71 | const bottomScrollTop = scrollHeight - containerRect.height
72 |
73 | let newScrollTop = scrollTop + delta
74 | newScrollTop = Math.min(newScrollTop, bottomScrollTop)
75 | newScrollTop = Math.max(newScrollTop, 0)
76 |
77 | if (shouldAnimate) {
78 | animate({
79 | from: scrollTop,
80 | to: newScrollTop,
81 | easing: "cubicBezier(.4, 0, .2, 1)",
82 | duration: 500,
83 | update: value => scrollbars.scrollTop(value)
84 | })
85 |
86 | } else {
87 | scrollbars.scrollTop(newScrollTop)
88 | }
89 | }
90 | }
91 |
92 | render() {
93 | return (
94 |
101 |
102 |
103 | )
104 | }
105 | }
106 |
107 | const DocsSidebarNavSection = () => (
108 |
109 | {({ location }) => (
110 |
111 | )}
112 |
113 | )
114 |
115 | export default DocsSidebarNavSection
116 |
--------------------------------------------------------------------------------
/src/components/docs-sidebar-nav.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { Location } from "@reach/router"
3 |
4 | import createFocusGroup from "focus-group"
5 |
6 | import DocsSidebarNavData from "./docs-sidebar-nav-data"
7 | import DocsSidebarNavItem from "./docs-sidebar-nav-item"
8 |
9 | class DocsSidebarNav extends React.Component {
10 |
11 | constructor(props) {
12 | super(props)
13 |
14 | this.ref = React.createRef()
15 | }
16 |
17 | componentDidMount() {
18 | const el = this.ref.current
19 |
20 | const getMembers = () => (el.querySelectorAll([
21 | `a[href]:not([tabindex="-1"])`,
22 | `button:not([tabindex="-1"])`
23 | ].join(", ")))
24 |
25 | this.focusGroup = createFocusGroup({
26 | members: getMembers(),
27 | stringSearch: true
28 | })
29 |
30 | this.observer = new MutationObserver(mutationList => {
31 | mutationList.forEach(mutation => {
32 | if (mutation.type !== "attributes") return
33 |
34 | this.focusGroup.setMembers(getMembers())
35 | })
36 | })
37 |
38 | this.observer.observe(el, {
39 | attributeFilter: ["tabindex"],
40 | subtree: true
41 | })
42 |
43 | this.focusGroup.activate()
44 | }
45 |
46 | componentWillUnmount() {
47 | this.focusGroup.deactivate()
48 | this.observer.disconnect()
49 | }
50 |
51 | render() {
52 | return (
53 |
54 | {({ data }) => (
55 |
56 | {({ location }) => (
57 |
58 | {data.map(node => (
59 |
65 | ))}
66 |
67 | )}
68 |
69 | )}
70 |
71 | )
72 | }
73 | }
74 |
75 | export default DocsSidebarNav
76 |
--------------------------------------------------------------------------------
/src/components/docs-sidebar-title-section.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { Link } from "gatsby"
3 |
4 | import DocsTitle from "./docs-title"
5 | import DocsProductLogo from "./docs-product-logo"
6 | import DocsNavLogoLockup from "./docs-nav-logo-lockup"
7 |
8 | import DocsSidebarMoreDropdown from "./docs-sidebar-more-dropdown"
9 |
10 | const DocsSidebarTitleSection = () => (
11 |
12 |
13 | }
15 | scaleTextClassName="DocsSidebar--docs-title-text-scaler"
16 | textLength={DocsTitle().length}
17 | text={ }
18 | />
19 |
20 |
21 |
22 |
23 | )
24 |
25 | export default DocsSidebarTitleSection
26 |
--------------------------------------------------------------------------------
/src/components/docs-sidebar.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | // import DocsSidebarHeaderSection from "./docs-sidebar-header-section"
4 | import DocsSidebarTitleSection from "./docs-sidebar-title-section"
5 | import DocsSidebarNavSection from "./docs-sidebar-nav-section"
6 |
7 | const DocsSidebar = () => (
8 |
17 | )
18 |
19 | export default DocsSidebar
20 |
--------------------------------------------------------------------------------
/src/components/docs-table-of-contents.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | class Item extends React.Component {
4 |
5 | constructor(props) {
6 | super(props)
7 | }
8 |
9 | render() {
10 | const { item } = this.props
11 |
12 | return (
13 |
14 |
15 | {item.title}
16 |
17 |
18 | {Array.isArray(item.items) && (
19 |
20 | {item.items.map(item => (
21 | -
22 | ))}
23 |
24 | )}
25 |
26 | )
27 | }
28 | }
29 |
30 | const DocsTableOfContents = ({ items }) => {
31 | const toTop = {
32 | // the top most div of content to scroll to
33 | url: "#docs-content",
34 | title: "↑ Top"
35 | }
36 |
37 | return items.length ? (
38 |
39 | -
40 |
41 | {items.map(item => (
42 |
-
43 | ))}
44 |
45 | ) : (<>>)
46 | }
47 |
48 | export default DocsTableOfContents
49 |
--------------------------------------------------------------------------------
/src/components/docs-title.js:
--------------------------------------------------------------------------------
1 | import getCloudflareDocsConfig from "../utils/get-cloudflare-docs-config"
2 |
3 | export default () => {
4 | return getCloudflareDocsConfig().product
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/docs-toolbar.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import getCloudflareDocsConfig from "../utils/get-cloudflare-docs-config"
4 |
5 | import DocsTitle from "./docs-title"
6 | import DocsSearch from "./docs-search"
7 | import AccessibleSVG from "./accessible-svg"
8 | import ThemeToggle from "./theme-toggle"
9 |
10 | const DocsToolbar = () => {
11 | const { contentRepo } = getCloudflareDocsConfig()
12 |
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
29 |
30 |
31 | Visit {DocsTitle()} on GitHub
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | )
44 | }
45 |
46 | export default DocsToolbar
47 |
--------------------------------------------------------------------------------
/src/components/docs-tutorials.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { Link, useStaticQuery, graphql } from "gatsby"
3 | import TimeAgo from "react-timeago"
4 |
5 | import getPageTitle from "../utils/get-page-title"
6 |
7 | const tenDaysInMS = 10 * 24 * 60 * 60 * 1000
8 |
9 | const DocsTutorials = () => {
10 | const query = useStaticQuery(graphql`
11 | query {
12 | allMdx {
13 | edges {
14 | node {
15 | fields {
16 | slug
17 | }
18 | frontmatter {
19 | title
20 | updated
21 | difficulty
22 | }
23 | headings(depth: h1) {
24 | value
25 | }
26 | wordCount {
27 | words
28 | }
29 | }
30 | }
31 | }
32 | }
33 | `)
34 |
35 | const tutorials = query.allMdx.edges
36 | .map(({ node }) => node)
37 | .filter(page => page.fields.slug.match(/^\/tutorials\/.+/))
38 | .map(page => ({
39 | title: getPageTitle(page),
40 | url: page.fields.slug,
41 | updated: page.frontmatter.updated,
42 | difficulty: page.frontmatter.difficulty,
43 | wordCount: page.wordCount.words,
44 | new: (+new Date() - +new Date(page.frontmatter.updated)) < tenDaysInMS
45 | }))
46 | .sort((a, b) => +new Date(b.updated) - +new Date(a.updated))
47 |
48 | let i = 0
49 | let longestWordCount = 0
50 |
51 | for (i = 0; i < tutorials.length; i += 1) {
52 | if (tutorials[i].wordCount > longestWordCount) {
53 | longestWordCount = tutorials[i].wordCount
54 | }
55 | }
56 |
57 | for (i = 0; i < tutorials.length; i += 1) {
58 | tutorials[i].length = ((tutorials[i].wordCount / longestWordCount) * 100) + "%"
59 | }
60 |
61 | return (
62 |
63 |
64 |
65 |
66 | Name
67 |
68 |
69 | Updated
70 |
71 |
72 | Difficulty
73 |
74 |
75 | Length
76 |
77 |
78 |
79 |
80 |
81 | {tutorials.map(tutorial => (
82 |
83 |
84 |
85 | {tutorial.title}
86 |
87 |
88 |
89 | (
90 |
91 | {value} {unit}{value > 1 ? "s" : ""} ago
92 |
93 | )} minPeriod={60}/>
94 |
95 |
{tutorial.difficulty}
96 |
101 |
102 | ))}
103 |
104 |
105 | )
106 | }
107 |
108 | export default DocsTutorials
109 |
--------------------------------------------------------------------------------
/src/components/dropdown.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import createFocusGroup from "focus-group"
4 |
5 | class Dropdown extends React.Component {
6 |
7 | constructor(props) {
8 | super(props)
9 |
10 | this.container = React.createRef()
11 | this.button = React.createRef()
12 |
13 | this.state = {
14 | expanded: false
15 | }
16 |
17 | this.onExpandButtonClick = this.onExpandButtonClick.bind(this)
18 | this.handleClickOutside = this.handleClickOutside.bind(this)
19 | this.handleBlur = this.handleBlur.bind(this)
20 | this.handleEscapeKey = this.handleEscapeKey.bind(this)
21 | }
22 |
23 | onExpandButtonClick() {
24 | this.setState(state => {
25 | const expanded = !state.expanded
26 |
27 | if (expanded) {
28 | this.focusGroup.focusNodeAtIndex(0)
29 | }
30 |
31 | return { expanded }
32 | })
33 | }
34 |
35 | handleClickOutside(event) {
36 | if (!this.state.expanded) return
37 | if (!this.container.current) return
38 | if (this.container.current.contains(event.target)) return
39 |
40 | this.setState({
41 | expanded: false
42 | })
43 | }
44 |
45 | handleBlur(event) {
46 | if (this.willEscape) return
47 | if (!this.state.expanded) return
48 | if (!this.button.current || !this.container.current) return
49 |
50 | const activeElement = event.relatedTarget
51 |
52 | if (activeElement === this.button.current) {
53 | event.preventDefault()
54 | const focusIndex = this.focusGroup.getMembers().length - 1
55 | this.focusGroup.focusNodeAtIndex(focusIndex)
56 | }
57 |
58 | else if (!this.container.current.contains(event.relatedTarget)) {
59 | event.preventDefault()
60 | this.focusGroup.focusNodeAtIndex(0)
61 | }
62 | }
63 |
64 | handleEscapeKey(event) {
65 | if (event.key !== "Escape") return
66 |
67 | this.willEscape = true
68 |
69 | if (this.button.current) {
70 | this.button.current.focus()
71 | }
72 |
73 | this.setState({ expanded: false })
74 | }
75 |
76 | componentDidMount() {
77 | const el = this.container.current
78 | const members = el.querySelectorAll(this.props.focusGroupSelector)
79 |
80 | this.focusGroup = createFocusGroup({
81 | members: members,
82 | wrap: true,
83 | stringSearch: true
84 | })
85 | }
86 |
87 | componentDidUpdate(prevProps, prevState) {
88 | if (!prevState.expanded && this.state.expanded) {
89 | this.focusGroup.activate()
90 | this.willEscape = false
91 | document.addEventListener("click", this.handleClickOutside)
92 | document.addEventListener("keyup", this.handleEscapeKey)
93 | }
94 |
95 | if (prevState.expanded && !this.state.expanded) {
96 | this.focusGroup.deactivate()
97 | document.removeEventListener("click", this.handleClickOutside)
98 | document.removeEventListener("keyup", this.handleEscapeKey)
99 | }
100 | }
101 |
102 | componentWillUnmount() {
103 | this.focusGroup.deactivate()
104 | }
105 |
106 | render() {
107 | const { expanded } = this.state
108 |
109 | let { className, buttonClassName } = this.props
110 | className += (className ? " " : "") + "Dropdown"
111 | buttonClassName += (className ? " " : "") + "Button"
112 |
113 | return (
114 |
115 |
122 |
123 | {this.props.children(this.state)}
124 |
125 | )
126 | }
127 | }
128 |
129 | Dropdown.defaultProps = {
130 | focusGroupSelector: "a[href]"
131 | }
132 |
133 | export default Dropdown
134 |
--------------------------------------------------------------------------------
/src/components/handle-mobile-page-navigations.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { globalHistory } from "@reach/router"
3 |
4 | import { sidebarClose } from "../utils/mobile-sidebar-manipulation"
5 |
6 | class HandleMobilePageNavigations extends React.Component {
7 |
8 | componentDidMount() {
9 | this.historyUnsubscribe = globalHistory.listen(() => sidebarClose())
10 | }
11 |
12 | componentWillUnmount() {
13 | this.historyUnsubscribe()
14 | }
15 |
16 | render() {
17 | return null
18 | }
19 | }
20 |
21 | export default HandleMobilePageNavigations
22 |
--------------------------------------------------------------------------------
/src/components/icons/base.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import PropTypes from "prop-types"
3 |
4 | import AccessibleSVG from "../accessible-svg"
5 |
6 | const IconBase = props => {
7 | const { className, description, children, ...rest } = props
8 |
9 | return (
10 |
11 |
12 | {children}
13 |
14 |
15 |
16 | )
17 | }
18 |
19 | IconBase.defaultProps = {
20 | viewBox: "0 0 16 16",
21 | className: "Icon"
22 | }
23 |
24 | IconBase.propTypes = {
25 | viewBox: PropTypes.string.isRequired,
26 | title: PropTypes.string.isRequired,
27 | description: PropTypes.string.isRequired
28 | }
29 |
30 | export default IconBase
31 |
--------------------------------------------------------------------------------
/src/components/icons/external-link.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import IconBase from "./base"
3 | import PropTypes from "prop-types"
4 |
5 | const IconExternalLink = ({ className, description }) => (
6 |
16 |
17 |
18 | )
19 |
20 | IconExternalLink.defaultProps = {
21 | description: "Open external link"
22 | }
23 |
24 | IconExternalLink.propTypes = {
25 | description: PropTypes.string.isRequired
26 | }
27 |
28 | export default IconExternalLink
29 |
--------------------------------------------------------------------------------
/src/components/icons/nav-menu.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import IconBase from "./base"
3 | import PropTypes from "prop-types"
4 |
5 | const IconNavMenu = ({ className, description }) => (
6 |
17 |
18 |
19 |
20 |
21 | )
22 |
23 | IconNavMenu.defaultProps = {
24 | description: "Open external link"
25 | }
26 |
27 | IconNavMenu.propTypes = {
28 | description: PropTypes.string.isRequired
29 | }
30 |
31 | export default IconNavMenu
32 |
--------------------------------------------------------------------------------
/src/components/mdx-custom-renderer.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { graphql, Link } from "gatsby"
3 |
4 | import { MDXProvider } from "@mdx-js/react"
5 | import { MDXRenderer } from "gatsby-plugin-mdx"
6 |
7 | import a from "./mdx/anchor-link"
8 | import headers from "./mdx/headers"
9 | import inlineCode from "./mdx/inline-code"
10 | import pre from "./mdx/code-block"
11 | import Button from "./mdx/button"
12 | import ButtonGroup from "./mdx/button-group"
13 | import Aside from "./mdx/aside"
14 | import ContentColumn from "./mdx/content-column"
15 | import Example from "./mdx/example"
16 | import Demo from "./mdx/demo"
17 | import TableWrap from "./mdx/table-wrap"
18 | import Definitions from "./mdx/definitions"
19 | import Code from "./mdx/code"
20 | import ParamType from "./mdx/param-type"
21 | import Type from "./mdx/type"
22 | import TypeLink from "./mdx/type-link"
23 | import PropMeta from "./mdx/prop-meta"
24 | import DirectoryListing from "./mdx/directory-listing"
25 | import YouTube from "./mdx/youtube"
26 | import StreamVideo from "./mdx/stream-video"
27 |
28 | // https://mdxjs.com/table-of-components
29 | // https://www.gatsbyjs.org/docs/mdx/customizing-components/
30 | const components = {
31 | // Replace native DOM elements
32 | a,
33 | ...headers,
34 | inlineCode,
35 | pre,
36 |
37 | // Add custom components
38 | Link,
39 | Button,
40 | ButtonGroup,
41 | Aside,
42 | ContentColumn,
43 | Example,
44 | Demo,
45 | TableWrap,
46 | Definitions,
47 | Code,
48 | ParamType,
49 | Type,
50 | TypeLink,
51 | PropMeta,
52 | DirectoryListing,
53 | YouTube,
54 | StreamVideo,
55 | }
56 |
57 | const MDXCustomRenderer = ({ data: { mdx } }) => {
58 | return (
59 |
60 |
61 | {mdx.body}
62 |
63 |
64 | )
65 | }
66 |
67 | export default MDXCustomRenderer
68 |
69 | export const query = graphql`
70 | query ($id: String) {
71 | mdx(id: { eq: $id }) {
72 | id
73 | body
74 | frontmatter {
75 | demo
76 | difficulty
77 | summary
78 | tags
79 | title
80 | type
81 | updated(formatString: "YYYY-MM-DD")
82 | }
83 | }
84 | }
85 | `
86 |
--------------------------------------------------------------------------------
/src/components/mdx/anchor-link.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { Link } from "gatsby"
3 | import { navigate } from "@reach/router"
4 | import getPathPrefix from "../../utils/get-path-prefix"
5 |
6 | import { className } from "./root"
7 | import IconExternalLink from "../icons/external-link"
8 | const linkClassName = className("link")
9 | const contentClassName = `${ linkClassName }-content`
10 | const externalIconClassName = `${linkClassName}-external-icon`
11 |
12 | export default ({ href, className, children, ...props }) => {
13 | // eslint-disable-next-line jsx-a11y/anchor-has-content
14 | if (!href || !children) return ( )
15 |
16 | // Similar to Gatsby’s own mdx-link.js https://git.io/JUmtg
17 | const isHash = href.indexOf("#") === 0
18 | const isExternal = !!href.match(/^https?:/)
19 | const isFile = /\.[^/]*$/.test(href)
20 | const useRegularLink = isHash || isExternal || isFile
21 |
22 | const linkHasChildren = typeof children === "object"
23 | const linkHasImageChild = linkHasChildren && React.Children.toArray(children).filter(item => {
24 | if (typeof item === "object" && item.props && (item.props.parentName === "img" || item.props.originalType === "img" || item.props.className === "gatsby-resp-image-wrapper"))
25 | return true
26 |
27 | return false
28 | }).length
29 |
30 | if (isHash) {
31 | props.onClick = event => {
32 | if (!event.target) return
33 | // Gatsby’s own scroll-to-anchor by hash doesn’t
34 | // work reliably. Soe take control of it ourselves
35 | // similar to what we do in:
36 | // `src/components/docs-table-of-contents.js`
37 | const link = event.target.closest("a")
38 | event.preventDefault()
39 | navigate(link.href)
40 | }
41 | }
42 |
43 | if (!useRegularLink) {
44 | // Unfortunately, MDX seems to be automatically prefixing[1]
45 | // the href with the `pathPrefix`[2] before we get access to
46 | // it here. Gatsby’s ` ` component below also prefixes
47 | // it, so we strip the prefix here first so that it doesn’t
48 | // end up getting double-prefixed.
49 | // [1] https://git.io/JJNPs
50 | // [2] https://www.gatsbyjs.com/docs/path-prefix/
51 | const pathPrefix = getPathPrefix()
52 |
53 | if (href.startsWith(`${pathPrefix}/`)) {
54 | href = href.substr(pathPrefix.length)
55 | }
56 | }
57 |
58 | return useRegularLink ? (
59 | (isExternal && !linkHasImageChild) ? (
60 |
61 | {children}
62 |
63 |
64 | ) : (
65 |
66 | {children}
67 |
68 | )
69 | ) : (
70 |
71 | {children}
72 |
73 | )
74 | }
75 |
--------------------------------------------------------------------------------
/src/components/mdx/aside.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import { className } from "./root"
4 |
5 | const Aside = props => (
6 |
7 | {props.header && (
8 |
9 | {props.header}
10 |
11 | )}
12 | {props.children}
13 |
14 | )
15 |
16 | Aside.defaultProps = {
17 | type: "note"
18 | }
19 |
20 | export default Aside
21 |
--------------------------------------------------------------------------------
/src/components/mdx/button-group.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { className } from "./root"
3 |
4 | export default props => (
5 |
6 |
7 | {props.children}
8 |
9 |
10 | )
11 |
--------------------------------------------------------------------------------
/src/components/mdx/button.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import AnchorLink from "./anchor-link"
3 |
4 | export default ({ type, href, children, ...props }) => {
5 | let className = "Button"
6 |
7 | if (type === "primary") {
8 | className += " Button-is-docs-primary"
9 | } else if (type === "secondary") {
10 | className += " Button-is-docs-secondary"
11 | }
12 |
13 | if (props.className) {
14 | className += ` ${props.className}`
15 | }
16 |
17 | if (href) {
18 | return (
19 |
20 | {children}
21 |
22 | )
23 | }
24 |
25 | return (
26 |
27 | {children}
28 |
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/mdx/code-block.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import * as frontMatterParser from "gray-matter"
4 |
5 | import Highlight, { defaultProps } from "prism-react-renderer"
6 |
7 | import {
8 | languageMappings,
9 | prismLanguages,
10 | transformToken,
11 | } from "./custom-syntax-highlighting"
12 |
13 | // Additional language support (See https://git.io/JJuZX)
14 | import Prism from "prism-react-renderer/prism"
15 | ;(typeof global !== "undefined" ? global : window).Prism = Prism
16 | require("prismjs/components/prism-toml")
17 | require("prismjs/components/prism-rust")
18 | Prism.languages.sh = prismLanguages.sh
19 |
20 | const codeBlockClassName = (theme) => {
21 | const themeClassName =
22 | theme === "dark" ? "" : " CodeBlock-is-light-in-light-theme"
23 | return `CodeBlock CodeBlock-with-rows CodeBlock-scrolls-horizontally${themeClassName}`
24 | }
25 |
26 | const addNewlineToEmptyLine = (line) => {
27 | if (line && line.length === 1 && line[0].empty) {
28 | // Improves copy/paste behavior
29 | line[0].content = "\n"
30 | }
31 |
32 | return line
33 | }
34 |
35 | const CodeBlock = (props) => {
36 | const { className, children } = props.children.props
37 |
38 | if (props.className) {
39 | return
40 | }
41 |
42 | let language = className ? className.split("-")[1] : "js"
43 | const mappedLanguage = languageMappings[language]
44 | if (mappedLanguage) language = mappedLanguage
45 |
46 | let code = children.trim()
47 |
48 | const tokenProps = ({ children, className, key }) => {
49 | const tokens = className.replace("token ", "").split(" ")
50 |
51 | className = ""
52 |
53 | tokens.forEach((token, i) => {
54 | token = transformToken({ token, children, language })
55 | if (token.indexOf("language-") !== 0) token = `token-${token}`
56 | className += ` CodeBlock--${token}`
57 | })
58 |
59 | className = className.trim()
60 |
61 | return {
62 | key,
63 | children,
64 | className,
65 | }
66 | }
67 |
68 | let codeFrontmatter = {}
69 |
70 | // For now, we don’t support code frontmatter
71 | // in Markdown or YAML blocks because they
72 | // themselves can contain frontmatter.
73 |
74 | // TODO: find workaround for this limitation
75 | if (language !== "markdown" && language !== "yaml") {
76 | const parsed = frontMatterParser(code)
77 |
78 | if (Object.keys(parsed.data).length) {
79 | code = parsed.content
80 | codeFrontmatter = parsed.data
81 | }
82 | }
83 |
84 | const theme = codeFrontmatter.theme || "light"
85 |
86 | return (
87 |
88 | {({ className, style, tokens, getLineProps, getTokenProps }) => (
89 |
95 | {codeFrontmatter.header && (
96 | {codeFrontmatter.header}
97 | )}
98 | {codeFrontmatter.filename && !codeFrontmatter.header && (
99 |
100 | {codeFrontmatter.filename}
101 |
102 | )}
103 |
104 |
105 |
106 | {tokens.map((line, i) => (
107 |
118 |
119 |
120 | {addNewlineToEmptyLine(line).map((token, key) => (
121 |
125 | ))}
126 |
127 |
128 | ))}
129 |
130 |
131 |
132 |
133 | )}
134 |
135 | )
136 | }
137 |
138 | export default CodeBlock
139 |
--------------------------------------------------------------------------------
/src/components/mdx/code.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | export default props => (
4 |
5 | {props.children}
6 |
7 | )
8 |
--------------------------------------------------------------------------------
/src/components/mdx/content-column.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import { className } from "./root"
4 |
5 | export default props => (
6 |
7 | {props.children}
8 |
9 | )
10 |
--------------------------------------------------------------------------------
/src/components/mdx/custom-syntax-highlighting.js:
--------------------------------------------------------------------------------
1 | export const languageMappings = {
2 | "shell": "sh",
3 | "javascript": "js",
4 | "markup": "html",
5 | "vue": "html"
6 | }
7 |
8 | export const prismLanguages = {
9 | sh: {
10 | comment: {
11 | pattern: /(^|[^"{\\$])#.*/,
12 | alias: "unselectable",
13 | lookbehind: true
14 | },
15 |
16 | directory: {
17 | pattern: /^[^\r\n$*!]+(?=[$])/m,
18 | alias: "unselectable"
19 | },
20 |
21 | command: {
22 | pattern: /[$](?:[^\r\n])+/,
23 | inside: {
24 | prompt: {
25 | pattern: /^[$] /,
26 | alias: "unselectable"
27 | }
28 | }
29 | }
30 | }
31 | }
32 |
33 | const transformations = {
34 | js: {
35 | keyword: {
36 | to: "declaration-keyword",
37 | for: ["const", "let", "var", "async", "await", "function", "class"]
38 | },
39 | punctuation: {
40 | to: "operator",
41 | for: ["."]
42 | },
43 | "class-name": {
44 | to: "api",
45 | for: ["HTMLRewriter", "Request", "Response", "URL", "Error"]
46 | },
47 | function: {
48 | to: "builtin",
49 | for: ["fetch", "console", "addEventListener", "atob", "btoa", "setInterval", "clearInterval", "setTimeout", "clearTimeout"]
50 | }
51 | }
52 | }
53 |
54 | transformations.html = {
55 | keyword: transformations.js.keyword
56 | }
57 |
58 | export const transformToken = ({ token, children, language }) => {
59 | const lang = transformations[language]
60 | if (!lang) return token
61 |
62 | const any = transformations[language]["*"]
63 | const trans = transformations[language][token]
64 |
65 | if (!any && !trans)
66 | return token
67 |
68 | if (trans && trans.for.includes(children))
69 | return trans.to
70 |
71 | if (any && any.for(children))
72 | return any.to
73 |
74 | return token
75 | }
76 |
--------------------------------------------------------------------------------
/src/components/mdx/definitions.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import { className } from "./root"
4 |
5 | export default props => (
6 |
7 | {props.children}
8 |
9 | )
10 |
--------------------------------------------------------------------------------
/src/components/mdx/demo.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import PropTypes from "prop-types"
3 |
4 | import { className as generateClassName } from "./root"
5 |
6 | const Demo = ({ src, title, height, aspectRatio }) => {
7 | const className = generateClassName("demo") + (aspectRatio ? " AspectRatio" : "")
8 | const iframeClassName = aspectRatio ? "AspectRatio--content" : ""
9 |
10 | const style = aspectRatio ? {
11 | "--aspect-ratio": `calc(${ aspectRatio })`
12 | } : {
13 | height: `${ height }px`
14 | }
15 |
16 | return (
17 |
18 |
24 |
25 | )
26 | }
27 |
28 | Demo.defaultProps = {
29 | height: 400
30 | }
31 |
32 | Demo.propTypes = {
33 | title: PropTypes.string.isRequired
34 | }
35 |
36 | export default Demo
37 |
--------------------------------------------------------------------------------
/src/components/mdx/directory-listing.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { useStaticQuery, graphql } from "gatsby"
3 |
4 | import AnchorLink from "./anchor-link"
5 |
6 | import getParentPath from "../../utils/get-parent-path"
7 | import getPageTitle from "../../utils/get-page-title"
8 | import getOrder from "../../utils/get-order"
9 |
10 | const DirectoryListing = props => {
11 | const query = useStaticQuery(graphql`
12 | query {
13 | allMdx {
14 | edges {
15 | node {
16 | fields {
17 | slug
18 | }
19 | frontmatter {
20 | title
21 | order
22 | hidden
23 | }
24 | headings(depth: h1) {
25 | value
26 | }
27 | }
28 | }
29 | }
30 | }
31 | `)
32 |
33 | const pages = query.allMdx.edges
34 | .map(({ node }) => node)
35 | .filter(page => !page.frontmatter.hidden)
36 | .filter(page => getParentPath(page.fields.slug) === props.path)
37 | .map(page => ({
38 | title: getPageTitle(page),
39 | url: page.fields.slug,
40 | order: getOrder(page),
41 | }))
42 | .sort((a, b) => {
43 | if (a.title < b.title) return -1
44 | if (a.title > b.title) return 1
45 | return 0
46 | })
47 | .sort((a, b) => a.order - b.order)
48 |
49 | return (
50 |
51 | {pages.map(page => (
52 |
53 |
54 | {page.title}
55 |
56 |
57 | ))}
58 |
59 | )
60 | }
61 |
62 | export default DirectoryListing
63 |
--------------------------------------------------------------------------------
/src/components/mdx/example.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import { className } from "./root"
4 |
5 | export default props => (
6 |
7 | {props.children}
8 |
9 | )
10 |
--------------------------------------------------------------------------------
/src/components/mdx/headers.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import { className } from "./root"
4 |
5 | const header = (TagName) => (props => {
6 | const href = `#${ props.id }`
7 |
8 | return (
9 |
10 |
11 |
12 |
13 | {props.children}
14 |
15 | )
16 | })
17 |
18 | export default {
19 | h2: header('h2'),
20 | h3: header('h3'),
21 | h4: header('h4'),
22 | h5: header('h5'),
23 | h6: header('h6'),
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/mdx/inline-code.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | export default props => (
4 |
5 | {props.children}
6 |
7 | )
8 |
--------------------------------------------------------------------------------
/src/components/mdx/param-type.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | export default props => (
4 |
5 | {props.children}
6 |
7 | )
8 |
--------------------------------------------------------------------------------
/src/components/mdx/prop-meta.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import { className } from "./root"
4 |
5 | export default props => (
6 |
7 | {props.children}
8 |
9 | )
10 |
--------------------------------------------------------------------------------
/src/components/mdx/root.js:
--------------------------------------------------------------------------------
1 | const prefix = "DocsMarkdown"
2 | export const className = suffix => !suffix ? prefix : `${prefix}--${suffix}`
3 |
--------------------------------------------------------------------------------
/src/components/mdx/stream-video.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | const defaultAspectRatio = 16 / 9
4 |
5 | export default props => {
6 | const style = {
7 | "--aspect-ratio": `calc(${ props.aspectRatio || defaultAspectRatio })`
8 | }
9 |
10 | return (
11 |
12 |
13 |
20 |
21 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/mdx/table-wrap.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import { className } from "./root"
4 |
5 | export default props => (
6 |
7 |
8 | {props.children}
9 |
10 |
11 | )
12 |
--------------------------------------------------------------------------------
/src/components/mdx/type-link.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import AnchorLink from "./anchor-link"
3 |
4 | export default props => (
5 |
6 |
7 | {props.children}
8 |
9 |
10 | )
11 |
--------------------------------------------------------------------------------
/src/components/mdx/type.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | export default props => (
4 |
5 | {props.children}
6 |
7 | )
8 |
--------------------------------------------------------------------------------
/src/components/mdx/youtube.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | const defaultAspectRatio = 16 / 9
4 |
5 | export default props => {
6 | const style = {
7 | "--aspect-ratio": `calc(${ props.aspectRatio || defaultAspectRatio })`
8 | }
9 |
10 | return (
11 |
12 |
13 | VIDEO
20 |
21 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/scrollbars-with-scroll-shadows.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 |
3 | import { Scrollbars } from "react-custom-scrollbars"
4 |
5 | class ScrollbarsWithScrollShadows extends Component {
6 |
7 | constructor(props) {
8 | super(props)
9 |
10 | this.state = {
11 | scrollTop: 0
12 | }
13 |
14 | this.handleUpdate = this.handleUpdate.bind(this)
15 | }
16 |
17 | componentDidMount() {
18 | const { scrollbars } = this.refs
19 |
20 | this.observer = new MutationObserver(mutations => {
21 | mutations.forEach(function(mutation) {
22 | scrollbars.update()
23 | })
24 | })
25 |
26 | this.observer.observe(scrollbars.container, {
27 | subtree: true,
28 | childList: true,
29 | attributes: true
30 | })
31 | }
32 |
33 | componentWillUnmount() {
34 | this.observer.disconnect()
35 | }
36 |
37 | handleUpdate(values) {
38 | const { shadow } = this.refs
39 | const { scrollTop } = values
40 | const shadowOpacity = 1 / 20 * Math.min(scrollTop, 20)
41 | shadow.style.opacity = shadowOpacity
42 | }
43 |
44 | render() {
45 | const { children, shadowClassName, ...props } = this.props
46 |
47 | return (
48 | (
52 |
53 | )}
54 | renderThumbVertical={props => (
55 |
56 | )}
57 | {...props}
58 | >
59 |
60 | {children}
61 |
62 | )
63 | }
64 | }
65 |
66 | export default ScrollbarsWithScrollShadows
67 |
--------------------------------------------------------------------------------
/src/components/seo.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import PropTypes from "prop-types"
3 | import Helmet from "react-helmet"
4 | import { useStaticQuery, graphql } from "gatsby"
5 |
6 | function SEO({ lang, title, description, meta }) {
7 | const { site } = useStaticQuery(
8 | graphql`
9 | query {
10 | site {
11 | siteMetadata {
12 | title
13 | description
14 | author
15 | image
16 | }
17 | }
18 | }
19 | `
20 | )
21 |
22 | const metaDescription = description || site.siteMetadata.description
23 |
24 | const pageTitle = title
25 | const siteTitle = site.siteMetadata.title
26 |
27 | const isHomeTitle = [
28 | "Home",
29 | "Docs",
30 | "Overview",
31 | "Welcome",
32 | siteTitle
33 | ].includes(pageTitle)
34 |
35 | title = isHomeTitle ? siteTitle : `${pageTitle} · ${siteTitle}`
36 |
37 | return (
38 |
39 |
40 |
41 | {title}
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | )
57 | }
58 |
59 | SEO.defaultProps = {
60 | lang: "en",
61 | description: "",
62 | meta: []
63 | }
64 |
65 | SEO.propTypes = {
66 | lang: PropTypes.string,
67 | title: PropTypes.string.isRequired,
68 | description: PropTypes.string,
69 | meta: PropTypes.arrayOf(PropTypes.object)
70 | }
71 |
72 | export default SEO
73 |
--------------------------------------------------------------------------------
/src/components/smooth-scroll-hash-changes.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import { globalHistory } from "@reach/router"
4 |
5 | import isMobile from "../utils/is-mobile"
6 | import userPrefersReducedMotion from "../utils/user-prefers-reduced-motion"
7 | import getNormalizedPath from "../utils/get-normalized-path"
8 |
9 | class SmoothScrollHashChanges extends React.Component {
10 |
11 | componentDidMount() {
12 | this.previousPathname = window.location.pathname
13 |
14 | // Apply smooth scrolling only during page transitions so
15 | // as not to disrupt the browser’s normal in-page search
16 | // https://css-tricks.com/downsides-of-smooth-scrolling/
17 | this.historyUnsubscribe = globalHistory.listen(({ location, action }) => {
18 | const pathname = getNormalizedPath(location.pathname)
19 | const previousPathname = getNormalizedPath(this.previousPathname)
20 | const pathnameChanged = pathname !== previousPathname
21 | this.previousPathname = location.pathname
22 |
23 | // Apply smooth scrolling only during hash changes
24 | if (pathnameChanged) return
25 |
26 | // Apply smooth scroll only on desktop devices
27 | if (isMobile()) return
28 |
29 | // Apply smooth scroll only for users who specify
30 | // `no-preference` for reduced motion
31 | // https://web.dev/prefers-reduced-motion/
32 | if (userPrefersReducedMotion()) return
33 |
34 | document.documentElement.setAttribute("is-smooth-scrolling", "")
35 |
36 | if (this.smoothScrollTimeout) {
37 | clearTimeout(this.smoothScrollTimeout)
38 | }
39 |
40 | // Unfortunately, there’s no way to know when the scrolling
41 | // animation has finished, even when using the native browser
42 | // APIs .scroll() or .scrollIntoView(). However, fortunately,
43 | // the browser seems to allow the animation to complete so
44 | // long as `scroll-behavior: smooth` is applied when the hash
45 | // change occurs and even if the styles are removed during
46 | // the animation. If a Promise or callback behavior is ever
47 | // added to the browser APIs, we can use that instead.
48 | // https://github.com/w3c/csswg-drafts/issues/3744
49 | this.smoothScrollTimeout = setTimeout(() => {
50 | document.documentElement.removeAttribute("is-smooth-scrolling")
51 | }, 100)
52 | })
53 | }
54 |
55 | componentWillUnmount() {
56 | this.historyUnsubscribe()
57 | }
58 |
59 | render() {
60 | return null
61 | }
62 | }
63 |
64 | export default SmoothScrollHashChanges
65 |
--------------------------------------------------------------------------------
/src/components/theme-toggle.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import Helmet from "react-helmet"
3 |
4 | import setIntervalVisible from "set-interval-visible"
5 | import AccessibleSVG from "./accessible-svg"
6 |
7 | const keyboardShortcutKey = "D"
8 | const colorSchemeQuery = "(prefers-color-scheme: dark)"
9 |
10 | const storeInterval = 30 * 1000
11 | const storeExpiration = 30 * 60 * 1000
12 |
13 | // Run theme initialization in the to avoid a flash
14 | const getThemeFromStorageSource = `
15 | (() => {
16 | getThemeFromStorage = () => {
17 | let storedTheme
18 |
19 | const query = window.matchMedia("${ colorSchemeQuery }")
20 | const queryTheme = query.matches ? "dark" : "light"
21 |
22 | try {
23 | const theme = JSON.parse(localStorage.theme)
24 | const themeIsValid = ["dark", "light"].includes(theme.theme)
25 | const themeWasRecentlySet = theme.updated > +new Date - ${ storeExpiration }
26 |
27 | if (themeIsValid && themeWasRecentlySet) {
28 | storedTheme = theme.theme
29 | }
30 | } catch (error) {}
31 |
32 | return storedTheme || queryTheme
33 | }
34 |
35 | document.documentElement.setAttribute("theme", getThemeFromStorage())
36 | })()
37 | `
38 |
39 | const getThemeFromStorage = () => {
40 | let storedTheme
41 |
42 | const query = window.matchMedia(colorSchemeQuery)
43 | const queryTheme = query.matches ? "dark" : "light"
44 |
45 | try {
46 | const theme = JSON.parse(localStorage.theme)
47 | const themeIsValid = ["dark", "light"].includes(theme.theme)
48 | const themeWasRecentlySet = theme.updated > +new Date() - storeExpiration
49 |
50 | if (themeIsValid && themeWasRecentlySet) {
51 | storedTheme = theme.theme
52 | }
53 | } catch (error) {}
54 |
55 | return storedTheme || queryTheme
56 | }
57 |
58 | const getTheme = () => {
59 | return document.documentElement.getAttribute("theme")
60 | }
61 |
62 | const setTheme = theme => {
63 | document.documentElement.setAttribute("theme-is-changing", "")
64 | document.documentElement.setAttribute("theme", theme)
65 | requestAnimationFrame(() => {
66 | document.documentElement.removeAttribute("theme-is-changing")
67 | })
68 | }
69 |
70 | const storeTheme = theme => {
71 | localStorage.theme = JSON.stringify({
72 | theme: theme,
73 | updated: +new Date()
74 | })
75 | }
76 |
77 | class ThemeToggle extends React.Component {
78 |
79 | constructor(props) {
80 | super(props)
81 |
82 | this.state = {
83 | loading: true,
84 | checked: false
85 | }
86 |
87 | this.onCheckboxChange = this.onCheckboxChange.bind(this)
88 | this.onMediaMatchChange = this.onMediaMatchChange.bind(this)
89 | this.handleKeyboardCommand = this.handleKeyboardCommand.bind(this)
90 | }
91 |
92 | toggle() {
93 | this.setState(state => {
94 | const checked = !state.checked
95 |
96 | const theme = checked ? "dark" : "light"
97 | setTheme(theme)
98 | storeTheme(theme)
99 |
100 | return { checked }
101 | })
102 | }
103 |
104 | onCheckboxChange() {
105 | this.toggle()
106 | }
107 |
108 | onMediaMatchChange(event) {
109 | const theme = event.matches ? "dark" : "light"
110 | setTheme(theme)
111 |
112 | const checked = theme === "dark"
113 | this.setState({ checked })
114 | }
115 |
116 | handleKeyboardCommand(event) {
117 | if (event.target && event.target) {
118 | const tag = event.target.tagName.toLowerCase()
119 | if (["input", "textarea"].includes(tag)) return
120 | }
121 |
122 | if (event.key !== keyboardShortcutKey) return
123 | if (!event.shiftKey) return
124 |
125 | this.toggle()
126 | }
127 |
128 | componentDidMount() {
129 | const theme = getThemeFromStorage()
130 | const checked = theme === "dark"
131 |
132 | const loading = false
133 | this.setState({ checked, loading })
134 |
135 | this.query = window.matchMedia(colorSchemeQuery)
136 | this.query.addListener(this.onMediaMatchChange)
137 |
138 | this.interval = setIntervalVisible(() => {
139 | storeTheme(getTheme())
140 | }, storeInterval)
141 |
142 | document.addEventListener("keydown", this.handleKeyboardCommand)
143 | }
144 |
145 | componentWillUnmount() {
146 | this.query.removeListener(this.onMediaMatchChange)
147 | this.interval.clear()
148 |
149 | document.removeEventListener("keydown", this.handleKeyboardCommand)
150 | }
151 |
152 | render() {
153 | const { loading } = this.state
154 |
155 | return (
156 | <>
157 |
158 | {/* Run theme initialization in the to avoid a flash */}
159 |
160 |
161 |
162 |
163 |
164 |
170 |
171 |
172 |
173 |
178 |
183 |
184 |
185 |
186 | {`Set theme to ${this.state.checked ? "light" : "dark"} (⇧+D)`}
187 |
188 |
189 |
190 | >
191 | )
192 | }
193 | }
194 |
195 | export default ThemeToggle
196 |
--------------------------------------------------------------------------------
/src/components/worker-starter.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import { CopyToClipboard } from "react-copy-to-clipboard"
4 |
5 | import AnchorLink from "./mdx/anchor-link"
6 | import CodeBlock from "./code-block"
7 |
8 | const WorkerStarter = props => {
9 | const { title, description, repo } = props
10 | const repoLink = `https://github.com/${repo}`
11 | const command = `wrangler generate my-app ${repoLink}`
12 |
13 | return (
14 |
15 |
18 |
{description}
19 |
20 |
21 |
22 | Copy
23 |
24 |
25 |
26 | {command}
27 |
28 |
29 |
30 | )
31 | }
32 |
33 | export default WorkerStarter
34 |
--------------------------------------------------------------------------------
/src/constants/sidebar-collapse-transition-duration.js:
--------------------------------------------------------------------------------
1 | export default 400
2 |
--------------------------------------------------------------------------------
/src/css/docs/components/architecture-diagram.css:
--------------------------------------------------------------------------------
1 | /* Note: px units are used throughout the file to avoid rounding issues */
2 |
3 | .ArchitectureDiagram {
4 | --user-code-size: 25px;
5 | --accent-color-rgb: var(--orange-rgb);
6 | position: relative;
7 | display: flex;
8 | flex-wrap: wrap;
9 | justify-content: space-between;
10 | width: 100%;
11 | max-width: 100%;
12 | }
13 |
14 | @media (max-width: 1350px) { .ArchitectureDiagram { --user-code-size: 22px }}
15 | @media (max-width: 1280px) { .ArchitectureDiagram { --user-code-size: 24px }}
16 | @media (max-width: 1250px) { .ArchitectureDiagram { --user-code-size: 22px }}
17 | @media (max-width: 1195px) { .ArchitectureDiagram { --user-code-size: 20px }}
18 | @media (max-width: 1152px) { .ArchitectureDiagram { --user-code-size: 25px }}
19 | @media (max-width: 1080px) { .ArchitectureDiagram { --user-code-size: 22px }}
20 | @media (max-width: 986px) { .ArchitectureDiagram { --user-code-size: 20px }}
21 | @media (max-width: 920px) { .ArchitectureDiagram { --user-code-size: 15px }}
22 | @media (max-width: 820px) { .ArchitectureDiagram { --user-code-size: 12px }}
23 | @media (max-width: 768px) { .ArchitectureDiagram { --user-code-size: 20px }}
24 | @media (max-width: 576px) { .ArchitectureDiagram { --user-code-size: 18px }}
25 | @media (max-width: 520px) { .ArchitectureDiagram { --user-code-size: 16px }}
26 |
27 | .ArchitectureDiagram--diagram {
28 | --size: calc(var(--user-code-size) * 9);
29 | width: var(--size);
30 | }
31 |
32 | @media (max-width: 480px) {
33 | .ArchitectureDiagram {
34 | --user-code-size: 25px;
35 | justify-content: flex-start;
36 | }
37 |
38 | .ArchitectureDiagram--diagram {
39 | width: 100%;
40 | }
41 |
42 | .ArchitectureDiagram--diagram + .ArchitectureDiagram--diagram {
43 | margin-top: 2em;
44 | }
45 |
46 | .ArchitectureDiagram--key {
47 | position: absolute;
48 | top: 0;
49 | right: 0;
50 | bottom: 0;
51 | }
52 |
53 | .ArchitectureDiagram--key-content {
54 | position: sticky;
55 | top: calc(var(--docs-header-height) + var(--user-code-size));
56 | }
57 |
58 | .ArchitectureDiagram--caption {
59 | width: var(--size);
60 | }
61 | }
62 |
63 | @media (max-width: 374px) {
64 | .ArchitectureDiagram--key {
65 | width: 5em;
66 | }
67 | }
68 |
69 | @media (max-width: 414px) { .ArchitectureDiagram { --user-code-size: 22px }}
70 | @media (max-width: 375px) { .ArchitectureDiagram { --user-code-size: 20px }}
71 | @media (max-width: 360px) { .ArchitectureDiagram { --user-code-size: 19px }}
72 | @media (max-width: 340px) { .ArchitectureDiagram { --user-code-size: 18px }}
73 | @media (max-width: 330px) { .ArchitectureDiagram { --user-code-size: 17px }}
74 |
75 | @media (max-width: 319px) {
76 | .ArchitectureDiagram {
77 | /* TODO */
78 | display: none;
79 | }
80 | }
81 |
82 | .ArchitectureDiagram--content {
83 | position: relative;
84 | display: flex;
85 | flex-wrap: wrap;
86 | flex-shrink: 0;
87 | width: var(--size);
88 | height: var(--size);
89 | }
90 |
91 | .ArchitectureDiagram--caption {
92 | margin-top: .666em;
93 | max-width: 100%;
94 | line-height: 1.3;
95 | }
96 |
97 | .ArchitectureDiagram--process-overhead {
98 | --size: calc(var(--user-code-size) * 3);
99 | position: relative;
100 | display: flex;
101 | flex-wrap: wrap;
102 | flex-shrink: 0;
103 | width: var(--size);
104 | height: var(--size);
105 | }
106 |
107 | .ArchitectureDiagram--process-overhead::after {
108 | content: "↻";
109 | display: block;
110 | position: absolute;
111 | top: 0;
112 | right: 0;
113 | bottom: 0;
114 | left: 0;
115 | height: 1em;
116 | width: 1em;
117 | margin: auto;
118 | font-size: 2em;
119 | line-height: 1;
120 | text-align: center;
121 | color: rgb(var(--accent-color-rgb));
122 | }
123 |
124 | .ArchitectureDiagram--process-overhead-background {
125 | position: absolute;
126 | top: 0;
127 | right: 0;
128 | bottom: 0;
129 | left: 0;
130 | background: rgba(var(--accent-color-rgb), .25);
131 | }
132 |
133 | .ArchitectureDiagram--user-code {
134 | position: relative;
135 | z-index: 1;
136 | width: var(--user-code-size);
137 | height: var(--user-code-size);
138 | background: rgba(var(--accent-color-rgb), .9);
139 | }
140 |
141 | .ArchitectureDiagram--process-overhead-background,
142 | .ArchitectureDiagram--user-code {
143 | --box-shadow: inset 0 0 0 1px rgb(var(--background-color-rgb)); /* Workaround for Safari bug - https://bugs.webkit.org/show_bug.cgi?id=185940 */
144 | box-shadow: var(--box-shadow);
145 | }
146 |
147 | .ArchitectureDiagram--user-code::after {
148 | content: "{ }";
149 | display: block;
150 | font-size: calc(var(--user-code-size) * .5);
151 | line-height: calc(var(--user-code-size) * .93);
152 | font-weight: bold;
153 | height: 100%;
154 | width: 100%;
155 | text-align: center;
156 | color: rgba(var(--background-color-rgb), .7);
157 | }
158 |
159 | .ArchitectureDiagram--key-user-code {
160 | margin-bottom: 1em;
161 | }
162 |
163 | .ArchitectureDiagram--key-label {
164 | opacity: .8;
165 | padding-top: .25em;
166 | }
167 |
--------------------------------------------------------------------------------
/src/css/docs/components/docs-body.css:
--------------------------------------------------------------------------------
1 | .DocsBody {
2 | position: relative;
3 | padding-top: var(--docs-header-height);
4 | padding-left: var(--docs-sidebar-width);
5 | }
6 |
7 | @media (max-width: 768px) {
8 | .DocsBody {
9 | padding-top: 0;
10 | padding-left: 0;
11 | will-change: opacity;
12 | transition: opacity .3s ease;
13 | }
14 |
15 | [is-mobile-sidebar-open] .DocsBody {
16 | opacity: .3;
17 | }
18 | }
19 |
20 | .DocsBody--sidebar {
21 | position: absolute;
22 | right: 0;
23 | top: calc(var(--docs-header-height) + 2.7em - 1em); /* TODO - clean up magic numbers */
24 | bottom: 1em;
25 | width: var(--docs-body-sidebar-width);
26 | }
27 |
28 | @media (max-width: 1152px) {
29 | .DocsBody--sidebar {
30 | display: none;
31 | }
32 | }
33 |
34 | .DocsBody--sidebar-content-scroll-fade {
35 | position: sticky;
36 | top: 0;
37 | display: block;
38 | background: linear-gradient(var(--background-color), rgba(var(--background-color-rgb), 0));
39 | height: 1em;
40 | margin-left: -1em;
41 | width: 100%;
42 | z-index: 1;
43 | }
44 |
45 | .DocsBody--sidebar-content {
46 | position: sticky;
47 | top: 0;
48 | width: calc(100% + 1em);
49 | max-height: 100vh;
50 | overflow-y: auto;
51 | -webkit-overflow-scrolling: touch;
52 | padding: 1em 0 0 1em;
53 | margin: -1em;
54 | transition: opacity .3s ease;
55 | scrollbar-width: thin;
56 | scrollbar-color: rgba(var(--color-rgb), .1) transparent;
57 | }
58 |
59 | @media (min-width: 1281px) {
60 | .DocsBody--sidebar {
61 | padding-left: 2em;
62 | }
63 | }
64 |
65 | .DocsBody--sidebar-content:hover {
66 | scrollbar-color: rgba(var(--color-rgb), .35) transparent;
67 | }
68 |
69 | [theme="dark"] .DocsBody--sidebar-content:not(:hover):not(:focus-within) {
70 | opacity: .55;
71 | }
72 |
--------------------------------------------------------------------------------
/src/css/docs/components/docs-code-examples-overview.css:
--------------------------------------------------------------------------------
1 | .DocsCodeExamplesOverview {
2 | margin: 2.5rem 0;
3 | display: grid;
4 | grid-template-columns: repeat(2, minmax(0, 1fr));
5 | grid-gap: var(--docs-content-gap);
6 | }
7 |
8 | .DocsCodeExamplesOverview--example {
9 | border-radius: .5em;
10 | }
11 |
12 | .DocsCodeExamplesOverview--example-title {
13 | font-size: 1.5em;
14 | line-height: 1.2;
15 | font-weight: 500;
16 | margin-bottom: .5em;
17 | white-space: nowrap;
18 | text-overflow: ellipsis;
19 | overflow: hidden;
20 | }
21 |
22 | .DocsCodeExamplesOverview--example-title .Link { /* TODO */
23 | color: inherit;
24 | --underline-opacity: .75;
25 | text-decoration: underline;
26 | text-decoration-color: rgba(var(--orange-rgb), var(--underline-opacity));
27 | box-shadow: 0 0 0 var(--focus-size) var(--focus-color);
28 | }
29 |
30 | .DocsCodeExamplesOverview--example-description {
31 | margin-bottom: 1em;
32 | }
33 |
34 | @media (min-width: 1153px) {
35 | .DocsCodeExamplesOverview--example-description {
36 | --lines: 2;
37 | display: -webkit-box;
38 | -webkit-line-clamp: var(--lines);
39 | -webkit-box-orient: vertical;
40 | height: calc(1em * var(--lines) * var(--line-height));
41 | overflow: hidden;
42 | }
43 | }
44 |
45 | .DocsCodeExamplesOverview--example-description > *:last-child {
46 | margin-bottom: 0;
47 | }
48 |
49 | .DocsCodeExamplesOverview--example-codeblock-link {
50 | display: block;
51 | position: relative;
52 | color: inherit;
53 | }
54 |
55 | .DocsCodeExamplesOverview--example-codeblock-link::after {
56 | content: "";
57 | position: absolute;
58 | top: 0;
59 | right: 0;
60 | bottom: 0;
61 | left: 0;
62 | --active-box-shadow-color: transparent;
63 | box-shadow: inset 0 .0625em .1875em var(--active-box-shadow-color);
64 | border-radius: .5em;
65 | }
66 |
67 | @media (hover: hover) {
68 | .DocsCodeExamplesOverview--example-codeblock-link:hover:not(:active)::after {
69 | background: rgba(255, 255, 255, .2);
70 | }
71 |
72 | [theme="dark"] .DocsCodeExamplesOverview--example-codeblock-link:hover:not(:active)::after {
73 | background: rgba(var(--color-rgb), .05);
74 | }
75 | }
76 |
77 | .DocsCodeExamplesOverview--example-codeblock-link:active::after {
78 | background: rgba(var(--shadow-color-rgb), .05);
79 | --active-box-shadow-color: rgba(var(--shadow-color-rgb), .2);
80 | }
81 |
82 | .DocsCodeExamplesOverview--example-codeblock-link pre {
83 | height: 19em;
84 | margin-bottom: 0;
85 | pointer-events: none;
86 | }
87 |
88 | .DocsCodeExamplesOverview--example-codeblock-link pre,
89 | .DocsCodeExamplesOverview--example-codeblock-link pre code {
90 | overflow: hidden;
91 | }
92 |
93 | @media (max-width: 1152px) {
94 | .DocsCodeExamplesOverview {
95 | margin-left: 0;
96 | margin-right: 0;
97 | grid-template-columns: 100%;
98 | }
99 | }
100 |
101 | @media (max-width: 768px) {
102 | .DocsCodeExamplesOverview--example-title {
103 | font-size: 1.375em;
104 | margin-bottom: .5rem;
105 | }
106 |
107 | .DocsCodeExamplesOverview--example-codeblock-link {
108 | margin-left: -1rem;
109 | width: calc(100% + 2rem);
110 | }
111 |
112 | .DocsCodeExamplesOverview--example-codeblock-link::after {
113 | border-radius: 0;
114 | }
115 |
116 | .DocsCodeExamplesOverview--example-codeblock-link pre {
117 | height: 12.2em;
118 | --border-radius: 0em;
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/css/docs/components/docs-content.css:
--------------------------------------------------------------------------------
1 | .DocsContent {
2 | padding: 2.6rem var(--docs-content-gap) 3.5rem; /* TODO */
3 | min-height: calc(100vh - 12.5em); /* TODO */
4 | }
5 |
6 | .DocsContent[page-type="document"] {
7 | width: calc(100% - var(--docs-body-sidebar-width));
8 | }
9 |
10 | @media (min-width: 769px) {
11 | [search-disabled] .DocsContent {
12 | padding-top: 0;
13 | }
14 | }
15 |
16 | @media (max-width: 768px) {
17 | .DocsContent {
18 | --docs-content-horizontal-padding: 1rem;
19 | padding: 2rem var(--docs-content-horizontal-padding);
20 | min-height: 1em; /* TODO */
21 | }
22 | }
23 |
24 | .DocsContent--breadcrumbs {
25 | margin: -.5em 0 .3125em;
26 | }
27 |
28 | @media (min-width: 769px) {
29 | .DocsContent--breadcrumbs {
30 | display: none;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/css/docs/components/docs-footer.css:
--------------------------------------------------------------------------------
1 | .DocsFooter {
2 | margin-left: var(--docs-sidebar-width);
3 | padding: 3.5rem 0; /* TODO */
4 | padding-left: var(--docs-content-gap);
5 | padding-right: calc(var(--docs-body-sidebar-width) + var(--docs-content-gap));
6 | --border-color: rgba(var(--color-rgb), .08);
7 | border-top: 1px solid var(--border-color);
8 | }
9 |
10 | @media (max-width: 768px) {
11 | .DocsFooter {
12 | margin-left: 0;
13 | padding: 2rem 1rem 4rem
14 | }
15 | }
16 |
17 | .DocsFooter--content {
18 | display: flex;
19 | }
20 |
21 | @media (max-width: 414px) {
22 | .DocsFooter--edit-on-gh-link-wrapper {
23 | display: block;
24 | margin-bottom: .5em;
25 | }
26 |
27 | .DocsFooter--content-dot-spacer {
28 | display: none;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/css/docs/components/docs-mobile-header.css:
--------------------------------------------------------------------------------
1 | .DocsMobileHeader {
2 | position: absolute;
3 | display: flex;
4 | align-items: center;
5 | top: 0;
6 | left: 0;
7 | width: 100%;
8 | height: var(--docs-header-height);
9 | background: var(--background-color);
10 | box-shadow: 0 1px rgba(var(--color-rgb), .05);
11 | z-index: 11;
12 | --content-indent: .666em;
13 | }
14 |
15 | [theme="dark"] .DocsMobileHeader {
16 | background: var(--gray-05);
17 | }
18 |
19 | @media (min-width: 769px) {
20 | .DocsMobileHeader {
21 | display: none;
22 | }
23 | }
24 |
25 | .DocsMobileHeader--cloudflare-logo-link {
26 | --font-size: 1.1em;
27 | display: flex;
28 | align-items: center;
29 | padding-left: var(--content-indent);
30 | padding-right: var(--content-indent);
31 | transition: opacity .2s ease, color .2s ease;
32 | }
33 |
34 | @media (hover: hover) {
35 | .DocsMobileHeader--cloudflare-logo-link:hover {
36 | opacity: .75;
37 | color: rgba(var(--color-rgb), .75);
38 | }
39 | }
40 |
41 | @media (hover: none) {
42 | .DocsMobileHeader--cloudflare-logo-link:active {
43 | opacity: .75;
44 | color: rgba(var(--color-rgb), .75);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/css/docs/components/docs-mobile-nav-backdrop.css:
--------------------------------------------------------------------------------
1 | .DocsMobileNavBackdrop {
2 | position: fixed;
3 | top: 0;
4 | right: 0;
5 | bottom: 0;
6 | left: 0;
7 | opacity: 0;
8 | pointer-events: none;
9 | background: rgba(var(--shadow-color-rgb), .5);
10 | will-change: opacity;
11 | transition: opacity .3s ease;
12 | z-index: 8;
13 | }
14 |
15 | @media (min-width: 769px) {
16 | .DocsMobileNavBackdrop {
17 | display: none;
18 | }
19 | }
20 |
21 | [is-mobile-sidebar-open] .DocsMobileNavBackdrop {
22 | opacity: 1;
23 | pointer-events: all;
24 | }
25 |
--------------------------------------------------------------------------------
/src/css/docs/components/docs-mobile-title-header.css:
--------------------------------------------------------------------------------
1 | .DocsMobileTitleHeader {
2 | position: sticky;
3 | display: flex;
4 | align-items: center;
5 | top: 0;
6 | left: 0;
7 | width: 100%;
8 | padding-right: .5rem;
9 | height: var(--docs-header-height);
10 | background: var(--background-color);
11 | box-shadow: 0 1px rgba(var(--shadow-color-rgb), .075), 0 .2em .3em -.1em rgba(var(--shadow-color-rgb), .075);
12 | z-index: 10;
13 | --content-indent: .666em;
14 | }
15 |
16 | [theme="dark"] .DocsMobileTitleHeader {
17 | background: var(--gray-05);
18 | }
19 |
20 | @media (min-width: 769px) {
21 | .DocsMobileTitleHeader {
22 | display: none;
23 | }
24 | }
25 |
26 | .DocsMobileTitleHeader--logo-link {
27 | --font-size: 1.333em;
28 | display: flex;
29 | align-items: center;
30 | padding-left: var(--content-indent);
31 | padding-right: var(--content-indent);
32 | transition: opacity .2s ease, color .2s ease;
33 | }
34 |
35 | @media (hover: hover) {
36 | .DocsMobileTitleHeader--logo-link:hover {
37 | opacity: .75;
38 | color: rgba(var(--color-rgb), .75);
39 | }
40 | }
41 |
42 | @media (hover: none) {
43 | .DocsMobileTitleHeader--logo-link:active {
44 | opacity: .75;
45 | color: rgba(var(--color-rgb), .75);
46 | }
47 | }
48 |
49 | .DocsMobileTitleHeader--text-scaler {
50 | /* --length set by React */
51 | --font-size: 1em * (1 - ((var(--length) - 10) / 50));
52 | font-size: clamp(.7em, var(--font-size), 1em);
53 | display: inherit;
54 | }
55 |
56 | .DocsMobileTitleHeader--sidebar-toggle-button {
57 | /* For pixel-fitted SVG */
58 | width: 40px;
59 | height: 40px;
60 | margin-left: auto;
61 | padding: 0;
62 | }
63 |
64 | .DocsMobileTitleHeader--sidebar-toggle-button path {
65 | transition: transform .3s ease, opacity .3s ease;
66 | }
67 |
68 | [is-mobile-sidebar-open] .DocsMobileTitleHeader--sidebar-toggle-button path[data-index="1"] {
69 | transform: translateY(15%) rotate(45deg);
70 | }
71 |
72 | [is-mobile-sidebar-open] .DocsMobileTitleHeader--sidebar-toggle-button path[data-index="2"] {
73 | opacity: 0;
74 | }
75 |
76 | [is-mobile-sidebar-open] .DocsMobileTitleHeader--sidebar-toggle-button path[data-index="3"] {
77 | transform: translateY(-15%) rotate(-45deg);
78 | }
79 |
--------------------------------------------------------------------------------
/src/css/docs/components/docs-nav-logo-lockup.css:
--------------------------------------------------------------------------------
1 | .DocsNavLogoLockup {
2 | display: flex;
3 | align-items: center;
4 | flex-shrink: 0;
5 | --gap: var(--docs-logo-lockup-gap);
6 | }
7 |
8 | .DocsNavLogoLockup.DocsNavLogoLockup-with-small-gap {
9 | --gap: var(--docs-logo-lockup-gap-small);
10 | }
11 |
12 | .DocsNavLogoLockup--logo {
13 | --size: var(--logo-size, 2.5em);
14 | height: var(--size);
15 | width: var(--size);
16 | color: var(--orange);
17 | margin-right: var(--gap);
18 | flex-shrink: 0;
19 | }
20 |
21 | .DocsNavLogoLockup--text {
22 | font-size: var(--font-size, 1em);
23 | font-weight: 700;
24 | line-height: 1;
25 | padding-top: .05em;
26 | letter-spacing: -.03em;
27 | }
28 |
29 | @-moz-document url-prefix() {
30 | .DocsNavLogoLockup--text {
31 | letter-spacing: 0;
32 | }
33 | }
34 |
35 | .DocsNavLogoLockup--text [data-text="Cloudflare"] ~ [data-text="Docs"] {
36 | font-weight: 500;
37 | margin-left: -.05em;
38 | }
39 |
40 | @media (max-width: 768px) {
41 | .DocsNavLogoLockup--text {
42 | font-weight: 600;
43 | }
44 |
45 | .DocsNavLogoLockup--text [data-text="Cloudflare"] ~ [data-text="Docs"] {
46 | font-weight: 400;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/css/docs/components/docs-noscript.css:
--------------------------------------------------------------------------------
1 | .DocsNoscript {
2 | position: fixed;
3 | top: 0;
4 | left: 50%;
5 | transform: translate3d(-50%, 0, 0);
6 | font-size: .8em;
7 | padding: .5em 1em;
8 | background: rgba(var(--orange-rgb), .15);
9 | border-radius: 0 0 .25em .25em;
10 | z-index: 100;
11 | }
12 |
--------------------------------------------------------------------------------
/src/css/docs/components/docs-page.css:
--------------------------------------------------------------------------------
1 | html[is-docs-page],
2 | html[is-docs-page] body {
3 | scroll-behavior: smooth;
4 | height: 100%;
5 | overscroll-behavior-y: none;
6 | }
7 |
8 | .DocsPage {
9 | --sixty-four-px-rem: 3.7647058824rem; /* 64 / 17 */
10 | --docs-content-gap: var(--sixty-four-px-rem);
11 | --docs-header-height: var(--sixty-four-px-rem);
12 | --docs-sidebar-width: 18.25rem;
13 | --docs-body-sidebar-width: var(--docs-sidebar-width);
14 | --docs-page-side-padding: 1rem;
15 | --docs-max-page-width: 1440px;
16 | --docs-page-width: var(--docs-max-page-width);
17 |
18 | --docs-sidebars-combined-width: calc(var(--docs-sidebar-width) + var(--docs-body-sidebar-width));
19 | --docs-body-width: calc(var(--docs-page-width) - var(--docs-sidebars-combined-width));
20 |
21 | --docs-logo-lockup-gap: .4rem;
22 | --docs-logo-lockup-gap-small: .2rem;
23 |
24 | position: relative;
25 | width: 100%;
26 | max-width: var(--docs-max-page-width);
27 | min-height: 100%;
28 | margin: 0 auto;
29 | }
30 |
31 | @media (max-width: 1439px) {
32 | .DocsPage {
33 | --docs-page-width: 100vw;
34 |
35 | --docs-page-sidebar-preserverd-width: calc(100vw - var(--docs-sidebar-width)
36 | - (var(--docs-max-page-width) - 2 * var(--docs-sidebar-width)));
37 |
38 | --docs-body-sidebar-width: calc(15vw + var(--docs-page-sidebar-preserverd-width) / 4);
39 | }
40 | }
41 |
42 | @media (max-width: 1350px) {
43 | .DocsPage {
44 | --docs-sidebar-width: 18rem;
45 | }
46 | }
47 |
48 | @media (max-width: 1280px) {
49 | .DocsPage {
50 | --docs-sidebar-width: 17rem;
51 | --docs-body-sidebar-width: 18vw;
52 | }
53 | }
54 |
55 | @media (max-width: 1152px) {
56 | .DocsPage {
57 | --docs-sidebar-width: calc(10rem + 12vw);
58 | --docs-body-sidebar-width: 0px;
59 | }
60 | }
61 |
62 | @media (max-width: 768px) {
63 | .DocsPage {
64 | --docs-header-height: 3rem;
65 | /* padding-top: var(--docs-header-height); */
66 | }
67 |
68 | html[is-docs-page][is-mobile-sidebar-open] {
69 | overflow: hidden;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/css/docs/components/docs-search.css:
--------------------------------------------------------------------------------
1 | .DocsSearch {
2 | --text-indent: calc(var(--docs-content-gap) + 1px);
3 | height: var(--docs-header-height);
4 | }
5 |
6 | .DocsSearch--input-wrapper {
7 | position: relative;
8 | height: 100%;
9 | }
10 |
11 | .DocsSearch--input {
12 | position: relative;
13 | color: inherit;
14 | height: 100%;
15 | width: 100%;
16 | border: 0;
17 | padding: 0 var(--text-indent) .05em;
18 | background: transparent;
19 | outline: none;
20 | z-index: 2;
21 | }
22 |
23 | @-moz-document url-prefix() {
24 | .DocsSearch--input {
25 | letter-spacing: 0;
26 | }
27 | }
28 |
29 | .DocsSearch--input::placeholder {
30 | color: rgba(var(--color-rgb), .45);
31 | opacity: 1;
32 | }
33 |
34 | [theme="dark"] .DocsSearch--input::placeholder {
35 | color: rgba(var(--color-rgb), .55);
36 | }
37 |
38 | .DocsSearch--input-icon {
39 | position: absolute;
40 | top: 0;
41 | bottom: .05em;
42 | margin-top: auto;
43 | margin-bottom: auto;
44 | left: 2.2em;
45 | height: .8823529411em;
46 | width: .8823529411em;
47 | pointer-events: none;
48 | opacity: .5;
49 | transition: color .3s ease, opacity .3s ease;
50 | z-index: 3;
51 | }
52 |
53 | .DocsSearch--input-wrapper:focus-within .DocsSearch--input-icon {
54 | color: var(--orange);
55 | opacity: 1;
56 | }
57 |
58 | .DocsSearch--input-bottom-border {
59 | position: absolute;
60 | left: 0;
61 | width: 50%;
62 | bottom: 0;
63 | height: 1px;
64 | background:
65 | linear-gradient(to right,
66 | rgba(var(--color-rgb), .07) 0%,
67 | rgba(var(--color-rgb), .07) calc(100% - 6em),
68 | rgba(var(--color-rgb), 0) 100%
69 | );
70 | }
71 |
72 | [theme="dark"] .DocsSearch--input {
73 | background:
74 | linear-gradient(to right,
75 | rgba(var(--shadow-color-rgb), .1) 0%,
76 | rgba(var(--shadow-color-rgb), .1) calc(30% - 6em),
77 | rgba(var(--shadow-color-rgb), 0) 30%
78 | );
79 | }
80 |
81 | [theme="dark"] .DocsSearch--input-bottom-border {
82 | bottom: -1px;
83 | }
84 |
85 | [theme="dark"] .DocsSearch--input-bottom-border::before {
86 | content: "";
87 | display: block;
88 | position: absolute;
89 | top: -1px;
90 | width: 100%;
91 | bottom: 0;
92 | height: 1px;
93 | background:
94 | linear-gradient(to right,
95 | rgba(var(--shadow-color-rgb), .5) 0%,
96 | rgba(var(--shadow-color-rgb), .5) calc(100% - 6em),
97 | rgba(var(--shadow-color-rgb), 0) 100%
98 | );
99 | }
100 |
101 | /* Algolia DocSearch (https://docsearch.algolia.com) */
102 |
103 | .DocsSearch .algolia-autocomplete {
104 | --dropdown-text-indent: var(--text-indent);
105 | --suggestion-item-vertical-padding: .5em;
106 |
107 | /* Combat Algolia-set inline styles */
108 | display: block !important;
109 | top: 0 !important;
110 | left: 0 !important;
111 | right: auto !important;
112 | width: var(--docs-body-width) !important;
113 | z-index: 1 !important;
114 |
115 | background: var(--background-color);
116 |
117 | --box-shadow:
118 | 0 3rem 6rem -1.2rem rgba(var(--shadow-color-rgb), .25),
119 | 0 3rem 6rem -3rem rgba(var(--shadow-color-rgb), .3);
120 |
121 | --backdrop-box-shadow-alpha: .07;
122 | --backdrop-box-shadow:
123 | 0 0 0 999em rgba(var(--shadow-color-rgb), var(--backdrop-box-shadow-alpha));
124 |
125 | box-shadow: var(--box-shadow), var(--backdrop-box-shadow);
126 |
127 | border-radius: 0 0 .5em .5em;
128 | will-change: opacity;
129 | transition: opacity .15s ease;
130 | }
131 |
132 | [theme="dark"] .DocsSearch .algolia-autocomplete {
133 | --dark-theme-shadow-offset: 1px;
134 | --dropdown-text-indent: calc(var(--text-indent) - var(--dark-theme-shadow-offset));
135 | --box-shadow: 0 0 0 1px rgb(var(--shadow-color-rgb), .3), 0 .3em 2em rgb(var(--shadow-color-rgb), .3);
136 | --backdrop-box-shadow-alpha: .3;
137 | background: var(--gray-0);
138 | margin-left: var(--dark-theme-shadow-offset);
139 | }
140 |
141 | .DocsSearch .algolia-autocomplete[style*="display: none"],
142 | .DocsSearch .algolia-autocomplete:not([data-expanded="true"]) {
143 | opacity: 0;
144 | --backdrop-box-shadow: none;
145 | pointer-events: none;
146 | }
147 |
148 | @supports ((-webkit-backdrop-filter: blur(1em)) or (backdrop-filter: blur(1em))) {
149 | .DocsSearch .algolia-autocomplete {
150 | background: rgba(var(--background-color-rgb), .8);
151 | -webkit-backdrop-filter: saturate(200%) blur(1.25em);
152 | backdrop-filter: saturate(200%) blur(1.25em);
153 | }
154 |
155 | [theme="dark"] .DocsSearch .algolia-autocomplete {
156 | background: rgba(var(--gray-0-rgb), .8);
157 | }
158 |
159 | /* Safari only hack */
160 | @media not all and (min-resolution:.001dpcm) { @media {
161 | .DocsSearch .algolia-autocomplete {
162 | background: rgba(var(--background-color-rgb), 1);
163 | }
164 | }}
165 | }
166 |
167 | .DocsSearch .algolia-autocomplete [class|="ds-dataset"] > *:first-child {
168 | position: relative;
169 | --padding-top: calc(var(--docs-header-height) + .66rem);
170 | padding: var(--padding-top) var(--dropdown-text-indent) 0;
171 | }
172 |
173 | .DocsSearch .algolia-autocomplete [class|="ds-dataset"] > *:first-child::before {
174 | content: "";
175 | position: absolute;
176 | left: 0;
177 | right: 0;
178 | top: var(--docs-header-height);
179 | margin-top: -1px;
180 | height: 1px;
181 | background: rgba(var(--color-rgb), .07);
182 | }
183 |
184 | [theme="dark"] .DocsSearch .algolia-autocomplete [class|="ds-dataset"] > *:first-child::before {
185 | margin-top: 0;
186 | background: rgba(var(--color-rgb), .07);
187 | box-shadow:
188 | 0 -1px rgba(var(--shadow-color-rgb), .2),
189 | 0 calc(-1 * var(--docs-header-height)) rgba(var(--shadow-color-rgb), .1);
190 | }
191 |
192 | .DocsSearch .ds-suggestion {
193 | display: block;
194 | --horizontal-padding: 1em;
195 | padding: var(--suggestion-item-vertical-padding) var(--horizontal-padding);
196 | margin: 0 calc(-1 * var(--horizontal-padding));
197 | border-radius: .25em;
198 | }
199 |
200 | .DocsSearch .ds-suggestion.ds-cursor {
201 | background: rgba(var(--gray-5-rgb), .2);
202 | }
203 |
204 | [theme="dark"] .DocsSearch .ds-suggestion.ds-cursor {
205 | background: rgba(var(--gray-6-rgb), .15);
206 | }
207 |
208 | .DocsSearch a.algolia-docsearch-suggestion {
209 | display: block;
210 | color: inherit;
211 | text-decoration: none;
212 | }
213 |
214 | .DocsSearch .algolia-docsearch-suggestion--text {
215 | font-size: .9em;
216 | color: rgba(var(--color-rgb), .8);
217 | }
218 |
219 | .DocsSearch .algolia-docsearch-suggestion--highlight {
220 | --highlight-opacity: .2;
221 | background-color: rgba(var(--orange-rgb), var(--highlight-opacity));
222 | color: var(--color);
223 | }
224 |
225 | [theme="dark"] .DocsSearch .algolia-docsearch-suggestion--highlight {
226 | --highlight-opacity: .4;
227 | }
228 |
229 | .DocsSearch .algolia-docsearch-suggestion--no-results {
230 | padding-top: var(--suggestion-item-vertical-padding);
231 | margin-bottom: 1em;
232 | }
233 |
234 | .DocsSearch .algolia-docsearch-suggestion--no-results .algolia-docsearch-suggestion--text {
235 | padding-top: var(--suggestion-item-vertical-padding);
236 | font-size: 1em;
237 | }
238 |
239 | .DocsSearch .algolia-docsearch-suggestion--no-results b {
240 | font-weight: inherit;
241 | }
242 |
243 | .DocsSearch .algolia-docsearch-footer {
244 | margin-top: .5rem;
245 | padding: 0 var(--dropdown-text-indent) 2rem;
246 | font-size: .7em;
247 | opacity: .5;
248 | }
249 |
250 | .DocsSearch .algolia-docsearch-footer--logo {
251 | color: inherit;
252 | }
253 |
254 | .DocsSearch .algolia-docsearch-suggestion--category-header-lvl0:empty::before {
255 | content: "Document";
256 | }
257 |
258 | .DocsSearch .algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--subcategory-column,
259 | .DocsSearch .algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--subcategory-inline,
260 | .DocsSearch .algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--title {
261 | display: none;
262 | }
263 |
264 | .DocsSearch .algolia-docsearch-suggestion:not(.algolia-docsearch-suggestion__main) .algolia-docsearch-suggestion--subcategory-column,
265 | .DocsSearch .algolia-docsearch-suggestion:not(.algolia-docsearch-suggestion__main) .algolia-docsearch-suggestion--subcategory-inline {
266 | display: none;
267 | }
268 |
269 | .DocsSearch .algolia-docsearch-suggestion:not(.algolia-docsearch-suggestion__main) .algolia-docsearch-suggestion--category-header,
270 | .DocsSearch .algolia-docsearch-suggestion:not(.algolia-docsearch-suggestion__main) .algolia-docsearch-suggestion--wrapper,
271 | .DocsSearch .algolia-docsearch-suggestion:not(.algolia-docsearch-suggestion__main) .algolia-docsearch-suggestion--content,
272 | .DocsSearch .algolia-docsearch-suggestion:not(.algolia-docsearch-suggestion__main) .algolia-docsearch-suggestion--title {
273 | display: inline;
274 | }
275 |
276 | .DocsSearch .algolia-docsearch-suggestion:not(.algolia-docsearch-suggestion__main) .algolia-docsearch-suggestion--content:not(.algolia-docsearch-suggestion--no-results)::before {
277 | content: "›";
278 | opacity: .15;
279 | padding: 0 .333em;
280 | display: inline-block;
281 | transform: scale3d(1.5, 1.2, 1);
282 | }
283 |
284 | .DocsSearch .algolia-docsearch-suggestion:not(.algolia-docsearch-suggestion__main) .algolia-docsearch-suggestion--content:not(.algolia-docsearch-suggestion--no-results) .algolia-docsearch-suggestion--title::before {
285 | content: "#";
286 | padding-right: .25em;
287 | opacity: .5;
288 | }
289 |
290 | @media (max-width: 768px) {
291 | .DocsSearch {
292 | pointer-events: all;
293 | }
294 |
295 | .DocsSearch--input:not(:focus) {
296 | padding: 0;
297 | opacity: 0;
298 | }
299 |
300 | .DocsSearch[is-focused] {
301 | position: fixed;
302 | top: 0;
303 | left: 0;
304 | right: 0;
305 | --text-indent: 3rem;
306 | z-index: 2;
307 | }
308 |
309 | .DocsSearch--input-icon {
310 | left: 15px;
311 | height: 15px;
312 | width: 15px;
313 | }
314 |
315 | .DocsSearch--input-wrapper:focus-within .DocsSearch--input-icon {
316 | left: 19px;
317 | }
318 |
319 | .DocsSearch[is-focused] .DocsSearch--input-wrapper {
320 | background: var(--background-color);
321 | height: var(--docs-header-height);
322 | }
323 |
324 | .DocsSearch--input-bottom-border {
325 | display: none;
326 | }
327 |
328 | .DocsSearch .algolia-autocomplete {
329 | position: fixed;
330 | top: var(--docs-header-height);
331 | width: 100% !important;
332 | }
333 |
334 | @supports ((-webkit-backdrop-filter: blur(1em)) or (backdrop-filter: blur(1em))) {
335 | .DocsSearch .algolia-autocomplete {
336 | background: var(--background-color);
337 | -webkit-backdrop-filter: none;
338 | backdrop-filter: none;
339 | }
340 |
341 | [theme="dark"] .DocsSearch .algolia-autocomplete {
342 | background: var(--background-color);
343 | }
344 | }
345 | }
346 |
--------------------------------------------------------------------------------
/src/css/docs/components/docs-sidebar.css:
--------------------------------------------------------------------------------
1 | .DocsSidebar {
2 | position: fixed;
3 | top: 0;
4 | bottom: 0;
5 | --sidebar-background-color: var(--background-color);
6 | --content-indent: var(--docs-page-side-padding);
7 | --logo-size: 2.8235294118rem; /* 48 / 17 */
8 | --text-indent: calc(var(--content-indent) + var(--logo-size) + var(--docs-logo-lockup-gap) + .1em);
9 | width: var(--docs-sidebar-width);
10 | z-index: 2;
11 | }
12 |
13 | /* TODO: remove once Chromium bug is resolved:
14 |
15 | Reference:
16 | https://github.com/cloudflare/cloudflare-docs-engine/issues/137
17 | https://bugs.chromium.org/p/chromium/issues/detail?id=997607
18 | */
19 | .DocsSidebar [is-visually-hidden] {
20 | clip-path: none;
21 | left: -10000px;
22 | top: auto;
23 | }
24 |
25 | [theme="dark"] .DocsSidebar {
26 | --sidebar-background-color: var(--gray-0F);
27 | }
28 |
29 | [theme="dark"] .DocsSidebar::before {
30 | content: "";
31 | position: absolute;
32 | top: 0;
33 | right: 0;
34 | bottom: 0;
35 | width: 999em;
36 | background: var(--sidebar-background-color);
37 | pointer-events: none;
38 | }
39 |
40 | .DocsSidebar--link {
41 | color: inherit;
42 | text-decoration: none;
43 | }
44 |
45 | [js-focus-visible-polyfill-available] .DocsSidebar--link:focus {
46 | outline: none;
47 | }
48 |
49 | .DocsSidebar--link[is-focus-visible]:focus {
50 | box-shadow: 0 0 0 var(--focus-size) var(--focus-color);
51 | }
52 |
53 | .DocsSidebar--link:not([is-focus-visible]) {
54 | --focus-size: 0;
55 | }
56 |
57 | .DocsSidebar--sections {
58 | height: 100%;
59 | display: flex;
60 | flex-direction: column;
61 | }
62 |
63 | .DocsSidebar--shadow {
64 | pointer-events: none;
65 | position: absolute;
66 | top: 0;
67 | right: 0;
68 | bottom: 0;
69 | z-index: 6;
70 | }
71 |
72 | .DocsSidebar--shadow::before,
73 | .DocsSidebar--shadow::after {
74 | content: "";
75 | position: absolute;
76 | top: 0;
77 | right: 0;
78 | bottom: 0;
79 | background:
80 | linear-gradient(to right,
81 | rgba(var(--shadow-color-rgb), 0),
82 | rgba(var(--shadow-color-rgb), .07)
83 | );
84 | }
85 |
86 | .DocsSidebar--shadow::before {
87 | width: 5px;
88 | opacity: .8;
89 | }
90 |
91 | .DocsSidebar--shadow::after {
92 | width: 2px;
93 | opacity: .5;
94 | }
95 |
96 | @media (min-width: 769px) {
97 | [theme="dark"] .DocsSidebar--shadow {
98 | left: calc(100% + 2px);
99 | right: auto;
100 | }
101 |
102 | [theme="dark"] .DocsSidebar--shadow::before {
103 | background: rgb(var(--shadow-color-rgb));
104 | border-right: 1px solid rgba(var(--color-rgb), .15);
105 | }
106 |
107 | [theme="dark"] .DocsSidebar--shadow::before {
108 | width: 1px;
109 | opacity: 1;
110 | }
111 |
112 | [theme="dark"] .DocsSidebar--shadow::after {
113 | opacity: 1;
114 | width: 14px;
115 | right: 1px;
116 | }
117 | }
118 |
119 | .DocsSidebar--section {
120 | position: relative;
121 | }
122 |
123 | .DocsSidebar--section-separator {
124 | position: relative;
125 | flex-shrink: 0;
126 | --fade-indent: 1.25em;
127 | --shadow-opacity: .07;
128 | margin-top: -1px;
129 | }
130 |
131 | [theme="dark"] .DocsSidebar--section-separator {
132 | --shadow-opacity: 1;
133 | }
134 |
135 | .DocsSidebar--section-separator::before,
136 | .DocsSidebar--section-separator::after {
137 | content: "";
138 | position: absolute;
139 | left: 0;
140 | right: 0;
141 | height: 1px;
142 | }
143 |
144 | .DocsSidebar--section-separator::before {
145 | background:
146 | linear-gradient(to right,
147 | rgba(var(--shadow-color-rgb), 0) 0,
148 | rgba(var(--shadow-color-rgb), var(--shadow-opacity)) var(--fade-indent),
149 | rgba(var(--shadow-color-rgb), var(--shadow-opacity)) 100%
150 | );
151 | }
152 |
153 | [theme="light"] .DocsSidebar--section-separator::after {
154 | display: none;
155 | }
156 |
157 | [theme="dark"] .DocsSidebar--section-separator::after {
158 | top: 1px;
159 | background:
160 | linear-gradient(to right,
161 | rgba(var(--color-rgb), 0) 0,
162 | rgba(var(--color-rgb), .07) var(--fade-indent),
163 | rgba(var(--color-rgb), .07) 100%
164 | );
165 | }
166 |
167 | @media (max-width: 1440px) {
168 | .DocsSidebar--section-separator::before {
169 | background: rgba(var(--shadow-color-rgb), var(--shadow-opacity));
170 | }
171 |
172 | [theme="dark"] .DocsSidebar--section-separator::after {
173 | background: rgba(var(--color-rgb), .07);
174 | }
175 | }
176 |
177 | .DocsSidebar--header-section {
178 | display: flex;
179 | flex-shrink: 0;
180 | height: var(--docs-header-height);
181 | z-index: 1;
182 | }
183 |
184 | .DocsSidebar--cloudflare-logo-link {
185 | --font-size: 1.17647em; /* 20 / 17 */
186 | display: flex;
187 | align-items: center;
188 | padding-left: var(--content-indent);
189 | padding-right: var(--content-indent);
190 | }
191 |
192 | .DocsSidebar--docs-title-section {
193 | position: relative;
194 | display: flex;
195 | z-index: 7;
196 | }
197 |
198 | .DocsSidebar--docs-title-logo-link {
199 | --font-size: 1.58824em;
200 | padding: .7em var(--content-indent);
201 | max-width: calc(100% - 3em);
202 | }
203 |
204 | .DocsSidebar--cloudflare-logo-link,
205 | .DocsSidebar--docs-title-logo-link {
206 | transition: opacity .2s ease, color .2s ease;
207 | }
208 |
209 | .DocsSidebar--cloudflare-logo-link:hover,
210 | .DocsSidebar--docs-title-logo-link:hover {
211 | opacity: .75;
212 | color: rgba(var(--color-rgb), .75);
213 | }
214 |
215 | .DocsSidebar--docs-title-text-scaler {
216 | /* --length set by React */
217 | --font-size: 1em * (1 - ((var(--length) - 10) / 20));
218 | font-size: clamp(.7em, var(--font-size), 1em);
219 | display: inherit;
220 | }
221 |
222 | .DocsSidebar--section-more {
223 | position: absolute;
224 | right: .75em;
225 | top: 0;
226 | bottom: 0;
227 | margin-top: auto;
228 | margin-bottom: auto;
229 | height: 2.5em;
230 | width: 1.75em;
231 | }
232 |
233 | .DocsSidebar--section-more-button {
234 | position: relative;
235 | height: 100%;
236 | width: 100%;
237 | transition: opacity .2s ease;
238 | }
239 |
240 | .DocsSidebar:not(:hover) .DocsSidebar--section-more:not([data-expanded="true"]) .DocsSidebar--section-more-button:not(:focus) {
241 | opacity: 0;
242 | }
243 |
244 | .DocsSidebar--section-more-button-icon {
245 | position: absolute;
246 | top: 0;
247 | right: 0;
248 | bottom: 0;
249 | left: 0;
250 | padding: .625em .4em;
251 | transition: opacity .2s ease;
252 | opacity: .5;
253 | }
254 |
255 | @media (hover: hover) {
256 | .DocsSidebar--section-more-button:hover .DocsSidebar--section-more-button-icon {
257 | opacity: 1;
258 | }
259 | }
260 |
261 | .DocsSidebar--nav-section {
262 | display: flex;
263 | flex-direction: column;
264 | flex: 1;
265 | overflow-y: auto;
266 | -webkit-overflow-scrolling: touch;
267 | scrollbar-width: thin;
268 | scrollbar-color: rgba(var(--color-rgb), .1) transparent;
269 | }
270 |
271 | @media (hover: hover) {
272 | .DocsSidebar--nav-section:hover {
273 | scrollbar-color: rgba(var(--color-rgb), .35) transparent;
274 | }
275 | }
276 |
277 | .DocsSidebar--nav-section-shadow {
278 | position: sticky;
279 | flex-shrink: 0;
280 | top: 0px;
281 | width: 100%;
282 | height: .25em;
283 | box-shadow: inset 0 1px rgba(var(--shadow-color-rgb), .04);
284 | background:
285 | linear-gradient(
286 | rgba(var(--shadow-color-rgb), .09),
287 | rgba(var(--shadow-color-rgb), 0)
288 | );
289 | opacity: 0;
290 | z-index: 4;
291 | }
292 |
293 | @media (min-width: 1441px) {
294 | .DocsSidebar--nav-section-shadow {
295 | -webkit-mask-image: linear-gradient(to right, transparent, #000 1.5em, #000);
296 | mask-image: linear-gradient(to right, transparent, #000 1.5em, #000);
297 | }
298 | }
299 |
300 | [theme="dark"] .DocsSidebar--nav-section-shadow {
301 | box-shadow: inset 0 1px rgba(var(--shadow-color-rgb), .5);
302 | background: linear-gradient(rgba(var(--shadow-color-rgb), .2), transparent);
303 | height: .5em;
304 | border-top: 1px solid #222;
305 | }
306 |
307 | .DocsSidebar--nav {
308 | list-style: none;
309 | font-size: 1.11em;
310 | padding-top: 0;
311 | padding-bottom: 2em; /* Prevent overlap with browser status bar */
312 | padding-left: 0;
313 | word-break: break-word;
314 | }
315 |
316 | @media (max-width: 1280px) {
317 | .DocsSidebar--nav {
318 | font-size: 1.05em;
319 | --text-indent: 3rem;
320 | }
321 | }
322 |
323 | .DocsSidebar--nav-item {
324 | position: relative;
325 | }
326 |
327 | .DocsSidebar--nav-item + .DocsSidebar--nav-item {
328 | margin-top: .333em;
329 | }
330 |
331 | .DocsSidebar--nav-expand-collapse-button {
332 | position: absolute;
333 | z-index: 3;
334 | --size: 2em; /* TODO - use calc to derive from effective nav item height */
335 | --margin-right: .75em;
336 | left: calc(var(--text-indent) - var(--size) - var(--margin-right) + (var(--depth, 0) * 1rem));
337 | top: 0;
338 | height: var(--size);
339 | width: var(--size);
340 | line-height: .5em;
341 | padding: 0;
342 | text-align: center;
343 | }
344 |
345 | @media (max-width: 1280px) {
346 | .DocsSidebar--nav-expand-collapse-button {
347 | --margin-right: .25em;
348 | }
349 | }
350 |
351 | @media (hover: hover) {
352 | .DocsSidebar--nav-expand-collapse-button:hover,
353 | [theme="dark"] .DocsSidebar--nav-expand-collapse-button:hover {
354 | --hover-box-shadow-color: transparent;
355 | }
356 |
357 | .DocsSidebar--nav-expand-collapse-button:hover {
358 | background: rgba(var(--gray-5-rgb), .2);
359 | }
360 |
361 | [theme="dark"] .DocsSidebar--nav-expand-collapse-button:hover {
362 | color: var(--code-orange);
363 | background: rgba(var(--orange-rgb), .08);
364 | }
365 | }
366 |
367 | .DocsSidebar--nav-expand-collapse-button::before {
368 | content: "";
369 | position: absolute;
370 | right: 0;
371 | top: 0;
372 | bottom: 0;
373 | width: 5em;
374 | z-index: -1;
375 | }
376 |
377 | .DocsSidebar--nav-expand-collapse-button-content {
378 | display: inline-block;
379 | vertical-align: middle;
380 | transition: transform .4s ease;
381 | border: solid transparent;
382 | border-width: .3333em .6em;
383 | border-left-color: currentColor;
384 | margin-left: 37%;
385 | will-change: transform;
386 | transform: translate3d(0, 0, 0);
387 | transform-origin: .2em .4em;
388 | }
389 |
390 | [theme="dark"] .DocsSidebar--nav-item[is-active] > .DocsSidebar--nav-expand-collapse-button > .DocsSidebar--nav-expand-collapse-button-content,
391 | .DocsSidebar--nav-item[is-active-root]:not([is-expanded]) > .DocsSidebar--nav-expand-collapse-button > .DocsSidebar--nav-expand-collapse-button-content {
392 | color: var(--orange);
393 | }
394 |
395 | @media (hover: hover) {
396 | .DocsSidebar--nav-expand-collapse-button:not(:hover) > .DocsSidebar--nav-expand-collapse-button-content {
397 | opacity: .4;
398 | }
399 |
400 | [theme="dark"] .DocsSidebar--nav-item[is-active] > .DocsSidebar--nav-expand-collapse-button > .DocsSidebar--nav-expand-collapse-button-content {
401 | opacity: .8;
402 | }
403 |
404 | .DocsSidebar--nav-item[is-active-root]:not([is-expanded]) > .DocsSidebar--nav-expand-collapse-button > .DocsSidebar--nav-expand-collapse-button-content {
405 | opacity: 1;
406 | }
407 | }
408 |
409 | .DocsSidebar--nav-item[is-expanded] > .DocsSidebar--nav-expand-collapse-button > .DocsSidebar--nav-expand-collapse-button-content {
410 | transform: rotate(90deg) translate3d(-.1em, 0, 0);
411 | }
412 |
413 | .DocsSidebar--nav-item-collapse-container {
414 | overflow: hidden;
415 | }
416 |
417 | .DocsSidebar--nav-item-collapse-container.DocsSidebar--nav-item-collapse-hidden {
418 | height: 0;
419 | }
420 |
421 | .DocsSidebar--nav-item-collapse-content {
422 | opacity: 0;
423 | transform: translate3d(-.5em, 0, 0);
424 | will-change: transform, opacity;
425 | transition: transform .4s ease, opacity .4s ease;
426 | }
427 |
428 | /* Use > selectors as not to trigger subnavs */
429 | .DocsSidebar--nav-item[is-expanded] >
430 | .DocsSidebar--nav-item-collapse-container >
431 | .DocsSidebar--nav-item-collapse-wrapper >
432 | .DocsSidebar--nav-item-collapse-wrapperInner >
433 | .DocsSidebar--nav-item-collapse-content,
434 |
435 | /* Handle prefers-reduced-motion case */
436 | .DocsSidebar--nav-item[is-expanded] >
437 | .DocsSidebar--nav-item-collapse-container >
438 | .DocsSidebar--nav-item-collapse-content {
439 | opacity: 1;
440 | transform: translate3d(0, 0, 0);
441 | }
442 |
443 | @media (prefers-reduced-motion: reduce) {
444 | .DocsSidebar--nav-expand-collapse-button-content,
445 | .DocsSidebar--nav-item-collapse-content {
446 | transition: none;
447 | }
448 | }
449 |
450 | .DocsSidebar--nav-subnav {
451 | list-style: none;
452 | font-size: .94rem;
453 | padding: .25em 0 .75em;
454 | }
455 |
456 | @media (max-width: 1280px) {
457 | .DocsSidebar--nav-subnav {
458 | font-size: .9em;
459 | }
460 | }
461 |
462 | .DocsSidebar--nav-subnav .DocsSidebar--nav-item + .DocsSidebar--nav-item {
463 | margin-top: .0625em;
464 | }
465 |
466 | .DocsSidebar--nav-link {
467 | color: inherit;
468 | text-decoration: none;
469 | position: relative;
470 | display: block;
471 | padding-left: var(--text-indent);
472 | }
473 |
474 | .DocsSidebar--nav-subnav .DocsSidebar--nav-link {
475 | padding-left: calc(var(--text-indent) + (var(--depth) * 1rem));
476 | }
477 |
478 | .DocsSidebar--nav-link:focus {
479 | z-index: 1;
480 | }
481 |
482 | .DocsSidebar--nav-link[is-active] {
483 | z-index: 2;
484 | }
485 |
486 | .DocsSidebar--nav-link[is-active]::before {
487 | content: "";
488 | position: absolute;
489 | top: 0;
490 | bottom: 0;
491 | left: 0;
492 | width: .4125em;
493 | background: var(--orange);
494 | z-index: 4;
495 | }
496 |
497 | .DocsSidebar--nav-link-text {
498 | position: relative;
499 | display: inline-block;
500 | padding: .25em .6em;
501 | margin-left: -.6em;
502 | z-index: 1;
503 | }
504 |
505 | @media (hover: hover) {
506 | .DocsSidebar--nav-link:hover .DocsSidebar--nav-link-text::before {
507 | content: "";
508 | position: absolute;
509 | top: 0;
510 | right: 0;
511 | bottom: 0;
512 | left: 0;
513 | pointer-events: none;
514 | height: 100%;
515 | background: rgba(var(--gray-5-rgb), .2);
516 | z-index: -1;
517 | border-radius: .25em;
518 | }
519 | }
520 |
521 | .DocsSidebar--nav-link[is-active] .DocsSidebar--nav-link-text::before {
522 | content: "";
523 | position: absolute;
524 | top: 0;
525 | right: 0;
526 | bottom: 0;
527 | left: 0;
528 | pointer-events: none;
529 | height: 100%;
530 | background: rgba(var(--gray-5-rgb), .2);
531 | z-index: -1;
532 | border-radius: .25em;
533 | }
534 |
535 | @media (max-width: 1440px) {
536 | .DocsSidebar--nav-link[is-active] .DocsSidebar--nav-link-text::before {
537 | left: calc(-1 * var(--docs-sidebar-width));
538 | border-top-left-radius: 0;
539 | border-bottom-left-radius: 0;
540 | }
541 | }
542 |
543 | @media (min-width: 1441px) {
544 | .DocsSidebar--nav-link[is-active]::before {
545 | display: none;
546 | }
547 | }
548 |
549 | @media (hover: hover) {
550 | [theme="dark"] .DocsSidebar--nav-link:hover .DocsSidebar--nav-link-text {
551 | color: var(--code-orange);
552 | }
553 |
554 | [theme="dark"] .DocsSidebar--nav-link:hover .DocsSidebar--nav-link-text::before {
555 | background: rgba(var(--orange-rgb), .08);
556 | }
557 | }
558 |
559 | [theme="dark"] .DocsSidebar--nav-link[is-active] .DocsSidebar--nav-link-text {
560 | color: var(--code-orange);
561 | }
562 |
563 | [theme="dark"] .DocsSidebar--nav-link[is-active] .DocsSidebar--nav-link-text::before {
564 | background: rgba(var(--orange-rgb), .08);
565 | }
566 |
567 | .DocsSidebar--nav-link .DocsSidebar--nav-link-text::after {
568 | content: "";
569 | position: absolute;
570 | top: 0;
571 | right: 0;
572 | bottom: 0;
573 | left: 0;
574 | pointer-events: none;
575 | border-radius: .25em;
576 | }
577 |
578 | [js-focus-visible-polyfill-available] .DocsSidebar--nav-link:focus {
579 | outline: none;
580 | }
581 |
582 | .DocsSidebar--nav-link[is-focus-visible]:focus .DocsSidebar--nav-link-text::after {
583 | box-shadow: 0 0 0 var(--focus-size) var(--focus-color);
584 | }
585 |
586 | @media (max-width: 768px) {
587 | .DocsSidebar {
588 | z-index: 20;
589 | background: var(--sidebar-background-color);
590 | width: calc(100% - 3.5rem);
591 | transform: translate3d(-100%, 0, 0);
592 | will-change: transform;
593 | transition: transform .3s ease;
594 | }
595 |
596 | [theme="dark"] .DocsSidebar {
597 | --sidebar-background-color: var(--background-color);
598 | }
599 |
600 | .DocsSidebar--shadow {
601 | opacity: 0;
602 | transition: opacity .3s ease;
603 | }
604 |
605 | .DocsSidebar--shadow,
606 | .DocsSidebar--shadow::before,
607 | .DocsSidebar--shadow::after {
608 | right: auto;
609 | left: 100%;
610 | }
611 |
612 | .DocsSidebar--shadow::before,
613 | .DocsSidebar--shadow::after {
614 | background:
615 | linear-gradient(to left,
616 | rgba(var(--shadow-color-rgb), 0),
617 | rgba(var(--shadow-color-rgb), .2)
618 | );
619 | }
620 |
621 | .DocsSidebar--shadow::before {
622 | width: .3125em;
623 | }
624 |
625 | [is-resizing] .DocsSidebar {
626 | transition: none;
627 | }
628 |
629 | [is-mobile-sidebar-open] .DocsSidebar {
630 | transform: translate3d(0, 0, 0);
631 | }
632 |
633 | [is-mobile-sidebar-open] .DocsSidebar--shadow {
634 | opacity: 1;
635 | }
636 |
637 | .DocsSidebar--header-section,
638 | .DocsSidebar--header-section + .DocsSidebar--section-separator,
639 | .DocsSidebar--docs-title-section,
640 | .DocsSidebar--nav-section-shadow {
641 | display: none;
642 | }
643 |
644 | .DocsSidebar--nav {
645 | padding: 1rem 0;
646 | }
647 | }
648 |
--------------------------------------------------------------------------------
/src/css/docs/components/docs-table-of-contents.css:
--------------------------------------------------------------------------------
1 | .DocsTableOfContents {
2 | list-style: none;
3 | font-size: .94117647059em; /* 16 / 17 */
4 | padding-left: 0;
5 | width: 100%;
6 | }
7 |
8 | .DocsTableOfContents ul {
9 | padding-left: 1.5em;
10 | list-style: none;
11 | }
12 |
13 | .DocsTableOfContents ul ul {
14 | font-size: .9em;
15 | padding-left: 1.5em;
16 | margin-bottom: .25em;
17 | }
18 |
19 | @media (max-height: 768px) {
20 | .DocsTableOfContents ul ul {
21 | display: none;
22 | }
23 | }
24 |
25 | .DocsTableOfContents ul ul ul {
26 | display: none;
27 | }
28 |
29 | .DocsTableOfContents-link {
30 | display: inline-block;
31 | line-height: 1.3;
32 | max-width: 100%;
33 | white-space: nowrap;
34 | text-overflow: ellipsis;
35 | overflow: hidden;
36 | text-decoration: none;
37 | color: inherit;
38 | text-decoration: none;
39 | padding: .2em 0;
40 | margin: -.2em 0;
41 | }
42 |
43 | .DocsTableOfContents-link:hover {
44 | color: var(--orange);
45 | }
46 |
47 | [js-focus-visible-polyfill-available] .DocsTableOfContents-link:focus {
48 | outline: none;
49 | }
50 |
51 | .DocsTableOfContents-link[is-focus-visible]:focus {
52 | box-shadow: 0 0 0 var(--focus-size) var(--focus-color);
53 | }
54 |
55 | .DocsTableOfContents-link:not([is-focus-visible]) {
56 | --focus-size: 0;
57 | }
58 |
--------------------------------------------------------------------------------
/src/css/docs/components/docs-toolbar.css:
--------------------------------------------------------------------------------
1 | .DocsToolbar {
2 | position: absolute;
3 | left: var(--docs-sidebar-width);
4 | top: 0;
5 | right: 0;
6 | display: flex;
7 | height: var(--docs-header-height);
8 | z-index: 2;
9 | }
10 |
11 | .DocsToolbar--search {
12 | flex: 1;
13 | max-width: var(--docs-body-width);
14 | margin-right: auto;
15 | }
16 |
17 | [search-disabled] .DocsToolbar--search {
18 | visibility: hidden;
19 | }
20 |
21 | .DocsToolbar--tools {
22 | display: flex;
23 | align-items: center;
24 | justify-content: flex-end;
25 | padding: 0 var(--docs-page-side-padding);
26 | }
27 |
28 | .DocsToolbar--tools-spacer {
29 | display: inline-flex;
30 | height: 1em;
31 | width: 1em;
32 | }
33 |
34 | .DocsToolbar--tools-icon-item {
35 | display: inline-flex;
36 | height: 1.647058823rem;
37 | width: 1.647058823rem;
38 | color: rgba(var(--color-rgb), .7);
39 | }
40 |
41 | [theme="light"] .DocsToolbar--tools-icon-item {
42 | color: var(--gray-3);
43 | }
44 |
45 | .DocsToolbar--tools-icon-item-content > a {
46 | display: block;
47 | height: 100%;
48 | width: 100%;
49 | padding: .1em;
50 | }
51 |
52 | @media (max-width: 768px) {
53 | .DocsToolbar {
54 | left: 0;
55 | z-index: 12;
56 | pointer-events: none;
57 | }
58 |
59 | .DocsToolbar--tools {
60 | pointer-events: all;
61 | padding-left: 0;
62 | padding-right: .9rem;
63 | }
64 |
65 | .DocsToolbar--search {
66 | flex-basis: 3em;
67 | flex-grow: 0;
68 | margin-left: auto;
69 | margin-right: 0;
70 | max-width: 100vw;
71 | }
72 |
73 | .DocsToolbar--tools-spacer,
74 | .DocsToolbar--tools-icon-item {
75 | display: none;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/css/docs/components/docs-tutorials.css:
--------------------------------------------------------------------------------
1 | .DocsTutorials {
2 | margin: 2em 0;
3 | width: 53em;
4 | max-width: 100%;
5 | }
6 |
7 | [theme="dark"] .DocsTutorials {
8 | color: var(--gray-7);
9 | }
10 |
11 | .DocsTutorials--row {
12 | display: flex;
13 | align-items: baseline;
14 | --gap: 1.5em;
15 | padding-bottom: .5em;
16 | border-bottom: 1px solid rgba(var(--color-rgb), .1);
17 | margin-bottom: .5em;
18 | }
19 |
20 | .DocsTutorials--body .DocsTutorials--row:last-child {
21 | margin-bottom: 0;
22 | border-bottom: 0;
23 | }
24 |
25 | .DocsTutorials--header .DocsTutorials--column-text {
26 | font-size: .9em;
27 | font-weight: bold;
28 | }
29 |
30 | .DocsTutorials--header .DocsTutorials--row {
31 | border-bottom-width: .125em;
32 | padding-bottom: .1875em;
33 | }
34 |
35 | .DocsTutorials--body .DocsTutorials--column:not([data-column="name"]) {
36 | position: relative;
37 | top: -.1em;
38 | }
39 |
40 | .DocsTutorials--body .DocsTutorials--column[data-column="length"] {
41 | top: -.2em;
42 | }
43 |
44 | .DocsTutorials--column[data-column="name"] {
45 | flex: 1;
46 | padding-right: calc(var(--gap) / 2);
47 | }
48 |
49 | .DocsTutorials--link {
50 | position: relative;
51 | font-size: 1.2em;
52 | color: inherit;
53 | --underline-opacity: .75;
54 | text-decoration-color: rgba(var(--orange-rgb), var(--underline-opacity));
55 | box-shadow: 0 0 0 var(--focus-size) var(--focus-color);
56 | }
57 |
58 | [theme="dark"] .DocsTutorials--link {
59 | --underline-opacity: .5;
60 | }
61 |
62 | .DocsTutorials--row-is-new .DocsTutorials--link::after {
63 | content: "";
64 | position: absolute;
65 | top: .45em;
66 | right: 100%;
67 | margin-right: .625em;
68 | height: 8px;
69 | width: 8px;
70 | border-radius: 999em;
71 | background: var(--orange);
72 | }
73 |
74 | @media (max-width: 1280px) {
75 | .DocsTutorials--link {
76 | text-decoration: none;
77 | box-shadow: inset 0 -2px rgba(var(--orange-rgb), var(--underline-opacity));
78 | }
79 |
80 | .DocsTutorials--row-is-new .DocsTutorials--link::after {
81 | top: .375em;
82 | }
83 | }
84 |
85 | @media (max-width: 768px) {
86 | .DocsTutorials--link {
87 | font-size: 1.1em;
88 | }
89 |
90 | .DocsTutorials--row-is-new .DocsTutorials--link::after {
91 | display: none;
92 | }
93 | }
94 |
95 | @media (max-width: 414px) {
96 | .DocsTutorials--link {
97 | font-size: 1em;
98 | }
99 |
100 | .DocsTutorials--ago-text {
101 | display: none;
102 | }
103 | }
104 |
105 | [js-focus-visible-polyfill-available] .DocsTutorials--link:focus {
106 | outline: none;
107 | }
108 |
109 | .DocsTutorials--link[is-focus-visible] {
110 | --underline-size: 0;
111 | }
112 |
113 | .DocsTutorials--link:not([is-focus-visible]) {
114 | --focus-size: 0;
115 | }
116 |
117 | .DocsTutorials--column[data-column="updated"] {
118 | padding-left: calc(var(--gap) / 2);
119 | padding-right: calc(var(--gap) / 2);
120 | }
121 |
122 | .DocsTutorials--column[data-column="difficulty"] {
123 | width: 7.5em;
124 | padding-left: calc(var(--gap) / 2);
125 | padding-right: calc(var(--gap) / 2);
126 | flex-shrink: 0;
127 | }
128 |
129 | .DocsTutorials--column[data-column="length"] {
130 | width: 4.5em;
131 | padding-left: calc(var(--gap) / 2);
132 | flex-shrink: 0;
133 | }
134 |
135 | .DocsTutorials--length-bar {
136 | width: 100%;
137 | height: .5em;
138 | border-radius: 1em;
139 | background: rgba(var(--color-rgb), .1);
140 | overflow: hidden;
141 | }
142 |
143 | .DocsTutorials--length-bar-inner {
144 | background: rgba(var(--color-rgb), .5);
145 | border-radius: 1em;
146 | min-width: .6em;
147 | height: 100%;
148 | }
149 |
150 | @media (max-width: 1024px) {
151 | .DocsTutorials--column[data-column="updated"] {
152 | padding-right: 0;
153 | }
154 |
155 | .DocsTutorials--column[data-column="difficulty"],
156 | .DocsTutorials--column[data-column="length"] {
157 | display: none;
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/css/docs/components/skip-nav-link.css:
--------------------------------------------------------------------------------
1 | .SkipNavLink {
2 | position: absolute;
3 | overflow: hidden;
4 | color: inherit;
5 | text-decoration: none;
6 | clip: rect(0 0 0 0);
7 | height: 1px;
8 | width: 1px;
9 | margin: -1px;
10 | padding: 0;
11 | }
12 |
13 | .SkipNavLink:focus {
14 | padding: .5em 1em;
15 | position: fixed;
16 | top: 0;
17 | left: 50%;
18 | transform: translate3d(-50%, 0, 0);
19 | background: var(--background-color);
20 | width: auto;
21 | height: auto;
22 | clip: auto;
23 | z-index: 100;
24 | outline: none;
25 | box-shadow: 0 0 0 var(--focus-size) var(--focus-color);
26 | }
27 |
--------------------------------------------------------------------------------
/src/css/docs/components/tags-filter.css:
--------------------------------------------------------------------------------
1 | .TagsFilter {
2 | display: flex;
3 | flex-wrap: wrap;
4 | margin: 0 -.25em;
5 | }
6 |
7 | .TagsFilter--button {
8 | --border-color: rgba(var(--color-rgb), .2);
9 | padding: .25em .3125em;
10 | margin: .3125em .25em;
11 | border-radius: .125em;
12 | }
13 |
14 | .TagsFilter--button[is-focus-visible] {
15 | --border-color: rgba(var(--orange-rgb), .7);
16 | }
17 |
18 | .TagsFilter--button-active {
19 | pointer-events: none;
20 | }
21 |
22 | [theme="light"] .TagsFilter--button-active {
23 | --border-color: transparent;
24 | }
25 |
26 | [theme="dark"] .TagsFilter--button-active {
27 | color: var(--code-orange);
28 | }
29 |
--------------------------------------------------------------------------------
/src/css/docs/components/worker-starter.css:
--------------------------------------------------------------------------------
1 | .WorkerStarter {
2 | display: block;
3 | margin-top: 2.5rem;
4 | }
5 |
6 | .WorkerStarter + .WorkerStarter {
7 | margin-top: 3rem;
8 | }
9 |
10 | .WorkerStarter--title {
11 | font-size: 1.35em;
12 | line-height: 1.2;
13 | font-weight: 500;
14 | margin-bottom: .3125em;
15 | }
16 |
17 | .WorkerStarter--title .Link { /* TODO */
18 | color: inherit;
19 | --underline-opacity: .75;
20 | text-decoration: underline;
21 | text-decoration-color: rgba(var(--orange-rgb), var(--underline-opacity));
22 | box-shadow: 0 0 0 var(--focus-size) var(--focus-color);
23 | }
24 |
25 | @media (max-width: 768px) {
26 | .WorkerStarter--title {
27 | font-size: 1.375em;
28 | margin-bottom: .5rem;
29 | }
30 | }
31 |
32 | .WorkerStarter--description {
33 | display: block;
34 | margin-bottom: .4375rem;
35 | width: calc(100% - var(--docs-body-sidebar-width));
36 | }
37 |
38 | .WorkerStarter--command {
39 | position: relative;
40 | font-size: .9em;
41 | --copy-button-width: 4rem;
42 | --height: 2.5rem;
43 | }
44 |
45 | .WorkerStarter--command-copy-button-wrapper {
46 | position: absolute;
47 | width: calc(var(--copy-button-width) - 2px);
48 | height: var(--height);
49 | }
50 |
51 | .WorkerStarter--command-copy-button-wrapper button {
52 | width: 100%;
53 | height: 100%;
54 | border-top-right-radius: 0;
55 | border-bottom-right-radius: 0;
56 | font-weight: 600;
57 | background: var(--code-block-background-color);
58 | }
59 |
60 | [theme="light"] .WorkerStarter--command-copy-button-wrapper button {
61 | background: var(--code-block-background-color-light-theme);
62 | }
63 |
64 | .WorkerStarter--command-codeblock-wrapper {
65 | padding-left: var(--copy-button-width);
66 | }
67 |
68 | .WorkerStarter--command .WorkerStarter--command-codeblock-wrapper pre {
69 | height: var(--height);
70 | line-height: 1.4;
71 | margin-bottom: 0;
72 | border-top-left-radius: 0;
73 | border-bottom-left-radius: 0;
74 | overflow: hidden;
75 | }
76 |
--------------------------------------------------------------------------------
/src/html.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import PropTypes from "prop-types"
3 | import googleAnalytics from "./utils/google-analytics"
4 |
5 | export default function HTML(props) {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 | { props.headComponents }
13 |
14 |
15 |
16 |
17 |
18 | { props.preBodyComponents }
19 |
20 |
21 | This app works best with JavaScript enabled.
22 |
23 |
24 |
25 |
26 | { props.postBodyComponents }
27 |
28 |
29 | )
30 | }
31 |
32 | HTML.propTypes = {
33 | htmlAttributes: PropTypes.object,
34 | headComponents: PropTypes.array,
35 | bodyAttributes: PropTypes.object,
36 | preBodyComponents: PropTypes.array,
37 | body: PropTypes.string,
38 | postBodyComponents: PropTypes.array,
39 | }
40 |
--------------------------------------------------------------------------------
/src/images/cloudflare-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Erisa/cloudflare-docs-engine/1a42d49275dc4623482ecf4899d4815b16b0499d/src/images/cloudflare-icon.png
--------------------------------------------------------------------------------
/src/pages/404.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import AnchorLink from "../components/mdx/anchor-link"
4 | import SEO from "../components/seo"
5 |
6 | import DocsTitle from "../components/docs-title"
7 |
8 | const NotFoundPage = () => (
9 | <>
10 |
11 | Not found
12 | Unfortunately, the page you requested cannot be found.
13 | Go to docs home
14 | >
15 | )
16 |
17 | export default NotFoundPage
18 |
--------------------------------------------------------------------------------
/src/utils/animate.js:
--------------------------------------------------------------------------------
1 | import anime from "animejs/lib/anime.es.js"
2 |
3 | export default options => {
4 | const obj = {
5 | objProperty: options.from
6 | }
7 |
8 | anime({
9 | targets: obj,
10 | objProperty: options.to,
11 | easing: options.easing,
12 | duration: options.duration,
13 | update: () => options.update(obj.objProperty)
14 | })
15 | }
16 |
--------------------------------------------------------------------------------
/src/utils/generate-nav-tree.js:
--------------------------------------------------------------------------------
1 | import getPageTitle from "./get-page-title"
2 | import getParentPath from "./get-parent-path"
3 | import getOrder from "./get-order"
4 |
5 | const formatNode = node => {
6 | node.href = node.path
7 | node.title = getPageTitle(node)
8 | return node
9 | }
10 |
11 | const generateNavTree = pages => {
12 | const pagesByPath = {}
13 |
14 | pages.forEach((page, i) => {
15 | const path = page.fields.slug
16 | pages[i].path = path
17 | pagesByPath[path] = page
18 | pages[i].title = getPageTitle(page)
19 |
20 | const depth = path.split('/').length - 2
21 | pages[i].depth = depth
22 | })
23 |
24 | pages.forEach((page, i) => {
25 | const parentPath = getParentPath(page.path)
26 | if (!parentPath) return
27 | const parentNode = pagesByPath[parentPath]
28 | if (!parentNode) return
29 | pages[i].parentId = pagesByPath[parentPath].id
30 | })
31 |
32 | pages.sort((a, b) => {
33 | if (a.title < b.title) return -1
34 | if (a.title > b.title) return 1
35 | return 0
36 | })
37 |
38 | pages.sort((a, b) => getOrder(a) - getOrder(b))
39 |
40 | const map = {}
41 | const tree = []
42 |
43 | for (let i = 0; i < pages.length; i += 1) {
44 | map[pages[i].id] = i
45 | }
46 |
47 | for (let i = 0; i < pages.length; i += 1) {
48 | const node = pages[i]
49 | if (node.depth > 0) {
50 | if (node.parentId) {
51 | if (!pages[map[node.parentId]].children) pages[map[node.parentId]].children = []
52 | pages[map[node.parentId]].children.push(formatNode(node))
53 | }
54 | } else {
55 | tree.push(formatNode(node))
56 | }
57 | }
58 |
59 | return tree
60 | }
61 |
62 | export default generateNavTree
63 |
--------------------------------------------------------------------------------
/src/utils/get-breadcrumbs.js:
--------------------------------------------------------------------------------
1 | import getPageTitle from "./get-page-title"
2 | import getPathPrefix from "./get-path-prefix"
3 | import getParentPath from "./get-parent-path"
4 | import getPageByPath from "./get-page-by-path"
5 | import getNormalizedPath from "./get-normalized-path"
6 |
7 | const pathPrefix = getPathPrefix()
8 |
9 | const getBreadcrumbs = (pages, location) => {
10 | let out = []
11 |
12 | const pathname =
13 | location.pathname.startsWith(pathPrefix) ?
14 | location.pathname.substr(pathPrefix.length) :
15 | location.pathname
16 |
17 | try {
18 | const page = getPageByPath(pages, pathname)
19 | let parent = getPageByPath(pages, getParentPath(page.fields.slug))
20 |
21 | while (parent) {
22 | out.unshift({
23 | title: getPageTitle(parent),
24 | url: parent.fields.slug
25 | })
26 |
27 | parent = getPageByPath(pages, getParentPath(parent.fields.slug))
28 |
29 | if (parent && getNormalizedPath(parent.fields.slug) === "") {
30 | parent = false
31 | }
32 | }
33 | } catch (error) {}
34 |
35 | return out
36 | }
37 |
38 | export default getBreadcrumbs
39 |
--------------------------------------------------------------------------------
/src/utils/get-cloudflare-docs-config.js:
--------------------------------------------------------------------------------
1 | import { useStaticQuery, graphql } from "gatsby"
2 |
3 | // This helps us get all of the cloudflareDocs metadata
4 | // at once. It’s not exactly ideal, but since we cannot
5 | // do string interpolation inside useStaticQuery, this
6 | // is about the best we can do.
7 |
8 | // See these relevant issues:
9 | // https://github.com/gatsbyjs/gatsby/issues/10482
10 | // https://github.com/gatsbyjs/gatsby/issues/9843
11 |
12 | export default () => {
13 | const { site: { siteMetadata }} = useStaticQuery(
14 | graphql`
15 | query {
16 | site {
17 | siteMetadata {
18 | cloudflareDocs {
19 | pathPrefix
20 | product
21 | productLogoPathD
22 | contentRepo
23 | contentRepoFolder
24 | externalLinks {
25 | title
26 | url
27 | }
28 | search {
29 | indexName
30 | apiKey
31 | algoliaOptions {
32 | facetFilters
33 | }
34 | }
35 | }
36 | }
37 | }
38 | }
39 | `
40 | )
41 |
42 | return siteMetadata.cloudflareDocs
43 | }
44 |
--------------------------------------------------------------------------------
/src/utils/get-normalized-path.js:
--------------------------------------------------------------------------------
1 | export default path => {
2 | if (path === "/") return path
3 |
4 | return path.replace(/\/$/, "")
5 | }
6 |
--------------------------------------------------------------------------------
/src/utils/get-order.js:
--------------------------------------------------------------------------------
1 | export default ({ frontmatter }) => {
2 | if (frontmatter && (frontmatter.order || frontmatter.order === 0)) {
3 | return frontmatter.order
4 | }
5 |
6 | // Use a large number instead of `Infinity` to be able to
7 | // use subtraction-based sort comparison method
8 | return 10e6
9 | }
10 |
--------------------------------------------------------------------------------
/src/utils/get-page-by-path.js:
--------------------------------------------------------------------------------
1 | import getNormalizedPath from "./get-normalized-path"
2 |
3 | export default (pages, path) => (
4 | pages.find(page => (
5 | getNormalizedPath(page.fields.slug) ===
6 | getNormalizedPath(path)
7 | ))
8 | )
9 |
--------------------------------------------------------------------------------
/src/utils/get-page-title.js:
--------------------------------------------------------------------------------
1 | export default ({ frontmatter, headings }, useHeading=false) => {
2 | if (!frontmatter) return "Not found"
3 |
4 | if (useHeading) {
5 | return (headings.length && headings[0].value) || frontmatter.title
6 | }
7 |
8 | return frontmatter.title || (headings.length && headings[0].value)
9 | }
10 |
--------------------------------------------------------------------------------
/src/utils/get-page-type.js:
--------------------------------------------------------------------------------
1 | export default ({ frontmatter }) => {
2 | if (!frontmatter) return "error"
3 |
4 | return frontmatter.type || "document"
5 | }
6 |
--------------------------------------------------------------------------------
/src/utils/get-parent-path.js:
--------------------------------------------------------------------------------
1 | import getNormalizedPath from "./get-normalized-path"
2 |
3 | export default path => {
4 | return getNormalizedPath(path).replace(/\/[^/]*$/, '')
5 | }
6 |
--------------------------------------------------------------------------------
/src/utils/get-path-prefix.js:
--------------------------------------------------------------------------------
1 | import { withPrefix } from "gatsby"
2 |
3 | // Gatsby’s ` ` component automatically applies the
4 | // `pathPrefix` set in gatsby-config.js. However when
5 | // constructing URLs manually, it can be useful to have
6 | // direct access to the `pathPrefix`. Gatsby offers a
7 | // method `withPrefix()` for this purpose, but this still
8 | // doesn’t give you direct access to the value. Here, we
9 | // "trick" `withPrefix` to giving us the value by passing
10 | // in "/" and then trimming the "/" off of the end. Sadly
11 | // you can’t just call `withPrefix("")` because that
12 | // somewhat surprisingly returns "".
13 | // See: https://www.gatsbyjs.com/docs/path-prefix/
14 | export default () => withPrefix("/").slice(0, -1)
15 |
--------------------------------------------------------------------------------
/src/utils/get-table-of-contents.js:
--------------------------------------------------------------------------------
1 | export default ({ tableOfContents: toc }) => {
2 | if (!toc || !toc.items || !toc.items.length)
3 | return []
4 |
5 | return toc.items[0].items
6 | }
7 |
--------------------------------------------------------------------------------
/src/utils/get-unique-readable-id.js:
--------------------------------------------------------------------------------
1 | const random = () => Math.random().toString().split('.')[1]
2 |
3 | export default prefix => `${prefix}-${random()}`
4 |
--------------------------------------------------------------------------------
/src/utils/google-analytics.js:
--------------------------------------------------------------------------------
1 | export default `const GA_ID = "UA-107218623-2"
2 | window.ga =
3 | window.ga ||
4 | function () {
5 | if (!GA_ID) {
6 | return
7 | }
8 | ;(ga.q = ga.q || []).push(arguments)
9 | }
10 | ga.l = +new Date()
11 | ga('create', GA_ID, 'auto')
12 | ga('set', 'transport', 'beacon')
13 | var timeout = setTimeout(
14 | (onload = function () {
15 | clearTimeout(timeout)
16 | ga('send', 'pageview')
17 | }),
18 | 1000,
19 | )`
20 |
--------------------------------------------------------------------------------
/src/utils/has-breadcrumbs.js:
--------------------------------------------------------------------------------
1 | export default ({ frontmatter }) => {
2 | if (frontmatter && frontmatter.breadcrumbs === false)
3 | return false
4 |
5 | return true
6 | }
7 |
--------------------------------------------------------------------------------
/src/utils/is-mobile.js:
--------------------------------------------------------------------------------
1 | export default () => {
2 | if (typeof window === "undefined") return false
3 | return matchMedia("(max-width: 768px)").matches
4 | }
5 |
--------------------------------------------------------------------------------
/src/utils/mobile-sidebar-manipulation.js:
--------------------------------------------------------------------------------
1 | const attr = "is-mobile-sidebar-open"
2 |
3 | export const sidebarIsOpen = () => {
4 | return document.documentElement.hasAttribute(attr)
5 | }
6 |
7 | export const sidebarOpen = () => {
8 | document.documentElement.setAttribute(attr, "")
9 | }
10 |
11 | export const sidebarClose = () => {
12 | document.documentElement.removeAttribute(attr)
13 | }
14 |
15 | export const sidebarToggle = () => {
16 | if (sidebarIsOpen()) return sidebarClose()
17 | sidebarOpen()
18 | }
19 |
--------------------------------------------------------------------------------
/src/utils/user-prefers-reduced-motion.js:
--------------------------------------------------------------------------------
1 | export default () => {
2 | if (typeof window === "undefined") return false
3 | return matchMedia("(prefers-reduced-motion: reduce)").matches
4 | }
5 |
--------------------------------------------------------------------------------
/workers-site/.cargo-ok:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Erisa/cloudflare-docs-engine/1a42d49275dc4623482ecf4899d4815b16b0499d/workers-site/.cargo-ok
--------------------------------------------------------------------------------
/workers-site/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | worker
3 |
--------------------------------------------------------------------------------
/workers-site/index.js:
--------------------------------------------------------------------------------
1 | import { getAssetFromKV, mapRequestToAsset } from '@cloudflare/kv-asset-handler'
2 | import analytics from 'workers-google-analytics'
3 | import redirector from 'lilredirector'
4 |
5 | const docsConfig = require("../docs-config.js")
6 |
7 | /**
8 | * The DEBUG flag will do two things that help during development:
9 | * 1. we will skip caching on the edge, which makes it easier to
10 | * debug.
11 | * 2. we will return an error message on exception in your Response rather
12 | * than the default 404.html page.
13 | */
14 | const DEBUG = false
15 |
16 | addEventListener('fetch', event => {
17 | try {
18 | event.respondWith(handleEvent(event))
19 | } catch (e) {
20 | if (DEBUG) {
21 | return event.respondWith(
22 | new Response(e.message || e.toString(), {
23 | status: 500,
24 | }),
25 | )
26 | }
27 | event.respondWith(new Response('Internal Error', { status: 500 }))
28 | }
29 | })
30 |
31 | async function handleEvent(event) {
32 | const url = new URL(event.request.url)
33 | let options = {}
34 |
35 | /**
36 | * You can add custom logic to how we fetch your assets
37 | * by configuring the function `mapRequestToAsset`
38 | */
39 | // options.mapRequestToAsset = handlePrefix(/^\/docs/)
40 |
41 | try {
42 | const { response } = await redirector(event, {
43 | baseUrl: `${docsConfig.pathPrefix}/_redirects`,
44 | validateRedirects: false
45 | })
46 | if (response) return response
47 |
48 | const analyticsResp = await analytics(event, {
49 | allowList: ['developers.cloudflare.com'],
50 | })
51 | if (analyticsResp) return analyticsResp
52 |
53 | if (DEBUG) {
54 | // customize caching
55 | options.cacheControl = {
56 | bypassCache: true,
57 | }
58 | } else {
59 | options.cacheControl = {
60 | browserTTL: null,
61 | edgeTTL: 5,
62 | bypassCache: false,
63 | }
64 | }
65 | return await getAssetFromKV(event, options)
66 | } catch (e) {
67 | // if an error is thrown try to serve the asset at 404.html
68 | if (!DEBUG) {
69 | try {
70 | let notFoundResponse = await getAssetFromKV(event, {
71 | mapRequestToAsset: req => new Request(`${new URL(req.url).origin}${docsConfig.pathPrefix}/404.html`, req),
72 | })
73 |
74 | return new Response(notFoundResponse.body, { ...notFoundResponse, status: 404 })
75 | } catch (e) {}
76 | }
77 |
78 | return new Response(e.message || e.toString(), { status: 500 })
79 | }
80 | }
81 |
82 | /**
83 | * Here's one example of how to modify a request to
84 | * remove a specific prefix, in this case `/docs` from
85 | * the url. This can be useful if you are deploying to a
86 | * route on a zone, or if you only want your static content
87 | * to exist at a specific path.
88 | */
89 | function handlePrefix(prefix) {
90 | return request => {
91 | // compute the default (e.g. / -> index.html)
92 | let defaultAssetKey = mapRequestToAsset(request)
93 | let url = new URL(defaultAssetKey.url)
94 |
95 | // strip the prefix from the path for lookup
96 | url.pathname = url.pathname.replace(prefix, '/')
97 |
98 | // inherit all other props from the default request
99 | return new Request(url.toString(), defaultAssetKey)
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/workers-site/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "worker",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@cloudflare/kv-asset-handler": {
8 | "version": "0.0.12",
9 | "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.0.12.tgz",
10 | "integrity": "sha512-3XCs9TRhQh/yUs1ST9cIMLB8C5c1JRKcDYTPJbAmcgI0kO2FPCSZz+kyNb9x25DuGQeqOMMwmxHPtiOfTki/Gw==",
11 | "requires": {
12 | "@cloudflare/workers-types": "^2.0.0",
13 | "@types/mime": "^2.0.2",
14 | "mime": "^2.4.6"
15 | }
16 | },
17 | "@cloudflare/workers-types": {
18 | "version": "2.0.0",
19 | "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-2.0.0.tgz",
20 | "integrity": "sha512-SFUPQzR5aV2TBLP4Re+xNX5KfAGArcRGA44OLulBDnfblEf3J+6kFvdJAQwFhFpqru3wImwT1cX0wahk6EeWTw=="
21 | },
22 | "@types/basic-auth": {
23 | "version": "1.1.3",
24 | "resolved": "https://registry.npmjs.org/@types/basic-auth/-/basic-auth-1.1.3.tgz",
25 | "integrity": "sha512-W3rv6J0IGlxqgE2eQ2pTb0gBjaGtejQpJ6uaCjz3UQ65+TFTPC5/lAE+POfx1YLdjtxvejJzsIAfd3MxWiVmfg==",
26 | "requires": {
27 | "@types/node": "*"
28 | }
29 | },
30 | "@types/mime": {
31 | "version": "2.0.3",
32 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz",
33 | "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q=="
34 | },
35 | "@types/node": {
36 | "version": "14.11.2",
37 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz",
38 | "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA=="
39 | },
40 | "@types/papaparse": {
41 | "version": "5.2.2",
42 | "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.2.2.tgz",
43 | "integrity": "sha512-e+3C4Mw/15uNC70ctfeehGooRlGv/h7fW8cf8HfBNDUngC/Ajtc6dqizx+ncDz7nj3/R1cshmYZnu86xZHvwRw==",
44 | "requires": {
45 | "@types/node": "*"
46 | }
47 | },
48 | "basic-auth": {
49 | "version": "2.0.1",
50 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
51 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
52 | "requires": {
53 | "safe-buffer": "5.1.2"
54 | }
55 | },
56 | "lilredirector": {
57 | "version": "0.5.1",
58 | "resolved": "https://registry.npmjs.org/lilredirector/-/lilredirector-0.5.1.tgz",
59 | "integrity": "sha512-tqjwxceVvQug6cpe1lTEC/n5gb1oOsOe5SnpOiKu15c5ZX4INy6DR/b5kCgbyun4WEZdUeI7pLQgfRdjUG04KQ==",
60 | "requires": {
61 | "@cloudflare/workers-types": "^2.0.0",
62 | "@types/basic-auth": "^1.1.3",
63 | "@types/papaparse": "^5.0.6",
64 | "basic-auth": "^2.0.1",
65 | "papaparse": "^5.2.0"
66 | }
67 | },
68 | "mime": {
69 | "version": "2.4.6",
70 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
71 | "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA=="
72 | },
73 | "papaparse": {
74 | "version": "5.3.0",
75 | "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.0.tgz",
76 | "integrity": "sha512-Lb7jN/4bTpiuGPrYy4tkKoUS8sTki8zacB5ke1p5zolhcSE4TlWgrlsxjrDTbG/dFVh07ck7X36hUf/b5V68pg=="
77 | },
78 | "safe-buffer": {
79 | "version": "5.1.2",
80 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
81 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
82 | },
83 | "workers-google-analytics": {
84 | "version": "0.0.3",
85 | "resolved": "https://registry.npmjs.org/workers-google-analytics/-/workers-google-analytics-0.0.3.tgz",
86 | "integrity": "sha512-R17b/uKw8jw9oq0nucIildywrJ4QsEpcQss5XHzpp9POCZxKs+/uIpJtuOQjjB1Vw9gTXotwkgtbqiorqu1MPg==",
87 | "requires": {
88 | "@cloudflare/workers-types": "^2.0.0"
89 | }
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/workers-site/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "worker",
4 | "version": "1.0.0",
5 | "description": "A template for kick starting a Cloudflare Workers project",
6 | "main": "index.js",
7 | "author": "Ashley Lewis ",
8 | "license": "MIT",
9 | "dependencies": {
10 | "@cloudflare/kv-asset-handler": "^0.0.12",
11 | "lilredirector": "^0.5.1",
12 | "workers-google-analytics": "0.0.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------