feat(ui): 고르기 모달 나무 M02 나무 꼴 — 층 단추 셋 · 잎 = 마지막 이름 · 폴더 먼저 · 왼쪽 폭 320px(173-2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jonfdxn1Xit9bdkNJpE4G
This commit is contained in:
@@ -0,0 +1,62 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""PLAN 173-2 — 고르기 모달 왼쪽 나무 = M02 마스터 나무 꼴.
|
||||||
|
|
||||||
|
자료 쪽(잎 글 · 폴더 먼저 · 툴팁)은 Node 시험(`ui/item-picker-data.test.ts`)으로,
|
||||||
|
층 단추 셋 · 뿌리 줄 없음 · 빈 자리 메뉴 · 폭 320px 은 소스로 본다.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
UI = os.path.join(ROOT, "ui_template")
|
||||||
|
|
||||||
|
|
||||||
|
def _text(name: str) -> str:
|
||||||
|
return open(os.path.join(UI, name), encoding="utf-8").read()
|
||||||
|
|
||||||
|
|
||||||
|
def test_자료_시험_통과_잎_글_폴더_먼저():
|
||||||
|
proc = subprocess.run(
|
||||||
|
[
|
||||||
|
"node",
|
||||||
|
"--experimental-strip-types",
|
||||||
|
os.path.join(ROOT, "resources", "tester", "ui", "item-picker-data.test.ts"),
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
encoding="utf-8",
|
||||||
|
timeout=60,
|
||||||
|
)
|
||||||
|
assert proc.returncode == 0, proc.stderr
|
||||||
|
assert "통과" in proc.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def test_층_단추_셋_뿌리_줄_없음():
|
||||||
|
ts = _text("ui_template_item_picker.ts")
|
||||||
|
assert "children: [search.root, layerBar, treeHost]" in ts
|
||||||
|
assert "...PICKER_LAYERS.map((one) =>" in ts
|
||||||
|
assert 'variant: one === layer ? "filled" : "ghost"' in ts
|
||||||
|
# 고른 층의 자식부터 그림(뿌리 줄 없음)
|
||||||
|
assert "groups.find((group) => group.id === layer)?.children ?? []" in ts
|
||||||
|
# 처음 층 = 지금 항목 층 · 없으면 개인 · 없으면 마스터
|
||||||
|
assert "layer = layerOf(start);" in ts
|
||||||
|
# 공용 줄 부품 그대로
|
||||||
|
assert "createTreeRow(node, caret, depth" in ts
|
||||||
|
|
||||||
|
|
||||||
|
def test_개인_맨_위_새_항목은_빈_자리_오른쪽_클릭():
|
||||||
|
ts = _text("ui_template_item_picker.ts")
|
||||||
|
assert 'treeHost.addEventListener("contextmenu"' in ts
|
||||||
|
assert 'closest(".ui-tree__row")' in ts
|
||||||
|
assert "folder: { layer, path: [] }" in ts
|
||||||
|
|
||||||
|
|
||||||
|
def test_왼쪽_칸_폭_320_테두리_없음():
|
||||||
|
css = _text("ui_template_item_picker.css")
|
||||||
|
assert "grid-template-columns: var(--wf-left-panel-width) minmax(320px, 1fr);" in css
|
||||||
|
tree = css[css.index(".ui-item-picker__tree {") :]
|
||||||
|
tree = tree[: tree.index("}")]
|
||||||
|
assert "border" not in tree
|
||||||
|
theme = _text("ui_template_theme.css")
|
||||||
|
assert "--wf-left-panel-width: 320px;" in theme
|
||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
folderLines,
|
folderLines,
|
||||||
isCurrent,
|
isCurrent,
|
||||||
itemName,
|
itemName,
|
||||||
|
leafLabel,
|
||||||
pathIds,
|
pathIds,
|
||||||
pathOf,
|
pathOf,
|
||||||
rowId,
|
rowId,
|
||||||
@@ -120,14 +121,14 @@ assert.equal(size?.label, "1.2m");
|
|||||||
assert.deepEqual(size?.folder, { layer: "system", path: ["돌기슭막이", "메쌓기", "1.2m"] });
|
assert.deepEqual(size?.folder, { layer: "system", path: ["돌기슭막이", "메쌓기", "1.2m"] });
|
||||||
assert.deepEqual(tree[2].folder, { layer: "personal", path: [] }); // 그룹 줄 = 빈 경로
|
assert.deepEqual(tree[2].folder, { layer: "personal", path: [] }); // 그룹 줄 = 빈 경로
|
||||||
const leaf = size?.children?.[0];
|
const leaf = size?.children?.[0];
|
||||||
assert.equal(leaf?.label, "돌기슭막이 메쌓기 1.2m 기초유");
|
assert.equal(leaf?.label, "기초유"); // 잎 글 = 경로 마지막 이름(M02 나무 꼴 · 173-2)
|
||||||
assert.equal(leaf?.tip, "마스터 · SS000001"); // 툴팁 = 층 이름 + 키
|
assert.equal(leaf?.tip, "돌기슭막이 메쌓기 1.2m 기초유 · 마스터 · SS000001"); // 툴팁 = 전체 이름 · 층 · 키
|
||||||
assert.equal(leaf?.count, null);
|
assert.equal(leaf?.count, null);
|
||||||
assert.ok(leaf?.row);
|
assert.ok(leaf?.row);
|
||||||
|
|
||||||
// 경로가 짧은 항목은 얕은 폴더에(옹벽 H=2m = 폴더 옹벽 · 잎 「옹벽 H=2m」)
|
// 경로가 짧은 항목은 얕은 폴더에(옹벽 H=2m = 폴더 옹벽 · 잎 「H=2m」)
|
||||||
assert.equal(tree[1].children?.[1].label, "옹벽");
|
assert.equal(tree[1].children?.[1].label, "옹벽");
|
||||||
assert.equal(tree[1].children?.[1].children?.[0].label, "옹벽 H=2m");
|
assert.equal(tree[1].children?.[1].children?.[0].label, "H=2m");
|
||||||
assert.equal(tree[1].children?.[1].children?.[0].folder, undefined); // 잎에는 folder 없음
|
assert.equal(tree[1].children?.[1].children?.[0].folder, undefined); // 잎에는 folder 없음
|
||||||
|
|
||||||
// 서버가 준 `경로` 가 종류 · 분류보다 앞 — 이름 = 경로 이음 · 자리표는 뺌
|
// 서버가 준 `경로` 가 종류 · 분류보다 앞 — 이름 = 경로 이음 · 자리표는 뺌
|
||||||
@@ -148,6 +149,31 @@ const deepTree = buildTree([deep], labels)[2];
|
|||||||
assert.equal(deepTree.children?.[0].children?.[0].children?.[0].children?.[0].label, "라");
|
assert.equal(deepTree.children?.[0].children?.[0].children?.[0].children?.[0].label, "라");
|
||||||
assert.equal(deepTree.count, 1);
|
assert.equal(deepTree.count, 1);
|
||||||
|
|
||||||
|
// 잎 글 = 마지막 이름 · 비면 열 id · 키
|
||||||
|
assert.equal(leafLabel(rows[6]), "H=2m");
|
||||||
|
assert.equal(leafLabel(fresh), "n1");
|
||||||
|
assert.equal(leafLabel({ key: "SS000009", 열: "" }), "SS000009");
|
||||||
|
|
||||||
|
// 폴더 먼저 · 잎 뒤 — 같은 갈래 안은 서버 차례 그대로(173-2)
|
||||||
|
const mixed = buildTree(
|
||||||
|
[
|
||||||
|
row("SS000501", "m1", "잎하나", ["", "", ""], "personal"),
|
||||||
|
row("SS000502", "m2", "가폴더", ["잎", "", ""], "personal"),
|
||||||
|
row("SS000503", "m3", "잎둘", ["", "", ""], "personal"),
|
||||||
|
row("SS000504", "m4", "나폴더", ["잎", "", ""], "personal"),
|
||||||
|
],
|
||||||
|
labels,
|
||||||
|
)[2];
|
||||||
|
assert.deepEqual(
|
||||||
|
mixed.children?.map((k) => [k.label, !!k.row]),
|
||||||
|
[
|
||||||
|
["가폴더", false],
|
||||||
|
["나폴더", false],
|
||||||
|
["잎하나", true],
|
||||||
|
["잎둘", true],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
// 빈 그룹도 한 줄은 남음(회사 없는 사람의 회사 · 개인 그룹)
|
// 빈 그룹도 한 줄은 남음(회사 없는 사람의 회사 · 개인 그룹)
|
||||||
const onlySystem = buildTree(
|
const onlySystem = buildTree(
|
||||||
rows.filter((r) => r.layer === "system"),
|
rows.filter((r) => r.layer === "system"),
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
.ui-item-picker__cols {
|
.ui-item-picker__cols {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(220px, 3fr) minmax(320px, 9fr);
|
/* 왼쪽 = M02 좌측 패널 폭(320px · 173-2) */
|
||||||
|
grid-template-columns: var(--wf-left-panel-width) minmax(320px, 1fr);
|
||||||
gap: var(--spacing-12);
|
gap: var(--spacing-12);
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
@@ -17,18 +18,28 @@
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-item-picker__layers {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 나무 상자 = M02 나무 꼴(테두리 없음 · 긴 이름은 줄바꿈 · 173-2) */
|
||||||
.ui-item-picker__tree {
|
.ui-item-picker__tree {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
padding: var(--spacing-4);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-item-picker__tree .ui-tree__row {
|
||||||
|
min-width: 0;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
.ui-item-picker__detail {
|
.ui-item-picker__detail {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*
|
*
|
||||||
* 26-3b — 카드 · 편집 · [재적용] · [개인에 저장] 은 **고르는 구조물의 복사본**(`structureId`) 기준.
|
* 26-3b — 카드 · 편집 · [재적용] · [개인에 저장] 은 **고르는 구조물의 복사본**(`structureId`) 기준.
|
||||||
* 나무 개인 그룹 · 폴더를 오른쪽 클릭하면 「새 항목」(프로젝트에서 새 항목 → 개인 층에도 만들고 폼에 지정).
|
* 나무 개인 그룹 · 폴더를 오른쪽 클릭하면 「새 항목」(프로젝트에서 새 항목 → 개인 층에도 만들고 폼에 지정).
|
||||||
|
* 173-2 — 나무 = M02 나무 꼴: 층 단추 셋(하나만 보임 · 뿌리 줄 없음) · 개인 맨 위 「새 항목」 = 빈 자리 오른쪽 클릭.
|
||||||
* ========================================================================== */
|
* ========================================================================== */
|
||||||
|
|
||||||
import "./ui_template_item_picker.css";
|
import "./ui_template_item_picker.css";
|
||||||
@@ -17,6 +18,7 @@ import {
|
|||||||
buildTree,
|
buildTree,
|
||||||
filterRows,
|
filterRows,
|
||||||
isCurrent,
|
isCurrent,
|
||||||
|
PICKER_LAYERS,
|
||||||
layerOf,
|
layerOf,
|
||||||
ownerOf,
|
ownerOf,
|
||||||
pathIds,
|
pathIds,
|
||||||
@@ -30,7 +32,13 @@ import {
|
|||||||
} from "./ui_template_item_picker_data";
|
} from "./ui_template_item_picker_data";
|
||||||
import { t } from "./ui_template_locale";
|
import { t } from "./ui_template_locale";
|
||||||
import { openModal } from "./ui_template_modal";
|
import { openModal } from "./ui_template_modal";
|
||||||
import { createTreeRow, renderTree, type TreeMenuItem, type TreeNode } from "./ui_template_tree";
|
import {
|
||||||
|
createTreeRow,
|
||||||
|
openTreeMenu,
|
||||||
|
renderTree,
|
||||||
|
type TreeMenuItem,
|
||||||
|
type TreeNode,
|
||||||
|
} from "./ui_template_tree";
|
||||||
import { askFields } from "./ui_template_item_picker_ask";
|
import { askFields } from "./ui_template_item_picker_ask";
|
||||||
import { projectItems, type CreatedItem } from "./ui_template_item_picker_project";
|
import { projectItems, type CreatedItem } from "./ui_template_item_picker_project";
|
||||||
import { createItemDetail, type EditRequest } from "./ui_template_item_picker_detail";
|
import { createItemDetail, type EditRequest } from "./ui_template_item_picker_detail";
|
||||||
@@ -119,11 +127,14 @@ export function openItemPicker(opts: ItemPickerOptions): Promise<ItemPickerResul
|
|||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
let rows: PickerRow[] = [];
|
let rows: PickerRow[] = [];
|
||||||
let chosen: PickerRow | null = null;
|
let chosen: PickerRow | null = null;
|
||||||
|
/** 보이는 층 하나(M02 층 단추 꼴) */
|
||||||
|
let layer: PickerLayer = "personal";
|
||||||
/** 접었다 펼친 것을 다시 그려도 남기려고 펼친 줄 id 를 기억 */
|
/** 접었다 펼친 것을 다시 그려도 남기려고 펼친 줄 id 를 기억 */
|
||||||
const opened = new Set<string>();
|
const opened = new Set<string>();
|
||||||
const leafButtons = new Map<string, HTMLElement>();
|
const leafButtons = new Map<string, HTMLElement>();
|
||||||
|
|
||||||
const search = createInputField({ placeholder: t("M02_PickerSearch"), compact: true });
|
const search = createInputField({ placeholder: t("M02_PickerSearch"), compact: true });
|
||||||
|
const layerBar = el("div", { className: "ui-item-picker__layers" });
|
||||||
const treeHost = el("div", { className: "ui-item-picker__tree" });
|
const treeHost = el("div", { className: "ui-item-picker__tree" });
|
||||||
const detailView = createItemDetail({
|
const detailView = createItemDetail({
|
||||||
projectId: opts.projectId ?? null,
|
projectId: opts.projectId ?? null,
|
||||||
@@ -138,7 +149,10 @@ export function openItemPicker(opts: ItemPickerOptions): Promise<ItemPickerResul
|
|||||||
const cols = el("div", {
|
const cols = el("div", {
|
||||||
className: "ui-item-picker__cols",
|
className: "ui-item-picker__cols",
|
||||||
children: [
|
children: [
|
||||||
el("div", { className: "ui-item-picker__left", children: [search.root, treeHost] }),
|
el("div", {
|
||||||
|
className: "ui-item-picker__left",
|
||||||
|
children: [search.root, layerBar, treeHost],
|
||||||
|
}),
|
||||||
detailView.root,
|
detailView.root,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@@ -198,20 +212,42 @@ export function openItemPicker(opts: ItemPickerOptions): Promise<ItemPickerResul
|
|||||||
children: node.children?.map((child) => toTree(child, searching)),
|
children: node.children?.map((child) => toTree(child, searching)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 층 단추 셋 — 고른 층 = filled · 찾는 중엔 층마다 맞은 수 */
|
||||||
|
const paintLayers = (groups: PickNode[], searching: boolean): void => {
|
||||||
|
layerBar.replaceChildren(
|
||||||
|
...PICKER_LAYERS.map((one) => {
|
||||||
|
const name = labels().layers[one];
|
||||||
|
const count = groups.find((group) => group.id === one)?.count ?? 0;
|
||||||
|
const button = createButton({
|
||||||
|
label: searching ? `${name} (${count})` : name,
|
||||||
|
variant: one === layer ? "filled" : "ghost",
|
||||||
|
onClick: () => {
|
||||||
|
layer = one;
|
||||||
|
paintTree();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
button.setAttribute("aria-pressed", String(one === layer));
|
||||||
|
return button;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const paintTree = (): void => {
|
const paintTree = (): void => {
|
||||||
leafButtons.clear();
|
leafButtons.clear();
|
||||||
const text = search.input.value;
|
const text = search.input.value;
|
||||||
const searching = text.trim() !== "";
|
const searching = text.trim() !== "";
|
||||||
const groups = buildTree(filterRows(rows, text), labels())
|
const groups = buildTree(filterRows(rows, text), labels());
|
||||||
// 찾는 중에는 결과가 없는 그룹을 감춤
|
paintLayers(groups, searching);
|
||||||
.filter((group) => !searching || (group.count ?? 0) > 0)
|
// 뿌리 줄 없음 — 고른 층의 폴더 · 항목부터(M02 29-6 꼴)
|
||||||
.map((group) => toTree(group, searching));
|
const nodes = (groups.find((group) => group.id === layer)?.children ?? []).map((node) =>
|
||||||
if (!groups.length) {
|
toTree(node, searching),
|
||||||
treeHost.replaceChildren(hint(t("M02_PickerNoMatch")));
|
);
|
||||||
|
if (!nodes.length) {
|
||||||
|
treeHost.replaceChildren(hint(t(searching ? "M02_PickerNoMatch" : "M02_TreeEmpty")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
treeHost.replaceChildren(
|
treeHost.replaceChildren(
|
||||||
...renderTree(groups, (node, caret, depth, onClick) => {
|
...renderTree(nodes, (node, caret, depth, onClick) => {
|
||||||
const button = createTreeRow(node, caret, depth, () => {
|
const button = createTreeRow(node, caret, depth, () => {
|
||||||
onClick();
|
onClick();
|
||||||
if (!node.children?.length) return;
|
if (!node.children?.length) return;
|
||||||
@@ -243,9 +279,10 @@ export function openItemPicker(opts: ItemPickerOptions): Promise<ItemPickerResul
|
|||||||
if (start) {
|
if (start) {
|
||||||
for (const id of pathIds(start, labels())) opened.add(id);
|
for (const id of pathIds(start, labels())) opened.add(id);
|
||||||
chosen = start;
|
chosen = start;
|
||||||
|
layer = layerOf(start);
|
||||||
} else {
|
} else {
|
||||||
// 지정된 게 없으면 개인(142-1 · M02 기본 층 134-4 와 같게) — 개인 항목이 없으면(회사 없음 등) 마스터
|
// 지정된 게 없으면 개인(142-1 · M02 기본 층 134-4 와 같게) — 개인 항목이 없으면(회사 없음 등) 마스터
|
||||||
opened.add(rows.some((row) => layerOf(row) === "personal") ? "personal" : "system");
|
layer = rows.some((row) => layerOf(row) === "personal") ? "personal" : "system";
|
||||||
}
|
}
|
||||||
paintTree();
|
paintTree();
|
||||||
paintDetail();
|
paintDetail();
|
||||||
@@ -253,6 +290,14 @@ export function openItemPicker(opts: ItemPickerOptions): Promise<ItemPickerResul
|
|||||||
};
|
};
|
||||||
|
|
||||||
search.input.addEventListener("input", paintTree);
|
search.input.addEventListener("input", paintTree);
|
||||||
|
// 빈 자리 오른쪽 클릭 = 고른 층 맨 위 메뉴(개인만 「새 항목」 · 줄 위는 줄 메뉴)
|
||||||
|
treeHost.addEventListener("contextmenu", (event) => {
|
||||||
|
if ((event.target as HTMLElement).closest(".ui-tree__row")) return;
|
||||||
|
const items = menuOf({ id: layer, label: "", count: 0, folder: { layer, path: [] } });
|
||||||
|
if (!items) return;
|
||||||
|
event.preventDefault();
|
||||||
|
openTreeMenu(event, items());
|
||||||
|
});
|
||||||
applyButton.addEventListener(
|
applyButton.addEventListener(
|
||||||
"click",
|
"click",
|
||||||
() => chosen && finish({ action: "apply", item: toSummary(chosen) }),
|
() => chosen && finish({ action: "apply", item: toSummary(chosen) }),
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ export function pathOf(row: PickerRow): string[] {
|
|||||||
/** 보이는 항목 이름 — 경로를 이음(비면 열 id · 키) */
|
/** 보이는 항목 이름 — 경로를 이음(비면 열 id · 키) */
|
||||||
export const itemName = (row: PickerRow): string => pathOf(row).join(" ") || row.열 || row.key;
|
export const itemName = (row: PickerRow): string => pathOf(row).join(" ") || row.열 || row.key;
|
||||||
|
|
||||||
|
/** 나무 잎 글 — 경로 마지막 이름(M02 나무와 같게 · 173-2) · 비면 열 id · 키 */
|
||||||
|
export const leafLabel = (row: PickerRow): string => pathOf(row).at(-1) || row.열 || row.key;
|
||||||
|
|
||||||
/** 마우스를 올렸을 때 — 층 이름 + 키 */
|
/** 마우스를 올렸을 때 — 층 이름 + 키 */
|
||||||
export const rowTip = (row: PickerRow, labels: TreeLabels): string =>
|
export const rowTip = (row: PickerRow, labels: TreeLabels): string =>
|
||||||
`${labels.layers[layerOf(row)]} · ${row.key}`;
|
`${labels.layers[layerOf(row)]} · ${row.key}`;
|
||||||
@@ -115,7 +118,13 @@ export interface TreeLabels {
|
|||||||
|
|
||||||
const folderId = (layer: PickerLayer, path: string[]): string => [layer, ...path].join("|");
|
const folderId = (layer: PickerLayer, path: string[]): string => [layer, ...path].join("|");
|
||||||
|
|
||||||
/** 그룹 → 폴더(경로 앞부분 · 깊이 자유) → 항목(잎). 서버가 준 차례를 지킴 · 빈 그룹도 한 줄 남김. */
|
/** 폴더 먼저 · 잎 뒤(M02 나무와 같게 · 173-2) — 같은 갈래 안은 서버 차례 그대로(안정 정렬) */
|
||||||
|
function foldersFirst(node: PickNode): PickNode {
|
||||||
|
node.children?.sort((a, b) => Number(!!a.row) - Number(!!b.row)).forEach(foldersFirst);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 그룹 → 폴더(경로 앞부분 · 깊이 자유) → 항목(잎). 폴더 먼저 · 빈 그룹도 한 줄 남김. */
|
||||||
export function buildTree(rows: PickerRow[], labels: TreeLabels): PickNode[] {
|
export function buildTree(rows: PickerRow[], labels: TreeLabels): PickNode[] {
|
||||||
return PICKER_LAYERS.map((layer) => {
|
return PICKER_LAYERS.map((layer) => {
|
||||||
const root: PickNode = {
|
const root: PickNode = {
|
||||||
@@ -142,8 +151,8 @@ export function buildTree(rows: PickerRow[], labels: TreeLabels): PickNode[] {
|
|||||||
});
|
});
|
||||||
parent.children?.push({
|
parent.children?.push({
|
||||||
id: rowId(row),
|
id: rowId(row),
|
||||||
label: itemName(row),
|
label: leafLabel(row),
|
||||||
tip: rowTip(row, labels),
|
tip: `${itemName(row)} · ${rowTip(row, labels)}`,
|
||||||
count: null,
|
count: null,
|
||||||
row,
|
row,
|
||||||
});
|
});
|
||||||
@@ -152,7 +161,7 @@ export function buildTree(rows: PickerRow[], labels: TreeLabels): PickNode[] {
|
|||||||
if (node) node.count = (node.count ?? 0) + 1;
|
if (node) node.count = (node.count ?? 0) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return root;
|
return foldersFirst(root);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user