Skip to content

Commit 928d0e2

Browse files
authored
feat: optimize interface (#52)
1 parent c147c6e commit 928d0e2

File tree

3 files changed

+141
-80
lines changed

3 files changed

+141
-80
lines changed

packages/ui/src/components/Tree/Tree.tsx

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -73,27 +73,40 @@ const TreeNodeComponent = component$(
7373
<div class="w-full">
7474
<div
7575
style={{ paddingLeft: `${props.level * props.gap}px` }}
76-
class={`flex w-full cursor-pointer items-center rounded-md p-1 transition-colors duration-150
77-
${
78-
isActive
79-
? 'bg-primary text-white '
80-
: 'hover:bg-primary-hover '
81-
}`}
76+
class={`flex w-full cursor-pointer items-center p-1 ${
77+
isActive
78+
? 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300 border border-emerald-300 dark:border-emerald-700/40 font-semibold'
79+
: ''
80+
}`}
8281
onClick$={handleNodeClick}
8382
>
84-
{hasChildren ? (
85-
<HiChevronUpMini
86-
class={`mr-2 h-4 w-4 flex-shrink-0 transition-transform duration-200 ${isExpanded.value ? 'rotate-90' : 'rotate-180'}`}
87-
/>
88-
) : (
89-
<div class="mr-2 w-4 flex-shrink-0"></div>
90-
)}
91-
<div class="cursor-pointer whitespace-nowrap text-sm">
92-
{props.renderNode ? (
93-
<>{props.renderNode(props.node)}</>
83+
<div
84+
class={`inline-flex items-center rounded-md px-2 py-1`}
85+
>
86+
{hasChildren ? (
87+
<HiChevronUpMini
88+
class={`mr-2 h-4 w-4 flex-shrink-0 text-muted-foreground transition-transform duration-200 ${
89+
isExpanded.value ? 'rotate-90' : 'rotate-180'
90+
}`}
91+
/>
9492
) : (
95-
`<${props.node.label || props.node.name} ${iterateProps(props.node.props! || {})}>`
93+
<div class="mr-2 w-4 flex-shrink-0"></div>
9694
)}
95+
<div class="cursor-pointer whitespace-nowrap text-sm">
96+
{props.renderNode ? (
97+
<>{props.renderNode(props.node)}</>
98+
) : (
99+
<>
100+
<span class="text-foreground/70">&lt;</span>
101+
<span class="text-violet-400 dark:text-violet-300">
102+
{props.node.label || props.node.name}
103+
</span>
104+
<span class="text-muted-foreground">
105+
{` ${iterateProps(props.node.props! || {})}`}&gt;
106+
</span>
107+
</>
108+
)}
109+
</div>
97110
</div>
98111
</div>
99112
{hasChildren ? (

packages/ui/src/features/RenderTree/RenderTree.tsx

Lines changed: 109 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,28 @@ import {
3131
} from '@devtools/kit';
3232
import { createHighlighter } from 'shiki';
3333
import { getQwikState, returnQrlData } from './data';
34+
import { HiChevronUpMini } from '@qwikest/icons/heroicons';
35+
36+
function getValueColorClass(node: TreeNode, valueText: string): string {
37+
switch (node.elementType) {
38+
case 'string':
39+
return 'text-red-400';
40+
case 'number':
41+
return 'text-green-400';
42+
case 'boolean':
43+
return 'text-amber-400';
44+
case 'function':
45+
return 'text-purple-400';
46+
case 'array':
47+
return 'text-muted-foreground';
48+
case 'object':
49+
return /(Array\[|Object\s\{|Class\s\{)/.test(valueText)
50+
? 'text-muted-foreground'
51+
: 'text-foreground';
52+
default:
53+
return 'text-foreground';
54+
}
55+
}
3456

3557
export const RenderTree = component$(() => {
3658
useStyles$(`
@@ -46,6 +68,7 @@ export const RenderTree = component$(() => {
4668

4769
const stateTree = useSignal<TreeNode[]>([]);
4870
const hookFilters = useSignal<{ key: QSeqsList; display: boolean }[]>([]);
71+
const hooksOpen = useSignal(true);
4972

5073
const qwikContainer = useComputed$(() => {
5174
try {
@@ -132,7 +155,7 @@ export const RenderTree = component$(() => {
132155
const currentTab = useSignal<'state' | 'code'>('state');
133156

134157
return (
135-
<div class="h-full w-full flex-1 overflow-hidden rounded-md border border-border">
158+
<div class="h-full w-full flex-1 overflow-hidden rounded-md border border-border bg-background">
136159
<div class="flex h-full w-full">
137160
<div class="w-1/2 overflow-hidden p-4" style={{ minWidth: '400px' }}>
138161
<Tree data={data} onNodeClick={onNodeClick}></Tree>
@@ -148,7 +171,7 @@ export const RenderTree = component$(() => {
148171
? { borderBottom: '2px solid var(--color-primary-active)' }
149172
: {}
150173
}
151-
class="border-b-2 border-b-transparent px-4 py-3 text-sm font-medium transition-all duration-300 ease-in-out"
174+
class="border-b-2 border-b-transparent px-4 py-3 text-sm font-medium transition-all duration-300 ease-in-out text-muted-foreground hover:text-foreground"
152175
>
153176
State
154177
</button>
@@ -159,74 +182,95 @@ export const RenderTree = component$(() => {
159182
? { borderBottom: '2px solid var(--color-primary-active)' }
160183
: {}
161184
}
162-
class="border-b-2 border-b-transparent px-4 py-3 text-sm font-medium transition-all duration-300 ease-in-out"
185+
class="border-b-2 border-b-transparent px-4 py-3 text-sm font-medium transition-all duration-300 ease-in-out text-muted-foreground hover:text-foreground"
163186
>
164187
Code
165188
</button>
166189
</div>
167190
</div>
168191

169192
{currentTab.value === 'state' && (
170-
<div class="mt-5 flex min-h-0 flex-1 flex-col rounded-lg border border-border bg-card-item-bg shadow-sm">
171-
<div class="flex flex-wrap items-center gap-2 border-b border-border px-2 py-2">
172-
<span class="text-xs text-muted-foreground">Hooks:</span>
173-
{hookFilters.value.map((item, idx) => (
174-
<label
175-
class="flex items-center space-x-1 rounded border border-border px-2 py-1 text-xs"
176-
key={idx}
193+
<div class="mt-5 flex min-h-0 flex-1 flex-col">
194+
<div class="rounded-lg border border-border bg-card-item-bg shadow-sm">
195+
<div class="flex items-center justify-between border-b border-border px-2 py-2">
196+
<span class="text-xs font-medium text-muted-foreground">Hooks</span>
197+
<div class="flex items-center space-x-2">
198+
<button
199+
class="text-xs text-primary hover:underline px-2 py-1"
200+
onClick$={$(() => {
201+
hookFilters.value = hookFilters.value.map((item) => {
202+
item.display = true;
203+
return item;
204+
});
205+
stateTree.value = buildTree().filter((item) =>
206+
hookFilters.value.some(
207+
(hook) => hook.key === item?.label && hook.display,
208+
),
209+
) as TreeNode[];
210+
})}
211+
>
212+
Select all
213+
</button>
214+
<button
215+
class="text-xs text-muted-foreground hover:text-foreground hover:underline px-2 py-1"
216+
onClick$={$(() => {
217+
hookFilters.value = hookFilters.value.map((item) => {
218+
item.display = false;
219+
return item;
220+
});
221+
stateTree.value = buildTree().filter((item) =>
222+
hookFilters.value.some(
223+
(hook) => hook.key === item?.label && hook.display,
224+
),
225+
) as TreeNode[];
226+
})}
177227
>
228+
Clear
229+
</button>
230+
<button
231+
aria-label="toggle hooks"
232+
onClick$={$(() => (hooksOpen.value = !hooksOpen.value))}
233+
class="rounded p-1 text-muted-foreground hover:text-foreground"
234+
>
235+
<HiChevronUpMini
236+
class={`h-4 w-4 transition-transform duration-200 ${
237+
hooksOpen.value ? 'rotate-180' : '-rotate-90'
238+
}`}
239+
/>
240+
</button>
241+
</div>
242+
</div>
243+
<div
244+
class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-x-6 gap-y-3 px-3 py-2 text-sm overflow-hidden"
245+
style={{
246+
maxHeight: hooksOpen.value ? '800px' : '0px',
247+
opacity: hooksOpen.value ? '1' : '0',
248+
transition: 'max-height 200ms ease, opacity 200ms ease',
249+
}}
250+
>
251+
{hookFilters.value.map((item, idx) => (
252+
<label key={idx} class="flex items-center">
178253
<input
254+
class="h-4 w-4 rounded-full border-border focus:ring-primary-active focus:ring-offset-0 dark:bg-[#1F2937] dark:border-[#374151]"
255+
style={{ accentColor: 'var(--color-primary-active)' }}
179256
type="checkbox"
180257
checked={item.display}
181258
onChange$={(ev) => {
182259
const target = ev.target as HTMLInputElement;
183-
184260
hookFilters.value[idx].display = target.checked;
185-
186261
stateTree.value = buildTree().filter((item) =>
187262
hookFilters.value.some(
188263
(hook) => hook.key === item?.label && hook.display,
189264
),
190265
) as TreeNode[];
191266
}}
192267
/>
193-
<span>{item.key}</span>
268+
<span class="ml-2 select-none">{item.key}</span>
194269
</label>
195270
))}
196-
<button
197-
class="ml-auto rounded border border-border px-2 py-1 text-xs"
198-
onClick$={$(() => {
199-
hookFilters.value = hookFilters.value.map((item) => {
200-
item.display = true;
201-
return item;
202-
});
203-
stateTree.value = buildTree().filter((item) =>
204-
hookFilters.value.some(
205-
(hook) => hook.key === item?.label && hook.display,
206-
),
207-
) as TreeNode[];
208-
})}
209-
>
210-
Select all
211-
</button>
212-
<button
213-
class="rounded border border-border px-2 py-1 text-xs"
214-
onClick$={$(() => {
215-
hookFilters.value = hookFilters.value.map((item) => {
216-
item.display = false;
217-
return item;
218-
});
219-
stateTree.value = buildTree().filter((item) =>
220-
hookFilters.value.some(
221-
(hook) => hook.key === item?.label && hook.display,
222-
),
223-
) as TreeNode[];
224-
})}
225-
>
226-
Clear
227-
</button>
228271
</div>
229-
<div class="min-h-0 flex-1 overflow-y-auto p-2">
272+
</div>
273+
<div class="min-h-0 flex-1 overflow-y-auto p-2 mt-4">
230274
<Tree
231275
data={stateTree}
232276
gap={10}
@@ -235,32 +279,35 @@ export const RenderTree = component$(() => {
235279
isHover
236280
renderNode={$((node) => {
237281
const label = node.label || node.name || '';
238-
const isProperty = label.split(':');
239-
return isProperty.length > 1 ? (
240-
<>
241-
<span class="text-red-300 dark:text-red-500">
242-
{isProperty[0]}
243-
</span>
244-
<span class="text-gray-700 dark:text-white">
245-
{' '}
246-
: {isProperty[1]}
247-
</span>
248-
</>
249-
) : (
250-
<span>{label}</span>
251-
);
282+
const parts = label.split(':');
283+
if (node.children && parts.length === 1) {
284+
return <span class="font-semibold text-pink-400">{label}</span>;
285+
}
286+
if (parts.length > 1) {
287+
const key = parts[0];
288+
const value = parts.slice(1).join(':').trim();
289+
const valueClass = getValueColorClass(node, value);
290+
return (
291+
<>
292+
<span class="text-blue-400">{key}</span>
293+
<span class="text-foreground/70"> : </span>
294+
<span class={valueClass}>{value}</span>
295+
</>
296+
);
297+
}
298+
return <span>{label}</span>;
252299
})}
253300
></Tree>
254301
</div>
255302
</div>
256303
)}
257304

258305
{currentTab.value === 'code' && (
259-
<div class="mt-5 min-h-0 flex-1 overflow-y-auto rounded-lg border border-border p-2 shadow-sm">
306+
<div class="mt-5 min-h-0 flex-1 overflow-y-auto rounded-lg border border-border p-2 shadow-sm">
260307
{codes.value.map((item, idx) => {
261308
return (
262309
<>
263-
<div class="mb-4 rounded-xl border border-border bg-background p-4 shadow-lg">
310+
<div class="mb-4 rounded-xl border border-border bg-card-item-bg p-4 shadow-sm hover:bg-card-item-hover-bg transition-colors">
264311
<div class="mb-2 break-all text-base font-semibold text-primary">
265312
{item.pathId}
266313
</div>

packages/ui/src/features/RenderTree/formatTreeData.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,8 @@ export function findAllQrl() {
279279
export function getQrlPath(qrl: QRL) {
280280
// CHUNK_KEY is not available when dev property isn't null or undefined
281281
if(qrl?.dev){
282-
return `/@fs${(qrl as any)?.[CHUNK_KEY]}`;
282+
// @ts-ignore
283+
return qrl?.dev.filename
283284
}
284285
return (qrl as any)?.[CHUNK_KEY];
285286
}

0 commit comments

Comments
 (0)