Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
index.tsx199 linesDownload Raw Back to FileTreeItem
1import cn from 'classnames';2import * as React from 'react';3import { ClasslistComposite } from 'aspen-decorations';4import { Directory, FileEntry, IItemRendererProps, ItemType, RenamePromptHandle, FileType, FileOrDir} from 'react-aspen';5import {IFileTreeXTriggerEvents, FileTreeXEvent } from '../types';6import _ from 'lodash';7import { Notificar } from 'notificar';8 9interface IItemRendererXProps {10    /**11     * In this implementation, decoration are null when item is `PromptHandle`12     *13     * If you would like decorations for `PromptHandle`s, then get them using `DecorationManager#getDecorations(<target>)`.14     * Where `<target>` can be either `NewFilePromptHandle.parent` or `RenamePromptHandle.target` depending on type of `PromptHandle`15     *16     * To determine the type of `PromptHandle`, use `IItemRendererProps.itemType`17     */18    decorations: ClasslistComposite19    onClick: (ev: React.MouseEvent, item: FileEntry | Directory, type: ItemType) => void20    onContextMenu: (ev: React.MouseEvent, item: FileEntry | Directory) => void21    onMouseEnter: (ev: React.MouseEvent, item: FileEntry | Directory) => void22    onMouseLeave: (ev: React.MouseEvent, item: FileEntry | Directory) => void23    onItemHovered: (ev: React.MouseEvent, item: FileEntry | Directory, type: ItemType) => void24    events: Notificar<FileTreeXEvent>25}26 27// DO NOT EXTEND FROM PureComponent!!! You might miss critical changes made deep within `item` prop28// as far as efficiency is concerned, `react-aspen` works hard to ensure unnecessary updates are ignored29export class FileTreeItem extends React.Component<IItemRendererXProps & IItemRendererProps> {30  public static getBoundingClientRectForItem(item: FileEntry | Directory): DOMRect {31    const divRef = FileTreeItem.itemIdToRefMap.get(item.id);32    if (divRef) {33      return divRef.getBoundingClientRect();34    }35    return null;36  }37 38  // ensure this syncs up with what goes in CSS, (em, px, % etc.) and what ultimately renders on the page39  public static readonly renderHeight: number = 24;40  private static readonly itemIdToRefMap: Map<number, HTMLDivElement> = new Map();41  private static readonly refToItemIdMap: Map<number, HTMLDivElement> = new Map();42  private fileTreeEvent: IFileTreeXTriggerEvents;43 44  constructor(props) {45    super(props);46    // used to apply decoration changes, you're welcome to use setState or other mechanisms as you see fit47    this.forceUpdate = this.forceUpdate.bind(this);48  }49 50  public render() {51    const { item, itemType, decorations } = this.props;52 53    const isRenamePrompt = itemType === ItemType.RenamePrompt;54    const isNewPrompt = itemType === ItemType.NewDirectoryPrompt || itemType === ItemType.NewFilePrompt;55    const isDirExpanded = itemType === ItemType.Directory56      ? (item as Directory).expanded57      : itemType === ItemType.RenamePrompt && (item as RenamePromptHandle).target.type === FileType.Directory58        ? ((item as RenamePromptHandle).target as Directory).expanded59        : false;60 61    const fileOrDir =62            (itemType === ItemType.File ||63                itemType === ItemType.NewFilePrompt ||64                (itemType === ItemType.RenamePrompt && (item as RenamePromptHandle).target.constructor === FileEntry))65              ? 'file'66              : 'directory';67 68    if (this.props.item.parent && this.props.item.parent.path) {69      this.props.item.resolvedPathCache = this.props.item.parent.path + '/' + this.props.item._metadata.data.id;70    }71 72    const itemChildren = item.children && item.children.length > 0 && item._metadata.data._type.indexOf('coll-') !== -1 ? '(' + item.children.length + ')' : '';73    const extraClasses = item._metadata.data.extraClasses ? item._metadata.data.extraClasses.join(' ') : '';74 75    return (76      <div77        className={cn('file-entry', {78          renaming: isRenamePrompt,79          prompt: isRenamePrompt || isNewPrompt,80          new: isNewPrompt,81        }, fileOrDir, decorations ? decorations.classlist : null, `depth-${item.depth}`, extraClasses)}82        data-depth={item.depth}83        onContextMenu={this.handleContextMenu}84        onClick={this.handleClick}85        onDoubleClick={this.handleDoubleClick}86        onDragStart={this.handleDragStartItem}87        onMouseEnter={this.handleMouseEnter}88        onMouseLeave={this.handleMouseLeave}89        onKeyDown={()=>{/* taken care by parent */}}90        // required for rendering context menus when opened through context menu button on keyboard91        ref={this.handleDivRef}92        draggable={true}>93 94        {!isNewPrompt && fileOrDir === 'directory' ?95          <i className={cn('directory-toggle', isDirExpanded ? 'open' : '')} />96          : null97        }98 99        <span className='file-label'>100          {101            item._metadata && item._metadata.data.icon ?102              <i className={cn('file-icon', item._metadata && item._metadata.data.icon ? item._metadata.data.icon : fileOrDir)} /> : null103          }104          <span className='file-name'>105            { _.unescape(this.props.item.getMetadata('data')._label)}106            <span className='children-count'>{itemChildren}</span>107          </span>108 109        </span>110      </div>);111  }112 113  public componentDidMount() {114    this.events = this.props.events;115    this.props.item.resolvedPathCache = this.props.item.parent.path + '/' + this.props.item._metadata.data.id;116    if (this.props.decorations) {117      this.props.decorations.addChangeListener(this.forceUpdate);118    }119    this.setActiveFile(this.props.item);120  }121 122  private setActiveFile = async (FileOrDir): Promise<void> => {123    this.props.changeDirectoryCount(FileOrDir.parent);124    if(FileOrDir._loaded !== true) {125      this.events.dispatch(FileTreeXEvent.onTreeEvents, window.event, 'added', FileOrDir);126    }127    FileOrDir._loaded = true;128  };129 130  public componentWillUnmount() {131    if (this.props.decorations) {132      this.props.decorations.removeChangeListener(this.forceUpdate);133    }134  }135 136  public componentDidUpdate(prevProps: IItemRendererXProps) {137    if (prevProps.decorations) {138      prevProps.decorations.removeChangeListener(this.forceUpdate);139    }140    if (this.props.decorations) {141      this.props.decorations.addChangeListener(this.forceUpdate);142    }143  }144 145  private handleDivRef = (r: HTMLDivElement) => {146    if (r === null) {147      FileTreeItem.itemIdToRefMap.delete(this.props.item.id);148    } else {149      FileTreeItem.itemIdToRefMap.set(this.props.item.id, r);150      FileTreeItem.refToItemIdMap.set(r, this.props.item);151    }152  };153 154  private handleContextMenu = (ev: React.MouseEvent) => {155    const { item, itemType, onContextMenu } = this.props;156    if (itemType === ItemType.File || itemType === ItemType.Directory) {157      onContextMenu(ev, item as FileOrDir);158    }159  };160 161  private handleClick = (ev: React.MouseEvent) => {162    const { item, itemType, onClick } = this.props;163    if (itemType === ItemType.File || itemType === ItemType.Directory) {164      onClick(ev, item as FileEntry, itemType);165    }166  };167 168  private handleDoubleClick = (ev: React.MouseEvent) => {169    const { item, itemType, onDoubleClick } = this.props;170    if (itemType === ItemType.File || itemType === ItemType.Directory) {171      onDoubleClick(ev, item as FileEntry, itemType);172    }173  };174 175  private handleMouseEnter = (ev: React.MouseEvent) => {176    const { item, itemType, onMouseEnter } = this.props;177    if (itemType === ItemType.File || itemType === ItemType.Directory) {178      onMouseEnter?.(ev, item as FileEntry);179    }180  };181 182  private handleMouseLeave = (ev: React.MouseEvent) => {183    const { item, itemType, onMouseLeave } = this.props;184    if (itemType === ItemType.File || itemType === ItemType.Directory) {185      onMouseLeave?.(ev, item as FileEntry);186    }187  };188 189  private handleDragStartItem = (e: React.DragEvent) => {190    const { item, itemType, events } = this.props;191    if (itemType === ItemType.File || itemType === ItemType.Directory) {192      const ref = FileTreeItem.itemIdToRefMap.get(item.id);193      if (ref) {194        events.dispatch(FileTreeXEvent.onTreeEvents, e, 'dragstart', item);195      }196    }197  };198}199 
codekingpro/portable-devtools · Team Ai