Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions src/components/DraftNotice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { useLocation } from '@docusaurus/router';
import latestVersions from '@site/static/versions/latestVersions.json';
import { draftDocs } from '../draftDocs';

/**
* Draft status marking for documents flagged `draft: true` in docConfig.js.
*
* Two surfaces, one message:
* - DraftStatusStrip — a slim strip at the top of the content column that sticks
* below the site header, so the status stays visible regardless of scroll
* position or deep-link entry point. It is deliberately bounded by the content
* column rather than the viewport: a full-bleed bar reads as site-wide chrome,
* while a column-width bar reads as status for this document.
* - DraftPrintFrame — wraps the document body in a table whose thead/tfoot the
* browser repeats on every printed sheet, marking each physical page.
*
* The strip is hidden in print and the frame is inert on screen (display: contents),
* so each surface only renders in the medium it is meant for.
*/

export const DRAFT_STATUS_TEXT = 'This document is pending final approval by the RMC Director.';

function getDocInfo(pathname) {
const stripped = pathname
.replace(/^\/RMC-Software-Documentation\/docs\//, '')
.replace(/^\/docs\//, '')
.replace(/^docs\//, '');
const match = stripped.match(/^(.+?)\/(v\d+\.\d+(?:\.\d+)?)(?:\/|$)/);
if (!match) return null;
return { docBasePath: match[1], version: match[2] };
}

function isDraftPath(pathname) {
const info = getDocInfo(pathname);
if (!info) return false;
const isFlagged = draftDocs.some((base) => info.docBasePath === base || info.docBasePath.startsWith(base + '/'));
if (!isFlagged) return false;
const latest = latestVersions[info.docBasePath];
if (!latest) return true;
return info.version === latest;
}

/** True when the current route is the latest version of a doc flagged `draft: true`. */
export function useIsDraftDoc() {
const location = useLocation();
return isDraftPath(location.pathname);
}

/**
* Slim status strip at the top of the content column.
*
* Renders nothing unless the current route is a draft doc, so it can be dropped
* into the layout unconditionally.
*
* The outer anchor is the sticky element and carries an opaque page-coloured
* band, so the gap between the header and the strip is preserved while scrolling
* without document text showing through it.
*/
export const DraftStatusStrip = () => {
const isDraft = useIsDraftDoc();
if (!isDraft) return null;

return (
<div className="draft-strip-anchor">
<div className="draft-strip" role="note" aria-label="Document status">
{DRAFT_STATUS_TEXT}
</div>
</div>
);
};

/**
* Repeats the draft band at the top and bottom of every printed page.
*
* Browsers repeat `thead` and `tfoot` across page breaks, which is the only
* cross-browser way to get a running header/footer — `@page` margin boxes are
* unsupported in Chrome and Firefox. On screen the whole table collapses via
* `display: contents`, so it generates no boxes and leaves layout untouched.
*/
export const DraftPrintFrame = ({ children }) => {
const isDraft = useIsDraftDoc();
if (!isDraft) return <>{children}</>;

return (
<table className="draft-print-frame">
<thead>
<tr>
<td>
<div className="draft-print-frame__band" aria-hidden="true">
{DRAFT_STATUS_TEXT}
</div>
</td>
</tr>
</thead>
<tbody>
<tr>
<td>{children}</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>
<div className="draft-print-frame__band draft-print-frame__band--foot" aria-hidden="true">
{DRAFT_STATUS_TEXT}
</div>
</td>
</tr>
</tfoot>
</table>
);
};

export default DraftStatusStrip;
1 change: 1 addition & 0 deletions src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@import '../../node_modules/@usace/groundwork/dist/style.css';
@import './schema.css';
@import './print.css';
@import './draft-notice.css';
@import './responsive.css';

@font-face {
Expand Down
141 changes: 141 additions & 0 deletions src/css/draft-notice.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/* ===== Draft Status Marking ===== */
/* Screen: a slim strip at the top of the content column that sticks below the
site header as the document scrolls.
Print: a band repeated at the top and bottom of every printed sheet.
See src/components/DraftNotice.js. */

/* ── Screen: document status strip ── */
/* Bounded by the content column, not the viewport: a full-bleed bar reads as
site-wide chrome, a column-width bar reads as status for this document. */
/* The anchor is the sticky element. It carries an opaque, page-coloured band so
the gap above the strip is preserved as the document scrolls without text
showing through it — the site header itself has a transparent background. */
.draft-strip-anchor {
position: sticky;
top: var(--ifm-navbar-height, 0px);
z-index: 5;

/* Holds the gap open as the document scrolls under it. Uses an explicit token
rather than --ifm-background-color, which resolves to #0000 in light mode. */
padding-top: 1rem;
padding-bottom: 0.35rem;
/* Cancels the container's top spacing so the strip sits at its stuck position
from first paint — it locks in place instead of shifting up on first scroll. */
margin-top: -1rem;
margin-bottom: 0.7rem;
background-color: var(--draft-strip-band);
}

.draft-strip {
display: flex;
align-items: baseline;
justify-content: center;
flex-wrap: wrap;
gap: 0.45rem;

padding: 0.4rem 0.9rem;
border: 1px solid var(--draft-strip-border);
border-radius: var(--ifm-global-radius);
background-color: var(--draft-strip-bg);

color: var(--draft-strip-fg);
font-size: 0.8125rem;
line-height: 1.3;
text-align: center;
}

/* Light mode — lighter red */
:root {
--draft-strip-bg: #fdf3e0;
--draft-strip-border: #eed9ab;
--draft-strip-fg: #6f4a08;
--draft-strip-band: #f7f8fa;
}

/* Dark mode — darker red */
html[data-theme='dark'] {
--draft-strip-bg: #33280f;
--draft-strip-border: #6a5423;
--draft-strip-fg: #f2e0be;
--draft-strip-band: #1b1b1b;
}

@media (max-width: 480px) {
.draft-strip {
font-size: 0.75rem;
padding: 0.35rem 0.7rem;
}
}

/* ── Print frame: inert on screen ── */
/* display: contents makes the wrapper generate no boxes, so the document lays
out exactly as it would without the table. */
.draft-print-frame,
.draft-print-frame > thead,
.draft-print-frame > tbody,
.draft-print-frame > tfoot,
.draft-print-frame > * > tr,
.draft-print-frame > * > tr > td {
display: contents;
}

.draft-print-frame__band {
display: none;
}

/* ── Print ── */
@media print {
/* The viewport strip is a screen affordance; the repeating band replaces it. */
.draft-strip-anchor {
display: none !important;
}

.draft-print-frame {
display: table !important;
width: 100% !important;
border-collapse: collapse !important;
}

/* thead/tfoot repeat across page breaks — this is what marks every sheet. */
.draft-print-frame > thead {
display: table-header-group !important;
}

.draft-print-frame > tfoot {
display: table-footer-group !important;
}

.draft-print-frame > tbody {
display: table-row-group !important;
}

.draft-print-frame > * > tr {
display: table-row !important;
}

.draft-print-frame > * > tr > td {
display: table-cell !important;
border: 0 !important;
padding: 0 !important;
}

/* Class specificity beats the `* { color: #000 !important }` reset in print.css. */
.draft-print-frame__band {
display: block !important;
margin: 0 0 8pt !important;
padding: 3pt 0 !important;
border-top: 1pt solid #8a5a00 !important;
border-bottom: 1pt solid #8a5a00 !important;
color: #8a5a00 !important;
font-size: 8.5pt !important;
font-weight: 700 !important;
letter-spacing: 0.06em;
text-align: center;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}

.draft-print-frame__band--foot {
margin: 8pt 0 0 !important;
}
}
1 change: 1 addition & 0 deletions src/docConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const docs = [
{
category: 'desktop-applications',
software: 'rmc-totalrisk',
doc_location: 'desktop-applications/rmc-totalrisk/verification-report',
doc_name: 'RMC TotalRisk Verification Report',
active: true,
draft: true,
Expand Down
9 changes: 8 additions & 1 deletion src/theme/DocItem/Layout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
* - Added FloatingNav FAB + bottom-sheet drawer so users can reach
* the same navigation without scrolling to the top when the
* viewport is narrow (< 1080 px).
* - Added DraftStatusStrip at the top of the content column and wrapped the
* content in DraftPrintFrame, so draft documents carry a sticky status
* strip on screen and a repeating DRAFT band on every printed page.
*/
import React from 'react';
import clsx from 'clsx';
Expand All @@ -21,6 +24,7 @@ import DocItemContent from '@theme/DocItem/Content';
import DocBreadcrumbs from '@theme/DocBreadcrumbs';
import ContentVisibility from '@theme/ContentVisibility';
import FloatingNav, { InlineNav } from '@site/src/components/FloatingNav';
import { DraftPrintFrame, DraftStatusStrip } from '@site/src/components/DraftNotice';
import styles from './styles.module.css';

function useDocTOC() {
Expand All @@ -41,11 +45,14 @@ export default function DocItemLayout({ children }) {
<ContentVisibility metadata={metadata} />
<DocVersionBanner />
<div className={styles.docItemContainer}>
<DraftStatusStrip />
<article>
<DocBreadcrumbs />
<DocVersionBadge />
<InlineNav />
<DocItemContent>{children}</DocItemContent>
<DraftPrintFrame>
<DocItemContent>{children}</DocItemContent>
</DraftPrintFrame>
<DocItemFooter />
</article>
<DocItemPaginator />
Expand Down
42 changes: 0 additions & 42 deletions src/theme/DocItem/index.js

This file was deleted.

Loading