Merge remote-tracking branch 'origin/sub_laptop_3' into main_laptop_1
This commit is contained in:
@@ -260,7 +260,7 @@ export async function mountRouteEditor(
|
||||
sizeWatch.disconnect();
|
||||
themeWatch();
|
||||
historyControls.dispose(); // 단축키는 창(window)에 달려 있어 안 떼면 떠난 뒤에도 산다.
|
||||
curveLabelBox.destroy();
|
||||
curveBar.destroy();
|
||||
overlay.remove();
|
||||
};
|
||||
/** 초기노선 갈래 — 로직 단추 · [초기화] · 비교값(계획서 20). 서버 묻기는 `_Initial` 몫. */
|
||||
@@ -423,7 +423,6 @@ export async function mountRouteEditor(
|
||||
draw();
|
||||
},
|
||||
});
|
||||
const curveLabelBox = curveBar.label;
|
||||
const syncCurveBar = curveBar.sync;
|
||||
|
||||
/** 한 번의 편집을 마무리한다 — 다시 그리고, 라벨·상태줄을 맞추고, 되돌리기에 쌓는다.
|
||||
|
||||
@@ -24,6 +24,8 @@ import {
|
||||
createCurveLabel,
|
||||
type CurveLabel,
|
||||
} from "./B05_Profile_UI_RouteEdit_Label";
|
||||
import { createCurveIcon } from "./B05_Profile_UI_RouteEdit_CurveIcon";
|
||||
import { openAfterPick } from "./logic/B05_Profile_RouteEdit_CurveIcon";
|
||||
import { fitNote } from "./logic/B05_Profile_RouteEdit_KeyNudge";
|
||||
import { offStationMcs } from "./logic/B05_Profile_RouteEdit_StationFit";
|
||||
|
||||
@@ -57,19 +59,27 @@ export interface CurveBarParams {
|
||||
toScreen: (vertex: Vertex) => [number, number];
|
||||
/** 한 번의 편집을 마무리한다 — 다시 그리고 되돌리기에 쌓는다. */
|
||||
applyEdit: (message: string) => void;
|
||||
/** 고른 꺾임점을 푼다 — 닫기 단추와 「빈 곳 누르기」가 부른다(계획서 0-9 ㉖). */
|
||||
/** 고른 꺾임점을 푼다 — 닫기 단추와 「빈 곳 누르기」가 부른다(계획서 0-9 ㉖ · 445-C1 닫기도 고름을 풂). */
|
||||
onUnselect: () => void;
|
||||
}
|
||||
|
||||
export interface CurveBar {
|
||||
label: CurveLabel;
|
||||
/** 고른 자리에 맞춰 패널을 옮겨 그린다. */
|
||||
/** 편집기를 버릴 때 — 창 · 아이콘을 칸에서 뗀다. */
|
||||
destroy: () => void;
|
||||
/** 고른 자리에 맞춰 패널(열림일 때) · 아이콘(닫힘일 때)을 옮겨 그린다. */
|
||||
sync: () => void;
|
||||
/** 끌기 · 손잡이 놓을 때 맞춤 결과를 상태줄 `message` 뒤에(430 · 스냅 끔이면 그대로). */
|
||||
noteFit: (message: string, before: Vertex[], picked: number) => void;
|
||||
}
|
||||
|
||||
export function createCurveBar(params: CurveBarParams): CurveBar {
|
||||
/** 정보 창이 열려 있나(445-C1) — 고르면 닫힌 채 아이콘 · 아이콘 누름 = 열림 · 닫기 · 고름 풂 = 닫힘. */
|
||||
let open = false;
|
||||
const icon = createCurveIcon(params.canvas, () => {
|
||||
open = true;
|
||||
sync();
|
||||
});
|
||||
const label = createCurveLabel(
|
||||
{
|
||||
onClose: () => params.onUnselect(),
|
||||
@@ -201,11 +211,20 @@ export function createCurveBar(params: CurveBarParams): CurveBar {
|
||||
limitRadiusM,
|
||||
limitArcM,
|
||||
} = params.state();
|
||||
const rect = params.canvas.getBoundingClientRect();
|
||||
if (!(picked >= 0 && picked < planned.length)) {
|
||||
open = openAfterPick(open, -1);
|
||||
icon.update(null, open);
|
||||
label.hide();
|
||||
return;
|
||||
}
|
||||
open = openAfterPick(open, picked);
|
||||
const [pickedX, pickedY] = params.toScreen(planned[picked]);
|
||||
icon.update([pickedX + rect.left, pickedY + rect.top], open);
|
||||
if (!open) {
|
||||
label.hide();
|
||||
return;
|
||||
}
|
||||
const rect = params.canvas.getBoundingClientRect();
|
||||
// 가둠 = 도구줄 아래 · 열린 횡단 판 왼쪽 · 뜬 종단 판 위(505-3 · 판 밑에 깔려 가리지 않게)
|
||||
const main = params.canvas.closest(".design-page__main") ?? params.canvas.parentElement;
|
||||
const box = (selector: string): DOMRect | null => {
|
||||
@@ -282,5 +301,13 @@ export function createCurveBar(params: CurveBarParams): CurveBar {
|
||||
});
|
||||
}
|
||||
|
||||
return { label, sync, noteFit };
|
||||
return {
|
||||
label,
|
||||
destroy: () => {
|
||||
icon.destroy();
|
||||
label.destroy();
|
||||
},
|
||||
sync,
|
||||
noteFit,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/* =============================================================================
|
||||
* B05_Profile_UI_RouteEdit_CurveIcon.ts
|
||||
* 교각점 정보 아이콘(445-C1) — 교각점을 고른 채 마우스가 근처일 때만 교각점 옆에 뜨는 작은 단추.
|
||||
* 누르면 정보 창이 열림(멀어져도 유지) · 멀어지면 0.7 s 뒤 숨김 · 터치(마우스 없음)는 고르면 늘 보임.
|
||||
* 규칙은 `logic/B05_Profile_RouteEdit_CurveIcon` · 창 열림 기억은 `_CurveBar`.
|
||||
* ========================================================================== */
|
||||
|
||||
import { createIconButton } from "@ui/ui_template_elements";
|
||||
import { iconVisible, isNear } from "./logic/B05_Profile_RouteEdit_CurveIcon";
|
||||
import "./B05_Profile_UI_Style_RouteEdit_CurveIcon.css";
|
||||
|
||||
/** 멀어진 뒤 아이콘이 남는 시간(ms) — 아이콘으로 옮겨 갈 틈(등고선 자물쇠와 같은 값). */
|
||||
const HIDE_DELAY_MS = 700;
|
||||
/** 아이콘은 교각점 오른쪽 위로 이만큼(px). */
|
||||
const OFFSET_PX = 14;
|
||||
|
||||
export interface CurveIcon {
|
||||
/** 고름 · 창 열림 · 교각점 화면 자리(클라이언트 좌표 · 고름 없으면 null)를 알린다. */
|
||||
update: (anchor: [number, number] | null, open: boolean) => void;
|
||||
destroy: () => void;
|
||||
}
|
||||
|
||||
export function createCurveIcon(canvas: HTMLCanvasElement, onOpen: () => void): CurveIcon {
|
||||
const wrap = canvas.parentElement ?? document.body;
|
||||
const button = createIconButton({
|
||||
icon: "adjustments",
|
||||
title: "교각점 정보 열기",
|
||||
size: 24,
|
||||
variant: "glass",
|
||||
});
|
||||
button.classList.add("b05-curveicon");
|
||||
button.dataset.act = "curve-icon";
|
||||
button.hidden = true;
|
||||
wrap.append(button);
|
||||
|
||||
let anchor: [number, number] | null = null;
|
||||
let open = false;
|
||||
let touch = false;
|
||||
let mouse: [number, number] | null = null;
|
||||
let overIcon = false;
|
||||
let pressing = false;
|
||||
let hideTimer = 0;
|
||||
|
||||
const keep = (): void => {
|
||||
window.clearTimeout(hideTimer);
|
||||
hideTimer = 0;
|
||||
};
|
||||
const hideNow = (): void => {
|
||||
keep();
|
||||
button.hidden = true;
|
||||
};
|
||||
const hideLater = (): void => {
|
||||
if (hideTimer) return;
|
||||
hideTimer = window.setTimeout(() => {
|
||||
hideTimer = 0;
|
||||
button.hidden = true;
|
||||
}, HIDE_DELAY_MS);
|
||||
};
|
||||
|
||||
/** 지금 상태로 보임 · 숨김을 다시 정함 — 멀면 바로 숨기지 않고 틈 뒤. */
|
||||
const refresh = (): void => {
|
||||
if (!anchor || open || pressing) return hideNow();
|
||||
const distance = mouse ? Math.hypot(mouse[0] - anchor[0], mouse[1] - anchor[1]) : Infinity;
|
||||
if (!iconVisible(0, false, isNear(distance, overIcon), touch)) {
|
||||
if (!button.hidden) hideLater();
|
||||
return;
|
||||
}
|
||||
keep();
|
||||
const origin = wrap.getBoundingClientRect();
|
||||
button.style.left = `${Math.round(anchor[0] - origin.left + OFFSET_PX)}px`;
|
||||
button.style.top = `${Math.round(anchor[1] - origin.top - OFFSET_PX - 24)}px`;
|
||||
button.hidden = false;
|
||||
};
|
||||
|
||||
canvas.addEventListener("pointermove", (event) => {
|
||||
touch = event.pointerType === "touch";
|
||||
mouse = [event.clientX, event.clientY];
|
||||
if (event.buttons === 0) refresh();
|
||||
});
|
||||
canvas.addEventListener("pointerdown", (event) => {
|
||||
touch = event.pointerType === "touch";
|
||||
mouse = [event.clientX, event.clientY];
|
||||
pressing = true;
|
||||
refresh();
|
||||
});
|
||||
// 놓은 뒤 다시 판정 — 고른 자리가 바뀌었으면 `update` 가 곧 이어서 부른다.
|
||||
const release = (): void => {
|
||||
pressing = false;
|
||||
refresh();
|
||||
};
|
||||
canvas.addEventListener("pointerup", release);
|
||||
canvas.addEventListener("pointercancel", release);
|
||||
canvas.addEventListener("wheel", hideNow, { passive: true });
|
||||
canvas.addEventListener("pointerleave", () => {
|
||||
mouse = null;
|
||||
if (!overIcon && !button.hidden) hideLater();
|
||||
});
|
||||
button.addEventListener("pointerenter", () => {
|
||||
overIcon = true;
|
||||
keep();
|
||||
});
|
||||
button.addEventListener("pointerleave", () => {
|
||||
overIcon = false;
|
||||
hideLater();
|
||||
});
|
||||
button.addEventListener("pointerdown", (event) => event.stopPropagation());
|
||||
button.addEventListener("click", (event) => {
|
||||
event.stopPropagation();
|
||||
hideNow();
|
||||
onOpen();
|
||||
});
|
||||
|
||||
return {
|
||||
update: (nextAnchor, nextOpen) => {
|
||||
anchor = nextAnchor;
|
||||
open = nextOpen;
|
||||
refresh();
|
||||
},
|
||||
destroy: () => {
|
||||
hideNow();
|
||||
button.remove();
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/* 교각점 정보 아이콘(445-C1) — 교각점 옆에 뜨는 작은 단추 · 자리는 `_CurveIcon.ts` 가 잡음. 꼴은 부품 상자 아이콘 단추 유리. */
|
||||
.b05-curveicon {
|
||||
position: absolute;
|
||||
z-index: 1; /* 지도 위 표지 층 — 도구줄 · 횡단 판(2) 밑(등고선 자물쇠와 같은 층) */
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/* =============================================================================
|
||||
* B05_Profile_RouteEdit_CurveIcon.ts
|
||||
* 교각점 정보 창 열림 · 근처 아이콘 규칙(445-C1) — 순수 함수. 화면 쪽은 `_UI_RouteEdit_CurveIcon`.
|
||||
* ========================================================================== */
|
||||
|
||||
/** 교각점에서 이만큼(px) 안에 마우스가 있으면 「근처」(노드 잡기 12 px 의 세 배쯤). */
|
||||
export const NEAR_PX = 40;
|
||||
|
||||
/** 마우스가 교각점 근처인가 — 아이콘 위에 있어도 근처(`overIcon`). */
|
||||
export function isNear(distancePx: number, overIcon: boolean): boolean {
|
||||
return overIcon || distancePx <= NEAR_PX;
|
||||
}
|
||||
|
||||
/**
|
||||
* 고른 교각점이 바뀐 뒤 정보 창이 열려 있나 — 열린 채 다른 교각점을 고르면 새 교각점으로 열린 채 ·
|
||||
* 닫혀 있었으면 닫힌 채 · 고름이 풀리면 닫힘.
|
||||
*/
|
||||
export function openAfterPick(open: boolean, picked: number): boolean {
|
||||
return open && picked >= 0;
|
||||
}
|
||||
|
||||
/** 아이콘이 보이나 — 고름 있음 · 창 닫힘 · 근처(터치는 마우스가 없어 늘 근처). */
|
||||
export function iconVisible(picked: number, open: boolean, near: boolean, touch: boolean): boolean {
|
||||
return picked >= 0 && !open && (near || touch);
|
||||
}
|
||||
@@ -9,6 +9,7 @@ const { makeLoader } = require("./helper_ts_stub_loader.cjs");
|
||||
const ROOT = path.join(__dirname, "..", "..");
|
||||
const B05 = path.join(ROOT, "B05_Profile");
|
||||
let handlers = null;
|
||||
let iconOpen = null; // 445-C1 아이콘 누름 흉내 — 정보 창을 열어야 `show` 가 불림
|
||||
const shows = [];
|
||||
const STUBS = {
|
||||
"./B05_Profile_UI_RouteEdit_Label": {
|
||||
@@ -18,6 +19,12 @@ const STUBS = {
|
||||
},
|
||||
centerDirectionOf: () => null,
|
||||
},
|
||||
"./B05_Profile_UI_RouteEdit_CurveIcon": {
|
||||
createCurveIcon: (_canvas, onOpen) => {
|
||||
iconOpen = onOpen;
|
||||
return { update: () => {}, destroy: () => {} };
|
||||
},
|
||||
},
|
||||
"@ui/ui_template_elements": { showToast: () => {} }, // 곡선 삭제 경고(430)
|
||||
};
|
||||
|
||||
@@ -118,6 +125,7 @@ function editor(snap) {
|
||||
const pick = (k) => {
|
||||
state.picked = k;
|
||||
made.sync();
|
||||
iconOpen(); // 아이콘 누름 = 열림(열린 채 다른 교각점 = 이어서 열림)
|
||||
};
|
||||
const lastShow = () => shows[shows.length - 1];
|
||||
return { state, status, handlers: () => handlers, drawn, arcOf, mc, pick, lastShow, markEdited };
|
||||
|
||||
@@ -17,6 +17,9 @@ const STUBS = {
|
||||
},
|
||||
centerDirectionOf: () => null,
|
||||
},
|
||||
"./B05_Profile_UI_RouteEdit_CurveIcon": {
|
||||
createCurveIcon: () => ({ update: () => {}, destroy: () => {} }), // 445-C1 근처 아이콘(화면 없음)
|
||||
},
|
||||
"@ui/ui_template_elements": { showToast: () => {} }, // 곡선 삭제 경고(430)
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
"""445-C1 B05 교각점 정보 = 근처 아이콘 — 규칙(`logic/B05_Profile_RouteEdit_CurveIcon.ts`)을 Node 로 돌려 판정 +
|
||||
배선 글 대조(`_CurveBar` 가 열림을 봄 · 닫기는 고름도 풂 · 아이콘은 부품 상자 유리 꼴).
|
||||
|
||||
./venv/Scripts/python.exe -m pytest resources/tester/test_445_curve_icon.py -q
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
B05 = ROOT / "B05_Profile"
|
||||
LOGIC = B05 / "logic" / "B05_Profile_RouteEdit_CurveIcon.ts"
|
||||
TS = ROOT / "config" / "node_modules" / "typescript"
|
||||
|
||||
DRIVER = r"""
|
||||
const fs = require("fs");
|
||||
const ts = require(process.argv[1]);
|
||||
const js = ts.transpileModule(fs.readFileSync(process.argv[2], "utf8"), {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
||||
}).outputText;
|
||||
const m = { exports: {} };
|
||||
new Function("exports", "module", js)(m.exports, m);
|
||||
const L = m.exports;
|
||||
console.log(JSON.stringify({
|
||||
near: L.NEAR_PX,
|
||||
nearIn: L.isNear(L.NEAR_PX, false),
|
||||
nearOut: L.isNear(L.NEAR_PX + 1, false),
|
||||
nearOnIcon: L.isNear(500, true),
|
||||
keepOpenOther: L.openAfterPick(true, 3),
|
||||
closedStaysClosed: L.openAfterPick(false, 3),
|
||||
releaseCloses: L.openAfterPick(true, -1),
|
||||
noPick: L.iconVisible(-1, false, true, false),
|
||||
picked_near: L.iconVisible(2, false, true, false),
|
||||
picked_far: L.iconVisible(2, false, false, false),
|
||||
picked_far_touch: L.iconVisible(2, false, false, true),
|
||||
open_near: L.iconVisible(2, true, true, false),
|
||||
}));
|
||||
"""
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def got() -> dict:
|
||||
run = subprocess.run( # noqa: S603 — 고정 실행 파일
|
||||
["node", "-e", DRIVER, str(TS), str(LOGIC)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
timeout=60,
|
||||
)
|
||||
assert run.returncode == 0, run.stderr
|
||||
return json.loads(run.stdout)
|
||||
|
||||
|
||||
def test_근처_판정(got) -> None:
|
||||
assert got["near"] == 40
|
||||
assert got["nearIn"] is True and got["nearOut"] is False
|
||||
assert got["nearOnIcon"] is True # 아이콘 위는 멀어도 근처
|
||||
|
||||
|
||||
def test_창_열림_이어감(got) -> None:
|
||||
assert got["keepOpenOther"] is True # 열린 채 다른 교각점 = 열린 채
|
||||
assert got["closedStaysClosed"] is False # 닫힘 = 닫힌 채
|
||||
assert got["releaseCloses"] is False # 고름 풂 = 닫힘
|
||||
|
||||
|
||||
def test_아이콘_보임(got) -> None:
|
||||
assert got["noPick"] is False
|
||||
assert got["picked_near"] is True and got["picked_far"] is False
|
||||
assert got["picked_far_touch"] is True # 터치 = 고르면 늘 보임
|
||||
assert got["open_near"] is False # 창이 열리면 아이콘 숨김
|
||||
|
||||
|
||||
def test_배선_글() -> None:
|
||||
bar = (B05 / "B05_Profile_UI_RouteEdit_CurveBar.ts").read_text(encoding="utf-8")
|
||||
assert "openAfterPick" in bar and "icon.update(" in bar
|
||||
assert "onClose: () => params.onUnselect()" in bar # 닫기 = 창 닫고 고름도 풂
|
||||
# 닫힘이면 창을 숨기고 아이콘만 — 창 show 보다 앞
|
||||
assert bar.index("if (!open) {") < bar.index("label.show({")
|
||||
icon = (B05 / "B05_Profile_UI_RouteEdit_CurveIcon.ts").read_text(encoding="utf-8")
|
||||
assert 'variant: "glass"' in icon and "createIconButton" in icon # 부품 상자 유리 꼴
|
||||
assert "document.createElement" not in icon.split("export function")[1] # 날 요소 없음
|
||||
@@ -78,6 +78,7 @@ def test_상태():
|
||||
assert '.ui-icon-btn:is(:hover, [data-state="hover"]):not(:disabled)' in css
|
||||
assert '.ui-icon-btn:is(:active, [data-state="active"]):not(:disabled)' in css
|
||||
assert '.ui-icon-btn[aria-pressed="true"]' in css and ".ui-icon-btn:disabled" in css
|
||||
assert '.ui-icon-btn--glass:is(:hover, [data-state="hover"]):not(:disabled)' in css # 445-C1
|
||||
src = _text(UI / "ui_template_button.ts")
|
||||
assert 'title: opts.title, "aria-label": opts.title' in src
|
||||
|
||||
|
||||
@@ -154,6 +154,20 @@
|
||||
background: var(--color-accent-soft);
|
||||
color: var(--color-accent);
|
||||
}
|
||||
/* 유리 — 그림 위에 뜸(색 = `.ui-btn--glass` 와 같은 공통 값) */
|
||||
.ui-icon-btn--glass {
|
||||
border-color: color-mix(in srgb, var(--color-border) 65%, transparent);
|
||||
border-radius: var(--radius-pills);
|
||||
background: color-mix(in srgb, var(--color-surface-raised) 72%, transparent);
|
||||
color: var(--color-text-body);
|
||||
backdrop-filter: blur(6px);
|
||||
-webkit-backdrop-filter: blur(6px);
|
||||
}
|
||||
.ui-icon-btn--glass:is(:hover, [data-state="hover"]):not(:disabled) {
|
||||
border-color: var(--color-primary);
|
||||
background: color-mix(in srgb, var(--color-primary) 82%, transparent);
|
||||
color: var(--color-primary-text);
|
||||
}
|
||||
.ui-icon-btn:is(:active, [data-state="active"]):not(:disabled) {
|
||||
translate: 0 1px;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* 부품 「단추」 · 「아이콘 단추」 · 크기 여덟 단(552-2 · 부품 모음 `#/b01-account?view=ui-kit`)
|
||||
*
|
||||
* 꼴 = 단추: 주(filled) · 보조(ghost) · 고스트 글(pill) · 위험(danger) · 위험 고스트(danger-ghost) · 유리(glass · 그림 위) · 글 링크(link)
|
||||
* 아이콘 단추: 고스트(ghost · 기본) · 선(outline) · 채움(filled) · 켜짐 = aria-pressed
|
||||
* 아이콘 단추: 고스트(ghost · 기본) · 선(outline) · 채움(filled) · 유리(glass · 그림 위) · 켜짐 = aria-pressed
|
||||
* 크기 = `data-size="18|20|22|24|28|32|36|42"`(높이 px) — 단추 · 아이콘 단추 · 입력 · 고르기 · 분할 · 탭 같은 값
|
||||
* 옛 `size: "sm"` = 18 · 크기를 안 주면 data-size 없이 옛 꼴(높이 32 · 아이콘 붙으면 34) 그대로
|
||||
* 실측 높이 = 단(꼴 · 페이지 글 크기 · 왼쪽 패널 양식과 상관없이) · `hidden` 이면 늘 숨음
|
||||
@@ -81,7 +81,7 @@ export function createButton(opts: ButtonOptions): HTMLButtonElement {
|
||||
return btn;
|
||||
}
|
||||
|
||||
export type IconButtonVariant = "ghost" | "outline" | "filled";
|
||||
export type IconButtonVariant = "ghost" | "outline" | "filled" | "glass";
|
||||
|
||||
export interface IconButtonOptions {
|
||||
/** 아이콘 이름 — 없으면 glyph */
|
||||
|
||||
@@ -279,8 +279,8 @@ export const KIT_PARTS: KitPart[] = [
|
||||
{ key: "button", name: "단추", group: "basic", kinds: "주 · 보조 · 고스트 글 · 위험 · 위험 고스트 · 유리 · 글 링크 · 여덟 단", file: "ui_template_button.ts",
|
||||
demo: (s) => (s === "error" ? null : row(...([["저장", "filled"], ["취소", "ghost"], ["보기", "pill"], ["삭제", "danger"], ["초기화", "danger-ghost"], ["사용법", "link"]] as const).map(([label, variant]) => markState(createButton({ label, variant, size: 28, onClick: noop }), s)))),
|
||||
sizes: (z) => createButton({ label: String(z), size: z, iconStart: "plus", onClick: noop }) },
|
||||
{ key: "icon-button", name: "아이콘 단추", group: "basic", kinds: "고스트 · 선 · 채움 · 켜짐(aria-pressed) · 글자 기호 · 여덟 단 · title 필수", file: "ui_template_button.ts",
|
||||
demo: (s) => (s === "error" ? null : row(markState(createIconButton({ icon: "settings", title: "설정" }), s), markState(createIconButton({ icon: "refresh", title: "새로 고침", variant: "outline" }), s), markState(createIconButton({ icon: "plus", title: "추가", variant: "filled" }), s), markState(createIconButton({ icon: "eye", title: "보기", pressed: true }), s), markState(createIconButton({ glyph: "▲", title: "위로", variant: "outline" }), s))),
|
||||
{ key: "icon-button", name: "아이콘 단추", group: "basic", kinds: "고스트 · 선 · 채움 · 유리 · 켜짐(aria-pressed) · 글자 기호 · 여덟 단 · title 필수", file: "ui_template_button.ts",
|
||||
demo: (s) => (s === "error" ? null : row(markState(createIconButton({ icon: "settings", title: "설정" }), s), markState(createIconButton({ icon: "refresh", title: "새로 고침", variant: "outline" }), s), markState(createIconButton({ icon: "plus", title: "추가", variant: "filled" }), s), markState(createIconButton({ icon: "eye", title: "보기", pressed: true }), s), markState(createIconButton({ glyph: "▲", title: "위로", variant: "outline" }), s), markState(createIconButton({ icon: "adjustments", title: "조정", variant: "glass" }), s))),
|
||||
sizes: (z) => createIconButton({ icon: "settings", title: `설정 ${z}`, size: z, variant: "outline" }) },
|
||||
{ key: "input", name: "입력", group: "basic", kinds: "글 · 수 · 단위 붙음 · 찾기 · 날짜 · 여러 줄 · 여덟 단", file: "ui_template_input.ts",
|
||||
demo: (s) => fieldDemo(s, () => { const f = createInputField({ placeholder: "폭", type: "number", unit: "m", size: 32 }); return { root: f.root, node: f.input, setError: f.setError }; }),
|
||||
|
||||
Reference in New Issue
Block a user