(
+ featureRows,
+ node,
+ FileFormatFeatureRow
+ );
+
+ let totalFeatures = featureRows.length;
+ let supportedFeatures = featureRows
+ .map((f) => {
+ const status = toFeatureStatusDefinitionFull(f.props.reading)?.status;
+ if (status == undefined) {
+ return 1;
+ }
+ switch (status) {
+ case FeatureStatus.Supported:
+ case FeatureStatus.Ignored:
+ case FeatureStatus.Unspecified:
+ return 1;
+ case FeatureStatus.Partial:
+ return 0.5;
+ case FeatureStatus.NotSupported:
+ default:
+ return 0;
+ }
+ })
+ .reduce((p, v) => p + v, 0);
+
+ const relevantFeatures = featureRows.filter(
+ (r) =>
+ toFeatureStatusDefinitionFull(r.props.model)?.status !==
+ FeatureStatus.Ignored
+ );
+ let totalRelevantFeatures = relevantFeatures.length;
+ let supportedRelevantFeatures = relevantFeatures.filter((f) => {
+ const status = toFeatureStatusDefinitionFull(f.props.reading)?.status;
+ return (
+ status === undefined ||
+ status == FeatureStatus.Ignored ||
+ status == FeatureStatus.Supported ||
+ status == FeatureStatus.Unspecified
+ );
+ }).length;
+
+ return {
+ totalFeatures,
+ supportedFeatures,
+ totalRelevantFeatures,
+ supportedRelevantFeatures,
+ };
+}
+
+export class FileFormatFeatureGroup extends React.Component<{
+ title: string;
+ children: React.ReactNode;
+}> {
+ public render() {
+ const tooltip = ReactDOMServer.renderToStaticMarkup(
+
+ );
+
+ const classNames = ["feature-status-has-tooltip", styles.noWrap];
+ if (tooltip) {
+ classNames.push(styles.hasTooltip);
+ }
+
+ return (
+ <>
+
+
+
+ {this.props.title}
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+ {this.props.children}
+ >
+ );
+ }
+}
+
+type FeatureStatusDefinition =
+ | FeatureStatus
+ | [FeatureStatus]
+ | [FeatureStatus, string]
+ | [FeatureStatus, string, string]
+ | FeatureStatusDefinitionFull;
+
+function toFeatureStatusDefinitionFull(
+ definition?: FeatureStatusDefinition
+): FeatureStatusDefinitionFull | undefined {
+ if (typeof definition === "undefined") {
+ return undefined;
+ }
+
+ if (typeof definition === "number") {
+ return {
+ status: definition,
+ };
+ }
+
+ if (Array.isArray(definition)) {
+ if (definition.length === 1) {
+ return {
+ status: definition[0] as number,
+ };
+ } else if (definition.length === 2) {
+ return {
+ status: definition[0] as number,
+ since: definition[1] as string,
+ };
+ } else if (definition.length === 3) {
+ return {
+ status: definition[0] as number,
+ since: definition[1] as string,
+ tooltip: definition[2] as string,
+ };
+ }
+ }
+
+ if ("status" in definition) {
+ return definition;
+ }
+
+ throw new Error("Invalid status definition");
+}
+
+export class FileFormatFeatureStatistics extends React.Component<{
+ node: React.ReactNode;
+}> {
+ public render() {
+ const statistics = computeFeatureStatistics(this.props.node);
+ return (
+
+ Number of total supported features:{" "}
+ {((statistics.supportedFeatures / statistics.totalFeatures) * 100) | 0}%
+ ({statistics.supportedFeatures}/{statistics.totalFeatures})
+ Number of relevant supported features:{" "}
+ {((statistics.supportedRelevantFeatures /
+ statistics.totalRelevantFeatures) *
+ 100) |
+ 0}
+ % ({statistics.supportedRelevantFeatures}/
+ {statistics.totalRelevantFeatures})
+
+ );
+ }
+}
+export class FileFormatFeatureRow extends React.Component<{
+ feature: string;
+ isNewFeature: boolean;
+
+ model?: FeatureStatusDefinition;
+ reading?: FeatureStatusDefinition;
+ render?: FeatureStatusDefinition;
+ audio?: FeatureStatusDefinition;
+ tex?: FeatureStatusDefinition;
+}> {
+ public render() {
+ return (
+
+ {(this.props.isNewFeature ? "⭐ " : "") + this.props.feature} |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+ );
+ }
+}
+
+function collectChildren(items: T[], node: React.ReactNode, type: any) {
+ if (typeof node === "object") {
+ // array
+ if (Symbol.iterator in node) {
+ for (const c of node as Iterable) {
+ collectChildren(items, c, type);
+ }
+ } else if (node.type === type) {
+ items.push(node as T);
+ } else if ("props" in node && "children" in node.props) {
+ collectChildren(items, node.props.children, type);
+ }
+ }
+}
+
+export class FileFormatFeatureTable extends React.Component<{
+ children: React.ReactNode;
+}> {
+ public render() {
+ return (
+ <>
+
+
+
+ The following table describes the support of the different features
+ of the input format.
+
+
+ Columns
+
+
+ -
+ Feature: The related feature. If marked with ⭐ its a new or
+ changed feature compared to the previous version of this format
+ (e.g. a feature added in Guitar Pro 6)
+
+ -
+ Data Model: Whether alphaTab supports storing this information in
+ its own data model (e.g. from other formats).
+
+ -
+ Reading: Whether alphaTab can read this information from the file
+ format.
+
+ -
+ Rendering: Whether alphaTab can display the information in the
+ music sheet when rendered (display might differ from reference
+ software).
+
+ -
+ Audio: Whether alphaTab can generate audio information for this
+ feature.
+
+ -
+ alphaTex: Whether this feature is supported when describing music
+ notation with alphaTex.
+
+
+
+ Values
+
+
+ -
+ - The
+ feature is fully supported by alphaTab.
+
+ -
+ - The
+ feature is partially supported by alphaTab, hover the item to see
+ more details.
+
+ -
+ - The
+ feature is not supported by alphaTab.
+
+ -
+ - The
+ feature is ignored from the input format because it is considered
+ not relevant for display or playback. This is opinionated based on
+ the feature set in alphaTab, open a feature request if you need
+ it.
+
+
+
+
+
+
+
+ Feature |
+ Data Model |
+ Reading |
+ Rendering |
+ Audio |
+ alphaTex |
+
+
+ {this.props.children}
+
+ >
+ );
+ }
+}
diff --git a/src/components/FileFormatFeatureTable/styles.module.scss b/src/components/FileFormatFeatureTable/styles.module.scss
new file mode 100644
index 0000000..0c916c3
--- /dev/null
+++ b/src/components/FileFormatFeatureTable/styles.module.scss
@@ -0,0 +1,19 @@
+.noWrap {
+ white-space: nowrap;
+}
+
+.hasTooltip {
+ text-decoration: underline;
+ text-decoration-style: dotted;
+ text-decoration-line: underline;
+ text-decoration-thickness: 1px;
+ cursor: help;
+}
+
+.ignored {
+ opacity: 0.5;
+}
+
+.featureTable tbody tr td:nth-child(3) {
+ background-color: rgba(210, 231, 255, 0.5);
+}
\ No newline at end of file
diff --git a/src/css/custom.scss b/src/css/custom.scss
index 9c82bb8..03445d2 100644
--- a/src/css/custom.scss
+++ b/src/css/custom.scss
@@ -77,4 +77,7 @@ li.reference-item ul {
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
no-repeat;
margin-right: 0.25rem;
-}
\ No newline at end of file
+}
+
+@import 'react-tooltip/dist/react-tooltip.css'
+
diff --git a/tsconfig.json b/tsconfig.json
index 314eab8..9c8ecfc 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,6 +2,7 @@
// This file is not used in compilation. It is here just for a nice editor experience.
"extends": "@docusaurus/tsconfig",
"compilerOptions": {
- "baseUrl": "."
+ "baseUrl": ".",
+ "downlevelIteration": true
}
}