Skip to content

Commit

Permalink
feat: build modern template TOC as JSON (#8498)
Browse files Browse the repository at this point in the history
  • Loading branch information
yufeih authored Mar 11, 2023
1 parent dea1aa7 commit 09393f9
Show file tree
Hide file tree
Showing 65 changed files with 1,341 additions and 1,046 deletions.
1 change: 1 addition & 0 deletions docs/docs/table-of-contents.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ The YAML document is a tree of TOC nodes, each of which has these properties:
- `name`: The display name for the TOC node.
- `href`: The path the TOC node leads to. Optional because a node can exist just to parent other nodes.
- `items`: If a node has children, they're listed in the items array.
- `expanded`: Expand children on load, only works if the template is `modern`.

## Navigation Bar

Expand Down
4 changes: 0 additions & 4 deletions samples/seed/articles/toc.md

This file was deleted.

11 changes: 11 additions & 0 deletions samples/seed/articles/toc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
- name: Getting Started
href: docfx_getting_started.md
- name: Engineering Docs
expanded: true
items:
- name: Engineering Guidelines
href: engineering_guidelines.md
- name: CSharp Coding Standards
href: csharp_coding_standards.md
- name: Markdown
href: markdown.md
3 changes: 2 additions & 1 deletion samples/seed/docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"src": "dotnet"
}
],
"namespaceLayout": "nested",
"dest": "obj/api"
}
],
Expand All @@ -27,7 +28,7 @@
"dest": "api"
},
{
"files": [ "articles/**/*.md", "*.md", "toc.yml", "restapi/**" ]
"files": [ "articles/**/*.{md,yml}", "*.md", "toc.yml", "restapi/**" ]
}
],
"resource": [
Expand Down
1 change: 0 additions & 1 deletion samples/seed/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
href: index.md
- name: Articles
href: articles/
homepage: articles/docfx_getting_started.md
- name: API Documentation
href: obj/api/
- name: REST API
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ private Manifest BuildCore(DocumentBuildParameters parameters)
{
using (new LoggerPhaseScope(PhaseName, LogLevel.Verbose))
{
Logger.LogInfo($"Max parallelism is {parameters.MaxParallelism}.");
Directory.CreateDirectory(parameters.OutputBaseDir);

var context = new DocumentBuildContext(parameters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,6 @@ public void Dispose()

public async Task ExtractMetadataAsync()
{
if (_config.NamespaceLayout is NamespaceLayout.Nested)
{
Logger.LogSuggestion($"Nested namespace is experimental and may produce undesired result.");
}

if (_files.TryGetValue(FileType.NotSupported, out List<FileInformation> unsupportedFiles))
{
foreach (var file in unsupportedFiles)
Expand Down
4 changes: 2 additions & 2 deletions templates/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,12 @@ function serve() {
open: true,
startPath: '/test',
files: [
'default/styles/**',
'modern/styles/**',
join(project, '_site', '**')
],
server: {
routes: {
'/test/styles': 'default/styles',
'/test/styles': 'modern/styles',
'/test': join(project, '_site')
}
}
Expand Down
2 changes: 1 addition & 1 deletion templates/modern/layout/_master.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="sidetoc" id="sidetoc"></nav>
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
Expand Down
8 changes: 5 additions & 3 deletions templates/modern/src/docfx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { breakText } from './helper'
import { renderMarkdown } from './markdown'
import { enableSearch } from './search'
import { renderToc } from './toc'
import { renderInThisArticle, renderNavbar } from './nav'
import { renderBreadcrumb, renderInThisArticle, renderNavbar } from './nav'

import 'highlight.js/scss/github.scss'
import 'bootstrap-icons/font/bootstrap-icons.scss'
Expand All @@ -30,8 +30,10 @@ document.addEventListener('DOMContentLoaded', function() {
enableSearch()

renderMarkdown()
renderNavbar()
renderToc()

Promise.all([renderNavbar(), renderToc()])
.then(([navbar, toc]) => renderBreadcrumb([...navbar, ...toc]))

renderInThisArticle()

breakText()
Expand Down
95 changes: 7 additions & 88 deletions templates/modern/src/helper.ts
Original file line number Diff line number Diff line change
@@ -1,110 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

import $ from 'jquery'

export function meta(name: string): string {
return (document.querySelector(`meta[name="${name}"]`) as HTMLMetaElement)?.content
}

export function isVisible(element: Element): boolean {
return (element as HTMLElement).offsetParent != null
}

export function getAbsolutePath(href: string): string {
if (isAbsolutePath(href)) {
return href
}
const currentAbsPath = getCurrentWindowAbsolutePath()
const stack = currentAbsPath.split('/')
stack.pop()
const parts = href.split('/')
for (let i = 0; i < parts.length; i++) {
if (parts[i] === '.') continue
if (parts[i] === '..' && stack.length > 0) {
stack.pop()
} else {
stack.push(parts[i])
}
}
return stack.join('/')
}

export function isRelativePath(href: string): boolean {
if (href === undefined || href === '' || href[0] === '/') {
return false
}
return !isAbsolutePath(href)
}

export function isAbsolutePath(href: string): boolean {
return (/^(?:[a-z]+:)?\/\//i).test(href)
}

export function getDirectory(href: string): string {
if (!href) return '.'
const index = href.lastIndexOf('/')
return index < 0 ? '.' : href.slice(0, index)
}

export function getCurrentWindowAbsolutePath() {
return window.location.origin + window.location.pathname
}

export function formList(item, classes) {
let level = 1
const model = {
items: item
}
const cls = [].concat(classes).join(' ')
return getList(model, cls)

function getList(model, cls) {
if (!model || !model.items) return null
const l = model.items.length
if (l === 0) return null
let html = '<ul class="level' + level + ' ' + (cls || '') + '">'
level++
for (let i = 0; i < l; i++) {
const item = model.items[i]
const href = item.href
const name = item.name
if (!name) continue
html += href ? '<li><a href="' + href + '">' + name + '</a>' : '<li>' + name
html += getList(item, cls) || ''
html += '</li>'
}
html += '</ul>'
return html
}
}

/**
* Add <wbr> into long word.
* @param {String} text - The word to break. It should be in plain text without HTML tags.
*/
export function breakPlainText(text) {
function breakPlainText(text) {
if (!text) return text
return text.replace(/([a-z])([A-Z])|(\.)(\w)/g, '$1$3<wbr>$2$4')
}

/**
* Add <wbr> into long word. The jQuery element should contain no html tags.
* If the jQuery element contains tags, this function will not change the element.
* Add <wbr> into long word.
*/
export function breakWord(e) {
if (!e.html().match(/(<\w*)((\s\/>)|(.*<\/\w*>))/g)) {
e.html(function(index, text) {
return breakPlainText(text)
})
function breakWord(e: Element) {
if (!e.innerHTML.match(/(<\w*)((\s\/>)|(.*<\/\w*>))/g)) {
e.innerHTML = breakPlainText(e.innerHTML)
}
return e
}

export function breakText() {
$('.xref').addClass('text-break')
const texts = $('.text-break')
texts.each(function() {
breakWord($(this))
})
document.querySelectorAll('.xref').forEach(e => e.classList.add('text-break'))
document.querySelectorAll('.text-break').forEach(e => breakWord(e))
}
6 changes: 4 additions & 2 deletions templates/modern/src/layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ body[data-layout="landing"] {
display: grid;
gap: 3rem;
grid-template-columns: .4fr 1fr min-content;
padding-bottom: 0;

@include media-breakpoint-down(lg) {
gap: 1rem;
Expand All @@ -123,7 +124,7 @@ body[data-layout="landing"] {
display: block;
overflow-x: hidden;
overflow-y: auto;
max-height: calc(100vh - #{$header-height} - #{$footer-height});
max-height: calc(100vh - #{$header-height});

@include sticky-top($header-height);

Expand All @@ -136,6 +137,7 @@ body[data-layout="landing"] {
>.content {
min-width: 0;
margin: 0 0 4rem;
padding-bottom: $main-padding-bottom;

>.actionbar {
display: flex;
Expand All @@ -151,7 +153,7 @@ body[data-layout="landing"] {
>.affix {
display: block;
width: 254px;
max-height: calc(100vh - #{$header-height} - #{$footer-height});
max-height: calc(100vh - #{$header-height});
overflow-x: hidden;
overflow-y: auto;

Expand Down
Loading

0 comments on commit 09393f9

Please sign in to comment.