diff --git a/public/styles.css b/public/styles.css index 1243f11..8edcbe6 100644 --- a/public/styles.css +++ b/public/styles.css @@ -25,7 +25,8 @@ body { box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); -moz-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 */ @@ -82,18 +83,12 @@ body { 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 */ .clock-time { color: #ffffff; font-size: 48px; font-family: 'IBM Plex Mono', monospace; text-align: center; - margin-bottom: 10px; } /* SearchWidget form */ @@ -107,51 +102,11 @@ body { padding: 5px; border: 1px solid rgba(255, 255, 255, 0.5); border-radius: 4px; - background: rgba(255, 255, 255, 0.1); - color: #fff; + background: rgba(255, 255, 255, 1); + color: #000; } -/* Widget settings modal */ -.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 { +.accept-button { margin-top: 10px; padding: 5px 10px; background: #007bff; @@ -161,7 +116,7 @@ body { cursor: pointer; } -.widget-modal-content button:hover { +.accept-button:hover { background: #0056b3; } @@ -204,7 +159,6 @@ body { left: 0; width: 100%; height: 100%; - background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; @@ -304,4 +258,10 @@ body { .grid-line.horizontal { width: 100%; height: 1px; +} + +.timezone-display{ + font-size: small; + text-align: center; + color: #ffffff; } \ No newline at end of file diff --git a/src/renderer/components/ClockWidget.tsx b/src/renderer/components/ClockWidget.tsx index 45e1f32..fc5b85b 100644 --- a/src/renderer/components/ClockWidget.tsx +++ b/src/renderer/components/ClockWidget.tsx @@ -27,7 +27,10 @@ export const ClockWidgetComponent: React.FC = ({ widget, updat return new Intl.DateTimeFormat('en-US', options).format(time); }; - return
{formatTime()}
; + return <> +
{formatTime()}
+
{widget.timezone}
+ ; }; export const ClockWidgetSettings = ({ widget, updateWidget }: ClockWidgetProps) => { diff --git a/src/renderer/components/Closebutton.tsx b/src/renderer/components/Closebutton.tsx new file mode 100644 index 0000000..24e149a --- /dev/null +++ b/src/renderer/components/Closebutton.tsx @@ -0,0 +1,11 @@ +import React, { MouseEvent } from "react" + +type ButtonProps = { + onClick: React.MouseEventHandler +} + +export function CloseButton({ onClick }: ButtonProps) { + return ( + + ) +} \ No newline at end of file diff --git a/src/renderer/components/ModalOverlay.tsx b/src/renderer/components/ModalOverlay.tsx new file mode 100644 index 0000000..fba2508 --- /dev/null +++ b/src/renderer/components/ModalOverlay.tsx @@ -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(null); + + return visible &&
+
+ {children} + +
+
+} \ No newline at end of file diff --git a/src/renderer/components/NewTab.tsx b/src/renderer/components/NewTab.tsx index ba68be0..bda2cfb 100644 --- a/src/renderer/components/NewTab.tsx +++ b/src/renderer/components/NewTab.tsx @@ -7,6 +7,7 @@ import { showGridState } from '../state/showGrid.state'; import { WidgetData } from '../types'; import { loadableBackgroundImageState } from '../state/backgroundImage.state'; import { settingsService } from '../services/settingsService'; +import { ModalOverlay } from './ModalOverlay'; const GRID_COLS = 36; const GRID_ROWS = 18; @@ -41,14 +42,21 @@ export const NewTab: React.FC = () => { ); setWidgets(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 ( <> + {showGrid && (
diff --git a/src/renderer/components/SettingsPanel.tsx b/src/renderer/components/SettingsPanel.tsx index f9085a3..6461f36 100644 --- a/src/renderer/components/SettingsPanel.tsx +++ b/src/renderer/components/SettingsPanel.tsx @@ -1,76 +1,70 @@ import React, { useState } from 'react'; import { CogwheelIcon } from './CogwheelIcon'; -import { useAtom } from 'jotai'; -import { draggableState } from '../state/draggable.state'; +import { useAtom, useSetAtom } from 'jotai'; +import { editState } from '../state/edit.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 { backgroundImage: string, - setBackgroundImage: (url: string) => void + setBackgroundImage: (url: string) => void, + addWidget: (widget: WidgetData) => void, } -export const SettingsPanel: React.FC = ({ backgroundImage, setBackgroundImage }) => { - //const [backgroundUrl, setBackgroundUrl] = useAtom(backgroundImageState); - const [dragEnabled, setDragEnabled] = useAtom(draggableState); +export const SettingsPanel: React.FC = ({ backgroundImage, setBackgroundImage, addWidget }) => { + const [editEnabled, setEditEnabled] = useAtom(editState); const [showGrid, setShowGrid] = useAtom(showGridState); - const [isOpen, setIsOpen] = useState(false); + const [isModalShowing, setModalShowing] = useState(false); - const handleSave = () => { - //settingsService.saveSetting('backgroundUrl', backgroundUrl) - settingsService.saveSetting('dragEnabled', dragEnabled) - settingsService.saveSetting('showGrid', showGrid) - - setIsOpen(false); + const createWidget = (type: WidgetTypes) => { + const newWidgetData = intitialWidgetData(type); + addWidget(newWidgetData) }; - const addWidget = (type: string) => { - //const newWidget = createBaseWidget(type, Math.floor(gridCols / 2), Math.floor(gridRows / 2), dragEnabled); - //saveSettings({ widgets: [...settings.widgets, newWidget] }); - }; return ( <> - - {isOpen && ( -
-
-

Settings

- - - -
- - -
- -
+ +

Settings

+ + + +
+ +
- )} +
); }; \ No newline at end of file diff --git a/src/renderer/components/Widget.tsx b/src/renderer/components/Widget.tsx index 4a0c464..5ab3557 100644 --- a/src/renderer/components/Widget.tsx +++ b/src/renderer/components/Widget.tsx @@ -1,14 +1,16 @@ -import React, { useRef, useState, useEffect } from 'react'; +import React, { useRef, useState, useEffect, ReactElement } from 'react'; import { WidgetData } from '../types'; import { CogwheelIcon } from './CogwheelIcon'; -import { useAtom, useAtomValue } from 'jotai'; -import { draggableState } from '../state/draggable.state'; +import { useAtomValue } from 'jotai'; +import { editState } from '../state/edit.state'; +import { CloseButton } from './Closebutton'; +import { ModalOverlay } from './ModalOverlay'; interface WidgetProps { widget: WidgetData; gridSize: { width: number; height: number }; children: React.ReactNode; - settingsContent?: React.ReactNode; + settingsContent: ReactElement; updateWidget: (id: string, data: Partial) => void } @@ -17,19 +19,21 @@ export const Widget: React.FC = ({ widget, gridSize, children, sett const modalRef = useRef(null); const [isDragging, setIsDragging] = useState(false); 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 [heightInput, setHeightInput] = useState(widget.gridHeight); - const isDraggable = useAtomValue(draggableState) + const isDraggable = useAtomValue(editState) const snapToGrid = (pos: number, grid: number) => Math.round(pos / grid); const handleMouseDown = (e: React.MouseEvent) => { - if(!isDraggable || isSettingsOpen){ + if (!isDraggable || isSettingsOpen) { return } - if (widgetRef.current && e.target === widgetRef.current) { + e.stopPropagation() + + if (!!widgetRef.current) { setIsDragging(true); const rect = widgetRef.current.getBoundingClientRect(); setDragOffset({ @@ -62,22 +66,16 @@ export const Widget: React.FC = ({ widget, gridSize, children, sett const handleClickOutside = (e: MouseEvent) => { 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 newHeight = Math.max(1, heightInput); updateWidget(widget.id, { gridWidth: newWidth, gridHeight: newHeight }); - setWidthInput(newWidth); - setHeightInput(newHeight); - setIsSettingsOpen(false); - }; + }, [heightInput, widthInput]) useEffect(() => { if (isDragging) { @@ -110,55 +108,70 @@ export const Widget: React.FC = ({ widget, gridSize, children, sett } }, [widget.gridX, widget.gridY, widget.gridWidth, widget.gridHeight, gridSize, isDragging]); - const genericSettingsContent = ( - <> - - - - - ); - return ( +
- - + + {settingsContent} + + + {isDraggable && <> + undefined /*removeWidget(widget.id)*/} /> + + } {children} - {isSettingsOpen && ( -
-
- - {settingsContent} - {genericSettingsContent} -
-
- )}
); -}; \ No newline at end of file +}; + + +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} + + + +} \ No newline at end of file diff --git a/src/renderer/components/WidgetFactory.tsx b/src/renderer/components/WidgetFactory.tsx index d813bb8..9c69a19 100644 --- a/src/renderer/components/WidgetFactory.tsx +++ b/src/renderer/components/WidgetFactory.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { WidgetData, SearchWidgetData, ClockWidgetData, WidgetExport, WidgetTypes } from '../types'; +import { WidgetData, WidgetExport, WidgetTypes } from '../types'; import { Widget } from './Widget'; import { SearchWidget } from './SearchWidget'; import { ClockWidget } from './ClockWidget'; diff --git a/src/renderer/defaults.ts b/src/renderer/defaults.ts index 88ef549..0f511ce 100644 --- a/src/renderer/defaults.ts +++ b/src/renderer/defaults.ts @@ -1,31 +1,35 @@ -import { GlobalSettings, SearchWidgetData, ClockWidgetData } from './types'; +import { GlobalSettings, SearchWidgetData, ClockWidgetData, WidgetTypes, WidgetData } from './types'; export const DEFAULT_SETTINGS: GlobalSettings = { backgroundUrl: '', - widgets: [ - { - id: 'clock-1', - type: 'clock', - gridX: 15, - gridY: 7, - gridWidth: 6, // Clock: 6x3 - gridHeight: 3, - dragEnabled: true, - 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, - dragEnabled: true, - engine: 'Google' - } as SearchWidgetData - ], - dragEnabled: true, - showGrid: false -}; \ No newline at end of file + widgets: [], + editEnabled: true, + showGrid: true +}; + +export const intitialWidgetData = (type: WidgetTypes): WidgetData => { + switch (type) { + case 'clock': + return { + gridHeight: 2, + 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' + } as SearchWidgetData + } +} \ No newline at end of file diff --git a/src/renderer/state/draggable.state.ts b/src/renderer/state/draggable.state.ts deleted file mode 100644 index be373d2..0000000 --- a/src/renderer/state/draggable.state.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { atom } from "jotai"; - -export const draggableState = atom(false) \ No newline at end of file diff --git a/src/renderer/state/edit.state.ts b/src/renderer/state/edit.state.ts new file mode 100644 index 0000000..fb523db --- /dev/null +++ b/src/renderer/state/edit.state.ts @@ -0,0 +1,4 @@ +import { atom } from "jotai"; +import { DEFAULT_SETTINGS } from "../defaults"; + +export const editState = atom(DEFAULT_SETTINGS.editEnabled) \ No newline at end of file diff --git a/src/renderer/state/modalOverlay.state.ts b/src/renderer/state/modalOverlay.state.ts new file mode 100644 index 0000000..f8a5152 --- /dev/null +++ b/src/renderer/state/modalOverlay.state.ts @@ -0,0 +1,5 @@ +import { atom } from "jotai"; +import { ReactNode } from "react"; + +export const modalVisible = atom(false) +export const modalContent = atom(undefined) \ No newline at end of file diff --git a/src/renderer/state/showGrid.state.ts b/src/renderer/state/showGrid.state.ts index 53bbf29..6bf10ac 100644 --- a/src/renderer/state/showGrid.state.ts +++ b/src/renderer/state/showGrid.state.ts @@ -1,3 +1,4 @@ import { atom } from "jotai"; +import { DEFAULT_SETTINGS } from "../defaults"; -export const showGridState = atom(false) \ No newline at end of file +export const showGridState = atom(DEFAULT_SETTINGS.showGrid) \ No newline at end of file diff --git a/src/renderer/types.ts b/src/renderer/types.ts index 38da01e..96e0f1b 100644 --- a/src/renderer/types.ts +++ b/src/renderer/types.ts @@ -25,7 +25,7 @@ export type WidgetTypes = (SearchWidgetData | ClockWidgetData)['type'] export interface GlobalSettings { backgroundUrl: string; widgets: WidgetData[]; - dragEnabled: boolean; + editEnabled: boolean; showGrid: boolean; }