feat: Doc page layout
- 3 Columns layout with Sidebar, Main and TOC - Night owl hljs theme - Sidebar with groups
This commit is contained in:
parent
5b1dacb6a1
commit
622667e130
7 changed files with 415 additions and 118 deletions
183
frappe/public/css/hljs-night-owl.css
Normal file
183
frappe/public/css/hljs-night-owl.css
Normal 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
105
frappe/public/scss/doc.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
@import 'page-builder';
|
||||
@import 'markdown';
|
||||
@import 'sidebar';
|
||||
@import 'doc';
|
||||
|
||||
.container {
|
||||
padding-left: 1.25rem;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue