docs(codes): Add (key) codes documentation

Create codes documentation for standardized keys.

Closes #218.  Fixes #308.  Ref #21.
This commit is contained in:
innovaker
2020-11-06 19:42:54 +00:00
committed by Pete Johanson
parent 194854ff7b
commit ff638eb010
44 changed files with 9578 additions and 3 deletions

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: CC-BY-NC-SA-4.0
*/
import React from "react";
import PropTypes from "prop-types";
export default function Context({ children }) {
return <p className="context">{children}</p>;
}
Context.propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.string])
.isRequired,
};

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: CC-BY-NC-SA-4.0
*/
import React from "react";
import PropTypes from "prop-types";
const specialCharactersRegex = /(?:^|\s)((?:&(?:(?:\w+)|(?:#\d+));)|[_]|[^\w\s])(?:\s*\[([^[\]]+?)\])/g;
function renderSpecialCharacters(description) {
const matches = Array.from(description.matchAll(specialCharactersRegex));
if (matches.length == 0) return description;
let lastIndex = 0;
const parts = matches.reduce((acc, match, i) => {
const { index } = match;
const str = match[0];
const chars = match[1];
const meaning = match[2];
if (index != lastIndex) {
acc.push(description.substring(lastIndex, index));
}
const pos = str.indexOf(chars);
if (pos > 0) {
acc.push(description.substr(index, pos));
}
acc.push(
<span key={i} className="symbol" title={meaning ?? ""}>
<code>{description.substr(index + pos, chars.length)}</code>
{meaning ? <span className="meaning">{meaning}</span> : undefined}
</span>
);
lastIndex = index + str.length;
return acc;
}, []);
if (lastIndex < description.length) {
parts.push(description.substr(lastIndex));
}
return parts;
}
export default function Description({ description = "" }) {
return (
<span className="description">{renderSpecialCharacters(description)}</span>
);
}
Description.propTypes = {
description: PropTypes.string.isRequired,
};

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: CC-BY-NC-SA-4.0
*/
import React from "react";
import PropTypes from "prop-types";
export default function Footnote({ children, symbol, id }) {
return (
<div className="footnote" id={id}>
<div className="symbol">{symbol}</div>
<div className="content">{children}</div>
</div>
);
}
Footnote.propTypes = {
children: PropTypes.element.isRequired,
symbol: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
id: PropTypes.string.isRequired,
};

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: CC-BY-NC-SA-4.0
*/
import React from "react";
import PropTypes from "prop-types";
export default function FootnoteRef({ children, anchor }) {
return (
<a href={"#" + anchor} className="footnoteRef">
{children}
</a>
);
}
FootnoteRef.propTypes = {
children: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,
anchor: PropTypes.string.isRequired,
};

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: CC-BY-NC-SA-4.0
*/
import React from "react";
import PropTypes from "prop-types";
import FootnoteRef from "./FootnoteRef";
function joinReactElements(arr, delimiter) {
return arr.reduce((acc, fragment) => {
if (acc === null) {
return fragment;
}
return (
<>
{acc}
{delimiter}
{fragment}
</>
);
}, null);
}
export default function FootnoteRefs({ footnotes }) {
return (
<span className="footnoteRefs">
{joinReactElements(
footnotes.map((footnote) => (
<FootnoteRef key={footnote.reference} {...footnote}>
{footnote.symbol}
</FootnoteRef>
)),
", "
)}
</span>
);
}
FootnoteRefs.propTypes = {
footnotes: PropTypes.array.isRequired,
};

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: CC-BY-NC-SA-4.0
*/
import React from "react";
import PropTypes from "prop-types";
import Footnote from "./Footnote";
export default function Footnotes({ footnotes = [], id }) {
return (
<div className="footnotes">
<a id={id} className="anchor" />
<div className="label">Notes</div>
<div className="notes">
{footnotes.map((footnote) => (
<Footnote key={footnote.id} {...footnote}>
{footnote.value}
</Footnote>
))}
</div>
</div>
);
}
Footnotes.propTypes = {
footnotes: PropTypes.array.isRequired,
id: PropTypes.string.isRequired,
};

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: CC-BY-NC-SA-4.0
*/
import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons";
export default function LinkIcon() {
return <FontAwesomeIcon className="icon" icon={faExternalLinkAlt} />;
}
LinkIcon.propTypes = {};

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: CC-BY-NC-SA-4.0
*/
import React from "react";
import PropTypes from "prop-types";
import ToastyCopyToClipboard from "./ToastyCopyToClipboard";
export default function Name({ children, name }) {
return (
<ToastyCopyToClipboard text={name}>
<code className="name" title="Copy 📋">
{children}
</code>
</ToastyCopyToClipboard>
);
}
Name.propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.string])
.isRequired,
name: PropTypes.string.isRequired,
};

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: CC-BY-NC-SA-4.0
*/
import React from "react";
import operatingSystems from "@site/src/data/operating-systems";
export default function OsLegend() {
return (
<div className="codes os legend">
{operatingSystems.map(({ key, className, heading, title }) => (
<div key={key} className={"os " + className}>
<span className="heading">{heading}</span>
<span className="title">{title}</span>
</div>
))}
</div>
);
}
OsLegend.propTypes = {};

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: CC-BY-NC-SA-4.0
*/
import React from "react";
import PropTypes from "prop-types";
import OsSupportIcon from "./OsSupportIcon";
import FootnoteRefs from "./FootnoteRefs";
export default function OsSupport({ value, footnotes = [] }) {
return (
<>
<OsSupportIcon value={value} />
{footnotes.length > 0 ? (
<FootnoteRefs footnotes={footnotes} />
) : undefined}
</>
);
}
OsSupport.propTypes = {
value: PropTypes.oneOf([true, false, null]),
footnotes: PropTypes.array.isRequired,
};

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: CC-BY-NC-SA-4.0
*/
import React from "react";
import PropTypes from "prop-types";
const Icon = ({ children, className, title }) => (
<span className={className} title={title}>
{children}
</span>
);
Icon.propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.string])
.isRequired,
className: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
};
export const Supported = () => (
<Icon className="supported" title="Supported 😄">
</Icon>
);
export const NotSupported = () => (
<Icon className="not-supported" title="Not Supported 😢">
</Icon>
);
export const NotTested = () => (
<Icon className="not-tested" title="Not Tested Yet - PR's welcomed! 🧐">
</Icon>
);
export default function OsSupportIcon({ value }) {
if (value === true) {
return <Supported />;
}
if (value === false) {
return <NotSupported />;
}
return <NotTested />;
}
OsSupportIcon.propTypes = {
value: PropTypes.oneOf([true, false, null]),
};

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: CC-BY-NC-SA-4.0
*/
import React from "react";
import PropTypes from "prop-types";
import TableRow from "./TableRow";
import Footnotes from "./Footnotes";
import LinkIcon from "./LinkIcon";
import operatingSystems from "@site/src/data/operating-systems";
import { getCodes } from "@site/src/hid";
import { getGroup } from "@site/src/groups";
import { getFootnote } from "@site/src/footnotes";
function extractFootnoteIds(codes) {
return Array.from(
new Set(
codes
.flatMap(({ footnotes }) => Object.values(footnotes))
.map((refs) => (Array.isArray(refs) ? refs.flat() : refs))
)
);
}
export default function Table({ group }) {
const codes = getCodes(getGroup(group));
const footnotesAnchor = group + "-" + "footnotes";
const tableFootnotes = extractFootnoteIds(codes).map((id, i) => {
const Component = getFootnote(id);
return {
id,
anchor: footnotesAnchor,
symbol: i + 1,
value: Component ? <Component /> : undefined,
};
});
return (
<div className="codes">
<table>
<thead>
<tr>
<th className="names">Names</th>
<th className="description">Description</th>
<th className="documentation" title="Documentation">
<LinkIcon />
</th>
{operatingSystems.map(({ key, className, heading, title }) => (
<th key={key} className={`os ${className}`} title={title}>
{heading}
</th>
))}
</tr>
</thead>
<tbody>
{Array.isArray(codes)
? codes.map((code) => (
<TableRow
key={code.names[0]}
{...code}
tableFootnotes={tableFootnotes}
/>
))
: undefined}
</tbody>
</table>
{tableFootnotes.length > 0 ? (
<Footnotes id={footnotesAnchor} footnotes={tableFootnotes} />
) : undefined}
</div>
);
}
Table.propTypes = {
group: PropTypes.string.isRequired,
};

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: CC-BY-NC-SA-4.0
*/
import React from "react";
import PropTypes from "prop-types";
import Name from "./Name";
import Description from "./Description";
import Context from "./Context";
import LinkIcon from "./LinkIcon";
import OsSupport from "./OsSupport";
import operatingSystems from "@site/src/data/operating-systems";
export default function TableRow({
names,
description,
context = "",
clarify = false,
documentation,
os,
footnotes,
tableFootnotes,
}) {
return (
<tr>
<td className="names">
{names.map((name) => (
<Name key={name} name={name}>
{name}
</Name>
))}
</td>
<td className="description">
<Description description={description} />
{clarify && context ? <Context>{context}</Context> : undefined}
</td>
<td className="documentation" title="Documentation">
<a href={documentation} target="_blank" rel="noreferrer">
<LinkIcon />
</a>
</td>
{operatingSystems.map(({ key, className, title }) => (
<td key={key} className={`os ${className}`} title={title}>
<OsSupport
value={os[key]}
footnotes={tableFootnotes.filter(
({ id }) =>
(Array.isArray(footnotes[key]) &&
footnotes[key].includes(id)) ||
footnotes[key] == id
)}
/>
</td>
))}
</tr>
);
}
TableRow.propTypes = {
names: PropTypes.array.isRequired,
description: PropTypes.string.isRequired,
context: PropTypes.string.isRequired,
clarify: PropTypes.bool,
documentation: PropTypes.string.isRequired,
os: PropTypes.object.isRequired,
footnotes: PropTypes.object.isRequired,
tableFootnotes: PropTypes.array.isRequired,
};

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: CC-BY-NC-SA-4.0
*/
import React from "react";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
export default function ToastyContainer() {
return (
<ToastContainer
position="bottom-right"
autoClose={2000}
hideProgressBar={true}
newestOnTop={true}
/>
);
}
ToastyContainer.propTypes = {};

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: CC-BY-NC-SA-4.0
*/
import React from "react";
import PropTypes from "prop-types";
import { toast } from "react-toastify";
import { CopyToClipboard } from "react-copy-to-clipboard";
export default function ToastyCopyToClipboard({ children, text }) {
const notify = () =>
toast(
<span>
📋 Copied <code>{text}</code>
</span>
);
return (
<div onClick={notify}>
<CopyToClipboard text={text}>{children}</CopyToClipboard>
</div>
);
}
ToastyCopyToClipboard.propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.string])
.isRequired,
text: PropTypes.string.isRequired,
};