Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Themable html program viewer #2660

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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/html-program-viewer",
"comment": "Add ability to change colors of the program viewer using css variables. A `ColorProvider` component is also provided for convenience.",
"type": "none"
}
],
"packageName": "@typespec/html-program-viewer"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/playground",
"comment": "Configure the program viewer to respect the color theme",
"type": "none"
}
],
"packageName": "@typespec/playground"
}
19 changes: 19 additions & 0 deletions packages/html-program-viewer/src/color-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { FunctionComponent, ReactNode, useMemo } from "react";
import { ColorPalette, ColorVariable, ColorsVariables } from "./constants.js";

export interface ColorProviderProps {
colors: Partial<ColorPalette>;
children?: ReactNode;
}

export const ColorProvider: FunctionComponent<ColorProviderProps> = ({ children, colors }) => {
const cssVariables = useMemo(() => {
const colorArray: [ColorVariable, string][] = Object.entries(colors) as any;
return Object.fromEntries(
colorArray.map(([key, value]) => {
return [ColorsVariables[key], value];
})
);
}, [colors]);
return <div style={cssVariables}>{children}</div>;
};
42 changes: 2 additions & 40 deletions packages/html-program-viewer/src/common.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,8 @@
import { css } from "@emotion/react";
import { FunctionComponent, PropsWithChildren, ReactElement } from "react";
import { FunctionComponent, ReactElement } from "react";
import { Colors } from "./constants.js";

export interface ItemProps {
title: string;
id?: string;
hide?: boolean;
}

const ItemStyles = css({
border: "1px solid #c5c5c5",
});

const ItemTitleStyles = css({
border: "1px solid #c5c5c5",
backgroundColor: "#dedede",
padding: "2px 5px",
});

const ItemContentStyles = css({
padding: "1rem",
});

export const Item: FunctionComponent<PropsWithChildren<ItemProps>> = ({
id,
title,
hide,
children,
}) => {
if (hide) {
return <div></div>;
}
return (
<div css={ItemStyles} id={id}>
<div css={ItemTitleStyles}>{title}</div>
<div css={ItemContentStyles}>{children}</div>
</div>
);
};

export const Literal: FunctionComponent<{ children: any }> = ({ children }) => (
<div css={{ color: "#5da713", display: "inline" }}>{children}</div>
<div css={{ color: Colors.literal, display: "inline" }}>{children}</div>
);

export const KeyValueSection: FunctionComponent<{ children: ReactElement | ReactElement[] }> = ({
Expand Down
28 changes: 26 additions & 2 deletions packages/html-program-viewer/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,28 @@
export const Colors = {
typeKind: "#7a3e9d",
const variablePrefix = `--tsp-tgv`;
export const ColorsVariables = {
background: `${variablePrefix}-background`,
dataKey: `${variablePrefix}-data-key`,
indentationGuide: `${variablePrefix}-indentation-guide`,
literal: `${variablePrefix}-literal`,
property: `${variablePrefix}-property`,
ref: `${variablePrefix}-ref`,
typeKind: `${variablePrefix}-type-kind`,
typeName: `${variablePrefix}-type-name`,
} as const;

export type ColorVariable = keyof typeof ColorsVariables;
export type ColorPalette = Record<ColorVariable, string>;
export const DefaultColors: ColorPalette = {
background: "#f3f3f3",
dataKey: "#333333",
indentationGuide: "#777",
literal: "#5da713",
property: "#9c5d27",
ref: "#268bd2",
typeKind: "#7a3e9d",
typeName: "#333333",
};

export const Colors: typeof ColorsVariables = Object.fromEntries(
Object.entries(ColorsVariables).map(([k, v]) => [k, `var(${v}, ${(DefaultColors as any)[k]})`])
) as any;
2 changes: 2 additions & 0 deletions packages/html-program-viewer/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export { ColorProvider, ColorProviderProps } from "./color-provider.js";
export type { ColorPalette } from "./constants.js";
export * from "./emitter.js";
export { TypeSpecProgramViewer } from "./ui.js";
4 changes: 2 additions & 2 deletions packages/html-program-viewer/src/type-ui-base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ export interface TypeUIBaseProps {

const TypeNameStyles = css({
display: "inline",
color: "#333333",
color: Colors.typeName,
});

export const TypeUIBase: FunctionComponent<TypeUIBaseProps> = (props) => {
const id = props.id ?? getIdForType(props.type);
const properties = props.properties.map((prop) => {
return (
<li key={prop.name}>
<span css={{ color: "#9c5d27" }} title={prop.description}>
<span css={{ color: Colors.property }} title={prop.description}>
{prop.name}
</span>
: <span>{prop.value}</span>
Expand Down
7 changes: 4 additions & 3 deletions packages/html-program-viewer/src/ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import React, { FunctionComponent, ReactElement, useContext } from "react";
import ReactDOMServer from "react-dom/server";
import { KeyValueSection, Literal } from "./common.js";
import { Colors } from "./constants.js";
import { inspect } from "./inspect.js";
import { TypeUIBase, TypeUIBaseProperty } from "./type-ui-base.js";
import { getIdForType, isNamedUnion } from "./utils.js";
Expand All @@ -39,7 +40,7 @@ export interface TypeSpecProgramViewerProps {

const ProgramViewerStyles = css({
fontFamily: "monospace",
backgroundColor: "#f3f3f3",
backgroundColor: Colors.background,
li: {
margin: 0,
listStyle: "none",
Expand Down Expand Up @@ -347,7 +348,7 @@ const NamedTypeRef: FunctionComponent<{ type: NamedType }> = ({ type }) => {
return (
<a
css={{
color: "#268bd2",
color: Colors.ref,
textDecoration: "none",

"&:hover": {
Expand Down Expand Up @@ -422,7 +423,7 @@ const TypeData: FunctionComponent<{ type: Type }> = ({ type }) => {
<KeyValueSection>
{entries.map(([k, v], i) => (
<div css={{ display: "flex" }} key={i}>
<div css={{ color: "#333", marginRight: "5px" }}>{k.toString()}:</div>{" "}
<div css={{ color: Colors.indentationGuide, marginRight: "5px" }}>{k.toString()}:</div>{" "}
<div>{inspect(v)}</div>
</div>
))}
Expand Down
26 changes: 24 additions & 2 deletions packages/playground/src/react/output-view.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { css } from "@emotion/react";
import { tokens } from "@fluentui/react-components";
import { Diagnostic, Program } from "@typespec/compiler";
import { TypeSpecProgramViewer } from "@typespec/html-program-viewer";
import { ColorPalette, ColorProvider, TypeSpecProgramViewer } from "@typespec/html-program-viewer";
import { FunctionComponent, useCallback, useEffect, useMemo, useState } from "react";
import { ErrorTab, InternalCompilerError } from "./error-tab.js";
import { FileOutput } from "./file-output.js";
Expand Down Expand Up @@ -159,7 +159,7 @@ const OutputContent: FunctionComponent<OutputContentProps> = ({
overflow: "scroll",
}}
>
{program && <TypeSpecProgramViewer program={program} />}
{program && <TypeGraphViewer program={program} />}
</div>
);
}
Expand All @@ -185,3 +185,25 @@ const ErrorTabCountStyles = css({
padding: "0 5px",
borderRadius: "20px",
});

interface TypeGraphViewerProps {
program: Program;
}
const TypeGraphViewer = ({ program }: TypeGraphViewerProps) => {
return (
<ColorProvider colors={TypeGraphColors}>
<TypeSpecProgramViewer program={program} />
</ColorProvider>
);
};

const TypeGraphColors: ColorPalette = {
background: tokens.colorNeutralBackground1,
typeKind: tokens.colorPaletteBerryForeground2,
typeName: tokens.colorNeutralForeground2,
dataKey: tokens.colorNeutralForeground2,
ref: tokens.colorBrandForeground1,
literal: tokens.colorPaletteLightGreenForeground2,
indentationGuide: tokens.colorNeutralForeground4,
property: tokens.colorPaletteMarigoldForeground2,
};