cedro 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +5 -0
- package/index.html +13 -0
- package/package.json +18 -0
- package/src/core/application.core.ts +182 -0
- package/src/core/index.ts +8 -0
- package/src/core/jsxsupport.ts +100 -0
- package/src/core/screeen.core.ts +81 -0
- package/src/core/seo.ts +79 -0
- package/src/core/themes.core.ts +237 -0
- package/src/index.ts +16 -0
- package/src/interfaces/application.interface.ts +45 -0
- package/src/interfaces/screen.interface.ts +17 -0
- package/src/interfaces/themes.interface.ts +19 -0
- package/src/interfaces/widget.interface.ts +123 -0
- package/src/types/vector2d.type.ts +4 -0
- package/src/ui/Icon.ui.ts +70 -0
- package/src/ui/IconButton.ui.ts +70 -0
- package/src/ui/button.ui.ts +88 -0
- package/src/ui/colors.ui.ts +7 -0
- package/src/ui/dialog.tsx +398 -0
- package/src/ui/index.ts +24 -0
- package/src/ui/label.ui.ts +61 -0
- package/src/ui/menu.ui.ts +192 -0
- package/src/ui/select.ui.ts +74 -0
- package/src/ui/styles/button.css +237 -0
- package/src/ui/styles/dialog.css +48 -0
- package/src/ui/styles/main.css +93 -0
- package/src/ui/styles/menu.css +56 -0
- package/src/ui/styles/textbox.css +35 -0
- package/src/ui/styles/theme/dark.css +37 -0
- package/src/ui/styles/theme/light.css +37 -0
- package/src/ui/textbox.ui.ts +132 -0
- package/src/ui/widget.builder.ui.tsx +676 -0
- package/src/ui/widget.collection.ts +19 -0
- package/src/ui/widget.ui.ts +832 -0
- package/tsconfig.json +30 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 hdrdevs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
package/index.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Vite + TS</title>
|
|
8
|
+
<link rel="stylesheet" href="src/ui/styles/main.css" />
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cedro",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"devDependencies": {
|
|
6
|
+
"@types/node": "^20.4.4",
|
|
7
|
+
"typescript": "^5.0.2"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@fontsource/roboto": "^5.0.5",
|
|
11
|
+
"@oothkoo/seo-js": "1.0.5",
|
|
12
|
+
"@types/color": "3.0.3",
|
|
13
|
+
"@types/react": "18.2.17",
|
|
14
|
+
"color": "4.2.3",
|
|
15
|
+
"material-icons": "1.13.9",
|
|
16
|
+
"navigo": "8.11.1"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import "../ui/styles/main.css";
|
|
2
|
+
import "@fontsource/roboto/100.css";
|
|
3
|
+
import "@fontsource/roboto/300.css";
|
|
4
|
+
import "@fontsource/roboto/400.css";
|
|
5
|
+
import "@fontsource/roboto/500.css";
|
|
6
|
+
import "@fontsource/roboto/700.css";
|
|
7
|
+
import "@fontsource/roboto/900.css";
|
|
8
|
+
import "material-icons/iconfont/material-icons.css";
|
|
9
|
+
import { Widget, WidgetAlignTypes, WidgetTypes } from "../ui/widget.ui";
|
|
10
|
+
import { Screen } from "./screeen.core";
|
|
11
|
+
import { IApplication, IScreenSize } from "../interfaces/application.interface";
|
|
12
|
+
import { IWidget } from "../interfaces/widget.interface";
|
|
13
|
+
import Navigo from "navigo";
|
|
14
|
+
import { Dialog } from "../ui/dialog";
|
|
15
|
+
import { Label } from "../ui/label.ui";
|
|
16
|
+
import { Seo } from "./seo";
|
|
17
|
+
import { DarkTheme, LightTheme, ThemeManager } from "./themes.core";
|
|
18
|
+
|
|
19
|
+
class WApplication implements IApplication {
|
|
20
|
+
seo: Seo;
|
|
21
|
+
|
|
22
|
+
screen: Screen;
|
|
23
|
+
root: Widget;
|
|
24
|
+
router: Navigo;
|
|
25
|
+
|
|
26
|
+
alertDialog: Dialog;
|
|
27
|
+
confirmDialog: Dialog;
|
|
28
|
+
|
|
29
|
+
mediaQueries: Map<string, IScreenSize>;
|
|
30
|
+
|
|
31
|
+
theme: ThemeManager;
|
|
32
|
+
|
|
33
|
+
constructor(title: string) {
|
|
34
|
+
this.seo = new Seo(title);
|
|
35
|
+
|
|
36
|
+
this.root = new Widget("root");
|
|
37
|
+
this.root.setType(WidgetTypes.FILL);
|
|
38
|
+
this.screen = new Screen();
|
|
39
|
+
this.router = new Navigo("/");
|
|
40
|
+
|
|
41
|
+
this.mediaQueries = new Map<string, IScreenSize>();
|
|
42
|
+
|
|
43
|
+
this.theme = new ThemeManager();
|
|
44
|
+
this.theme.add(LightTheme);
|
|
45
|
+
this.theme.add(DarkTheme);
|
|
46
|
+
|
|
47
|
+
this.screen.onResize(() => {
|
|
48
|
+
this.getRoot().resize();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
window.addEventListener("load", () => {
|
|
52
|
+
this.screen.updateSize();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
this.alertDialog = new Dialog("Dialog.alert", null);
|
|
56
|
+
this.confirmDialog = new Dialog("Dialog.confirm", null);
|
|
57
|
+
|
|
58
|
+
this.theme.load();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
alert(
|
|
62
|
+
msg: string,
|
|
63
|
+
onOk: () => void = () => {},
|
|
64
|
+
onCancell: () => void = () => {}
|
|
65
|
+
): void {
|
|
66
|
+
const mesageLabel = new Label("alert.label", "span");
|
|
67
|
+
|
|
68
|
+
mesageLabel.setType(WidgetTypes.FILL);
|
|
69
|
+
mesageLabel.setAlign(WidgetAlignTypes.VERTICAL);
|
|
70
|
+
mesageLabel.setText(msg);
|
|
71
|
+
|
|
72
|
+
this.alertDialog.setOkCallback({ event: "click", then: onOk });
|
|
73
|
+
this.alertDialog.setCancellCallback({
|
|
74
|
+
event: "click",
|
|
75
|
+
then: onCancell,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
this.alertDialog.getContentCntainer().attachWidget(mesageLabel);
|
|
79
|
+
|
|
80
|
+
this.alertDialog.show();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
confirm(
|
|
84
|
+
msg: string,
|
|
85
|
+
onOk: () => void = () => {},
|
|
86
|
+
onCancell: () => void = () => {}
|
|
87
|
+
): void {
|
|
88
|
+
const mesageLabel = new Label("alert.label", "span");
|
|
89
|
+
|
|
90
|
+
mesageLabel.setType(WidgetTypes.FILL);
|
|
91
|
+
mesageLabel.setAlign(WidgetAlignTypes.VERTICAL);
|
|
92
|
+
mesageLabel.setText(msg);
|
|
93
|
+
|
|
94
|
+
this.confirmDialog.setOkCallback({ event: "click", then: onOk });
|
|
95
|
+
this.confirmDialog.setCancellCallback({
|
|
96
|
+
event: "click",
|
|
97
|
+
then: onCancell,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
this.confirmDialog.getContentCntainer().attachWidget(mesageLabel);
|
|
101
|
+
|
|
102
|
+
this.confirmDialog.show();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
attachWidget(guest: IWidget, host: IWidget): void {
|
|
106
|
+
if (!host) {
|
|
107
|
+
console.log("guest:", guest);
|
|
108
|
+
}
|
|
109
|
+
for (const child of host.getBody().childNodes) {
|
|
110
|
+
child.parentNode?.removeChild(child);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
for (const child of host.childs) {
|
|
114
|
+
child.free();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
host.removeAllChilds();
|
|
118
|
+
|
|
119
|
+
host.addChild(guest);
|
|
120
|
+
guest.setParent(host);
|
|
121
|
+
guest.render();
|
|
122
|
+
this.root.resize();
|
|
123
|
+
this.root.render();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
addMediaQuery(
|
|
127
|
+
query: string,
|
|
128
|
+
minWidth: number,
|
|
129
|
+
maxWidth: number,
|
|
130
|
+
cb: (app: IApplication) => void
|
|
131
|
+
): void {
|
|
132
|
+
this.mediaQueries.set(query, { minWidth, maxWidth, cb });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Initializes the application.
|
|
137
|
+
*
|
|
138
|
+
* @return {void} This function does not return a value.
|
|
139
|
+
*/
|
|
140
|
+
init(): void {
|
|
141
|
+
this.root.subscribe({
|
|
142
|
+
event: "resize",
|
|
143
|
+
then: () => {
|
|
144
|
+
const app = this;
|
|
145
|
+
|
|
146
|
+
for (let query of app.mediaQueries.entries()) {
|
|
147
|
+
const { minWidth, maxWidth, cb } = query[1];
|
|
148
|
+
if (
|
|
149
|
+
minWidth <= app.screen.dimensions.x &&
|
|
150
|
+
maxWidth > app.screen.dimensions.x
|
|
151
|
+
) {
|
|
152
|
+
cb(this as IApplication);
|
|
153
|
+
//break; quite el break para dar soporte a mas de un media quiery del mismo tipo.
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
this.root.render();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Retrieves the root widget of the application.
|
|
164
|
+
*
|
|
165
|
+
* @return {Widget} The root widget of the application.
|
|
166
|
+
*/
|
|
167
|
+
getRoot(): Widget {
|
|
168
|
+
return this.root;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Sets the root widget of the object.
|
|
173
|
+
*
|
|
174
|
+
* @param {Widget} root - The root widget to set.
|
|
175
|
+
* @return {void}
|
|
176
|
+
*/
|
|
177
|
+
setRoot(root: Widget): void {
|
|
178
|
+
this.root = root;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export default WApplication;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/*export function h(el: any, attrs: any, content: any) {
|
|
2
|
+
const node = document.createElement(el);
|
|
3
|
+
|
|
4
|
+
Object.entries(attrs || {}).forEach(([name, value]) => {
|
|
5
|
+
node.setAttribute(name, value);
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
if (typeof content === "string") {
|
|
9
|
+
content = document.createTextNode(content);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
node.appendChild(content);
|
|
13
|
+
|
|
14
|
+
return node;
|
|
15
|
+
}*/
|
|
16
|
+
|
|
17
|
+
interface entityMapData {
|
|
18
|
+
[key: string]: string;
|
|
19
|
+
}
|
|
20
|
+
export const entityMap: entityMapData = {
|
|
21
|
+
"&": "amp",
|
|
22
|
+
"<": "lt",
|
|
23
|
+
">": "gt",
|
|
24
|
+
'"': "quot",
|
|
25
|
+
"'": "#39",
|
|
26
|
+
"/": "#x2F",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const escapeHtml = (str: object[] | string) =>
|
|
30
|
+
String(str).replace(/[&<>"'\/\\]/g, (s) => `&${entityMap[s]};`);
|
|
31
|
+
|
|
32
|
+
// To keep some consistency with React DOM, lets use a mapper
|
|
33
|
+
// https://reactjs.org/docs/dom-elements.html
|
|
34
|
+
export const AttributeMapper = (val: string) =>
|
|
35
|
+
({
|
|
36
|
+
tabIndex: "tabindex",
|
|
37
|
+
className: "class",
|
|
38
|
+
readOnly: "readonly",
|
|
39
|
+
}[val] || val);
|
|
40
|
+
|
|
41
|
+
// tslint:disable-next-line:no-default-export
|
|
42
|
+
export function DOMcreateElement(
|
|
43
|
+
tag: Function | string,
|
|
44
|
+
attrs?: { [key: string]: any },
|
|
45
|
+
...children: (HTMLElement | string)[]
|
|
46
|
+
): HTMLElement {
|
|
47
|
+
attrs = attrs || {};
|
|
48
|
+
const stack: any[] = [...children];
|
|
49
|
+
|
|
50
|
+
// Support for components(ish)
|
|
51
|
+
if (typeof tag === "function") {
|
|
52
|
+
attrs.children = stack;
|
|
53
|
+
return tag(attrs);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const elm = document.createElement(tag);
|
|
57
|
+
|
|
58
|
+
// Add attributes
|
|
59
|
+
for (let [name, val] of Object.entries(attrs)) {
|
|
60
|
+
name = escapeHtml(AttributeMapper(name));
|
|
61
|
+
if (name.startsWith("on") && name.toLowerCase() in window) {
|
|
62
|
+
elm.addEventListener(name.toLowerCase().substr(2), val);
|
|
63
|
+
} else if (name === "ref") {
|
|
64
|
+
val(elm);
|
|
65
|
+
} else if (name === "style") {
|
|
66
|
+
Object.assign(elm.style, val);
|
|
67
|
+
} else if (val === true) {
|
|
68
|
+
elm.setAttribute(name, name);
|
|
69
|
+
} else if (val !== false && val != null) {
|
|
70
|
+
elm.setAttribute(name, escapeHtml(val));
|
|
71
|
+
} else if (val === false) {
|
|
72
|
+
elm.removeAttribute(name);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Append children
|
|
77
|
+
while (stack.length) {
|
|
78
|
+
const child = stack.shift();
|
|
79
|
+
|
|
80
|
+
// Is child a leaf?
|
|
81
|
+
if (!Array.isArray(child)) {
|
|
82
|
+
elm.appendChild(
|
|
83
|
+
(child as HTMLElement).nodeType == null
|
|
84
|
+
? document.createTextNode(child.toString())
|
|
85
|
+
: child
|
|
86
|
+
);
|
|
87
|
+
} else {
|
|
88
|
+
stack.push(...child);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return elm;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const DOMcreateFragment = (
|
|
96
|
+
_attrs?: { [key: string]: any },
|
|
97
|
+
...children: (HTMLElement | string)[]
|
|
98
|
+
): (HTMLElement | string)[] => {
|
|
99
|
+
return children;
|
|
100
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { IScreen } from "../interfaces/screen.interface";
|
|
2
|
+
import { Vector2D } from "../types/vector2d.type";
|
|
3
|
+
|
|
4
|
+
export class Screen implements IScreen {
|
|
5
|
+
dimensions: Vector2D;
|
|
6
|
+
onResizeCB: () => void;
|
|
7
|
+
constructor() {
|
|
8
|
+
this.dimensions = {
|
|
9
|
+
x: 0,
|
|
10
|
+
y: 0,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
this.onResizeCB = () => {};
|
|
14
|
+
|
|
15
|
+
window.addEventListener("resize", () => {
|
|
16
|
+
|
|
17
|
+
this.updateSize();
|
|
18
|
+
this.onResizeCB();
|
|
19
|
+
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
this.updateSize();
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
onResize(cb: () => void): void {
|
|
27
|
+
this.onResizeCB = cb;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Populates the size of the object.
|
|
32
|
+
*
|
|
33
|
+
* @return {void} This function does not return anything.
|
|
34
|
+
*/
|
|
35
|
+
populateSize(): void {
|
|
36
|
+
console.log("populateSize", this.dimensions);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
updateSize(): void {
|
|
40
|
+
this.setWidth(window.innerWidth);
|
|
41
|
+
this.setHeight(window.innerHeight);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Retrieves the width of the object.
|
|
46
|
+
*
|
|
47
|
+
* @return {number} The width of the object.
|
|
48
|
+
*/
|
|
49
|
+
getWidth(): number {
|
|
50
|
+
return this.dimensions.x;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get the height of the object.
|
|
55
|
+
*
|
|
56
|
+
* @return {number} The height of the object.
|
|
57
|
+
*/
|
|
58
|
+
getHeight(): number {
|
|
59
|
+
return this.dimensions.y;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Sets the width of the object.
|
|
64
|
+
*
|
|
65
|
+
* @param {number} width - The width value to set.
|
|
66
|
+
* @return {void} This function does not return anything.
|
|
67
|
+
*/
|
|
68
|
+
setWidth(width: number): void {
|
|
69
|
+
this.dimensions.x = width;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Sets the height of the object.
|
|
74
|
+
*
|
|
75
|
+
* @param {number} height - The height value to set.
|
|
76
|
+
* @return {void} This function does not return a value.
|
|
77
|
+
*/
|
|
78
|
+
setHeight(height: number): void {
|
|
79
|
+
this.dimensions.y = height;
|
|
80
|
+
}
|
|
81
|
+
}
|
package/src/core/seo.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export type TextDirectionValue = "ltr" | "rtl" | "auto";
|
|
2
|
+
|
|
3
|
+
export interface IMetaDataElement {
|
|
4
|
+
lang: string | null;
|
|
5
|
+
dir: TextDirectionValue | null;
|
|
6
|
+
name: string;
|
|
7
|
+
content: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface IMetaData {
|
|
11
|
+
items: IMetaDataElement[];
|
|
12
|
+
add(element: IMetaDataElement): void;
|
|
13
|
+
get(name: string): IMetaDataElement | null;
|
|
14
|
+
deleteByName(name: string): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class MetaData implements IMetaData {
|
|
18
|
+
items: IMetaDataElement[];
|
|
19
|
+
constructor() {
|
|
20
|
+
this.items = [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
add(element: IMetaDataElement): void {
|
|
24
|
+
this.items.push(element);
|
|
25
|
+
const meta = document.createElement("meta");
|
|
26
|
+
meta.setAttribute("name", element.name);
|
|
27
|
+
meta.setAttribute("content", element.content);
|
|
28
|
+
if (element.lang) meta.setAttribute("lang", element.lang);
|
|
29
|
+
if (element.dir) meta.setAttribute("dir", element.dir);
|
|
30
|
+
document.head.appendChild(meta);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get(name: string): IMetaDataElement | null {
|
|
34
|
+
for (let i = 0; i < this.items.length; i++) {
|
|
35
|
+
if (this.items[i].name === name) {
|
|
36
|
+
return this.items[i];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
deleteByName(name: string): void {
|
|
43
|
+
for (let i = 0; i < this.items.length; i++) {
|
|
44
|
+
if (this.items[i].name === name) {
|
|
45
|
+
this.items.splice(i, 1);
|
|
46
|
+
const deleteItem = document.querySelector(
|
|
47
|
+
`meta[name="${name}"]`
|
|
48
|
+
);
|
|
49
|
+
if (deleteItem) document.head.removeChild(deleteItem);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface ISeo {
|
|
56
|
+
title: string;
|
|
57
|
+
meta: MetaData;
|
|
58
|
+
|
|
59
|
+
setTitle(title: string): void;
|
|
60
|
+
getTitle(): string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class Seo implements ISeo {
|
|
64
|
+
title: string;
|
|
65
|
+
meta: MetaData;
|
|
66
|
+
constructor(title: string) {
|
|
67
|
+
this.title = title;
|
|
68
|
+
this.meta = new MetaData();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
setTitle(title: string): void {
|
|
72
|
+
document.title = title;
|
|
73
|
+
this.title = title;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getTitle(): string {
|
|
77
|
+
return this.title;
|
|
78
|
+
}
|
|
79
|
+
}
|