feat: Doc page layout

- 3 Columns layout with Sidebar, Main and TOC
- Night owl hljs theme
- Sidebar with groups
This commit is contained in:
Faris Ansari 2020-05-24 07:38:10 +05:30
parent 5b1dacb6a1
commit 622667e130
7 changed files with 415 additions and 118 deletions

View file

@ -0,0 +1,183 @@
/*
Night Owl for highlight.js (c) Carl Baxter <carl@cbax.tech>
An adaptation of Sarah Drasner's Night Owl VS Code Theme
https://github.com/sdras/night-owl-vscode-theme
Copyright (c) 2018 Sarah Drasner
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
.hljs {
display: block;
overflow-x: auto;
padding: 1rem 1.25rem;
background: #011627;
color: #d6deeb;
border-radius: 0.5rem;
}
/* General Purpose */
.hljs-keyword {
color: #c792ea;
font-style: italic;
}
.hljs-built_in {
color: #addb67;
font-style: italic;
}
.hljs-type {
color: #82aaff;
}
.hljs-literal {
color: #ff5874;
}
.hljs-number {
color: #F78C6C;
}
.hljs-regexp {
color: #5ca7e4;
}
.hljs-string {
color: #ecc48d;
}
.hljs-subst {
color: #d3423e;
}
.hljs-symbol {
color: #82aaff;
}
.hljs-class {
color: #ffcb8b;
}
.hljs-function {
color: #82AAFF;
}
.hljs-title {
color: #DCDCAA;
font-style: italic;
}
.hljs-params {
color: #7fdbca;
}
/* Meta */
.hljs-comment {
color: #637777;
font-style: italic;
}
.hljs-doctag {
color: #7fdbca;
}
.hljs-meta {
color: #82aaff;
}
.hljs-meta-keyword {
color: #82aaff;
}
.hljs-meta-string {
color: #ecc48d;
}
/* Tags, attributes, config */
.hljs-section {
color: #82b1ff;
}
.hljs-tag,
.hljs-name,
.hljs-builtin-name {
color: #7fdbca;
}
.hljs-attr {
color: #7fdbca;
}
.hljs-attribute {
color: #80cbc4;
}
.hljs-variable {
color: #addb67;
}
/* Markup */
.hljs-bullet {
color: #d9f5dd;
}
.hljs-code {
color: #80CBC4;
}
.hljs-emphasis {
color: #c792ea;
font-style: italic;
}
.hljs-strong {
color: #addb67;
font-weight: bold;
}
.hljs-formula {
color: #c792ea;
}
.hljs-link {
color: #ff869a;
}
.hljs-quote {
color: #697098;
font-style: italic;
}
/* CSS */
.hljs-selector-tag {
color: #ff6363;
}
.hljs-selector-id {
color: #fad430;
}
.hljs-selector-class {
color: #addb67;
font-style: italic;
}
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #c792ea;
font-style: italic;
}
/* Templates */
.hljs-template-tag {
color: #c792ea;
}
.hljs-template-variable {
color: #addb67;
}
/* diff */
.hljs-addition {
color: #addb67ff;
font-style: italic;
}
.hljs-deletion {
color: #EF535090;
font-style: italic;
}

105
frappe/public/scss/doc.scss Normal file
View file

@ -0,0 +1,105 @@
.doc-layout {
padding-left: 0;
padding-right: 0;
border-bottom: 1px solid $gray-200;
}
.doc-layout .sidebar-column {
background-color: $light;
}
.doc-search {
position: relative;
.search-icon {
position: absolute;
left: 0;
top: 0;
width: 2.5rem;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
svg {
color: $gray-600;
}
input {
padding-left: 2.5rem;
}
}
.doc-sidebar {
position: sticky;
top: 0;
padding-left: 4rem;
padding-right: 2rem;
padding-bottom: 4rem;
height: 100vh;
overflow: hidden;
.web-sidebar {
height: 100%;
overflow: auto;
padding-top: 0;
padding-bottom: 4rem;
}
}
.doc-main .page-content-wrapper {
padding: 2rem 4rem 4rem 4rem;
}
.doc-sidebar-logo {
padding-top: 2.5rem;
padding-bottom: 2rem;
}
.sidebar-group {
padding-top: 1rem;
padding-bottom: 1rem;
> ul {
padding-left: 1rem;
}
}
.page-toc {
font-size: $font-size-sm;
h5 {
font-size: $font-size-sm;
margin-bottom: 0.5rem;
color: $gray-500;
}
> div {
padding-top: 8rem;
position: sticky;
top: 0;
}
ul {
padding-left: 0;
list-style-type: none;
}
li > ul {
padding-left: 0.5rem;
}
a {
display: block;
padding: 0.25rem 0;
color: $gray-600;
text-decoration: none;
font-weight: 500;
&:hover {
color: $gray-700;
}
}
}

View file

@ -50,7 +50,6 @@
}
h1 + p {
max-width: 42rem;
margin-top: 0.75rem;
font-size: $font-size-base;
color: $gray-900;
@ -114,6 +113,12 @@
border: 1px solid $gray-400;
border-radius: 0.375rem;
}
code:not(.hljs) {
padding: 0 0.25rem;
background: $light;
border-radius: 0.125rem;
}
}
// apply margin on first h1 if container is full width without top margin
@ -122,41 +127,3 @@ main:not(.my-5) .from-markdown {
margin-top: 5rem;
}
}
.page-toc {
font-size: $font-size-sm;
h5 {
font-size: $font-size-sm;
margin-bottom: 0.5rem;
color: $gray-500;
}
> div {
padding-top: 2rem;
position: sticky;
top: 0;
}
ul {
padding-left: 0;
list-style-type: none;
}
li > ul {
padding-left: 0.5rem;
}
a {
display: block;
padding: 0.25rem 0;
color: $gray-600;
text-decoration: none;
font-weight: 500;
&:hover {
color: $gray-700;
}
}
}

View file

@ -7,6 +7,7 @@
@import 'page-builder';
@import 'markdown';
@import 'sidebar';
@import 'doc';
.container {
padding-left: 1.25rem;

View file

@ -1,69 +1,68 @@
{% extends base_template_path %}
{% extends "templates/base.html" %}
{% macro page_content() %}
{%- block page_content -%}{%- endblock -%}
{% endmacro %}
{%- block head_include %}
<link rel="stylesheet" href="/assets/frappe/css/hljs-night-owl.css">
{% endblock -%}
{%- block navbar -%}{%- endblock -%}
{% block content %}
{% macro main_content() %}
<div class="page-content-wrapper">
<!-- breadcrumbs -->
<div class="page-breadcrumbs">
{%- block breadcrumbs -%}
{%- include 'templates/includes/breadcrumbs.html' -%}
{%- endblock -%}
</div>
asdfasdf
{% block page_container %}
<main class="row">
<div class="page_content page-content {{ 'col-sm-9' if page_toc else 'col-sm-12' }}">
<main>
<div class="page_content page-content">
<div class="doc-search">
<div class="search-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
</div>
<input type="search" class="form-control" placeholder="Search" />
</div>
{{ page_content() }}
</div>
{%- if page_toc -%}
<div class="page-toc col-sm-3">
<div>
<h5>On this page</h5>
{{ page_toc_html }}
</div>
{% include "templates/includes/web_sidebar.html" %}
</div>
{%- endif -%}
</main>
{% endblock %}
</div>
{% endmacro %}
{% macro sidebar() %}
{%- if show_sidebar -%}
<div class="sidebar-column col-sm-{{ columns }}">
{% block page_sidebar %}
{% include "templates/includes/web_sidebar.html" %}
{% endblock %}
</div>
{%- endif -%}
{% endmacro %}
{% macro container_attributes() -%}
id="page-{{ name or route | e }}" data-path="{{ pathname | e }}"
{%- if page_or_generator=="Generator" %}source-type="Generator" data-doctype="{{ doctype }}"{%- endif %}
{%- if source_content_type %}source-content-type="{{ source_content_type }}"{%- endif %}
{%- endmacro %}
<div class="container">
<div class="row" {{ container_attributes() }}>
{%- set columns = (sidebar_columns or 2) if show_sidebar else 0 -%}
{%- if not sidebar_right -%}
{{ sidebar() }}
<div class="container-fluid doc-layout">
<div class="row no-gutters" {{ container_attributes() }}>
{%- if show_sidebar -%}
<div class="sidebar-column col-sm-3">
<aside class="doc-sidebar">
<div class="doc-sidebar-logo">
<a href="/">
<img src="/files/Frappe Framework No Padding.svg" style="height: 16px; width: auto;">
</a>
</div>
{% block page_sidebar %}
{% include "templates/includes/web_sidebar.html" %}
{% endblock %}
</aside>
</div>
{%- endif -%}
<div class="main-column col-sm-{{ 12 - columns }}">
<div class="main-column doc-main col-sm-6">
{{ main_content() }}
</div>
{%- if sidebar_right -%}
{{ sidebar() }}
{%- endif -%}
<div class="page-toc col-sm-3">
{%- if page_toc -%}
<div>
<h5>On this page</h5>
{{ page_toc_html }}
</div>
{%- endif -%}
</div>
</div>
</div>

View file

@ -1,46 +1,71 @@
{% macro render_sidebar_item(item) %}
<li class="{{ 'sidebar-group' if item.group_title else 'sidebar-item' }}">
{%- if item.group_title -%}
<h6>{{ item.group_title }}</h6>
{{ render_sidebar_items(item.group_items) }}
{%- else -%}
{% if item.type != 'input' %}
{%- set item_route = item.route[1:] if item.route[0] == '/' else item.route -%}
<a href="{{ item.route }}" class="{{ 'active' if pathname == item_route else '' }}"
{% if item.target %}target="{{ item.target }}" {% endif %}>
{{ _(item.title or item.label) }}
</a>
{% else %}
<form action='{{ item.route }}' class="mr-3">
<input name='q' class='form-control' type='text' style="outline: none"
placeholder="{{ _(item.title or item.label) }}">
</form>
{% endif %}
{%- endif -%}
</li>
{% endmacro %}
{% macro render_sidebar_items(items) %}
{%- if items | len > 0 -%}
<ul class="list-unstyled">
{% for item in items -%}
{{ render_sidebar_item(item) }}
{%- endfor %}
</ul>
{%- endif -%}
{% endmacro %}
{% macro my_account() %}
{% if frappe.user != 'Guest' %}
<ul class="list-unstyled">
<li class="sidebar-item">
<a href="/me">{{ _("My Account") }}</a>
</li>
</ul>
{% endif %}
{% endmacro %}
<div class="web-sidebar">
{% if sidebar_title %}
<li class="title">
{{ sidebar_title }}
</li>
{% endif %}
<div class="sidebar-items">
<ul class="list-unstyled">
{% if sidebar_title %}
<li class="title">
{{ sidebar_title }}
</li>
{% endif %}
{% for item in sidebar_items -%}
<li class="sidebar-item">
{% if item.type != 'input' %}
{%- set item_route = item.route[1:] if item.route[0] == '/' else item.route -%}
<a href="{{ item.route }}" class="{{ 'active' if pathname == item_route else '' }}"
{% if item.target %}target="{{ item.target }}"{% endif %}>
{{ _(item.title or item.label) }}
</a>
{% else %}
<form action='{{ item.route }}' class="mr-3">
<input name='q' class='form-control' type='text' style="outline: none"
placeholder="{{ _(item.title or item.label) }}">
</form>
{% endif %}
</li>
{%- endfor %}
{% if frappe.user != 'Guest' %}
<li class="sidebar-item">
<a href="/me">{{ _("My Account") }}</a>
</li>
{% endif %}
</ul>
{{ render_sidebar_items(sidebar_items) }}
{{ my_account() }}
</div>
</div>
<script>
frappe.ready(function() {
$('.sidebar-item a').each(function(index) {
const active_class = 'active'
const non_active_class = ''
if(this.href.trim() == window.location) {
$(this).removeClass(non_active_class).addClass(active_class);
} else {
$(this).removeClass(active_class).addClass(non_active_class);
}
});
});
frappe.ready(function () {
$('.sidebar-item a').each(function (index) {
const active_class = 'active'
const non_active_class = ''
if (this.href.trim() == window.location) {
$(this).removeClass(non_active_class).addClass(active_class);
} else {
$(this).removeClass(active_class).addClass(non_active_class);
}
});
});
</script>

View file

@ -329,6 +329,22 @@ $.extend(frappe, {
add_switch_to_desk: function() {
$('.switch-to-desk').removeClass('hidden');
},
add_link_to_headings: function() {
$('.from-markdown').find('h2, h3, h4, h5, h6').each((i, $heading) => {
let id = $heading.id;
let $a = $('<a class="no-underline">')
.prop('href', '#' + id)
.attr('aria-hidden', 'true')
.html(`
<svg xmlns="http://www.w3.org/2000/svg" style="width: 0.8em; height: 0.8em;" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link">
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
</svg>
`);
$($heading).append($a);
});
},
setup_lazy_images: function() {
// Use IntersectionObserver to only load images that are visible in the viewport
// Fallback for browsers that don't support it
@ -445,6 +461,7 @@ $(document).on("page-change", function() {
frappe.trigger_ready();
frappe.bind_filters();
frappe.highlight_code_blocks();
frappe.add_link_to_headings();
frappe.make_navbar_active();
// scroll to hash
if (window.location.hash) {