Functional but settings modals position and size badly
This commit is contained in:
+11
-51
@@ -26,6 +26,7 @@ body {
|
|||||||
-moz-box-sizing: border-box;
|
-moz-box-sizing: border-box;
|
||||||
-webkit-box-sizing: border-box;
|
-webkit-box-sizing: border-box;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Styles for the close button */
|
/* Styles for the close button */
|
||||||
@@ -82,18 +83,12 @@ body {
|
|||||||
background: #333;
|
background: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure container contents don't overlap the buttons */
|
|
||||||
.container > *:not(.close-button):not(.settings-button) {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Styles for ClockWidget time display */
|
/* Styles for ClockWidget time display */
|
||||||
.clock-time {
|
.clock-time {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-size: 48px;
|
font-size: 48px;
|
||||||
font-family: 'IBM Plex Mono', monospace;
|
font-family: 'IBM Plex Mono', monospace;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SearchWidget form */
|
/* SearchWidget form */
|
||||||
@@ -107,51 +102,11 @@ body {
|
|||||||
padding: 5px;
|
padding: 5px;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 1);
|
||||||
color: #fff;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Widget settings modal */
|
.accept-button {
|
||||||
.widget-settings-modal {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-modal-content {
|
|
||||||
position: relative;
|
|
||||||
background: rgba(255, 255, 255, 0.9);
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
|
||||||
max-width: 300px;
|
|
||||||
width: 90%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-modal-content label {
|
|
||||||
display: block;
|
|
||||||
margin: 10px 0;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-modal-content select,
|
|
||||||
.widget-modal-content input[type="number"] {
|
|
||||||
width: 100%;
|
|
||||||
padding: 5px;
|
|
||||||
margin-top: 5px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-modal-content button {
|
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
background: #007bff;
|
background: #007bff;
|
||||||
@@ -161,7 +116,7 @@ body {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.widget-modal-content button:hover {
|
.accept-button:hover {
|
||||||
background: #0056b3;
|
background: #0056b3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +159,6 @@ body {
|
|||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -305,3 +259,9 @@ body {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.timezone-display{
|
||||||
|
font-size: small;
|
||||||
|
text-align: center;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
@@ -27,7 +27,10 @@ export const ClockWidgetComponent: React.FC<ClockWidgetProps> = ({ widget, updat
|
|||||||
return new Intl.DateTimeFormat('en-US', options).format(time);
|
return new Intl.DateTimeFormat('en-US', options).format(time);
|
||||||
};
|
};
|
||||||
|
|
||||||
return <div className="clock-time">{formatTime()}</div>;
|
return <>
|
||||||
|
<div className="clock-time">{formatTime()}</div>
|
||||||
|
<div className="timezone-display">{widget.timezone}</div>
|
||||||
|
</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ClockWidgetSettings = ({ widget, updateWidget }: ClockWidgetProps) => {
|
export const ClockWidgetSettings = ({ widget, updateWidget }: ClockWidgetProps) => {
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import React, { MouseEvent } from "react"
|
||||||
|
|
||||||
|
type ButtonProps = {
|
||||||
|
onClick: React.MouseEventHandler<HTMLButtonElement>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CloseButton({ onClick }: ButtonProps) {
|
||||||
|
return (
|
||||||
|
<button className="close-button" onClick={onClick}>×</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { ReactElement, ReactNode, useRef, useState } from "react";
|
||||||
|
|
||||||
|
interface ModalOverlayProps {
|
||||||
|
visible: boolean;
|
||||||
|
setVisible: (b: boolean) => void;
|
||||||
|
children: ReactElement | ReactElement[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ModalOverlay({visible, setVisible, children}: ModalOverlayProps) {
|
||||||
|
|
||||||
|
const modalRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
return visible && <div className="settings-modal" ref={modalRef}>
|
||||||
|
<div className="modal-content">
|
||||||
|
{children}
|
||||||
|
<button className="close-modal" onClick={() => setVisible(false)}>Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import { showGridState } from '../state/showGrid.state';
|
|||||||
import { WidgetData } from '../types';
|
import { WidgetData } from '../types';
|
||||||
import { loadableBackgroundImageState } from '../state/backgroundImage.state';
|
import { loadableBackgroundImageState } from '../state/backgroundImage.state';
|
||||||
import { settingsService } from '../services/settingsService';
|
import { settingsService } from '../services/settingsService';
|
||||||
|
import { ModalOverlay } from './ModalOverlay';
|
||||||
|
|
||||||
const GRID_COLS = 36;
|
const GRID_COLS = 36;
|
||||||
const GRID_ROWS = 18;
|
const GRID_ROWS = 18;
|
||||||
@@ -41,14 +42,21 @@ export const NewTab: React.FC = () => {
|
|||||||
);
|
);
|
||||||
setWidgets(updatedWidgets);
|
setWidgets(updatedWidgets);
|
||||||
settingsService.saveSetting('widgets', updatedWidgets)
|
settingsService.saveSetting('widgets', updatedWidgets)
|
||||||
console.log('Updated widget:', { id, updates })
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addWidget = (data: WidgetData) => {
|
||||||
|
const updatedWidgets = [...widgets, data]
|
||||||
|
setWidgets([...widgets, data])
|
||||||
|
settingsService.saveSetting('widgets', updatedWidgets)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<ModalOverlay />
|
||||||
<SettingsPanel
|
<SettingsPanel
|
||||||
backgroundImage={backgroundImage}
|
backgroundImage={backgroundImage}
|
||||||
setBackgroundImage={setBackgroundImage}
|
setBackgroundImage={setBackgroundImage}
|
||||||
|
addWidget={addWidget}
|
||||||
/>
|
/>
|
||||||
{showGrid && (
|
{showGrid && (
|
||||||
<div className="grid-overlay">
|
<div className="grid-overlay">
|
||||||
|
|||||||
@@ -1,42 +1,39 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { CogwheelIcon } from './CogwheelIcon';
|
import { CogwheelIcon } from './CogwheelIcon';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom, useSetAtom } from 'jotai';
|
||||||
import { draggableState } from '../state/draggable.state';
|
import { editState } from '../state/edit.state';
|
||||||
import { showGridState } from '../state/showGrid.state';
|
import { showGridState } from '../state/showGrid.state';
|
||||||
import { settingsService } from '../services/settingsService';
|
import { intitialWidgetData } from '../defaults';
|
||||||
|
import { WidgetData, WidgetTypes } from '../types';
|
||||||
|
import { modalContent, modalVisible } from '../state/modalOverlay.state';
|
||||||
|
import { ModalOverlay } from './ModalOverlay';
|
||||||
|
|
||||||
interface SettingsPanelProps {
|
interface SettingsPanelProps {
|
||||||
backgroundImage: string,
|
backgroundImage: string,
|
||||||
setBackgroundImage: (url: string) => void
|
setBackgroundImage: (url: string) => void,
|
||||||
|
addWidget: (widget: WidgetData) => void,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SettingsPanel: React.FC<SettingsPanelProps> = ({ backgroundImage, setBackgroundImage }) => {
|
export const SettingsPanel: React.FC<SettingsPanelProps> = ({ backgroundImage, setBackgroundImage, addWidget }) => {
|
||||||
//const [backgroundUrl, setBackgroundUrl] = useAtom(backgroundImageState);
|
const [editEnabled, setEditEnabled] = useAtom(editState);
|
||||||
const [dragEnabled, setDragEnabled] = useAtom(draggableState);
|
|
||||||
const [showGrid, setShowGrid] = useAtom(showGridState);
|
const [showGrid, setShowGrid] = useAtom(showGridState);
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isModalShowing, setModalShowing] = useState(false);
|
||||||
|
|
||||||
const handleSave = () => {
|
const createWidget = (type: WidgetTypes) => {
|
||||||
//settingsService.saveSetting('backgroundUrl', backgroundUrl)
|
const newWidgetData = intitialWidgetData(type);
|
||||||
settingsService.saveSetting('dragEnabled', dragEnabled)
|
addWidget(newWidgetData)
|
||||||
settingsService.saveSetting('showGrid', showGrid)
|
|
||||||
|
|
||||||
setIsOpen(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const addWidget = (type: string) => {
|
|
||||||
//const newWidget = createBaseWidget(type, Math.floor(gridCols / 2), Math.floor(gridRows / 2), dragEnabled);
|
|
||||||
//saveSettings({ widgets: [...settings.widgets, newWidget] });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button className="settings-toggle" onClick={() => setIsOpen(!isOpen)}>
|
<button className="settings-toggle" onClick={() => setModalShowing(true)}>
|
||||||
<CogwheelIcon size={24} />
|
<CogwheelIcon size={24} />
|
||||||
</button>
|
</button>
|
||||||
{isOpen && (
|
<ModalOverlay
|
||||||
<div className="settings-modal">
|
setVisible={setModalShowing}
|
||||||
<div className="modal-content">
|
visible={isModalShowing}
|
||||||
|
>
|
||||||
<h2>Settings</h2>
|
<h2>Settings</h2>
|
||||||
<label>
|
<label>
|
||||||
Background URL:
|
Background URL:
|
||||||
@@ -51,8 +48,8 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({ backgroundImage, s
|
|||||||
Drag Enabled:
|
Drag Enabled:
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={dragEnabled}
|
checked={editEnabled}
|
||||||
onChange={(e) => setDragEnabled(e.target.checked)}
|
onChange={() => setEditEnabled(b => !b)}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
@@ -60,17 +57,14 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({ backgroundImage, s
|
|||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={showGrid}
|
checked={showGrid}
|
||||||
onChange={(e) => setShowGrid(e.target.checked)}
|
onChange={() => setShowGrid(b => !b)}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<div className="widget-buttons">
|
<div className="widget-buttons">
|
||||||
<button onClick={() => addWidget('clock')}>Add Clock</button>
|
<button onClick={() => createWidget('clock')}>Add Clock</button>
|
||||||
<button onClick={() => addWidget('search')}>Add Search</button>
|
<button onClick={() => createWidget('search')}>Add Search</button>
|
||||||
</div>
|
</div>
|
||||||
<button className="close-modal" onClick={handleSave}>Save & Close</button>
|
</ModalOverlay >
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
import React, { useRef, useState, useEffect } from 'react';
|
import React, { useRef, useState, useEffect, ReactElement } from 'react';
|
||||||
import { WidgetData } from '../types';
|
import { WidgetData } from '../types';
|
||||||
import { CogwheelIcon } from './CogwheelIcon';
|
import { CogwheelIcon } from './CogwheelIcon';
|
||||||
import { useAtom, useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import { draggableState } from '../state/draggable.state';
|
import { editState } from '../state/edit.state';
|
||||||
|
import { CloseButton } from './Closebutton';
|
||||||
|
import { ModalOverlay } from './ModalOverlay';
|
||||||
|
|
||||||
interface WidgetProps {
|
interface WidgetProps {
|
||||||
widget: WidgetData;
|
widget: WidgetData;
|
||||||
gridSize: { width: number; height: number };
|
gridSize: { width: number; height: number };
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
settingsContent?: React.ReactNode;
|
settingsContent: ReactElement;
|
||||||
updateWidget: (id: string, data: Partial<WidgetData>) => void
|
updateWidget: (id: string, data: Partial<WidgetData>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,19 +19,21 @@ export const Widget: React.FC<WidgetProps> = ({ widget, gridSize, children, sett
|
|||||||
const modalRef = useRef<HTMLDivElement>(null);
|
const modalRef = useRef<HTMLDivElement>(null);
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
|
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
|
||||||
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
const [isSettingsOpen, setSettingsOpen] = useState(false);
|
||||||
const [widthInput, setWidthInput] = useState(widget.gridWidth);
|
const [widthInput, setWidthInput] = useState(widget.gridWidth);
|
||||||
const [heightInput, setHeightInput] = useState(widget.gridHeight);
|
const [heightInput, setHeightInput] = useState(widget.gridHeight);
|
||||||
|
|
||||||
const isDraggable = useAtomValue(draggableState)
|
const isDraggable = useAtomValue(editState)
|
||||||
const snapToGrid = (pos: number, grid: number) => Math.round(pos / grid);
|
const snapToGrid = (pos: number, grid: number) => Math.round(pos / grid);
|
||||||
|
|
||||||
const handleMouseDown = (e: React.MouseEvent) => {
|
const handleMouseDown = (e: React.MouseEvent) => {
|
||||||
if(!isDraggable || isSettingsOpen){
|
if (!isDraggable || isSettingsOpen) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widgetRef.current && e.target === widgetRef.current) {
|
e.stopPropagation()
|
||||||
|
|
||||||
|
if (!!widgetRef.current) {
|
||||||
setIsDragging(true);
|
setIsDragging(true);
|
||||||
const rect = widgetRef.current.getBoundingClientRect();
|
const rect = widgetRef.current.getBoundingClientRect();
|
||||||
setDragOffset({
|
setDragOffset({
|
||||||
@@ -62,22 +66,16 @@ export const Widget: React.FC<WidgetProps> = ({ widget, gridSize, children, sett
|
|||||||
|
|
||||||
const handleClickOutside = (e: MouseEvent) => {
|
const handleClickOutside = (e: MouseEvent) => {
|
||||||
if (modalRef.current && !modalRef.current.contains(e.target as Node)) {
|
if (modalRef.current && !modalRef.current.contains(e.target as Node)) {
|
||||||
setIsSettingsOpen(false);
|
setSettingsOpen(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const stopPropagation = (e: React.MouseEvent) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSizeChange = () => {
|
useEffect(() => {
|
||||||
const newWidth = Math.max(1, widthInput);
|
const newWidth = Math.max(1, widthInput);
|
||||||
const newHeight = Math.max(1, heightInput);
|
const newHeight = Math.max(1, heightInput);
|
||||||
updateWidget(widget.id, { gridWidth: newWidth, gridHeight: newHeight });
|
updateWidget(widget.id, { gridWidth: newWidth, gridHeight: newHeight });
|
||||||
setWidthInput(newWidth);
|
}, [heightInput, widthInput])
|
||||||
setHeightInput(newHeight);
|
|
||||||
setIsSettingsOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isDragging) {
|
if (isDragging) {
|
||||||
@@ -110,15 +108,60 @@ export const Widget: React.FC<WidgetProps> = ({ widget, gridSize, children, sett
|
|||||||
}
|
}
|
||||||
}, [widget.gridX, widget.gridY, widget.gridWidth, widget.gridHeight, gridSize, isDragging]);
|
}, [widget.gridX, widget.gridY, widget.gridWidth, widget.gridHeight, gridSize, isDragging]);
|
||||||
|
|
||||||
const genericSettingsContent = (
|
return (
|
||||||
<>
|
|
||||||
|
<div
|
||||||
|
ref={widgetRef}
|
||||||
|
className="container"
|
||||||
|
onMouseDown={handleMouseDown}
|
||||||
|
style={{ cursor: isDraggable && !isSettingsOpen ? 'grab' : 'default' }}
|
||||||
|
>
|
||||||
|
<ModalOverlay
|
||||||
|
setVisible={setSettingsOpen}
|
||||||
|
visible={isSettingsOpen}
|
||||||
|
>
|
||||||
|
<SettingsComponent
|
||||||
|
heightInput={heightInput}
|
||||||
|
setHeightInput={setHeightInput}
|
||||||
|
setWidthInput={setWidthInput}
|
||||||
|
widthInput={widthInput}
|
||||||
|
>
|
||||||
|
{settingsContent}
|
||||||
|
</SettingsComponent>
|
||||||
|
</ModalOverlay>
|
||||||
|
{isDraggable && <>
|
||||||
|
<CloseButton onClick={() => undefined /*removeWidget(widget.id)*/} />
|
||||||
|
<button
|
||||||
|
className="settings-button"
|
||||||
|
onClick={() => setSettingsOpen(true)}
|
||||||
|
>
|
||||||
|
<CogwheelIcon size={24} />
|
||||||
|
</button>
|
||||||
|
</>}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
type GenericSettingsProps = {
|
||||||
|
widthInput: number;
|
||||||
|
setWidthInput: (n: number) => void;
|
||||||
|
heightInput: number;
|
||||||
|
setHeightInput: (n: number) => void;
|
||||||
|
children: ReactElement | ReactElement[]
|
||||||
|
}
|
||||||
|
|
||||||
|
function SettingsComponent({ widthInput, setWidthInput, heightInput, setHeightInput, children }: GenericSettingsProps) {
|
||||||
|
return <>
|
||||||
|
{children}
|
||||||
<label>
|
<label>
|
||||||
Width (grid cells):
|
Width (grid cells):
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
value={widthInput}
|
value={widthInput}
|
||||||
onChange={(e) => setWidthInput(parseInt(e.target.value) || 1)}
|
onChange={(e) => { setWidthInput(parseInt(e.target.value)); }}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
@@ -127,38 +170,8 @@ export const Widget: React.FC<WidgetProps> = ({ widget, gridSize, children, sett
|
|||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
value={heightInput}
|
value={heightInput}
|
||||||
onChange={(e) => setHeightInput(parseInt(e.target.value) || 1)}
|
onChange={(e) => { setHeightInput(parseInt(e.target.value)); }}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<button onClick={handleSizeChange}>Apply</button>
|
|
||||||
</>
|
</>
|
||||||
);
|
}
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={widgetRef}
|
|
||||||
className="container"
|
|
||||||
onMouseDown={handleMouseDown}
|
|
||||||
style={{ cursor: isDraggable && !isSettingsOpen ? 'grab' : 'default' }}
|
|
||||||
>
|
|
||||||
<button className="close-button" onClick={() => undefined /*removeWidget(widget.id)*/} onMouseDown={stopPropagation}>×</button>
|
|
||||||
<button
|
|
||||||
className="settings-button"
|
|
||||||
onClick={() => setIsSettingsOpen(!isSettingsOpen)}
|
|
||||||
onMouseDown={stopPropagation}
|
|
||||||
>
|
|
||||||
<CogwheelIcon size={24} />
|
|
||||||
</button>
|
|
||||||
{children}
|
|
||||||
{isSettingsOpen && (
|
|
||||||
<div className="widget-settings-modal" onMouseDown={stopPropagation}>
|
|
||||||
<div ref={modalRef} className="widget-modal-content">
|
|
||||||
<button className="close-button" onClick={() => setIsSettingsOpen(false)} onMouseDown={stopPropagation}>×</button>
|
|
||||||
{settingsContent}
|
|
||||||
{genericSettingsContent}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { WidgetData, SearchWidgetData, ClockWidgetData, WidgetExport, WidgetTypes } from '../types';
|
import { WidgetData, WidgetExport, WidgetTypes } from '../types';
|
||||||
import { Widget } from './Widget';
|
import { Widget } from './Widget';
|
||||||
import { SearchWidget } from './SearchWidget';
|
import { SearchWidget } from './SearchWidget';
|
||||||
import { ClockWidget } from './ClockWidget';
|
import { ClockWidget } from './ClockWidget';
|
||||||
|
|||||||
+29
-25
@@ -1,31 +1,35 @@
|
|||||||
import { GlobalSettings, SearchWidgetData, ClockWidgetData } from './types';
|
import { GlobalSettings, SearchWidgetData, ClockWidgetData, WidgetTypes, WidgetData } from './types';
|
||||||
|
|
||||||
export const DEFAULT_SETTINGS: GlobalSettings = {
|
export const DEFAULT_SETTINGS: GlobalSettings = {
|
||||||
backgroundUrl: '',
|
backgroundUrl: '',
|
||||||
widgets: [
|
widgets: [],
|
||||||
{
|
editEnabled: true,
|
||||||
id: 'clock-1',
|
showGrid: true
|
||||||
type: 'clock',
|
};
|
||||||
gridX: 15,
|
|
||||||
gridY: 7,
|
export const intitialWidgetData = (type: WidgetTypes): WidgetData => {
|
||||||
gridWidth: 6, // Clock: 6x3
|
switch (type) {
|
||||||
gridHeight: 3,
|
case 'clock':
|
||||||
dragEnabled: true,
|
return {
|
||||||
showSeconds: true,
|
|
||||||
use24Hour: false,
|
|
||||||
timezone: 'UTC' // Default timezone
|
|
||||||
} as ClockWidgetData,
|
|
||||||
{
|
|
||||||
id: 'search-1',
|
|
||||||
type: 'search',
|
|
||||||
gridX: 13,
|
|
||||||
gridY: 10,
|
|
||||||
gridWidth: 10, // Search: 10x2
|
|
||||||
gridHeight: 2,
|
gridHeight: 2,
|
||||||
dragEnabled: true,
|
gridWidth: 6,
|
||||||
|
gridX: 15,
|
||||||
|
gridY: 6,
|
||||||
|
timezone: 'UTC',
|
||||||
|
showSeconds: false,
|
||||||
|
use24Hour: true,
|
||||||
|
type: 'clock',
|
||||||
|
id: `${type}-${Date.now()}`
|
||||||
|
} as ClockWidgetData
|
||||||
|
case 'search':
|
||||||
|
return {
|
||||||
|
gridHeight: 2,
|
||||||
|
gridWidth: 10,
|
||||||
|
gridX: 13,
|
||||||
|
gridY: 6,
|
||||||
|
id: `${type}-${Date.now()}`,
|
||||||
|
type: 'search',
|
||||||
engine: 'Google'
|
engine: 'Google'
|
||||||
} as SearchWidgetData
|
} as SearchWidgetData
|
||||||
],
|
}
|
||||||
dragEnabled: true,
|
}
|
||||||
showGrid: false
|
|
||||||
};
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { atom } from "jotai";
|
|
||||||
|
|
||||||
export const draggableState = atom<boolean>(false)
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { atom } from "jotai";
|
||||||
|
import { DEFAULT_SETTINGS } from "../defaults";
|
||||||
|
|
||||||
|
export const editState = atom<boolean>(DEFAULT_SETTINGS.editEnabled)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { atom } from "jotai";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
export const modalVisible = atom<boolean>(false)
|
||||||
|
export const modalContent = atom<ReactNode>(undefined)
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
import { atom } from "jotai";
|
import { atom } from "jotai";
|
||||||
|
import { DEFAULT_SETTINGS } from "../defaults";
|
||||||
|
|
||||||
export const showGridState = atom<boolean>(false)
|
export const showGridState = atom<boolean>(DEFAULT_SETTINGS.showGrid)
|
||||||
@@ -25,7 +25,7 @@ export type WidgetTypes = (SearchWidgetData | ClockWidgetData)['type']
|
|||||||
export interface GlobalSettings {
|
export interface GlobalSettings {
|
||||||
backgroundUrl: string;
|
backgroundUrl: string;
|
||||||
widgets: WidgetData[];
|
widgets: WidgetData[];
|
||||||
dragEnabled: boolean;
|
editEnabled: boolean;
|
||||||
showGrid: boolean;
|
showGrid: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user