Skip to content

Commit 81820dc

Browse files
committed
fix: terminal page meta tags and title
1 parent fc1db83 commit 81820dc

File tree

7 files changed

+180
-75
lines changed

7 files changed

+180
-75
lines changed

.github/copilot-instructions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,4 @@ The repository contains library code and example usage.
3737
- Write maintainable, self-documenting code
3838
- Follow security best practices
3939
- Ensure proper type coverage
40+
- Avoid default exports unless absolutely necessary

examples/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "lightweight-charts-react-components-examples",
33
"version": "0.0.1",
4-
"description": "Examples for Lightweight Charts React Components",
4+
"description": "Examples of Lightweight Charts React Components library usage in React.js application.",
55
"homepage": "https://ukorvl.github.io/lightweight-charts-react-components",
66
"private": true,
77
"type": "module",
@@ -17,6 +17,7 @@
1717
"devDependencies": {
1818
"@vitejs/plugin-react": "^4.4.1",
1919
"vite-plugin-bundlesize": "^0.2.0",
20+
"vite-plugin-capo": "^1.0.3",
2021
"vite-plugin-checker": "^0.9.3",
2122
"vite-plugin-circular-dependency": "^0.5.0",
2223
"vite-plugin-compression": "^0.5.1",
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { useEffect, useRef } from "react";
2+
3+
type PropsWithName = {
4+
name: string;
5+
property?: never;
6+
};
7+
8+
type PropsWithProperty = {
9+
name?: never;
10+
property: string;
11+
};
12+
13+
type ControlledMetaProps = {
14+
content: string;
15+
} & (PropsWithName | PropsWithProperty);
16+
17+
const ControlledMeta = ({ name, property, content }: ControlledMetaProps) => {
18+
const initialMeta = useRef<HTMLMetaElement | null>(null);
19+
const selector = name
20+
? `meta[name="${name}"]`
21+
: property
22+
? `meta[property="${property}"]`
23+
: null;
24+
25+
useEffect(() => {
26+
if (!selector) {
27+
throw new Error("Either 'name' or 'property' must be provided for ControlledMeta.");
28+
}
29+
30+
let meta = document.head.querySelector<HTMLMetaElement>(selector);
31+
32+
if (meta) {
33+
initialMeta.current = meta.cloneNode(true) as HTMLMetaElement;
34+
} else {
35+
meta = document.createElement("meta");
36+
37+
if (name) meta.setAttribute("name", name);
38+
if (property) meta.setAttribute("property", property);
39+
40+
document.head.appendChild(meta);
41+
}
42+
43+
meta.setAttribute("content", content);
44+
45+
return () => {
46+
const completelyRemoveMeta = () =>
47+
meta && meta.parentNode === document.head && document.head.removeChild(meta);
48+
49+
if (initialMeta.current) {
50+
const existingMeta = document.head.querySelector<HTMLMetaElement>(selector);
51+
52+
if (existingMeta) {
53+
existingMeta.setAttribute("content", initialMeta.current.content);
54+
} else {
55+
completelyRemoveMeta();
56+
}
57+
} else {
58+
completelyRemoveMeta();
59+
}
60+
};
61+
}, [name, property]);
62+
63+
return null;
64+
};
65+
66+
export { ControlledMeta };
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useEffect, useRef } from "react";
2+
3+
type ControlledTitleProps = {
4+
title?: string;
5+
};
6+
7+
const ControlledTitle = ({ title }: ControlledTitleProps) => {
8+
const initialTitle = useRef(document.title);
9+
10+
useEffect(() => {
11+
const initialPageTitle = initialTitle.current;
12+
13+
if (title) {
14+
document.title = title;
15+
} else {
16+
document.title = initialPageTitle;
17+
}
18+
19+
return () => {
20+
if (initialPageTitle) {
21+
document.title = initialPageTitle;
22+
}
23+
};
24+
}, [title]);
25+
26+
return null;
27+
};
28+
29+
export { ControlledTitle };

examples/src/pages/Terminal.tsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,43 @@
11
import { Stack } from "@mui/material";
2+
import { ControlledMeta } from "@/common/ControlledMeta";
3+
import { ControlledTitle } from "@/common/ControlledTitle";
4+
import { homepage } from "../../package.json";
5+
6+
const { VITE_APP_DEFAULT_TITLE } = import.meta.env;
7+
8+
const PageMeta = () => {
9+
return (
10+
<>
11+
<ControlledTitle title={`${VITE_APP_DEFAULT_TITLE} - Terminal`} />
12+
<ControlledMeta
13+
name="description"
14+
content="Trading terminal built with lightweight-charts-react-components for visualizing financial data in a React.js application."
15+
/>
16+
<ControlledMeta
17+
name="keywords"
18+
content="terminal, lightweight-charts, react, examples, charts, visualization, react components, financial data"
19+
/>
20+
<ControlledMeta
21+
property="og:title"
22+
content="Lightweight Charts React Components - Terminal"
23+
/>
24+
<ControlledMeta
25+
property="og:description"
26+
content="Explore the terminal example built with lightweight-charts-react-components for visualizing financial data in a React.js application."
27+
/>
28+
<ControlledMeta property="og:url" content={`${homepage}/terminal`} />
29+
<ControlledMeta
30+
name="twitter:title"
31+
content="Lightweight Charts React Components - Terminal"
32+
/>
33+
<ControlledMeta
34+
name="twitter:description"
35+
content="Explore the terminal example built with lightweight-charts-react-components for visualizing financial data in a React.js application."
36+
/>
37+
<ControlledMeta name="twitter:url" content={`${homepage}/terminal`} />
38+
</>
39+
);
40+
};
241

342
const Terminal = () => {
443
return (
@@ -11,6 +50,7 @@ const Terminal = () => {
1150
marginBottom: 12,
1251
}}
1352
>
53+
<PageMeta />
1454
Terminal
1555
</Stack>
1656
);

examples/vite.config.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import path from "node:path";
22
import react from "@vitejs/plugin-react";
33
import { loadEnv } from "vite";
44
import bundlesize from "vite-plugin-bundlesize";
5+
import { capo } from "vite-plugin-capo";
56
import checker from "vite-plugin-checker";
67
import circleDependency from "vite-plugin-circular-dependency";
78
import viteCompression from "vite-plugin-compression";
@@ -77,43 +78,43 @@ export const htmlConfig: Options = {
7778
content: `${homepage}/og.jpg`,
7879
},
7980
{
80-
name: "og:title",
81+
property: "og:title",
8182
content: env.VITE_APP_DEFAULT_TITLE,
8283
},
8384
{
84-
name: "og:description",
85+
property: "og:description",
8586
content: description,
8687
},
8788
{
88-
name: "og:url",
89+
property: "og:url",
8990
content: homepage,
9091
},
9192
{
92-
name: "og:image",
93+
property: "og:image",
9394
content: `${homepage}/og.jpg`,
9495
},
9596
{
96-
name: "og:type",
97+
property: "og:type",
9798
content: "website",
9899
},
99100
{
100-
name: "og:image:alt",
101+
property: "og:image:alt",
101102
content: env.VITE_APP_DEFAULT_TITLE,
102103
},
103104
{
104-
name: "og:locale",
105+
property: "og:locale",
105106
content: "en_US",
106107
},
107108
{
108-
name: "og:site_name",
109+
property: "og:site_name",
109110
content: env.VITE_APP_DEFAULT_TITLE,
110111
},
111112
{
112-
name: "og:image:width",
113+
property: "og:image:width",
113114
content: "1200",
114115
},
115116
{
116-
name: "og:image:height",
117+
property: "og:image:height",
117118
content: "630",
118119
},
119120
{
@@ -242,6 +243,7 @@ const getUserConfig: UserConfigFn = ({ command }) => ({
242243
}),
243244
]
244245
: []),
246+
capo(),
245247
],
246248
build: {
247249
emptyOutDir: true,

0 commit comments

Comments
 (0)