1- import React , { createContext , useEffect , useState } from "react" ;
1+ import React , { createContext , useEffect , useRef , useState } from "react" ;
22import Widget , { WidgetState } from "./Widget" ;
33import WidgetMap from "./WidgetMap" ;
44import Header from "./Header" ;
55import Menu from "./Menu" ;
66import { Grid } from "./Grid" ;
77import { nanoid } from "nanoid" ;
8+ import { toPng } from "html-to-image" ;
89import styles from "./App.css" ;
910
11+ export interface Template {
12+ name : string ;
13+ image : string ;
14+ widgets : WidgetState < any > [ ] ;
15+ // settings:
16+ // theme:
17+ }
18+
1019interface AppContextType {
1120 widgets : WidgetState < any > [ ] ;
21+ templates : Template [ ] ;
22+ activeTemplate : number ;
1223 editing : boolean ;
1324 deleting : boolean ;
25+ hidden : boolean ;
1426 menuOpen : boolean ;
1527
1628 setWidgets : React . Dispatch < React . SetStateAction < WidgetState < any > [ ] > > ;
29+ setTemplates : React . Dispatch < React . SetStateAction < Template [ ] > > ;
1730 setEditing : React . Dispatch < React . SetStateAction < boolean > > ;
1831 setDeleting : React . Dispatch < React . SetStateAction < boolean > > ;
32+ setHidden : React . Dispatch < React . SetStateAction < boolean > > ;
1933 setMenuOpen : React . Dispatch < React . SetStateAction < boolean > > ;
2034
21- saveTemplate : ( ) => void ;
22- loadTemplate : ( ) => boolean ;
35+ saveTemplate : ( name ?: string ) => void ;
36+ loadTemplate : ( index ?: number ) => void ;
2337
2438 addWidget : ( type : string ) => WidgetState < any > ;
2539 removeWidget : ( id : string ) => void ;
@@ -47,28 +61,67 @@ const FallbackTemplate: WidgetState<any>[] = [
4761const App = ( ) => {
4862 const [ editing , setEditing ] = useState ( false ) ;
4963 const [ deleting , setDeleting ] = useState ( false ) ;
64+ const [ hidden , setHidden ] = useState ( false ) ;
5065 const [ menuOpen , setMenuOpen ] = useState ( false ) ;
5166 const [ widgets , setWidgets ] = useState < WidgetState < any > [ ] > ( [ ] ) ;
67+ const [ templates , setTemplates ] = useState < Template [ ] > ( [ ] ) ;
68+ const [ activeTemplate , setActiveTemplate ] = useState ( 0 ) ;
69+
70+ const gridRef = useRef ( null ) ;
71+
72+ async function saveTemplate ( name ?: string ) {
73+ const template = {
74+ name : name ?? templates [ activeTemplate ] ?. name ?? "Default" ,
75+ image : await toPng ( gridRef . current , {
76+ canvasWidth : 240 ,
77+ canvasHeight : 135 ,
78+ } ) ,
79+ widgets : structuredClone ( widgets ) ,
80+ } ;
5281
53- // TODO: This system will eventually keep an
54- // array of templates and store the index of
55- // the active template. For the time being,
56- // this uses a single template structure.
82+ if ( name === null || name === undefined ) {
83+ const _templates = [ ...templates ] ;
84+ _templates [ activeTemplate ] = template ;
85+ setTemplates ( [ ..._templates ] ) ;
86+ } else {
87+ setTemplates ( [ ...templates , template ] ) ;
88+ setActiveTemplate ( templates . length ) ;
89+ }
5790
58- function saveTemplate ( ) {
59- localStorage . setItem ( "template" , JSON . stringify ( widgets ) ) ;
91+ writeTemplates ( ) ;
6092 }
6193
62- function loadTemplate ( ) : boolean {
63- const template = localStorage . getItem ( "template" ) ;
94+ function loadTemplate ( index ?: number ) {
95+ const i = index ?? activeTemplate ;
96+ if ( i >= templates . length ) return ;
97+ setWidgets ( templates [ i ] . widgets ) ;
98+ setActiveTemplate ( i ) ;
99+ localStorage . setItem ( "activeTemplate" , JSON . stringify ( i ) ) ;
100+ }
64101
65- if ( template === null ) {
66- console . warn ( "No template stored" ) ;
67- return false ;
102+ function readTemplates ( ) {
103+ const _templates : Template [ ] =
104+ JSON . parse ( localStorage . getItem ( "templates" ) ) ?? [ ] ;
105+ const _activeTemplate : number =
106+ JSON . parse ( localStorage . getItem ( "activeTemplate" ) ) ?? 0 ;
107+
108+ if ( _templates . length === 0 ) {
109+ _templates . push ( {
110+ name : "Default" ,
111+ image : null ,
112+ widgets : structuredClone ( FallbackTemplate ) ,
113+ } ) ;
68114 }
69115
70- setWidgets ( JSON . parse ( template ) as WidgetState < any > [ ] ) ;
71- return true ;
116+ setTemplates ( _templates ) ;
117+ setActiveTemplate ( _activeTemplate ) ;
118+
119+ setWidgets ( _templates [ _activeTemplate ] . widgets ) ;
120+ }
121+
122+ function writeTemplates ( ) {
123+ localStorage . setItem ( "templates" , JSON . stringify ( templates ) ) ;
124+ localStorage . setItem ( "activeTemplate" , JSON . stringify ( activeTemplate ) ) ;
72125 }
73126
74127 function addWidget ( type : string ) {
@@ -94,9 +147,7 @@ const App = () => {
94147 }
95148
96149 useEffect ( ( ) => {
97- if ( ! loadTemplate ( ) ) {
98- setWidgets ( FallbackTemplate ) ;
99- }
150+ readTemplates ( ) ;
100151 } , [ ] ) ;
101152
102153 return (
@@ -109,13 +160,18 @@ const App = () => {
109160 < AppContext . Provider
110161 value = { {
111162 widgets,
163+ templates,
164+ activeTemplate,
112165 editing,
113166 deleting,
167+ hidden,
114168 menuOpen,
115169
116170 setWidgets,
171+ setTemplates,
117172 setEditing,
118173 setDeleting,
174+ setHidden,
119175 setMenuOpen,
120176
121177 saveTemplate,
@@ -126,7 +182,7 @@ const App = () => {
126182 } }
127183 >
128184 < Header > </ Header >
129- < Grid width = { 24 } height = { 12 } >
185+ < Grid width = { 24 } height = { 12 } ref = { gridRef } >
130186 { widgets . map ( ( state ) => {
131187 const map = WidgetMap [ state . type ] ;
132188 const Component = map . component ;
0 commit comments