dataflow-rs 2.1.5

A lightweight rules engine for building IFTTT-style automation and data processing pipelines in Rust. Define rules with JSONLogic conditions, execute actions, and chain workflows.
Documentation
import { useState } from 'react';
import { ChevronRight, ChevronDown } from 'lucide-react';

interface JsonViewerProps {
  data: unknown;
  initialExpanded?: boolean;
  maxStringLength?: number;
  className?: string;
}

export function JsonViewer({
  data,
  initialExpanded = false,
  maxStringLength = 50,
  className = '',
}: JsonViewerProps) {
  return (
    <div className={`df-json-viewer ${className}`}>
      <JsonNode
        data={data}
        initialExpanded={initialExpanded}
        maxStringLength={maxStringLength}
      />
    </div>
  );
}

interface JsonNodeProps {
  data: unknown;
  initialExpanded: boolean;
  maxStringLength: number;
  keyName?: string;
  isLast?: boolean;
}

function JsonNode({
  data,
  initialExpanded,
  maxStringLength,
  keyName,
  isLast = true,
}: JsonNodeProps) {
  const [expanded, setExpanded] = useState(initialExpanded);

  const renderValue = () => {
    if (data === null) {
      return <span className="df-json-null">null</span>;
    }

    if (data === undefined) {
      return <span className="df-json-undefined">undefined</span>;
    }

    if (typeof data === 'boolean') {
      return <span className="df-json-boolean">{data.toString()}</span>;
    }

    if (typeof data === 'number') {
      return <span className="df-json-number">{data}</span>;
    }

    if (typeof data === 'string') {
      const display =
        data.length > maxStringLength
          ? `${data.slice(0, maxStringLength)}...`
          : data;
      return <span className="df-json-string">"{display}"</span>;
    }

    if (Array.isArray(data)) {
      if (data.length === 0) {
        return <span className="df-json-bracket">[]</span>;
      }

      return (
        <span className="df-json-array">
          <button
            onClick={() => setExpanded(!expanded)}
            className="df-json-toggle"
          >
            {expanded ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
          </button>
          <span className="df-json-bracket">[</span>
          {!expanded && (
            <span className="df-json-preview">
              {data.length} item{data.length !== 1 ? 's' : ''}
            </span>
          )}
          {expanded && (
            <div className="df-json-children">
              {/* Index key is acceptable here: array items are immutable display data */}
              {data.map((item, index) => (
                <JsonNode
                  key={index}
                  data={item}
                  initialExpanded={false}
                  maxStringLength={maxStringLength}
                  isLast={index === data.length - 1}
                />
              ))}
            </div>
          )}
          <span className="df-json-bracket">]</span>
        </span>
      );
    }

    if (typeof data === 'object') {
      const entries = Object.entries(data as Record<string, unknown>);
      if (entries.length === 0) {
        return <span className="df-json-bracket">{'{}'}</span>;
      }

      return (
        <span className="df-json-object">
          <button
            onClick={() => setExpanded(!expanded)}
            className="df-json-toggle"
          >
            {expanded ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
          </button>
          <span className="df-json-bracket">{'{'}</span>
          {!expanded && (
            <span className="df-json-preview">
              {entries.length} key{entries.length !== 1 ? 's' : ''}
            </span>
          )}
          {expanded && (
            <div className="df-json-children">
              {entries.map(([key, value], index) => (
                <JsonNode
                  key={key}
                  keyName={key}
                  data={value}
                  initialExpanded={false}
                  maxStringLength={maxStringLength}
                  isLast={index === entries.length - 1}
                />
              ))}
            </div>
          )}
          <span className="df-json-bracket">{'}'}</span>
        </span>
      );
    }

    return <span>{String(data)}</span>;
  };

  return (
    <div className="df-json-node">
      {keyName !== undefined && (
        <>
          <span className="df-json-key">"{keyName}"</span>
          <span className="df-json-colon">: </span>
        </>
      )}
      {renderValue()}
      {!isLast && <span className="df-json-comma">,</span>}
    </div>
  );
}