content,internal: update pages to use new base layout

Moves pages into domain specific directories
and updates the template parsing logic to
begin the switch to the new base layout. The
unit page migration will come in a later CL.

Change-Id: I424643146aacaefd61e88cb9a767b9d4b581aa5e
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/324391
Trust: Jamal Carvalho <jamal@golang.org>
Run-TryBot: Jamal Carvalho <jamal@golang.org>
Reviewed-by: Julie Qiu <julie@golang.org>
This commit is contained in:
Jamal Carvalho 2021-06-02 16:16:48 -04:00
Родитель 136b8bddd2
Коммит 1d6f21af26
97 изменённых файлов: 2077 добавлений и 886 удалений

Просмотреть файл

@ -17,7 +17,6 @@
.Badge-formElement > input {
display: block;
margin-top: 1rem;
max-width: 50.25rem;
}
.Badge-formElement > input {
border: 0.0625rem solid var(--gray-8);
@ -49,7 +48,6 @@
background-color: var(--gray-10);
display: block;
margin-top: 1rem;
max-width: 50.25rem;
padding: 1rem;
}
.Badge-gopherLanding {

Просмотреть файл

@ -1,4 +1,4 @@
/*!
/**
* @license
* Copyright 2019-2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style

Просмотреть файл

@ -1,7 +1,7 @@
{
"version": 3,
"sources": ["badge.ts"],
"sourcesContent": ["/*!\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nconst snippetEls = document.querySelectorAll('.js-toolsCopySnippet');\nsnippetEls.forEach(inputEl => {\n inputEl.addEventListener('click', e => {\n e.preventDefault();\n (e.currentTarget as HTMLInputElement)?.select();\n document.execCommand('copy');\n });\n});\n"],
"sourcesContent": ["/**\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nconst snippetEls = document.querySelectorAll('.js-toolsCopySnippet');\nsnippetEls.forEach(inputEl => {\n inputEl.addEventListener('click', e => {\n e.preventDefault();\n (e.currentTarget as HTMLInputElement)?.select();\n document.execCommand('copy');\n });\n});\n"],
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAOA,KAAM,YAAa,SAAS,iBAAiB,wBAC7C,WAAW,QAAQ,GAAW,CAC5B,EAAQ,iBAAiB,QAAS,GAAK,CACrC,EAAE,iBACD,EAAE,eAAoC,SACvC,SAAS,YAAY",
"names": []
}

Просмотреть файл

@ -4,14 +4,20 @@
license that can be found in the LICENSE file.
-->
{{define "pre_content"}}
<link href="/static/css/badge.css?version={{.AppVersionLabel}}" rel="stylesheet">
{{define "title"}}<title>Badge · pkg.go.dev</title>{{end}}
{{define "description"}}
<meta name="description" content="Create a badge to link to pkg.go.dev from your project website or README file.">
{{end}}
{{define "main_content"}}
<div class="Container">
<div class="Content">
<h1 class="Content-header">Create a badge</h1>
{{define "pre-content"}}
<link href="/static/badge/badge.css?version={{.AppVersionLabel}}" rel="stylesheet">
{{end}}
{{define "main"}}
<main class="go-Container">
<div class="go-Content">
<h1>Create a badge</h1>
<p>Create a badge to link to pkg.go.dev from your project website or README file.</p>
<label class="Badge-formElement">
Badge
@ -46,16 +52,16 @@
{{else}}
<div class="Badge-gopherLanding">
<img src="/static/img/gopher-airplane.svg" alt="The Go Gopher"/>
<p>Type a pkg.go.dev URL above to create a badge link.</p>
<p class="go-textSubtle">Type a pkg.go.dev URL above to create a badge link.</p>
</div>
{{end}}
</div>
</div>
</div>
</main>
{{end}}
{{define "post_content"}}
<script>
loadScript("/static/js/badge.js");
</script>
{{define "post-content"}}
<script>
loadScript("/static/badge/badge.js");
</script>
{{end}}

Просмотреть файл

@ -1,4 +1,4 @@
/*!
/**
* @license
* Copyright 2019-2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style

Просмотреть файл

@ -4,8 +4,6 @@
* license that can be found in the LICENSE file.
*/
@import url('./reset.css');
@import url('../typography/typography.css');
@import url('../button/button.css');
@ -25,38 +23,53 @@
@import url('../tooltip/tooltip.css');
:root {
/* Padding at the left and right of the viewport. */
--gutter: 1rem;
/* Margin between containers in the grid layout. */
--gap: 1rem;
/* The margin placed above elements scrolled to by clicking hash links. */
--scroll-margin: calc(
var(--js-sticky-header-height, 0) + var(--js-sticky-nav-height, 0) + var(--gap) * 2
var(--js-sticky-header-height, 3.5rem) + var(--js-sticky-nav-height, 0) + 2rem
);
/* Default styles for page elements. */
--border: 0.0625rem solid var(--color-border);
--border-radius: 0.25rem;
--box-shadow: 0 0 0.375rem 0 rgb(0 0 0 / 25%);
--focus-box-shadow: 0 0 0.0625rem 0.0625rem rgba(0, 112, 210, 0.6);
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme='light']) {
--box-shadow: 0 0.3125rem 0.9375rem rgb(0 0 0 / 45%);
}
}
[data-theme='dark'] :root {
--box-shadow: 0 0.3125rem 0.9375rem rgb(0 0 0 / 45%);
}
@media (min-width: 50rem) {
:root {
--gutter: 1.5rem;
--gap: 2rem;
--scroll-margin: calc(
var(--js-sticky-header-height, 0) + var(--js-sticky-nav-height, 0) + var(--gap)
var(--js-sticky-header-height, 3.5rem) + var(--js-sticky-nav-height, 0) + 1rem
);
}
}
@media (min-width: 80rem) {
:root {
--gutter: 2rem;
}
}
@media (min-width: 112rem) {
:root {
--gutter: 2.5rem;
}
}
*:target {
scroll-margin-top: var(--scroll-margin);
}
body {
background-color: var(--color-background);
display: flex;
flex-direction: column;
min-height: 100vh;
@ -72,11 +85,11 @@ body {
.go-Content {
display: flex;
flex-flow: column;
gap: var(--gap);
gap: 1rem;
margin: 0 auto;
max-width: 63rem;
min-height: 32rem;
padding: var(--gutter);
padding: 2rem var(--gutter);
width: 100%;
}
.go-Content--center {

Просмотреть файл

@ -1,8 +1,7 @@
(()=>{function l(){let t=document.querySelector(".js-header"),r=document.querySelectorAll(".js-headerMenuButton");r.forEach(o=>{o.addEventListener("click",e=>{e.preventDefault(),t.classList.toggle("is-active"),o.setAttribute("aria-expanded",String(t.classList.contains("is-active")))})}),document.querySelector(".js-scrim").addEventListener("click",o=>{o.preventDefault(),t.classList.remove("is-active"),r.forEach(e=>{e.setAttribute("aria-expanded",String(t.classList.contains("is-active")))})})}function m(){let t=512,r=document.querySelector(".js-headerLogo"),s=document.querySelector(".js-searchForm"),o=document.querySelector(".js-searchFormSubmit"),e=s.querySelector("input");i(),window.addEventListener("resize",i);function i(){window.innerWidth>t?(r.classList.remove("go-Header-logo--hidden"),s.classList.remove("go-SearchForm--open"),e.removeEventListener("focus",n),e.removeEventListener("keypress",a),e.removeEventListener("focusout",d)):(o.addEventListener("click",u),e.addEventListener("focus",n),e.addEventListener("keypress",a),e.addEventListener("focusout",d))}function a(c){c.key==="Enter"&&s.submit()}function n(){r.classList.add("go-Header-logo--hidden"),s.classList.add("go-SearchForm--open")}function d(){r.classList.remove("go-Header-logo--hidden"),s.classList.remove("go-SearchForm--open")}function u(c){c.preventDefault(),n(),e.focus()}}l();m();})();
/**
* @license
* Copyright 2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
*/import"../header/header.js";import"../keyboard/keyboard.js";import{ClipboardController as n}from"../clipboard/clipboard.js";import{ToolTipController as a}from"../tooltip/tooltip.js";import{SelectNavController as l}from"../outline/select.js";import{ModalController as i}from"../modal/modal.js";for(const e of document.querySelectorAll(".js-clipboard"))new n(e);for(const e of document.querySelectorAll(".js-modal"))new i(e);for(const e of document.querySelectorAll(".js-tooltip"))new a(e);for(const e of document.querySelectorAll(".js-selectNav"))new l(e);(function(){window.dataLayer=window.dataLayer||[],window.dataLayer.push({"gtm.start":new Date().getTime(),event:"gtm.js"})})();function r(){const e=new URLSearchParams(window.location.search),o=e.get("utm_source");if(o!=="gopls"&&o!=="godoc"&&o!=="pkggodev")return;const t=new URL(window.location.href);e.delete("utm_source"),t.search=e.toString(),window.history.replaceState(null,"",t.toString())}document.querySelector(".js-gtmID")?.dataset.gtmid&&window.dataLayer?window.dataLayer.push(function(){r()}):r();
//# sourceMappingURL=base.js.map

Просмотреть файл

@ -1,7 +1,7 @@
{
"version": 3,
"sources": ["../header/header.ts", "base.ts"],
"sourcesContent": ["function registerHeaderListeners() {\n const header = document.querySelector('.js-header');\n const menuButtons = document.querySelectorAll('.js-headerMenuButton');\n menuButtons.forEach(button => {\n button.addEventListener('click', e => {\n e.preventDefault();\n header.classList.toggle('is-active');\n button.setAttribute('aria-expanded', String(header.classList.contains('is-active')));\n });\n });\n\n const scrim = document.querySelector('.js-scrim');\n scrim.addEventListener('click', e => {\n e.preventDefault();\n header.classList.remove('is-active');\n menuButtons.forEach(button => {\n button.setAttribute('aria-expanded', String(header.classList.contains('is-active')));\n });\n });\n}\n\nfunction registerSearchFormListeners() {\n const BREAKPOINT = 512;\n const logo = document.querySelector('.js-headerLogo');\n const form = document.querySelector<HTMLFormElement>('.js-searchForm');\n const button = document.querySelector('.js-searchFormSubmit');\n const input = form.querySelector('input');\n\n renderForm();\n\n window.addEventListener('resize', renderForm);\n\n function renderForm() {\n if (window.innerWidth > BREAKPOINT) {\n logo.classList.remove('go-Header-logo--hidden');\n form.classList.remove('go-SearchForm--open');\n input.removeEventListener('focus', showSearchBox);\n input.removeEventListener('keypress', handleKeypress);\n input.removeEventListener('focusout', hideSearchBox);\n } else {\n button.addEventListener('click', handleSearchClick);\n input.addEventListener('focus', showSearchBox);\n input.addEventListener('keypress', handleKeypress);\n input.addEventListener('focusout', hideSearchBox);\n }\n }\n\n /**\n * Submits form if Enter key is pressed\n */\n function handleKeypress(e: KeyboardEvent) {\n if (e.key === 'Enter') form.submit();\n }\n\n /**\n * Shows the search box when it receives focus (expands it from\n * just the spyglass if we're on mobile).\n */\n function showSearchBox() {\n logo.classList.add('go-Header-logo--hidden');\n form.classList.add('go-SearchForm--open');\n }\n\n /**\n * Hides the search box (shrinks to just the spyglass icon).\n */\n function hideSearchBox() {\n logo.classList.remove('go-Header-logo--hidden');\n form.classList.remove('go-SearchForm--open');\n }\n\n /**\n * Expands the searchbox so input is visible and gives\n * the input focus.\n */\n function handleSearchClick(e: Event) {\n e.preventDefault();\n\n showSearchBox();\n input.focus();\n }\n}\n\nregisterHeaderListeners();\nregisterSearchFormListeners();\n", "/**\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport '../header/header.js';\n"],
"mappings": "MAAA,YAAmC,CACjC,GAAM,GAAS,SAAS,cAAc,cAChC,EAAc,SAAS,iBAAiB,wBAC9C,EAAY,QAAQ,GAAU,CAC5B,EAAO,iBAAiB,QAAS,GAAK,CACpC,EAAE,iBACF,EAAO,UAAU,OAAO,aACxB,EAAO,aAAa,gBAAiB,OAAO,EAAO,UAAU,SAAS,mBAK1E,AADc,SAAS,cAAc,aAC/B,iBAAiB,QAAS,GAAK,CACnC,EAAE,iBACF,EAAO,UAAU,OAAO,aACxB,EAAY,QAAQ,GAAU,CAC5B,EAAO,aAAa,gBAAiB,OAAO,EAAO,UAAU,SAAS,mBAK5E,YAAuC,CACrC,GAAM,GAAa,IACb,EAAO,SAAS,cAAc,kBAC9B,EAAO,SAAS,cAA+B,kBAC/C,EAAS,SAAS,cAAc,wBAChC,EAAQ,EAAK,cAAc,SAEjC,IAEA,OAAO,iBAAiB,SAAU,GAElC,YAAsB,CACpB,AAAI,OAAO,WAAa,EACtB,GAAK,UAAU,OAAO,0BACtB,EAAK,UAAU,OAAO,uBACtB,EAAM,oBAAoB,QAAS,GACnC,EAAM,oBAAoB,WAAY,GACtC,EAAM,oBAAoB,WAAY,IAEtC,GAAO,iBAAiB,QAAS,GACjC,EAAM,iBAAiB,QAAS,GAChC,EAAM,iBAAiB,WAAY,GACnC,EAAM,iBAAiB,WAAY,IAOvC,WAAwB,EAAkB,CACxC,AAAI,EAAE,MAAQ,SAAS,EAAK,SAO9B,YAAyB,CACvB,EAAK,UAAU,IAAI,0BACnB,EAAK,UAAU,IAAI,uBAMrB,YAAyB,CACvB,EAAK,UAAU,OAAO,0BACtB,EAAK,UAAU,OAAO,uBAOxB,WAA2B,EAAU,CACnC,EAAE,iBAEF,IACA,EAAM,SAIV,IACA,ICpFA",
"sources": ["base.ts"],
"sourcesContent": ["/**\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport '../header/header.js';\nimport '../keyboard/keyboard.js';\nimport { ClipboardController } from '../clipboard/clipboard.js';\nimport { ToolTipController } from '../tooltip/tooltip.js';\nimport { SelectNavController } from '../outline/select.js';\nimport { ModalController } from '../modal/modal.js';\n\nfor (const el of document.querySelectorAll<HTMLButtonElement>('.js-clipboard')) {\n new ClipboardController(el);\n}\n\nfor (const el of document.querySelectorAll<HTMLDialogElement>('.js-modal')) {\n new ModalController(el);\n}\n\nfor (const t of document.querySelectorAll<HTMLDetailsElement>('.js-tooltip')) {\n new ToolTipController(t);\n}\n\nfor (const el of document.querySelectorAll<HTMLSelectElement>('.js-selectNav')) {\n new SelectNavController(el);\n}\n\ninterface TagManagerEvent {\n event: string;\n 'gtm.start': number;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\ndeclare global {\n interface Window {\n dataLayer?: (TagManagerEvent | VoidFunction)[];\n }\n}\n\n/**\n * setupGoogleTagManager intializes Google Tag Manager.\n */\n(function setupGoogleTagManager() {\n window.dataLayer = window.dataLayer || [];\n window.dataLayer.push({\n 'gtm.start': new Date().getTime(),\n event: 'gtm.js',\n });\n})();\n\n/**\n * removeUTMSource removes the utm_source GET parameter if present.\n * This is done using JavaScript, so that the utm_source is still\n * captured by Google Analytics.\n */\nfunction removeUTMSource() {\n const urlParams = new URLSearchParams(window.location.search);\n const utmSource = urlParams.get('utm_source');\n if (utmSource !== 'gopls' && utmSource !== 'godoc' && utmSource !== 'pkggodev') {\n return;\n }\n\n /** Strip the utm_source query parameter and replace the URL. **/\n const newURL = new URL(window.location.href);\n urlParams.delete('utm_source');\n newURL.search = urlParams.toString();\n window.history.replaceState(null, '', newURL.toString());\n}\n\nif (document.querySelector<HTMLElement>('.js-gtmID')?.dataset.gtmid && window.dataLayer) {\n window.dataLayer.push(function () {\n removeUTMSource();\n });\n} else {\n removeUTMSource();\n}\n"],
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAOA,4BACA,gCACA,gEACA,0DACA,2DACA,oDAEA,SAAW,KAAM,UAAS,iBAAoC,iBAC5D,GAAI,GAAoB,GAG1B,SAAW,KAAM,UAAS,iBAAoC,aAC5D,GAAI,GAAgB,GAGtB,SAAW,KAAK,UAAS,iBAAqC,eAC5D,GAAI,GAAkB,GAGxB,SAAW,KAAM,UAAS,iBAAoC,iBAC5D,GAAI,GAAoB,GAkB1B,AAAC,WAAiC,CAChC,OAAO,UAAY,OAAO,WAAa,GACvC,OAAO,UAAU,KAAK,CACpB,YAAa,GAAI,QAAO,UACxB,MAAO,eASX,YAA2B,CACzB,KAAM,GAAY,GAAI,iBAAgB,OAAO,SAAS,QAChD,EAAY,EAAU,IAAI,cAChC,GAAI,IAAc,SAAW,IAAc,SAAW,IAAc,WAClE,OAIF,KAAM,GAAS,GAAI,KAAI,OAAO,SAAS,MACvC,EAAU,OAAO,cACjB,EAAO,OAAS,EAAU,WAC1B,OAAO,QAAQ,aAAa,KAAM,GAAI,EAAO,YAG/C,AAAI,SAAS,cAA2B,cAAc,QAAQ,OAAS,OAAO,UAC5E,OAAO,UAAU,KAAK,UAAY,CAChC,MAGF",
"names": []
}

Просмотреть файл

@ -5,7 +5,7 @@
-->
<!DOCTYPE html>
<html lang="en" data-layout="responsive">
<html lang="en" data-layout="{{if .UseResponsiveLayout}}responsive{{end}}">
<head>
<!-- This will capture unhandled errors during page load for reporting later. -->
<script>
@ -19,11 +19,15 @@
{{end}}
<meta class="js-gtmID" data-gtmid="{{.GoogleTagManagerID}}">
<link rel="shortcut icon" href="/static/icon/favicon.ico">
<!-- Only load the css reset for non-legacy pages -->
{{if not .UseSiteWrapper}}
<link href="/static/base/reset.css" rel="stylesheet">
{{end}}
<link href="/static/base/base.css" rel="stylesheet">
{{block "title" .}}
<title>pkg.go.dev</title>
{{end}}
{{block "pre_content" .}}{{end}}
{{block "pre-content" .}}{{end}}
</head>
<body>
<!-- loadScript appends JS sources to the document head. It loads scripts as asynchronous
@ -39,9 +43,15 @@
}
loadScript("/static/base/base.js");
</script>
{{if .UseSiteWrapper}}
<div class="Site Site--wide Site--redesign"><div class="Site-content">
{{end}}
{{template "header" .}}
{{template "main" .}}
{{template "footer" .}}
{{if .UseSiteWrapper}}
</div></div>
{{end}}
{{if .GoogleTagManagerID}}
<script>
// this will throw if the querySelector cant find the element
@ -57,6 +67,6 @@
</iframe>
</noscript>
{{end}}
{{block "post_content" .}}{{end}}
{{block "post-content" .}}{{end}}
</body>
</html>

Просмотреть файл

@ -6,3 +6,74 @@
*/
import '../header/header.js';
import '../keyboard/keyboard.js';
import { ClipboardController } from '../clipboard/clipboard.js';
import { ToolTipController } from '../tooltip/tooltip.js';
import { SelectNavController } from '../outline/select.js';
import { ModalController } from '../modal/modal.js';
for (const el of document.querySelectorAll<HTMLButtonElement>('.js-clipboard')) {
new ClipboardController(el);
}
for (const el of document.querySelectorAll<HTMLDialogElement>('.js-modal')) {
new ModalController(el);
}
for (const t of document.querySelectorAll<HTMLDetailsElement>('.js-tooltip')) {
new ToolTipController(t);
}
for (const el of document.querySelectorAll<HTMLSelectElement>('.js-selectNav')) {
new SelectNavController(el);
}
interface TagManagerEvent {
event: string;
'gtm.start': number;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
declare global {
interface Window {
dataLayer?: (TagManagerEvent | VoidFunction)[];
}
}
/**
* setupGoogleTagManager intializes Google Tag Manager.
*/
(function setupGoogleTagManager() {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'gtm.start': new Date().getTime(),
event: 'gtm.js',
});
})();
/**
* removeUTMSource removes the utm_source GET parameter if present.
* This is done using JavaScript, so that the utm_source is still
* captured by Google Analytics.
*/
function removeUTMSource() {
const urlParams = new URLSearchParams(window.location.search);
const utmSource = urlParams.get('utm_source');
if (utmSource !== 'gopls' && utmSource !== 'godoc' && utmSource !== 'pkggodev') {
return;
}
/** Strip the utm_source query parameter and replace the URL. **/
const newURL = new URL(window.location.href);
urlParams.delete('utm_source');
newURL.search = urlParams.toString();
window.history.replaceState(null, '', newURL.toString());
}
if (document.querySelector<HTMLElement>('.js-gtmID')?.dataset.gtmid && window.dataLayer) {
window.dataLayer.push(function () {
removeUTMSource();
});
} else {
removeUTMSource();
}

Просмотреть файл

@ -0,0 +1,7 @@
/**
* @license
* Copyright 2021 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/export class ClipboardController{constructor(t){this.el=t;this._data=t.dataset.toCopy??t.parentElement.querySelector("input")?.value??t.innerText,t.addEventListener("click",e=>this.handleCopyClick(e))}handleCopyClick(t){t.preventDefault();const e=1e3;if(!navigator.clipboard){this.showTooltipText("Unable to copy",e);return}navigator.clipboard.writeText(this._data).then(()=>{this.showTooltipText("Copied!",e)}).catch(()=>{this.showTooltipText("Unable to copy",e)})}showTooltipText(t,e){this.el.setAttribute("data-tooltip",t),setTimeout(()=>this.el.setAttribute("data-tooltip",""),e)}}
//# sourceMappingURL=clipboard.js.map

Просмотреть файл

@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["clipboard.ts"],
"sourcesContent": ["/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * This class decorates an element to copy arbitrary data attached via a data-\n * attribute to the clipboard.\n */\nexport class ClipboardController {\n /**\n * The data to be copied to the clipboard.\n */\n private _data: string;\n\n /**\n * @param el The element that will trigger copying text to the clipboard. The text is\n * expected to be within its data-to-copy attribute.\n */\n constructor(private el: HTMLButtonElement) {\n this._data =\n el.dataset['toCopy'] ?? el.parentElement.querySelector('input')?.value ?? el.innerText;\n el.addEventListener('click', e => this.handleCopyClick(e));\n }\n\n /**\n * Handles when the primary element is clicked.\n */\n handleCopyClick(e: MouseEvent): void {\n e.preventDefault();\n const TOOLTIP_SHOW_DURATION_MS = 1000;\n\n // This API is not available on iOS.\n if (!navigator.clipboard) {\n this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);\n return;\n }\n navigator.clipboard\n .writeText(this._data)\n .then(() => {\n this.showTooltipText('Copied!', TOOLTIP_SHOW_DURATION_MS);\n })\n .catch(() => {\n this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);\n });\n }\n\n /**\n * Shows the given text in a tooltip for a specified amount of time, in milliseconds.\n */\n showTooltipText(text: string, durationMs: number): void {\n this.el.setAttribute('data-tooltip', text);\n setTimeout(() => this.el.setAttribute('data-tooltip', ''), durationMs);\n }\n}\n"],
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAWO,gCAA0B,CAU/B,YAAoB,EAAuB,CAAvB,UAClB,KAAK,MACH,EAAG,QAAQ,QAAa,EAAG,cAAc,cAAc,UAAU,OAAS,EAAG,UAC/E,EAAG,iBAAiB,QAAS,GAAK,KAAK,gBAAgB,IAMzD,gBAAgB,EAAqB,CACnC,EAAE,iBACF,KAAM,GAA2B,IAGjC,GAAI,CAAC,UAAU,UAAW,CACxB,KAAK,gBAAgB,iBAAkB,GACvC,OAEF,UAAU,UACP,UAAU,KAAK,OACf,KAAK,IAAM,CACV,KAAK,gBAAgB,UAAW,KAEjC,MAAM,IAAM,CACX,KAAK,gBAAgB,iBAAkB,KAO7C,gBAAgB,EAAc,EAA0B,CACtD,KAAK,GAAG,aAAa,eAAgB,GACrC,WAAW,IAAM,KAAK,GAAG,aAAa,eAAgB,IAAK",
"names": []
}

Просмотреть файл

@ -60,19 +60,6 @@
--color-button-accented-text-disabled: var(--gray-3);
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme='light']) {
--color-brand-primary: var(--turq-med);
--color-background: var(--gray-1);
--color-background-accented: var(--slate);
--color-background-highlighted: var(--gray-2);
--color-background-warning: var(--yellow);
--color-background-alert: var(--pink);
--color-border: var(--gray-4);
--color-text: var(--white);
--color-text-subtle: var(--gray-7);
}
}
[data-theme='dark'] {
--color-brand-primary: var(--turq-med);
--color-background: var(--gray-1);

Просмотреть файл

@ -541,6 +541,7 @@ pre,
.Container {
color: var(--gray-1);
flex-grow: 1;
margin: 0 auto;
max-width: 60em;
position: relative;

Просмотреть файл

@ -0,0 +1,16 @@
<!--
Copyright 2020 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{define "title"}}<title>{{.MessageData}} · pkg.go.dev</title>{{end}}
{{define "main"}}
<main class="go-Container">
<div class="go-Content go-Content--center">
<img class="Error-gopher" src="/static/img/gopher-airplane.svg" alt="The Go Gopher">
{{template "message" .MessageData}}
</div>
</main>
{{end}}

Просмотреть файл

@ -10,15 +10,11 @@
border-radius: 0.5rem;
color: var(--turq-dark);
font-size: 1rem;
height: 2.5rem;
margin: 1rem 0;
margin: 1rem auto;
min-height: 2.5rem;
min-width: 5rem;
padding: 0 1rem;
}
.Fetch-container {
align-items: center;
display: flex;
flex-flow: column;
padding: 0.5rem 1rem;
width: fit-content;
}
@keyframes blink {
0% {
@ -44,6 +40,7 @@
}
.Fetch-loading {
display: none;
text-align: center;
}
.Fetch-loading:nth-child(2) {
animation-delay: 0.2s;

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -1,17 +1,22 @@
<!--
Copyright 2019 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
Copyright 2020 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{define "pre_content"}}
<link href="/static/css/fetch.css?version={{.AppVersionLabel}}" rel="stylesheet">
{{define "title"}}<title>{{.MessageData}} · pkg.go.dev</title>{{end}}
{{define "description"}}
<meta name="description" content="Create a badge to link to pkg.go.dev from your project website or README file.">
{{end}}
{{define "main_content"}}
<div class="Container">
<div class="Content">
<div class="Fetch-container">
{{define "pre-content"}}
<link href="/static/fetch/fetch.css?version={{.AppVersionLabel}}" rel="stylesheet">
{{end}}
{{define "main"}}
<main class="go-Container">
<div class="go-Content go-Content--center">
<img class="Fetch-gopher" src="/static/img/gopher-airplane.svg" alt="The Go Gopher">
<h3 class="Fetch-message js-fetchMessage"
data-test-id="fetch-message"
@ -33,12 +38,12 @@
Request “{{.MessageData}}”
</button>
</div>
</div>
</div>
</main>
{{end}}
{{define "post_content"}}
<script>
loadScript("/static/js/fetch.js");
</script>
{{define "post-content"}}
<script>
loadScript("/static/fetch/fetch.js");
</script>
{{end}}

Просмотреть файл

Просмотреть файл

@ -20,7 +20,7 @@ input::placeholder {
align-items: start;
display: flex;
flex-direction: column;
gap: var(--gap);
gap: 1rem;
}
.go-Label {

Просмотреть файл

@ -0,0 +1,12 @@
<!--
Copyright 2019 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{define "empty-content"}}
<div>
<img class="EmptyContent-gopher" src="/static/img/gopher-airplane.svg" alt="The Go Gopher">
<h3 class="EmptyContent-message">{{.}}</h3>
</div>
{{end}}

Просмотреть файл

@ -16,7 +16,7 @@
.Error-message,
.EmptyContent-message,
.SearchResults-emptyContentMessage {
margin: 0.5rem 0;
color: var(--color-textSubtle);
text-align: center;
}
.Error-gopher,
@ -25,7 +25,7 @@
.SearchResults-emptyContentGopher {
display: block;
height: 15rem;
margin: auto;
margin: 0 auto;
padding: 1.25rem 0;
width: 15rem;
}

Просмотреть файл

@ -20,7 +20,7 @@
margin: 0 auto;
max-width: 75.75rem;
min-height: 2.5rem;
padding: 0.5rem 1.5rem;
padding: 0.5rem var(--gutter);
}
.Site--wide .go-Banner-inner {
max-width: 98rem;
@ -48,17 +48,17 @@
background: #007d9c;
border-bottom: none;
box-shadow: 0 0.0625rem 0.125rem rgba(171, 171, 171, 0.3);
padding: 0 var(--gutter);
top: 0;
width: 100%;
z-index: 10;
}
.go-Header-inner {
margin: 0 auto;
max-width: 73.75rem;
max-width: 75.75rem;
padding: 0 var(--gutter);
}
.Site--wide .go-Header-inner {
max-width: 96rem;
max-width: 98rem;
}
.go-Header--full .go-Header-inner {
max-width: initial;

Просмотреть файл

@ -0,0 +1,2 @@
function registerHeaderListeners(){const t=document.querySelector(".js-header"),s=document.querySelectorAll(".js-headerMenuButton");s.forEach(n=>{n.addEventListener("click",e=>{e.preventDefault(),t.classList.toggle("is-active"),n.setAttribute("aria-expanded",String(t.classList.contains("is-active")))})}),document.querySelector(".js-scrim").addEventListener("click",n=>{n.preventDefault(),t.classList.remove("is-active"),s.forEach(e=>{e.setAttribute("aria-expanded",String(t.classList.contains("is-active")))})})}function registerSearchFormListeners(){const t=512,s=document.querySelector(".js-headerLogo"),r=document.querySelector(".js-searchForm"),n=document.querySelector(".js-searchFormSubmit"),e=r.querySelector("input");i(),window.addEventListener("resize",i);function i(){window.innerWidth>t?(s.classList.remove("go-Header-logo--hidden"),r.classList.remove("go-SearchForm--open"),e.removeEventListener("focus",o),e.removeEventListener("keypress",a),e.removeEventListener("focusout",d)):(n.addEventListener("click",u),e.addEventListener("focus",o),e.addEventListener("keypress",a),e.addEventListener("focusout",d))}function a(c){c.key==="Enter"&&r.submit()}function o(){s.classList.add("go-Header-logo--hidden"),r.classList.add("go-SearchForm--open")}function d(){s.classList.remove("go-Header-logo--hidden"),r.classList.remove("go-SearchForm--open")}function u(c){c.preventDefault(),o(),e.focus()}}registerHeaderListeners(),registerSearchFormListeners();
//# sourceMappingURL=header.js.map

Просмотреть файл

@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["header.ts"],
"sourcesContent": ["function registerHeaderListeners() {\n const header = document.querySelector('.js-header');\n const menuButtons = document.querySelectorAll('.js-headerMenuButton');\n menuButtons.forEach(button => {\n button.addEventListener('click', e => {\n e.preventDefault();\n header.classList.toggle('is-active');\n button.setAttribute('aria-expanded', String(header.classList.contains('is-active')));\n });\n });\n\n const scrim = document.querySelector('.js-scrim');\n scrim.addEventListener('click', e => {\n e.preventDefault();\n header.classList.remove('is-active');\n menuButtons.forEach(button => {\n button.setAttribute('aria-expanded', String(header.classList.contains('is-active')));\n });\n });\n}\n\nfunction registerSearchFormListeners() {\n const BREAKPOINT = 512;\n const logo = document.querySelector('.js-headerLogo');\n const form = document.querySelector<HTMLFormElement>('.js-searchForm');\n const button = document.querySelector('.js-searchFormSubmit');\n const input = form.querySelector('input');\n\n renderForm();\n\n window.addEventListener('resize', renderForm);\n\n function renderForm() {\n if (window.innerWidth > BREAKPOINT) {\n logo.classList.remove('go-Header-logo--hidden');\n form.classList.remove('go-SearchForm--open');\n input.removeEventListener('focus', showSearchBox);\n input.removeEventListener('keypress', handleKeypress);\n input.removeEventListener('focusout', hideSearchBox);\n } else {\n button.addEventListener('click', handleSearchClick);\n input.addEventListener('focus', showSearchBox);\n input.addEventListener('keypress', handleKeypress);\n input.addEventListener('focusout', hideSearchBox);\n }\n }\n\n /**\n * Submits form if Enter key is pressed\n */\n function handleKeypress(e: KeyboardEvent) {\n if (e.key === 'Enter') form.submit();\n }\n\n /**\n * Shows the search box when it receives focus (expands it from\n * just the spyglass if we're on mobile).\n */\n function showSearchBox() {\n logo.classList.add('go-Header-logo--hidden');\n form.classList.add('go-SearchForm--open');\n }\n\n /**\n * Hides the search box (shrinks to just the spyglass icon).\n */\n function hideSearchBox() {\n logo.classList.remove('go-Header-logo--hidden');\n form.classList.remove('go-SearchForm--open');\n }\n\n /**\n * Expands the searchbox so input is visible and gives\n * the input focus.\n */\n function handleSearchClick(e: Event) {\n e.preventDefault();\n\n showSearchBox();\n input.focus();\n }\n}\n\nregisterHeaderListeners();\nregisterSearchFormListeners();\n"],
"mappings": "AAAA,kCAAmC,CACjC,KAAM,GAAS,SAAS,cAAc,cAChC,EAAc,SAAS,iBAAiB,wBAC9C,EAAY,QAAQ,GAAU,CAC5B,EAAO,iBAAiB,QAAS,GAAK,CACpC,EAAE,iBACF,EAAO,UAAU,OAAO,aACxB,EAAO,aAAa,gBAAiB,OAAO,EAAO,UAAU,SAAS,mBAK1E,AADc,SAAS,cAAc,aAC/B,iBAAiB,QAAS,GAAK,CACnC,EAAE,iBACF,EAAO,UAAU,OAAO,aACxB,EAAY,QAAQ,GAAU,CAC5B,EAAO,aAAa,gBAAiB,OAAO,EAAO,UAAU,SAAS,mBAK5E,sCAAuC,CACrC,KAAM,GAAa,IACb,EAAO,SAAS,cAAc,kBAC9B,EAAO,SAAS,cAA+B,kBAC/C,EAAS,SAAS,cAAc,wBAChC,EAAQ,EAAK,cAAc,SAEjC,IAEA,OAAO,iBAAiB,SAAU,GAElC,YAAsB,CACpB,AAAI,OAAO,WAAa,EACtB,GAAK,UAAU,OAAO,0BACtB,EAAK,UAAU,OAAO,uBACtB,EAAM,oBAAoB,QAAS,GACnC,EAAM,oBAAoB,WAAY,GACtC,EAAM,oBAAoB,WAAY,IAEtC,GAAO,iBAAiB,QAAS,GACjC,EAAM,iBAAiB,QAAS,GAChC,EAAM,iBAAiB,WAAY,GACnC,EAAM,iBAAiB,WAAY,IAOvC,WAAwB,EAAkB,CACxC,AAAI,EAAE,MAAQ,SAAS,EAAK,SAO9B,YAAyB,CACvB,EAAK,UAAU,IAAI,0BACnB,EAAK,UAAU,IAAI,uBAMrB,YAAyB,CACvB,EAAK,UAAU,OAAO,0BACtB,EAAK,UAAU,OAAO,uBAOxB,WAA2B,EAAU,CACnC,EAAE,iBAEF,IACA,EAAM,SAIV,0BACA",
"names": []
}

Просмотреть файл

@ -5,16 +5,16 @@
-->
{{define "header"}}
<div class="go-Banner{{if .AllowWideContent}} go-Banner--full{{end}}">
<div class="go-Banner-inner">
<div class="go-Banner-message">Black Lives Matter</div>
<a class="go-Banner-action"
href="https://support.eji.org/give/153413/#!/donation/checkout"
target="_blank"
rel="noopener">Support the Equal Justice Initiative</a>
</div>
</div>
<header class="go-Header{{if .AllowWideContent}} go-Header--full{{end}} js-siteHeader">
<div class="go-Banner{{if .AllowWideContent}} go-Banner--full{{end}}">
<div class="go-Banner-inner">
<div class="go-Banner-message">Black Lives Matter</div>
<a class="go-Banner-action"
href="https://support.eji.org/give/153413/#!/donation/checkout"
target="_blank"
rel="noopener">Support the Equal Justice Initiative</a>
</div>
</div>
<div class="go-Header-inner go-Header-inner--dark">
<nav class="go-Header-nav">
<a href="https://go.dev/" data-gtmc="nav link">
@ -27,7 +27,10 @@
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.76 10.27L17.49 16L16 17.49L10.27 11.76C9.2 12.53 7.91 13 6.5 13C2.91 13 0 10.09 0 6.5C0 2.91 2.91 0 6.5 0C10.09 0 13 2.91 13 6.5C13 7.91 12.53 9.2 11.76 10.27ZM6.5 2C4.01 2 2 4.01 2 6.5C2 8.99 4.01 11 6.5 11C8.99 11 11 8.99 11 6.5C11 4.01 8.99 2 6.5 2Z">
</path></svg>
</button>
<input class="go-SearchForm-input" aria-label="Search for a package" type="text" name="q" size="1" placeholder="Search for a package" autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false" title="Search for a package">
<input class="go-SearchForm-input js-searchFocus" aria-label="Search for a package"
type="text" name="q" size="1" placeholder="Search for a package"
autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false"
title="Search for a package">
</form>
<ul class="go-Header-menu">
<li class="go-Header-menuItem">

Просмотреть файл

@ -4,11 +4,10 @@
* license that can be found in the LICENSE file.
*/
.Homepage {
display: flex;
flex-direction: column;
margin: 0 auto;
.go-SearchForm {
display: none;
}
.Homepage-logo {
display: block;
height: 14.6rem;
@ -86,7 +85,6 @@ a.Homepage-helpLink {
display: block;
font-size: 0.875rem;
font-weight: 500;
margin: 1.125rem auto 1rem;
text-align: center;
width: 100%;
}
@ -116,7 +114,8 @@ a.Homepage-helpLink {
background: var(--gray-10);
color: var(--gray-2);
display: flex;
padding: 0.5rem 0;
padding-bottom: 1rem;
padding-top: 0.5rem;
}
.Questions-header {
color: var(--gray-2);

Просмотреть файл

@ -4,18 +4,13 @@
license that can be found in the LICENSE file.
-->
{{define "header_search"}}
{{/* This ideally would be empty, but you cannot redefine a template with only whitespace or comments. */}}
<wbr>
{{define "pre-content"}}
<link href="/static/homepage/homepage.css?version={{.AppVersionLabel}}" rel="stylesheet">
{{end}}
{{define "pre_content"}}
<link href="/static/css/homepage.css?version={{.AppVersionLabel}}" rel="stylesheet">
{{end}}
{{define "main_content"}}
<div class="Container">
<div class="Homepage">
{{define "main"}}
<main class="go-Container">
<div class="go-Content go-Content--center">
<img class="Homepage-logo" src="/static/img/gopher-homepage.jpg" alt="Cartoon gopher typing">
<form class="Homepage-searchForm" action="/search" role="search" data-gtmc="homepage search form"
aria-label="Search for a Package">
@ -49,10 +44,10 @@
</a>
<span>
</div>
</div>
</main>
{{end}}
{{define "pre_footer"}}
{{define "pre-footer"}}
<div class="Questions">
<div class="Questions-content">
<div class="Questions-header">Frequently asked questions:</div>

Просмотреть файл

@ -1,244 +0,0 @@
<!--
Copyright 2019 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
<!DOCTYPE html>
<html lang="en">
<!-- This will capture unhandled errors during page load for reporting later. -->
<script>window.addEventListener('error', window.__err=function f(e){f.p=f.p||[];f.p.push(e)});</script>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{if .MetaDescription.String}}
{{.MetaDescription}}
{{else}}
<meta name="Description" content="Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.">
{{end}}
<meta class="js-gtmID" data-gtmid="{{.GoogleTagManagerID}}">
<link href="/static/css/stylesheet.css?version={{.AppVersionLabel}}" rel="stylesheet">
<link href="/third_party/dialog-polyfill/dialog-polyfill.css?version={{.AppVersionLabel}}" rel="stylesheet">
<title>{{if .HTMLTitle}}{{.HTMLTitle}} · {{end}}pkg.go.dev</title>
{{block "pre_content" .}}{{end}}
<body class="Site{{if .AllowWideContent}} Site--wide{{end}} Site--redesign">
<header class="Site-header Site-header--dark">
<div class="Banner">
<div class="Banner-inner">
<div class="Banner-message">Black Lives Matter</div>
<a class="Banner-action"
href="https://support.eji.org/give/153413/#!/donation/checkout"
target="_blank"
rel="noopener">Support the Equal Justice Initiative</a>
</div>
</div>
<div class="Header">
<nav class="Header-nav">
<a href="https://go.dev/" class="Header-logoLink" aria-label="Link to Go Homepage" data-gtmc="nav link">
<img class="Header-logo" src="/static/img/go-logo-white.svg" alt="Go">
</a>
{{template "header_search" .}}
<ul class="Header-menu">
<li class="Header-menuItem">
<a href="https://go.dev/solutions" title="Why Go" data-gtmc="nav link">Why Go</a>
</li>
<li class="Header-menuItem">
<a href="https://learn.go.dev" title="Getting Started" data-gtmc="nav link">Getting Started</a>
</li>
<li class="Header-menuItem Header-menuItem--active">
<a href="/" title="Discover Packages" data-gtmc="nav link">Discover Packages</a>
</li>
<li class="Header-menuItem">
<a href="https://go.dev/about" title="" data-gtmc="nav link">About</a>
</li>
</ul>
<button class="Header-navOpen js-headerMenuButton" data-gtmc="nav link" aria-label="Open navigation.">
</button>
</nav>
</div>
</header>
<aside class="NavigationDrawer js-header">
<nav class="NavigationDrawer-nav">
<div class="NavigationDrawer-header">
<a href="https://go.dev/" data-gtmc="nav link" aria-label="Link to Go Homepage">
<img class="NavigationDrawer-logo" src="/static/img/go-logo-blue.svg" alt="Go">
</a>
<button class="NavigationDrawer-close js-headerMenuButton" aria-label="Close navigation.">
</button>
</div>
<ul class="NavigationDrawer-list">
<li class="NavigationDrawer-listItem">
<a href="https://go.dev/solutions" title="Why Go" data-gtmc="nav link">Why Go</a>
</li>
<li class="NavigationDrawer-listItem">
<a href="https://learn.go.dev" title="Getting Started" data-gtmc="nav link">Getting Started</a>
</li>
<li class="NavigationDrawer-listItem NavigationDrawer-listItem--active">
<a href="/" title="Discover Packages" data-gtmc="nav link">Discover Packages</a>
</li>
<li class="NavigationDrawer-listItem">
<a href="https://go.dev/about" title="" data-gtmc="nav link">About</a>
</li>
<li class="NavigationDrawer-listItem">
<a href="https://golang.org" title="golang.org" data-gtmc="nav link">golang.org</a>
</li>
</ul>
</nav>
</aside>
<div class="NavigationDrawer-scrim js-scrim" role="presentation">
</div>
<div class="Site-content">{{block "main_content" .}}{{end}}</div>
<footer class="Site-footer">
{{block "pre_footer" .}}{{end}}
<div class="Footer">
<div class="Footer-links">
<div class="Footer-linkColumn">
<a href="https://go.dev/solutions" class="Footer-link Footer-link--primary" title="Why Go"
data-gtmc="footer link">
Why Go
</a>
<a href="https://go.dev/solutions#use-cases" class="Footer-link" title="Use Cases"
data-gtmc="footer link">
Use Cases
</a>
<a href="https://go.dev/solutions#case-studies" class="Footer-link" title="Case Studies"
data-gtmc="footer link">
Case Studies
</a>
</div>
<div class="Footer-linkColumn">
<a href="https://learn.go.dev/" class="Footer-link Footer-link--primary" title="Getting Started"
data-gtmc="footer link">
Getting Started
</a>
<a href="https://play.golang.org" class="Footer-link" title="" data-gtmc="footer link">
Playground
</a>
<a href="https://tour.golang.org" class="Footer-link" title="" data-gtmc="footer link">
Tour
</a>
<a href="https://stackoverflow.com/questions/tagged/go?tab=Newest" class="Footer-link" title=""
data-gtmc="footer link">
Stack Overflow
</a>
</div>
<div class="Footer-linkColumn">
<a href="https://pkg.go.dev" class="Footer-link Footer-link--primary" title="Discover Packages"
data-gtmc="footer link">
Discover Packages
</a>
</div>
<div class="Footer-linkColumn">
<a href="https://go.dev/about" class="Footer-link Footer-link--primary" title="About"
data-gtmc="footer link">
About
</a>
<a href="https://golang.org/dl/" class="Footer-link" title=""
data-gtmc="footer link">
Download
</a>
<a href="https://blog.golang.org" class="Footer-link" title=""
data-gtmc="footer link">
Blog
</a>
<a href="https://golang.org/doc/devel/release.html" class="Footer-link" title=""
data-gtmc="footer link">
Release Notes
</a>
<a href="https://blog.golang.org/go-brand" class="Footer-link" title=""
data-gtmc="footer link">
Brand Guidelines
</a>
<a href="https://golang.org/conduct" class="Footer-link"
data-gtmc="footer link">
Code of Conduct
</a>
</div>
<div class="Footer-linkColumn">
<a href="https://www.twitter.com/golang" class="Footer-link Footer-link--primary" title="Connect"
data-gtmc="footer link">
Connect
</a>
<a href="https://www.twitter.com/golang" class="Footer-link" title=""
data-gtmc="footer link">
Twitter
</a>
<a href="https://github.com/golang" class="Footer-link" title=""
data-gtmc="footer link">
GitHub
</a>
<a href="https://invite.slack.golangbridge.org/" class="Footer-link" title=""
data-gtmc="footer link">
Slack
</a>
<a href="https://www.meetup.com/pro/go" class="Footer-link" title=""
data-gtmc="footer link">
Meetup
</a>
</div>
</div>
</div>
<div class="Footer">
<div class="Container Container--fullBleed">
<div class="Footer-bottom">
<img class="Footer-gopher" src="/static/img/pilot-bust.svg" alt="Gopher in flight goggles">
<ul class="Footer-listRow">
<li class="Footer-listItem">
<a href="https://go.dev/copyright" data-gtmc="footer link">Copyright</a>
</li>
<li class="Footer-listItem">
<a href="https://go.dev/tos" data-gtmc="footer link">Terms of Service</a>
</li>
<li class="Footer-listItem">
<a href="http://www.google.com/intl/en/policies/privacy/" data-gtmc="footer link" target="_blank"
rel="noopener">
Privacy Policy
</a>
</li>
<li class="Footer-listItem">
<a href="https://golang.org/s/pkgsite-feedback" target="_blank" rel="noopener"
data-gtmc="footer link">
Report an Issue
</a>
</li>
<li class="Footer-listItem">
<a href="https://golang.org" target="_blank" rel="noopener" data-gtmc="footer link">golang.org</a>
</li>
</ul>
<a class="Footer-googleLogo" href="https://google.com" target="_blank" rel="noopener"
data-gtmc="footer link">
<img class="Footer-googleLogoImg" src="/static/img/google-white.svg" alt="Google logo">
</a>
</div>
</div>
</div>
</footer>
<script>
function loadScript(src, props = {}) {
let s = document.createElement('script');
s.src = src;
for (const [k, v] of Object.entries(props)) {
s[k] = v
}
document.head.appendChild(s);
}
loadScript("/static/js/site.js");
</script>
{{block "post_content" .}}{{end}}
{{if .GoogleTagManagerID}}
<script async>
const gtmId = document.querySelector('.js-gtmID').dataset.gtmid; // this will throw if the querySelector cant find the element
if (!gtmId) {
throw new Error('Google Tag Manager ID not found');
}
loadScript(`https://www.googletagmanager.com/gtm.js?id=${gtmId}`);
</script>
<noscript>
<iframe src="https://www.googletagmanager.com/ns.html?id={{.GoogleTagManagerID}}"
height="0" width="0" style="display:none;visibility:hidden">
</iframe>
</noscript>
{{end}}

Просмотреть файл

@ -1,14 +0,0 @@
<!--
Copyright 2019 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{define "main_content"}}
<div class="Container">
<div class="Content">
<img class="Error-gopher" src="/static/img/gopher-airplane.svg" alt="The Go Gopher">
{{template "message" .MessageData}}
</div>
</div>
{{end}}

Просмотреть файл

@ -1,55 +0,0 @@
<!--
Copyright 2019 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{define "main_content"}}
<div class="Container">
<div class="Content">
<h1 class="Content-header">License Disclaimer</h1>
<p>
The Go website displays license information in order to help users evaluate
packages for their intended use. Licenses are detected using heuristics
based on their file name and contents. We hope this information is helpful,
but this is not legal advice and we do not make any guarantees regarding
the accuracy of our license detection.
</p>
<p>
If we are not able to detect one of the licenses below, only
limited package and module information will be made available. If you are
a package author seeking to make your content available on the Go
website, please be aware that our detection algorithms can be affected by
any modifications of the license text, or by the use of an uncommon
license file name.
</p>
<p>
We currently use
<a href="https://pkg.go.dev/github.com/google/licensecheck">github.com/google/licensecheck</a>
for license detection, and look for licenses in files with the following names:
{{commaseparate .LicenseFileNames}}. The match is case-insensitive.
</p>
<p>
We currently detect and recognize the following licenses:
<ul>
{{range .LicenseTypes -}}
<li>
{{if .URL -}}
<a href="{{.URL}}" target="_blank" rel="noopener">{{.Name}}</a>
{{else}}
{{.Name}}
{{- end}}
</li>
{{- end}}
</ul>
</p>
<p>
If you use a package whose license is not detected, please inform the package author.
If you are a package author who believes a license for one of your packages
should have been detected and was not, please check for discrepancies between your license
and the official text. If you still believe there is an error, please
<a href="https://golang.org/s/pkgsite-feedback">file an issue</a>.
</p>
</div>
</div>
{{end}}

Просмотреть файл

@ -4,17 +4,18 @@
license that can be found in the LICENSE file.
-->
{{define "pre_content"}}
{{define "pre-content"}}
<link href="/static/css/stylesheet.css?version={{.AppVersionLabel}}" rel="stylesheet">
<link href="/static/css/unit.css?version={{.AppVersionLabel}}" rel="stylesheet">
{{block "unit_pre_content" .}}{{end}}
<link href="/static/css/unit_outline.css?version={{.AppVersionLabel}}" rel="stylesheet">
{{end}}
{{define "main_content"}}
<div class="Container">
{{define "main"}}
<main class="Container">
{{block "unit_header" .}}{{end}}
{{block "unit_content" .}}{{end}}
</div>
</main>
<dialog class="JumpDialog Dialog">
<h2 class="Dialog-title">Jump to</h2>
@ -48,7 +49,7 @@
</dialog>
{{end}}
{{define "post_content"}}
{{define "post-content"}}
<div class="js-canonicalURLPath" data-canonical-url-path="{{.CanonicalURLPath}}" hidden />
<script>
loadScript('/static/js/keyboard.js', {type: 'module', async: true, defer: true})

Просмотреть файл

@ -16,12 +16,7 @@
filter: brightness(0) saturate(100%) invert(100%) sepia(97%) saturate(13%) hue-rotate(245deg)
brightness(103%) contrast(107%);
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme='light']) .go-Icon:not(.go-Icon--accented) {
filter: brightness(0) saturate(100%) invert(100%) sepia(97%) saturate(13%) hue-rotate(245deg)
brightness(103%) contrast(107%);
}
}
[data-theme='dark'] .go-Icon:not(.go-Icon--accented) {
filter: brightness(0) saturate(100%) invert(100%) sepia(97%) saturate(13%) hue-rotate(245deg)
brightness(103%) contrast(107%);

Просмотреть файл

@ -1,7 +0,0 @@
/*!
* @license
* Copyright 2019-2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/(function(){"use strict";const e=document.querySelector(".js-header"),a=document.querySelectorAll(".js-headerMenuButton");a.forEach(n=>{n.addEventListener("click",s=>{s.preventDefault(),e?.classList.toggle("is-active"),n.setAttribute("aria-expanded",`${e?.classList.contains("is-active")??!1}`)})});const r=document.querySelector(".js-scrim");r&&r.hasOwnProperty("addEventListener")&&r.addEventListener("click",n=>{n.preventDefault(),e?.classList.remove("is-active"),a.forEach(s=>{s.setAttribute("aria-expanded",`${e?.classList.contains("is-active")??!1}`)})})})(),function(){window.dataLayer=window.dataLayer||[],window.dataLayer.push({"gtm.start":new Date().getTime(),event:"gtm.js"})}();function removeUTMSource(){const t=new URLSearchParams(window.location.search),e=t.get("utm_source");if(e!=="gopls"&&e!=="godoc"&&e!=="pkggodev")return;const a=new URL(window.location.href);t.delete("utm_source"),a.search=t.toString(),window.history.replaceState(null,"",a.toString())}document.querySelector(".js-gtmID")?.dataset.gtmid&&window.dataLayer?window.dataLayer.push(function(){removeUTMSource()}):removeUTMSource();
//# sourceMappingURL=site.js.map

Просмотреть файл

@ -1,7 +0,0 @@
{
"version": 3,
"sources": ["site.ts"],
"sourcesContent": ["/*!\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * site.ts contains a set of functions that should be invoked for\n * all page views before other scripts are added to the page.\n */\n\n/**\n * A bit of navigation related code for handling dismissible elements.\n */\n(function registerHeaderListeners() {\n 'use strict';\n\n const header = document.querySelector('.js-header');\n const menuButtons = document.querySelectorAll('.js-headerMenuButton');\n menuButtons.forEach(button => {\n button.addEventListener('click', e => {\n e.preventDefault();\n header?.classList.toggle('is-active');\n button.setAttribute('aria-expanded', `${header?.classList.contains('is-active') ?? false}`);\n });\n });\n\n const scrim = document.querySelector('.js-scrim');\n // eslint-disable-next-line no-prototype-builtins\n if (scrim && scrim.hasOwnProperty('addEventListener')) {\n scrim.addEventListener('click', e => {\n e.preventDefault();\n header?.classList.remove('is-active');\n menuButtons.forEach(button => {\n button.setAttribute('aria-expanded', `${header?.classList.contains('is-active') ?? false}`);\n });\n });\n }\n})();\n\ninterface TagManagerEvent {\n event: string;\n 'gtm.start': number;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\ninterface Window {\n dataLayer?: (TagManagerEvent | VoidFunction)[];\n}\n\n/**\n * setupGoogleTagManager intializes Google Tag Manager.\n */\n(function setupGoogleTagManager() {\n window.dataLayer = window.dataLayer || [];\n window.dataLayer.push({\n 'gtm.start': new Date().getTime(),\n event: 'gtm.js',\n });\n})();\n\n/**\n * removeUTMSource removes the utm_source GET parameter if present.\n * This is done using JavaScript, so that the utm_source is still\n * captured by Google Analytics.\n */\nfunction removeUTMSource() {\n const urlParams = new URLSearchParams(window.location.search);\n const utmSource = urlParams.get('utm_source');\n if (utmSource !== 'gopls' && utmSource !== 'godoc' && utmSource !== 'pkggodev') {\n return;\n }\n\n /** Strip the utm_source query parameter and replace the URL. **/\n const newURL = new URL(window.location.href);\n urlParams.delete('utm_source');\n newURL.search = urlParams.toString();\n window.history.replaceState(null, '', newURL.toString());\n}\n\nif (document.querySelector<HTMLElement>('.js-gtmID')?.dataset.gtmid && window.dataLayer) {\n window.dataLayer.push(function () {\n removeUTMSource();\n });\n} else {\n removeUTMSource();\n}\n"],
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAeA,AAAC,WAAmC,CAClC,aAEA,KAAM,GAAS,SAAS,cAAc,cAChC,EAAc,SAAS,iBAAiB,wBAC9C,EAAY,QAAQ,GAAU,CAC5B,EAAO,iBAAiB,QAAS,GAAK,CACpC,EAAE,iBACF,GAAQ,UAAU,OAAO,aACzB,EAAO,aAAa,gBAAiB,GAAG,GAAQ,UAAU,SAAS,cAAgB,UAIvF,KAAM,GAAQ,SAAS,cAAc,aAErC,AAAI,GAAS,EAAM,eAAe,qBAChC,EAAM,iBAAiB,QAAS,GAAK,CACnC,EAAE,iBACF,GAAQ,UAAU,OAAO,aACzB,EAAY,QAAQ,GAAU,CAC5B,EAAO,aAAa,gBAAiB,GAAG,GAAQ,UAAU,SAAS,cAAgB,cAmB1F,UAAiC,CAChC,OAAO,UAAY,OAAO,WAAa,GACvC,OAAO,UAAU,KAAK,CACpB,YAAa,GAAI,QAAO,UACxB,MAAO,cASX,0BAA2B,CACzB,KAAM,GAAY,GAAI,iBAAgB,OAAO,SAAS,QAChD,EAAY,EAAU,IAAI,cAChC,GAAI,IAAc,SAAW,IAAc,SAAW,IAAc,WAClE,OAIF,KAAM,GAAS,GAAI,KAAI,OAAO,SAAS,MACvC,EAAU,OAAO,cACjB,EAAO,OAAS,EAAU,WAC1B,OAAO,QAAQ,aAAa,KAAM,GAAI,EAAO,YAG/C,AAAI,SAAS,cAA2B,cAAc,QAAQ,OAAS,OAAO,UAC5E,OAAO,UAAU,KAAK,UAAY,CAChC,oBAGF",
"names": []
}

Просмотреть файл

@ -1,88 +0,0 @@
/*!
* @license
* Copyright 2019-2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
/**
* site.ts contains a set of functions that should be invoked for
* all page views before other scripts are added to the page.
*/
/**
* A bit of navigation related code for handling dismissible elements.
*/
(function registerHeaderListeners() {
'use strict';
const header = document.querySelector('.js-header');
const menuButtons = document.querySelectorAll('.js-headerMenuButton');
menuButtons.forEach(button => {
button.addEventListener('click', e => {
e.preventDefault();
header?.classList.toggle('is-active');
button.setAttribute('aria-expanded', `${header?.classList.contains('is-active') ?? false}`);
});
});
const scrim = document.querySelector('.js-scrim');
// eslint-disable-next-line no-prototype-builtins
if (scrim && scrim.hasOwnProperty('addEventListener')) {
scrim.addEventListener('click', e => {
e.preventDefault();
header?.classList.remove('is-active');
menuButtons.forEach(button => {
button.setAttribute('aria-expanded', `${header?.classList.contains('is-active') ?? false}`);
});
});
}
})();
interface TagManagerEvent {
event: string;
'gtm.start': number;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Window {
dataLayer?: (TagManagerEvent | VoidFunction)[];
}
/**
* setupGoogleTagManager intializes Google Tag Manager.
*/
(function setupGoogleTagManager() {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'gtm.start': new Date().getTime(),
event: 'gtm.js',
});
})();
/**
* removeUTMSource removes the utm_source GET parameter if present.
* This is done using JavaScript, so that the utm_source is still
* captured by Google Analytics.
*/
function removeUTMSource() {
const urlParams = new URLSearchParams(window.location.search);
const utmSource = urlParams.get('utm_source');
if (utmSource !== 'gopls' && utmSource !== 'godoc' && utmSource !== 'pkggodev') {
return;
}
/** Strip the utm_source query parameter and replace the URL. **/
const newURL = new URL(window.location.href);
urlParams.delete('utm_source');
newURL.search = urlParams.toString();
window.history.replaceState(null, '', newURL.toString());
}
if (document.querySelector<HTMLElement>('.js-gtmID')?.dataset.gtmid && window.dataLayer) {
window.dataLayer.push(function () {
removeUTMSource();
});
} else {
removeUTMSource();
}

Просмотреть файл

@ -0,0 +1,7 @@
/*!
* @license
* Copyright 2019-2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/const jumpDialog=document.querySelector(".JumpDialog"),jumpBody=jumpDialog?.querySelector(".JumpDialog-body"),jumpList=jumpDialog?.querySelector(".JumpDialog-list"),jumpFilter=jumpDialog?.querySelector(".JumpDialog-input"),doc=document.querySelector(".js-documentation");let jumpListItems;function collectJumpListItems(){const t=[];if(!!doc){for(const e of doc.querySelectorAll("[data-kind]"))t.push(newJumpListItem(e));for(const e of t)e.link.addEventListener("click",function(){jumpDialog?.close()});return t.sort(function(e,n){return e.lower.localeCompare(n.lower)}),t}}function newJumpListItem(t){const e=document.createElement("a"),n=t.getAttribute("id");e.setAttribute("href","#"+n),e.setAttribute("tabindex","-1"),e.setAttribute("data-gtmc","jump to link");const o=t.getAttribute("data-kind");return{link:e,name:n??"",kind:o??"",lower:n?.toLowerCase()??""}}let lastFilterValue,activeJumpItem=-1;function updateJumpList(t){for(lastFilterValue=t,jumpListItems||(jumpListItems=collectJumpListItems()),setActiveJumpItem(-1);jumpList?.firstChild;)jumpList.firstChild.remove();if(t){const e=t.toLowerCase(),n=[],o=[],l=[],c=(i,s,r)=>i.name.substring(0,s)+"<b>"+i.name.substring(s,r)+"</b>"+i.name.substring(r);for(const i of jumpListItems??[]){const s=i.name.toLowerCase();if(s===e)i.link.innerHTML=c(i,0,i.name.length),n.push(i);else if(s.startsWith(e))i.link.innerHTML=c(i,0,t.length),o.push(i);else{const r=s.indexOf(e);r>-1&&(i.link.innerHTML=c(i,r,r+t.length),l.push(i))}}for(const i of n.concat(o).concat(l))jumpList?.appendChild(i.link)}else{if(!jumpListItems||jumpListItems.length===0){const e=document.createElement("i");e.innerHTML="There are no identifiers on this page.",jumpList?.appendChild(e)}for(const e of jumpListItems??[])e.link.innerHTML=e.name+" <i>"+e.kind+"</i>",jumpList?.appendChild(e.link)}jumpBody&&(jumpBody.scrollTop=0),jumpListItems?.length&&jumpList&&jumpList.children.length>0&&setActiveJumpItem(0)}function setActiveJumpItem(t){const e=jumpList?.children;if(!(!e||!jumpBody)){if(activeJumpItem>=0&&e[activeJumpItem].classList.remove("JumpDialog-active"),t>=e.length&&(t=e.length-1),t>=0){e[t].classList.add("JumpDialog-active");const n=e[t].offsetTop-e[0].offsetTop,o=n+e[t].clientHeight;n<jumpBody.scrollTop?jumpBody.scrollTop=n:o>jumpBody.scrollTop+jumpBody.clientHeight&&(jumpBody.scrollTop=o-jumpBody.clientHeight)}activeJumpItem=t}}function incActiveJumpItem(t){if(activeJumpItem<0)return;let e=activeJumpItem+t;e<0&&(e=0),setActiveJumpItem(e)}jumpFilter?.addEventListener("keyup",function(){jumpFilter.value.toUpperCase()!=lastFilterValue.toUpperCase()&&updateJumpList(jumpFilter.value)}),jumpFilter?.addEventListener("keydown",function(t){const e=38,n=40,o=13;switch(t.which){case e:incActiveJumpItem(-1),t.preventDefault();break;case n:incActiveJumpItem(1),t.preventDefault();break;case o:activeJumpItem>=0&&jumpList&&(jumpList.children[activeJumpItem].click(),t.preventDefault());break}});const shortcutsDialog=document.querySelector(".ShortcutsDialog");document.addEventListener("keypress",function(t){if(jumpDialog?.open||shortcutsDialog?.open)return;const e=t.target,n=e?.tagName;if(n=="INPUT"||n=="SELECT"||n=="TEXTAREA"||e?.contentEditable=="true"||t.metaKey||t.ctrlKey)return;switch(String.fromCharCode(t.which)){case"f":case"F":t.preventDefault(),jumpFilter&&(jumpFilter.value=""),jumpDialog?.showModal(),jumpFilter.focus(),updateJumpList("");break;case"?":shortcutsDialog?.showModal();break}});const jumpOutlineInput=document.querySelector(".js-jumpToInput");jumpOutlineInput&&jumpOutlineInput.addEventListener("click",()=>{jumpFilter&&(jumpFilter.value=""),updateJumpList("")});
//# sourceMappingURL=jump.js.map

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

300
content/static/jump/jump.ts Normal file
Просмотреть файл

@ -0,0 +1,300 @@
/*!
* @license
* Copyright 2019-2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
// This file implements the behavior of the "jump to identifer" dialog for Go
// package documentation, as well as the simple dialog that displays keyboard
// shortcuts.
// The DOM for the dialogs is at the bottom of content/static/html/pages/pkg_doc.tmpl.
// The CSS is in content/static/css/stylesheet.css.
// The dialog is activated by pressing the 'f' key. It presents a list
// (#JumpDialog-list) of all Go identifiers displayed in the documentation.
// Entering text in the dialog's text box (#JumpDialog-filter) restricts the
// list to identifiers containing the text. Clicking on an identifier jumps to
// its documentation.
// This code is based on
// https://go.googlesource.com/gddo/+/refs/heads/master/gddo-server/assets/site.js.
// It was modified to remove the dependence on jquery and bootstrap.
const jumpDialog = document.querySelector<HTMLDialogElement>('.JumpDialog');
const jumpBody = jumpDialog?.querySelector<HTMLDivElement>('.JumpDialog-body');
const jumpList = jumpDialog?.querySelector<HTMLDivElement>('.JumpDialog-list');
const jumpFilter = jumpDialog?.querySelector<HTMLInputElement>('.JumpDialog-input');
const doc = document.querySelector<HTMLDivElement>('.js-documentation');
interface JumpListItem {
link: HTMLAnchorElement;
name: string;
kind: string;
lower: string;
}
let jumpListItems: JumpListItem[] | undefined; // All the identifiers in the doc; computed only once.
// collectJumpListItems returns a list of items, one for each identifier in the
// documentation on the current page.
//
// It uses the data-kind attribute generated in the documentation HTML to find
// the identifiers and their id attributes.
//
// If there are no data-kind attributes, then we have older doc; fall back to
// a less precise method.
function collectJumpListItems() {
const items = [];
if (!doc) return;
for (const el of doc.querySelectorAll('[data-kind]')) {
items.push(newJumpListItem(el));
}
// Clicking on any of the links closes the dialog.
for (const item of items) {
item.link.addEventListener('click', function () {
jumpDialog?.close();
});
}
// Sort case-insensitively by identifier name.
items.sort(function (a, b) {
return a.lower.localeCompare(b.lower);
});
return items;
}
// newJumpListItem creates a new item for the DOM element el.
// An item is an object with:
// - name: the element's id (which is the identifer name)
// - kind: the element's kind (function, variable, etc.),
// - link: a link ('a' tag) to the element
// - lower: the name in lower case, just for sorting
function newJumpListItem(el: Element): JumpListItem {
const a = document.createElement('a');
const name = el.getAttribute('id');
a.setAttribute('href', '#' + name);
a.setAttribute('tabindex', '-1');
a.setAttribute('data-gtmc', 'jump to link');
const kind = el.getAttribute('data-kind');
return {
link: a,
name: name ?? '',
kind: kind ?? '',
lower: name?.toLowerCase() ?? '', // for sorting
};
}
let lastFilterValue: string; // The last contents of the filter text box.
let activeJumpItem = -1; // The index of the currently active item in the list.
// updateJumpList sets the elements of the dialog list to
// everything whose name contains filter.
function updateJumpList(filter: string) {
lastFilterValue = filter;
if (!jumpListItems) {
jumpListItems = collectJumpListItems();
}
setActiveJumpItem(-1);
// Remove all children from list.
while (jumpList?.firstChild) {
jumpList.firstChild.remove();
}
if (filter) {
// A filter is set. We treat the filter as a substring that can appear in
// an item name (case insensitive), and find the following matches - in
// order of priority:
//
// 1. Exact matches (the filter matches the item's name exactly)
// 2. Prefix matches (the item's name starts with filter)
// 3. Infix matches (the filter is a substring of the item's name)
const filterLowerCase = filter.toLowerCase();
const exactMatches = [];
const prefixMatches = [];
const infixMatches = [];
// makeLinkHtml creates the link name HTML for a list item. item is the DOM
// item. item.name.substr(boldStart, boldEnd) will be bolded.
const makeLinkHtml = (item: JumpListItem, boldStart: number, boldEnd: number) => {
return (
item.name.substring(0, boldStart) +
'<b>' +
item.name.substring(boldStart, boldEnd) +
'</b>' +
item.name.substring(boldEnd)
);
};
for (const item of jumpListItems ?? []) {
const nameLowerCase = item.name.toLowerCase();
if (nameLowerCase === filterLowerCase) {
item.link.innerHTML = makeLinkHtml(item, 0, item.name.length);
exactMatches.push(item);
} else if (nameLowerCase.startsWith(filterLowerCase)) {
item.link.innerHTML = makeLinkHtml(item, 0, filter.length);
prefixMatches.push(item);
} else {
const index = nameLowerCase.indexOf(filterLowerCase);
if (index > -1) {
item.link.innerHTML = makeLinkHtml(item, index, index + filter.length);
infixMatches.push(item);
}
}
}
for (const item of exactMatches.concat(prefixMatches).concat(infixMatches)) {
jumpList?.appendChild(item.link);
}
} else {
if (!jumpListItems || jumpListItems.length === 0) {
const msg = document.createElement('i');
msg.innerHTML = 'There are no identifiers on this page.';
jumpList?.appendChild(msg);
}
// No filter set; display all items in their existing order.
for (const item of jumpListItems ?? []) {
item.link.innerHTML = item.name + ' <i>' + item.kind + '</i>';
jumpList?.appendChild(item.link);
}
}
if (jumpBody) {
jumpBody.scrollTop = 0;
}
if (jumpListItems?.length && jumpList && jumpList.children.length > 0) {
setActiveJumpItem(0);
}
}
// Set the active jump item to n.
function setActiveJumpItem(n: number) {
const cs = jumpList?.children as HTMLCollectionOf<HTMLElement> | null | undefined;
if (!cs || !jumpBody) {
return;
}
if (activeJumpItem >= 0) {
cs[activeJumpItem].classList.remove('JumpDialog-active');
}
if (n >= cs.length) {
n = cs.length - 1;
}
if (n >= 0) {
cs[n].classList.add('JumpDialog-active');
// Scroll so the active item is visible.
// For some reason cs[n].scrollIntoView() doesn't behave as I'd expect:
// it moves the entire dialog box in the viewport.
// Get the top and bottom of the active item relative to jumpBody.
const activeTop = cs[n].offsetTop - cs[0].offsetTop;
const activeBottom = activeTop + cs[n].clientHeight;
if (activeTop < jumpBody.scrollTop) {
// Off the top; scroll up.
jumpBody.scrollTop = activeTop;
} else if (activeBottom > jumpBody.scrollTop + jumpBody.clientHeight) {
// Off the bottom; scroll down.
jumpBody.scrollTop = activeBottom - jumpBody.clientHeight;
}
}
activeJumpItem = n;
}
// Increment the activeJumpItem by delta.
function incActiveJumpItem(delta: number) {
if (activeJumpItem < 0) {
return;
}
let n = activeJumpItem + delta;
if (n < 0) {
n = 0;
}
setActiveJumpItem(n);
}
// Pressing a key in the filter updates the list (if the filter actually changed).
jumpFilter?.addEventListener('keyup', function () {
if (jumpFilter.value.toUpperCase() != lastFilterValue.toUpperCase()) {
updateJumpList(jumpFilter.value);
}
});
// Pressing enter in the filter selects the first element in the list.
jumpFilter?.addEventListener('keydown', function (event) {
const upArrow = 38;
const downArrow = 40;
const enterKey = 13;
switch (event.which) {
case upArrow:
incActiveJumpItem(-1);
event.preventDefault();
break;
case downArrow:
incActiveJumpItem(1);
event.preventDefault();
break;
case enterKey:
if (activeJumpItem >= 0) {
if (jumpList) {
(jumpList.children[activeJumpItem] as HTMLElement).click();
event.preventDefault();
}
}
break;
}
});
const shortcutsDialog = document.querySelector<HTMLDialogElement>('.ShortcutsDialog');
// Keyboard shortcuts:
// - Pressing '/' focuses the search box
// - Pressing 'f' or 'F' opens the jump-to-identifier dialog.
// - Pressing '?' opens up the shortcut dialog.
// Ignore a keypress if a dialog is already open, or if it is pressed on a
// component that wants to consume it.
document.addEventListener('keypress', function (e) {
if (jumpDialog?.open || shortcutsDialog?.open) {
return;
}
const target = e.target as HTMLElement | null;
const t = target?.tagName;
if (t == 'INPUT' || t == 'SELECT' || t == 'TEXTAREA') {
return;
}
if (target?.contentEditable == 'true') {
return;
}
if (e.metaKey || e.ctrlKey) {
return;
}
const ch = String.fromCharCode(e.which);
switch (ch) {
case 'f':
case 'F':
e.preventDefault();
if (jumpFilter) {
jumpFilter.value = '';
}
jumpDialog?.showModal();
jumpFilter.focus();
updateJumpList('');
break;
case '?':
shortcutsDialog?.showModal();
break;
}
});
const jumpOutlineInput = document.querySelector('.js-jumpToInput');
if (jumpOutlineInput) {
jumpOutlineInput.addEventListener('click', () => {
if (jumpFilter) {
jumpFilter.value = '';
}
updateJumpList('');
});
}

Просмотреть файл

@ -0,0 +1,7 @@
/*!
* @license
* Copyright 2019-2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/const searchInput=document.querySelector(".js-searchFocus"),canonicalURLPath=document.querySelector(".js-canonicalURLPath")?.dataset.canonicalUrlPath;document.addEventListener("keydown",e=>{const t=e.target?.tagName;if(!(t==="INPUT"||t==="SELECT"||t==="TEXTAREA")&&!e.target?.isContentEditable&&!(e.metaKey||e.ctrlKey))switch(e.key){case"y":canonicalURLPath&&canonicalURLPath!==""&&window.history.replaceState(null,"",canonicalURLPath);break;case"/":searchInput&&!window.navigator.userAgent.includes("Firefox")&&(e.preventDefault(),searchInput.focus());break}});
//# sourceMappingURL=keyboard.js.map

Просмотреть файл

@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["keyboard.ts"],
"sourcesContent": ["/*!\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// Keyboard shortcuts:\n// - Pressing '/' focuses the search box\n// - Pressing 'y' changes the browser URL to the canonical URL\n// without triggering a reload.\n\nconst searchInput = document.querySelector<HTMLInputElement>('.js-searchFocus');\nconst canonicalURLPath = document.querySelector<HTMLDivElement>('.js-canonicalURLPath')?.dataset[\n 'canonicalUrlPath'\n];\n\ndocument.addEventListener('keydown', e => {\n // TODO(golang.org/issue/40246): consolidate keyboard shortcut behavior across the site.\n const t = (e.target as HTMLElement)?.tagName;\n if (t === 'INPUT' || t === 'SELECT' || t === 'TEXTAREA') {\n return;\n }\n if ((e.target as HTMLElement)?.isContentEditable) {\n return;\n }\n if (e.metaKey || e.ctrlKey) {\n return;\n }\n switch (e.key) {\n case 'y':\n if (canonicalURLPath && canonicalURLPath !== '') {\n window.history.replaceState(null, '', canonicalURLPath);\n }\n break;\n case '/':\n // Favoring the Firefox quick find feature over search input\n // focus. See: https://github.com/golang/go/issues/41093.\n if (searchInput && !window.navigator.userAgent.includes('Firefox')) {\n e.preventDefault();\n searchInput.focus();\n }\n break;\n }\n});\n"],
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAYA,KAAM,aAAc,SAAS,cAAgC,mBACvD,iBAAmB,SAAS,cAA8B,yBAAyB,QACvF,iBAGF,SAAS,iBAAiB,UAAW,GAAK,CAExC,KAAM,GAAK,EAAE,QAAwB,QACrC,GAAI,MAAM,SAAW,IAAM,UAAY,IAAM,aAGxC,GAAE,QAAwB,mBAG3B,IAAE,SAAW,EAAE,SAGnB,OAAQ,EAAE,SACH,IACH,AAAI,kBAAoB,mBAAqB,IAC3C,OAAO,QAAQ,aAAa,KAAM,GAAI,kBAExC,UACG,IAGH,AAAI,aAAe,CAAC,OAAO,UAAU,UAAU,SAAS,YACtD,GAAE,iBACF,YAAY,SAEd",
"names": []
}

Просмотреть файл

@ -0,0 +1,45 @@
/*!
* @license
* Copyright 2019-2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
// Keyboard shortcuts:
// - Pressing '/' focuses the search box
// - Pressing 'y' changes the browser URL to the canonical URL
// without triggering a reload.
const searchInput = document.querySelector<HTMLInputElement>('.js-searchFocus');
const canonicalURLPath = document.querySelector<HTMLDivElement>('.js-canonicalURLPath')?.dataset[
'canonicalUrlPath'
];
document.addEventListener('keydown', e => {
// TODO(golang.org/issue/40246): consolidate keyboard shortcut behavior across the site.
const t = (e.target as HTMLElement)?.tagName;
if (t === 'INPUT' || t === 'SELECT' || t === 'TEXTAREA') {
return;
}
if ((e.target as HTMLElement)?.isContentEditable) {
return;
}
if (e.metaKey || e.ctrlKey) {
return;
}
switch (e.key) {
case 'y':
if (canonicalURLPath && canonicalURLPath !== '') {
window.history.replaceState(null, '', canonicalURLPath);
}
break;
case '/':
// Favoring the Firefox quick find feature over search input
// focus. See: https://github.com/golang/go/issues/41093.
if (searchInput && !window.navigator.userAgent.includes('Firefox')) {
e.preventDefault();
searchInput.focus();
}
break;
}
});

Просмотреть файл

@ -0,0 +1,64 @@
<!--
Copyright 2021 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{define "title"}}<title>License Policy · pkg.go.dev</title>{{end}}
{{define "main"}}
<style>
.LicenseTypes-list {
line-height: 1.25rem;
list-style: initial;
padding-left: 2.25rem;
}
</style>
<main class="go-Container">
<div class="go-Content">
<h1 data-test-id="license-policy-heading">License Disclaimer</h1>
<p>
The Go website displays license information in order to help users evaluate
packages for their intended use. Licenses are detected using heuristics
based on their file name and contents. We hope this information is helpful,
but this is not legal advice and we do not make any guarantees regarding
the accuracy of our license detection.
</p>
<p>
If we are not able to detect one of the licenses below, only
limited package and module information will be made available. If you are
a package author seeking to make your content available on the Go
website, please be aware that our detection algorithms can be affected by
any modifications of the license text, or by the use of an uncommon
license file name.
</p>
<p>
We currently use
<a href="https://pkg.go.dev/github.com/google/licensecheck">github.com/google/licensecheck</a>
for license detection, and look for licenses in files with the following names:
{{commaseparate .LicenseFileNames}}. The match is case-insensitive.
</p>
<p>
We currently detect and recognize the following licenses:
<ul class="LicenseTypes-list">
{{range .LicenseTypes -}}
<li>
{{if .URL -}}
<a href="{{.URL}}" target="_blank" rel="noopener">{{.Name}}</a>
{{else}}
{{.Name}}
{{- end}}
</li>
{{- end}}
</ul>
</p>
<p>
If you use a package whose license is not detected, please inform the package author.
If you are a package author who believes a license for one of your packages
should have been detected and was not, please check for discrepancies between your license
and the official text. If you still believe there is an error, please
<a href="https://golang.org/s/pkgsite-feedback">file an issue</a>.
</p>
</div>
</main>
{{end}}

Просмотреть файл

@ -0,0 +1,7 @@
/**
* @license
* Copyright 2021 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/const r=3.5;export class MainLayoutController{constructor(e,t){this.mainHeader=e;this.mainNav=t;this.handleDoubleClick=e=>{e.target===this.mainHeader.lastElementChild&&(window.getSelection()?.removeAllRanges(),window.scrollTo({top:0,behavior:"smooth"}))};this.handleResize=()=>{const e=(t,i)=>document.documentElement.style.setProperty(t,i);e("--js-unit-header-height","0"),setTimeout(()=>{const t=(this.mainHeader?.getBoundingClientRect().height??0)/16;e("--js-unit-header-height",`${t}rem`),e("--js-sticky-header-height",`${r}rem`),e("--js-unit-header-top",`${(t-r)*-1}rem`)})};this.headerObserver=new IntersectionObserver(([i])=>{if(i.intersectionRatio<1)for(const n of document.querySelectorAll('[class^="go-Main-header"'))n.setAttribute("data-fixed","true");else{for(const n of document.querySelectorAll('[class^="go-Main-header"'))n.removeAttribute("data-fixed");this.handleResize(null)}},{threshold:1}),this.navObserver=new IntersectionObserver(([i])=>{i.intersectionRatio<1?(this.mainNav?.classList.add("go-Main-nav--fixed"),this.mainNav?.setAttribute("data-fixed","true")):(this.mainNav?.classList.remove("go-Main-nav--fixed"),this.mainNav?.removeAttribute("data-fixed"))},{threshold:1,rootMargin:`-${(r??0)*16+10}px`}),this.init()}init(){if(this.handleResize(null),window.addEventListener("resize",this.handleResize),this.mainHeader?.addEventListener("dblclick",this.handleDoubleClick),this.mainHeader.hasChildNodes()){const e=document.createElement("div");this.mainHeader?.prepend(e),this.headerObserver.observe(e)}if(this.mainNav.hasChildNodes()){const e=document.createElement("div");this.mainNav?.prepend(e),this.navObserver.observe(e)}}}const a=s=>document.querySelector(s);new MainLayoutController(a(".js-mainHeader"),a(".js-mainNav"));
//# sourceMappingURL=main-layout.js.map

Просмотреть файл

@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["main-layout.ts"],
"sourcesContent": ["/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * MainLayoutController calculates dynamic height values for header elements\n * to support variable size sticky positioned elements in the header so that\n * banners and breadcumbs may overflow to multiple lines.\n */\n\nconst headerHeight = 3.5;\n\nexport class MainLayoutController {\n private headerObserver: IntersectionObserver;\n private navObserver: IntersectionObserver;\n\n constructor(private mainHeader?: Element | null, private mainNav?: Element | null) {\n this.headerObserver = new IntersectionObserver(\n ([e]) => {\n if (e.intersectionRatio < 1) {\n for (const x of document.querySelectorAll('[class^=\"go-Main-header\"')) {\n x.setAttribute('data-fixed', 'true');\n }\n } else {\n for (const x of document.querySelectorAll('[class^=\"go-Main-header\"')) {\n x.removeAttribute('data-fixed');\n }\n this.handleResize(null);\n }\n },\n { threshold: 1 }\n );\n this.navObserver = new IntersectionObserver(\n ([e]) => {\n if (e.intersectionRatio < 1) {\n this.mainNav?.classList.add('go-Main-nav--fixed');\n this.mainNav?.setAttribute('data-fixed', 'true');\n } else {\n this.mainNav?.classList.remove('go-Main-nav--fixed');\n this.mainNav?.removeAttribute('data-fixed');\n }\n },\n { threshold: 1, rootMargin: `-${(headerHeight ?? 0) * 16 + 10}px` }\n );\n this.init();\n }\n\n private init() {\n this.handleResize(null);\n window.addEventListener('resize', this.handleResize);\n this.mainHeader?.addEventListener('dblclick', this.handleDoubleClick);\n if (this.mainHeader.hasChildNodes()) {\n const headerSentinel = document.createElement('div');\n this.mainHeader?.prepend(headerSentinel);\n this.headerObserver.observe(headerSentinel);\n }\n if (this.mainNav.hasChildNodes()) {\n const navSentinel = document.createElement('div');\n this.mainNav?.prepend(navSentinel);\n this.navObserver.observe(navSentinel);\n }\n }\n\n private handleDoubleClick: EventListener = e => {\n const target = e.target;\n if (target === this.mainHeader.lastElementChild) {\n window.getSelection()?.removeAllRanges();\n window.scrollTo({ top: 0, behavior: 'smooth' });\n }\n };\n\n private handleResize: EventListener = () => {\n const setProp = (name: string, value: string) =>\n document.documentElement.style.setProperty(name, value);\n setProp('--js-unit-header-height', '0');\n setTimeout(() => {\n const mainHeaderHeight = (this.mainHeader?.getBoundingClientRect().height ?? 0) / 16;\n setProp('--js-unit-header-height', `${mainHeaderHeight}rem`);\n setProp('--js-sticky-header-height', `${headerHeight}rem`);\n setProp('--js-unit-header-top', `${(mainHeaderHeight - headerHeight) * -1}rem`);\n });\n };\n}\n\nconst el = <T extends HTMLElement>(selector: string) => document.querySelector<T>(selector);\nnew MainLayoutController(el('.js-mainHeader'), el('.js-mainNav'));\n"],
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAaA,KAAM,GAAe,IAEd,iCAA2B,CAIhC,YAAoB,EAAqC,EAA0B,CAA/D,kBAAqC,eA+CjD,uBAAmC,GAAK,CAE9C,AAAI,AADW,EAAE,SACF,KAAK,WAAW,kBAC7B,QAAO,gBAAgB,kBACvB,OAAO,SAAS,CAAE,IAAK,EAAG,SAAU,aAIhC,kBAA8B,IAAM,CAC1C,KAAM,GAAU,CAAC,EAAc,IAC7B,SAAS,gBAAgB,MAAM,YAAY,EAAM,GACnD,EAAQ,0BAA2B,KACnC,WAAW,IAAM,CACf,KAAM,GAAoB,MAAK,YAAY,wBAAwB,QAAU,GAAK,GAClF,EAAQ,0BAA2B,GAAG,QACtC,EAAQ,4BAA6B,GAAG,QACxC,EAAQ,uBAAwB,GAAI,GAAmB,GAAgB,YA9DzE,KAAK,eAAiB,GAAI,sBACxB,CAAC,CAAC,KAAO,CACP,GAAI,EAAE,kBAAoB,EACxB,SAAW,KAAK,UAAS,iBAAiB,4BACxC,EAAE,aAAa,aAAc,YAE1B,CACL,SAAW,KAAK,UAAS,iBAAiB,4BACxC,EAAE,gBAAgB,cAEpB,KAAK,aAAa,QAGtB,CAAE,UAAW,IAEf,KAAK,YAAc,GAAI,sBACrB,CAAC,CAAC,KAAO,CACP,AAAI,EAAE,kBAAoB,EACxB,MAAK,SAAS,UAAU,IAAI,sBAC5B,KAAK,SAAS,aAAa,aAAc,SAEzC,MAAK,SAAS,UAAU,OAAO,sBAC/B,KAAK,SAAS,gBAAgB,gBAGlC,CAAE,UAAW,EAAG,WAAY,IAAK,IAAgB,GAAK,GAAK,SAE7D,KAAK,OAGC,MAAO,CAIb,GAHA,KAAK,aAAa,MAClB,OAAO,iBAAiB,SAAU,KAAK,cACvC,KAAK,YAAY,iBAAiB,WAAY,KAAK,mBAC/C,KAAK,WAAW,gBAAiB,CACnC,KAAM,GAAiB,SAAS,cAAc,OAC9C,KAAK,YAAY,QAAQ,GACzB,KAAK,eAAe,QAAQ,GAE9B,GAAI,KAAK,QAAQ,gBAAiB,CAChC,KAAM,GAAc,SAAS,cAAc,OAC3C,KAAK,SAAS,QAAQ,GACtB,KAAK,YAAY,QAAQ,KAyB/B,KAAM,GAAK,AAAwB,GAAqB,SAAS,cAAiB,GAClF,GAAI,sBAAqB,EAAG,kBAAmB,EAAG",
"names": []
}

Просмотреть файл

@ -4,27 +4,36 @@
license that can be found in the LICENSE file.
-->
{{define "main"}}
{{define "pre-content"}}
{{block "main-styles".}}{{end}}
{{end}}
{{define "main"}}
<main class="go-Main">
<div class="go-Main-banner">
{{block "main-banner" .}}{{end}}
<div class="go-Main-banner" role="alert">
{{- block "main-banner" .}}{{end -}}
</div>
<header class="go-Main-header go-Main-header--sticky js-mainHeader">
{{block "main-header" .}}{{end}}
<header class="go-Main-header js-mainHeader">
{{- block "main-header" .}}{{end -}}
</header>
<aside class="go-Main-aside">
{{- block "main-aside" .}}{{end -}}
</aside>
<nav class="go-Main-nav go-Main-nav--sticky js-mainNav" aria-label="Outline">
{{block "main-nav" .}}{{end}}
{{- block "main-nav" .}}{{end -}}
</nav>
<article class="go-Main-article js-mainContent">
{{block "main-content" .}}{{end}}
{{- block "main-content" .}}{{end -}}
</article>
<aside class="go-Main-aside">
{{block "main-aside" .}}{{end}}
</aside>
<footer class="go-Main-footer">
{{block "main-footer" .}}{{end}}
{{- block "main-footer" .}}{{end -}}
</footer>
</main>
{{block "main-scripts" .}}{{end}}
{{end}}
{{define "post-content"}}
{{block "main-scripts" .}}{{end}}
<script>
loadScript('/static/main-layout/main-layout.js')
</script>
{{end}}

Просмотреть файл

@ -10,21 +10,24 @@
* to support variable size sticky positioned elements in the header so that
* banners and breadcumbs may overflow to multiple lines.
*/
const headerHeight = 3.5;
export class MainLayoutController {
private stickyHeader: Element | null;
private stickyNav: Element | null;
private headerObserver: IntersectionObserver;
private navObserver: IntersectionObserver;
constructor(private mainHeader?: Element | null, private mainNav?: Element | null) {
this.stickyHeader = mainHeader.querySelector('.js-stickyHeader') ?? mainHeader.lastElementChild;
this.stickyNav = mainNav.lastElementChild;
this.headerObserver = new IntersectionObserver(
([e]) => {
if (e.intersectionRatio < 1) {
this.mainHeader?.classList.add('go-Main-header--fixed');
for (const x of document.querySelectorAll('[class^="go-Main-header"')) {
x.setAttribute('data-fixed', 'true');
}
} else {
this.mainHeader?.classList.remove('go-Main-header--fixed');
for (const x of document.querySelectorAll('[class^="go-Main-header"')) {
x.removeAttribute('data-fixed');
}
this.handleResize(null);
}
},
@ -34,11 +37,13 @@ export class MainLayoutController {
([e]) => {
if (e.intersectionRatio < 1) {
this.mainNav?.classList.add('go-Main-nav--fixed');
this.mainNav?.setAttribute('data-fixed', 'true');
} else {
this.mainNav?.classList.remove('go-Main-nav--fixed');
this.mainNav?.removeAttribute('data-fixed');
}
},
{ threshold: 1, rootMargin: `-${this.stickyHeader?.clientHeight ?? 0 + 1}px` }
{ threshold: 1, rootMargin: `-${(headerHeight ?? 0) * 16 + 10}px` }
);
this.init();
}
@ -46,20 +51,22 @@ export class MainLayoutController {
private init() {
this.handleResize(null);
window.addEventListener('resize', this.handleResize);
this.stickyHeader?.addEventListener('dblclick', this.handleDoubleClick);
const headerSentinel = document.createElement('div');
this.mainHeader?.prepend(headerSentinel);
this.headerObserver.observe(headerSentinel);
const navSentinel = document.createElement('div');
this.mainNav?.prepend(navSentinel);
this.navObserver.observe(navSentinel);
this.mainHeader?.addEventListener('dblclick', this.handleDoubleClick);
if (this.mainHeader.hasChildNodes()) {
const headerSentinel = document.createElement('div');
this.mainHeader?.prepend(headerSentinel);
this.headerObserver.observe(headerSentinel);
}
if (this.mainNav.hasChildNodes()) {
const navSentinel = document.createElement('div');
this.mainNav?.prepend(navSentinel);
this.navObserver.observe(navSentinel);
}
}
private handleDoubleClick: EventListener = e => {
const target = e.target;
if (target === this.stickyHeader) {
if (target === this.mainHeader.lastElementChild) {
window.getSelection()?.removeAllRanges();
window.scrollTo({ top: 0, behavior: 'smooth' });
}
@ -68,14 +75,15 @@ export class MainLayoutController {
private handleResize: EventListener = () => {
const setProp = (name: string, value: string) =>
document.documentElement.style.setProperty(name, value);
setProp('--js-main-header-height', '0');
setProp('--js-unit-header-height', '0');
setTimeout(() => {
const mainHeaderHeight = (this.mainHeader?.getBoundingClientRect().height ?? 0) / 16;
const stickyHeaderHeight = (this.stickyHeader?.getBoundingClientRect().height ?? 0) / 16;
const stickyNavHeight = (this.stickyNav?.getBoundingClientRect().height ?? 0) / 16;
setProp('--js-main-header-height', `${mainHeaderHeight}rem`);
setProp('--js-sticky-header-height', `${stickyHeaderHeight}rem`);
setProp('--js-sticky-nav-height', `${stickyNavHeight}rem`);
setProp('--js-unit-header-height', `${mainHeaderHeight}rem`);
setProp('--js-sticky-header-height', `${headerHeight}rem`);
setProp('--js-unit-header-top', `${(mainHeaderHeight - headerHeight) * -1}rem`);
});
};
}
const el = <T extends HTMLElement>(selector: string) => document.querySelector<T>(selector);
new MainLayoutController(el('.js-mainHeader'), el('.js-mainNav'));

Просмотреть файл

@ -16,14 +16,11 @@
'nav'
'article'
'footer';
grid-template-columns: 100%;
grid-template-rows: repeat(6, min-content);
min-height: 32rem;
}
.go-Main > *:not(.go-Main-banner) {
padding: 0 var(--gutter);
}
.go-Main > *:empty {
padding: 0;
}
.go-Main-banner {
grid-area: banner;
}
@ -32,90 +29,148 @@
border-bottom: var(--border);
font-size: 0.875rem;
grid-area: header;
min-height: var(--js-unit-header-height);
padding: 0 var(--gutter);
position: sticky;
top: var(--js-unit-header-top, 0);
transition: box-shadow 0.25s linear;
z-index: 10;
}
.go-Main-nav {
background-color: var(--color-background);
border-bottom: var(--border);
font-size: 0.875rem;
grid-area: nav;
padding: 0 var(--gutter);
}
.go-Main-article {
background-color: var(--color-background);
grid-area: article;
margin-bottom: 5rem;
margin-top: 1rem;
margin: var(--gap) 0 5rem 0;
min-height: 32rem;
padding: 0 var(--gutter);
}
.go-Main-aside {
background-color: var(--color-background);
font-size: 0.875rem;
grid-area: aside;
padding: 1rem var(--gutter);
}
.go-Main-footer {
background-color: var(--color-background);
grid-area: footer;
padding: 0 var(--gutter);
}
.go-Main-header--sticky {
min-height: calc(var(--js-main-header-height, 0));
position: sticky;
top: calc(var(--js-main-header-height, 0) * -1 + var(--js-sticky-header-height, 0));
transition: box-shadow 0.25s linear;
white-space: nowrap;
z-index: 2;
.go-Main > *:empty {
border: none;
margin: 0;
padding: 0;
}
.go-Main-header--sticky > :last-child {
.go-Main-headerBreadcrumb {
margin-top: 1rem;
}
.go-Main-headerContent {
margin-bottom: 1rem;
position: sticky;
top: 0;
}
.go-Main-header--fixed {
box-shadow: none;
}
.go-Main-header > .go-Breadcrumb {
margin-top: 0.75rem;
}
.go-Main-headerContent {
.go-Main-headerContent[data-fixed] {
align-items: center;
display: flex;
gap: var(--gap);
min-height: 3.5rem;
vertical-align: middle;
margin-bottom: 0;
min-height: 0;
}
@media screen and (min-width: 80rem) {
.go-Main-headerContent[data-fixed] {
justify-content: space-between;
}
}
.go-Main-headerContent[data-fixed] .go-Chip {
display: none;
}
.go-Main-headerTitle {
font-size: 1.5rem;
line-height: 1.5rem;
align-items: center;
display: flex;
gap: 0.5rem;
height: 3.5rem;
}
.go-Main-headerTitle .go-Clipboard {
display: none;
}
.go-Main-headerTitle[data-fixed] .go-Clipboard {
display: initial;
}
.go-Main-headerLogo {
--logo-height: 1.75rem;
--logo-width: calc(var(--logo-height) / 0.3768);
align-items: center;
display: flex;
margin-right: calc(var(--gap) * -1);
margin-right: -0.5rem;
opacity: 0;
transition: opacity 0.25s ease-in-out, width 0.25s ease-out;
visibility: hidden;
width: 0;
}
.go-Main-headerLogo > img {
.go-Main-headerLogo img {
height: var(--logo-height);
margin: -1rem 0;
width: var(--logo-width);
}
.go-Main-header--fixed .go-Main-headerLogo {
.go-Main-headerLogo[data-fixed] {
margin-right: 0;
opacity: 1;
visibility: visible;
width: var(--logo-width);
}
.go-Main-headerDetails {
display: flex;
flex-direction: column;
flex-wrap: wrap;
white-space: nowrap;
}
@media only screen and (min-width: 50rem) {
.go-Main-headerDetails {
display: flex;
flex-direction: row;
gap: 0 1rem;
}
}
.go-Main-headerDetails[data-fixed] {
display: none;
}
@media screen and (min-width: 80rem) {
:root:not([data-layout='compact']) .go-Main-headerDetails[data-fixed] {
display: flex;
}
}
.go-Main-headerDetailItem {
color: var(--gray-4);
display: inline;
font-size: 0.875rem;
height: 1.75rem;
line-height: 1.75rem;
}
@media only screen and (min-width: 50rem) {
.go-Main-headerDetailItem:not(:last-of-type)::after {
content: '|';
padding-left: 1rem;
}
}
.go-Main-nav--sticky {
position: sticky;
top: var(--js-sticky-header-height, 0);
top: var(--js-sticky-header-height, 3.5rem);
transition: box-shadow 0.25s linear;
z-index: 1;
}
.go-Main-nav--fixed {
border-top: initial;
box-shadow: var(--box-shadow);
}
@ -124,7 +179,7 @@
margin-top: var(--gap);
overflow-y: auto;
position: sticky;
top: calc(var(--js-sticky-header-height, 0) + var(--gap));
top: calc(var(--js-sticky-header-height, 3.5rem) + 1rem);
}
.go-Main-navMobile {
display: flex;
@ -136,6 +191,7 @@
}
.go-Main-navMobile .go-Select {
padding-left: 1.75rem;
width: 100%;
}
.go-Main-navMobile .go-Label::before {
background: url(/static/icon/list_gm_grey_24dp.svg);
@ -161,46 +217,66 @@
}
@media screen and (min-width: 80rem) {
:root:not([data-layout='stacked']) .go-Main {
:root[data-layout='responsive'] .go-Main {
grid-template-areas:
'banner banner'
'header header'
'aside aside'
'nav article'
'footer footer';
grid-template-columns: minmax(25%, 1fr) minmax(0, 4fr);
grid-template-columns: 21.5% minmax(0, auto);
grid-template-rows: repeat(5, min-content);
}
:root:not([data-layout='stacked']) .go-Main-nav {
:root[data-layout='responsive'] .go-Main-nav {
border-bottom: none;
border-top: none;
padding: 0 0 0 var(--gutter);
}
:root:not([data-layout='stacked']) .go-Main-header--fixed {
:root[data-layout='responsive'] .go-Main-header[data-fixed] {
box-shadow: var(--box-shadow);
}
:root:not([data-layout='stacked']) .go-Main-nav--sticky {
:root[data-layout='responsive'] .go-Main-article {
border-bottom: none;
border-top: none;
margin: var(--gap) 0 5rem var(--gap);
padding: 0 var(--gutter) 0 0;
}
:root[data-layout='responsive'] .go-Main-aside {
border-bottom: var(--border);
}
:root[data-layout='responsive'] .go-Main-nav--sticky {
position: initial;
}
:root:not([data-layout='stacked']) .go-Main-nav--fixed {
:root[data-layout='responsive'] .go-Main-nav--fixed {
box-shadow: none;
}
:root:not([data-layout='stacked']) .go-Main-navDesktop {
:root[data-layout='responsive'] .go-Main-navDesktop {
display: block;
}
:root:not([data-layout='stacked']) .go-Main-navMobile {
:root[data-layout='responsive'] .go-Main-navMobile {
display: none;
}
}
@media screen and (min-width: 95rem) {
:root:not([data-layout='stacked']) .go-Main {
@media screen and (min-width: 112rem) {
:root[data-layout='responsive'] .go-Main {
grid-template-areas:
'banner banner banner'
'header header header'
'nav article aside'
'footer footer footer';
grid-template-columns: minmax(10%, 1fr) minmax(0, 4fr) minmax(10%, 1fr);
grid-template-columns: minmax(17.5%, 1fr) minmax(0, 4fr) minmax(17.5%, 1fr);
grid-template-rows: repeat(4, min-content);
}
:root[data-layout='responsive'] .go-Main-article {
margin: var(--gap) var(--gap) 5rem;
padding: 0;
}
:root[data-layout='responsive'] .go-Main-aside {
border-bottom: none;
margin: var(--gap) 0 0 0;
padding: 0 var(--gutter) 0 0;
}
}
@media screen and (min-width: 80rem) {
@ -219,13 +295,13 @@
align-items: center;
border-bottom: var(--border);
display: flex;
top: calc((var(--js-main-header-height, 0) - var(--js-sticky-header-height, 0)) * -1);
top: calc((var(--js-main-header-height, 0) - var(--js-sticky-header-height, 3.5rem)) * -1);
}
:root[data-layout='compact'] .go-Main-header--fixed {
:root[data-layout='compact'] .go-Main-header[data-fixed] {
box-shadow: none;
}
:root[data-layout='compact'] .go-Main-nav--sticky {
height: var(--js-sticky-header-height, 0);
height: var(--js-sticky-header-height, 3.5rem);
position: sticky;
top: 0;
}

Просмотреть файл

@ -25,11 +25,7 @@
.go-Message > .go-Icon {
vertical-align: text-top;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme='light']) .go-Message--warning > .go-Icon {
filter: none;
}
}
[data-theme='dark'] .go-Message--warning > .go-Icon {
filter: none;
}

Просмотреть файл

@ -15,7 +15,7 @@
color: var(--color-text);
display: flex;
flex-direction: column;
gap: var(--gap);
gap: 1rem;
max-height: 100%;
max-width: 100%;
position: fixed;
@ -37,7 +37,7 @@
border-bottom: var(--border);
display: flex;
justify-content: space-between;
padding-bottom: var(--gap);
padding-bottom: 1rem;
}
.go-Modal-header h2 {
font-size: 1.15rem;

Просмотреть файл

@ -0,0 +1,7 @@
/**
* @license
* Copyright 2021 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/export class ModalController{constructor(e){this.el=e;!window.HTMLDialogElement&&!e.showModal&&import("../../../third_party/dialog-polyfill/dialog-polyfill.esm.js").then(({default:l})=>{l.registerDialog(e)});const t=e.id,o=document.querySelector(`[aria-controls="${t}"]`);o&&o.addEventListener("click",()=>{this.el.showModal?this.el.showModal():this.el.open=!0,e.querySelector("input")?.focus()});for(const l of this.el.querySelectorAll("[data-modal-close]"))l.addEventListener("click",()=>{this.el.close?this.el.close():this.el.open=!1})}}
//# sourceMappingURL=modal.js.map

Просмотреть файл

@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["modal.ts"],
"sourcesContent": ["/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * ModalController registers a dialog element with the polyfill if\n * necessary for the current browser, add adds event listeners to\n * close and open modals.\n */\nexport class ModalController {\n constructor(private el: HTMLDialogElement) {\n // Only load the dialog polyfill if necessary for the environment.\n if (!window.HTMLDialogElement && !el.showModal) {\n import('../../../third_party/dialog-polyfill/dialog-polyfill.esm.js').then(\n ({ default: polyfill }) => {\n polyfill.registerDialog(el);\n }\n );\n }\n const id = el.id;\n const button = document.querySelector<HTMLButtonElement>(`[aria-controls=\"${id}\"]`);\n if (button) {\n button.addEventListener('click', () => {\n if (this.el.showModal) {\n this.el.showModal();\n } else {\n this.el.open = true;\n }\n el.querySelector('input')?.focus();\n });\n }\n for (const close of this.el.querySelectorAll<HTMLButtonElement>('[data-modal-close]')) {\n close.addEventListener('click', () => {\n if (this.el.close) {\n this.el.close();\n } else {\n this.el.open = false;\n }\n });\n }\n }\n}\n"],
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAYO,4BAAsB,CAC3B,YAAoB,EAAuB,CAAvB,UAElB,AAAI,CAAC,OAAO,mBAAqB,CAAC,EAAG,WACnC,AAAO,sEAA+D,KACpE,CAAC,CAAE,QAAS,KAAe,CACzB,EAAS,eAAe,KAI9B,KAAM,GAAK,EAAG,GACR,EAAS,SAAS,cAAiC,mBAAmB,OAC5E,AAAI,GACF,EAAO,iBAAiB,QAAS,IAAM,CACrC,AAAI,KAAK,GAAG,UACV,KAAK,GAAG,YAER,KAAK,GAAG,KAAO,GAEjB,EAAG,cAAc,UAAU,UAG/B,SAAW,KAAS,MAAK,GAAG,iBAAoC,sBAC9D,EAAM,iBAAiB,QAAS,IAAM,CACpC,AAAI,KAAK,GAAG,MACV,KAAK,GAAG,QAER,KAAK,GAAG,KAAO",
"names": []
}

Просмотреть файл

@ -8,7 +8,7 @@ The size modifer class is optional. The base modal will grow to fit the inner co
```html
<dialog id="example-modal-id1" class="go-Modal go-Modal--sm js-modal">
<form action="#modals">
<form method="dialog">
<div class="go-Modal-header">
<h2>Small Modal</h2>
<button
@ -42,7 +42,7 @@ The size modifer class is optional. The base modal will grow to fit the inner co
```html
<dialog id="example-modal-id2" class="go-Modal go-Modal--md js-modal">
<form action="#modals">
<form method="dialog">
<div class="go-Modal-header">
<h2>Medium Modal</h2>
<button
@ -76,7 +76,7 @@ The size modifer class is optional. The base modal will grow to fit the inner co
```html
<dialog id="example-modal-id3" class="go-Modal go-Modal--lg js-modal">
<form action="#modals">
<form method="dialog">
<div class="go-Modal-header">
<h2>Large Modal</h2>
<button

Просмотреть файл

@ -5,8 +5,6 @@
* license that can be found in the LICENSE file.
*/
import polyfill from '../../../third_party/dialog-polyfill/dialog-polyfill.js';
/**
* ModalController registers a dialog element with the polyfill if
* necessary for the current browser, add adds event listeners to
@ -14,8 +12,13 @@ import polyfill from '../../../third_party/dialog-polyfill/dialog-polyfill.js';
*/
export class ModalController {
constructor(private el: HTMLDialogElement) {
// Only load the dialog polyfill if necessary for the environment.
if (!window.HTMLDialogElement && !el.showModal) {
polyfill.registerDialog(el);
import('../../../third_party/dialog-polyfill/dialog-polyfill.esm.js').then(
({ default: polyfill }) => {
polyfill.registerDialog(el);
}
);
}
const id = el.id;
const button = document.querySelector<HTMLButtonElement>(`[aria-controls="${id}"]`);
@ -26,6 +29,7 @@ export class ModalController {
} else {
this.el.open = true;
}
el.querySelector('input')?.focus();
});
}
for (const close of this.el.querySelectorAll<HTMLButtonElement>('[data-modal-close]')) {

Просмотреть файл

@ -8,10 +8,16 @@ exports[`creates select nav from tree 1`] = `
<select
class="go-Select js-selectNav"
>
<option
disabled=""
<optgroup
label="Outline"
/>
>
<option
label="Level 1"
value="#one"
>
Level 1
</option>
</optgroup>
<optgroup
label="Level 1"
>
@ -34,6 +40,58 @@ exports[`creates select nav from tree 1`] = `
Level 2-3
</option>
</optgroup>
<optgroup
label="Level 2-2"
>
<option
label="Level 3-1"
value="#three-one"
>
Level 3-1
</option>
</optgroup>
<optgroup
label="Level 3-1"
>
<option
label="Level 4-1"
value="#four-one"
>
Level 4-1
</option>
<option
label="Level 4-2"
value="#four-two"
>
Level 4-2
</option>
</optgroup>
<optgroup
label="Level 2-3"
>
<option
label="Level 3-2"
value="#three-two"
>
Level 3-2
</option>
</optgroup>
<optgroup
label="Level 3-2"
>
<option
label="Level 4-3"
value="#four-three"
>
Level 4-3
</option>
<option
label="Level 4-4"
value="#four-four"
>
Level 4-4
</option>
</optgroup>
</select>
</label>
`;

Просмотреть файл

@ -16,7 +16,7 @@ exports[`creates tree nav from ul 1`] = `
<a
aria-expanded="false"
aria-level="1"
aria-owns=" nav group Level 1"
aria-owns="_nav_group_Level_1"
class="focus"
href="#one"
role="treeitem"
@ -27,7 +27,7 @@ exports[`creates tree nav from ul 1`] = `
<ul
id=" nav group Level 1"
id="_nav_group_Level_1"
role="group"
>
@ -58,7 +58,7 @@ exports[`creates tree nav from ul 1`] = `
<a
aria-expanded="false"
aria-level="2"
aria-owns="Level 1 nav group Level 2-2"
aria-owns="Level_1_nav_group_Level_2_2"
href="#two-two"
role="treeitem"
tabindex="-1"
@ -68,7 +68,7 @@ exports[`creates tree nav from ul 1`] = `
<ul
id="Level 1 nav group Level 2-2"
id="Level_1_nav_group_Level_2_2"
role="group"
>
@ -81,7 +81,7 @@ exports[`creates tree nav from ul 1`] = `
<a
aria-expanded="false"
aria-level="3"
aria-owns="Level 2-2 nav group Level 3-1"
aria-owns="Level_2_2_nav_group_Level_3_1"
href="#three-one"
role="treeitem"
tabindex="-1"
@ -91,7 +91,7 @@ exports[`creates tree nav from ul 1`] = `
<ul
id="Level 2-2 nav group Level 3-1"
id="Level_2_2_nav_group_Level_3_1"
role="group"
>
@ -152,7 +152,7 @@ exports[`creates tree nav from ul 1`] = `
<a
aria-expanded="false"
aria-level="2"
aria-owns="Level 1 nav group Level 2-3"
aria-owns="Level_1_nav_group_Level_2_3"
href="#two-three"
role="treeitem"
tabindex="-1"
@ -162,7 +162,7 @@ exports[`creates tree nav from ul 1`] = `
<ul
id="Level 1 nav group Level 2-3"
id="Level_1_nav_group_Level_2_3"
role="group"
>
@ -175,7 +175,7 @@ exports[`creates tree nav from ul 1`] = `
<a
aria-expanded="false"
aria-level="3"
aria-owns="Level 2-3 nav group Level 3-2"
aria-owns="Level_2_3_nav_group_Level_3_2"
href="#three-two"
role="treeitem"
tabindex="-1"
@ -185,7 +185,7 @@ exports[`creates tree nav from ul 1`] = `
<ul
id="Level 2-3 nav group Level 3-2"
id="Level_2_3_nav_group_Level_3_2"
role="group"
>

Просмотреть файл

@ -0,0 +1,7 @@
/**
* @license
* Copyright 2021 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/export class SelectNavController{constructor(n){this.el=n;this.el.addEventListener("change",t=>{const o=t.target;let r=o.value;o.value.startsWith("/")||(r="/"+r),window.location.href=r})}}export function makeSelectNav(c){const n=document.createElement("label");n.classList.add("go-Label"),n.setAttribute("aria-label","Menu");const t=document.createElement("select");t.classList.add("go-Select","js-selectNav"),n.appendChild(t);const o=document.createElement("optgroup");o.label="Outline",t.appendChild(o);const r={};let l;for(const e of c.treeitems){if(Number(e.depth)>4)continue;e.groupTreeitem?(l=r[e.groupTreeitem.label],l||(l=r[e.groupTreeitem.label]=document.createElement("optgroup"),l.label=e.groupTreeitem.label,t.appendChild(l))):l=o;const a=document.createElement("option");a.label=e.label,a.textContent=e.label,a.value=e.el.href.replace(window.location.origin,"").replace("/",""),l.appendChild(a)}return c.addObserver(e=>{const a=t.querySelector(`[label="${e.label}"]`)?.value;a&&(t.value=a)},50),n}
//# sourceMappingURL=select.js.map

Просмотреть файл

@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["select.ts"],
"sourcesContent": ["/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { TreeNavController } from './tree.js';\n\nexport class SelectNavController {\n constructor(private el: Element) {\n this.el.addEventListener('change', e => {\n const target = e.target as HTMLSelectElement;\n let href = target.value;\n if (!target.value.startsWith('/')) {\n href = '/' + href;\n }\n window.location.href = href;\n });\n }\n}\n\nexport function makeSelectNav(tree: TreeNavController): HTMLLabelElement {\n const label = document.createElement('label');\n label.classList.add('go-Label');\n label.setAttribute('aria-label', 'Menu');\n const select = document.createElement('select');\n select.classList.add('go-Select', 'js-selectNav');\n label.appendChild(select);\n const outline = document.createElement('optgroup');\n outline.label = 'Outline';\n select.appendChild(outline);\n const groupMap = {};\n let group: HTMLOptGroupElement;\n for (const t of tree.treeitems) {\n if (Number(t.depth) > 4) continue;\n if (t.groupTreeitem) {\n group = groupMap[t.groupTreeitem.label];\n if (!group) {\n group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');\n group.label = t.groupTreeitem.label;\n select.appendChild(group);\n }\n } else {\n group = outline;\n }\n const o = document.createElement('option');\n o.label = t.label;\n o.textContent = t.label;\n o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');\n group.appendChild(o);\n }\n tree.addObserver(t => {\n const value = select.querySelector<HTMLOptionElement>(`[label=\"${t.label}\"]`)?.value;\n if (value) {\n select.value = value;\n }\n }, 50);\n return label;\n}\n"],
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASO,gCAA0B,CAC/B,YAAoB,EAAa,CAAb,UAClB,KAAK,GAAG,iBAAiB,SAAU,GAAK,CACtC,KAAM,GAAS,EAAE,OACjB,GAAI,GAAO,EAAO,MAClB,AAAK,EAAO,MAAM,WAAW,MAC3B,GAAO,IAAM,GAEf,OAAO,SAAS,KAAO,KAKtB,8BAAuB,EAA2C,CACvE,KAAM,GAAQ,SAAS,cAAc,SACrC,EAAM,UAAU,IAAI,YACpB,EAAM,aAAa,aAAc,QACjC,KAAM,GAAS,SAAS,cAAc,UACtC,EAAO,UAAU,IAAI,YAAa,gBAClC,EAAM,YAAY,GAClB,KAAM,GAAU,SAAS,cAAc,YACvC,EAAQ,MAAQ,UAChB,EAAO,YAAY,GACnB,KAAM,GAAW,GACjB,GAAI,GACJ,SAAW,KAAK,GAAK,UAAW,CAC9B,GAAI,OAAO,EAAE,OAAS,EAAG,SACzB,AAAI,EAAE,cACJ,GAAQ,EAAS,EAAE,cAAc,OAC5B,GACH,GAAQ,EAAS,EAAE,cAAc,OAAS,SAAS,cAAc,YACjE,EAAM,MAAQ,EAAE,cAAc,MAC9B,EAAO,YAAY,KAGrB,EAAQ,EAEV,KAAM,GAAI,SAAS,cAAc,UACjC,EAAE,MAAQ,EAAE,MACZ,EAAE,YAAc,EAAE,MAClB,EAAE,MAAS,EAAE,GAAyB,KAAK,QAAQ,OAAO,SAAS,OAAQ,IAAI,QAAQ,IAAK,IAC5F,EAAM,YAAY,GAEpB,SAAK,YAAY,GAAK,CACpB,KAAM,GAAQ,EAAO,cAAiC,WAAW,EAAE,YAAY,MAC/E,AAAI,GACF,GAAO,MAAQ,IAEhB,IACI",
"names": []
}

Просмотреть файл

@ -11,7 +11,11 @@ export class SelectNavController {
constructor(private el: Element) {
this.el.addEventListener('change', e => {
const target = e.target as HTMLSelectElement;
window.location.href = target.value;
let href = target.value;
if (!target.value.startsWith('/')) {
href = '/' + href;
}
window.location.href = href;
});
}
}
@ -23,31 +27,28 @@ export function makeSelectNav(tree: TreeNavController): HTMLLabelElement {
const select = document.createElement('select');
select.classList.add('go-Select', 'js-selectNav');
label.appendChild(select);
const o = document.createElement('option');
o.disabled = true;
o.selected = true;
o.label = 'Outline';
select.appendChild(o);
const outline = document.createElement('optgroup');
outline.label = 'Outline';
select.appendChild(outline);
const groupMap = {};
let group: HTMLOptGroupElement;
for (const [i, t] of tree.treeitems.entries()) {
if (Number(t.depth) > 2) continue;
if (t.depth === 1 && tree.treeitems[i + 1]?.depth > 1) {
group = document.createElement('optgroup');
group.label = t.label;
select.appendChild(group);
} else {
const o = document.createElement('option');
o.label = t.label;
o.textContent = t.label;
o.value = (t.el as HTMLAnchorElement).href
.replace(window.location.origin, '')
.replace('/', '');
if (t.depth === 1) {
select.appendChild(o);
} else {
group.appendChild(o);
for (const t of tree.treeitems) {
if (Number(t.depth) > 4) continue;
if (t.groupTreeitem) {
group = groupMap[t.groupTreeitem.label];
if (!group) {
group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');
group.label = t.groupTreeitem.label;
select.appendChild(group);
}
} else {
group = outline;
}
const o = document.createElement('option');
o.label = t.label;
o.textContent = t.label;
o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');
group.appendChild(o);
}
tree.addObserver(t => {
const value = select.querySelector<HTMLOptionElement>(`[label="${t.label}"]`)?.value;

Просмотреть файл

@ -26,10 +26,8 @@
display: block;
}
.go-Tree a[aria-level='1'] + ul[role='group'] {
-webkit-mask-image: linear-gradient(to bottom, black 93%, transparent 100%);
mask-image: linear-gradient(to bottom, black 93%, transparent 100%);
max-height: calc(
100vh - var(--js-tree-height, 0) - var(--js-sticky-header-height) - var(--gap) * 2
100vh - var(--js-tree-height, 0) - var(--js-sticky-header-height, 3.5rem) - 5rem
);
overflow-y: auto;
padding: 0.5rem 0.25rem 0 0.25rem;

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -81,8 +81,8 @@ export class TreeNavController {
for (const href of this.treeitems.map(t => t.el.getAttribute('href'))) {
if (href) {
const id = href.replace(window.location.origin, '').replace('/', '');
const target = document.querySelector(id);
const id = href.replace(window.location.origin, '').replace('/', '').replace('#', '');
const target = document.getElementById(id);
if (target) {
observer.observe(target);
}
@ -308,7 +308,7 @@ class TreeItem {
let curr = el.nextElementSibling;
while (curr) {
if (curr.tagName.toLowerCase() == 'ul') {
const groupId = `${group?.label ?? ''} nav group ${this.label}`;
const groupId = `${group?.label ?? ''} nav group ${this.label}`.replace(/[\W_]+/g, '_');
el.setAttribute('aria-owns', groupId);
el.setAttribute('aria-expanded', 'false');
curr.setAttribute('role', 'group');

Просмотреть файл

@ -0,0 +1,7 @@
/*!
* @license
* Copyright 2021 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/const n={PLAY_HREF:".js-exampleHref",PLAY_CONTAINER:".js-exampleContainer",EXAMPLE_INPUT:".Documentation-exampleCode",EXAMPLE_OUTPUT:".Documentation-exampleOutput",EXAMPLE_ERROR:".Documentation-exampleError",PLAY_BUTTON:".Documentation-examplePlayButton",SHARE_BUTTON:".Documentation-exampleShareButton",FORMAT_BUTTON:".Documentation-exampleFormatButton",RUN_BUTTON:".Documentation-exampleRunButton"};export class PlaygroundExampleController{constructor(t){this.exampleEl=t;this.exampleEl=t,this.anchorEl=t.querySelector("a"),this.errorEl=t.querySelector(n.EXAMPLE_ERROR),this.playButtonEl=t.querySelector(n.PLAY_BUTTON),this.shareButtonEl=t.querySelector(n.SHARE_BUTTON),this.formatButtonEl=t.querySelector(n.FORMAT_BUTTON),this.runButtonEl=t.querySelector(n.RUN_BUTTON),this.inputEl=this.makeTextArea(t.querySelector(n.EXAMPLE_INPUT)),this.outputEl=t.querySelector(n.EXAMPLE_OUTPUT),this.playButtonEl?.addEventListener("click",()=>this.handleShareButtonClick()),this.shareButtonEl?.addEventListener("click",()=>this.handleShareButtonClick()),this.formatButtonEl?.addEventListener("click",()=>this.handleFormatButtonClick()),this.runButtonEl?.addEventListener("click",()=>this.handleRunButtonClick()),!!this.inputEl&&(this.resize(),this.inputEl.addEventListener("keyup",()=>this.resize()),this.inputEl.addEventListener("keydown",e=>this.onKeydown(e)))}makeTextArea(t){const e=document.createElement("textarea");return e.classList.add("Documentation-exampleCode","code"),e.spellcheck=!1,e.value=t?.textContent??"",t?.parentElement?.replaceChild(e,t),e}getAnchorHash(){return this.anchorEl?.hash}expand(){this.exampleEl.open=!0}resize(){if(this.inputEl?.value){const t=(this.inputEl.value.match(/\n/g)||[]).length;this.inputEl.style.height=`${(20+t*20+12+2)/16}rem`}}onKeydown(t){t.key==="Tab"&&(document.execCommand("insertText",!1," "),t.preventDefault())}setInputText(t){this.inputEl&&(this.inputEl.value=t)}setOutputText(t){this.outputEl&&(this.outputEl.innerHTML=t)}setErrorText(t){this.errorEl&&(this.errorEl.textContent=t),this.setOutputText("An error has occurred\u2026")}handleShareButtonClick(){const t="https://play.golang.org/p/";this.setOutputText("Waiting for remote server\u2026"),fetch("/play/share",{method:"POST",body:this.inputEl?.value}).then(e=>e.text()).then(e=>{const r=t+e;this.setOutputText(`<a href="${r}">${r}</a>`),window.open(r)}).catch(e=>{this.setErrorText(e)})}handleFormatButtonClick(){this.setOutputText("Waiting for remote server\u2026");const t=new FormData;t.append("body",this.inputEl?.value??""),fetch("/play/fmt",{method:"POST",body:t}).then(e=>e.json()).then(({Body:e,Error:r})=>{this.setOutputText(r||"Done."),e&&(this.setInputText(e),this.resize())}).catch(e=>{this.setErrorText(e)})}handleRunButtonClick(){this.setOutputText("Waiting for remote server\u2026"),fetch("/play/compile",{method:"POST",body:JSON.stringify({body:this.inputEl?.value,version:2})}).then(t=>t.json()).then(async({Events:t,Errors:e})=>{this.setOutputText(e||"");for(const r of t||[])this.setOutputText(r.Message),await new Promise(a=>setTimeout(a,r.Delay/1e6))}).catch(t=>{this.setErrorText(t)})}}const l=location.hash.match(/^#(example-.*)$/);if(l){const o=document.getElementById(l[1]);o&&(o.open=!0)}const i=[...document.querySelectorAll(n.PLAY_HREF)],s=o=>i.find(t=>t.hash===o.getAnchorHash());for(const o of document.querySelectorAll(n.PLAY_CONTAINER)){const t=new PlaygroundExampleController(o),e=s(t);e?e.addEventListener("click",()=>{t.expand()}):console.warn("example href not found")}
//# sourceMappingURL=playground.js.map

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -0,0 +1,179 @@
/*!
* @license
* Copyright 2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
import { PlaygroundExampleController } from './playground';
import { mocked } from 'ts-jest/utils';
const flushPromises = () => new Promise(setImmediate);
const el = <T extends HTMLElement>(selector: string) => document.querySelector<T>(selector);
const codeSnippet = `package main
import (
"fmt"
"io"
"os"
)
func main() {
const name, age = "Kim", 22
s := fmt.Sprintln( name, "is", age, "years old.")
io.WriteString( os.Stdout, s) // Ignoring error for simplicity.
// HTML special characters: & ' < > "
}
`;
const escapeHTML = (s: string) => {
return s
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/'/g, '&#39;')
.replace(/"/g, '&#34;');
};
describe('PlaygroundExampleController', () => {
let example: HTMLDetailsElement;
window.fetch = jest.fn();
window.open = jest.fn();
beforeEach(() => {
document.body.innerHTML = `
<details tabindex="-1" id="example-Sprintln" class="Documentation-exampleDetails js-exampleContainer">
<summary class="Documentation-exampleDetailsHeader">Example <a href="#example-Sprintln"></a></summary>
<div class="Documentation-exampleDetailsBody">
<p>Code:</p>
<pre class="Documentation-exampleCode">${escapeHTML(codeSnippet)}</pre>
<pre>
<span class="Documentation-exampleOutputLabel">Output:</span>
<span class="Documentation-exampleOutput">Kim is 22 years old.</span>
</pre>
</div>
<div class="Documentation-exampleButtonsContainer">
<p class="Documentation-exampleError" role="alert" aria-atomic="true"></p>
<button class="Documentation-exampleShareButton" aria-label="Share Code">Share</button>
<button class="Documentation-exampleFormatButton" aria-label="Format Code">Format</button>
<button class="Documentation-exampleRunButton" aria-label="Run Code">Run</button>
</div></details>
`;
example = el('.js-exampleContainer') as HTMLDetailsElement;
new PlaygroundExampleController(example);
});
afterEach(() => {
document.body.innerHTML = '';
mocked(window.fetch).mockClear();
mocked(window.open).mockClear();
});
it('expands and collapses example when summary is clicked', () => {
const summary = example.firstElementChild as HTMLDetailsElement;
summary.click();
expect(example.open).toBeTruthy();
summary.click();
expect(example.open).toBeFalsy();
});
it('replaces the pre element with a text area', () => {
const input = document.querySelector('.Documentation-exampleCode');
expect(input.tagName).toBe('TEXTAREA');
});
it('opens playground after pressing share', async () => {
mocked(window.fetch).mockResolvedValue({
text: () => Promise.resolve('abcdefg'),
} as Response);
el('[aria-label="Share Code"]').click();
await flushPromises();
expect(window.fetch).toHaveBeenCalledWith('/play/share', {
body: codeSnippet,
method: 'POST',
});
expect(window.open).toHaveBeenCalledWith('https://play.golang.org/p/abcdefg');
});
it('replaces textarea with formated code after pressing format', async () => {
mocked(window.fetch).mockResolvedValue({
json: () =>
Promise.resolve({
Body: '// mocked response',
Error: '',
}),
} as Response);
el('[aria-label="Format Code"]').click();
const body = new FormData();
body.append('body', codeSnippet);
await flushPromises();
expect(window.fetch).toHaveBeenCalledWith('/play/fmt', {
body: body,
method: 'POST',
});
expect(el<HTMLTextAreaElement>('.Documentation-exampleCode').value).toBe('// mocked response');
});
it('displays error message after pressing format with invalid code', async () => {
mocked(window.fetch).mockResolvedValue({
json: () =>
Promise.resolve({
Body: '',
Error: '// mocked error',
}),
} as Response);
el('[aria-label="Format Code"]').click();
const body = new FormData();
body.append('body', codeSnippet);
await flushPromises();
expect(window.fetch).toHaveBeenCalledWith('/play/fmt', {
body: body,
method: 'POST',
});
expect(el<HTMLTextAreaElement>('.Documentation-exampleCode').value).toBe(codeSnippet);
expect(el('.Documentation-exampleOutput').textContent).toContain('// mocked error');
});
it('displays code output after pressing run', async () => {
mocked(window.fetch).mockResolvedValue({
json: () =>
Promise.resolve({
Events: [{ Message: '// mocked response', Kind: 'stdout', Delay: 0 }],
Errors: null,
}),
} as Response);
el('[aria-label="Run Code"]').click();
await flushPromises();
expect(window.fetch).toHaveBeenCalledWith('/play/compile', {
body: JSON.stringify({ body: codeSnippet, version: 2 }),
method: 'POST',
});
expect(el('.Documentation-exampleOutput').textContent).toContain('// mocked response');
});
it('displays error message after pressing run with invalid code', async () => {
mocked(window.fetch).mockResolvedValue({
json: () =>
Promise.resolve({
Events: null,
Errors: '// mocked error',
}),
} as Response);
el('[aria-label="Run Code"]').click();
await flushPromises();
expect(window.fetch).toHaveBeenCalledWith('/play/compile', {
body: JSON.stringify({ body: codeSnippet, version: 2 }),
method: 'POST',
});
expect(el('.Documentation-exampleOutput').textContent).toContain('// mocked error');
});
});

Просмотреть файл

@ -0,0 +1,290 @@
/*!
* @license
* Copyright 2021 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
// This file implements the playground implementation of the documentation
// page. The playground involves a "play" button that allows you to open up
// a new link to play.golang.org using the example code.
// The CSS is in content/static/css/stylesheet.css.
/**
* CSS classes used by PlaygroundExampleController
*/
const PlayExampleClassName = {
PLAY_HREF: '.js-exampleHref',
PLAY_CONTAINER: '.js-exampleContainer',
EXAMPLE_INPUT: '.Documentation-exampleCode',
EXAMPLE_OUTPUT: '.Documentation-exampleOutput',
EXAMPLE_ERROR: '.Documentation-exampleError',
PLAY_BUTTON: '.Documentation-examplePlayButton',
SHARE_BUTTON: '.Documentation-exampleShareButton',
FORMAT_BUTTON: '.Documentation-exampleFormatButton',
RUN_BUTTON: '.Documentation-exampleRunButton',
};
/**
* This controller enables playground examples to expand their dropdown or
* generate shareable Go Playground URLs.
*/
export class PlaygroundExampleController {
/**
* The anchor tag used to identify the container with an example href.
* There is only one in an example container div.
*/
private readonly anchorEl: HTMLAnchorElement | null;
/**
* The error element
*/
private readonly errorEl: Element | null;
/**
* Buttons that redirect to an example's playground, this element
* only exists in executable examples.
*/
private readonly playButtonEl: Element | null;
private readonly shareButtonEl: Element | null;
/**
* Button that formats the code in an example's playground.
*/
private readonly formatButtonEl: Element | null;
/**
* Button that runs the code in an example's playground, this element
* only exists in executable examples.
*/
private readonly runButtonEl: Element | null;
/**
* The executable code of an example.
*/
private readonly inputEl: HTMLTextAreaElement | null;
/**
* The output of the given example code. This only exists if the
* author of the package provides an output for this example.
*/
private readonly outputEl: Element | null;
/**
* @param exampleEl The div that contains playground content for the given example.
*/
constructor(private readonly exampleEl: HTMLDetailsElement) {
this.exampleEl = exampleEl;
this.anchorEl = exampleEl.querySelector('a');
this.errorEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_ERROR);
this.playButtonEl = exampleEl.querySelector(PlayExampleClassName.PLAY_BUTTON);
this.shareButtonEl = exampleEl.querySelector(PlayExampleClassName.SHARE_BUTTON);
this.formatButtonEl = exampleEl.querySelector(PlayExampleClassName.FORMAT_BUTTON);
this.runButtonEl = exampleEl.querySelector(PlayExampleClassName.RUN_BUTTON);
this.inputEl = this.makeTextArea(exampleEl.querySelector(PlayExampleClassName.EXAMPLE_INPUT));
this.outputEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT);
// This is legacy listener to be replaced the listener for shareButtonEl.
this.playButtonEl?.addEventListener('click', () => this.handleShareButtonClick());
this.shareButtonEl?.addEventListener('click', () => this.handleShareButtonClick());
this.formatButtonEl?.addEventListener('click', () => this.handleFormatButtonClick());
this.runButtonEl?.addEventListener('click', () => this.handleRunButtonClick());
if (!this.inputEl) return;
this.resize();
this.inputEl.addEventListener('keyup', () => this.resize());
this.inputEl.addEventListener('keydown', e => this.onKeydown(e));
}
/**
* Replace the pre element with a textarea. The examples are initially rendered
* as pre elements so they're fully visible when JS is disabled.
*/
makeTextArea(el: Element | null): HTMLTextAreaElement {
const t = document.createElement('textarea');
t.classList.add('Documentation-exampleCode', 'code');
t.spellcheck = false;
t.value = el?.textContent ?? '';
el?.parentElement?.replaceChild(t, el);
return t;
}
/**
* Retrieve the hash value of the anchor element.
*/
getAnchorHash(): string | undefined {
return this.anchorEl?.hash;
}
/**
* Expands the current playground example.
*/
expand(): void {
this.exampleEl.open = true;
}
/**
* Resizes the input element to accomodate the amount of text present.
*/
private resize(): void {
if (this.inputEl?.value) {
const numLineBreaks = (this.inputEl.value.match(/\n/g) || []).length;
// min-height + lines x line-height + padding + border
this.inputEl.style.height = `${(20 + numLineBreaks * 20 + 12 + 2) / 16}rem`;
}
}
/**
* Handler to override keyboard behavior in the playground's
* textarea element.
*
* Tab key inserts tabs into the example playground instead of
* switching to the next interactive element.
* @param e input element keyboard event.
*/
private onKeydown(e: KeyboardEvent) {
if (e.key === 'Tab') {
document.execCommand('insertText', false, '\t');
e.preventDefault();
}
}
/**
* Changes the text of the example's input box.
*/
private setInputText(output: string) {
if (this.inputEl) {
this.inputEl.value = output;
}
}
/**
* Changes the text of the example's output box.
*/
private setOutputText(output: string) {
if (this.outputEl) {
this.outputEl.innerHTML = output;
}
}
/**
* Sets the error message text and overwrites
* output box to indicate a failed response.
*/
private setErrorText(err: string) {
if (this.errorEl) {
this.errorEl.textContent = err;
}
this.setOutputText('An error has occurred…');
}
/**
* Opens a new window to play.golang.org using the
* example snippet's code in the playground.
*/
private handleShareButtonClick() {
const PLAYGROUND_BASE_URL = 'https://play.golang.org/p/';
this.setOutputText('Waiting for remote server…');
fetch('/play/share', {
method: 'POST',
body: this.inputEl?.value,
})
.then(res => res.text())
.then(shareId => {
const href = PLAYGROUND_BASE_URL + shareId;
this.setOutputText(`<a href="${href}">${href}</a>`);
window.open(href);
})
.catch(err => {
this.setErrorText(err);
});
}
/**
* Runs gofmt on the example snippet in the playground.
*/
private handleFormatButtonClick() {
this.setOutputText('Waiting for remote server…');
const body = new FormData();
body.append('body', this.inputEl?.value ?? '');
fetch('/play/fmt', {
method: 'POST',
body: body,
})
.then(res => res.json())
.then(({ Body, Error }) => {
this.setOutputText(Error || 'Done.');
if (Body) {
this.setInputText(Body);
this.resize();
}
})
.catch(err => {
this.setErrorText(err);
});
}
/**
* Runs the code snippet in the example playground.
*/
private handleRunButtonClick() {
this.setOutputText('Waiting for remote server…');
fetch('/play/compile', {
method: 'POST',
body: JSON.stringify({ body: this.inputEl?.value, version: 2 }),
})
.then(res => res.json())
.then(async ({ Events, Errors }) => {
this.setOutputText(Errors || '');
for (const e of Events || []) {
this.setOutputText(e.Message);
await new Promise(resolve => setTimeout(resolve, e.Delay / 1000000));
}
})
.catch(err => {
this.setErrorText(err);
});
}
}
const exampleHashRegex = location.hash.match(/^#(example-.*)$/);
if (exampleHashRegex) {
const exampleHashEl = document.getElementById(exampleHashRegex[1]) as HTMLDetailsElement;
if (exampleHashEl) {
exampleHashEl.open = true;
}
}
// We use a spread operator to convert a nodelist into an array of elements.
const exampleHrefs = [
...document.querySelectorAll<HTMLAnchorElement>(PlayExampleClassName.PLAY_HREF),
];
/**
* Sometimes exampleHrefs and playContainers are in different order, so we
* find an exampleHref from a common hash.
* @param playContainer - playground container
*/
const findExampleHash = (playContainer: PlaygroundExampleController) =>
exampleHrefs.find(ex => {
return ex.hash === playContainer.getAnchorHash();
});
for (const el of document.querySelectorAll(PlayExampleClassName.PLAY_CONTAINER)) {
// There should be the same amount of hrefs referencing examples as example containers.
const playContainer = new PlaygroundExampleController(el as HTMLDetailsElement);
const exampleHref = findExampleHash(playContainer);
if (exampleHref) {
exampleHref.addEventListener('click', () => {
playContainer.expand();
});
} else {
console.warn('example href not found');
}
}

Просмотреть файл

@ -1,13 +1,15 @@
<!--
Copyright 2019 The Go Authors. All rights reserved.
Copyright 2021 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{define "main_content"}}
<div class="Container">
<div class="Content">
<h1 class="Content-header">Search help</h1>
{{define "title"}}<title>Search Help · pkg.go.dev</title>{{end}}
{{define "main"}}
<main class="go-Container">
<div class="go-Content">
<h1>Search help</h1>
<p>You can use symbols or words in your search to make your search results more precise.</p>
<h2>Search for an exact match</h2>
<p>Put a word or phrase inside quotes. For example, <a href="/search?q=&quot;go+cloud&quot;">"go cloud"</a>.</p>
@ -17,5 +19,5 @@
<p>You can search for a package by its full or partial import path. For example, <a href="/search?q=go%2Fpackages">go/packages</a>.</p>
<p>If the query matches a package import path, you will be redirected to the package details page for the latest version of that package. For example, <a href="/search?q=golang.org/x/tools/go/packages">golang.org/x/tools/go/packages</a>.</p>
</div>
</div>
</main>
{{end}}

Просмотреть файл

@ -0,0 +1,39 @@
<!--
Copyright 2019 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{define "pagination_summary"}}
{{- if gt .TotalCount .ResultCount -}}
{{- add .Offset 1}} – {{add .Offset .ResultCount}} of
{{- end}} {{if .Approximate}}about {{end}}{{.TotalCount -}}
{{end}}
{{define "pagination_nav"}}
{{if gt (len .Pages) 1}}
<div class="Pagination-nav">
<div class="Pagination-navInner">
{{ $pagination := . }}
{{if .PrevPage}}
<a class="Pagination-previous" href="{{.PageURL .PrevPage}}">Previous</a>
{{else}}
<span class="Pagination-previous" aria-disabled="true">Previous</span>
{{end}}
{{$page := .Page}}
{{range $i := .Pages}}
{{if eq $i $page}}
<b class="Pagination-number">{{$i}}</b>
{{else}}
<a class="Pagination-number" href="{{$pagination.PageURL $i}}">{{$i}}</a>
{{end}}
{{end}}
{{if .NextPage}}
<a class="Pagination-next" href="{{.PageURL .NextPage}}">Next</a>
{{else}}
<span class="Pagination-next" aria-disabled="true">Next</span>
{{end}}
</div>
</div>
{{end}}
{{end}}

Просмотреть файл

@ -11,17 +11,11 @@
}
.SearchResults-header {
font-size: 1.875rem;
margin: 0;
}
.SearchResults-help {
margin-top: 0.3125rem;
}
.SearchResults-resultCount {
color: var(--gray-3);
display: flex;
justify-content: space-between;
margin-bottom: 0.625rem;
margin-top: 1.125rem;
}
.SearchResults-footer {
display: flex;

Просмотреть файл

@ -1,16 +1,18 @@
<!--
Copyright 2019 The Go Authors. All rights reserved.
Copyright 2021 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{define "pre_content"}}
<link href="/static/css/search.css?version={{.AppVersionLabel}}" rel="stylesheet">
{{define "title"}}<title>Search Help · pkg.go.dev</title>{{end}}
{{define "pre-content"}}
<link href="/static/search/search.css?version={{.AppVersionLabel}}" rel="stylesheet">
{{end}}
{{define "main_content"}}
<div class="Container">
<div class="SearchResults">
{{define "main"}}
<main class="go-Container">
<div class="go-Content">
<h1 class="SearchResults-header">Results for “{{.Query}}”</h1>
<div class="SearchResults-help"><a href="/search-help">Search help</a></div>
<div class="SearchResults-resultCount">
@ -38,7 +40,7 @@
{{template "pagination_nav" .Pagination}}
</div>
</div>
</div>
</main>
{{end}}

Просмотреть файл

@ -4,21 +4,23 @@
* license that can be found in the LICENSE file.
*/
@import url('../unit/main/_meta.css');
.StyleGuide {
background-color: var(--color-background);
}
.StyleGuide > section {
align-items: center;
display: grid;
gap: var(--gap) 2rem;
gap: 1rem 2rem;
grid-template-columns: 100%;
margin-bottom: var(--gap);
margin-bottom: 1rem;
}
.StyleGuide > section > header {
border-bottom: var(--border);
grid-column: 1/-1;
margin-bottom: var(--gap);
padding-bottom: var(--gap);
margin-bottom: 1rem;
padding-bottom: 1rem;
}
.StyleGuide > section > h2 {
grid-column: 1/-1;
@ -61,7 +63,7 @@
grid-template-columns: 20rem auto;
}
}
@media (min-width: 95rem) {
@media (min-width: 112rem) {
.StyleGuide .Icon {
grid-template-columns: 10rem auto 50%;
}
@ -83,7 +85,7 @@
align-items: baseline;
display: flex;
flex-direction: column;
gap: var(--gap);
gap: 1rem;
padding: 0.25rem;
}
.ElementMarkup > pre,
@ -132,61 +134,6 @@
text-transform: capitalize;
}
.MainHeader-toggle,
.MainHeader-toggle legend {
display: none;
}
@media (min-width: 28rem) {
.MainHeader-toggle {
display: flex;
}
}
@media (min-width: 37.5rem) {
.MainHeader-toggle legend {
display: initial;
}
}
/* Safari only */
@media not all and (min-resolution: 0.001dpcm) {
@supports (-webkit-appearance: none) {
.MainHeader-toggle legend {
display: none;
}
}
}
.UnitMeta {
display: grid;
gap: var(--gap);
grid-template-columns: 100%;
margin-bottom: var(--gap);
margin-top: var(--gap);
}
.UnitMeta-details {
display: flex;
flex-flow: wrap;
flex-direction: column;
gap: var(--gap);
}
.UnitMeta-repo {
align-items: center;
.MainHeader-toggle {
display: flex;
}
.UnitMeta-repo a {
overflow: hidden;
text-overflow: ellipsis;
}
@media (min-width: 80rem) {
.UnitMeta {
grid-template-columns: max-content auto max-content;
}
.UnitMeta-details {
flex-direction: row;
}
}
@media (min-width: 95rem) {
:root:not([data-layout='compact']) .UnitMeta {
grid-template-columns: 100%;
}
}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -24,7 +24,7 @@
{{end}}
{{define "main-header"}}
<nav class="go-Breadcrumb" aria-label="Breadcrumb">
<nav class="go-Main-headerBreadcrumb go-Breadcrumb" aria-label="Breadcrumb">
<ol>
<li><a href="/" data-gtmc="breadcrumb link">Discover Packages</a></li>
<li>
@ -33,46 +33,50 @@
</ol>
</nav>
<div class="go-Main-headerContent">
<a class="go-Main-headerLogo" href="https://go.dev/" aria-hidden="true" tabindex="-1" data-gtmc="header link"
aria-label="Link to Go Homepage">
<img height="78" width="207" src="/static/logo/go-blue.svg" alt="Go">
</a>
<h1 class="go-Main-headerTitle">Style Guide</h1>
<fieldset class="go-Label go-Label--inline MainHeader-toggle">
<legend>Theme</legend>
<div class="go-InputGroup">
<button class="go-Button go-Button--inverted js-toggleTheme" data-value="light" title="Light"
aria-label="Set light theme">
<img class="go-Icon" height="24" width="24" src="/static/icon/light_mode_gm_grey_24dp.svg"
alt="">
</button>
<button class="go-Button go-Button--inverted js-toggleTheme" data-value="dark" title="Dark"
aria-label="Set dark theme">
<img class="go-Icon" height="24" width="24" alt=""
src="/static/icon/brightness_2_gm_grey_24dp.svg">
</button>
</div>
</fieldset>
<fieldset class="go-Label go-Label--inline MainHeader-toggle">
<legend>Layout</legend>
<div class="go-InputGroup">
<button class="go-Button go-Button--inverted js-toggleLayout" data-value="responsive"
title="Responsive" aria-label="Set responsive layout">
<img class="go-Icon" height="24" width="24" src="/static/icon/responsive_layout_gm_grey_24dp.svg"
alt="">
</button>
<button class="go-Button go-Button--inverted js-toggleLayout" data-value="stacked"
title="Stacked" aria-label="Set stacked layout">
<img class="go-Icon" height="24" width="24" src="/static/icon/table_rows_gm_grey_24dp.svg"
alt="">
</button>
<button class="go-Button go-Button--inverted js-toggleLayout" data-value="compact"
title="Compact" aria-label="Set compact layout">
<img class="go-Icon" height="24" width="24" src="/static/icon/toolbar_gm_grey_24dp.svg"
alt="">
</button>
</div>
</fieldset>
<div class="go-Main-headerTitle">
<a class="go-Main-headerLogo" href="https://go.dev/" aria-hidden="true" tabindex="-1" data-gtmc="header link"
aria-label="Link to Go Homepage">
<img height="78" width="207" src="/static/logo/go-blue.svg" alt="Go">
</a>
<h1 class="go-Main-headerTitle">Style Guide</h1>
</div>
<div class="go-Main-headerDetails">
<fieldset class="go-Main-headerDetailItem go-Label go-Label--inline MainHeader-toggle">
<legend>Theme</legend>
<div class="go-InputGroup">
<button class="go-Button go-Button--inverted js-toggleTheme" data-value="light" title="Light"
aria-label="Set light theme">
<img class="go-Icon" height="24" width="24" src="/static/icon/light_mode_gm_grey_24dp.svg"
alt="">
</button>
<button class="go-Button go-Button--inverted js-toggleTheme" data-value="dark" title="Dark"
aria-label="Set dark theme">
<img class="go-Icon" height="24" width="24" alt=""
src="/static/icon/brightness_2_gm_grey_24dp.svg">
</button>
</div>
</fieldset>
<fieldset class="go-Main-headerDetailItem go-Label go-Label--inline MainHeader-toggle">
<legend>Layout</legend>
<div class="go-InputGroup">
<button class="go-Button go-Button--inverted js-toggleLayout" data-value="responsive"
title="Responsive" aria-label="Set responsive layout">
<img class="go-Icon" height="24" width="24" src="/static/icon/responsive_layout_gm_grey_24dp.svg"
alt="">
</button>
<button class="go-Button go-Button--inverted js-toggleLayout" data-value="stacked"
title="Stacked" aria-label="Set stacked layout">
<img class="go-Icon" height="24" width="24" src="/static/icon/table_rows_gm_grey_24dp.svg"
alt="">
</button>
<button class="go-Button go-Button--inverted js-toggleLayout" data-value="compact"
title="Compact" aria-label="Set compact layout">
<img class="go-Icon" height="24" width="24" src="/static/icon/toolbar_gm_grey_24dp.svg"
alt="">
</button>
</div>
</fieldset>
</div>
</div>
{{end}}

Просмотреть файл

@ -0,0 +1,7 @@
/*!
* @license
* Copyright 2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/export class ExpandableRowsTableController{constructor(t,e){this.table=t;this.toggleAll=e;this.expandAllItems=()=>{this.toggles.map(t=>t.setAttribute("aria-expanded","true")),this.update()};this.collapseAllItems=()=>{this.toggles.map(t=>t.setAttribute("aria-expanded","false")),this.update()};this.update=()=>{this.updateVisibleItems(),setTimeout(()=>this.updateGlobalToggle())};this.rows=Array.from(t.querySelectorAll("[data-aria-controls]")),this.toggles=Array.from(this.table.querySelectorAll("[aria-expanded]")),this.setAttributes(),this.attachEventListeners(),this.update()}setAttributes(){for(const t of["data-aria-controls","data-aria-labelledby","data-id"])this.table.querySelectorAll(`[${t}]`).forEach(e=>{e.setAttribute(t.replace("data-",""),e.getAttribute(t)??""),e.removeAttribute(t)})}attachEventListeners(){this.rows.forEach(t=>{t.addEventListener("click",e=>{this.handleToggleClick(e)})}),this.toggleAll?.addEventListener("click",()=>{this.expandAllItems()}),document.addEventListener("keydown",t=>{(t.ctrlKey||t.metaKey)&&t.key==="f"&&this.expandAllItems()})}handleToggleClick(t){let e=t.currentTarget;e?.hasAttribute("aria-expanded")||(e=this.table.querySelector(`button[aria-controls="${e?.getAttribute("aria-controls")}"]`));const l=e?.getAttribute("aria-expanded")==="true";e?.setAttribute("aria-expanded",l?"false":"true"),t.stopPropagation(),this.update()}updateVisibleItems(){this.rows.map(t=>{const e=t?.getAttribute("aria-expanded")==="true";t?.getAttribute("aria-controls")?.trimEnd().split(" ")?.map(s=>{const a=document.getElementById(`${s}`);e?(a?.classList.add("visible"),a?.classList.remove("hidden")):(a?.classList.add("hidden"),a?.classList.remove("visible"))})})}updateGlobalToggle(){if(!this.toggleAll)return;this.rows.some(e=>e.hasAttribute("aria-expanded"))&&(this.toggleAll.style.display="block"),this.toggles.some(e=>e.getAttribute("aria-expanded")==="false")?(this.toggleAll.innerText="Expand all",this.toggleAll.onclick=this.expandAllItems):(this.toggleAll.innerText="Collapse all",this.toggleAll.onclick=this.collapseAllItems)}}
//# sourceMappingURL=table.js.map

Просмотреть файл

@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["table.ts"],
"sourcesContent": ["/*!\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * Controller for a table element with expandable rows. Adds event listeners to\n * a toggle within a table row that controls visiblity of additional related\n * rows in the table.\n *\n * @example\n * ```typescript\n * import {ExpandableRowsTableController} from '/static/js/table';\n *\n * const el = document .querySelector<HTMLTableElement>('.js-myTableElement')\n * new ExpandableRowsTableController(el));\n * ```\n */\nexport class ExpandableRowsTableController {\n private rows: HTMLTableRowElement[];\n private toggles: HTMLButtonElement[];\n\n /**\n * Create a table controller.\n * @param table - The table element to which the controller binds.\n */\n constructor(private table: HTMLTableElement, private toggleAll?: HTMLButtonElement | null) {\n this.rows = Array.from(table.querySelectorAll<HTMLTableRowElement>('[data-aria-controls]'));\n this.toggles = Array.from(this.table.querySelectorAll('[aria-expanded]'));\n this.setAttributes();\n this.attachEventListeners();\n this.update();\n }\n\n /**\n * setAttributes sets data-aria-* and data-id attributes to regular\n * html attributes as a workaround for limitations from safehtml.\n */\n private setAttributes() {\n for (const a of ['data-aria-controls', 'data-aria-labelledby', 'data-id']) {\n this.table.querySelectorAll(`[${a}]`).forEach(t => {\n t.setAttribute(a.replace('data-', ''), t.getAttribute(a) ?? '');\n t.removeAttribute(a);\n });\n }\n }\n\n private attachEventListeners() {\n this.rows.forEach(t => {\n t.addEventListener('click', e => {\n this.handleToggleClick(e);\n });\n });\n this.toggleAll?.addEventListener('click', () => {\n this.expandAllItems();\n });\n\n document.addEventListener('keydown', e => {\n if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n this.expandAllItems();\n }\n });\n }\n\n private handleToggleClick(e: MouseEvent) {\n let target = e.currentTarget as HTMLTableRowElement | null;\n if (!target?.hasAttribute('aria-expanded')) {\n target = this.table.querySelector(\n `button[aria-controls=\"${target?.getAttribute('aria-controls')}\"]`\n );\n }\n const isExpanded = target?.getAttribute('aria-expanded') === 'true';\n target?.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');\n e.stopPropagation();\n this.update();\n }\n\n private expandAllItems = () => {\n this.toggles.map(t => t.setAttribute('aria-expanded', 'true'));\n this.update();\n };\n\n private collapseAllItems = () => {\n this.toggles.map(t => t.setAttribute('aria-expanded', 'false'));\n this.update();\n };\n\n private update = () => {\n this.updateVisibleItems();\n setTimeout(() => this.updateGlobalToggle());\n };\n\n private updateVisibleItems() {\n this.rows.map(t => {\n const isExpanded = t?.getAttribute('aria-expanded') === 'true';\n const rowIds = t?.getAttribute('aria-controls')?.trimEnd().split(' ');\n rowIds?.map(id => {\n const target = document.getElementById(`${id}`);\n if (isExpanded) {\n target?.classList.add('visible');\n target?.classList.remove('hidden');\n } else {\n target?.classList.add('hidden');\n target?.classList.remove('visible');\n }\n });\n });\n }\n\n private updateGlobalToggle() {\n if (!this.toggleAll) return;\n if (this.rows.some(t => t.hasAttribute('aria-expanded'))) {\n this.toggleAll.style.display = 'block';\n }\n const someCollapsed = this.toggles.some(el => el.getAttribute('aria-expanded') === 'false');\n if (someCollapsed) {\n this.toggleAll.innerText = 'Expand all';\n this.toggleAll.onclick = this.expandAllItems;\n } else {\n this.toggleAll.innerText = 'Collapse all';\n this.toggleAll.onclick = this.collapseAllItems;\n }\n }\n}\n"],
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAoBO,0CAAoC,CAQzC,YAAoB,EAAiC,EAAsC,CAAvE,aAAiC,iBAmD7C,oBAAiB,IAAM,CAC7B,KAAK,QAAQ,IAAI,GAAK,EAAE,aAAa,gBAAiB,SACtD,KAAK,UAGC,sBAAmB,IAAM,CAC/B,KAAK,QAAQ,IAAI,GAAK,EAAE,aAAa,gBAAiB,UACtD,KAAK,UAGC,YAAS,IAAM,CACrB,KAAK,qBACL,WAAW,IAAM,KAAK,uBA9DtB,KAAK,KAAO,MAAM,KAAK,EAAM,iBAAsC,yBACnE,KAAK,QAAU,MAAM,KAAK,KAAK,MAAM,iBAAiB,oBACtD,KAAK,gBACL,KAAK,uBACL,KAAK,SAOC,eAAgB,CACtB,SAAW,KAAK,CAAC,qBAAsB,uBAAwB,WAC7D,KAAK,MAAM,iBAAiB,IAAI,MAAM,QAAQ,GAAK,CACjD,EAAE,aAAa,EAAE,QAAQ,QAAS,IAAK,EAAE,aAAa,IAAM,IAC5D,EAAE,gBAAgB,KAKhB,sBAAuB,CAC7B,KAAK,KAAK,QAAQ,GAAK,CACrB,EAAE,iBAAiB,QAAS,GAAK,CAC/B,KAAK,kBAAkB,OAG3B,KAAK,WAAW,iBAAiB,QAAS,IAAM,CAC9C,KAAK,mBAGP,SAAS,iBAAiB,UAAW,GAAK,CACxC,AAAK,GAAE,SAAW,EAAE,UAAY,EAAE,MAAQ,KACxC,KAAK,mBAKH,kBAAkB,EAAe,CACvC,GAAI,GAAS,EAAE,cACf,AAAK,GAAQ,aAAa,kBACxB,GAAS,KAAK,MAAM,cAClB,yBAAyB,GAAQ,aAAa,uBAGlD,KAAM,GAAa,GAAQ,aAAa,mBAAqB,OAC7D,GAAQ,aAAa,gBAAiB,EAAa,QAAU,QAC7D,EAAE,kBACF,KAAK,SAkBC,oBAAqB,CAC3B,KAAK,KAAK,IAAI,GAAK,CACjB,KAAM,GAAa,GAAG,aAAa,mBAAqB,OAExD,AADe,GAAG,aAAa,kBAAkB,UAAU,MAAM,MACzD,IAAI,GAAM,CAChB,KAAM,GAAS,SAAS,eAAe,GAAG,KAC1C,AAAI,EACF,IAAQ,UAAU,IAAI,WACtB,GAAQ,UAAU,OAAO,WAEzB,IAAQ,UAAU,IAAI,UACtB,GAAQ,UAAU,OAAO,gBAMzB,oBAAqB,CAC3B,GAAI,CAAC,KAAK,UAAW,OACrB,AAAI,KAAK,KAAK,KAAK,GAAK,EAAE,aAAa,mBACrC,MAAK,UAAU,MAAM,QAAU,SAGjC,AADsB,KAAK,QAAQ,KAAK,GAAM,EAAG,aAAa,mBAAqB,SAEjF,MAAK,UAAU,UAAY,aAC3B,KAAK,UAAU,QAAU,KAAK,gBAE9B,MAAK,UAAU,UAAY,eAC3B,KAAK,UAAU,QAAU,KAAK",
"names": []
}

Просмотреть файл

@ -0,0 +1,150 @@
/*!
* @license
* Copyright 2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
import { ExpandableRowsTableController } from './table';
describe('ExpandableRowsTableController', () => {
let table: HTMLTableElement;
let toggle1: HTMLButtonElement;
let toggle2: HTMLButtonElement;
let toggleAll: HTMLButtonElement;
beforeEach(() => {
document.body.innerHTML = `
<div>
<button class="js-toggleAll">Expand all</button>
</div>
<table class="js-table">
<tbody>
<tr>
<th>Toggle</th>
<th>Foo</th>
<th>Bar</th>
</tr>
<tr>
<td></td>
<td data-id="label-id-1">Hello World</td>
<td>Simple row with no toggle or hidden elements</td>
</tr>
<tr data-aria-controls="hidden-row-id-1 hidden-row-id-2">
<td>
<button
type="button"
aria-expanded="false"
aria-label="2 more from"
data-aria-controls="hidden-row-id-1 hidden-row-id-2"
data-aria-labelledby="toggle-id-1 label-id-2"
data-id="toggle-id-1"
>
+
</button>
</td>
<td data-id="label-id-2">
<span>Baz</span>
</td>
<td></td>
</tr>
<tr data-id="hidden-row-id-1">
<td></td>
<td>First hidden row</td>
<td></td>
</tr>
<tr data-id="hidden-row-id-2">
<td></td>
<td>Second hidden row</td>
<td></td>
</tr>
<tr data-aria-controls="hidden-row-id-3">
<td>
<button
type="button"
aria-expanded="false"
aria-label="2 more from"
data-aria-controls="hidden-row-id-3"
data-aria-labelledby="toggle-id-2 label-id-3"
data-id="toggle-id-2"
>
+
</button>
</td>
<td data-id="label-id-3">
<span>Baz</span>
</td>
<td></td>
</tr>
<tr data-id="hidden-row-id-3">
<td></td>
<td>First hidden row</td>
<td></td>
</tr>
</tbody>
</table>
`;
table = document.querySelector<HTMLTableElement>('.js-table');
toggleAll = document.querySelector<HTMLButtonElement>('.js-toggleAll');
new ExpandableRowsTableController(table, toggleAll);
toggle1 = document.querySelector<HTMLButtonElement>('#toggle-id-1');
toggle2 = document.querySelector<HTMLButtonElement>('#toggle-id-2');
});
afterEach(() => {
document.body.innerHTML = '';
});
it('sets data-aria-* and data-id attributes to regular html attributes', () => {
expect(document.querySelector('#label-id-1')).toBeTruthy();
expect(
document.querySelector('[aria-controls="hidden-row-id-1 hidden-row-id-2"]')
).toBeTruthy();
expect(document.querySelector('[aria-labelledby="toggle-id-1 label-id-2"]')).toBeTruthy();
expect(document.querySelector('#toggle-id-1')).toBeTruthy();
expect(document.querySelector('#label-id-2')).toBeTruthy();
expect(document.querySelector('#hidden-row-id-1')).toBeTruthy();
expect(document.querySelector('#hidden-row-id-2')).toBeTruthy();
});
it('hides rows with unexpanded toggles', () => {
expect(document.querySelector('#hidden-row-id-1').classList).toContain('hidden');
expect(document.querySelector('#hidden-row-id-2').classList).toContain('hidden');
});
it('shows rows with expanded toggles', () => {
toggleAll.click();
expect(document.querySelector('#hidden-row-id-1').classList).toContain('visible');
expect(document.querySelector('#hidden-row-id-2').classList).toContain('visible');
});
it('expands rows when entering text search', () => {
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'f', ctrlKey: true }));
expect(document.querySelector('#hidden-row-id-1').classList).toContain('visible');
expect(document.querySelector('#hidden-row-id-2').classList).toContain('visible');
});
it('toggle expands and collapses all elements', async () => {
jest.useFakeTimers();
toggleAll.click();
jest.runAllTimers();
expect(document.querySelector('#hidden-row-id-1').classList).toContain('visible');
expect(document.querySelector('#hidden-row-id-2').classList).toContain('visible');
expect(toggleAll.innerText).toBe('Collapse all');
toggleAll.click();
jest.runAllTimers();
expect(document.querySelector('#hidden-row-id-1').classList).toContain('hidden');
expect(document.querySelector('#hidden-row-id-2').classList).toContain('hidden');
expect(toggleAll.innerText).toBe('Expand all');
});
it('toggle changes text only when all items expanded', () => {
jest.useFakeTimers();
toggle1.click();
jest.runAllTimers();
expect(toggleAll.innerText).toBe('Expand all');
toggle2.click();
jest.runAllTimers();
expect(toggleAll.innerText).toBe('Collapse all');
});
});

Просмотреть файл

@ -0,0 +1,126 @@
/*!
* @license
* Copyright 2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
/**
* Controller for a table element with expandable rows. Adds event listeners to
* a toggle within a table row that controls visiblity of additional related
* rows in the table.
*
* @example
* ```typescript
* import {ExpandableRowsTableController} from '/static/js/table';
*
* const el = document .querySelector<HTMLTableElement>('.js-myTableElement')
* new ExpandableRowsTableController(el));
* ```
*/
export class ExpandableRowsTableController {
private rows: HTMLTableRowElement[];
private toggles: HTMLButtonElement[];
/**
* Create a table controller.
* @param table - The table element to which the controller binds.
*/
constructor(private table: HTMLTableElement, private toggleAll?: HTMLButtonElement | null) {
this.rows = Array.from(table.querySelectorAll<HTMLTableRowElement>('[data-aria-controls]'));
this.toggles = Array.from(this.table.querySelectorAll('[aria-expanded]'));
this.setAttributes();
this.attachEventListeners();
this.update();
}
/**
* setAttributes sets data-aria-* and data-id attributes to regular
* html attributes as a workaround for limitations from safehtml.
*/
private setAttributes() {
for (const a of ['data-aria-controls', 'data-aria-labelledby', 'data-id']) {
this.table.querySelectorAll(`[${a}]`).forEach(t => {
t.setAttribute(a.replace('data-', ''), t.getAttribute(a) ?? '');
t.removeAttribute(a);
});
}
}
private attachEventListeners() {
this.rows.forEach(t => {
t.addEventListener('click', e => {
this.handleToggleClick(e);
});
});
this.toggleAll?.addEventListener('click', () => {
this.expandAllItems();
});
document.addEventListener('keydown', e => {
if ((e.ctrlKey || e.metaKey) && e.key === 'f') {
this.expandAllItems();
}
});
}
private handleToggleClick(e: MouseEvent) {
let target = e.currentTarget as HTMLTableRowElement | null;
if (!target?.hasAttribute('aria-expanded')) {
target = this.table.querySelector(
`button[aria-controls="${target?.getAttribute('aria-controls')}"]`
);
}
const isExpanded = target?.getAttribute('aria-expanded') === 'true';
target?.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');
e.stopPropagation();
this.update();
}
private expandAllItems = () => {
this.toggles.map(t => t.setAttribute('aria-expanded', 'true'));
this.update();
};
private collapseAllItems = () => {
this.toggles.map(t => t.setAttribute('aria-expanded', 'false'));
this.update();
};
private update = () => {
this.updateVisibleItems();
setTimeout(() => this.updateGlobalToggle());
};
private updateVisibleItems() {
this.rows.map(t => {
const isExpanded = t?.getAttribute('aria-expanded') === 'true';
const rowIds = t?.getAttribute('aria-controls')?.trimEnd().split(' ');
rowIds?.map(id => {
const target = document.getElementById(`${id}`);
if (isExpanded) {
target?.classList.add('visible');
target?.classList.remove('hidden');
} else {
target?.classList.add('hidden');
target?.classList.remove('visible');
}
});
});
}
private updateGlobalToggle() {
if (!this.toggleAll) return;
if (this.rows.some(t => t.hasAttribute('aria-expanded'))) {
this.toggleAll.style.display = 'block';
}
const someCollapsed = this.toggles.some(el => el.getAttribute('aria-expanded') === 'false');
if (someCollapsed) {
this.toggleAll.innerText = 'Expand all';
this.toggleAll.onclick = this.expandAllItems;
} else {
this.toggleAll.innerText = 'Collapse all';
this.toggleAll.onclick = this.collapseAllItems;
}
}
}

Просмотреть файл

@ -33,5 +33,5 @@
top: 1.5rem;
white-space: normal;
width: 12rem;
z-index: 1;
z-index: 100;
}

Просмотреть файл

@ -0,0 +1,7 @@
/**
* @license
* Copyright 2021 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/export class ToolTipController{constructor(i){this.el=i;document.addEventListener("click",e=>{this.el.contains(e.target)||this.el.removeAttribute("open")})}}
//# sourceMappingURL=tooltip.js.map

Просмотреть файл

@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["tooltip.ts"],
"sourcesContent": ["/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * ToolTipController handles closing tooltips on external clicks.\n */\nexport class ToolTipController {\n constructor(private el: HTMLDetailsElement) {\n document.addEventListener('click', e => {\n const insideTooltip = this.el.contains(e.target as Element);\n if (!insideTooltip) {\n this.el.removeAttribute('open');\n }\n });\n }\n}\n"],
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAUO,8BAAwB,CAC7B,YAAoB,EAAwB,CAAxB,UAClB,SAAS,iBAAiB,QAAS,GAAK,CAEtC,AAAK,AADiB,KAAK,GAAG,SAAS,EAAE,SAEvC,KAAK,GAAG,gBAAgB",
"names": []
}

Просмотреть файл

@ -11,29 +11,53 @@ body {
font-size: 1rem;
}
h1 {
font-size: 1.75rem;
line-height: 2rem;
}
h2 {
font-size: 1.5rem;
line-height: 1.75rem;
}
h3 {
h2 {
font-size: 1.375rem;
line-height: 1.625rem;
line-height: 1.5rem;
}
h3 {
font-size: 1.25rem;
line-height: 1.375rem;
}
h4 {
font-size: 1.125rem;
line-height: 1.375rem;
line-height: 1.25rem;
}
h5 {
font-size: 1rem;
line-height: 1.25rem;
line-height: 1.125rem;
}
h6 {
font-size: 0.875rem;
line-height: 1.125rem;
}
@media screen and (min-width: 80rem) {
h1 {
font-size: 1.75rem;
line-height: 2rem;
}
h2 {
font-size: 1.5rem;
line-height: 1.75rem;
}
h3 {
font-size: 1.375rem;
line-height: 1.625rem;
}
h4 {
font-size: 1.125rem;
line-height: 1.375rem;
}
h5 {
font-size: 1rem;
line-height: 1.25rem;
}
}
h1,
h2,
h3,
@ -64,8 +88,11 @@ strong {
}
.go-textSubtle {
color: var(--color-text-subtle);
font-size: 1rem;
line-height: 1.5rem;
}
.go-textTitle {
font-size: 1.125rem;
font-weight: 600;
line-height: 1.25rem;
}
.go-textLabel {
font-size: 0.875rem;
@ -80,7 +107,7 @@ code,
pre,
textarea.code {
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
font-size: 0.8125rem;
font-size: 0.84375rem;
}
pre,
textarea.code {

Просмотреть файл

@ -13,12 +13,7 @@ import (
)
func main() {
_, err := static.Build(static.Config{StaticPath: "content/static/js", Write: true})
if err != nil {
log.Fatal(err)
}
_, err = static.Build(static.Config{StaticPath: "content/static/styleguide", Write: true, Bundle: true})
_, err := static.Build(static.Config{StaticPath: "content/static", Write: true})
if err != nil {
log.Fatal(err)
}

Просмотреть файл

@ -21,7 +21,7 @@ export async function prepare(page: Page): Promise<void> {
pg.$$eval(page, pg.select('UnitHeader-importedby', 'a'), els =>
els.map(el => (el.innerHTML = 'Imported by: 0'))
),
pg.$eval(page, '.Site-header', el => ((el as HTMLElement).style.visibility = 'hidden')),
pg.$eval(page, '.Site-footer', el => ((el as HTMLElement).style.visibility = 'hidden')),
pg.$eval(page, '.go-Header', el => ((el as HTMLElement).style.visibility = 'hidden')),
pg.$eval(page, '.go-Footer', el => ((el as HTMLElement).style.visibility = 'hidden')),
]);
}

Просмотреть файл

@ -170,7 +170,7 @@ func pathNotFoundError(fullPath, requestedVersion string) error {
return &serverError{
status: http.StatusNotFound,
epage: &errorPage{
templateName: "fetch.tmpl",
templateName: "fetch",
MessageData: path,
},
}

Просмотреть файл

@ -40,5 +40,5 @@ func (s *Server) badgeHandler(w http.ResponseWriter, r *http.Request) {
LinkPath: path,
BadgePath: "badge/" + path + ".svg",
}
s.servePage(r.Context(), w, "badge.tmpl", page)
s.servePage(r.Context(), w, "badge", page)
}

Просмотреть файл

@ -28,7 +28,7 @@ func TestBadgeHandler_ServeBadgeTool(t *testing.T) {
}{
{
"/badge/",
"<p>Type a pkg.go.dev URL above to create a badge link.</p>",
`<p class="go-textSubtle">Type a pkg.go.dev URL above to create a badge link.</p>`,
},
{
"/badge/?path=net/http",

Просмотреть файл

@ -30,7 +30,7 @@ func (s *Server) serveDetails(w http.ResponseWriter, r *http.Request, ds interna
return &serverError{status: http.StatusMethodNotAllowed}
}
if r.URL.Path == "/" {
s.staticPageHandler("index.tmpl", "Home")(w, r)
s.staticPageHandler("homepage", "Home")(w, r)
return nil
}
if strings.HasPrefix(r.URL.Path, "/github.com/golang/go") {

Просмотреть файл

@ -280,7 +280,7 @@ func (s *Server) serveSearch(w http.ResponseWriter, r *http.Request, ds internal
return fmt.Errorf("fetchSearchPage(ctx, db, %q): %v", query, err)
}
page.basePage = s.newBasePage(r, fmt.Sprintf("%s - Search Results", query))
s.servePage(ctx, w, "search.tmpl", page)
s.servePage(ctx, w, "search", page)
return nil
}

Просмотреть файл

@ -98,7 +98,7 @@ func NewServer(scfg ServerConfig) (_ *Server, err error) {
serveStats: scfg.ServeStats,
reportingClient: scfg.ReportingClient,
}
errorPageBytes, err := s.renderErrorPage(context.Background(), http.StatusInternalServerError, "error.tmpl", nil)
errorPageBytes, err := s.renderErrorPage(context.Background(), http.StatusInternalServerError, "error", nil)
if err != nil {
return nil, fmt.Errorf("s.renderErrorPage(http.StatusInternalServerError, nil): %v", err)
}
@ -142,7 +142,7 @@ func (s *Server) Install(handle func(string, http.Handler), redisClient *redis.C
handle("/play/fmt", http.HandlerFunc(s.handleFmt))
handle("/play/share", http.HandlerFunc(s.proxyPlayground))
handle("/search", searchHandler)
handle("/search-help", s.staticPageHandler("search_help.tmpl", "Search Help"))
handle("/search-help", s.staticPageHandler("search-help", "Search Help"))
handle("/license-policy", s.licensePolicyHandler())
handle("/about", http.RedirectHandler("https://go.dev/about", http.StatusFound))
handle("/badge/", http.HandlerFunc(s.badgeHandler))
@ -265,6 +265,14 @@ type basePage struct {
// AllowWideContent indicates whether the content should be displayed in a
// way thats amenable to wider viewports.
AllowWideContent bool
// Enables the two and three column layouts on the unit page.
UseResponsiveLayout bool
// UseSiteWrapper indicates whether the page content should be wrapped in the
// Site class. This is only used for unit pages until the migration to the new
// layout base page is completed.
UseSiteWrapper bool
}
// licensePolicyPage is used to generate the static license policy page.
@ -282,7 +290,7 @@ func (s *Server) licensePolicyHandler() http.HandlerFunc {
LicenseFileNames: licenses.FileNames,
LicenseTypes: lics,
}
s.servePage(r.Context(), w, "license_policy.tmpl", page)
s.servePage(r.Context(), w, "license-policy", page)
})
}
@ -312,7 +320,7 @@ type errorPage struct {
func (s *Server) PanicHandler() (_ http.HandlerFunc, err error) {
defer derrors.Wrap(&err, "PanicHandler")
status := http.StatusInternalServerError
buf, err := s.renderErrorPage(context.Background(), status, "error.tmpl", nil)
buf, err := s.renderErrorPage(context.Background(), status, "error", nil)
if err != nil {
return nil, err
}
@ -392,7 +400,7 @@ func (s *Server) reportError(ctx context.Context, err error, w http.ResponseWrit
}
func (s *Server) serveErrorPage(w http.ResponseWriter, r *http.Request, status int, page *errorPage) {
template := "error.tmpl"
template := "error"
if page != nil {
if page.AppVersionLabel == "" || page.GoogleTagManagerID == "" {
// If the basePage was properly created using newBasePage, both
@ -436,7 +444,7 @@ func (s *Server) renderErrorPage(ctx context.Context, status int, templateName s
page.HTMLTitle = statusInfo
}
if templateName == "" {
templateName = "error.tmpl"
templateName = "error"
}
etmpl, err := s.findTemplate(templateName)
@ -538,14 +546,7 @@ func parsePageTemplates(base template.TrustedSource) (map[string]*template.Templ
tsc := template.TrustedSourceFromConstant
join := template.TrustedSourceJoin
htmlSets := [][]template.TrustedSource{
{tsc("badge.tmpl")},
{tsc("error.tmpl")},
{tsc("fetch.tmpl")},
{tsc("index.tmpl")},
{tsc("license_policy.tmpl")},
{tsc("search.tmpl")},
{tsc("search_help.tmpl")},
legacyHtmlSets := [][]template.TrustedSource{
{tsc("unit_details.tmpl"), tsc("unit.tmpl")},
{tsc("unit_importedby.tmpl"), tsc("unit.tmpl")},
{tsc("unit_imports.tmpl"), tsc("unit.tmpl")},
@ -554,8 +555,8 @@ func parsePageTemplates(base template.TrustedSource) (map[string]*template.Templ
}
templates := make(map[string]*template.Template)
for _, set := range htmlSets {
t, err := template.New("base.tmpl").Funcs(templateFuncs).ParseFilesFromTrustedSources(join(base, tsc("html"), tsc("base.tmpl")))
for _, set := range legacyHtmlSets {
t, err := template.New("base.tmpl").Funcs(templateFuncs).ParseFilesFromTrustedSources(join(base, tsc("base"), tsc("base.tmpl")))
if err != nil {
return nil, fmt.Errorf("ParseFiles: %v", err)
}
@ -563,6 +564,11 @@ func parsePageTemplates(base template.TrustedSource) (map[string]*template.Templ
if _, err := t.ParseGlobFromTrustedSource(helperGlob); err != nil {
return nil, fmt.Errorf("ParseGlob(%q): %v", helperGlob, err)
}
header := join(base, tsc("header"), tsc("header.partial.tmpl"))
footer := join(base, tsc("footer"), tsc("footer.partial.tmpl"))
if _, err := t.ParseFilesFromTrustedSources(header, footer); err != nil {
return nil, fmt.Errorf("ParseFilesFromTrustedSources(%v, %v): %v", header, footer, err)
}
var files []template.TrustedSource
for _, f := range set {
@ -574,10 +580,18 @@ func parsePageTemplates(base template.TrustedSource) (map[string]*template.Templ
templates[set[0].String()] = t
}
styleGuideSets := [][]template.TrustedSource{
htmlSets := [][]template.TrustedSource{
{tsc("styleguide"), tsc("main-layout")},
{tsc("homepage")},
{tsc("badge")},
{tsc("error")},
{tsc("fetch")},
{tsc("license-policy")},
{tsc("search-help")},
{tsc("search")},
}
for _, set := range styleGuideSets {
for _, set := range htmlSets {
t, err := template.New("base.tmpl").Funcs(templateFuncs).ParseFilesFromTrustedSources(join(base, tsc("base/base.tmpl")))
if err != nil {
return nil, fmt.Errorf("ParseFilesFromTrustedSources: %v", err)
@ -605,7 +619,7 @@ func (s *Server) staticHandler() http.Handler {
// and rebuild them on file changes.
if s.devMode {
ctx := context.Background()
_, err := static.Build(static.Config{StaticPath: staticPath + "/js", Watch: true, Write: true})
_, err := static.Build(static.Config{StaticPath: staticPath, Watch: true, Write: true})
if err != nil {
log.Error(ctx, err)
}

Просмотреть файл

@ -683,8 +683,8 @@ func serverTestCases() []serverTestCase {
urlPath: "/license-policy",
wantStatusCode: http.StatusOK,
want: in("",
in(".Content-header", hasText("License Disclaimer")),
in(".Content",
in("h1", hasText("License Disclaimer")),
in(".go-Content",
hasText("The Go website displays license information"),
hasText("this is not legal advice"))),
},
@ -1554,27 +1554,27 @@ func TestCheckTemplates(t *testing.T) {
// error.tmpl omitted because relies on an associated "message" template
// that's parsed on demand; see renderErrorPage above.
{"fetch", nil, errorPage{}},
{"index", nil, basePage{}},
{"license_policy", nil, licensePolicyPage{}},
{"homepage", nil, basePage{}},
{"license-policy", nil, licensePolicyPage{}},
{"search", nil, SearchPage{}},
{"search_help", nil, basePage{}},
{"unit_details", nil, UnitPage{}},
{"search-help", nil, basePage{}},
{"unit_details.tmpl", nil, UnitPage{}},
{
"unit_details",
"unit_details.tmpl",
[]string{"unit_outline", "unit_readme", "unit_doc", "unit_files", "unit_directories"},
MainDetails{},
},
{"unit_importedby", nil, UnitPage{}},
{"unit_importedby", []string{"importedby"}, ImportedByDetails{}},
{"unit_imports", nil, UnitPage{}},
{"unit_imports", []string{"imports"}, ImportsDetails{}},
{"unit_licenses", nil, UnitPage{}},
{"unit_licenses", []string{"licenses"}, LicensesDetails{}},
{"unit_versions", nil, UnitPage{}},
{"unit_versions", []string{"versions"}, VersionsDetails{}},
{"unit_importedby.tmpl", nil, UnitPage{}},
{"unit_importedby.tmpl", []string{"importedby"}, ImportedByDetails{}},
{"unit_imports.tmpl", nil, UnitPage{}},
{"unit_imports.tmpl", []string{"imports"}, ImportsDetails{}},
{"unit_licenses.tmpl", nil, UnitPage{}},
{"unit_licenses.tmpl", []string{"licenses"}, LicensesDetails{}},
{"unit_versions.tmpl", nil, UnitPage{}},
{"unit_versions.tmpl", []string{"versions"}, VersionsDetails{}},
} {
t.Run(c.name, func(t *testing.T) {
tm := templates[c.name+".tmpl"]
tm := templates[c.name]
if tm == nil {
t.Fatalf("no template %q", c.name)
}

Просмотреть файл

@ -38,6 +38,7 @@ func (s *Server) serveStyleGuide(w http.ResponseWriter, r *http.Request, ds inte
page, err := styleGuide(ctx, s.staticPath.String())
page.basePage = s.newBasePage(r, "")
page.AllowWideContent = true
page.UseResponsiveLayout = true
if err != nil {
return err
}

Просмотреть файл

@ -178,7 +178,7 @@ func (s *Server) serveUnitPage(ctx context.Context, w http.ResponseWriter, r *ht
tabSettings := unitTabLookup[tab]
title := pageTitle(um)
basePage := s.newBasePage(r, title)
basePage.AllowWideContent = true
basePage.UseSiteWrapper = true
lv := linkVersion(um.Version, um.ModulePath)
page := UnitPage{
basePage: basePage,

Просмотреть файл

@ -15,14 +15,12 @@ var scriptHashes = []string{
"'sha256-CoGrkqEM1Kjjf5b1bpcnDLl8ZZLAsVX+BoAzZ5+AOmc='",
"'sha256-karKh1IrXOF1g+uoSxK+k9BuciCwYY/ytGuQVUiRzcM='",
"'sha256-2owiLItzX793qLVobnd0z9Z90a2wej4/C73Qb0qNtO4='",
// From content/static/html/base.tmpl
"'sha256-dwce5DnVX7uk6fdvvNxQyLTH/cJrTMDK6zzrdKwdwcg='",
"'sha256-M35cNZ8vPcaBGw5WTgh0Gn7DLsxkvPbdTFN1pELeevM='",
"'sha256-CgM7SjnSbDyuIteS+D1CQuSnzyKwL0qtXLU6ZW2hB+g='",
// From content/static/html/pages/badge.tmpl
"'sha256-v9+UvX+P27rKraeTl7uAfOWdLmmQU39RskIoqUrU4wo='",
// From content/static/html/pages/fetch.tmpl
"'sha256-4FhQmh9Hu76JzYm35KNNysU2Z7buJwg3cMSHsGwKSCE='",
// From content/static/badge/badge.tmpl
"'sha256-lYbH/hg0O4Oc1stV4ysso2zlUCXY0yGRUZ5zDOnZ9hI='",
// From content/static/fetch/fetch.tmpl
"'sha256-NL8cRfvzPNDO6ZYKQYWS1kPknRV9gUCJoTk+fRR04zg='",
// From content/static/main-layout/main-layout.tmpl
"'sha256-cVUOUH4vbDOJY6vJAjvJj3d2jAmDiwy/SyVsZgZVxVM='",
// From content/static/html/pages/unit.tmpl
"'sha256-r4g06j/B7WYKOSl8cFfvuZOyiYA1tOyrbnxapiSP64g='",
// From content/static/html/pages/unit_details.tmpl

Просмотреть файл

@ -7,7 +7,8 @@ package static
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/evanw/esbuild/pkg/api"
@ -33,13 +34,13 @@ type Config struct {
func Build(config Config) (*api.BuildResult, error) {
var entryPoints []string
scriptDir := config.StaticPath
files, err := ioutil.ReadDir(scriptDir)
files, err := getEntry(config.StaticPath)
if err != nil {
return nil, err
}
for _, v := range files {
if strings.HasSuffix(v.Name(), ".ts") && !strings.HasSuffix(v.Name(), ".test.ts") {
entryPoints = append(entryPoints, scriptDir+"/"+v.Name())
if strings.HasSuffix(v, ".ts") && !strings.HasSuffix(v, ".test.ts") {
entryPoints = append(entryPoints, v)
}
}
options := api.BuildOptions{
@ -66,3 +67,25 @@ func Build(config Config) (*api.BuildResult, error) {
}
return &result, nil
}
func getEntry(dir string) ([]string, error) {
var matches []string
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if matched, err := filepath.Match("*.ts", filepath.Base(path)); err != nil {
return err
} else if matched {
matches = append(matches, path)
}
return nil
})
if err != nil {
return nil, err
}
return matches, nil
}