Skip to content

Commit 8fc0ddc

Browse files
authored
Merge pull request #18 from raduwen/text-widget-editor
Text widget editor
2 parents 03b97e8 + d3eebf4 commit 8fc0ddc

File tree

16 files changed

+867
-121
lines changed

16 files changed

+867
-121
lines changed

.editorconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
end_of_line = lf
6+
insert_final_newline = true
7+
indent_style = space
8+
indent_size = 2
9+
trim_trailing_whitespace = true

database.rules.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"rules": {
33
".read": true,
4-
".write": false,
4+
".write": "auth != null"
55
}
6-
}
6+
}

package.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@
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",
18-
"react-dom": "^17.0.2"
19+
"react-dom": "^17.0.2",
20+
"styled-components": "^5.3.0"
1921
},
2022
"devDependencies": {
23+
"@firebase/app-types": "^0.6.3",
24+
"@firebase/auth-types": "^0.10.3",
2125
"@firebase/database-types": "^0.7.2",
2226
"@types/node": "^14.17.1",
2327
"@types/react": "^17.0.2",
@@ -27,5 +31,8 @@
2731
"typescript": "^4.3.2",
2832
"vite": "^2.4.4",
2933
"vite-tsconfig-paths": "^3.3.13"
34+
},
35+
"resolutions": {
36+
"styled-components": "^5"
3037
}
3138
}

src/components/App.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React from 'react';
2+
3+
import '@/global.css';
4+
import { Preview } from '@/components/Preview'
5+
import { Index as Admin } from '@/components/admin'
6+
import { FirebaseDatabaseProvider, FirebaseDatabaseNode } from '@react-firebase/database';
7+
import firebase from '@/lib/firebase';
8+
9+
const App = () => {
10+
const params = new URLSearchParams(location.search);
11+
12+
const mode = params.get('mode') || 'preview';
13+
14+
const Component = mode === 'admin' ? Admin : Preview;
15+
16+
return (
17+
<FirebaseDatabaseProvider firebase={firebase}>
18+
<Component />
19+
</FirebaseDatabaseProvider>
20+
);
21+
};
22+
23+
export { App };

src/components/Preview.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React, { VFC } from 'react';
2+
import { FirebaseDatabaseNode } from '@react-firebase/database';
3+
4+
import { TextWidget } from '@/components/TextWidget';
5+
import { TimeWidget } from '@/components/TimeWidget';
6+
7+
const Widgets = {
8+
'text': TextWidget,
9+
'time': TimeWidget,
10+
};
11+
12+
const Preview: VFC = () => {
13+
return (
14+
<div>
15+
<FirebaseDatabaseNode path="/widgets">
16+
{d => {
17+
const widgets = d.value || {};
18+
console.log(Object.values(widgets));
19+
return (
20+
<>
21+
{
22+
Object.keys(widgets).map((id) => {
23+
const widget: any = widgets[id];
24+
const Widget = Widgets[widget.name];
25+
return <Widget key={id} {...widget.props} />
26+
})
27+
}
28+
</>
29+
);
30+
}}
31+
</FirebaseDatabaseNode>
32+
</div>
33+
);
34+
};
35+
36+
export { Preview };
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import React, { Component } from 'react';
2+
import styled from 'styled-components';
3+
import { TextField, Button, Typography } from '@material-ui/core';
4+
import { FirebaseDatabaseMutation } from '@react-firebase/database'
5+
import type { TextWidgetProps } from '@/components/TextWidget/types';
6+
7+
type Props = {
8+
id: string;
9+
props: TextWidgetProps;
10+
};
11+
12+
const FormGroup = styled.div`
13+
display: flex;
14+
margin-bottom: 1rem;
15+
width: 480px;
16+
& > label {
17+
width: 20%;
18+
}
19+
& > input {
20+
flex-grow: 1;
21+
}
22+
`;
23+
24+
class TextWidgetEditor extends Component<Props, TextWidgetProps> {
25+
constructor(props) {
26+
super(props);
27+
this.state = this.props.props;
28+
}
29+
30+
render() {
31+
return (
32+
<div>
33+
<Typography variant="h6">
34+
TextWidget : {this.props.id}
35+
</Typography>
36+
<FormGroup>
37+
<TextField
38+
type="text"
39+
label="text"
40+
fullWidth
41+
multiline={true}
42+
variant="outlined"
43+
onChange={(e) => {
44+
this.setState({ ...this.state, text: e.target.value });
45+
}}
46+
value={this.state.text}
47+
/>
48+
</FormGroup>
49+
50+
<FirebaseDatabaseMutation
51+
type="set"
52+
path={`/widgets/${this.props.id}/props`}
53+
>
54+
{({ runMutation }) => {
55+
return (
56+
<FormGroup>
57+
<Button
58+
type="button"
59+
color="primary"
60+
variant="contained"
61+
onClick={async (e: any) => {
62+
e.preventDefault();
63+
await runMutation(this.state);
64+
}}
65+
>
66+
Save
67+
</Button>
68+
</FormGroup>
69+
);
70+
}}
71+
</FirebaseDatabaseMutation>
72+
</div>
73+
);
74+
}
75+
}
76+
77+
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 }

0 commit comments

Comments
 (0)