diff --git a/docs/contribute/locally.md b/docs/contribute/locally.md index 08eb9c0f2..9e4c844fd 100644 --- a/docs/contribute/locally.md +++ b/docs/contribute/locally.md @@ -30,8 +30,6 @@ This guide uses option one. If you'd like to clone the repository and build from :::{tab-item} macOS -### macOS - 1. **Download the Binary:** Download the latest macOS binary from [releases](https://github.com/elastic/docs-builder/releases/latest/): ```sh @@ -54,8 +52,6 @@ This guide uses option one. If you'd like to clone the repository and build from :::{tab-item} Windows -### Windows - 1. **Download the Binary:** Download the latest Windows binary from [releases](https://github.com/elastic/docs-builder/releases/latest/): ```sh @@ -78,8 +74,6 @@ This guide uses option one. If you'd like to clone the repository and build from :::{tab-item} Linux -### Linux - 1. **Download the Binary:** Download the latest Linux binary from [releases](https://github.com/elastic/docs-builder/releases/latest/): ```sh @@ -147,4 +141,4 @@ After you've made your changes locally, ## Step 5: View on elastic.co/docs -soon... \ No newline at end of file +soon... diff --git a/src/Elastic.Markdown/Assets/main.ts b/src/Elastic.Markdown/Assets/main.ts index 45b3f2846..b3078f6b7 100644 --- a/src/Elastic.Markdown/Assets/main.ts +++ b/src/Elastic.Markdown/Assets/main.ts @@ -1,8 +1,9 @@ import {initNav} from "./pages-nav"; import {initTocNav} from "./toc-nav"; - import {initHighlight} from "./hljs"; +import {initTabs} from "./tabs"; initNav(); initTocNav(); initHighlight(); +initTabs(); diff --git a/src/Elastic.Markdown/Assets/markdown/tabs.css b/src/Elastic.Markdown/Assets/markdown/tabs.css new file mode 100644 index 000000000..9457fa676 --- /dev/null +++ b/src/Elastic.Markdown/Assets/markdown/tabs.css @@ -0,0 +1,36 @@ +@layer components { + .tabs { + @apply flex flex-wrap relative overflow-hidden; + + .tabs-label { + @apply cursor-pointer px-6 py-2 z-20 text-ink-light flex items-center; + + &:hover { + @apply border-b-1 border-b-black text-black bg-gray-100; + } + } + + .tabs-input { + @apply opacity-0 absolute; + } + + .tabs-content { + @apply w-full order-99 border-t-1 border-gray-300 pl-6 pt-4 z-0 hidden; + transform: translateY(-1px); + } + + .tabs-input:checked+.tabs-label+.tabs-content { + @apply block; + } + + .tabs-input:checked+.tabs-label, + .tabs-label:active { + @apply border-b-1 border-b-blue-elastic text-blue-elastic; + } + + .tabs-input:focus-visible+.tabs-label { + outline: var(--outline-size) var(--outline-style) var(--outline-color); + outline-offset: var(--outline-offset, var(--outline-size)); + } + } +} diff --git a/src/Elastic.Markdown/Assets/styles.css b/src/Elastic.Markdown/Assets/styles.css index cf784cac0..ebf9d0200 100644 --- a/src/Elastic.Markdown/Assets/styles.css +++ b/src/Elastic.Markdown/Assets/styles.css @@ -4,6 +4,7 @@ @import "highlight.js/styles/atom-one-dark.css"; @import "./markdown/typography.css"; @import "./markdown/list.css"; +@import "./markdown/tabs.css"; #default-search::-webkit-search-cancel-button { padding-right: calc(var(--spacing) * 2); @@ -88,3 +89,24 @@ * { scroll-margin-top: calc(var(--spacing) * 26); } + +:root { + --outline-size: max(2px, 0.08em); + --outline-style: auto; + --outline-color: var(--color-blue-elastic); + --outline-offset: 5; +} + +:is(a, button, input, textarea, summary):focus { + outline: var(--outline-size) var(--outline-style) var(--outline-color); + outline-offset: var(--outline-offset, var(--outline-size)); +} + +:is(a, button, input, textarea, summary):focus-visible { + outline: var(--outline-size) var(--outline-style) var(--outline-color); + outline-offset: var(--outline-offset, var(--outline-size)); +} + +:is(a, button, input, textarea, summary):focus:not(:focus-visible) { + outline: none; +} diff --git a/src/Elastic.Markdown/Assets/tabs.ts b/src/Elastic.Markdown/Assets/tabs.ts new file mode 100644 index 000000000..cfc692dd0 --- /dev/null +++ b/src/Elastic.Markdown/Assets/tabs.ts @@ -0,0 +1,106 @@ +// TODO: refactor to typescript. this was copied from the previous implementation + +// @ts-check + +// Extra JS capability for selected tabs to be synced +// The selection is stored in local storage so that it persists across page loads. + +/** + * @type {Record} + */ +let sd_id_to_elements = {}; +const storageKeyPrefix = "sphinx-design-tab-id-"; + +/** + * Create a key for a tab element. + * @param {HTMLElement} el - The tab element. + * @returns {[string, string, string] | null} - The key. + * + */ +function create_key(el) { + let syncId = el.getAttribute("data-sync-id"); + let syncGroup = el.getAttribute("data-sync-group"); + if (!syncId || !syncGroup) return null; + return [syncGroup, syncId, syncGroup + "--" + syncId]; +} + +/** + * Initialize the tab selection. + * + */ +function ready() { + // Find all tabs with sync data + + /** @type {string[]} */ + let groups = []; + + document.querySelectorAll(".tabs-label").forEach((label) => { + if (label instanceof HTMLElement) { + let data = create_key(label); + if (data) { + let [group, id, key] = data; + + // add click event listener + // @ts-ignore + label.onclick = onSDLabelClick; + + // store map of key to elements + if (!sd_id_to_elements[key]) { + sd_id_to_elements[key] = []; + } + sd_id_to_elements[key].push(label); + + if (groups.indexOf(group) === -1) { + groups.push(group); + // Check if a specific tab has been selected via URL parameter + const tabParam = new URLSearchParams(window.location.search).get( + group + ); + if (tabParam) { + console.log( + "sphinx-design: Selecting tab id for group '" + + group + + "' from URL parameter: " + + tabParam + ); + window.sessionStorage.setItem(storageKeyPrefix + group, tabParam); + } + } + + // Check is a specific tab has been selected previously + let previousId = window.sessionStorage.getItem( + storageKeyPrefix + group + ); + if (previousId === id) { + // console.log( + // "sphinx-design: Selecting tab from session storage: " + id + // ); + // @ts-ignore + label.previousElementSibling.checked = true; + } + } + } + }); +} + +/** + * Activate other tabs with the same sync id. + * + * @this {HTMLElement} - The element that was clicked. + */ +function onSDLabelClick() { + let data = create_key(this); + if (!data) return; + let [group, id, key] = data; + for (const label of sd_id_to_elements[key]) { + if (label === this) continue; + // @ts-ignore + label.previousElementSibling.checked = true; + } + window.sessionStorage.setItem(storageKeyPrefix + group, id); +} + +export function initTabs() { + console.log("inittabs"); + document.addEventListener("DOMContentLoaded", ready, false); +} diff --git a/src/Elastic.Markdown/Elastic.Markdown.csproj b/src/Elastic.Markdown/Elastic.Markdown.csproj index d4a8701ca..15b337c15 100644 --- a/src/Elastic.Markdown/Elastic.Markdown.csproj +++ b/src/Elastic.Markdown/Elastic.Markdown.csproj @@ -40,10 +40,10 @@ - - - - + + + + diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs index b5f907722..d0c892c1c 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs @@ -170,6 +170,19 @@ private void WriteTabSet(HtmlRenderer renderer, TabSetBlock block) RenderRazorSlice(slice, renderer, block); } + private void WriteTabItem(HtmlRenderer renderer, TabItemBlock block) + { + var slice = TabItem.Create(new TabItemViewModel + { + Index = block.Index, + Title = block.Title, + TabSetIndex = block.TabSetIndex, + SyncKey = block.SyncKey, + TabSetGroupKey = block.TabSetGroupKey + }); + RenderRazorSlice(slice, renderer, block); + } + private void WriteMermaid(HtmlRenderer renderer, MermaidBlock block) { var slice = Mermaid.Create(new MermaidViewModel()); @@ -185,19 +198,6 @@ private void WriteApplies(HtmlRenderer renderer, AppliesBlock block) RenderRazorSliceNoContent(slice, renderer); } - private void WriteTabItem(HtmlRenderer renderer, TabItemBlock block) - { - var slice = TabItem.Create(new TabItemViewModel - { - Index = block.Index, - Title = block.Title, - TabSetIndex = block.TabSetIndex, - SyncKey = block.SyncKey, - TabSetGroupKey = block.TabSetGroupKey - }); - RenderRazorSlice(slice, renderer, block); - } - private void WriteLiteralIncludeBlock(HtmlRenderer renderer, IncludeBlock block) { if (!block.Found || block.IncludePath is null) diff --git a/src/Elastic.Markdown/Slices/Directives/TabItem.cshtml b/src/Elastic.Markdown/Slices/Directives/TabItem.cshtml index 181a101c9..b11cd161b 100644 --- a/src/Elastic.Markdown/Slices/Directives/TabItem.cshtml +++ b/src/Elastic.Markdown/Slices/Directives/TabItem.cshtml @@ -1,6 +1,6 @@ @inherits RazorSlice - - -
+ + +
[CONTENT]
diff --git a/src/Elastic.Markdown/Slices/Directives/TabSet.cshtml b/src/Elastic.Markdown/Slices/Directives/TabSet.cshtml index d11bb9532..a22edf60b 100644 --- a/src/Elastic.Markdown/Slices/Directives/TabSet.cshtml +++ b/src/Elastic.Markdown/Slices/Directives/TabSet.cshtml @@ -1,4 +1,4 @@ @inherits RazorSlice -
+
[CONTENT]
diff --git a/src/Elastic.Markdown/Slices/Directives/_ViewModels.cs b/src/Elastic.Markdown/Slices/Directives/_ViewModels.cs index 6960dccf9..534ccce7b 100644 --- a/src/Elastic.Markdown/Slices/Directives/_ViewModels.cs +++ b/src/Elastic.Markdown/Slices/Directives/_ViewModels.cs @@ -2,6 +2,7 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information using System.Text; +using Elastic.Markdown.Myst.Directives; using Elastic.Markdown.Myst.Settings; namespace Elastic.Markdown.Slices.Directives;