Skip to content

Commit

Permalink
Add inline apidoc and some improvement (primefaces#4044)
Browse files Browse the repository at this point in the history
* generate typedocs

* Refactor on api-doc

* Added new formatter

* Refactor on navbar

* Refactor on section nav

* Update docsectionnav.js

* Refactor

* Refactor on doc sections

* Update apidoc implementation and improve doc styling

* Refactor on menu and docs

* Update menu.json

---------

Co-authored-by: Cagatay Civici <[email protected]>
  • Loading branch information
mertsincan and cagataycivici authored Feb 13, 2023
1 parent 799562d commit 870b26d
Show file tree
Hide file tree
Showing 164 changed files with 31,837 additions and 1,053 deletions.
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
"esbenp.prettier-vscode",
"sibiraj-s.vscode-scss-formatter"
]
}
8 changes: 8 additions & 0 deletions .vscode/settings.team.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,13 @@
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[css]": {
"editor.defaultFormatter": "sibiraj-s.vscode-scss-formatter",
"editor.formatOnSave": true
},
"[scss]": {
"editor.defaultFormatter": "sibiraj-s.vscode-scss-formatter",
"editor.formatOnSave": true
}
}
389 changes: 389 additions & 0 deletions api-generator/build-apidoc.js

Large diffs are not rendered by default.

30,455 changes: 30,455 additions & 0 deletions components/doc/common/apidoc/index.json

Large diffs are not rendered by default.

429 changes: 407 additions & 22 deletions components/doc/common/docapisection.js

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions components/doc/common/doccomponent.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Head from 'next/head';
import { useRouter } from 'next/router';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { classNames } from '../../lib/utils/Utils';
import { DocApiSection } from './docapisection';
import { DocSectionNav } from './docsectionnav';
Expand All @@ -15,8 +15,13 @@ export function DocComponent(props) {
router.replace(router.pathname);
};

useEffect(() => {
setTab(router.asPath.includes('#api.') ? 1 : 0);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return (
<div className={classNames(props.className, 'doc-component')}>
<div className="doc-component">
<Head>
<title>{props.title}</title>
<meta name="description" content={props.description} />
Expand Down Expand Up @@ -48,7 +53,7 @@ export function DocComponent(props) {
) : null}
{tab === 1 ? (
<div className="doc-tabpanel">
<DocApiSection doc={props.apiDocs} />
<DocApiSection header={props.header} doc={props.apiDocs} />
</div>
) : null}
</div>
Expand Down
183 changes: 84 additions & 99 deletions components/doc/common/docsectionnav.js
Original file line number Diff line number Diff line change
@@ -1,119 +1,104 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
import React, { useEffect, useState } from 'react';
import { classNames } from '../../lib/utils/Utils';
import React, { useEffect, useRef, useState } from 'react';
import { useEventListener } from '../../lib/hooks/Hooks';
import { classNames, DomHandler, ObjectUtils } from '../../lib/utils/Utils';

export function DocSectionNav(props) {
export function DocSectionNav({ docs = [] }) {
const router = useRouter();
const [activeTab, setActiveTab] = useState('');
const [activeId, setActiveId] = useState(null);
const navRef = useRef(null);
const topbarHeight = useRef(0);
const isScrollBlocked = useRef(false);
const scrollEndTimer = useRef(undefined);

const [bindDocumentScrollListener] = useEventListener({
target: 'window',
type: 'scroll',
listener: (event) => {
if (!isScrollBlocked.current) {
const labels = DomHandler.find(event.target, ':is(h1,h2,h3).doc-section-label');
const windowScrollTop = DomHandler.getWindowScrollTop();

labels.forEach((label) => {
const { top } = DomHandler.getOffset(label);
const threshold = getThreshold(label);

if (top - threshold <= windowScrollTop) {
const link = DomHandler.findSingle(label, 'a');

setActiveId(link.id);
}
});
}

const onButtonClick = (doc) => {
// Scroll to the clicked button's parent element
scrollToTheSection(doc.id, 'smooth');
};
clearTimeout(scrollEndTimer.current);
scrollEndTimer.current = setTimeout(() => {
isScrollBlocked.current = false;

const scrollToTheSection = (id, behavior) => {
document.getElementById(id).parentElement.scrollIntoView({ block: 'start', behavior });
};
const activeItem = DomHandler.findSingle(navRef.current, '.active-navbar-item');

const getIdOfTheSection = (section) => {
return section.querySelector('a').getAttribute('id');
activeItem && activeItem.scrollIntoView({ block: 'nearest', inline: 'start' });
}, 50);
}
});

const onClick = (id) => {
setActiveId(id);
setTimeout(() => {
scrollToLabelById(id, 'smooth');
isScrollBlocked.current = true;
}, 1);
};

useEffect(() => {
const handleHashChange = (url) => {
const hash = url.split('#')[1];

setActiveTab(hash);
};
const scrollToLabelById = (id, behavior) => {
const label = document.getElementById(id);

router.events.on('onRouteChangeComplete', handleHashChange);
label && label.parentElement.scrollIntoView({ block: 'start', behavior });
};

return () => {
router.events.off('onRouteChangeComplete', handleHashChange);
};
}, [props.docs, router.events]);
const getThreshold = (label) => {
if (!topbarHeight.current) {
const topbar = DomHandler.findSingle(document.body, '.layout-topbar');

useEffect(() => {
const sections = document.querySelectorAll('section'); // Get all sections on the page
const topbarEl = document.getElementsByClassName('layout-topbar')[0]; // Get the topbar element
const hash = window.location.hash.substring(1); // Get the initial hash

// Set the active tab to the initial hash and scroll into view if it exists
if (hash) {
setActiveTab(hash);
// Scroll to the section with the current hash
scrollToTheSection(hash);
} else if (window.scrollY + window.innerHeight >= document.body.offsetHeight) {
// Set the active tab to the first section
setActiveTab(getIdOfTheSection(sections[0].querySelector('.doc-section-label')));
topbarHeight.current = topbar ? DomHandler.getHeight(topbar) : 0;
}

const onScroll = () => {
sections.forEach((section) => {
const sectionLabelEl = section.querySelectorAll('.doc-section-label'); //Get all labels on the currrent section
// Check if the section is currently scrolled to center of the screen
const isScrolledTo = (section) => window.scrollY >= section.offsetTop - topbarEl.clientHeight - 20 && window.scrollY < section.offsetTop + section.offsetHeight - topbarEl.clientHeight - 20;

if (isScrolledTo(section)) {
// Check if the section has multiple child elements
if (sectionLabelEl.length > 1) {
sectionLabelEl.forEach((child) => {
// Check if the child element is currently scrolled to
if (isScrolledTo(child)) {
// Set the active tab to the id of the currently scrolled to child element
setActiveTab(getIdOfTheSection(child));
}
});
} else {
setActiveTab(getIdOfTheSection(sectionLabelEl[0]));
}
}
});
};

window.addEventListener('scroll', onScroll, { passive: true });
return topbarHeight.current + DomHandler.getHeight(label) * 1.5;
};

return () => {
window.removeEventListener('scroll', onScroll, { passive: true });
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
const hash = window.location.hash.substring(1);
const hasHash = ObjectUtils.isNotEmpty(hash);
const id = hasHash ? hash : (docs[0] || {}).id;

setActiveId(id);
hasHash && scrollToLabelById(id);
bindDocumentScrollListener();
}, []); // eslint-disable-line react-hooks/exhaustive-deps

const createItem = ({ id, label, children }, level = 0) => {
const { basePath, pathname } = router;
const href = `${basePath}${pathname}#${id}`;

return (
<li key={id} className={classNames('navbar-item', { 'active-navbar-item': activeId === id })}>
<div className="navbar-item-content">
<Link href={href}>
<button className="p-link" onClick={() => onClick(id)} title={label}>
{label}
</button>
</Link>
</div>

<ul>{level !== 1 && children && children.map((child) => createItem(child, 1))}</ul>
</li>
);
};

return (
<ul className="doc-section-nav">
{props.docs.map((doc) => {
const hash = doc.id;

return (
<React.Fragment key={doc.label}>
<li key={doc.label} className={classNames('navbar-item', { 'active-navbar-item': activeTab === doc.id })}>
<Link href={router.basePath + router.pathname + '#' + hash}>
<button className="p-link" onClick={() => onButtonClick(doc)}>
{doc.label}
</button>
</Link>
</li>
{doc.children && (
<ul>
{doc.children.map((child) => {
const hash = child.id;

return (
<li key={child.label} className={classNames('navbar-child-item', { 'active-navbar-child-item': activeTab === child.id })}>
<Link href={router.basePath + router.pathname + '#' + hash}>
<button className="p-link" onClick={(event) => onButtonClick(child)}>
{child.label}
</button>
</Link>
</li>
);
})}
</ul>
)}
</React.Fragment>
);
})}
<ul ref={navRef} className="doc-section-nav">
{docs.map((item) => createItem(item))}
</ul>
);
}
48 changes: 19 additions & 29 deletions components/doc/common/docsections.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,30 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
import React from 'react';
import { classNames } from '../../lib/utils/Utils';
import { DocSectionText } from './docsectiontext';

export function DocSections({ docs }) {
const router = useRouter();
const renderDocChildren = (doc, level = 2) => {
return (
<React.Fragment key={doc.id + '_' + level}>
<DocSectionText {...doc} level={level}>
{doc.description ? <p>{doc.description}</p> : null}
</DocSectionText>
{doc.children.map((d) => {
const { id, label, component, children } = d;
const Component = component;

return component ? <Component id={id} key={id} label={label} level={level + 1} /> : children ? renderDocChildren(d, level + 1) : null;
})}
</React.Fragment>
);
};

const renderDocs = () => {
return docs.map((doc) => {
return docs.map((doc, i) => {
const Comp = doc.component;

return (
<section key={doc.label} className="py-3">
{doc.children ? (
<React.Fragment>
<div id={doc.id}>
<h2 className="doc-section-label" id={doc.id}>
{doc.label}
<Link href={router.basePath + router.pathname + '#' + doc.id}>
<a id={doc.id}>#</a>
</Link>
</h2>
<div className={classNames('doc-section-description')}>
<p>{doc.description || 'Section Content'}</p>
</div>
</div>
{doc.children.map((comp, i) => {
const { id, label, component } = comp;
const Component = component;

return <Component id={id} key={label} label={label} level="2" />;
})}
</React.Fragment>
) : (
doc.component && <Comp id={doc.id} label={doc.label} />
)}
<section key={doc.label + '_' + i} className="py-3">
{doc.children ? renderDocChildren(doc) : doc.component && <Comp id={doc.id} label={doc.label} />}
</section>
);
});
Expand Down
26 changes: 20 additions & 6 deletions components/doc/common/docsectiontext.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,39 @@ import { useRouter } from 'next/router';
import React from 'react';

export function DocSectionText(props) {
const { id, label, level = 2, children } = props;
const router = useRouter();

const onClick = (event) => {
const parentElement = event.currentTarget.parentElement;
const hash = window.location.hash.substring(1);

hash === id && event.preventDefault();

setTimeout(() => {
parentElement.scrollIntoView({ block: 'start' });
}, 0);
};

const content = (
<>
{props.label}
<Link href={router.basePath + router.pathname + '#' + props.id} target="_self">
<a id={props.id}>#</a>
{label}
<Link href={router.basePath + router.pathname + '#' + id} target="_self">
<a id={id} onClick={onClick}>
#
</a>
</Link>
</>
);
const tag = props.level === '2' ? 'h3' : 'h2';

const Title = (titleProps) => {
return React.createElement(tag, { className: 'doc-section-label' }, titleProps.children);
return React.createElement(`h${level}`, { className: 'doc-section-label' }, titleProps.children);
};

return (
<>
<Title>{content}</Title>
<div className="doc-section-description">{props.children}</div>
<div className="doc-section-description">{children}</div>
</>
);
}
Loading

0 comments on commit 870b26d

Please sign in to comment.