@vixoniccom/aqi 0.0.1-dev.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/.prettierrc +8 -0
- package/CHANGELOG.md +14 -0
- package/LICENSE.md +15 -0
- package/README.md +29 -0
- package/build/index.html +11 -0
- package/build/main.js +1 -0
- package/build.zip +0 -0
- package/configuration/appeareanceGroup/AppeareanceInputs.ts +75 -0
- package/configuration/appeareanceGroup/index.ts +8 -0
- package/configuration/index.ts +8 -0
- package/configuration.json +123 -0
- package/icon.png +0 -0
- package/package.json +35 -0
- package/src/App.tsx +114 -0
- package/src/components/Card.tsx +43 -0
- package/src/components/FontLoader.tsx +41 -0
- package/src/components/FormattedText.tsx +81 -0
- package/src/index.html +11 -0
- package/src/main.ts +27 -0
- package/src/parameters.d.ts +9 -0
- package/src/test/downloads/123.ttf +0 -0
- package/src/test/downloads/7953953c-7029-4730-ae4d-cff4abd5288f.ttf +0 -0
- package/src/test/downloads/Earthquakes.png +0 -0
- package/src/test/downloads/c705a739-312e-4334-9231-e73a05e9d382.ttf +0 -0
- package/src/test/downloads/futura-font.ttf +0 -0
- package/src/test/parameters.json +34 -0
- package/src/types.d.ts +80 -0
- package/src/utils.ts +31 -0
- package/tsconfig.json +37 -0
package/build.zip
ADDED
|
Binary file
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Label,
|
|
3
|
+
SelectAssetKna,
|
|
4
|
+
SelectInput,
|
|
5
|
+
TextFormat,
|
|
6
|
+
TextInput,
|
|
7
|
+
} from '@vixoniccom/modules'
|
|
8
|
+
|
|
9
|
+
export const appeareanceInputs = [
|
|
10
|
+
new Label({ label: 'General' }),
|
|
11
|
+
new TextInput({
|
|
12
|
+
id: 'padding',
|
|
13
|
+
label: 'Márgenes',
|
|
14
|
+
required: false,
|
|
15
|
+
description:
|
|
16
|
+
'CSS para mover la tarjeta. El formato es arriba/derecha/abajo/izquieda',
|
|
17
|
+
}),
|
|
18
|
+
new SelectAssetKna({
|
|
19
|
+
id: 'backgroundImage',
|
|
20
|
+
label: 'Fondo',
|
|
21
|
+
required: false,
|
|
22
|
+
extensions: ['jpg', 'png'],
|
|
23
|
+
}),
|
|
24
|
+
|
|
25
|
+
new Label({ label: 'Diseño de la tarjeta' }),
|
|
26
|
+
new TextInput({
|
|
27
|
+
id: 'cardWidth',
|
|
28
|
+
label: 'Ancho',
|
|
29
|
+
required: false,
|
|
30
|
+
description: 'Ancho de la tarjeta',
|
|
31
|
+
}),
|
|
32
|
+
new TextInput({
|
|
33
|
+
id: 'cardHeight',
|
|
34
|
+
label: 'Alto',
|
|
35
|
+
required: false,
|
|
36
|
+
description: 'Altura de la tarjeta',
|
|
37
|
+
}),
|
|
38
|
+
new TextInput({
|
|
39
|
+
id: 'cardGap',
|
|
40
|
+
label: 'Separación entre elementos',
|
|
41
|
+
required: false,
|
|
42
|
+
description: 'Separación entre los elementos de la tarjeta',
|
|
43
|
+
}),
|
|
44
|
+
|
|
45
|
+
new Label({ label: 'Textos de la tarjeta' }),
|
|
46
|
+
new TextFormat({
|
|
47
|
+
id: 'aqiFormat',
|
|
48
|
+
label: 'Formato del índice de calidad del aire',
|
|
49
|
+
}),
|
|
50
|
+
new TextFormat({
|
|
51
|
+
id: 'stationFormat',
|
|
52
|
+
label: 'Formato de estación que realizó la medición',
|
|
53
|
+
}),
|
|
54
|
+
new TextFormat({
|
|
55
|
+
id: 'qualityFormat',
|
|
56
|
+
label: 'Formato del mensaje sobre la calidad del aire',
|
|
57
|
+
}),
|
|
58
|
+
|
|
59
|
+
new Label({ label: 'Datos' }),
|
|
60
|
+
new SelectInput({
|
|
61
|
+
id: 'updateData',
|
|
62
|
+
label: 'Frecuencia con la que se piden datos',
|
|
63
|
+
items: [
|
|
64
|
+
{ label: '1 minuto', value: 1 },
|
|
65
|
+
{ label: '5 minutos', value: 5 },
|
|
66
|
+
{ label: '10 minutos', value: 10 },
|
|
67
|
+
{ label: '30 minutos', value: 30 },
|
|
68
|
+
{ label: '45 minutos', value: 45 },
|
|
69
|
+
{ label: '1 hora', value: 60 },
|
|
70
|
+
],
|
|
71
|
+
defaultValue: 1,
|
|
72
|
+
}),
|
|
73
|
+
new TextInput({ id: 'msj0', label: 'Mensaje sin Datos' }),
|
|
74
|
+
new TextFormat({ id: 'formatMjs', label: 'Formato Mensaje sin Datos' }),
|
|
75
|
+
]
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": [
|
|
3
|
+
{
|
|
4
|
+
"id": "appeareanceGroup",
|
|
5
|
+
"label": "Apariencia",
|
|
6
|
+
"type": "group",
|
|
7
|
+
"items": [
|
|
8
|
+
{
|
|
9
|
+
"type": "label",
|
|
10
|
+
"label": "General"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"id": "padding",
|
|
14
|
+
"label": "Márgenes",
|
|
15
|
+
"type": "text-input",
|
|
16
|
+
"description": "CSS para mover la tarjeta. El formato es arriba/derecha/abajo/izquieda",
|
|
17
|
+
"required": false
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "backgroundImage",
|
|
21
|
+
"label": "Fondo",
|
|
22
|
+
"type": "select-asset-kna-input",
|
|
23
|
+
"required": false,
|
|
24
|
+
"extensions": [
|
|
25
|
+
"jpg",
|
|
26
|
+
"png"
|
|
27
|
+
],
|
|
28
|
+
"multiple": false
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"type": "label",
|
|
32
|
+
"label": "Diseño de la tarjeta"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "cardWidth",
|
|
36
|
+
"label": "Ancho",
|
|
37
|
+
"type": "text-input",
|
|
38
|
+
"description": "Ancho de la tarjeta",
|
|
39
|
+
"required": false
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"id": "cardHeight",
|
|
43
|
+
"label": "Alto",
|
|
44
|
+
"type": "text-input",
|
|
45
|
+
"description": "Altura de la tarjeta",
|
|
46
|
+
"required": false
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "cardGap",
|
|
50
|
+
"label": "Separación entre elementos",
|
|
51
|
+
"type": "text-input",
|
|
52
|
+
"description": "Separación entre los elementos de la tarjeta",
|
|
53
|
+
"required": false
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"type": "label",
|
|
57
|
+
"label": "Textos de la tarjeta"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "aqiFormat",
|
|
61
|
+
"label": "Formato del índice de calidad del aire",
|
|
62
|
+
"type": "text-format"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "stationFormat",
|
|
66
|
+
"label": "Formato de estación que realizó la medición",
|
|
67
|
+
"type": "text-format"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"id": "qualityFormat",
|
|
71
|
+
"label": "Formato del mensaje sobre la calidad del aire",
|
|
72
|
+
"type": "text-format"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"type": "label",
|
|
76
|
+
"label": "Datos"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"id": "updateData",
|
|
80
|
+
"label": "Frecuencia con la que se piden datos",
|
|
81
|
+
"type": "select-input",
|
|
82
|
+
"items": [
|
|
83
|
+
{
|
|
84
|
+
"label": "1 minuto",
|
|
85
|
+
"value": 1
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"label": "5 minutos",
|
|
89
|
+
"value": 5
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"label": "10 minutos",
|
|
93
|
+
"value": 10
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"label": "30 minutos",
|
|
97
|
+
"value": 30
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"label": "45 minutos",
|
|
101
|
+
"value": 45
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"label": "1 hora",
|
|
105
|
+
"value": 60
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
"defaultValue": 1
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"id": "msj0",
|
|
112
|
+
"label": "Mensaje sin Datos",
|
|
113
|
+
"type": "text-input"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"id": "formatMjs",
|
|
117
|
+
"label": "Formato Mensaje sin Datos",
|
|
118
|
+
"type": "text-format"
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}
|
package/icon.png
ADDED
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vixoniccom/aqi",
|
|
3
|
+
"alias": "Air-Quality-Index",
|
|
4
|
+
"description": "Muestra información sobre la calidad del aire en una ubicación específica",
|
|
5
|
+
"color": "#3395FF",
|
|
6
|
+
"license": "ISC",
|
|
7
|
+
"tags": [],
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Daniel Alvayay"
|
|
10
|
+
},
|
|
11
|
+
"version": "0.0.1-dev.0",
|
|
12
|
+
"scripts": {
|
|
13
|
+
"prepublishOnly": "vixonic-module-packager --mode=build",
|
|
14
|
+
"watch": "vixonic-module-packager --mode=watch",
|
|
15
|
+
"run": "vixonic-module-packager --mode=run",
|
|
16
|
+
"configuration": "vixonic-module-packager --mode generate-configuration",
|
|
17
|
+
"configuration:validate": "vixonic-module-packager --mode validate-configuration",
|
|
18
|
+
"release": "standard-version",
|
|
19
|
+
"prerelease-beta": "standard-version --prerelease beta",
|
|
20
|
+
"prerelease-dev": "standard-version --prerelease dev"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"axios": "^0.21.1",
|
|
24
|
+
"localforage": "^1.10.0",
|
|
25
|
+
"react": "^16.14.0",
|
|
26
|
+
"react-dom": "^16.14.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/react": "^16.14.14",
|
|
30
|
+
"@types/react-dom": "^16.9.14",
|
|
31
|
+
"@vixoniccom/module-packager": "^2.6.0",
|
|
32
|
+
"@vixoniccom/modules": "^2.16.0",
|
|
33
|
+
"standard-version": "^9.3.1"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/App.tsx
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import React, { useEffect, useState } from 'react'
|
|
3
|
+
import { Card } from './components/Card'
|
|
4
|
+
import { FontLoader } from './components/FontLoader'
|
|
5
|
+
import { FormattedText } from './components/FormattedText'
|
|
6
|
+
import { AirQuality, StorageData } from './types'
|
|
7
|
+
import { API_RESPONSE_STATUS } from './utils'
|
|
8
|
+
import localforage from 'localforage'
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
data: VixonicData
|
|
12
|
+
start: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const App: React.FunctionComponent<Props> = ({ data, start }) => {
|
|
16
|
+
const { parameters, downloadsPath } = data
|
|
17
|
+
const backgroundImageState = parameters.backgroundImage ? `url('${downloadsPath}/${parameters.backgroundImage.filename}')` : ''
|
|
18
|
+
const [formattedData, setFormattedData] = useState<AirQuality | null>()
|
|
19
|
+
const updateTime = (parameters?.updateData || 1) * 60000
|
|
20
|
+
|
|
21
|
+
const refetchData = (referenceDate: Date): boolean => {
|
|
22
|
+
const now = new Date()
|
|
23
|
+
|
|
24
|
+
const sameYear = referenceDate.getFullYear() === now.getFullYear()
|
|
25
|
+
const sameMonth = referenceDate.getMonth() === now.getMonth()
|
|
26
|
+
const sameDay = referenceDate.getDate() === now.getDate()
|
|
27
|
+
|
|
28
|
+
if (sameYear && sameMonth && sameDay) {
|
|
29
|
+
const differenceMinutes = Math.abs(now.getTime() - referenceDate.getTime())
|
|
30
|
+
return differenceMinutes >= updateTime
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return true
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const requestData = async (): Promise<AirQuality | null> => {
|
|
37
|
+
const TOKEN = "9fa7d8df1a621c9d518a351a13ff9b94093b9dac"
|
|
38
|
+
const URL = `https://api.waqi.info/feed/here/?token=${TOKEN}`
|
|
39
|
+
try {
|
|
40
|
+
const response = await axios.get<AirQuality>(URL)
|
|
41
|
+
if (response?.status === 200) {
|
|
42
|
+
return response.data
|
|
43
|
+
}
|
|
44
|
+
return null
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.log(error)
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const getData = async () => {
|
|
52
|
+
const storageData: StorageData | null = await localforage.getItem('aqi')
|
|
53
|
+
let data
|
|
54
|
+
if (!storageData?.item || refetchData(storageData?.date ?? new Date(0))) {
|
|
55
|
+
data = await requestData()
|
|
56
|
+
await localforage.setItem('aqi', { item: data, date: new Date() })
|
|
57
|
+
} else {
|
|
58
|
+
data = storageData.item
|
|
59
|
+
}
|
|
60
|
+
setFormattedData(data)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (!start) return
|
|
65
|
+
getData()
|
|
66
|
+
const interval = setInterval(() => {
|
|
67
|
+
getData()
|
|
68
|
+
}, updateTime)
|
|
69
|
+
return () => clearInterval(interval)
|
|
70
|
+
}, [start, updateTime])
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div
|
|
74
|
+
style={{
|
|
75
|
+
position: 'absolute',
|
|
76
|
+
top: 0,
|
|
77
|
+
bottom: 0,
|
|
78
|
+
left: 0,
|
|
79
|
+
right: 0,
|
|
80
|
+
backgroundImage: backgroundImageState,
|
|
81
|
+
backgroundSize: '100% 100%',
|
|
82
|
+
padding: parameters?.padding
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
85
|
+
{formattedData?.status === API_RESPONSE_STATUS.OK ? (
|
|
86
|
+
<div>
|
|
87
|
+
<FontLoader paths={['aqiFormat.font', 'stationFormat.font', 'qualityFormat.font']} parameters={parameters} downloadsPath={downloadsPath} />
|
|
88
|
+
|
|
89
|
+
<Card data={formattedData?.data}
|
|
90
|
+
format={{
|
|
91
|
+
cardWidth: parameters?.cardWidth,
|
|
92
|
+
cardHeight: parameters?.cardHeight,
|
|
93
|
+
cardGap: parameters?.cardGap,
|
|
94
|
+
aqiFormat: parameters?.aqiFormat,
|
|
95
|
+
stationFormat: parameters?.stationFormat,
|
|
96
|
+
qualityFormat: parameters?.qualityFormat
|
|
97
|
+
}} />
|
|
98
|
+
</div>
|
|
99
|
+
) : (
|
|
100
|
+
<div>
|
|
101
|
+
<FontLoader paths={['formatMjs.font']} parameters={parameters} downloadsPath={downloadsPath} />
|
|
102
|
+
<div style={{
|
|
103
|
+
display: 'flex',
|
|
104
|
+
position: 'relative',
|
|
105
|
+
flex: '1 1 0%',
|
|
106
|
+
flexDirection: 'column',
|
|
107
|
+
}}>
|
|
108
|
+
<FormattedText text={parameters?.msj0 || 'No hay datos para mostrar'} format={parameters?.formatMjs} />
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
)}
|
|
112
|
+
</div>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { assingAirQuality } from '../utils'
|
|
3
|
+
import { Data } from '../types';
|
|
4
|
+
import { TextFormat } from '@vixoniccom/modules'
|
|
5
|
+
import { FormattedText } from './FormattedText'
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
data: Data;
|
|
9
|
+
format: {
|
|
10
|
+
cardWidth?: string;
|
|
11
|
+
cardHeight?: string;
|
|
12
|
+
cardGap?: string;
|
|
13
|
+
aqiFormat?: TextFormat.Value;
|
|
14
|
+
stationFormat?: TextFormat.Value;
|
|
15
|
+
qualityFormat?: TextFormat.Value;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const Card: React.FunctionComponent<Props> = ({ data, format }) => {
|
|
20
|
+
const { color, quality } = assingAirQuality(data?.aqi)
|
|
21
|
+
return (
|
|
22
|
+
<div style={{
|
|
23
|
+
display: 'flex',
|
|
24
|
+
width: format.cardWidth || 300,
|
|
25
|
+
height: format.cardHeight || 200,
|
|
26
|
+
flexDirection: 'column',
|
|
27
|
+
padding: '1rem',
|
|
28
|
+
backgroundColor: color,
|
|
29
|
+
border: '2px solid #000',
|
|
30
|
+
gap: format.cardGap || 2,
|
|
31
|
+
}}>
|
|
32
|
+
<div style={{ textAlign: `${format.aqiFormat?.alignment?.horizontal || 'left'}` }}>
|
|
33
|
+
<FormattedText text={String(data?.aqi)} format={format.aqiFormat} />
|
|
34
|
+
</div>
|
|
35
|
+
<div style={{ textAlign: `${format.stationFormat?.alignment?.horizontal || 'left'}` }}>
|
|
36
|
+
<FormattedText text={String(`Estación: ${data?.city?.name}`)} format={format.stationFormat} />
|
|
37
|
+
</div>
|
|
38
|
+
<div style={{ textAlign: `${format.qualityFormat?.alignment?.horizontal || 'left'}` }}>
|
|
39
|
+
<FormattedText text={data?.aqi ? quality : ''} format={format.qualityFormat} />
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
interface Props {
|
|
4
|
+
paths: Array<string>
|
|
5
|
+
parameters: any
|
|
6
|
+
downloadsPath: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const FontLoader: React.FunctionComponent<Props> = ({ paths, parameters, downloadsPath }) => {
|
|
10
|
+
const _getValueByPath = (path: any, obj: any): any => {
|
|
11
|
+
let pathsArray = path.split('.')
|
|
12
|
+
let propertyName = pathsArray[0]
|
|
13
|
+
if (pathsArray.length === 1) return obj[propertyName]
|
|
14
|
+
else {
|
|
15
|
+
pathsArray.splice(0, 1)
|
|
16
|
+
return _getValueByPath(pathsArray.join('.'), obj[propertyName])
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const fonts = paths.map((path: any) => {
|
|
21
|
+
try {
|
|
22
|
+
let fontParam = _getValueByPath(path, parameters)
|
|
23
|
+
let font = {
|
|
24
|
+
family: fontParam.filename.replace('.', '-'),
|
|
25
|
+
src: `${downloadsPath}/${fontParam.filename}`
|
|
26
|
+
}
|
|
27
|
+
return `
|
|
28
|
+
@font-face {
|
|
29
|
+
font-family: "${font.family}";
|
|
30
|
+
src: url("${font.src}");
|
|
31
|
+
}
|
|
32
|
+
`
|
|
33
|
+
} catch (err) {
|
|
34
|
+
return ''
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<style>{fonts}</style>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
const alignments = {
|
|
4
|
+
center: 'center',
|
|
5
|
+
left: 'flex-start',
|
|
6
|
+
right: 'flex-end'
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type Aligment = {
|
|
10
|
+
horizontal: keyof typeof alignments,
|
|
11
|
+
vertical: keyof typeof alignments
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
format?: any
|
|
16
|
+
lineHeight?: number
|
|
17
|
+
maxChar?: number
|
|
18
|
+
style?: number
|
|
19
|
+
text: string
|
|
20
|
+
unit?: string
|
|
21
|
+
paddingBottom?: string
|
|
22
|
+
paddingTop?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const FormattedText: React.FunctionComponent<Props> = ({ text, format, maxChar, lineHeight, style, unit, paddingBottom, paddingTop }) => {
|
|
26
|
+
const trimText = (text: any, maxChar: any) => {
|
|
27
|
+
const isValid = maxChar && maxChar >= 3
|
|
28
|
+
if (isValid && (text && text.length > maxChar) || false) {
|
|
29
|
+
let returnText = text.substring(0, maxChar - 3)
|
|
30
|
+
returnText.substr(-1, 3)
|
|
31
|
+
return `${returnText.trim()}...`
|
|
32
|
+
}
|
|
33
|
+
return text
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const checkNested = (obj: any, path: any): any => {
|
|
37
|
+
let arr = path.split('.')
|
|
38
|
+
if (arr.length > 0) {
|
|
39
|
+
if (obj.hasOwnProperty(arr[0])) {
|
|
40
|
+
if (arr.length > 1) return checkNested(obj[arr[0]], arr.splice(1).join('.'))
|
|
41
|
+
else return true
|
|
42
|
+
} else return false
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const getHorizontalAlignment = (alignment: Aligment) => {
|
|
47
|
+
if (alignment) {
|
|
48
|
+
let hA = alignment.horizontal
|
|
49
|
+
return alignments.hasOwnProperty(hA) ? alignments[alignment.horizontal] : 'flex-start'
|
|
50
|
+
}
|
|
51
|
+
return 'flex-start'
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const renderText = maxChar ? trimText(text, maxChar) : text
|
|
55
|
+
let containerStyle = Object.assign({
|
|
56
|
+
display: 'inline-flex',
|
|
57
|
+
justifyContent: getHorizontalAlignment(format.alignment)
|
|
58
|
+
}, style)
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div style={containerStyle}>
|
|
62
|
+
<span style={{
|
|
63
|
+
color: format?.fontColor,
|
|
64
|
+
fontFamily: checkNested(format, 'font.filename') ? `"${format?.font?.filename?.replace('.', '-')}"` : '',
|
|
65
|
+
fontSize: `${format?.fontSize}${unit}`,
|
|
66
|
+
textAlign: checkNested(format, 'alignment.horizontal') ? format?.alignment?.horizontal : 'left',
|
|
67
|
+
lineHeight: lineHeight,
|
|
68
|
+
paddingBottom: paddingBottom,
|
|
69
|
+
paddingTop: paddingTop,
|
|
70
|
+
display: 'inline-flex'
|
|
71
|
+
}}>{renderText}</span>
|
|
72
|
+
</div>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
FormattedText.defaultProps = {
|
|
77
|
+
format: {},
|
|
78
|
+
lineHeight: 1,
|
|
79
|
+
unit: 'vh',
|
|
80
|
+
maxChar: undefined
|
|
81
|
+
}
|
package/src/index.html
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html style="position: absolute; height:100%; width: 100%; overflow: hidden;">
|
|
3
|
+
<head>
|
|
4
|
+
<title></title>
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
</head>
|
|
7
|
+
<body style="margin:0; overflow: hidden;">
|
|
8
|
+
<div id="root" style="position: absolute; top: 0; right: 0; bottom: 0; left: 0; overflow: hidden;">
|
|
9
|
+
</div>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|
package/src/main.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom';
|
|
3
|
+
import { App } from './App';
|
|
4
|
+
|
|
5
|
+
const { ipcRenderer } = require('electron')
|
|
6
|
+
let start: boolean = false
|
|
7
|
+
|
|
8
|
+
ipcRenderer.on('preload', (_event: any, data: VixonicData) => {
|
|
9
|
+
render(data)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
ipcRenderer.on('start', (_event: any, data: VixonicData) => {
|
|
13
|
+
start = true
|
|
14
|
+
render(data)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
ipcRenderer.on('update', (_event: any, data: VixonicData) => {
|
|
18
|
+
render(data)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
ipcRenderer.on('finish', (_event: VixonicData) => {
|
|
22
|
+
console.log('finish')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
function render(data: VixonicData) {
|
|
26
|
+
ReactDOM.render(React.createElement(App, { data, start }), document.getElementById('root'))
|
|
27
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare type VixonicData = {
|
|
2
|
+
downloadsPath: string
|
|
3
|
+
services: { [key: string]: { data?: any, updatedAt?: number } }
|
|
4
|
+
parameters: VixonicParameters
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
declare type VixonicParameters = Partial<{
|
|
8
|
+
padding: string, backgroundImage: {id?: string, filename?: string, extension?: string}, cardWidth: string, cardHeight: string, cardGap: string, aqiFormat: { fontSize?: number, fontColor?: string, alignment?: { horizontal?: 'left' | 'right' | 'center' }, font?: { filename: string, id: string, __isAsset: true } }, stationFormat: { fontSize?: number, fontColor?: string, alignment?: { horizontal?: 'left' | 'right' | 'center' }, font?: { filename: string, id: string, __isAsset: true } }, qualityFormat: { fontSize?: number, fontColor?: string, alignment?: { horizontal?: 'left' | 'right' | 'center' }, font?: { filename: string, id: string, __isAsset: true } }, updateData: 1 | 5 | 10 | 30 | 45 | 60, msj0: string, formatMjs: { fontSize?: number, fontColor?: string, alignment?: { horizontal?: 'left' | 'right' | 'center' }, font?: { filename: string, id: string, __isAsset: true } }
|
|
9
|
+
}>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"parameters": {
|
|
3
|
+
"padding": "200px 100px 100px 600px",
|
|
4
|
+
"backgroundImage": {
|
|
5
|
+
"id": "earthquakes",
|
|
6
|
+
"filename": "Earthquakes.png",
|
|
7
|
+
"__isAsset": true
|
|
8
|
+
},
|
|
9
|
+
"cardWidth": "700px",
|
|
10
|
+
"cardHeight": "600px",
|
|
11
|
+
"cardGap": "5rem",
|
|
12
|
+
"aqiFormat": {
|
|
13
|
+
"fontSize": 18,
|
|
14
|
+
"fontColor": "#000000",
|
|
15
|
+
"alignment": {
|
|
16
|
+
"horizontal": "center"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"stationFormat": {
|
|
20
|
+
"fontSize": 8,
|
|
21
|
+
"fontColor": "#000000",
|
|
22
|
+
"alignment": {
|
|
23
|
+
"horizontal": "center"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"qualityFormat": {
|
|
27
|
+
"fontSize": 4,
|
|
28
|
+
"fontColor": "#000000",
|
|
29
|
+
"alignment": {
|
|
30
|
+
"horizontal": "center"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|