mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-23 18:01:08 +01:00
Styled it up a bit
This commit is contained in:
@@ -38,7 +38,7 @@ export interface EditorProps {
|
||||
className?: string;
|
||||
heightMode?: 'auto' | 'full';
|
||||
contentType?: string | null;
|
||||
forceUpdateKey?: string;
|
||||
forceUpdateKey?: string | number;
|
||||
autoFocus?: boolean;
|
||||
autoSelect?: boolean;
|
||||
defaultValue?: string | null;
|
||||
|
||||
@@ -43,6 +43,10 @@ const icons = {
|
||||
arrowUpFromDot: lucide.ArrowUpFromDotIcon,
|
||||
arrowDownToDot: lucide.ArrowDownToDotIcon,
|
||||
arrowUpDown: lucide.ArrowUpDownIcon,
|
||||
arrowDown: lucide.ArrowDownIcon,
|
||||
arrowUp: lucide.ArrowUpIcon,
|
||||
arrowBigDownDash: lucide.ArrowBigDownDashIcon,
|
||||
arrowBigUpDash: lucide.ArrowBigUpDashIcon,
|
||||
x: lucide.XIcon,
|
||||
|
||||
empty: (props: HTMLAttributes<HTMLSpanElement>) => <span {...props} />,
|
||||
@@ -51,7 +55,7 @@ const icons = {
|
||||
export interface IconProps {
|
||||
icon: keyof typeof icons;
|
||||
className?: string;
|
||||
size?: 'xs' | 'sm' | 'md';
|
||||
size?: 'xs' | 'sm' | 'md' | 'lg';
|
||||
spin?: boolean;
|
||||
}
|
||||
|
||||
@@ -61,7 +65,8 @@ export const Icon = memo(function Icon({ icon, spin, size = 'md', className }: I
|
||||
<Component
|
||||
className={classNames(
|
||||
className,
|
||||
'text-inherit',
|
||||
'text-inherit flex-shrink-0',
|
||||
size === 'lg' && 'h-5 w-5',
|
||||
size === 'md' && 'h-4 w-4',
|
||||
size === 'sm' && 'h-3.5 w-3.5',
|
||||
size === 'xs' && 'h-3 w-3',
|
||||
|
||||
109
src-web/components/core/JsonAttributeTree.tsx
Normal file
109
src-web/components/core/JsonAttributeTree.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
interface Props {
|
||||
depth?: number;
|
||||
attrValue: any;
|
||||
attrKey?: string | number;
|
||||
attrKeyJsonPath?: string;
|
||||
}
|
||||
|
||||
export const JsonAttributeTree = ({ depth = 0, attrKey, attrValue, attrKeyJsonPath }: Props) => {
|
||||
attrKeyJsonPath = attrKeyJsonPath ?? `${attrKey}`;
|
||||
|
||||
const [isExpanded, setIsExpanded] = useState(depth === 0);
|
||||
const toggleExpanded = () => setIsExpanded((v) => !v);
|
||||
|
||||
const { isExpandable, children, label, labelClassName } = useMemo<{
|
||||
isExpandable: boolean;
|
||||
children: ReactNode;
|
||||
label?: string;
|
||||
labelClassName?: string;
|
||||
}>(() => {
|
||||
const jsonType = Object.prototype.toString.call(attrValue);
|
||||
if (jsonType === '[object Object]') {
|
||||
return {
|
||||
children: isExpanded
|
||||
? Object.keys(attrValue)
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
.flatMap((k) => (
|
||||
<JsonAttributeTree
|
||||
depth={depth + 1}
|
||||
attrValue={attrValue[k]}
|
||||
attrKey={k}
|
||||
attrKeyJsonPath={joinObjectKey(attrKeyJsonPath, k)}
|
||||
/>
|
||||
))
|
||||
: null,
|
||||
isExpandable: true,
|
||||
label: isExpanded ? undefined : `{⋯}`,
|
||||
labelClassName: 'text-gray-500',
|
||||
};
|
||||
} else if (jsonType === '[object Array]') {
|
||||
return {
|
||||
children: isExpanded
|
||||
? attrValue.flatMap((v: any, i: number) => (
|
||||
<JsonAttributeTree
|
||||
depth={depth + 1}
|
||||
attrValue={v}
|
||||
attrKey={i}
|
||||
attrKeyJsonPath={joinArrayKey(attrKeyJsonPath, i)}
|
||||
/>
|
||||
))
|
||||
: null,
|
||||
isExpandable: true,
|
||||
label: isExpanded ? undefined : `[⋯]`,
|
||||
labelClassName: 'text-gray-500',
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
children: null,
|
||||
isExpandable: false,
|
||||
label: jsonType === '[object String]' ? `"${attrValue}"` : `${attrValue}`,
|
||||
labelClassName: classNames(
|
||||
jsonType === '[object Boolean]' && 'text-pink-600',
|
||||
jsonType === '[object Number]' && 'text-blue-600',
|
||||
jsonType === '[object String]' && 'text-yellow-600',
|
||||
jsonType === '[object Null]' && 'text-red-600',
|
||||
),
|
||||
};
|
||||
}
|
||||
}, [attrValue, attrKeyJsonPath, isExpanded, depth]);
|
||||
|
||||
return (
|
||||
<div className={classNames(depth === 0 && '-ml-4', 'font-mono text-xs')}>
|
||||
<div className="flex items-center">
|
||||
{depth === 0 ? null : isExpandable ? (
|
||||
<button className="relative flex items-center pl-4" onClick={toggleExpanded}>
|
||||
<Icon
|
||||
className={classNames(
|
||||
'left-0 absolute transition-transform text-gray-500 flex gap-1 items-center',
|
||||
isExpanded ? 'rotate-90' : '',
|
||||
)}
|
||||
size="xs"
|
||||
icon="chevronRight"
|
||||
/>
|
||||
<span className="text-violet-600 mr-1.5 whitespace-nowrap">{attrKey}:</span>
|
||||
</button>
|
||||
) : (
|
||||
<span className="text-violet-600 mr-1.5 pl-4 whitespace-nowrap">{attrKey}:</span>
|
||||
)}
|
||||
<span className={classNames(labelClassName, 'select-text')}>{label}</span>
|
||||
</div>
|
||||
{children && <div className="ml-4 whitespace-nowrap">{children}</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function joinObjectKey(baseKey: string | undefined, key: string): string {
|
||||
const quotedKey = key.match(/^[a-z0-9_]+$/i) ? key : `\`${key}\``;
|
||||
|
||||
if (baseKey == null) return quotedKey;
|
||||
else return `${baseKey}.${quotedKey}`;
|
||||
}
|
||||
|
||||
function joinArrayKey(baseKey: string | undefined, index: number): string {
|
||||
return `${baseKey ?? ''}[${index}]`;
|
||||
}
|
||||
Reference in New Issue
Block a user