diff --git a/desktop/src/widget-main.tsx b/desktop/src/widget-main.tsx index 268c9ec..87db751 100644 --- a/desktop/src/widget-main.tsx +++ b/desktop/src/widget-main.tsx @@ -1,5 +1,5 @@ import './forceServerApi'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import ReactDOM from 'react-dom/client'; import { getCurrentWindow } from '@tauri-apps/api/window'; import { LogicalSize } from '@tauri-apps/api/dpi'; @@ -8,6 +8,7 @@ import { ensureDesktopApiBase } from './ensureDesktopApiBase'; import FloatingWidgetWindow from '@frontend/components/floatingWidgets/FloatingWidgetWindow'; import { defaultGridFr, + NATIVE_WIDGET_PICKER_WINDOW, type FloatingWidgetInstance, } from '@frontend/types/floatingWidget'; import type { Theme } from '@frontend/types'; @@ -134,6 +135,27 @@ const WidgetApp: React.FC = () => { void getCurrentWindow().close(); }, []); + const sizeBeforePickerRef = useRef<{ width: number; height: number } | null>(null); + + const handlePickerOpenChange = useCallback(async (open: boolean) => { + const win = getCurrentWindow(); + if (open) { + const size = await win.innerSize(); + sizeBeforePickerRef.current = { width: size.width, height: size.height }; + const maxW = Math.max(480, window.screen.availWidth - 48); + const maxH = Math.max(520, window.screen.availHeight - 48); + const w = Math.min(NATIVE_WIDGET_PICKER_WINDOW.width, maxW); + const h = Math.min(NATIVE_WIDGET_PICKER_WINDOW.height, maxH); + await win.setSize(new LogicalSize(w, h)); + await win.center(); + return; + } + const prev = sizeBeforePickerRef.current; + if (!prev) return; + sizeBeforePickerRef.current = null; + await win.setSize(new LogicalSize(prev.width, prev.height)); + }, []); + if (!instance) { return

위젯 정보를 불러올 수 없습니다.

; } @@ -161,6 +183,7 @@ const WidgetApp: React.FC = () => { nativeShell onClose={handleClose} onFocus={() => {}} + onPickerOpenChange={open => { void handlePickerOpenChange(open); }} onUpdateSlots={slots => setInstance(prev => (prev ? { ...prev, slots } : prev))} onUpdateSize={(width, height) => { setInstance(prev => (prev ? { ...prev, width, height } : prev)); diff --git a/frontend/src/components/floatingWidgets/FloatingWidgetWindow.tsx b/frontend/src/components/floatingWidgets/FloatingWidgetWindow.tsx index 6c16abd..65b027b 100644 --- a/frontend/src/components/floatingWidgets/FloatingWidgetWindow.tsx +++ b/frontend/src/components/floatingWidgets/FloatingWidgetWindow.tsx @@ -24,6 +24,8 @@ interface Props { onUpdateGridFr: (rowFr: number[], colFr: number[]) => void; /** Tauri OS 창 — 네이티브 타이틀·리사이즈 사용, 내부 플로팅 크롬 숨김 */ nativeShell?: boolean; + /** 위젯 선택 팝업 열림/닫힘 (네이티브 창 크기 조절용) */ + onPickerOpenChange?: (open: boolean) => void; } const FloatingWidgetWindow: React.FC = ({ @@ -35,8 +37,19 @@ const FloatingWidgetWindow: React.FC = ({ onUpdateSize, onUpdateGridFr, nativeShell = false, + onPickerOpenChange, }) => { const [pickSlotId, setPickSlotId] = useState(null); + + const openPicker = useCallback((slotId: string) => { + setPickSlotId(slotId); + onPickerOpenChange?.(true); + }, [onPickerOpenChange]); + + const closePicker = useCallback(() => { + setPickSlotId(null); + onPickerOpenChange?.(false); + }, [onPickerOpenChange]); const bodyWrapRef = useRef(null); const bodyRef = useRef(null); const [bodySize, setBodySize] = useState({ width: 0, height: 0 }); @@ -130,8 +143,8 @@ const FloatingWidgetWindow: React.FC = ({ const applyWidgetType = useCallback((widgetType: string) => { if (!pickSlotId) return; updateSlot(pickSlotId, s => ({ ...s, widgetType, config: {} })); - setPickSlotId(null); - }, [pickSlotId, updateSlot]); + closePicker(); + }, [pickSlotId, updateSlot, closePicker]); const clearWidget = useCallback((slotId: string) => { updateSlot(slotId, s => ({ ...s, widgetType: null, config: {} })); @@ -233,7 +246,7 @@ const FloatingWidgetWindow: React.FC = ({ setPickSlotId(id)} + onPickWidget={openPicker} onClearWidget={id => clearWidget(id)} /> @@ -291,7 +304,8 @@ const FloatingWidgetWindow: React.FC = ({ setPickSlotId(null)} + nativeCompact={nativeShell} + onClose={closePicker} onSelect={applyWidgetType} /> diff --git a/frontend/src/components/widgetDashboard/WidgetPickerModal.tsx b/frontend/src/components/widgetDashboard/WidgetPickerModal.tsx index 28c6c1c..3a91294 100644 --- a/frontend/src/components/widgetDashboard/WidgetPickerModal.tsx +++ b/frontend/src/components/widgetDashboard/WidgetPickerModal.tsx @@ -10,6 +10,8 @@ interface Props { category: WidgetPickerCategory; onClose: () => void; onSelect: (widgetType: string) => void; + /** Tauri 네이티브 위젯 창 — 작은 viewport에 맞춘 전체 화면형 팝업 */ + nativeCompact?: boolean; } const WidgetPickerModal: React.FC = ({ @@ -17,6 +19,7 @@ const WidgetPickerModal: React.FC = ({ category, onClose, onSelect, + nativeCompact = false, }) => { if (!open) return null; @@ -37,11 +40,12 @@ const WidgetPickerModal: React.FC = ({ onClose={onClose} title={title} titleKo="Widget" - width={620} + width={720} maxWidth="96vw" backdrop closeOnBackdrop zIndex={13200} + shellClassName={nativeCompact ? 'app-popup-shell--widget-picker-native' : 'app-popup-shell--widget-picker'} bodyClassName="wd-picker-popup-body" >

{intro}

diff --git a/frontend/src/styles/widgetDashboard.css b/frontend/src/styles/widgetDashboard.css index 51601ef..5f46a80 100644 --- a/frontend/src/styles/widgetDashboard.css +++ b/frontend/src/styles/widgetDashboard.css @@ -1231,6 +1231,29 @@ line-height: 1.45; } +/* 위젯 선택 팝업 — 웹·대시보드 (서버와 동일한 넓은 목록) */ +.app-popup-shell--widget-picker { + width: min(720px, 96vw) !important; + max-height: min(860px, 92vh); +} + +.app-popup-shell--widget-picker .wd-picker-popup-body { + max-height: none; +} + +/* Tauri 네이티브 위젯 창 — OS 창 확대 후 팝업이 거의 전체를 채움 */ +html.fw-native-widget .app-popup-shell--widget-picker-native { + width: calc(100vw - 12px) !important; + max-width: calc(100vw - 12px) !important; + max-height: calc(100vh - 12px); + min-height: calc(100vh - 16px); +} + +html.fw-native-widget .app-popup-shell--widget-picker-native .wd-picker-popup-body { + flex: 1; + min-height: 0; +} + .bps-page--wd-no-left .bps-side-wrap--left, .bps-page--wd-no-left .bps-side-wrap--left + .bps-splitter { display: none !important; diff --git a/frontend/src/types/floatingWidget.ts b/frontend/src/types/floatingWidget.ts index fe13f99..34d877a 100644 --- a/frontend/src/types/floatingWidget.ts +++ b/frontend/src/types/floatingWidget.ts @@ -25,6 +25,9 @@ export const FLOATING_WIDGET_CELL_MIN_W = 140; export const FLOATING_WIDGET_CELL_MIN_H = 100; export const FLOATING_WIDGET_GRID_GAP = 1; +/** Tauri 네이티브 위젯 창 — 위젯 선택 팝업 표시용 OS 창 크기 */ +export const NATIVE_WIDGET_PICKER_WINDOW = { width: 740, height: 860 } as const; + export interface FloatingWidgetInstance { id: string; rows: number;