<commit_before>/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 */

import React from 'react';
import {
  Inspectable,
  InspectableObject,
  Metadata,
  MetadataId,
  UINode,
} from '../../../types';
import {DataInspector, Panel, styled} from 'flipper-plugin';
import {Checkbox, Col, Row} from 'antd';
import {displayableName} from '../utilities/displayableName';
import ColorInspector from './ColorInspector';
import SizeInspector from './SizeInspector';
import SpaceBoxInspector from './SpaceBoxInspector';
import BoundsInspector from './BoundsInspector';
import Coordinate3DInspector from './Coordinate3DInspector';
import CoordinateInspector from './CoordinateInspector';
import {
  AutoMarginStyle,
  EnumAttributeValueStyle,
  NumberAttributeValueStyle,
  ObjectContainerStyle,
  RowStyle,
  TextAttributeValueStyle,
} from './Styles';
import {Glyph} from 'flipper';
import {transform} from '../../../dataTransform';

const NumberValue = styled.span(NumberAttributeValueStyle);
const TextValue = styled.span(TextAttributeValueStyle);
const EnumValue = styled.span(EnumAttributeValueStyle);
const ObjectContainer = styled.div(ObjectContainerStyle);
const CenteredContentContainer = styled.div(AutoMarginStyle);

type NamedAttributeInspectorProps = {
  name: string;
};

const NamedAttributeInspector: React.FC<NamedAttributeInspectorProps> = ({
  name,
  children,
}) => {
  return (
    <Row style={RowStyle}>
      <Col span={8} style={AutoMarginStyle}>
        {name}
      </Col>
      <Col span={16}>
        <CenteredContentContainer>{children}</CenteredContentContainer>
      </Col>
    </Row>
  );
};

const ObjectAttributeInspector: React.FC<{
  metadata: Map<MetadataId, Metadata>;
  name: string;
  fields: Record<MetadataId, Inspectable>;
  level: number;
}> = ({metadata, name, fields, level}) => {
  return (
    <div style={RowStyle}>
      {name}
      {Object.keys(fields).map(function (key, _) {
        const metadataId: number = Number(key);
        const inspectableValue = fields[metadataId];
        const attributeName = metadata.get(metadataId)?.name ?? '';
        return (
          <ObjectContainer
            key={metadataId}
            style={{
              paddingLeft: level,
            }}>
            {create(metadata, attributeName, inspectableValue, level + 2)}
          </ObjectContainer>
        );
      })}
    </div>
  );
};

function create(
  metadata: Map<MetadataId, Metadata>,
  name: string,
  inspectable: Inspectable,
  level: number = 2,
) {
  switch (inspectable.type) {
    case 'boolean':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <Checkbox checked={inspectable.value} disabled />
        </NamedAttributeInspector>
      );
    case 'enum':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <EnumValue>{inspectable.value}</EnumValue>
        </NamedAttributeInspector>
      );
    case 'text':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <TextValue>{inspectable.value}</TextValue>
        </NamedAttributeInspector>
      );
    case 'number':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <NumberValue>{inspectable.value}</NumberValue>
        </NamedAttributeInspector>
      );
    case 'color':
      return (
        <ColorInspector
          name={displayableName(name)}
          color={inspectable.value}
        />
      );
    case 'size':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <SizeInspector value={inspectable.value} />
        </NamedAttributeInspector>
      );
    case 'bounds':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <BoundsInspector value={inspectable.value} />
        </NamedAttributeInspector>
      );
    case 'coordinate':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <CoordinateInspector value={inspectable.value} />
        </NamedAttributeInspector>
      );
    case 'coordinate3d':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <Coordinate3DInspector value={inspectable.value} />
        </NamedAttributeInspector>
      );
    case 'space':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <SpaceBoxInspector value={inspectable.value} />
        </NamedAttributeInspector>
      );
    case 'unknown':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <TextValue>{inspectable.value}</TextValue>
        </NamedAttributeInspector>
      );
    case 'object':
      return (
        <ObjectAttributeInspector
          metadata={metadata}
          name={displayableName(name)}
          fields={inspectable.fields}
          level={level}
        />
      );
    default:
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <TextValue>{JSON.stringify(inspectable)}</TextValue>
        </NamedAttributeInspector>
      );
  }
}

function createSection(
  mode: InspectorMode,
  metadata: Map<MetadataId, Metadata>,
  name: string,
  inspectable: InspectableObject,
) {
  const children: any[] = [];
  Object.keys(inspectable.fields).forEach((key, _index) => {
    const metadataId: number = Number(key);
    const attributeMetadata = metadata.get(metadataId);
    if (attributeMetadata && attributeMetadata.type === mode) {
      const attributeValue = inspectable.fields[metadataId];
      children.push(create(metadata, attributeMetadata.name, attributeValue));
    }
  });

  if (children.length > 0) {
    return (
      <Panel key={mode.concat(name)} title={name}>
        {...children}
      </Panel>
    );
  }
}

type InspectorMode = 'layout' | 'attribute';
type Props = {
  node: UINode;
  metadata: Map<MetadataId, Metadata>;
  mode: InspectorMode;
  rawDisplayEnabled?: boolean;
};
export const AttributesInspector: React.FC<Props> = ({
  node,
  metadata,
  mode,
  rawDisplayEnabled = false,
}) => {
  const keys = Object.keys(node.attributes);
  const sections = keys
    .map(function (key, _) {
      const metadataId: number = Number(key);
      /**
       * The node top-level attributes refer to the displayable panels.
       * The panel name is obtained by querying the metadata.
       * The inspectable contains the actual attributes belonging to each panel.
       */
      return createSection(
        mode,
        metadata,
        metadata.get(metadataId)?.name ?? '',
        node.attributes[metadataId] as InspectableObject,
      );
    })
    .filter((section) => section !== undefined);

  if (sections.length === 0) {
    return (
      <div style={{textAlign: 'center'}}>
        <Glyph name="stop" size={24} style={{margin: 20}} />
        <p>No data is available</p>
      </div>
    );
  }

  return (
    <>
      {...sections}
      {rawDisplayEnabled && (
        <Panel key="Raw" title="Raw Data" collapsed>
          <DataInspector data={transform(node.attributes, metadata)} />
        </Panel>
      )}
    </>
  );
};<commit_msg>Rename raw attributes flag to rawEnabled<commit_after>/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 */

import React from 'react';
import {
  Inspectable,
  InspectableObject,
  Metadata,
  MetadataId,
  UINode,
} from '../../../types';
import {DataInspector, Panel, styled} from 'flipper-plugin';
import {Checkbox, Col, Row} from 'antd';
import {displayableName} from '../utilities/displayableName';
import ColorInspector from './ColorInspector';
import SizeInspector from './SizeInspector';
import SpaceBoxInspector from './SpaceBoxInspector';
import BoundsInspector from './BoundsInspector';
import Coordinate3DInspector from './Coordinate3DInspector';
import CoordinateInspector from './CoordinateInspector';
import {
  AutoMarginStyle,
  EnumAttributeValueStyle,
  NumberAttributeValueStyle,
  ObjectContainerStyle,
  RowStyle,
  TextAttributeValueStyle,
} from './Styles';
import {Glyph} from 'flipper';
import {transform} from '../../../dataTransform';

const NumberValue = styled.span(NumberAttributeValueStyle);
const TextValue = styled.span(TextAttributeValueStyle);
const EnumValue = styled.span(EnumAttributeValueStyle);
const ObjectContainer = styled.div(ObjectContainerStyle);
const CenteredContentContainer = styled.div(AutoMarginStyle);

type NamedAttributeInspectorProps = {
  name: string;
};

const NamedAttributeInspector: React.FC<NamedAttributeInspectorProps> = ({
  name,
  children,
}) => {
  return (
    <Row style={RowStyle}>
      <Col span={8} style={AutoMarginStyle}>
        {name}
      </Col>
      <Col span={16}>
        <CenteredContentContainer>{children}</CenteredContentContainer>
      </Col>
    </Row>
  );
};

const ObjectAttributeInspector: React.FC<{
  metadata: Map<MetadataId, Metadata>;
  name: string;
  fields: Record<MetadataId, Inspectable>;
  level: number;
}> = ({metadata, name, fields, level}) => {
  return (
    <div style={RowStyle}>
      {name}
      {Object.keys(fields).map(function (key, _) {
        const metadataId: number = Number(key);
        const inspectableValue = fields[metadataId];
        const attributeName = metadata.get(metadataId)?.name ?? '';
        return (
          <ObjectContainer
            key={metadataId}
            style={{
              paddingLeft: level,
            }}>
            {create(metadata, attributeName, inspectableValue, level + 2)}
          </ObjectContainer>
        );
      })}
    </div>
  );
};

function create(
  metadata: Map<MetadataId, Metadata>,
  name: string,
  inspectable: Inspectable,
  level: number = 2,
) {
  switch (inspectable.type) {
    case 'boolean':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <Checkbox checked={inspectable.value} disabled />
        </NamedAttributeInspector>
      );
    case 'enum':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <EnumValue>{inspectable.value}</EnumValue>
        </NamedAttributeInspector>
      );
    case 'text':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <TextValue>{inspectable.value}</TextValue>
        </NamedAttributeInspector>
      );
    case 'number':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <NumberValue>{inspectable.value}</NumberValue>
        </NamedAttributeInspector>
      );
    case 'color':
      return (
        <ColorInspector
          name={displayableName(name)}
          color={inspectable.value}
        />
      );
    case 'size':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <SizeInspector value={inspectable.value} />
        </NamedAttributeInspector>
      );
    case 'bounds':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <BoundsInspector value={inspectable.value} />
        </NamedAttributeInspector>
      );
    case 'coordinate':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <CoordinateInspector value={inspectable.value} />
        </NamedAttributeInspector>
      );
    case 'coordinate3d':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <Coordinate3DInspector value={inspectable.value} />
        </NamedAttributeInspector>
      );
    case 'space':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <SpaceBoxInspector value={inspectable.value} />
        </NamedAttributeInspector>
      );
    case 'unknown':
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <TextValue>{inspectable.value}</TextValue>
        </NamedAttributeInspector>
      );
    case 'object':
      return (
        <ObjectAttributeInspector
          metadata={metadata}
          name={displayableName(name)}
          fields={inspectable.fields}
          level={level}
        />
      );
    default:
      return (
        <NamedAttributeInspector name={displayableName(name)}>
          <TextValue>{JSON.stringify(inspectable)}</TextValue>
        </NamedAttributeInspector>
      );
  }
}

function createSection(
  mode: InspectorMode,
  metadata: Map<MetadataId, Metadata>,
  name: string,
  inspectable: InspectableObject,
) {
  const children: any[] = [];
  Object.keys(inspectable.fields).forEach((key, _index) => {
    const metadataId: number = Number(key);
    const attributeMetadata = metadata.get(metadataId);
    if (attributeMetadata && attributeMetadata.type === mode) {
      const attributeValue = inspectable.fields[metadataId];
      children.push(create(metadata, attributeMetadata.name, attributeValue));
    }
  });

  if (children.length > 0) {
    return (
      <Panel key={mode.concat(name)} title={name}>
        {...children}
      </Panel>
    );
  }
}

type InspectorMode = 'layout' | 'attribute';
type Props = {
  node: UINode;
  metadata: Map<MetadataId, Metadata>;
  mode: InspectorMode;
  rawEnabled?: boolean;
};
export const AttributesInspector: React.FC<Props> = ({
  node,
  metadata,
  mode,
  rawEnabled = true,
}) => {
  const keys = Object.keys(node.attributes);
  const sections = keys
    .map(function (key, _) {
      const metadataId: number = Number(key);
      /**
       * The node top-level attributes refer to the displayable panels.
       * The panel name is obtained by querying the metadata.
       * The inspectable contains the actual attributes belonging to each panel.
       */
      return createSection(
        mode,
        metadata,
        metadata.get(metadataId)?.name ?? '',
        node.attributes[metadataId] as InspectableObject,
      );
    })
    .filter((section) => section !== undefined);

  if (sections.length === 0) {
    return (
      <div style={{textAlign: 'center'}}>
        <Glyph name="stop" size={24} style={{margin: 20}} />
        <p>No data is available</p>
      </div>
    );
  }

  return (
    <>
      {...sections}
      {rawEnabled && (
        <Panel key="Raw" title="Raw Data" collapsed>
          <DataInspector data={transform(node.attributes, metadata)} />
        </Panel>
      )}
    </>
  );
};
{'fraction_non_alphanumeric': 0.1272365805168986, 'fraction_numerical': 0.002056625762665387, 'mean_word_length': 2.904710920770878, 'pattern_counts': {'":': 0, '<': 147, '<?xml version=': 0, '>': 159, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3527559', 'n_tokens_mistral': 4131, 'n_tokens_neox': 4114, 'n_words': 885}