feat(486 B07 CAD 화면): 제목줄 · 램프 · 하단 도면 칸 지움 · ↶ ↷ = 리본 탭 줄 오른쪽 · 손가락 단추 = 자판 없는 기기만 · 탭 줄 빈 자리 변수
- 제목줄(Aislo CAD · 부제 · 「현재 도면」 램프) 지움 · --cad-title-height 0 · 💾 · JSON 내보내기 = DOM 두고 숨김(QUICK_ACCESS_HIDDEN) - 빠른 실행 = 리본 탭 줄 빈 자리 뒤 · 접기 단추 앞(줄 높이 26 그대로 · M02 hostSave 면 안 그림) - 빈 자리([출력] 오른쪽 ~ ↶ 왼쪽) → .cad-host 에 --cad-tab-slot-left/right(iframe 끝 기준 px) — 부모 탭 줄 겹침 자리 - ☰ 비키기 패딩 = 리본 탭 줄로 · 손가락 단추 = (hover: none) and (pointer: coarse) 만 - 하단 도면 이름 · ‹ › · 확정 칸(QuantityPanel) · 이동 요청 지움 - 시험 test_cad_touch(미디어 조건) · test_cad_host_tabs(머리줄 · 빈 자리 변수) 고쳐 씀 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
@import "../../../ui_template/ui_template_theme.css";
|
||||
|
||||
:root {
|
||||
--cad-title-height: 42px;
|
||||
/* 제목줄 없음(요구 280-6) — 칸들이 이 변수로 자리 잡아 0 으로 둠 */
|
||||
--cad-title-height: 0px;
|
||||
/* 116px 에서는 리본 줄의 내용(85px)이 보이는 높이(81px)를 넘어 세로 스크롤바가
|
||||
생겼다(2026-09-04 실측) — 모자란 4px + 여유 2px 를 더한다. */
|
||||
--cad-ribbon-height: 122px;
|
||||
@@ -55,11 +56,7 @@ button {
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
}
|
||||
.cad-host-save {
|
||||
--cad-title-height: 0px;
|
||||
}
|
||||
.cad-bare {
|
||||
--cad-title-height: 0px;
|
||||
--cad-status-height: 0px;
|
||||
}
|
||||
.controls {
|
||||
@@ -85,64 +82,6 @@ body > canvas[data-id="canvas"] {
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
.cad-titlebar {
|
||||
position: fixed;
|
||||
inset: 0 0 auto 0;
|
||||
height: var(--cad-title-height);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
padding: 0 12px;
|
||||
background: var(--cad-chrome);
|
||||
border-bottom: 1px solid var(--cad-line);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
.cad-brand {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 9px;
|
||||
min-width: 220px;
|
||||
}
|
||||
.cad-brand strong {
|
||||
color: var(--color-text);
|
||||
font-size: 14px;
|
||||
}
|
||||
.cad-brand span,
|
||||
.cad-file-state {
|
||||
color: var(--cad-text-dim);
|
||||
font-size: 11px;
|
||||
}
|
||||
.cad-file-state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
}
|
||||
.cad-file-state__dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: var(--color-success);
|
||||
box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-success) 25%, transparent);
|
||||
}
|
||||
.cad-title-actions {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-left: auto;
|
||||
}
|
||||
.cad-title-actions button {
|
||||
height: 28px;
|
||||
min-width: 32px;
|
||||
padding: 0 9px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
background: transparent;
|
||||
font-size: 12px;
|
||||
}
|
||||
.cad-title-actions button:hover {
|
||||
border-color: var(--cad-line);
|
||||
background: var(--cad-hover);
|
||||
}
|
||||
|
||||
.cad-ribbon {
|
||||
position: fixed;
|
||||
top: var(--cad-title-height);
|
||||
@@ -168,8 +107,11 @@ body > canvas[data-id="canvas"] {
|
||||
padding: 0 8px;
|
||||
border-bottom: 1px solid var(--cad-line);
|
||||
}
|
||||
.cad-ribbon-tabs__slot {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.cad-ribbon-tabs__collapse {
|
||||
margin-left: auto;
|
||||
width: 24px;
|
||||
height: 20px;
|
||||
align-self: center;
|
||||
@@ -275,14 +217,15 @@ body > canvas[data-id="canvas"] {
|
||||
.cad-quick-access {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
margin-left: 14px;
|
||||
align-self: center;
|
||||
}
|
||||
.cad-quick-access button {
|
||||
width: 26px;
|
||||
height: 24px;
|
||||
.cad-ribbon-tabs .cad-quick-access button {
|
||||
width: 24px;
|
||||
height: 20px;
|
||||
padding: 0;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
background: transparent;
|
||||
border-radius: 4px;
|
||||
line-height: 1;
|
||||
font-size: 13px;
|
||||
}
|
||||
.cad-quick-access button:hover {
|
||||
@@ -718,153 +661,13 @@ body.cad-file-dragging::after {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* 수량 산출표 패널 (CAD 화면 하단 중심, 접이식) — 첨부 양식
|
||||
* -------------------------------------------------------------------------- */
|
||||
.cad-qtable {
|
||||
position: fixed;
|
||||
z-index: 3;
|
||||
/* 명령행 위로 띄운다 — 겹치면 명령 입력칸을 클릭으로 누를 수 없다(2026-08-31).
|
||||
명령행을 접으면 --cad-command-height가 0이라 예전처럼 상태막대 위에 붙는다. */
|
||||
bottom: calc(var(--cad-status-height) + var(--cad-command-height) + 8px);
|
||||
left: calc(var(--cad-panel-width) + (100vw - var(--cad-panel-width)) / 2);
|
||||
transform: translateX(-50%);
|
||||
/* 표(가장 넓은 자식) 기준 폭 고정 → 접었다 펴도 폭이 변하지 않는다 */
|
||||
width: max-content;
|
||||
max-width: min(1040px, calc(100vw - var(--cad-panel-width) - 24px));
|
||||
border: 1px solid var(--cad-line);
|
||||
border-radius: 6px;
|
||||
background: var(--cad-chrome-raised);
|
||||
box-shadow: var(--shadow-lg);
|
||||
color: var(--cad-text);
|
||||
}
|
||||
|
||||
.cad-qtable__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 5px 8px;
|
||||
}
|
||||
.cad-qtable__title {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
.cad-qtable__title strong {
|
||||
color: var(--color-text);
|
||||
font-size: 14px;
|
||||
}
|
||||
.cad-qtable__title span {
|
||||
overflow: hidden;
|
||||
color: var(--cad-text-dim);
|
||||
font-size: 11px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.cad-qtable__status {
|
||||
padding: 1px 7px;
|
||||
border-radius: 999px;
|
||||
font-size: 10px;
|
||||
font-style: normal;
|
||||
}
|
||||
.cad-qtable__status[data-confirmed="true"] {
|
||||
background: color-mix(in srgb, var(--color-success) 22%, transparent);
|
||||
color: var(--color-success);
|
||||
}
|
||||
.cad-qtable__status[data-confirmed="false"] {
|
||||
background: color-mix(in srgb, var(--color-warning) 22%, transparent);
|
||||
color: var(--color-warning);
|
||||
}
|
||||
.cad-qtable__nav,
|
||||
.cad-qtable__collapse {
|
||||
width: 26px;
|
||||
height: 24px;
|
||||
border: 1px solid var(--cad-line);
|
||||
border-radius: 4px;
|
||||
background: var(--cad-chrome-sunken);
|
||||
color: var(--cad-text);
|
||||
font-size: 13px;
|
||||
}
|
||||
.cad-qtable__nav:hover:not(:disabled),
|
||||
.cad-qtable__collapse:hover {
|
||||
background: var(--cad-hover);
|
||||
}
|
||||
.cad-qtable__nav:disabled {
|
||||
opacity: 0.35;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.cad-qtable__body {
|
||||
padding: 0 8px 8px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
/* 접힘: 표는 DOM에 남겨 폭은 유지하되 세로로만 감춘다 */
|
||||
.cad-qtable[data-collapsed="true"] .cad-qtable__body {
|
||||
max-height: 0;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
}
|
||||
.cad-qtable table {
|
||||
margin: 0 auto;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.cad-qtable th,
|
||||
.cad-qtable td {
|
||||
padding: 0;
|
||||
border: 1px solid var(--cad-line);
|
||||
font-size: 11px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.cad-qtable__label {
|
||||
padding: 2px 9px;
|
||||
color: var(--cad-text-dim);
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
background: var(--cad-chrome);
|
||||
}
|
||||
.cad-qtable__label--vertical {
|
||||
width: 18px;
|
||||
writing-mode: vertical-rl;
|
||||
text-orientation: upright;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.cad-qtable__value input {
|
||||
width: 72px;
|
||||
padding: 3px 4px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--cad-text);
|
||||
font: inherit;
|
||||
text-align: center;
|
||||
}
|
||||
.cad-qtable__value input:focus {
|
||||
outline: 2px solid var(--color-focus-ring);
|
||||
outline-offset: -2px;
|
||||
background: var(--cad-hover);
|
||||
}
|
||||
.cad-qtable__value--derived input {
|
||||
color: var(--cad-text-muted);
|
||||
background: var(--cad-chrome-sunken);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
:root {
|
||||
--cad-panel-width: 200px;
|
||||
}
|
||||
.cad-file-state,
|
||||
.cad-statusbar__hint {
|
||||
display: none;
|
||||
}
|
||||
.cad-brand {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* 특성 팔레트 — 값을 바로 고치는 입력들 */
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import './App.css';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import { FramePlaceholderPanel } from './components/FramePlaceholderPanel.tsx';
|
||||
import { QuantityPanel } from './components/QuantityPanel.tsx';
|
||||
import { Toolbar } from './components/Toolbar.tsx';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="cad-app">
|
||||
<Toolbar />
|
||||
<QuantityPanel />
|
||||
<FramePlaceholderPanel />
|
||||
<ToastContainer position="bottom-right" theme="light" />
|
||||
</div>
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import { type FC, useCallback, useEffect, useState } from 'react';
|
||||
import { HtmlEvent } from '../App.types';
|
||||
import { requestDrawingNavigation } from '../integration/aislo-drawing-bridge';
|
||||
import { getDesignMeta } from '../state';
|
||||
|
||||
/**
|
||||
* 도면 내비게이션 바 (CAD 화면 하단 중심).
|
||||
* 수량 산출표는 HTML 테이블 대신 도면 자체의 CAD 테이블 레이어(b08-cross-table)로
|
||||
* 렌더되므로, 여기서는 제목·측점정보·확정상태·이전/다음 이동만 담당한다.
|
||||
*/
|
||||
export const QuantityPanel: FC = () => {
|
||||
const [meta, setMeta] = useState(getDesignMeta());
|
||||
|
||||
const refresh = useCallback(() => setMeta(getDesignMeta()), []);
|
||||
useEffect(() => {
|
||||
window.addEventListener(HtmlEvent.UPDATE_STATE, refresh);
|
||||
return () => window.removeEventListener(HtmlEvent.UPDATE_STATE, refresh);
|
||||
}, [refresh]);
|
||||
|
||||
if (!meta) return null;
|
||||
|
||||
return (
|
||||
<section className="cad-qtable controls" data-collapsed="true">
|
||||
<header className="cad-qtable__header">
|
||||
<button
|
||||
type="button"
|
||||
className="cad-qtable__nav"
|
||||
disabled={!meta.hasPrev}
|
||||
title="이전 도면"
|
||||
onClick={() => requestDrawingNavigation('prev')}
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
<div className="cad-qtable__title">
|
||||
<strong>{meta.title}</strong>
|
||||
{meta.info && <span>{meta.info}</span>}
|
||||
<em
|
||||
className="cad-qtable__status"
|
||||
data-confirmed={meta.confirmed}
|
||||
title={meta.confirmed ? '설계 확정됨' : '미확정'}
|
||||
>
|
||||
{meta.confirmed ? '확정' : '미확정'}
|
||||
</em>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="cad-qtable__nav"
|
||||
disabled={!meta.hasNext}
|
||||
title="다음 도면"
|
||||
onClick={() => requestDrawingNavigation('next')}
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</header>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -1,26 +1,14 @@
|
||||
/** 제목표시줄 + 빠른 실행 도구막대 (AutoCAD 상단 막대) */
|
||||
import { type FC, useEffect, useState } from 'react';
|
||||
import { HtmlEvent } from '../App.types';
|
||||
/** 빠른 실행 단추(↶ ↷) — 리본 탭 줄 오른쪽(접기 단추 앞)에 그린다 · 숨김 목록은 DOM 만 둔다 */
|
||||
import type { FC } from 'react';
|
||||
import { getCommandById } from '../commands/registry';
|
||||
import { runCommand } from '../commands/run-command';
|
||||
import { QUICK_ACCESS_COMMANDS } from '../ribbon/ribbon.config';
|
||||
import { getHostTitle, isHostSave } from '../state';
|
||||
import { QUICK_ACCESS_COMMANDS, QUICK_ACCESS_HIDDEN } from '../ribbon/ribbon.config';
|
||||
import { isHostSave } from '../state';
|
||||
|
||||
export const QuickAccessBar: FC = () => {
|
||||
const [hostTitle, setTitle] = useState(getHostTitle());
|
||||
useEffect(() => {
|
||||
const refresh = () => setTitle(getHostTitle());
|
||||
window.addEventListener(HtmlEvent.UPDATE_STATE, refresh);
|
||||
return () => window.removeEventListener(HtmlEvent.UPDATE_STATE, refresh);
|
||||
}, []);
|
||||
// 부모 페이지가 저장·이름을 맡는 M02 에선 제목 줄을 숨겨 그림 칸을 넓힌다.
|
||||
// 부모 [저장]이 맡는 M02 는 우하단 ViewControls 에 ↶ ↷ 가 있어 안 그린다
|
||||
if (isHostSave()) return null;
|
||||
return (
|
||||
<header className="cad-titlebar controls">
|
||||
<div className="cad-brand">
|
||||
<strong>Aislo CAD</strong>
|
||||
<span>{hostTitle}</span>
|
||||
</div>
|
||||
<div className="cad-quick-access">
|
||||
{QUICK_ACCESS_COMMANDS.map((id) => {
|
||||
const command = getCommandById(id);
|
||||
@@ -29,6 +17,7 @@ export const QuickAccessBar: FC = () => {
|
||||
<button
|
||||
type="button"
|
||||
key={command.id}
|
||||
hidden={QUICK_ACCESS_HIDDEN.includes(id)}
|
||||
title={`${command.label} (${command.id})`}
|
||||
onClick={() => runCommand(command)}
|
||||
>
|
||||
@@ -37,10 +26,5 @@ export const QuickAccessBar: FC = () => {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="cad-file-state">
|
||||
<span className="cad-file-state__dot" />
|
||||
현재 도면
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* CAD 화면 골격 조립 — 제목표시줄 · 리본 · 좌측 팔레트 · 탐색막대 · 명령행 · 상태막대.
|
||||
* CAD 화면 골격 조립 — 리본(빠른 실행 포함) · 좌측 팔레트 · 탐색막대 · 명령행 · 상태막대.
|
||||
* 각 조각은 자기 파일에 있고, 여기서는 배치와 표시 상태만 다룬다.
|
||||
*/
|
||||
import { type FC, useEffect, useState } from 'react';
|
||||
@@ -8,7 +8,6 @@ import { InspectorPanel } from './InspectorPanel';
|
||||
import { QuickProperties } from './QuickProperties';
|
||||
import { BlockLibraryPanel } from './BlockLibraryPanel';
|
||||
import { LayersWidget, PropertiesWidget, TextStyleWidget } from './RibbonWidgets';
|
||||
import { QuickAccessBar } from './QuickAccessBar';
|
||||
import { Ribbon } from '../ribbon/Ribbon';
|
||||
import { StatusBar } from './StatusBar';
|
||||
import { useCadRefresh } from './use-cad-refresh';
|
||||
@@ -50,7 +49,6 @@ export const Toolbar: FC = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<QuickAccessBar />
|
||||
<Ribbon
|
||||
activeTool={activeTool}
|
||||
widgets={{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* 손가락 기기 명령 단추(기기대응 4-1 · `input-touch.ts`) — 자판 없이 Esc · Enter · 지움 · 되돌리기 · 다시 · 스냅.
|
||||
마우스만 있는 화면엔 안 보임. 그림 면 오른쪽 가운데에 세로로. */
|
||||
자판 없는 기기(손가락이 주 입력 · 마우스 스침 없음)만 보임 — 터치 노트북엔 안 보임(요구 280-6). 그림 면 오른쪽 가운데에 세로로. */
|
||||
.cad-touch-bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (any-pointer: coarse) {
|
||||
@media (hover: none) and (pointer: coarse) {
|
||||
.cad-touch-bar {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
|
||||
@@ -41,7 +41,6 @@ export const AISLO_DRAWING_ERROR_MESSAGE = 'aislo:b08:drawing-error';
|
||||
export const AISLO_DRAWING_CHANGED_MESSAGE = 'aislo:b08:drawing-changed';
|
||||
export const AISLO_DRAWING_SAVE_REQUEST_MESSAGE = 'aislo:b08:save-request';
|
||||
export const AISLO_DRAWING_SAVE_RESPONSE_MESSAGE = 'aislo:b08:save-response';
|
||||
export const AISLO_DRAWING_NAVIGATE_MESSAGE = 'aislo:b08:navigate';
|
||||
export const AISLO_DRAWING_EXPORT_MESSAGE = 'aislo:b08:export-file';
|
||||
export const AISLO_DRAWING_HOST_SAVE_MESSAGE = 'aislo:b08:host-save';
|
||||
/** 작도 영역 가이드(169-1 · 169-3) — 부모 숫자 칸 → CAD 갈기 · CAD 끌기 → 부모 숫자 칸 */
|
||||
@@ -95,11 +94,6 @@ function notifyParent(type: string, payload: Record<string, unknown> = {}) {
|
||||
window.parent.postMessage({ type, ...payload }, window.location.origin);
|
||||
}
|
||||
|
||||
/** 수량 패널에서 이전/다음 도면으로 이동 요청 (부모가 처리). */
|
||||
export function requestDrawingNavigation(direction: 'prev' | 'next') {
|
||||
notifyParent(AISLO_DRAWING_NAVIGATE_MESSAGE, { direction });
|
||||
}
|
||||
|
||||
/** 저장 요청을 부모 페이지 [저장]에 넘긴다 — 캐드는 양식 파일을 모른다. */
|
||||
export function requestHostSave() {
|
||||
notifyParent(AISLO_DRAWING_HOST_SAVE_MESSAGE);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* 앱 ☰ 자리 비키기(기기대응 7-2) — 값은 host-burger.ts 가 적음 · 틀 없이 띄우면 0 */
|
||||
html .cad-titlebar {
|
||||
padding-left: max(12px, var(--host-burger-pad, 0px));
|
||||
html .cad-ribbon-tabs {
|
||||
padding-left: max(8px, var(--host-burger-pad, 0px));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* 앱 ☰ 자리 비키기(기기대응 7-2) — 앱(B07)이 왼쪽 위에 띄운 ☰ 가 이 틀(iframe) 맨 위 줄에 걸치면
|
||||
* 걸친 폭 + 틈 8 을 `--host-burger-pad` 로 적음(App.css `.cad-titlebar` 가 그만큼 왼쪽을 비움).
|
||||
* 걸친 폭 + 틈 8 을 `--host-burger-pad` 로 적음(리본 탭 줄 `.cad-ribbon-tabs` 가 그만큼 왼쪽을 비움).
|
||||
* 좌측 패널을 여닫으면 틀 폭이 바뀌어 resize 로 다시 잼 · 홀로 띄운 CAD(틀 없음)는 0.
|
||||
*/
|
||||
import './host-burger.css';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/** 리본 렌더러 — ribbon.config.ts의 데이터와 명령 레지스트리만 보고 그린다. */
|
||||
import { type FC, type ReactNode, useEffect, useRef, useState } from 'react';
|
||||
import type { CadCommand } from '../commands/command.types';
|
||||
import { QuickAccessBar } from '../components/QuickAccessBar';
|
||||
import { getCommandById } from '../commands/registry';
|
||||
import { runCommand } from '../commands/run-command';
|
||||
import {
|
||||
@@ -157,8 +158,33 @@ export const Ribbon: FC<RibbonProps> = ({ activeTool, widgets, onCommandRun }) =
|
||||
// (오피스 「리본 자동 숨기기」). 접기 단추로 펼치면 고정 펼침.
|
||||
const [peek, setPeek] = useState(false);
|
||||
const navRef = useRef<HTMLElement>(null);
|
||||
const slotRef = useRef<HTMLSpanElement>(null);
|
||||
const peeking = collapsed && peek;
|
||||
|
||||
// 탭 줄 빈 자리([출력] 오른쪽 ~ ↶ 왼쪽)를 부모 `.cad-host` 에 적음 — 부모 페이지가 자기 탭 줄을 거기 겹침.
|
||||
// 값 = iframe 왼쪽 끝 → 빈 자리 왼쪽 · 빈 자리 오른쪽 → iframe 오른쪽 끝(px · CSS right 꼴) · 짝 선례 `integration/host-burger.ts`.
|
||||
useEffect(() => {
|
||||
const host = window.frameElement?.parentElement;
|
||||
const slot = slotRef.current;
|
||||
if (!host?.classList.contains('cad-host') || !slot) return;
|
||||
const update = (): void => {
|
||||
const s = slot.getBoundingClientRect();
|
||||
host.style.setProperty('--cad-tab-slot-left', `${Math.round(s.left)}px`);
|
||||
host.style.setProperty(
|
||||
'--cad-tab-slot-right',
|
||||
`${Math.round(window.innerWidth - s.right)}px`
|
||||
);
|
||||
};
|
||||
update();
|
||||
const observer = new ResizeObserver(update);
|
||||
observer.observe(slot);
|
||||
window.addEventListener('resize', update);
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
window.removeEventListener('resize', update);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!peeking) return;
|
||||
const close = (event: PointerEvent) => {
|
||||
@@ -189,6 +215,8 @@ export const Ribbon: FC<RibbonProps> = ({ activeTool, widgets, onCommandRun }) =
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
<span ref={slotRef} className="cad-ribbon-tabs__slot" />
|
||||
<QuickAccessBar />
|
||||
<button
|
||||
type="button"
|
||||
className="cad-ribbon-tabs__collapse"
|
||||
|
||||
@@ -204,5 +204,7 @@ export const RIBBON_TABS: RibbonTab[] = [
|
||||
},
|
||||
];
|
||||
|
||||
/** 제목표시줄의 빠른 실행 도구막대 */
|
||||
/** 리본 탭 줄 오른쪽 빠른 실행 단추 */
|
||||
export const QUICK_ACCESS_COMMANDS: string[] = ['QSAVE', 'EXPORT', 'UNDO', 'REDO'];
|
||||
/** 그려 두고 숨기는 빠른 실행 — 출력 탭에 같은 명령이 있음 · 되살림 = 여기서 뺌 */
|
||||
export const QUICK_ACCESS_HIDDEN: string[] = ['QSAVE', 'EXPORT'];
|
||||
|
||||
@@ -218,7 +218,26 @@ def test_B07_목록에서_연_도면이_탭_미저장_편집_지킴():
|
||||
assert "tab.dirty ? tab.drawing : undefined" in page # 돌아오면 받아 둔 편집본
|
||||
assert "cadDirty = !!stash;" in page and "cad.load(stash ?? response.drawing" in page
|
||||
assert "if (!cadDirty && !tabs.anyDirty()) return true;" in page
|
||||
assert "void openTab(drawings[target]);" in page # 이전 · 다음 = 그 도면 탭
|
||||
|
||||
|
||||
def test_CAD_머리줄_없음_탭줄_빈자리_변수():
|
||||
"""요구 280-6 — 제목줄 · 하단 도면 칸(‹ ›) 지움 · ↶ ↷ = 리본 탭 줄 · 빈 자리를 부모 탭 줄 자리로 적음."""
|
||||
src = ROOT / "B07_DesignDetail/openwebcad/src"
|
||||
ribbon = (src / "ribbon/Ribbon.tsx").read_text("utf-8")
|
||||
quick = (src / "components/QuickAccessBar.tsx").read_text("utf-8")
|
||||
css = (src / "App.css").read_text("utf-8")
|
||||
assert '<span ref={slotRef} className="cad-ribbon-tabs__slot" />' in ribbon
|
||||
assert ribbon.index("cad-ribbon-tabs__slot") < ribbon.index("<QuickAccessBar />")
|
||||
assert "'--cad-tab-slot-left', `${Math.round(s.left)}px`" in ribbon
|
||||
assert "'--cad-tab-slot-right', `${Math.round(window.innerWidth - s.right)}px`" in ribbon
|
||||
assert "hidden={QUICK_ACCESS_HIDDEN.includes(id)}" in quick and "cad-titlebar" not in quick
|
||||
assert "QUICK_ACCESS_HIDDEN: string[] = ['QSAVE', 'EXPORT']" in (
|
||||
src / "ribbon/ribbon.config.ts"
|
||||
).read_text("utf-8")
|
||||
assert "--cad-title-height: 0px;" in css and ".cad-titlebar" not in css
|
||||
assert ".cad-qtable" not in css and not (src / "components/QuantityPanel.tsx").exists()
|
||||
assert "QuantityPanel" not in (src / "App.tsx").read_text("utf-8")
|
||||
assert "html .cad-ribbon-tabs {" in (src / "integration/host-burger.css").read_text("utf-8")
|
||||
|
||||
|
||||
def test_여러_장_문서_장_도번_백업_칸(got):
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
"""B07 CAD 손가락(PLAN_기기대응 4-1 · `openwebcad/src/inputController/input-touch.ts`) — 빌드된 CAD 화면.
|
||||
|
||||
① 손가락 기기 = 명령 단추 6개(44px) · 마우스 기기 = 안 보임 ② 확대 막기(user-scalable=no) 없음
|
||||
① 자판 없는 기기(hover:none · pointer:coarse) = 명령 단추 6개(44px) · 마우스 · 터치 노트북 = 안 보임(요구 280-6)
|
||||
② 확대 막기(user-scalable=no) 없음
|
||||
③ 캔버스 touch-action none · 두 손가락 벌림 · 한 손가락 끌기가 페이지를 확대 · 밀지 않음(페이지 오류 0)
|
||||
"""
|
||||
|
||||
import os
|
||||
import urllib.request
|
||||
|
||||
import pytest
|
||||
|
||||
URL = "http://localhost:5174/b07-cad/"
|
||||
URL = os.environ.get("CAD_TOUCH_URL", "http://localhost:5174/b07-cad/")
|
||||
|
||||
|
||||
def _up() -> bool:
|
||||
@@ -42,7 +44,12 @@ def test_cad_touch_bar_and_gestures():
|
||||
mouse.evaluate("getComputedStyle(document.querySelector('.cad-touch-bar')).display")
|
||||
== "none"
|
||||
)
|
||||
|
||||
# 터치 노트북(any-pointer: coarse · 주 입력 마우스) — 단추는 hover:none 일 때만(에뮬은 터치 노트북을 못 꾸밈)
|
||||
media = mouse.evaluate(
|
||||
"[...document.styleSheets].flatMap(s => [...s.cssRules]).filter(r => r.media"
|
||||
" && [...r.cssRules].some(c => c.selectorText === '.cad-touch-bar')).map(r => r.media.mediaText)"
|
||||
)
|
||||
assert media == ["(hover: none) and (pointer: coarse)"], media
|
||||
ctx = browser.new_context(
|
||||
viewport={"width": 1180, "height": 820}, has_touch=True, is_mobile=True
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user