style(spreadsheet): 셀 서식 · 메모 · 찾기 · 패널 창 · 오류 ⚠ 를 부품 모달 · 탭 · 떠 있는 판 · 단추로 (PLAN 553-6)
- 셀 서식 창 = 부품 모달(40rem) · 탭(32) · 칸 = 부품 입력 · 고르기 28 · 자체 판 · 탭 · 칸 CSS 지움 - 메모 편집 = 공용 확인창 판(.ui-confirm) · 글 칸 = 부품 입력 - 찾기 · 바꾸기 = 떠 있는 판(머리 없음) · 칸 24 · 단추 22 · 닫기 = 아이콘 단추 - 데이터 패널 창(조건부 서식 · 유효성 · 이름) = 떠 있는 판(머리 · 닫기 ✕) - 오류 ⚠ = 아이콘 단추(선 18) · 골라 붙여넣기 · 정렬 칸 · 알약 찾기 칸 = 부품 단추 · 고르기 · 입력 - 옛 보라 별칭 · 날 검정 가림막 → 공통 값(hover · accent · scrim) · 창 닫힘 감시가 모달도 기다림 - 날 단추 23 → 15(남음 = 수식 줄 칸 단추 · 격자 안 ▼ · 알약) · 자체 꼴 규칙 95 → 77 · 격자 색 전후 같음 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -109,7 +109,7 @@
|
||||
bottom: 40px;
|
||||
z-index: 20;
|
||||
padding: 6px 10px;
|
||||
border-radius: 4px;
|
||||
border-radius: var(--radius-buttons);
|
||||
background: var(--color-primary);
|
||||
color: var(--color-primary-text);
|
||||
}
|
||||
@@ -126,6 +126,10 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ss-brush:hover {
|
||||
background: var(--color-hover);
|
||||
}
|
||||
|
||||
.ss-brush.is-on {
|
||||
background: color-mix(in srgb, var(--color-focus-ring) 20%, var(--color-surface-raised));
|
||||
box-shadow: inset 0 0 0 2px var(--color-focus-ring);
|
||||
@@ -135,19 +139,17 @@
|
||||
cursor: copy;
|
||||
}
|
||||
|
||||
.ss-find {
|
||||
position: absolute;
|
||||
/* 찾기 = 떠 있는 판 부품(머리 없음) · 칸 · 단추 = 부품 — 여기는 자리 · 줄 배치만(553-6) */
|
||||
.ss-find.ui-float-panel {
|
||||
top: 4px;
|
||||
right: 16px;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.ss-find > .ui-float-panel__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 6px;
|
||||
background: var(--color-surface-raised);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.ss-find[hidden],
|
||||
@@ -166,32 +168,17 @@
|
||||
|
||||
.ss-find__input {
|
||||
width: 180px;
|
||||
padding: 2px 6px;
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-surface-raised);
|
||||
color: var(--color-text-body);
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.ss-find__btn {
|
||||
padding: 2px 8px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 4px;
|
||||
background: var(--color-surface);
|
||||
color: var(--color-text-body);
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ss-find__opt {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
font-size: 12px;
|
||||
font-size: var(--text-caption);
|
||||
}
|
||||
|
||||
.ss-find__status {
|
||||
font-size: 12px;
|
||||
font-size: var(--text-caption);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
@@ -223,19 +210,11 @@
|
||||
gap: 2px;
|
||||
padding: 4px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-cards);
|
||||
background: var(--color-surface-raised);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.ss-pick > button {
|
||||
padding: 2px 8px;
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-surface);
|
||||
color: var(--color-text-body);
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 함수 자동 완성 · 인자 도움말 */
|
||||
.ss-hint {
|
||||
position: absolute;
|
||||
@@ -265,22 +244,17 @@
|
||||
}
|
||||
|
||||
.ss-hint__item.is-on {
|
||||
background: color-mix(in srgb, var(--color-accent) 18%, var(--color-surface-raised));
|
||||
background: var(--color-accent-soft);
|
||||
}
|
||||
|
||||
/* 데이터 메뉴 — 패널 창 · 유효성 목록 ▼ */
|
||||
.ss-dialog {
|
||||
position: absolute;
|
||||
/* 패널 창 = 떠 있는 판 부품 — 여기는 자리 · 크기만(553-6) */
|
||||
.ss-dialog.ui-float-panel {
|
||||
top: 4px;
|
||||
right: 16px;
|
||||
z-index: 21;
|
||||
max-width: min(720px, calc(100% - 32px));
|
||||
max-height: calc(100% - 8px);
|
||||
overflow: auto;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 6px;
|
||||
background: var(--color-surface-raised);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.ss-dialog[hidden],
|
||||
@@ -288,20 +262,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ss-dialog__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
padding: 6px 8px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ss-dialog__body {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.ss-list-arrow {
|
||||
position: absolute;
|
||||
z-index: 8;
|
||||
|
||||
@@ -118,10 +118,15 @@ export function dialogKeys(overlay: HTMLElement, close: () => void, run?: () =>
|
||||
});
|
||||
}
|
||||
|
||||
/** body 에 뜬 창(.ui-confirm)이 다 닫히면 초점을 격자로 — 창을 연 바로 뒤에 부름 */
|
||||
/** body 에 뜬 창(.ui-confirm · 모달)이 다 닫히면 초점을 격자로 — 창을 연 바로 뒤에 부름 */
|
||||
export function watchClose(back: () => void): void {
|
||||
const w = new MutationObserver(() => {
|
||||
if (document.querySelector("body > .ui-confirm, body > .ui-confirm__panel")) return;
|
||||
if (
|
||||
document.querySelector(
|
||||
"body > .ui-confirm, body > .ui-confirm__panel, body > .ui-modal__backdrop",
|
||||
)
|
||||
)
|
||||
return;
|
||||
w.disconnect();
|
||||
back();
|
||||
});
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgb(0 0 0 / 18%);
|
||||
background: var(--color-scrim);
|
||||
}
|
||||
|
||||
.ss-steps__box {
|
||||
@@ -115,18 +115,10 @@
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.ss-errtip__btn {
|
||||
/* ⚠ = 아이콘 단추 부품(선 · 18) — 여기는 자리 · 경고 색만(553-6) */
|
||||
.ss-errtip__btn.ui-icon-btn {
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 0;
|
||||
font-size: 11px;
|
||||
line-height: 16px;
|
||||
cursor: pointer;
|
||||
color: var(--color-warning);
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
.ss-errtip__balloon {
|
||||
@@ -183,5 +175,5 @@
|
||||
}
|
||||
|
||||
.ss-errtip__item:hover {
|
||||
background: var(--color-mist-violet);
|
||||
background: var(--color-hover);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
import "./spreadsheet_stage3.css"; // sub4 패널 스타일(잇는 쪽이 붙임)
|
||||
import { createMapContextMenu, type MapContextMenuItem } from "@ui/ui_template_context_menu";
|
||||
import { el, showToast } from "@ui/ui_template_elements";
|
||||
import { createFloatPanel } from "@ui/ui_template_float_panel";
|
||||
import { parseRange, rangeToA1, toA1 } from "./spreadsheet_address";
|
||||
import { autoSumCells, autoSumRange } from "./spreadsheet_autosum";
|
||||
import { isError } from "./spreadsheet_eval";
|
||||
@@ -40,7 +41,6 @@ const TEXT = {
|
||||
validation: "데이터 유효성…",
|
||||
names: "이름 관리…",
|
||||
grid: "눈금선 보이기 · 숨기기",
|
||||
close: "닫기",
|
||||
list: "값 목록(Alt+↓)",
|
||||
sheets: "시트 숨기기 · 탭 색…",
|
||||
audit: "수식 분석",
|
||||
@@ -121,18 +121,14 @@ export function mountDataMenu(ctx: SpreadsheetContext, back: () => void): DataMe
|
||||
};
|
||||
|
||||
// ── 패널 창(조건부 서식 · 유효성 · 이름) — 한 번에 하나 ─────────────────────
|
||||
const title = el("span", { className: "ss-dialog__title" });
|
||||
const body = el("div", { className: "ss-dialog__body" });
|
||||
const shut = el("button", {
|
||||
className: "ss-find__btn",
|
||||
text: "×",
|
||||
attrs: { type: "button", title: TEXT.close },
|
||||
});
|
||||
const dialog = el("div", {
|
||||
className: "ss-dialog ui-popup",
|
||||
attrs: { hidden: "" },
|
||||
children: [el("div", { className: "ss-dialog__head", children: [title, shut] }), body],
|
||||
// 떠 있는 판 부품(머리 · 닫기 ✕ · 몸 · 553-6) — 자리는 `.ss-dialog`
|
||||
const { root: dialog, body } = createFloatPanel({
|
||||
title: " ",
|
||||
onClose: () => (closePanel(), back()),
|
||||
});
|
||||
dialog.classList.add("ss-dialog");
|
||||
dialog.setAttribute("hidden", "");
|
||||
const title = dialog.querySelector(".ui-float-panel__title") as HTMLElement;
|
||||
ctx.root.append(dialog);
|
||||
let panel: PartHandle | null = null;
|
||||
const closePanel = (): void => {
|
||||
@@ -141,7 +137,6 @@ export function mountDataMenu(ctx: SpreadsheetContext, back: () => void): DataMe
|
||||
body.replaceChildren();
|
||||
dialog.setAttribute("hidden", "");
|
||||
};
|
||||
shut.addEventListener("click", () => (closePanel(), back()));
|
||||
dialog.addEventListener("keydown", (e) => {
|
||||
if (e.key !== "Escape") return;
|
||||
e.stopPropagation();
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* 시트 전체를 훑지 말고 그리는 칸만 물음(값 하나 · 칸 하나 읽는 값싼 부름).
|
||||
* ========================================================================== */
|
||||
|
||||
import { el } from "@ui/ui_template_elements";
|
||||
import { createIconButton, el } from "@ui/ui_template_elements";
|
||||
import { currentLanguageIndex } from "@ui/ui_template_locale";
|
||||
import { toA1 } from "./spreadsheet_address";
|
||||
import { isError } from "./spreadsheet_eval";
|
||||
@@ -137,11 +137,9 @@ export function attachErrorTip(ctx: SpreadsheetContext, trace?: TraceHandle): Er
|
||||
const g = ctx.grid.root.getBoundingClientRect();
|
||||
layer.style.left = `${box.x - (a.left - g.left) - 20}px`;
|
||||
layer.style.top = `${box.y - (a.top - g.top)}px`;
|
||||
const btn = el("button", {
|
||||
className: "ss-errtip__btn",
|
||||
text: "⚠",
|
||||
attrs: { type: "button", "aria-label": w.까닭 },
|
||||
});
|
||||
const btn = createIconButton({ glyph: "⚠", title: w.까닭, variant: "outline", size: 18 });
|
||||
btn.classList.add("ss-errtip__btn");
|
||||
btn.removeAttribute("title"); // 까닭은 옆 풍선이 보임
|
||||
const balloon = el("div", { className: "ss-errtip__balloon", text: w.까닭 });
|
||||
btn.addEventListener("mousedown", (ev) => ev.stopPropagation());
|
||||
btn.addEventListener("click", () => (menu ? closeMenu() : openMenu(w, r, c)));
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* 빨간 세모는 C 격자 · 우클릭 [메모 …] 는 E 메뉴 몫(브레인 배정).
|
||||
* ========================================================================== */
|
||||
|
||||
import { el } from "@ui/ui_template_elements";
|
||||
import { createButton, el } from "@ui/ui_template_elements";
|
||||
import { watchClose } from "./spreadsheet_actions";
|
||||
import { parseClipboard } from "./spreadsheet_clipboard";
|
||||
import { attachComments, getComment, setComment } from "./spreadsheet_comments";
|
||||
@@ -138,10 +138,8 @@ export function attachExtras(
|
||||
: undefined;
|
||||
picker.replaceChildren(
|
||||
...(Object.keys(TEXT.modes) as PasteSpecialMode[]).map((mode) => {
|
||||
const b = el("button", {
|
||||
text: TEXT.modes[mode],
|
||||
attrs: { type: "button", "data-mode": mode },
|
||||
});
|
||||
const b = createButton({ label: TEXT.modes[mode], variant: "ghost", size: 22 });
|
||||
b.dataset.mode = mode;
|
||||
b.addEventListener("click", () => {
|
||||
picker.setAttribute("hidden", "");
|
||||
// 늦게 — 크기 예산
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
* Enter = 다음 · Esc = 닫고 격자로. 바꾸기 = 활성 칸이 맞으면 바꾸고 다음으로(엑셀).
|
||||
* ========================================================================== */
|
||||
|
||||
import { el } from "@ui/ui_template_elements";
|
||||
import { createButton, createIconButton, el, setUiSize } from "@ui/ui_template_elements";
|
||||
import { createFloatPanel } from "@ui/ui_template_float_panel";
|
||||
import { findAll, findNext, replaceAll, replaceOne, type FindOptions } from "./spreadsheet_find";
|
||||
import { selectCell } from "./spreadsheet_selection";
|
||||
import type { PartHandle, SpreadsheetContext } from "./spreadsheet_view_types";
|
||||
@@ -29,12 +30,14 @@ export interface FindPanelHandle extends PartHandle {
|
||||
}
|
||||
|
||||
export function mountFindPanel(ctx: SpreadsheetContext, back: () => void): FindPanelHandle {
|
||||
const query = el("input", { className: "ss-find__input", attrs: { placeholder: TEXT.find } });
|
||||
const repl = el("input", { className: "ss-find__input", attrs: { placeholder: TEXT.replace } });
|
||||
const field = (placeholder: string): HTMLInputElement =>
|
||||
setUiSize(el("input", { className: "ui-input ss-find__input", attrs: { placeholder } }), 24);
|
||||
const query = field(TEXT.find);
|
||||
const repl = field(TEXT.replace);
|
||||
const status = el("span", { className: "ss-find__status" });
|
||||
const button = (text: string, onClick: () => void): HTMLButtonElement => {
|
||||
const b = el("button", { className: "ss-find__btn", text, attrs: { type: "button" } });
|
||||
b.addEventListener("click", onClick);
|
||||
const b = createButton({ label: text, variant: "ghost", size: 22, onClick });
|
||||
b.classList.add("ss-find__btn");
|
||||
return b;
|
||||
};
|
||||
const check = (text: string): HTMLInputElement => {
|
||||
@@ -42,8 +45,8 @@ export function mountFindPanel(ctx: SpreadsheetContext, back: () => void): FindP
|
||||
options.append(el("label", { className: "ss-find__opt", children: [box, text] }));
|
||||
return box;
|
||||
};
|
||||
const closeBtn = button("×", close);
|
||||
closeBtn.title = TEXT.close;
|
||||
const closeBtn = createIconButton({ glyph: "×", title: TEXT.close, size: 22, onClick: close });
|
||||
closeBtn.classList.add("ss-find__btn");
|
||||
const options = el("div", { className: "ss-find__row" });
|
||||
const caseBox = check(TEXT.case);
|
||||
const wholeBox = check(TEXT.whole);
|
||||
@@ -52,19 +55,19 @@ export function mountFindPanel(ctx: SpreadsheetContext, back: () => void): FindP
|
||||
className: "ss-find__row",
|
||||
children: [repl, button(TEXT.one, one), button(TEXT.all, all)],
|
||||
});
|
||||
const root = el("div", {
|
||||
className: "ss-find",
|
||||
attrs: { hidden: "" },
|
||||
children: [
|
||||
el("div", {
|
||||
className: "ss-find__row",
|
||||
children: [query, button(TEXT.next, next), closeBtn],
|
||||
}),
|
||||
replaceRow,
|
||||
options,
|
||||
status,
|
||||
],
|
||||
});
|
||||
// 떠 있는 판 부품(머리 없음 · 553-6) — 자리는 `.ss-find`
|
||||
const { root, body } = createFloatPanel({ popover: true });
|
||||
root.classList.add("ss-find");
|
||||
root.setAttribute("hidden", "");
|
||||
body.append(
|
||||
el("div", {
|
||||
className: "ss-find__row",
|
||||
children: [query, button(TEXT.next, next), closeBtn],
|
||||
}),
|
||||
replaceRow,
|
||||
options,
|
||||
status,
|
||||
);
|
||||
ctx.root.append(root);
|
||||
|
||||
const opts = (): FindOptions => ({
|
||||
|
||||
@@ -1,50 +1,9 @@
|
||||
/* =============================================================================
|
||||
* spreadsheet_format_dialog.css (2단계 · 주인 sub_laptop_1)
|
||||
* 셀 서식 대화상자 — 뿌리 배경(`.ui-confirm`)은 공용 확인 모달과 같은 것을 그대로 씀.
|
||||
* 셀 서식 대화상자 — 창 · 탭 · 칸 = 부품 모달(40rem) · 탭(32) · 입력 · 고르기(28) · 여기는 안 배치만(553-6).
|
||||
* 색 · 간격은 테마 토큰만(`ui_template_theme.css`).
|
||||
* ========================================================================== */
|
||||
|
||||
.ss-fmtdlg__panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: min(560px, 92vw);
|
||||
max-height: 84vh;
|
||||
padding: var(--spacing-16);
|
||||
border-radius: var(--radius-cards);
|
||||
background: var(--color-surface-raised);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.ss-fmtdlg__title {
|
||||
margin: 0 0 var(--spacing-8);
|
||||
font-size: 1rem;
|
||||
color: var(--color-text-body);
|
||||
}
|
||||
|
||||
.ss-fmtdlg__tabs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.ss-fmtdlg__tabbtn {
|
||||
padding: 6px 12px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-bottom: 0;
|
||||
border-radius: var(--radius-sm) var(--radius-sm) 0 0;
|
||||
background: var(--color-paper);
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.82rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ss-fmtdlg__tabbtn.is-active {
|
||||
background: var(--color-canvas);
|
||||
color: var(--color-text-body);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.ss-fmtdlg__body {
|
||||
flex: 1;
|
||||
min-height: 300px;
|
||||
@@ -68,21 +27,7 @@
|
||||
width: 140px;
|
||||
flex-shrink: 0;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.ss-fmtdlg__row select,
|
||||
.ss-fmtdlg__row input[type="text"],
|
||||
.ss-fmtdlg__row input:not([type]),
|
||||
.ss-fmtdlg__row input[type="number"],
|
||||
.ss-fmtdlg__code {
|
||||
height: 28px;
|
||||
padding: 0 6px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--color-canvas);
|
||||
color: var(--color-text-body);
|
||||
font-size: 0.82rem;
|
||||
font-size: var(--text-body-sm);
|
||||
}
|
||||
|
||||
.ss-fmtdlg__code {
|
||||
@@ -135,7 +80,7 @@
|
||||
.ss-fmtdlg__note {
|
||||
margin: 0 0 var(--spacing-8);
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.8rem;
|
||||
font-size: var(--text-caption);
|
||||
}
|
||||
|
||||
.ss-fmtdlg__actions {
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
* 우클릭 메뉴 [셀 서식] 도 같은 함수를 부르면 됨(`spreadsheet_menu.ts`).
|
||||
* ========================================================================== */
|
||||
|
||||
import { createButton, el } from "@ui/ui_template_elements";
|
||||
import { createButton, el, setUiSize } from "@ui/ui_template_elements";
|
||||
import { createTabs } from "@ui/ui_template_tabs";
|
||||
import { openModal } from "@ui/ui_template_modal";
|
||||
import { toFrac } from "@ui/sheet/ui_template_sheet_frac";
|
||||
import { colName, rangeToA1, toA1 } from "./spreadsheet_address";
|
||||
import {
|
||||
@@ -39,6 +41,7 @@ const TEXT = {
|
||||
tabProtect: "보호",
|
||||
ok: "확인",
|
||||
cancel: "취소",
|
||||
close: "닫기",
|
||||
category: "범주",
|
||||
decimals: "소수 자릿수",
|
||||
thousands: "천 단위 구분 기호 사용",
|
||||
@@ -105,17 +108,9 @@ export function openFormatDialog(ctx: SpreadsheetContext): void {
|
||||
const spansMany = range.r0 !== range.r1 || range.c0 !== range.c1;
|
||||
let mergeWanted = (ctx.sheet().병합 ?? []).includes(rangeToA1(range));
|
||||
|
||||
const overlay = el("div", { className: "ui-confirm ss-fmtdlg" });
|
||||
const panel = el("div", { className: "ss-fmtdlg__panel" });
|
||||
const tabsBar = el("div", { className: "ss-fmtdlg__tabs" });
|
||||
const body = el("div", { className: "ss-fmtdlg__body" });
|
||||
overlay.append(panel);
|
||||
panel.append(el("h2", { className: "ss-fmtdlg__title", text: TEXT.title }), tabsBar, body);
|
||||
|
||||
const close = (): void => overlay.remove();
|
||||
overlay.addEventListener("click", (ev) => {
|
||||
if (ev.target === overlay) close();
|
||||
});
|
||||
let close = (): void => {};
|
||||
|
||||
// ── 표시 형식 ────────────────────────────────────────────────────────
|
||||
let category: Category = guessCategory(draft.형식);
|
||||
@@ -464,22 +459,25 @@ export function openFormatDialog(ctx: SpreadsheetContext): void {
|
||||
[TEXT.tabProtect, protectTab],
|
||||
];
|
||||
|
||||
function showTab(index: number): void {
|
||||
body.replaceChildren(tabs[index][1]());
|
||||
tabsBar.querySelectorAll(".ss-fmtdlg__tabbtn").forEach((btn, i) => {
|
||||
btn.classList.toggle("is-active", i === index);
|
||||
});
|
||||
}
|
||||
|
||||
tabs.forEach(([label], i) => {
|
||||
const btn = el("button", {
|
||||
className: "ss-fmtdlg__tabbtn",
|
||||
text: label,
|
||||
attrs: { type: "button" },
|
||||
});
|
||||
btn.addEventListener("click", () => showTab(i));
|
||||
tabsBar.append(btn);
|
||||
const tabBar = createTabs({
|
||||
tabs: tabs.map(([label], i) => ({ key: String(i), label })),
|
||||
value: "0",
|
||||
size: 32,
|
||||
onChange: (key) => showTab(Number(key)),
|
||||
});
|
||||
tabsBar.append(tabBar.root);
|
||||
|
||||
function showTab(index: number): void {
|
||||
const tab = tabs[index][1]();
|
||||
// 칸 = 부품 입력 · 고르기 28(553-6)
|
||||
const fields = "select, input:not([type]), input[type=text], input[type=number]";
|
||||
for (const f of tab.querySelectorAll<HTMLElement>(fields)) {
|
||||
f.classList.add(f.tagName === "SELECT" ? "ui-select" : "ui-input");
|
||||
setUiSize(f, 28);
|
||||
}
|
||||
body.replaceChildren(tab);
|
||||
tabBar.select(String(index));
|
||||
}
|
||||
|
||||
function apply(): void {
|
||||
ctx.dispatch({ 종류: "서식", 시트: ctx.selection.시트, 범위: ctx.selection.범위, 바꿀: draft });
|
||||
@@ -497,13 +495,21 @@ export function openFormatDialog(ctx: SpreadsheetContext): void {
|
||||
const actions = el("div", {
|
||||
className: "ss-fmtdlg__actions",
|
||||
children: [
|
||||
createButton({ label: TEXT.cancel, variant: "ghost", onClick: close }),
|
||||
createButton({ label: TEXT.cancel, variant: "ghost", onClick: () => close() }),
|
||||
createButton({ label: TEXT.ok, variant: "filled", onClick: apply }),
|
||||
],
|
||||
});
|
||||
panel.append(actions);
|
||||
|
||||
showTab(0);
|
||||
document.body.append(overlay);
|
||||
dialogKeys(overlay, close, apply);
|
||||
openModal({
|
||||
title: TEXT.title,
|
||||
closeLabel: TEXT.close,
|
||||
width: "40rem",
|
||||
dialogClass: "ss-fmtdlg",
|
||||
mount: (box, shut) => {
|
||||
close = shut;
|
||||
box.append(tabsBar, body, actions);
|
||||
dialogKeys(box.parentElement ?? box, shut, apply);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -120,20 +120,11 @@
|
||||
width: 320px;
|
||||
padding: var(--spacing-8);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--radius-cards);
|
||||
background: var(--color-surface-raised);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.ss-links__search {
|
||||
padding: 4px 6px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--color-canvas);
|
||||
color: var(--color-text-body);
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.ss-links__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -155,7 +146,7 @@
|
||||
}
|
||||
|
||||
.ss-links__row:hover {
|
||||
background: var(--color-mist-violet);
|
||||
background: var(--color-hover);
|
||||
}
|
||||
|
||||
.ss-links__note {
|
||||
|
||||
@@ -585,8 +585,8 @@ export function mountLinks(
|
||||
function openPick(anchor: HTMLElement): void {
|
||||
closePick();
|
||||
const input = el("input", {
|
||||
className: "ss-links__search",
|
||||
attrs: { type: "search", placeholder: TEXT.search },
|
||||
className: "ui-input ss-links__search",
|
||||
attrs: { type: "search", placeholder: TEXT.search, "data-size": "28" },
|
||||
}) as HTMLInputElement;
|
||||
const list = el("div", { className: "ss-links__list" });
|
||||
const note = (text: string) => el("div", { className: "ss-links__note", text });
|
||||
|
||||
@@ -59,15 +59,15 @@ export function attachMenu(ctx: SpreadsheetContext): PartHandle {
|
||||
/** 메모 글 입력 — 작은 오버레이(native prompt 안 씀, 자동화 창이 native 창을 자동 취소하는
|
||||
* 문제는 `showConfirmDialog` 머리 주석과 같은 까닭). 저장을 누르면 `onSave`. */
|
||||
function openCommentEditor(initial: string, onSave: (text: string) => void): void {
|
||||
const overlay = el("div", { className: "ss-comment-editor" });
|
||||
const panel = el("div", { className: "ss-comment-editor__panel" });
|
||||
const overlay = el("div", { className: "ui-confirm ss-comment-editor" });
|
||||
const panel = el("div", { className: "ui-confirm__panel" });
|
||||
const textarea = el("textarea", {
|
||||
className: "ss-comment-editor__input",
|
||||
className: "ui-input ss-comment-editor__input",
|
||||
}) as HTMLTextAreaElement;
|
||||
textarea.value = initial;
|
||||
const done = (): void => overlay.remove();
|
||||
const actions = el("div", {
|
||||
className: "ss-comment-editor__actions",
|
||||
className: "ui-confirm__actions",
|
||||
children: [
|
||||
createButton({ label: "취소", variant: "ghost", onClick: done }),
|
||||
createButton({
|
||||
|
||||
@@ -100,12 +100,17 @@ export function openSortDialog(ctx: SpreadsheetContext): () => void {
|
||||
function drawKeys() {
|
||||
list.replaceChildren(
|
||||
...keys.map((k, i) => {
|
||||
const col = el("select", { attrs: { "aria-label": t("Column") } });
|
||||
const col = el("select", {
|
||||
className: "ui-select",
|
||||
attrs: { "aria-label": t("Column"), "data-size": "28" },
|
||||
});
|
||||
for (let c = rg.c0; c <= rg.c1; c++)
|
||||
col.append(el("option", { text: columnLabel(c), attrs: { value: String(c) } }));
|
||||
col.value = String(k.열);
|
||||
col.addEventListener("change", () => (k.열 = Number(col.value)));
|
||||
const dir = el("select", {
|
||||
className: "ui-select",
|
||||
attrs: { "data-size": "28" },
|
||||
children: [
|
||||
el("option", { text: t("Asc"), attrs: { value: "asc" } }),
|
||||
el("option", { text: t("Desc"), attrs: { value: "desc" } }),
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgb(0 0 0 / 18%);
|
||||
background: var(--color-scrim);
|
||||
}
|
||||
|
||||
.ss-sort__box,
|
||||
@@ -48,11 +48,6 @@
|
||||
|
||||
.ss-sort__row select {
|
||||
flex: 1;
|
||||
padding: var(--spacing-4);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--color-canvas);
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.ss-sort__row option {
|
||||
@@ -92,8 +87,8 @@
|
||||
}
|
||||
|
||||
.ss-filter__btn.is-on {
|
||||
color: var(--color-royal-amethyst);
|
||||
border-color: var(--color-royal-amethyst);
|
||||
color: var(--color-accent);
|
||||
border-color: var(--color-accent);
|
||||
}
|
||||
|
||||
.ss-filter__panel {
|
||||
@@ -130,5 +125,5 @@
|
||||
}
|
||||
|
||||
.ss-filter__link:hover {
|
||||
background: var(--color-mist-violet);
|
||||
background: var(--color-hover);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
padding: var(--spacing-8);
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 8px;
|
||||
border-radius: var(--radius-cards);
|
||||
min-width: 280px;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
}
|
||||
|
||||
.ss-validation__dropdown-item:hover {
|
||||
background: var(--color-mist-violet);
|
||||
background: var(--color-hover);
|
||||
}
|
||||
|
||||
.ss-statusbar {
|
||||
|
||||
@@ -215,42 +215,13 @@
|
||||
|
||||
/* ── 메모 편집 창(칸 우클릭 [메모 삽입 · 편집]) ──────────────────────────────── */
|
||||
|
||||
.ss-comment-editor {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: var(--z-modal);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--color-scrim);
|
||||
}
|
||||
|
||||
.ss-comment-editor__panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-8);
|
||||
width: 280px;
|
||||
padding: var(--spacing-16);
|
||||
border-radius: var(--radius-cards);
|
||||
background: var(--color-surface-raised);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
/* 창 = 공용 확인창(.ui-confirm) · 칸 = 부품 입력(여러 줄) — 여기는 칸 크기만(553-6) */
|
||||
.ss-comment-editor__input {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
min-height: 80px;
|
||||
padding: var(--spacing-8);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--color-canvas);
|
||||
color: var(--color-text-body);
|
||||
font: inherit;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.ss-comment-editor__actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: var(--spacing-8);
|
||||
margin-bottom: var(--spacing-16);
|
||||
}
|
||||
|
||||
/* ⋯ 칸 메뉴(기기대응 3-5) — 터치 모드에서만 */
|
||||
|
||||
Reference in New Issue
Block a user