fix(desktop): 네이티브 위젯 picker를 창 전체 뷰로 전환
portal/overlay 대신 widget-main에서 picker 전용 화면으로 전환해 macOS·Windows WebView 모두에서 위젯 선택 팝업이 보이도록 합니다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -24,7 +24,9 @@ interface Props {
|
||||
onUpdateGridFr: (rowFr: number[], colFr: number[]) => void;
|
||||
/** Tauri OS 창 — 네이티브 타이틀·리사이즈 사용, 내부 플로팅 크롬 숨김 */
|
||||
nativeShell?: boolean;
|
||||
/** 위젯 선택 팝업 열림/닫힘 (네이티브 창 크기 조절 — 완료 후 picker 표시) */
|
||||
/** 네이티브 위젯 창 — picker는 부모(widget-main)에서 전환 렌더 */
|
||||
onNativePickRequest?: (slotId: string) => void;
|
||||
/** 위젯 선택 팝업 열림/닫힘 (웹 플로팅 레이어용) */
|
||||
onPickerOpenChange?: (open: boolean) => void | Promise<void>;
|
||||
}
|
||||
|
||||
@@ -37,27 +39,22 @@ const FloatingWidgetWindow: React.FC<Props> = ({
|
||||
onUpdateSize,
|
||||
onUpdateGridFr,
|
||||
nativeShell = false,
|
||||
onNativePickRequest,
|
||||
onPickerOpenChange,
|
||||
}) => {
|
||||
const [pickSlotId, setPickSlotId] = useState<string | null>(null);
|
||||
const nativePickerExternal = nativeShell && Boolean(onNativePickRequest);
|
||||
|
||||
const openPicker = useCallback((slotId: string) => {
|
||||
if (nativePickerExternal) {
|
||||
onNativePickRequest!(slotId);
|
||||
return;
|
||||
}
|
||||
setPickSlotId(slotId);
|
||||
if (nativeShell) {
|
||||
document.documentElement.classList.add('fw-native-picker-open');
|
||||
}
|
||||
if (nativeShell && onPickerOpenChange) {
|
||||
void Promise.resolve(onPickerOpenChange(true)).catch(err => {
|
||||
console.warn('[FloatingWidgetWindow] picker window resize failed', err);
|
||||
});
|
||||
}
|
||||
}, [nativeShell, onPickerOpenChange]);
|
||||
}, [nativePickerExternal, onNativePickRequest]);
|
||||
|
||||
const closePicker = useCallback(async () => {
|
||||
setPickSlotId(null);
|
||||
if (nativeShell) {
|
||||
document.documentElement.classList.remove('fw-native-picker-open');
|
||||
}
|
||||
if (nativeShell && onPickerOpenChange) {
|
||||
try {
|
||||
await onPickerOpenChange(false);
|
||||
@@ -66,9 +63,6 @@ const FloatingWidgetWindow: React.FC<Props> = ({
|
||||
}
|
||||
}
|
||||
}, [nativeShell, onPickerOpenChange]);
|
||||
useEffect(() => () => {
|
||||
document.documentElement.classList.remove('fw-native-picker-open');
|
||||
}, []);
|
||||
|
||||
const bodyWrapRef = useRef<HTMLDivElement>(null);
|
||||
const bodyRef = useRef<HTMLDivElement>(null);
|
||||
@@ -265,7 +259,7 @@ const FloatingWidgetWindow: React.FC<Props> = ({
|
||||
<WidgetSlotView
|
||||
slot={slot}
|
||||
zoneId="center"
|
||||
onPickWidget={id => { void openPicker(id); }}
|
||||
onPickWidget={id => { openPicker(id); }}
|
||||
onClearWidget={id => clearWidget(id)}
|
||||
/>
|
||||
</div>
|
||||
@@ -322,7 +316,7 @@ const FloatingWidgetWindow: React.FC<Props> = ({
|
||||
</>
|
||||
);
|
||||
|
||||
const pickerModal = pickSlotId != null ? (
|
||||
const pickerModal = !nativePickerExternal && pickSlotId != null ? (
|
||||
<WidgetPickerModal
|
||||
open
|
||||
category="all"
|
||||
@@ -333,12 +327,7 @@ const FloatingWidgetWindow: React.FC<Props> = ({
|
||||
) : null;
|
||||
|
||||
if (nativeShell) {
|
||||
return (
|
||||
<>
|
||||
{content}
|
||||
{pickerModal && createPortal(pickerModal, document.body)}
|
||||
</>
|
||||
);
|
||||
return content;
|
||||
}
|
||||
|
||||
return createPortal(
|
||||
|
||||
@@ -12,6 +12,8 @@ interface Props {
|
||||
onSelect: (widgetType: string) => void;
|
||||
/** Tauri 네이티브 위젯 창 — OS 창 전체를 쓰는 고정형 picker */
|
||||
nativeCompact?: boolean;
|
||||
/** 네이티브 위젯 창 루트를 picker 전용으로 쓸 때 (overlay 없음) */
|
||||
nativeFullRoot?: boolean;
|
||||
}
|
||||
|
||||
function pickerMeta(category: WidgetPickerCategory) {
|
||||
@@ -69,36 +71,45 @@ const NativeWidgetPicker: React.FC<Props> = ({
|
||||
category,
|
||||
onClose,
|
||||
onSelect,
|
||||
nativeFullRoot = false,
|
||||
}) => {
|
||||
if (!open) return null;
|
||||
const { title } = pickerMeta(category);
|
||||
|
||||
const shell = (
|
||||
<div
|
||||
className="wd-picker-native-shell"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="wd-picker-native-title"
|
||||
>
|
||||
<div className="wd-picker-native-header">
|
||||
<h2 id="wd-picker-native-title" className="wd-picker-native-title">
|
||||
{title}
|
||||
<span className="wd-picker-native-title-ko"> (Widget)</span>
|
||||
</h2>
|
||||
<button
|
||||
type="button"
|
||||
className="wd-picker-native-close"
|
||||
onClick={onClose}
|
||||
aria-label="닫기"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<div className="wd-picker-native-body wd-picker-popup-body">
|
||||
<WidgetPickerContent category={category} onSelect={onSelect} onClose={onClose} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (nativeFullRoot) {
|
||||
return <div className="wd-picker-native-root">{shell}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="wd-picker-native-overlay" role="presentation">
|
||||
<div
|
||||
className="wd-picker-native-shell"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="wd-picker-native-title"
|
||||
>
|
||||
<div className="wd-picker-native-header">
|
||||
<h2 id="wd-picker-native-title" className="wd-picker-native-title">
|
||||
{title}
|
||||
<span className="wd-picker-native-title-ko"> (Widget)</span>
|
||||
</h2>
|
||||
<button
|
||||
type="button"
|
||||
className="wd-picker-native-close"
|
||||
onClick={onClose}
|
||||
aria-label="닫기"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<div className="wd-picker-native-body wd-picker-popup-body">
|
||||
<WidgetPickerContent category={category} onSelect={onSelect} onClose={onClose} />
|
||||
</div>
|
||||
</div>
|
||||
{shell}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -109,6 +120,7 @@ const WidgetPickerModal: React.FC<Props> = ({
|
||||
onClose,
|
||||
onSelect,
|
||||
nativeCompact = false,
|
||||
nativeFullRoot = false,
|
||||
}) => {
|
||||
if (!open) return null;
|
||||
|
||||
@@ -119,6 +131,7 @@ const WidgetPickerModal: React.FC<Props> = ({
|
||||
category={category}
|
||||
onClose={onClose}
|
||||
onSelect={onSelect}
|
||||
nativeFullRoot={nativeFullRoot}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user