Merge branch '12.0-one-doc-edi' of github.com:odoo/documentation-user into 12.0-one-doc-edi
This commit is contained in:
commit
6091147656
5
Makefile
5
Makefile
@ -30,13 +30,16 @@ clean:
|
|||||||
$(RM_CMD) extensions/odoo_theme/static/style.css
|
$(RM_CMD) extensions/odoo_theme/static/style.css
|
||||||
@echo "Cleaning finished."
|
@echo "Cleaning finished."
|
||||||
|
|
||||||
edi: SPHINXOPTS += -A collapse_menu=True
|
#edi: SPHINXOPTS += -A collapse_menu=True # If needed, comment rather than setting False
|
||||||
edi: VERSIONS += 12.0,13.0,14.0
|
edi: VERSIONS += 12.0,13.0,14.0
|
||||||
edi: CANONICAL_VERSION += 14.0
|
edi: CANONICAL_VERSION += 14.0
|
||||||
edi: LANGUAGES += en,fr,es
|
edi: LANGUAGES += en,fr,es
|
||||||
edi: CURRENT_LANG += fr
|
edi: CURRENT_LANG += fr
|
||||||
edi: clean html
|
edi: clean html
|
||||||
|
|
||||||
|
static: extensions/odoo_theme/static extensions/odoo_theme/static/style.css
|
||||||
|
cp -r extensions/odoo_theme/static/* _build/html/_static/
|
||||||
|
|
||||||
html: extensions/odoo_theme/static/style.css
|
html: extensions/odoo_theme/static/style.css
|
||||||
@echo "Starting build..."
|
@echo "Starting build..."
|
||||||
$(SPHINX_BUILD) -c $(CONFIG_DIR) -b html $(SPHINXOPTS) $(SOURCE_DIR) $(BUILD_DIR)/html
|
$(SPHINX_BUILD) -c $(CONFIG_DIR) -b html $(SPHINXOPTS) $(SOURCE_DIR) $(BUILD_DIR)/html
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
:show_content:
|
||||||
|
|
||||||
============
|
============
|
||||||
Applications
|
Applications
|
||||||
============
|
============
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
:empty_page:
|
:show_content:
|
||||||
|
|
||||||
=================================
|
=================================
|
||||||
Contributing to the documentation
|
Contributing to the documentation
|
||||||
|
@ -1,32 +1,27 @@
|
|||||||
from . import pygments_override
|
from docutils import nodes
|
||||||
from . import translator
|
|
||||||
|
|
||||||
import sphinx.builders.html
|
|
||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx.environment.adapters import toctree
|
from sphinx.environment.adapters import toctree
|
||||||
from docutils import nodes
|
|
||||||
|
from . import pygments_override, translator
|
||||||
|
|
||||||
|
|
||||||
def setup(app):
|
def setup(app):
|
||||||
app.set_translator('html', translator.BootstrapTranslator)
|
app.set_translator('html', translator.BootstrapTranslator)
|
||||||
|
|
||||||
# VFE TODO check if default meta initialization is necessary.
|
app.connect('html-page-context', set_missing_meta)
|
||||||
# If not, remove update_meta method
|
|
||||||
app.connect('html-page-context', update_meta)
|
|
||||||
|
|
||||||
|
app.add_js_file('js/utils.js') # Keep in first position
|
||||||
app.add_js_file('js/layout.js')
|
app.add_js_file('js/layout.js')
|
||||||
app.add_js_file('js/menu.js')
|
app.add_js_file('js/menu.js')
|
||||||
app.add_js_file('js/page_toc.js')
|
app.add_js_file('js/page_toc.js')
|
||||||
|
|
||||||
|
|
||||||
def update_meta(app, pagename, templatename, context, doctree):
|
def set_missing_meta(app, pagename, templatename, context, doctree):
|
||||||
meta = context.get('meta')
|
if context.get('meta') is None: # Pages without title (used with `include::`) have no meta
|
||||||
if meta is None:
|
context['meta'] = {}
|
||||||
meta = context['meta'] = {}
|
|
||||||
|
|
||||||
# TODO VFE detailed explanation of the patch logic and use.
|
|
||||||
|
|
||||||
class Monkey(object):
|
class Monkey(object):
|
||||||
|
""" Replace patched method of an object by a new method receiving the old one in argument. """
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
def __call__(self, fn):
|
def __call__(self, fn):
|
||||||
@ -36,41 +31,67 @@ class Monkey(object):
|
|||||||
|
|
||||||
@Monkey(toctree.TocTree)
|
@Monkey(toctree.TocTree)
|
||||||
def resolve(old_resolve, tree, docname, *args, **kwargs):
|
def resolve(old_resolve, tree, docname, *args, **kwargs):
|
||||||
|
|
||||||
|
def _update_toctree_nodes(_node) -> None:
|
||||||
|
""" Make necessary changes to Docutils' nodes of the toc.
|
||||||
|
|
||||||
|
Internal structure of toc nodes:
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><a/></p>
|
||||||
|
<ul>
|
||||||
|
...
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li/>
|
||||||
|
<ul/>
|
||||||
|
"""
|
||||||
|
if isinstance(_node, nodes.reference): # The node is a reference (<a/>)
|
||||||
|
_node_docname = _get_docname(_node)
|
||||||
|
_clear_reference_if_empty_page(_node, _node_docname)
|
||||||
|
_set_docname_as_class(_node, _node_docname)
|
||||||
|
elif isinstance(_node, (addnodes.compact_paragraph, nodes.bullet_list, nodes.list_item)):
|
||||||
|
for _subnode in _node.children:
|
||||||
|
_update_toctree_nodes(_subnode)
|
||||||
|
|
||||||
|
def _get_docname(_node):
|
||||||
|
"""
|
||||||
|
docname = a/b/c/the_page_being_rendered
|
||||||
|
_ref = ../../contributing/documentation
|
||||||
|
_path_parts = ['..', '..', 'contributing', 'documentation']
|
||||||
|
_res = ['a', 'contributing', 'documentation']
|
||||||
|
_docname = a/contributing/documentation
|
||||||
|
"""
|
||||||
|
_ref = _node['refuri'].replace('.html', '')
|
||||||
|
_parent_directory_occurrences = _ref.count('..')
|
||||||
|
if not _parent_directory_occurrences: # The ref is already the docname
|
||||||
|
_docname = _ref
|
||||||
|
else:
|
||||||
|
_path_parts = _ref.split('/')
|
||||||
|
_res = docname.split('/')[:-(_parent_directory_occurrences+1)] \
|
||||||
|
+ _path_parts[_parent_directory_occurrences:]
|
||||||
|
_docname = '/'.join(_res)
|
||||||
|
return _docname
|
||||||
|
|
||||||
|
def _clear_reference_if_empty_page(_reference_node, _node_docname):
|
||||||
|
""" Clear reference of 'empty' toctree pages.
|
||||||
|
|
||||||
|
Inspect parent node's siblings to determine whether the node references a toc and, if so,
|
||||||
|
clear its reference URL. (<a href="#"/>)
|
||||||
|
If the page has the `show_content` metadata, don't clear the reference.
|
||||||
|
"""
|
||||||
|
if _node_docname and any(
|
||||||
|
isinstance(_subnode, nodes.bullet_list)
|
||||||
|
for _subnode in _reference_node.parent.parent.children
|
||||||
|
): # The node references a toc
|
||||||
|
if 'show_content' not in tree.env.metadata[_node_docname]:
|
||||||
|
_reference_node['refuri'] = '#' # The page must not be accessible
|
||||||
|
|
||||||
|
def _set_docname_as_class(_reference_node, _node_docname):
|
||||||
|
_node_docname = _node_docname or docname # refuri==None <-> href="#"
|
||||||
|
_reference_node.parent.parent['classes'].append(f'o_menu_{_node_docname.replace("/", "_")}')
|
||||||
|
|
||||||
resolved_toc = old_resolve(tree, docname, *args, **kwargs)
|
resolved_toc = old_resolve(tree, docname, *args, **kwargs)
|
||||||
if resolved_toc:
|
if resolved_toc: # `resolve` returns None if the depth of the TOC to resolve is too high
|
||||||
# Not sure set_class really does what we want.
|
_update_toctree_nodes(resolved_toc)
|
||||||
_toctree_add_empty_class(tree, resolved_toc, docname)
|
|
||||||
resolved_toc['classes'].append('testtesttest')
|
|
||||||
return resolved_toc
|
return resolved_toc
|
||||||
|
|
||||||
def _toctree_add_empty_class(tree, node, docname) -> None:
|
|
||||||
for subnode in node.children:
|
|
||||||
if isinstance(subnode, (
|
|
||||||
addnodes.compact_paragraph,
|
|
||||||
nodes.list_item,
|
|
||||||
nodes.bullet_list
|
|
||||||
)):
|
|
||||||
# for <p>, <li> and <ul> just recurse
|
|
||||||
_toctree_add_empty_class(tree, subnode, docname)
|
|
||||||
elif isinstance(subnode, nodes.reference):
|
|
||||||
toc_ref = get_reference(subnode, docname)
|
|
||||||
if toc_ref and 'empty_page' in tree.env.metadata[toc_ref]:
|
|
||||||
subnode['classes'].append('o_empty_page')
|
|
||||||
|
|
||||||
def get_reference(node, docname):
|
|
||||||
ref = node['refuri'].replace('.html', '') # applications.html
|
|
||||||
if ref.find('..') < 0:
|
|
||||||
# direct reference
|
|
||||||
return ref
|
|
||||||
splitted_refuri = ref.split('/')
|
|
||||||
count = 0 # Number of ../ in refuri
|
|
||||||
for split in splitted_refuri:
|
|
||||||
if split == "..":
|
|
||||||
count += 1
|
|
||||||
# ref = ../../../contributing/documentation
|
|
||||||
# docname = services/legal/terms/enterprise
|
|
||||||
# res = contributing/documentation
|
|
||||||
res = docname.split('/')[:-(count+1)] + splitted_refuri[count:]
|
|
||||||
return "/".join(
|
|
||||||
res
|
|
||||||
)
|
|
||||||
|
@ -64,11 +64,11 @@
|
|||||||
|
|
||||||
{% set main_classes = [] %}
|
{% set main_classes = [] %}
|
||||||
{% if pagename == master_doc %} {# The current page is the homepage #}
|
{% if pagename == master_doc %} {# The current page is the homepage #}
|
||||||
{% set main_classes = main_classes + ['index'] %}
|
{% set main_classes = main_classes + ['index'] %} {# TODO ANVFE should be 'o_index' #}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if 'code-column' in meta %} {# The page contains a 'memento' (side dynamic block) #}
|
{% if 'code-column' in meta %} {# The page contains a 'memento' (side dynamic block) #}
|
||||||
{% set main_classes = main_classes + ['has_code_col'] %}
|
{% set main_classes = main_classes + ['has_code_col'] %} {# TODO ANVFE see #}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if 'classes' in meta %} {# The page source defines custom classes #}
|
{% if 'classes' in meta %} {# The page source defines custom classes #}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
<div class="o_logo_wrapper">
|
<div class="o_logo_wrapper">
|
||||||
<a href="{{ pathto(master_doc) }}" class="o_logo">
|
<a href="{{ pathto(master_doc) }}" class="o_logo">
|
||||||
{# TODO EDI/design make one unique image for Odoo docs s.t. when clicking on docs you are also redirected #}
|
|
||||||
<img src="{{ pathto('_static/img/logos/odoo_logo.svg', 1) }}"
|
<img src="{{ pathto('_static/img/logos/odoo_logo.svg', 1) }}"
|
||||||
height="30" alt="Odoo"/>
|
height="30" alt="Odoo"/>
|
||||||
<span class="text-dark fw_extralight">docs</span>
|
<span class="text-dark fw_extralight">docs</span>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<button class="btn border dropdown-toggle" id="languages" data-bs-toggle="dropdown">
|
<button class="btn border dropdown-toggle" id="languages" data-bs-toggle="dropdown">
|
||||||
{{ language }} {# The current language #}
|
{{ language }} {# The current language #}
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="languages">
|
<ul class="dropdown-menu" aria-labelledby="languages">
|
||||||
{% for alternate_language, language_code, url in alternate_languages %}
|
{% for alternate_language, language_code, url in alternate_languages %}
|
||||||
<li><a class="dropdown-item" href="{{ url }}">{{ alternate_language }}</a></li>
|
<li><a class="dropdown-item" href="{{ url }}">{{ alternate_language }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -5,5 +5,8 @@
|
|||||||
title_only: Whether menu items for content pages (without toctree) should be hidden
|
title_only: Whether menu items for content pages (without toctree) should be hidden
|
||||||
includehidden: Whether menu items of pages inside a hidden toctree should be rendered
|
includehidden: Whether menu items of pages inside a hidden toctree should be rendered
|
||||||
#}
|
#}
|
||||||
{{ toctree(collapse=collapse_menu, titles_only=True, includehidden=True)}}
|
{#
|
||||||
{# TODO replace `collapse_menu` by `True` and remove Makefile entry 'light' #}
|
`collapse_menu` is passed directly to Jinja with sphinx-build's option `-A collapse_menu=True`.
|
||||||
|
It it evaluated as a string, so what we're really evaluating here is `collapse_menu != None`.
|
||||||
|
#}
|
||||||
|
{{ toctree(collapse=collapse_menu, titles_only=True, includehidden=False)}}
|
@ -27,6 +27,9 @@ class OdooStyle(Style):
|
|||||||
Keyword.Constant: 'bold',
|
Keyword.Constant: 'bold',
|
||||||
Name.Builtin: '#2c2cff',
|
Name.Builtin: '#2c2cff',
|
||||||
Name.Function: 'bold italic',
|
Name.Function: 'bold italic',
|
||||||
|
Name.Class: "bold #0000FF",
|
||||||
|
Name.Namespace: "bold #0000FF",
|
||||||
|
Name.Exception: 'bg:#e3d2d2 #a61717',
|
||||||
Name.Variable: 'bold #2c2cff',
|
Name.Variable: 'bold #2c2cff',
|
||||||
Name.Attribute: '#2c2cff',
|
Name.Attribute: '#2c2cff',
|
||||||
Name.Tag: "bold #008000",
|
Name.Tag: "bold #008000",
|
||||||
|
@ -1,26 +1,42 @@
|
|||||||
(function ($) {
|
(function ($) {
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const navigationMenu = document.getElementById('o_main_toctree');
|
this.navigationMenu = document.getElementById('o_main_toctree');
|
||||||
|
|
||||||
// Add a class with the name of the file to each corresponding menu item
|
// Allow to automatically collapse and expand TOC entries
|
||||||
_flagMenuItemsWithFileName(navigationMenu);
|
_prepareAccordion(this.navigationMenu);
|
||||||
|
|
||||||
|
// Allow to respectively highlight and expand the TOC entries and their related TOC
|
||||||
|
// entry list whose page is displayed.
|
||||||
|
_flagActiveTocEntriesAndLists();
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the name of the file as class of the corresponding menu item.
|
* Add the relevant classes on the TOC entries (and lists) whose page is displayed.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} navigationMenu - The navigation menu containing the global TOC
|
* TOC entries (<li> elements) that are on the path of the displayed page receive the
|
||||||
|
* `o_active_toc_entry` class, and their related (parent) TOC entry list (<ul> elements) receive
|
||||||
|
* the `show` (Bootstrap) class. The child TOC entry list of the deepest TOC entry also
|
||||||
|
* receives the `show` class.
|
||||||
*/
|
*/
|
||||||
const _flagMenuItemsWithFileName = (navigationMenu) => {
|
const _flagActiveTocEntriesAndLists = () => {
|
||||||
navigationMenu.querySelectorAll('li').forEach(menuItem => {
|
let deepestTocEntry = undefined;
|
||||||
let href = menuItem.querySelector('a').href;
|
this.navigationMenu.querySelectorAll('.current').forEach(element => {
|
||||||
if (href === '#') { // Selected nodes don't have their file name in the href
|
if (element.tagName === 'UL') {
|
||||||
href = window.location.href; // Get it from the current window location
|
// Expand all related <ul>
|
||||||
|
element.classList.add('show');
|
||||||
|
} else if (element.tagName === 'LI') {
|
||||||
|
// Highlight all <li> in the active hierarchy
|
||||||
|
element.classList.add('o_active_toc_entry');
|
||||||
|
deepestTocEntry = element;
|
||||||
}
|
}
|
||||||
const fileName = href.substring(href.lastIndexOf('/') + 1, href.lastIndexOf('.html'));
|
})
|
||||||
menuItem.classList.add(`o_menu_${fileName}`);
|
if (deepestTocEntry) {
|
||||||
});
|
const childTocEntryList = deepestTocEntry.querySelector('ul');
|
||||||
|
if (childTocEntryList) {
|
||||||
|
childTocEntryList.classList.add('show');
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -12,23 +12,28 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow to hide the TOC entry referring the title (<h1> heading)
|
|
||||||
_flagFirstHeadingRef();
|
|
||||||
|
|
||||||
// Allow to automatically collapse and expand TOC entries
|
// Allow to automatically collapse and expand TOC entries
|
||||||
_prepareAccordion();
|
_prepareAccordion(this.pageToc);
|
||||||
|
|
||||||
// Allow to respectively highlight and expand the TOC entries and their related TOC
|
// Allow to respectively highlight and expand the TOC entries and their related TOC
|
||||||
// entry list whose section is focused.
|
// entry list whose section is focused.
|
||||||
_flagActiveTocEntriesAndLists();
|
_flagActiveTocEntriesAndLists();
|
||||||
|
|
||||||
|
// Allow to hide the TOC entry referring the title (<h1> heading)
|
||||||
|
_flagFirstHeadingRef();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entirely hide the local tree of contents.
|
||||||
|
*/
|
||||||
|
const _hidePageToc = () => this.pageToc.style.visibility = 'hidden';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the relevant classes on the TOC entries (and lists) whose section is focused.
|
* Add the relevant classes on the TOC entries (and lists) whose section is focused.
|
||||||
*
|
*
|
||||||
* TOC entries whose section is focused (<li> elements) receive the `active` class and their
|
* TOC entries whose section is focused (<li> elements) receive the `o_active_toc_entry` class
|
||||||
* related TOC entry list (<ul> elements) receive the `show` class.
|
* and their related TOC entry list (<ul> elements) receive the `show` (Bootstrap) class.
|
||||||
*/
|
*/
|
||||||
const _flagActiveTocEntriesAndLists = () => {
|
const _flagActiveTocEntriesAndLists = () => {
|
||||||
|
|
||||||
@ -70,7 +75,7 @@
|
|||||||
|
|
||||||
const _unflagAll = () => {
|
const _unflagAll = () => {
|
||||||
this.pageToc.querySelectorAll('li,ul').forEach(element => {
|
this.pageToc.querySelectorAll('li,ul').forEach(element => {
|
||||||
element.classList.remove('active', 'show');
|
element.classList.remove('o_active_toc_entry', 'show');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -78,10 +83,13 @@
|
|||||||
let tocEntry = headingRef.parentElement;
|
let tocEntry = headingRef.parentElement;
|
||||||
while (tocEntry !== this.pageToc) {
|
while (tocEntry !== this.pageToc) {
|
||||||
if (tocEntry.tagName === 'LI') {
|
if (tocEntry.tagName === 'LI') {
|
||||||
tocEntry.classList.add('active'); // Highlight all <li> in the active hierarchy
|
// Highlight all <li> in the active hierarchy
|
||||||
|
tocEntry.classList.add('o_active_toc_entry');
|
||||||
|
|
||||||
|
// Expand all related <ul>
|
||||||
const relatedTocEntryList = tocEntry.querySelector('ul');
|
const relatedTocEntryList = tocEntry.querySelector('ul');
|
||||||
if (relatedTocEntryList) {
|
if (relatedTocEntryList) {
|
||||||
relatedTocEntryList.classList.add('show'); // Expand all related <ul>
|
relatedTocEntryList.classList.add('show');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tocEntry = tocEntry.parentElement;
|
tocEntry = tocEntry.parentElement;
|
||||||
@ -94,10 +102,10 @@
|
|||||||
});
|
});
|
||||||
let timeoutId = undefined;
|
let timeoutId = undefined;
|
||||||
document.addEventListener('scroll', () => {
|
document.addEventListener('scroll', () => {
|
||||||
|
clearTimeout(timeoutId); // For each scroll event, cancel the previous timeout callback
|
||||||
timeoutId = setTimeout(() => {
|
timeoutId = setTimeout(() => {
|
||||||
clickedHeadingRef = undefined; // Go back to highlighting the heading ref in view
|
clickedHeadingRef = undefined; // Go back to highlighting the heading ref in view
|
||||||
}, 100);
|
}, 100);
|
||||||
clearTimeout(timeoutId); // For each scroll event, cancel the previous timeout callback
|
|
||||||
_updateFlags();
|
_updateFlags();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -108,30 +116,8 @@
|
|||||||
/**
|
/**
|
||||||
* Add the class `o_page_toc_title` on the first heading reference.
|
* Add the class `o_page_toc_title` on the first heading reference.
|
||||||
*/
|
*/
|
||||||
const _flagFirstHeadingRef = () => this.headingRefs[0].classList.add('o_page_toc_title');
|
const _flagFirstHeadingRef = () => {
|
||||||
|
this.headingRefs[0].parentNode.classList.add('o_page_toc_title');
|
||||||
/**
|
}
|
||||||
* Entirely hide the local tree of contents.
|
|
||||||
*/
|
|
||||||
const _hidePageToc = () => this.pageToc.style.visibility = 'hidden';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the page TOC entries and heading references to allow collapsing them.
|
|
||||||
*/
|
|
||||||
const _prepareAccordion = () => {
|
|
||||||
// Start at the second TOC entry list (<ul>) to avoid collapsing the entire TOC
|
|
||||||
const pageTocRoot = this.pageToc.querySelectorAll('ul')[1];
|
|
||||||
pageTocRoot.querySelectorAll('ul').forEach(tocEntryList => {
|
|
||||||
const relatedHeadingRef = tocEntryList.previousSibling; // The preceding <a> element
|
|
||||||
tocEntryList.id = `o_target_${relatedHeadingRef.getAttribute('href').replace('#', '')}`
|
|
||||||
tocEntryList.classList.add('collapse');
|
|
||||||
relatedHeadingRef.setAttribute('data-bs-target', `#${tocEntryList.id}`);
|
|
||||||
relatedHeadingRef.setAttribute('data-bs-toggle', 'collapse');
|
|
||||||
});
|
|
||||||
// TODO [ANV]
|
|
||||||
// current output: <a class="reference internal collapsed" href="#text" data-bs-target="#o_target_text" data-bs-toggle="collapse" aria-expanded="false">Text</a>
|
|
||||||
// desired output: <a class="reference internal" href="#text"><i class="i-chevron-right collapsed" data-bs-target="#o_target_text" data-bs-toggle="collapse" aria-expanded="false"></i> Text</a>
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
64
extensions/odoo_theme/static/js/utils.js
Normal file
64
extensions/odoo_theme/static/js/utils.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
let tocEntryListId = 0; // Used to generate IDs of toc entry lists for both the menu and page TOC
|
||||||
|
/**
|
||||||
|
* Update the provided TOC to allow collapsing its entries with Bootstrap's accordion.
|
||||||
|
*
|
||||||
|
* The typical structure of a TOC menu is a follows:
|
||||||
|
* <ul><li>
|
||||||
|
* <a href="#"/>
|
||||||
|
* <ul>
|
||||||
|
* <li><a href="#heading_without_child"/></li>
|
||||||
|
* <li>
|
||||||
|
* <a href="#heading_with_children"/>
|
||||||
|
* <ul>...</ul>
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
* </li></ul>
|
||||||
|
*
|
||||||
|
* Since a <ul> is always preceded by a <a>, and since we only need to make change to <a>
|
||||||
|
* elements followed by a <ul>, we simply loop on <ul> elements to access all parts of the DOM
|
||||||
|
* that need to be modified.
|
||||||
|
*
|
||||||
|
* The final structure must look like this:
|
||||||
|
* <ul><li>
|
||||||
|
* <!-- Only <a> element with empty href must expand/collapse on click -->
|
||||||
|
* <a href="#" data-bs-target="#o_target_{id}>" data-bs-toggle="collapse"/>
|
||||||
|
* <ul>
|
||||||
|
* <li><a href="#heading_without_child"/></li>
|
||||||
|
* <li>
|
||||||
|
* <div class="o_toc_entry_wrapper">
|
||||||
|
* <i class="i-chevron-right" data-bs-target="#o_target_{id}" data-bs-toggle="collapse"/>
|
||||||
|
* <a href="#heading_with_children"/>
|
||||||
|
* </div>
|
||||||
|
* <ul id="o_target_{id}" class="collapse">...</ul>
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
* </li></ul>
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} tocElement - The element containing the TOC
|
||||||
|
*/
|
||||||
|
const _prepareAccordion = (tocElement) => {
|
||||||
|
// Start at the second TOC entry list (<ul>) to avoid collapsing the entire TOC
|
||||||
|
const tocRoot = tocElement.querySelector('ul');
|
||||||
|
tocRoot.querySelectorAll('ul').forEach(tocEntryList => {
|
||||||
|
// Modify the <ul> element
|
||||||
|
tocEntryList.id = `o_target_${tocEntryListId++}`
|
||||||
|
tocEntryList.classList.add('collapse');
|
||||||
|
// Create and configure an <i> element
|
||||||
|
const arrowButton = document.createElement('I');
|
||||||
|
arrowButton.setAttribute('data-bs-target', `#${tocEntryList.id}`);
|
||||||
|
arrowButton.setAttribute('data-bs-toggle', 'collapse');
|
||||||
|
arrowButton.classList.add('i-chevron-right');
|
||||||
|
// Modify the <a> element (only if it has no href, otherwise let the redirection happen)
|
||||||
|
const relatedHeadingRef = tocEntryList.previousSibling;
|
||||||
|
if (relatedHeadingRef.getAttribute('href') === '#') {
|
||||||
|
relatedHeadingRef.setAttribute('data-bs-target', `#${tocEntryList.id}`);
|
||||||
|
relatedHeadingRef.setAttribute('data-bs-toggle', 'collapse');
|
||||||
|
}
|
||||||
|
// Create a <div> element
|
||||||
|
const tocEntryWrapper = document.createElement('DIV');
|
||||||
|
tocEntryWrapper.classList.add('o_toc_entry_wrapper');
|
||||||
|
// Insert the <i> and <a> elements inside the <div> and prepend the <div> to the <ul>
|
||||||
|
tocEntryWrapper.append(arrowButton, relatedHeadingRef);
|
||||||
|
tocEntryList.parentNode.insertBefore(tocEntryWrapper, tocEntryList);
|
||||||
|
});
|
||||||
|
};
|
@ -59,6 +59,7 @@ $display-line-height: 1;
|
|||||||
// For each of Bootstrap's buttons, define text, background, and border color.
|
// For each of Bootstrap's buttons, define text, background, and border color.
|
||||||
|
|
||||||
$btn-font-weight: $fw_semibold;
|
$btn-font-weight: $fw_semibold;
|
||||||
|
$btn-focus-box-shadow: 0 0 0 transparent;
|
||||||
|
|
||||||
// Badges
|
// Badges
|
||||||
|
|
||||||
@ -106,3 +107,6 @@ $list-group-border-color: $card-border-color;
|
|||||||
|
|
||||||
$nav-pills-link-active-color: #fff;
|
$nav-pills-link-active-color: #fff;
|
||||||
$nav-pills-link-active-bg: o-color('o-color-2');
|
$nav-pills-link-active-bg: o-color('o-color-2');
|
||||||
|
|
||||||
|
// Dropdowns
|
||||||
|
$dropdown-min-width: 4.5rem;
|
@ -115,25 +115,42 @@ header.o_main_header{
|
|||||||
}
|
}
|
||||||
|
|
||||||
> .toctree-l1 {
|
> .toctree-l1 {
|
||||||
> a:before {
|
&[class^="o_menu_"] > .o_toc_entry_wrapper > i:before {
|
||||||
@include inline-icomoon($i-doc-apps, 0 1rem 0 -30px);
|
@include inline-icomoon($i-doc-apps, 0 1rem 0 -30px);
|
||||||
}
|
}
|
||||||
&.o_menu_applications > a:before{
|
&.o_menu_applications > .o_toc_entry_wrapper > i:before{
|
||||||
content:'#{$i-doc-apps}';
|
content:'#{$i-doc-apps}';
|
||||||
}
|
}
|
||||||
&.o_menu_administration > a:before{
|
&.o_menu_administration > .o_toc_entry_wrapper > i:before{
|
||||||
content:'#{$i-doc-admin}';
|
content:'#{$i-doc-admin}';
|
||||||
}
|
}
|
||||||
&.o_menu_developer > a:before{
|
&.o_menu_developer > .o_toc_entry_wrapper > i:before{
|
||||||
content:'#{$i-doc-dev}';
|
content:'#{$i-doc-dev}';
|
||||||
}
|
}
|
||||||
&.o_menu_services > a:before{
|
&.o_menu_services > .o_toc_entry_wrapper > i:before{
|
||||||
content:'#{$i-doc-services}';
|
content:'#{$i-doc-services}';
|
||||||
}
|
}
|
||||||
&.o_menu_contributing > a:before{
|
&.o_menu_contributing > .o_toc_entry_wrapper > i:before{
|
||||||
content:'#{$i-doc-contribute}';
|
content:'#{$i-doc-contribute}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.o_toc_entry_wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
> .toctree-l2 {
|
||||||
|
.o_toc_entry_wrapper {
|
||||||
|
> i[class^="i-"] {
|
||||||
|
transition: rotate .3s;
|
||||||
|
|
||||||
|
&[aria-expanded="true"]{
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -620,7 +637,7 @@ header.o_main_header{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.active > a{
|
.o_active_toc_entry > a {
|
||||||
font-weight: $fw_bold;
|
font-weight: $fw_bold;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user