안드로이드 이이콘 변경
This commit is contained in:
@@ -609,7 +609,7 @@ const DesktopTab: React.FC<{ canBuildDesktop: boolean }> = ({ canBuildDesktop })
|
|||||||
};
|
};
|
||||||
|
|
||||||
const AppDownloadModal: React.FC<Props> = ({ onClose, canBuildDesktop = false }) => {
|
const AppDownloadModal: React.FC<Props> = ({ onClose, canBuildDesktop = false }) => {
|
||||||
const [tab, setTab] = useState<TabId>('mobile');
|
const [tab, setTab] = useState<TabId>('desktop');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DraggableModalFrame
|
<DraggableModalFrame
|
||||||
@@ -619,6 +619,7 @@ const AppDownloadModal: React.FC<Props> = ({ onClose, canBuildDesktop = false })
|
|||||||
badge={tab === 'mobile' ? 'Mobile' : 'Desktop'}
|
badge={tab === 'mobile' ? 'Mobile' : 'Desktop'}
|
||||||
width={480}
|
width={480}
|
||||||
dialogClassName="sp-modal app-download-modal"
|
dialogClassName="sp-modal app-download-modal"
|
||||||
|
recenterOnResize
|
||||||
>
|
>
|
||||||
<div className="app-download-tabs" role="tablist" aria-label="다운로드 종류">
|
<div className="app-download-tabs" role="tablist" aria-label="다운로드 종류">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ export interface AppPopupProps {
|
|||||||
overlayClassName?: string;
|
overlayClassName?: string;
|
||||||
bodyClassName?: string;
|
bodyClassName?: string;
|
||||||
centered?: boolean;
|
centered?: boolean;
|
||||||
|
/** 콘텐츠 크기 변화·창 리사이즈 시 (드래그 전까지) 중앙 재정렬 */
|
||||||
|
recenterOnResize?: boolean;
|
||||||
zIndex?: number;
|
zIndex?: number;
|
||||||
closeOnBackdrop?: boolean;
|
closeOnBackdrop?: boolean;
|
||||||
backdrop?: boolean;
|
backdrop?: boolean;
|
||||||
@@ -48,6 +50,7 @@ export const AppPopup: React.FC<AppPopupProps> = ({
|
|||||||
overlayClassName,
|
overlayClassName,
|
||||||
bodyClassName = 'app-popup-body',
|
bodyClassName = 'app-popup-body',
|
||||||
centered = true,
|
centered = true,
|
||||||
|
recenterOnResize = false,
|
||||||
zIndex = 9999,
|
zIndex = 9999,
|
||||||
closeOnBackdrop = true,
|
closeOnBackdrop = true,
|
||||||
backdrop = false,
|
backdrop = false,
|
||||||
@@ -68,7 +71,7 @@ export const AppPopup: React.FC<AppPopupProps> = ({
|
|||||||
headerTouchStyle,
|
headerTouchStyle,
|
||||||
panelStyle,
|
panelStyle,
|
||||||
headerCursor,
|
headerCursor,
|
||||||
} = useDraggablePanel({ centerOnMount: centered, initialPosition });
|
} = useDraggablePanel({ centerOnMount: centered, recenterOnResize, initialPosition });
|
||||||
|
|
||||||
const overlayCls = [
|
const overlayCls = [
|
||||||
'app-popup-overlay',
|
'app-popup-overlay',
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ export interface DraggableModalFrameProps {
|
|||||||
zIndex?: number;
|
zIndex?: number;
|
||||||
width?: number | string;
|
width?: number | string;
|
||||||
closeOnBackdrop?: boolean;
|
closeOnBackdrop?: boolean;
|
||||||
|
/** 콘텐츠 크기 변화 시 (드래그 전까지) 중앙 재정렬 */
|
||||||
|
recenterOnResize?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DraggableModalFrame: React.FC<DraggableModalFrameProps> = ({
|
export const DraggableModalFrame: React.FC<DraggableModalFrameProps> = ({
|
||||||
@@ -30,6 +32,7 @@ export const DraggableModalFrame: React.FC<DraggableModalFrameProps> = ({
|
|||||||
zIndex = 1000,
|
zIndex = 1000,
|
||||||
width,
|
width,
|
||||||
closeOnBackdrop = true,
|
closeOnBackdrop = true,
|
||||||
|
recenterOnResize = false,
|
||||||
}) => (
|
}) => (
|
||||||
<AppPopup
|
<AppPopup
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
@@ -44,6 +47,7 @@ export const DraggableModalFrame: React.FC<DraggableModalFrameProps> = ({
|
|||||||
backdrop
|
backdrop
|
||||||
closeOnBackdrop={closeOnBackdrop}
|
closeOnBackdrop={closeOnBackdrop}
|
||||||
centered
|
centered
|
||||||
|
recenterOnResize={recenterOnResize}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</AppPopup>
|
</AppPopup>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const WindowsLogo: React.FC<{ size?: number; className?: string }> = ({
|
|||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Android 로봇 로고 — 공식 그린 (#3DDC84) */
|
/** Android 로봇 로고 — 머리·몸통·양팔·양다리 (공식 그린 #A4C639/#8BC34A 톤) */
|
||||||
export const AndroidLogo: React.FC<{ size?: number; className?: string }> = ({
|
export const AndroidLogo: React.FC<{ size?: number; className?: string }> = ({
|
||||||
size = 22,
|
size = 22,
|
||||||
className,
|
className,
|
||||||
@@ -45,11 +45,31 @@ export const AndroidLogo: React.FC<{ size?: number; className?: string }> = ({
|
|||||||
width={size}
|
width={size}
|
||||||
height={size}
|
height={size}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="#3ddc84"
|
fill="none"
|
||||||
className={className}
|
className={className}
|
||||||
aria-hidden
|
aria-hidden
|
||||||
>
|
>
|
||||||
<path d="M17.6 9.48l1.84-3.18a.38.38 0 0 0-.66-.38l-1.86 3.22a11.4 11.4 0 0 0-9.84 0L5.22 5.92a.38.38 0 1 0-.66.38L6.4 9.48A10.78 10.78 0 0 0 1 18h22a10.78 10.78 0 0 0-5.4-8.52zM7 15.25a1.25 1.25 0 1 1 1.25-1.25A1.25 1.25 0 0 1 7 15.25zm10 0A1.25 1.25 0 1 1 18.25 14 1.25 1.25 0 0 1 17 15.25z" />
|
{/* 안테나 */}
|
||||||
|
<g stroke="#9bcf4e" strokeWidth="0.95" strokeLinecap="round">
|
||||||
|
<line x1="8.7" y1="2.9" x2="9.9" y2="4.9" />
|
||||||
|
<line x1="15.3" y1="2.9" x2="14.1" y2="4.9" />
|
||||||
|
</g>
|
||||||
|
{/* 머리(돔) */}
|
||||||
|
<path
|
||||||
|
d="M6.4 9.2C6.4 6.05 8.9 3.7 12 3.7s5.6 2.35 5.6 5.5z"
|
||||||
|
fill="#9bcf4e"
|
||||||
|
/>
|
||||||
|
{/* 눈 */}
|
||||||
|
<circle cx="9.7" cy="6.6" r="0.72" fill="#ffffff" />
|
||||||
|
<circle cx="14.3" cy="6.6" r="0.72" fill="#ffffff" />
|
||||||
|
{/* 몸통 */}
|
||||||
|
<rect x="6.5" y="9.1" width="11" height="8.5" rx="1.3" fill="#9bcf4e" />
|
||||||
|
{/* 양팔 */}
|
||||||
|
<rect x="3.3" y="9.7" width="2.3" height="6.4" rx="1.15" fill="#9bcf4e" />
|
||||||
|
<rect x="18.4" y="9.7" width="2.3" height="6.4" rx="1.15" fill="#9bcf4e" />
|
||||||
|
{/* 양다리 */}
|
||||||
|
<rect x="8.3" y="16.7" width="2.3" height="4.4" rx="1.15" fill="#9bcf4e" />
|
||||||
|
<rect x="13.4" y="16.7" width="2.3" height="4.4" rx="1.15" fill="#9bcf4e" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ export interface DraggablePosition {
|
|||||||
export interface UseDraggablePanelOptions {
|
export interface UseDraggablePanelOptions {
|
||||||
/** 마운트 후 패널 크기 기준 화면 중앙 배치 */
|
/** 마운트 후 패널 크기 기준 화면 중앙 배치 */
|
||||||
centerOnMount?: boolean;
|
centerOnMount?: boolean;
|
||||||
|
/** 콘텐츠 크기 변화·창 리사이즈 시 (드래그 전까지) 중앙 재정렬 */
|
||||||
|
recenterOnResize?: boolean;
|
||||||
initialPosition?: DraggablePosition;
|
initialPosition?: DraggablePosition;
|
||||||
panelRef?: RefObject<HTMLElement | null>;
|
panelRef?: RefObject<HTMLElement | null>;
|
||||||
}
|
}
|
||||||
@@ -32,6 +34,7 @@ function estimateCenterPosition(
|
|||||||
export function useDraggablePanel(options: UseDraggablePanelOptions = {}) {
|
export function useDraggablePanel(options: UseDraggablePanelOptions = {}) {
|
||||||
const {
|
const {
|
||||||
centerOnMount = false,
|
centerOnMount = false,
|
||||||
|
recenterOnResize = false,
|
||||||
initialPosition = DEFAULT_POS,
|
initialPosition = DEFAULT_POS,
|
||||||
panelRef: externalRef,
|
panelRef: externalRef,
|
||||||
} = options;
|
} = options;
|
||||||
@@ -44,29 +47,42 @@ export function useDraggablePanel(options: UseDraggablePanelOptions = {}) {
|
|||||||
const [positionReady, setPositionReady] = useState(!centerOnMount);
|
const [positionReady, setPositionReady] = useState(!centerOnMount);
|
||||||
const [dragging, setDragging] = useState(false);
|
const [dragging, setDragging] = useState(false);
|
||||||
const dragOrigin = useRef({ mx: 0, my: 0, px: 0, py: 0 });
|
const dragOrigin = useRef({ mx: 0, my: 0, px: 0, py: 0 });
|
||||||
const centeredOnce = useRef(false);
|
const movedByUser = useRef(false);
|
||||||
const unbindDragRef = useRef<(() => void) | null>(null);
|
const unbindDragRef = useRef<(() => void) | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!centerOnMount || centeredOnce.current) return;
|
if (!centerOnMount) return;
|
||||||
const el = panelRef.current;
|
const el = panelRef.current;
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
|
|
||||||
const placeCenter = () => {
|
const placeCenter = () => {
|
||||||
|
if (movedByUser.current) return;
|
||||||
const w = el.offsetWidth || DEFAULT_CENTER_ESTIMATE.width;
|
const w = el.offsetWidth || DEFAULT_CENTER_ESTIMATE.width;
|
||||||
const h = el.offsetHeight || DEFAULT_CENTER_ESTIMATE.height;
|
const h = el.offsetHeight || DEFAULT_CENTER_ESTIMATE.height;
|
||||||
setPos({
|
setPos({
|
||||||
x: Math.max(8, (window.innerWidth - w) / 2),
|
x: Math.max(8, (window.innerWidth - w) / 2),
|
||||||
y: Math.max(8, (window.innerHeight - h) / 2),
|
y: Math.max(8, (window.innerHeight - h) / 2),
|
||||||
});
|
});
|
||||||
centeredOnce.current = true;
|
|
||||||
setPositionReady(true);
|
setPositionReady(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
placeCenter();
|
placeCenter();
|
||||||
const id = requestAnimationFrame(placeCenter);
|
const id = requestAnimationFrame(placeCenter);
|
||||||
return () => cancelAnimationFrame(id);
|
|
||||||
}, [centerOnMount, panelRef]);
|
if (!recenterOnResize) {
|
||||||
|
return () => cancelAnimationFrame(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ro = new ResizeObserver(() => placeCenter());
|
||||||
|
ro.observe(el);
|
||||||
|
const onResize = () => placeCenter();
|
||||||
|
window.addEventListener('resize', onResize);
|
||||||
|
return () => {
|
||||||
|
cancelAnimationFrame(id);
|
||||||
|
ro.disconnect();
|
||||||
|
window.removeEventListener('resize', onResize);
|
||||||
|
};
|
||||||
|
}, [centerOnMount, recenterOnResize, panelRef]);
|
||||||
|
|
||||||
const endDrag = useCallback(() => {
|
const endDrag = useCallback(() => {
|
||||||
unbindDragRef.current?.();
|
unbindDragRef.current?.();
|
||||||
@@ -86,6 +102,7 @@ export function useDraggablePanel(options: UseDraggablePanelOptions = {}) {
|
|||||||
/* ignore */
|
/* ignore */
|
||||||
}
|
}
|
||||||
const { x, y } = clientXYFromReact(e);
|
const { x, y } = clientXYFromReact(e);
|
||||||
|
movedByUser.current = true;
|
||||||
setDragging(true);
|
setDragging(true);
|
||||||
dragOrigin.current = { mx: x, my: y, px: pos.x, py: pos.y };
|
dragOrigin.current = { mx: x, my: y, px: pos.x, py: pos.y };
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user