Skip to content
This repository was archived by the owner on Jul 8, 2026. It is now read-only.

Commit 6f37c8f

Browse files
committed
feat: enhance property tree styling and type badge functionality
- Added new CSS variables for improved styling of property rows based on depth and odd/even states. - Refactored the property type badge rendering in JavaScript to utilize a circular design with dynamic colors based on type. - Improved the visual representation of property types in the property tree for better user experience.
1 parent 8b74696 commit 6f37c8f

2 files changed

Lines changed: 93 additions & 49 deletions

File tree

src/prop-tree.js

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -118,25 +118,42 @@ function inferType(key, value) {
118118
return 'unknown';
119119
}
120120

121-
const TYPE_ICONS = {
122-
string: 'typeString',
123-
int: 'typeInt',
124-
float: 'typeFloat',
125-
bool: 'typeBool',
126-
color: 'typeColor',
127-
object: 'typeObject',
128-
array: 'typeArray',
129-
vec2: 'typeVec2',
130-
vec3: 'typeVec3',
131-
vec4: 'typeVec4',
132-
resource: 'typeResource',
133-
soundevent: 'typeSound',
134-
null: 'typeNull',
135-
unknown: 'typeUnknown',
136-
components: 'typeVec3',
137-
readonly_string: 'typeString',
138-
float_slider_01: 'typeFloat'
121+
const TYPE_ICON_COLORS = {
122+
int: '#4A90D9',
123+
float: '#7EC8E3',
124+
string: '#E8943A',
125+
bool: '#5CB85C',
126+
color: '#9B59B6',
127+
vec2: '#9B59B6',
128+
vec3: '#9B59B6',
129+
vec4: '#9B59B6',
130+
array: '#F0C040',
131+
object: '#E05555',
132+
components: '#9B59B6',
133+
readonly_string: '#E8943A',
134+
float_slider_01: '#7EC8E3'
139135
};
136+
const TYPE_ICON_FALLBACK_COLOR = '#AAAAAA';
137+
138+
function darkenHexColor(hex, amount = 28) {
139+
if (typeof hex !== 'string') return TYPE_ICON_FALLBACK_COLOR;
140+
const clean = hex.replace('#', '');
141+
if (!/^[0-9a-fA-F]{6}$/.test(clean)) return TYPE_ICON_FALLBACK_COLOR;
142+
const clamp = (n) => Math.max(0, Math.min(255, n));
143+
const r = clamp(parseInt(clean.slice(0, 2), 16) - amount);
144+
const g = clamp(parseInt(clean.slice(2, 4), 16) - amount);
145+
const b = clamp(parseInt(clean.slice(4, 6), 16) - amount);
146+
return '#' + [r, g, b].map((v) => v.toString(16).padStart(2, '0')).join('');
147+
}
148+
149+
function paintTypeBadgeCircle(el, type) {
150+
if (!el) return;
151+
const fill = TYPE_ICON_COLORS[type] || TYPE_ICON_FALLBACK_COLOR;
152+
const border = darkenHexColor(fill, 24);
153+
el.innerHTML = '<span class="prop-type-circle" aria-hidden="true"></span>';
154+
el.style.setProperty('--type-circle-fill', fill);
155+
el.style.setProperty('--type-circle-border', border);
156+
}
140157

141158
function getActiveMode() {
142159
if (window.VDataEditorModes?.resolveActiveEditorMode) {
@@ -174,8 +191,7 @@ function buildTypeBadge(currentType, onCast) {
174191
const wrap = document.createElement('span');
175192
wrap.className = 'prop-type-icon-badge prop-type-badge-interactive';
176193
wrap.title = `Type: ${currentType} (click to change)`;
177-
const ik = TYPE_ICONS[currentType];
178-
if (ik && ICONS[ik]) wrap.innerHTML = ICONS[ik];
194+
paintTypeBadgeCircle(wrap, currentType);
179195

180196
const options = TYPE_CAST_OPTIONS[currentType];
181197
if (!options || options.length === 0) {
@@ -608,8 +624,7 @@ function buildPropRow(key, value, type, depth, parentRef, arrayIdx, propPath, hi
608624
const keyIcon = document.createElement('span');
609625
keyIcon.className = 'prop-type-icon-badge';
610626
keyIcon.title = type;
611-
const iconKey = TYPE_ICONS[type];
612-
if (iconKey && ICONS[iconKey]) keyIcon.innerHTML = ICONS[iconKey];
627+
paintTypeBadgeCircle(keyIcon, type);
613628

614629
const treeNodeIcon = document.createElement('span');
615630
treeNodeIcon.className = 'prop-tree-node-icon';

style.css

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@
7070
--color-string:rgb(255,209,153);--color-expr:rgb(255,123,125);
7171
--color-bool:var(--text-primary);--color-combo:var(--text-secondary);
7272
--color-vec3:rgb(181,255,239);--color-color:#f5c2e7;
73+
--prop-row-even-bg:transparent;
74+
--prop-row-odd-bg:rgba(255,255,255,.018);
75+
--prop-row-depth-1:rgba(255,255,255,.006);
76+
--prop-row-depth-2:rgba(255,255,255,.010);
77+
--prop-row-depth-3:rgba(255,255,255,.014);
78+
--prop-row-depth-4:rgba(255,255,255,.018);
79+
--prop-row-depth-5:rgba(255,255,255,.020);
80+
--prop-row-depth-6:rgba(255,255,255,.022);
81+
--prop-row-depth-7:rgba(255,255,255,.024);
82+
--prop-row-depth-8:rgba(255,255,255,.026);
83+
--prop-row-depth-9:rgba(255,255,255,.028);
7384
}
7485
html[data-theme="light"]{
7586
color-scheme:light;
@@ -92,6 +103,17 @@ html[data-theme="light"]{
92103
--border:#c4c4cc;
93104
--border-subtle:#d0d0d8;
94105
--scrollbar-thumb:#b0b8c4;
106+
--prop-row-even-bg:#ffffff;
107+
--prop-row-odd-bg:#f3f5f9;
108+
--prop-row-depth-1:#f8f9fc;
109+
--prop-row-depth-2:#f4f6fb;
110+
--prop-row-depth-3:#f1f3f9;
111+
--prop-row-depth-4:#eef1f8;
112+
--prop-row-depth-5:#ebeff7;
113+
--prop-row-depth-6:#e9edf6;
114+
--prop-row-depth-7:#e6ebf5;
115+
--prop-row-depth-8:#e4e9f4;
116+
--prop-row-depth-9:#e2e7f3;
95117
}
96118
html,body{height:100%;overflow:hidden;font-family:var(--font-sans);background:var(--bg-primary);color:var(--text-primary);font-size:10pt;line-height:1.5}
97119
input,select,textarea,button{font-family:inherit;font-size:inherit;color:inherit}
@@ -344,19 +366,19 @@ input,select,textarea,button{font-family:inherit;font-size:inherit;color:inherit
344366
.prop-tree-panel{display:flex;flex-direction:column;height:100%;overflow:hidden;min-height:0}
345367
.prop-tree{flex:1;overflow-y:auto;padding:4px 0;min-height:0}
346368
.prop-row{display:grid;grid-template-columns:minmax(120px,220px) 1fr;align-items:center;min-height:28px;padding:0 8px;border-bottom:1px solid var(--border-subtle);gap:8px}
347-
.prop-row.prop-row-even{background:transparent}
348-
.prop-row.prop-row-odd{background:rgba(255,255,255,.018)}
369+
.prop-row.prop-row-even{background:var(--prop-row-even-bg)}
370+
.prop-row.prop-row-odd{background:var(--prop-row-odd-bg)}
349371
.prop-row:hover{background:var(--bg-hover)!important}
350-
.prop-row[data-depth="1"]{--row-indent-tint:rgba(255,255,255,.006)}
351-
.prop-row[data-depth="2"]{--row-indent-tint:rgba(255,255,255,.010)}
352-
.prop-row[data-depth="3"]{--row-indent-tint:rgba(255,255,255,.014)}
353-
.prop-row[data-depth="4"]{--row-indent-tint:rgba(255,255,255,.018)}
354-
.prop-row[data-depth="5"]{--row-indent-tint:rgba(255,255,255,.020)}
355-
.prop-row[data-depth="6"]{--row-indent-tint:rgba(255,255,255,.022)}
356-
.prop-row[data-depth="7"]{--row-indent-tint:rgba(255,255,255,.024)}
357-
.prop-row[data-depth="8"]{--row-indent-tint:rgba(255,255,255,.026)}
358-
.prop-row[data-depth="9"]{--row-indent-tint:rgba(255,255,255,.028)}
359-
.prop-row[data-depth]:not([data-depth="0"]).prop-row-odd{background:var(--row-indent-tint,rgba(255,255,255,.018))}
372+
.prop-row[data-depth="1"]{--row-indent-tint:var(--prop-row-depth-1)}
373+
.prop-row[data-depth="2"]{--row-indent-tint:var(--prop-row-depth-2)}
374+
.prop-row[data-depth="3"]{--row-indent-tint:var(--prop-row-depth-3)}
375+
.prop-row[data-depth="4"]{--row-indent-tint:var(--prop-row-depth-4)}
376+
.prop-row[data-depth="5"]{--row-indent-tint:var(--prop-row-depth-5)}
377+
.prop-row[data-depth="6"]{--row-indent-tint:var(--prop-row-depth-6)}
378+
.prop-row[data-depth="7"]{--row-indent-tint:var(--prop-row-depth-7)}
379+
.prop-row[data-depth="8"]{--row-indent-tint:var(--prop-row-depth-8)}
380+
.prop-row[data-depth="9"]{--row-indent-tint:var(--prop-row-depth-9)}
381+
.prop-row[data-depth]:not([data-depth="0"]).prop-row-odd{background:var(--row-indent-tint,var(--prop-row-odd-bg))}
360382
.prop-row[data-depth="1"] .prop-key{padding-left:16px}
361383
.prop-row[data-depth="2"] .prop-key{padding-left:32px}
362384
.prop-row[data-depth="3"] .prop-key{padding-left:48px}
@@ -739,19 +761,26 @@ html[data-theme="light"] #cmEditor .cm-content:focus *::selection{
739761
.wc-readonly-list{opacity:.55;pointer-events:none}
740762
.wc-sys-row{margin-bottom:3px}
741763
/* ── Property type icon (key column) ────────────── */
742-
.prop-type-icon-badge{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;flex-shrink:0;color:var(--text-muted);opacity:.7}
743-
.prop-type-icon-badge svg{width:13px;height:13px}
744-
.prop-type-badge-interactive.prop-type-icon-badge{cursor:pointer;border-radius:3px;padding:1px;transition:background .1s,opacity .1s}
745-
.prop-type-badge-interactive.prop-type-icon-badge:hover{background:var(--bg-hover);opacity:1;color:var(--text-primary)}
764+
.prop-type-icon-badge{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;flex-shrink:0;opacity:.95}
765+
.prop-type-circle{
766+
width:12px;
767+
height:12px;
768+
border-radius:50%;
769+
background:var(--type-circle-fill,#AAAAAA);
770+
border:1px solid transparent;
771+
box-sizing:border-box;
772+
}
773+
.prop-type-badge-interactive.prop-type-icon-badge{
774+
cursor:pointer;
775+
border-radius:3px;
776+
padding:1px;
777+
transition:background .1s,opacity .1s;
778+
}
779+
.prop-type-badge-interactive.prop-type-icon-badge:hover{
780+
background:var(--bg-hover);
781+
opacity:1;
782+
}
783+
.prop-type-badge-interactive.prop-type-icon-badge:hover .prop-type-circle{
784+
border-color:var(--type-circle-border,#888888);
785+
}
746786
.prop-value-summary{font-size:10px;color:var(--text-muted);font-family:var(--font-mono),monospace;flex-shrink:0}
747-
.prop-row[data-type="string"] .prop-type-icon-badge{color:var(--color-string)}
748-
.prop-row[data-type="int"] .prop-type-icon-badge{color:var(--color-int)}
749-
.prop-row[data-type="float"] .prop-type-icon-badge{color:var(--color-float)}
750-
.prop-row[data-type="bool"] .prop-type-icon-badge{color:var(--color-bool)}
751-
.prop-row[data-type="color"] .prop-type-icon-badge{color:var(--color-color)}
752-
.prop-row[data-type="vec2"] .prop-type-icon-badge,.prop-row[data-type="vec3"] .prop-type-icon-badge,.prop-row[data-type="vec4"] .prop-type-icon-badge{color:var(--color-vec3)}
753-
.prop-row[data-type="resource"] .prop-type-icon-badge{color:var(--accent)}
754-
.prop-row[data-type="soundevent"] .prop-type-icon-badge{color:var(--green)}
755-
.prop-row[data-type="object"] .prop-type-icon-badge{color:var(--yellow)}
756-
.prop-row[data-type="array"] .prop-type-icon-badge{color:var(--peach)}
757-
.prop-row[data-type="null"] .prop-type-icon-badge,.prop-row[data-type="unknown"] .prop-type-icon-badge{color:var(--text-muted)}

0 commit comments

Comments
 (0)