Skip to content

Commit 54517c2

Browse files
committed
implements TextWdiget editor
1 parent 1534c33 commit 54517c2

File tree

10 files changed

+450
-115
lines changed

10 files changed

+450
-115
lines changed

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99
"scripts": {
1010
"dev": "vite",
1111
"build": "tsc && vite build",
12-
"serve": "vite preview",
13-
"firebase:emulator:start": "firebase emulators:start"
12+
"serve": "vite preview"
1413
},
1514
"dependencies": {
15+
"@material-ui/core": "^4.12.3",
16+
"@react-firebase/database": "^0.3.11",
1617
"firebase": "^8.6.2",
1718
"react": "^17.0.2",
1819
"react-dom": "^17.0.2",

src/components/App.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import React from 'react';
33
import '@/global.css';
44
import { Preview } from '@/components/Preview'
55
import { Index as Admin } from '@/components/admin'
6+
import { FirebaseDatabaseProvider, FirebaseDatabaseNode } from '@react-firebase/database';
7+
import firebase from '@/lib/firebase';
68

79
const App = () => {
810
const params = new URLSearchParams(location.search);
@@ -13,7 +15,9 @@ const App = () => {
1315
mode === 'admin' ? Admin : <></>;
1416

1517
return (
16-
<Component />
18+
<FirebaseDatabaseProvider firebase={firebase}>
19+
<Component />
20+
</FirebaseDatabaseProvider>
1721
);
1822
};
1923

src/components/Preview.tsx

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { VFC } from 'react';
2+
import { FirebaseDatabaseNode } from '@react-firebase/database';
23

34
import { TextWidget } from '@/components/TextWidget';
45
import { TimeWidget } from '@/components/TimeWidget';
@@ -9,19 +10,24 @@ const Widgets = {
910
};
1011

1112
const Preview: VFC = () => {
12-
const text = `オレオレOBSウィジェットの整理`;
13-
14-
const widgets = [
15-
{ name: 'text', props: { text: text } },
16-
{ name: 'time', props: { size: 30 } },
17-
];
18-
1913
return (
2014
<div>
21-
{widgets.map((widget) => {
22-
const Widget = Widgets[widget.name];
23-
return <Widget {...widget.props} />
24-
})}
15+
<FirebaseDatabaseNode path="/widgets">
16+
{d => {
17+
const widgets = d.value || {};
18+
console.log(Object.values(widgets));
19+
return (
20+
<>
21+
{
22+
Object.entries(widgets).map(([id, widget]) => {
23+
const Widget = Widgets[widget.name];
24+
return <Widget key={id} {...widget.props} />
25+
})
26+
}
27+
</>
28+
);
29+
}}
30+
</FirebaseDatabaseNode>
2531
</div>
2632
);
2733
};
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React, { Component } from 'react';
2+
import { FirebaseDatabaseMutation } from '@react-firebase/database'
3+
import type { TextWidgetProps } from '@/components/TextWidget/types';
4+
5+
type Props = {
6+
id: string;
7+
props: TextWidgetProps;
8+
};
9+
10+
class TextWidgetEditor extends Component<Props, TextWidgetProps> {
11+
constructor(props) {
12+
super(props);
13+
this.state = this.props.props;
14+
}
15+
16+
render() {
17+
return (
18+
<div>
19+
<label>
20+
text:
21+
<input
22+
type="text"
23+
onChange={(e) => {
24+
this.setState({ ...this.state, text: e.target.value });
25+
}}
26+
value={this.state.text}
27+
/>
28+
</label>
29+
30+
<FirebaseDatabaseMutation type="set" path={`/widgets/${this.props.id}/props`}>
31+
{({ runMutation }) => {
32+
return (
33+
<button
34+
onClick={async (e: Event) => {
35+
e.preventDefault();
36+
await runMutation(this.state);
37+
}}
38+
>
39+
Save
40+
</button>
41+
);
42+
}}
43+
</FirebaseDatabaseMutation>
44+
</div>
45+
);
46+
}
47+
}
48+
49+
export { TextWidgetEditor };
Lines changed: 2 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,2 @@
1-
import React, { VFC, CSSProperties } from 'react';
2-
3-
type Position = {
4-
top?: number; // px
5-
right?: number; // px
6-
bottom?: number; // px
7-
left?: number; // px
8-
}
9-
10-
type TextWidgetProps = {
11-
text: string;
12-
textColor?: string;
13-
backgroundColor?: string;
14-
edgeWeight?: number; // px
15-
edgeColor?: string;
16-
width?: number; // px
17-
height?: number; // px
18-
padding?: string; // px
19-
position?: Position;
20-
};
21-
22-
const calcTextShadow = (weight, color) => {
23-
const edge = [
24-
`${weight}px ${weight}px 0 ${color}`,
25-
`-${weight}px -${weight}px 0 ${color}`,
26-
`-${weight}px ${weight}px 0 ${color}`,
27-
`${weight}px -${weight}px 0 ${color}`,
28-
`0px ${weight}px 0 ${color}`,
29-
`0-${weight}px 0 ${color}`,
30-
`-${weight}px 0 0 ${color}`,
31-
`${weight}px 0 0 ${color}`
32-
].join(', ');
33-
return edge;
34-
};
35-
36-
const defaultStyle: CSSProperties = {
37-
boxSizing: 'border-box',
38-
whiteSpace: 'pre-wrap',
39-
overflow: 'hidden',
40-
color: 'white',
41-
backgroundColor: 'rgba(0, 0, 0 0.1)',
42-
textShadow: calcTextShadow(1, 'black'),
43-
width: 320,
44-
height: 540,
45-
padding: '4px 8px',
46-
position: 'absolute',
47-
};
48-
49-
const TextWidget: VFC<TextWidgetProps> = ({
50-
text,
51-
textColor,
52-
backgroundColor,
53-
edgeWeight,
54-
edgeColor,
55-
width,
56-
height,
57-
padding,
58-
position,
59-
}) => {
60-
const edge = calcTextShadow(edgeWeight || 1, edgeColor || 'black');
61-
62-
const style: CSSProperties = {
63-
...defaultStyle,
64-
width: `${width || 320}px`,
65-
height: `${height | 540}px`,
66-
padding: padding || '4px 8px',
67-
color: textColor || 'white',
68-
textShadow: edge,
69-
backgroundColor: backgroundColor || 'rgba(0, 0, 0, 0.1)',
70-
};
71-
72-
if (position?.top || position?.right || position?.bottom || position?.left) {
73-
if (position?.top) style.top = position.top;
74-
if (position?.right) style.right = position.right;
75-
if (position?.bottom) style.bottom = position.bottom;
76-
if (position?.left) style.left = position.left;
77-
} else {
78-
style.right = 0;
79-
}
80-
81-
console.log(style);
82-
83-
return <div style={style}>{text}</div>;
84-
};
85-
86-
export { TextWidget }
1+
export { TextWidget } from './widget';
2+
export { TextWidgetEditor } from './editor';

src/components/TextWidget/types.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
type Position = {
2+
top?: number; // px
3+
right?: number; // px
4+
bottom?: number; // px
5+
left?: number; // px
6+
}
7+
8+
type TextWidgetProps = {
9+
text: string;
10+
textColor?: string;
11+
backgroundColor?: string;
12+
edgeWeight?: number; // px
13+
edgeColor?: string;
14+
width?: number; // px
15+
height?: number; // px
16+
padding?: string; // px
17+
position?: Position;
18+
};
19+
20+
export type { Position, TextWidgetProps };
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import React, { VFC, CSSProperties } from 'react';
2+
import type { TextWidgetProps } from '@/components/TextWidget/types';
3+
4+
const calcTextShadow = (weight, color) => {
5+
const edge = [
6+
`${weight}px ${weight}px 0 ${color}`,
7+
`-${weight}px -${weight}px 0 ${color}`,
8+
`-${weight}px ${weight}px 0 ${color}`,
9+
`${weight}px -${weight}px 0 ${color}`,
10+
`0px ${weight}px 0 ${color}`,
11+
`0-${weight}px 0 ${color}`,
12+
`-${weight}px 0 0 ${color}`,
13+
`${weight}px 0 0 ${color}`
14+
].join(', ');
15+
return edge;
16+
};
17+
18+
const defaultStyle: CSSProperties = {
19+
boxSizing: 'border-box',
20+
whiteSpace: 'pre-wrap',
21+
overflow: 'hidden',
22+
color: 'white',
23+
backgroundColor: 'rgba(0, 0, 0 0.1)',
24+
textShadow: calcTextShadow(1, 'black'),
25+
width: 320,
26+
height: 540,
27+
padding: '4px 8px',
28+
position: 'absolute',
29+
};
30+
31+
const TextWidget: VFC<TextWidgetProps> = ({
32+
text,
33+
textColor,
34+
backgroundColor,
35+
edgeWeight,
36+
edgeColor,
37+
width,
38+
height,
39+
padding,
40+
position,
41+
}) => {
42+
const edge = calcTextShadow(edgeWeight || 1, edgeColor || 'black');
43+
44+
const style: CSSProperties = {
45+
...defaultStyle,
46+
width: `${width || 320}px`,
47+
height: `${height | 540}px`,
48+
padding: padding || '4px 8px',
49+
color: textColor || 'white',
50+
textShadow: edge,
51+
backgroundColor: backgroundColor || 'rgba(0, 0, 0, 0.1)',
52+
};
53+
54+
if (position?.top || position?.right || position?.bottom || position?.left) {
55+
if (position?.top) style.top = position.top;
56+
if (position?.right) style.right = position.right;
57+
if (position?.bottom) style.bottom = position.bottom;
58+
if (position?.left) style.left = position.left;
59+
} else {
60+
style.right = 0;
61+
}
62+
63+
console.log(style);
64+
65+
return <div style={style}>{text}</div>;
66+
};
67+
68+
export { TextWidget }

src/components/admin/index.tsx

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,35 @@
11
import React, { VFC, useEffect, useState } from 'react';
22
import { User } from '@firebase/auth-types';
3-
3+
import { FirebaseDatabaseNode } from '@react-firebase/database';
44
import { AuthProvider } from '@/lib/AuthProvider';
55
import { auth } from '@/lib/firebase';
6-
import { Signin } from "@/components/admin/signin";
6+
import { Signin } from '@/components/admin/signin';
7+
import { TextWidgetEditor } from '@/components/TextWidget';
8+
9+
const Editors = {
10+
'text': TextWidgetEditor,
11+
};
12+
13+
const Widgets: VFC = () => {
14+
return (
15+
<div>
16+
<FirebaseDatabaseNode path="/widgets">
17+
{d => {
18+
return (
19+
<>
20+
{
21+
Object.entries(d.value || {}).map(([id, widget]) => {
22+
const Editor = Editors[widget.name];
23+
return <Editor key={id} id={id} props={widget.props} />
24+
})
25+
}
26+
</>
27+
);
28+
}}
29+
</FirebaseDatabaseNode>
30+
</div>
31+
);
32+
}
733

834
const Index: VFC = () => {
935
const [currentUser, setCurrentUser] = useState<User | null>(null);
@@ -23,10 +49,11 @@ const Index: VFC = () => {
2349
};
2450

2551
return currentUser !== null ? (
26-
<AuthProvider>
52+
<AuthProvider>
2753
<h1>Admin</h1>
2854
<div>{currentUser?.displayName}</div>
2955
<button onClick={signout}>Sign Out</button>
56+
<Widgets />
3057
</AuthProvider>
3158
) : (
3259
<Signin />

src/lib/firebase.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'firebase/auth';
2+
import 'firebase/database';
23
import firebase from 'firebase/app';
34

45
const config = {
@@ -12,16 +13,12 @@ const config = {
1213
};
1314

1415
let auth;
16+
let db;
1517
if (firebase.apps.length === 0) {
1618
firebase.initializeApp(config);
1719
auth = firebase.app().auth();
18-
if (import.meta.env.VITE_FIREBASE_AUTH_EMULATOR_HOST) {
19-
auth.useEmulator(
20-
`http://${import.meta.env.VITE_FIREBASE_AUTH_EMULATOR_HOST}`
21-
);
22-
}
20+
db = firebase.database();
2321
}
2422

25-
// TODO: use database emulator
26-
27-
export { auth };
23+
export default firebase;
24+
export { auth, db };

0 commit comments

Comments
 (0)