/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable prettier/prettier */
import { IconLookup } from '@fortawesome/fontawesome-svg-core';
import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Checkbox, Tooltip } from '@mui/material';
import { Colors, EditableText, IconButton, NumberInput, Size, Slider, Type } from 'browndash-components';
import { concat } from 'lodash';
import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ColorResult, SketchPicker } from 'react-color';
import * as Icons from 'react-icons/bs'; // {BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../ClientUtils';
import { emptyFunction } from '../../Utils';
import { Doc, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
import { AclAdmin, DocAcl, DocData } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
import { InkField } from '../../fields/InkField';
import { List } from '../../fields/List';
import { ComputedField } from '../../fields/ScriptField';
import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
import { GetEffectiveAcl, SharingPermissions, normalizeEmail } from '../../fields/util';
import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
import { GroupManager } from '../util/GroupManager';
import { LinkManager } from '../util/LinkManager';
import { SharingManager } from '../util/SharingManager';
import { SnappingManager } from '../util/SnappingManager';
import { Transform } from '../util/Transform';
import { UndoManager, undoBatch, undoable } from '../util/UndoManager';
import { EditableView } from './EditableView';
import { FilterPanel } from './FilterPanel';
import { InkStrokeProperties } from './InkStrokeProperties';
import { ObservableReactComponent } from './ObservableReactComponent';
import { PropertiesButtons } from './PropertiesButtons';
import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector';
import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
import { PropertiesSection } from './PropertiesSection';
import './PropertiesView.scss';
import { DefaultStyleProvider, SetFilterOpener as SetPropertiesFilterOpener } from './StyleProvider';
import { DocumentView } from './nodes/DocumentView';
import { StyleProviderFuncType } from './nodes/FieldView';
import { OpenWhere } from './nodes/OpenWhere';
import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails';

const _global = (window /* browser */ || global) /* node */ as any;

interface PropertiesViewProps {
    width: number;
    height: number;
    styleProvider?: StyleProviderFuncType;
    addDocTab: (doc: Doc, where: OpenWhere) => boolean;
}

@observer
export class PropertiesView extends ObservableReactComponent<PropertiesViewProps> {
    private _widthUndo?: UndoManager.Batch;

    // eslint-disable-next-line no-use-before-define
    public static Instance: PropertiesView | undefined;
    constructor(props: any) {
        super(props);
        makeObservable(this);
        PropertiesView.Instance = this;
        SetPropertiesFilterOpener(
            action(() => {
                this.CloseAll();
                this.openFilters = true;
            })
        );
    }

    @computed get MAX_EMBED_HEIGHT() {
        return 200;
    }

    @computed get selectedDoc() {
        return DocumentView.SelectedSchemaDoc() || this.selectedDocumentView?.Document || Doc.ActiveDashboard;
    }

    @computed get selectedLink() {
        return LinkManager.Instance.currentLink;
    }

    @computed get selectedLayoutDoc() {
        return DocumentView.SelectedSchemaDoc() || this.selectedDocumentView?.layoutDoc || Doc.ActiveDashboard;
    }
    @computed get selectedDocumentView() {
        return DocumentView.Selected().lastElement();
    }
    @computed get isPres(): boolean {
        return this.selectedDoc?.type === DocumentType.PRES;
    }
    @computed get dataDoc() {
        return this.selectedDoc?.[DocData];
    }

    @observable layoutFields: boolean = false;
    @observable layoutDocAcls: boolean = false;

    @observable openOptions: boolean = true;
    @observable openSharing: boolean = true;
    @observable openFields: boolean = false;
    @observable openLayout: boolean = false;
    @observable openContexts: boolean = true;
    @observable openLinks: boolean = true;
    @observable openAppearance: boolean = true;
    @observable openTransform: boolean = true;
    @observable openFilters: boolean = false;
    @observable openStyling: boolean = true;

    // Pres Trails booleans:
    @observable openPresTransitions: boolean = true;
    @observable openPresProgressivize: boolean = false;
    @observable openPresVisibilityAndDuration: boolean = false;
    @observable openAddSlide: boolean = false;
    @observable openSlideOptions: boolean = false;

    @observable _controlButton: boolean = false;
    private _disposers: { [name: string]: IReactionDisposer } = {};

    componentDidMount() {
        this._disposers.link = reaction(
            () => this.selectedLink,
            link => {
                link && this.CloseAll();
                link && (this.openLinks = true);
            },
            { fireImmediately: true }
        );
    }

    componentWillUnmount() {
        Object.values(this._disposers).forEach(disposer => disposer?.());
    }

    @computed get isInk() {
        return this.selectedDoc?.type === DocumentType.INK;
    }
    @computed get isGroup() {
        return this.selectedDoc?.isGroup;
    }
    @computed get isStack() {
        return [CollectionViewType.Masonry, CollectionViewType.Multicolumn, CollectionViewType.Multirow, CollectionViewType.Stacking, CollectionViewType.NoteTaking].includes(this.selectedDoc?.type_collection as any);
    }

    rtfWidth = () => (!this.selectedLayoutDoc ? 0 : Math.min(NumCast(this.selectedLayoutDoc?._width), this._props.width - 20));
    rtfHeight = () => (!this.selectedLayoutDoc ? 0 : this.rtfWidth() <= NumCast(this.selectedLayoutDoc?._width) ? Math.min(NumCast(this.selectedLayoutDoc?._height), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT);

    @action
    docWidth = () => {
        const layoutDoc = this.selectedLayoutDoc;
        if (layoutDoc) {
            const aspect = Doc.NativeAspect(layoutDoc, undefined, !layoutDoc._layout_fitWidth);
            if (aspect) return Math.min(NumCast(layoutDoc._width), Math.min(this.MAX_EMBED_HEIGHT * aspect, this._props.width - 20));
            return Doc.NativeWidth(layoutDoc) ? Math.min(NumCast(layoutDoc._width), this._props.width - 20) : this._props.width - 20;
        }
        return 0;
    };

    @action
    docHeight = () => {
        const layoutDoc = this.selectedLayoutDoc;
        if (layoutDoc && this.dataDoc) {
            return Math.max(
                70,
                Math.min(
                    this.MAX_EMBED_HEIGHT,
                    Doc.NativeAspect(layoutDoc, undefined, true)
                        ? this.docWidth() / Doc.NativeAspect(layoutDoc, undefined, true)
                        : layoutDoc._layout_fitWidth
                          ? !Doc.NativeHeight(this.dataDoc)
                              ? NumCast(this._props.height)
                              : Math.min((this.docWidth() * Doc.NativeHeight(layoutDoc)) / Doc.NativeWidth(layoutDoc) || NumCast(this._props.height))
                          : NumCast(layoutDoc._height) || 50
                )
            );
        }
        return 0;
    };

    editableFields = (filter: (key: string) => boolean, reqdKeys: string[]) => {
        const rows: JSX.Element[] = [];
        if (this.dataDoc && this.selectedDoc) {
            const ids = new Set<string>(reqdKeys);
            const docs: Doc[] =
                DocumentView.Selected().length < 2 //
                    ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc]
                    : DocumentView.Selected().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc));
            docs.forEach(doc =>
                Object.keys(doc)
                    .filter(filter)
                    .forEach(key => doc[key] !== ComputedField.undefined && key && ids.add(key))
            );

            Array.from(ids)
                .sort()
                .forEach(key => {
                    const multiple = Array.from(docs.reduce((set, doc) => set.add(doc[key]), new Set<FieldResult>()).keys()).length > 1;
                    const editableContents = multiple ? '-multiple-' : Field.toKeyValueString(docs[0], key);
                    const displayContents = multiple ? '-multiple-' : Field.toString(docs[0][key] as FieldType);
                    const contentElement = (
                        <EditableView
                            key="editableView"
                            contents={displayContents}
                            height={13}
                            fontSize={10}
                            GetValue={() => editableContents}
                            SetValue={(value: string) => {
                                value !== '-multiple-' && docs.map(doc => Doc.SetField(doc, key, value, true));
                                return true;
                            }}
                        />
                    );
                    rows.push(
                        <div style={{ display: 'flex', overflowY: 'visible', marginBottom: '-1px' }} key={key}>
                            <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key + ':'}</span>
                            &nbsp;
                            {contentElement}
                        </div>
                    );
                });

            rows.push(
                <div className="propertiesView-field" key="newKeyValue" style={{ marginTop: '3px', backgroundColor: SnappingManager.userBackgroundColor, textAlign: 'center' }}>
                    <EditableView key="editableView" oneLine contents="add key:value or #tags" height={13} fontSize={10} GetValue={returnEmptyString} SetValue={this.setKeyValue} />
                </div>
            );
        }
        return rows;
    };

    @computed get expandedField() {
        return this.editableFields(returnTrue, []);
    }

    @computed get noviceFields() {
        const noviceReqFields = ['author', 'author_date', 'tags', '_layout_curPage'];
        return this.editableFields(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('acl_')), noviceReqFields);
    }

    @undoBatch
    setKeyValue = (value: string) => {
        const docs =
            DocumentView.Selected().length < 2 && this.selectedDoc
                ? [this.layoutFields
                        ? Doc.Layout(this.selectedDoc) //
                        : this.dataDoc!]
                : DocumentView.Selected().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc)); // prettier-ignore
        docs.forEach(doc => {
            if (value.indexOf(':') !== -1) {
                const newVal = value[0].toUpperCase() + value.substring(1, value.length);
                const splits = newVal.split(':');
                Doc.SetField(doc, splits[0], splits[1], true);
                const tags = StrCast(doc.tags, ':');
                if (tags.includes(`${splits[0]}:`) && splits[1] === 'undefined') {
                    Doc.SetField(doc, 'tags', `"${tags.replace(splits[0] + ':', '')}"`, true);
                }
                return true;
            }
            if (value[0] === '#') {
                const tags = StrListCast(doc.tags);
                if (!tags.includes(value)) {
                    tags.push(value);
                    doc[DocData].tags = tags.length ? new List<string>(tags) : undefined;
                }
                return true;
            }
            return undefined;
        });
        return false;
    };

    @observable transform: Transform = Transform.Identity();
    getTransform = () => this.transform;
    propertiesDocViewRef = (ref: HTMLDivElement) => {
        const resizeObserver = new _global.ResizeObserver(
            action(() => {
                const cliRect = ref.getBoundingClientRect();
                this.transform = new Transform(-cliRect.x, -cliRect.y, 1);
            })
        );
        ref && resizeObserver.observe(ref);
    };

    @computed get contexts() {
        return !this.selectedDoc ? null : <PropertiesDocContextSelector DocView={this.selectedDocumentView} hideTitle addDocTab={this._props.addDocTab} />;
    }

    @computed get contextCount() {
        if (this.selectedDocumentView) {
            const target = this.selectedDocumentView.Document;
            return Doc.GetEmbeddings(target).length - 1;
        }
        return 0;
    }

    @computed get links() {
        const selAnchor = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.Instance.currentLinkAnchor ?? this.selectedDoc;
        return !selAnchor ? null : <PropertiesDocBacklinksSelector Document={selAnchor} hideTitle addDocTab={this._props.addDocTab} />;
    }

    @computed get linkCount() {
        const selAnchor = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.Instance.currentLinkAnchor ?? this.selectedDoc;
        let counter = 0;

        Doc.Links(selAnchor).forEach(() => counter++);

        return counter;
    }

    @computed get layoutPreview() {
        if (DocumentView.Selected().length > 1) {
            return '-- multiple selected --';
        }
        if (this.selectedDoc) {
            const layoutDoc = Doc.Layout(this.selectedDoc);
            const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes('FormattedTextBox') ? this.rtfHeight : this.docHeight;
            const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes('FormattedTextBox') ? this.rtfWidth : this.docWidth;
            return (
                <div ref={this.propertiesDocViewRef} style={{ pointerEvents: 'none', display: 'inline-block', height: panelHeight() }} key={this.selectedDoc[Id]}>
                    <DocumentView
                        Document={this.selectedDoc}
                        TemplateDataDocument={Doc.AreProtosEqual(this.dataDoc, this.selectedDoc) ? undefined : this.dataDoc}
                        renderDepth={1}
                        fitContentsToBox={returnTrue}
                        styleProvider={DefaultStyleProvider}
                        containerViewPath={returnEmptyDoclist}
                        dontCenter="y"
                        isDocumentActive={returnFalse}
                        isContentActive={emptyFunction}
                        NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
                        NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
                        PanelWidth={panelWidth}
                        PanelHeight={panelHeight}
                        focus={emptyFunction}
                        ScreenToLocalTransform={this.getTransform}
                        childFilters={returnEmptyFilter}
                        childFiltersByRanges={returnEmptyFilter}
                        searchFilterDocs={returnEmptyDoclist}
                        addDocument={returnFalse}
                        moveDocument={undefined}
                        removeDocument={returnFalse}
                        whenChildContentsActiveChanged={emptyFunction}
                        addDocTab={returnFalse}
                        pinToPres={emptyFunction}
                        dontRegisterView
                    />
                </div>
            );
        }
        return null;
    }

    /**
     * Handles the changing of a user's permissions from the permissions panel.
     */
    @undoBatch
    changePermissions = (e: any, user: string) => {
        const docs = DocumentView.Selected().length < 2 ? [this.selectedDoc] : DocumentView.Selected().map(dv => (this.layoutDocAcls ? dv.layoutDoc : dv.dataDoc));
        SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs, this.layoutDocAcls);
    };

    /**
     * @returns the options for the permissions dropdown.
     */
    getPermissionsSelect(user: string, permission: string, showGuestOptions: boolean) {
        const dropdownValues: string[] = showGuestOptions ? [SharingPermissions.None, SharingPermissions.View] : Object.values(SharingPermissions);
        if (permission === '-multiple-') dropdownValues.unshift(permission);
        return (
            <select className="propertiesView-permissions-select" value={permission} onChange={e => this.changePermissions(e, user)}>
                {dropdownValues.map(permissionVal => (
                    <option className="propertiesView-permisssions-select" key={permissionVal} value={permissionVal}>
                        {concat(ReverseHierarchyMap.get(permissionVal)?.image, ' ', permissionVal)}
                    </option>
                ))}
            </select>
        );
    }

    /**
     * @returns the notification icon. On clicking, it should notify someone of a document been shared with them.
     */
    @computed get notifyIcon() {
        return (
            <Tooltip title={<div className="dash-tooltip">Notify with message</div>}>
                <div className="notify-button">
                    <FontAwesomeIcon className="notify-button-icon" icon="bell" size="sm" />
                </div>
            </Tooltip>
        );
    }

    /**
     * ... next to the owner that opens the main SharingManager interface on click.
     */
    @computed get expansionIcon() {
        return (
            <div className="expansion-button">
                <IconButton
                    icon={<FontAwesomeIcon icon="ellipsis-h" />}
                    size={Size.XSMALL}
                    color={SnappingManager.userColor}
                    onClick={action(() => {
                        if (this.selectedDocumentView || this.selectedDoc) {
                            SharingManager.Instance.open(this.selectedDocumentView?.Document === this.selectedDoc ? this.selectedDocumentView : undefined, this.selectedDoc);
                        }
                    })}
                />
            </div>
        );
    }

    /**
     * @returns a row of the permissions panel
     */
    sharingItem(nameIn: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
        const name = nameIn === ClientUtils.CurrentUserEmail() ? 'Me' : nameIn;
        return (
            <div
                className="propertiesView-sharingTable-item"
                key={name + permission}
                // style={{ backgroundColor: this.selectedUser === name ? "#bcecfc" : "" }}
                // onPointerDown={action(() => this.selectedUser = this.selectedUser === name ? "" : name)}
            >
                <div className="propertiesView-sharingTable-item-name" style={{ width: name !== 'Me' ? '85px' : '80px' }}>
                    {' '}
                    {name}{' '}
                </div>
                {/* {name !== "Me" ? this.notifyIcon : null} */}
                <div className="propertiesView-sharingTable-item-permission">
                    {this.colorACLDropDown(name, admin, permission, false)}
                    {(permission === 'Owner' && name === 'Me') || showExpansionIcon ? this.expansionIcon : null}
                </div>
            </div>
        );
    }

    /**
     * @returns a colored dropdown bar reflective of the permission
     */
    colorACLDropDown(name: string, admin: boolean, permission: string, showGuestOptions: boolean) {
        const shareImage = ReverseHierarchyMap.get(permission)?.image;
        return (
            <div>
                <div className="propertiesView-shareDropDown">
                    <div className={`propertiesView-shareDropDown${permission}`}>
                        <div>{admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission, showGuestOptions) : concat(shareImage, ' ', permission)}</div>
                    </div>
                </div>
            </div>
        );
    }

    /**
     * Sorting algorithm to sort users.
     */
    sortUsers = (u1: String, u2: String) => (u1 > u2 ? -1 : u1 === u2 ? 0 : 1);

    /**
     * Sorting algorithm to sort groups.
     */
    sortGroups = (group1: Doc, group2: Doc) => {
        const g1 = StrCast(group1.title);
        const g2 = StrCast(group2.title);
        return g1 > g2 ? -1 : g1 === g2 ? 0 : 1;
    };

    /**
     * @returns the sharing and permissions panel.
     */
    @computed get sharingTable() {
        // all selected docs
        const docs = DocumentView.Selected().length < 2 && this.selectedDoc ? [this.selectedDoc] : DocumentView.SelectedDocs();
        const target = docs[0];

        const showAdmin = GetEffectiveAcl(target) === AclAdmin;
        const individualTableEntries = [];
        const usersAdded: string[] = []; // all shared users being added - organized by denormalized email

        const seldoc = this.layoutDocAcls ? this.selectedLayoutDoc : this.selectedDoc?.[DocData];
        // adds each user to usersAdded
        SharingManager.Instance.users.forEach(eachUser => {
            let userOnDoc = true;
            if (seldoc) {
                if (Doc.GetT(seldoc, 'acl_' + normalizeEmail(eachUser.user.email), 'string', true) === '' || Doc.GetT(seldoc, 'acl_' + normalizeEmail(eachUser.user.email), 'string', true) === undefined) {
                    userOnDoc = false;
                }
            }
            if (userOnDoc && !usersAdded.includes(eachUser.user.email) && eachUser.user.email !== 'guest' && eachUser.user.email !== target.author) {
                usersAdded.push(eachUser.user.email);
            }
        });

        // sorts and then adds each user to the table
        usersAdded.sort(this.sortUsers);
        usersAdded.forEach(userEmail => {
            const userKey = `acl_${normalizeEmail(userEmail)}`;
            const aclField = Doc.GetT(this.layoutDocAcls ? target : Doc.GetProto(target), userKey, 'string', true);
            const permission = StrCast(aclField);
            individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
        });

        // adds current user
        let userEmail = ClientUtils.CurrentUserEmail();
        if (userEmail === 'guest') userEmail = 'Guest';
        const userKey = `acl_${normalizeEmail(userEmail)}`;
        if (!usersAdded.includes(userEmail) && userEmail !== 'Guest' && userEmail !== target.author) {
            let permission;
            if (this.layoutDocAcls) {
                if (target[DocAcl][userKey]) permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
                else if (target.embedContainer) permission = StrCast(Doc.GetProto(DocCast(target.embedContainer))[userKey]);
                else permission = StrCast(Doc.GetProto(target)?.[userKey]);
            } else permission = StrCast(target[userKey]);
            individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
        }

        // shift owner to top
        individualTableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false);

        // adds groups
        const groupTableEntries: JSX.Element[] = [];
        const groupList = GroupManager.Instance?.allGroups || [];
        groupList.sort(this.sortGroups);
        groupList.forEach(group => {
            if (group.title !== 'Guest' && this.selectedDoc) {
                const groupKey = 'acl_' + normalizeEmail(StrCast(group.title));
                if (this.selectedDoc[groupKey] !== '' && this.selectedDoc[groupKey] !== undefined) {
                    let permission;
                    if (this.layoutDocAcls) {
                        if (target[DocAcl][groupKey]) {
                            permission = HierarchyMapping.get(target[DocAcl][groupKey])?.name;
                        } else if (target.embedContainer) permission = StrCast(Doc.GetProto(DocCast(target.embedContainer))[groupKey]);
                        else permission = StrCast(Doc.GetProto(target)?.[groupKey]);
                    } else permission = StrCast(target[groupKey]);
                    groupTableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission!, false));
                }
            }
        });

        // guest permission
        const guestPermission = StrCast((this.layoutDocAcls ? target : Doc.GetProto(target)).acl_Guest);

        return (
            <div>
                <div>
                    <br /> Individuals with Access to this Document
                </div>
                <div className="propertiesView-sharingTable" style={{ background: SnappingManager.userBackgroundColor, color: SnappingManager.userColor }}>
                    <div> {individualTableEntries}</div>
                </div>
                {groupTableEntries.length > 0 ? (
                    <div>
                        <div>
                            <br /> Groups with Access to this Document
                        </div>
                        <div className="propertiesView-sharingTable" style={{ background: SnappingManager.userBackgroundColor, color: SnappingManager.userColor }}>
                            <div> {groupTableEntries}</div>
                        </div>
                    </div>
                ) : null}
                <br /> Guest
                <div>{this.colorACLDropDown('Guest', showAdmin, guestPermission!, true)}</div>
            </div>
        );
    }

    @computed get fieldsCheckbox() {
        // color= "primary"
        return <Checkbox color="primary" onChange={this.toggleCheckbox} checked={this.layoutFields} />;
    }

    @action
    toggleCheckbox = () => {
        this.layoutFields = !this.layoutFields;
    };

    @computed get color() {
        return SnappingManager.userColor;
    }

    @computed get backgroundColor() {
        return SnappingManager.userBackgroundColor;
    }

    @computed get variantColor() {
        return SnappingManager.userVariantColor;
    }

    @computed get editableTitle() {
        const titles = new Set<string>();
        DocumentView.Selected().forEach(dv => titles.add(StrCast(dv.Document.title)));
        const title = Array.from(titles.keys()).length > 1 ? '--multiple selected--' : StrCast(this.selectedDoc?.title);
        return (
            <div>
                <EditableText val={title} setVal={this.setTitle} color={this.color} type={Type.SEC} formLabel="Title" fillWidth />
                {LinkManager.Instance.currentLinkAnchor ? (
                    <p className="propertiesView-titleExtender">
                        <b>Anchor:</b>
                        {StrCast(LinkManager.Instance.currentLinkAnchor.title)}
                    </p>
                ) : null}
                {this.selectedLink?.title ? (
                    <p className="propertiesView-titleExtender">
                        <b>Link:</b>
                        {StrCast(this.selectedLink.title)}
                    </p>
                ) : null}
            </div>
        );
    }

    @computed get currentType() {
        const docType = StrCast(this.selectedDoc?.type) as DocumentType;
        const colType = StrCast(this.selectedDoc?.type_collection) as CollectionViewType;
        const capitalizedDocType = ClientUtils.cleanDocumentType(docType, colType);

        return (
            <div>
                Type
                {/* <div className = "propertiesView-wordType">Type</div>   */}
                <div className="currentType">
                    <div className="currentType-icon">{this.currentComponent}</div>

                    {capitalizedDocType}
                </div>
            </div>
        );
    }

    @computed get currentComponent() {
        const iconName = StrCast(this.selectedDoc?.systemIcon);

        if (iconName) {
            const Icon = Icons[iconName as keyof typeof Icons];
            return <Icon />;
        }
        return <Icons.BsFillCollectionFill />;
    }

    @undoBatch
    setTitle = (value: string | number) => {
        if (DocumentView.Selected().length > 1) {
            DocumentView.Selected().map(dv => Doc.SetInPlace(dv.Document, 'title', value, true));
        } else if (this.dataDoc) {
            if (this.selectedDoc) Doc.SetInPlace(this.selectedDoc, 'title', value, true);
            else Doc.SetField(this.dataDoc, 'title', value as string, true);
        }
    };

    @undoBatch
    rotate = (angle: number) => {
        const _centerPoints: { X: number; Y: number }[] = [];
        const doc = this.selectedDoc;
        const layout = this.selectedLayoutDoc;
        if (doc && layout) {
            if (doc.type === DocumentType.INK && doc.x && doc.y && layout._width && layout._height && doc.data) {
                const ink = Cast(doc.stroke, InkField)?.inkData;
                if (ink) {
                    const xs = ink.map(p => p.X);
                    const ys = ink.map(p => p.Y);
                    const left = Math.min(...xs);
                    const top = Math.min(...ys);
                    _centerPoints.push({ X: left, Y: top });
                }
            }

            let index = 0;
            if (doc.type === DocumentType.INK && doc.x && doc.y && layout._width && layout._height && doc.data) {
                layout.rotation = NumCast(layout.rotation) + angle;
                const inks = Cast(doc.stroke, InkField)?.inkData;
                if (inks) {
                    const newPoints: { X: number; Y: number }[] = [];
                    inks.forEach(ink => {
                        const newX = Math.cos(angle) * (ink.X - _centerPoints[index].X) - Math.sin(angle) * (ink.Y - _centerPoints[index].Y) + _centerPoints[index].X;
                        const newY = Math.sin(angle) * (ink.X - _centerPoints[index].X) + Math.cos(angle) * (ink.Y - _centerPoints[index].Y) + _centerPoints[index].Y;
                        newPoints.push({ X: newX, Y: newY });
                    });
                    doc.stroke = new InkField(newPoints);
                    const xs = newPoints.map(p => p.X);
                    const ys = newPoints.map(p => p.Y);
                    const left = Math.min(...xs);
                    const top = Math.min(...ys);
                    const right = Math.max(...xs);
                    const bottom = Math.max(...ys);

                    layout._height = bottom - top;
                    layout._width = right - left;
                }
                index++;
            }
        }
    };

    @computed
    get controlPointsButton() {
        return (
            <div className="inking-button">
                <Tooltip title={<div className="dash-tooltip">Edit points</div>}>
                    <div
                        className="inking-button-points"
                        style={{ backgroundColor: InkStrokeProperties.Instance._controlButton ? 'black' : '' }}
                        onPointerDown={action(() => {
                            InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton;
                        })}>
                        <FontAwesomeIcon icon="bezier-curve" size="lg" />
                    </div>
                </Tooltip>
            </div>
        );
    }

    inputBox = (key: string, value: any, setter: (val: string) => {}, title: string) => (
        <div
            className="inputBox"
            style={{
                marginRight: title === 'X:' ? '19px' : '',
                marginLeft: title === '∠:' ? '39px' : '',
            }}>
            <div className="inputBox-title"> {title} </div>
            <input className="inputBox-input" type="text" value={value} style={{ color: SnappingManager.userColor, backgroundColor: SnappingManager.userBackgroundColor }} onChange={e => setter(e.target.value)} onKeyDown={e => e.stopPropagation()} />
            <div className="inputBox-button">
                <div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
                    <FontAwesomeIcon icon="caret-up" size="sm" />
                </div>
                <div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
                    <FontAwesomeIcon icon="caret-down" size="sm" />
                </div>
            </div>
        </div>
    );

    inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => (
        <div className="inputBox-duo">
            {this.inputBox(key, value, setter, title1)}
            {title2 === '' ? null : this.inputBox(key2, value2, setter2, title2)}
        </div>
    );

    @action
    upDownButtons = (dirs: string, field: string) => {
        const selDoc = this.selectedDoc;
        if (!selDoc) return;
        // prettier-ignore
        switch (field) {
            case 'Xps': selDoc.x = NumCast(this.selectedDoc?.x) + (dirs === 'up' ? 10 : -10); break;
            case 'Yps': selDoc.y = NumCast(this.selectedDoc?.y) + (dirs === 'up' ? 10 : -10); break;
            case 'stk': selDoc.stroke_width = NumCast(this.selectedDoc?.[DocData].stroke_width) + (dirs === 'up' ? 0.1 : -0.1); break;
            case 'wid': {
                    const oldWidth = NumCast(selDoc._width);
                    const oldHeight = NumCast(selDoc._height);
                    const oldX = NumCast(selDoc.x);
                    const oldY = NumCast(selDoc.y);
                    selDoc._width = oldWidth + (dirs === 'up' ? 10 : -10);
                    if (selDoc.type === DocumentType.INK && selDoc.x && selDoc.y && selDoc._height && selDoc._width) {
                        const ink = Cast(selDoc.data, InkField)?.inkData;
                        if (ink) {
                            const newPoints: { X: number; Y: number }[] = [];
                            for (let j = 0; j < ink.length; j++) {
                                // (new x — oldx) + (oldxpoint * newWidt)/oldWidth
                                const newX = NumCast(selDoc.x) - oldX + (ink[j].X * NumCast(selDoc._width)) / oldWidth;
                                const newY = NumCast(selDoc.y) - oldY + (ink[j].Y * NumCast(selDoc._height)) / oldHeight;
                                newPoints.push({ X: newX, Y: newY });
                            }
                            selDoc.data = new InkField(newPoints);
                        }
                    }
                }
                break;
            case 'hgt': {
                    const oWidth = NumCast(selDoc._width);
                    const oHeight = NumCast(selDoc._height);
                    const oX = NumCast(selDoc.x);
                    const oY = NumCast(selDoc.y);
                    selDoc._height = oHeight + (dirs === 'up' ? 10 : -10);
                    if (selDoc.type === DocumentType.INK && selDoc.x && selDoc.y && selDoc._height && selDoc._width) {
                        const ink = Cast(selDoc.data, InkField)?.inkData;
                        if (ink) {
                            const newPoints: { X: number; Y: number }[] = [];
                            for (let j = 0; j < ink.length; j++) {
                                // (new x — oldx) + (oldxpoint * newWidt)/oldWidth
                                const newX = NumCast(selDoc.x) - oX + (ink[j].X * NumCast(selDoc._width)) / oWidth;
                                const newY = NumCast(selDoc.y) - oY + (ink[j].Y * NumCast(selDoc._height)) / oHeight;
                                newPoints.push({ X: newX, Y: newY });
                            }
                            selDoc.data = new InkField(newPoints);
                        }
                    }
                }
                break;
            default: { /* empty */ }
        }
    };

    getField(key: string) {
        return Field.toString(this.selectedDoc?.[DocData][key] as FieldType);
    }

    @computed get shapeXps() { return NumCast(this.selectedDoc?.x); } // prettier-ignore
    set shapeXps(value)  { this.selectedDoc && (this.selectedDoc.x = Math.round(value * 100) / 100); } // prettier-ignore
    @computed get shapeYps() { return NumCast(this.selectedDoc?.y); } // prettier-ignore
    set shapeYps(value)  { this.selectedDoc && (this.selectedDoc.y = Math.round(value * 100) / 100); } // prettier-ignore
    @computed get shapeWid() { return NumCast(this.selectedDoc?._width); } // prettier-ignore
    set shapeWid(value)  { this.selectedDoc && (this.selectedDoc._width = Math.round(value * 100) / 100); } // prettier-ignore
    @computed get shapeHgt() { return NumCast(this.selectedDoc?._height); } // prettier-ignore
    set shapeHgt(value)  { this.selectedDoc && (this.selectedDoc._height = Math.round(value * 100) / 100); } // prettier-ignore
    @computed get strokeThk(){ return NumCast(this.selectedDoc?.[DocData].stroke_width); } // prettier-ignore
    set strokeThk(value) { this.selectedDoc && (this.selectedDoc[DocData].stroke_width = Math.round(value * 100) / 100); } // prettier-ignore

    @computed get hgtInput() {
        return this.inputBoxDuo(
            'hgt',
            this.shapeHgt,
            undoable((val: string) => {
                !Number(val) && (this.shapeHgt = +val);
            }, 'set height'),
            'H:',
            'wid',
            this.shapeWid,
            undoable((val: string) => {
                !isNaN(Number(val)) && (this.shapeWid = +val);
            }, 'set width'),
            'W:'
        );
    }
    @computed get XpsInput() {
        // prettier-ignore
        return this.inputBoxDuo(
            'Xps',
            this.shapeXps,
            undoable((val: string) => { val !== '0' && !isNaN(Number(val)) && (this.shapeXps = +val);  }, 'set x coord'),
            'X:',
            'Yps',
            this.shapeYps,
            undoable((val: string) => { val !== '0' && !isNaN(Number(val)) && (this.shapeYps = +val); }, 'set y coord'),
            'Y:'
        );
    }

    @observable private _fillBtn = false;
    @observable private _lineBtn = false;

    private _lastDash: any = '2';

    @computed get colorFil() { return StrCast(this.selectedDoc?.[DocData].fillColor); } // prettier-ignore
    set colorFil(value) { this.selectedDoc && (this.selectedDoc[DocData].fillColor = value || undefined); } // prettier-ignore
    @computed get colorStk() { return StrCast(this.selectedDoc?.[DocData].color); } // prettier-ignore
    set colorStk(value) { this.selectedDoc && (this.selectedDoc[DocData].color = value || undefined); } // prettier-ignore
    @computed get borderColor() { 
        const doc = this.selectedDoc;
        const layoutDoc = doc ? Doc.Layout(doc) : doc;
        return StrCast(layoutDoc);
    }
    set borderColor(value) { this.selectedDoc && (this.selectedDoc[DocData].color = value || undefined); } // prettier-ignore

    colorButton(value: string, type: string, setter: () => void) {
        return (
            <div className="color-button" key="color" onPointerDown={action(() => setter())}>
                <div
                    className="color-button-preview"
                    style={{
                        backgroundColor: value ?? '121212',
                        width: 15,
                        height: 15,
                        display: value === '' || value === 'transparent' ? 'none' : '',
                    }}
                />
                {value === '' || value === 'transparent' ? <p style={{ fontSize: 25, color: 'red', marginTop: -14 }}>☒</p> : ''}
            </div>
        );
    }

    colorPicker(color: string, setter: (color: string) => void) {
        return (
            <SketchPicker
                onChange={undoable(
                    action((col: ColorResult) => setter(col.hex)),
                    'set stroke color property'
                )}
                presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
                color={color}
            />
        );
    }

    @computed get fillButton() {
        return this.colorButton(this.colorFil, 'fill', () => {
            this._fillBtn = !this._fillBtn;
            this._lineBtn = false;
        });
    }
    @computed get lineButton() {
        return this.colorButton(this.colorStk, 'line', () => {
            this._lineBtn = !this._lineBtn;
            this._fillBtn = false;
        });
    }

    @computed get fillPicker() {
        return this.colorPicker(this.colorFil, (color: string) => { this.colorFil = color;  }); // prettier-ignore
    }
    @computed get linePicker() {
        return this.colorPicker(this.colorStk, (color: string) => { this.colorStk = color; }); // prettier-ignore
    }

    @computed get borderColorPicker() {
        return this.colorPicker(this.colorStk, (color: string) => { this.colorStk = color; }); // prettier-ignore
    }
 
    @computed get strokeAndFill() {
        return (
            <div>
                <div key="fill" className="strokeAndFill">
                    <div className="fill">
                        <div className="fill-title">Fill:</div>
                        <div className="fill-button">{this.fillButton}</div>
                    </div>
                    <div className="stroke">
                        <div className="stroke-title"> Stroke: </div>
                        <div className="stroke-button">{this.lineButton}</div>
                    </div>
                </div>
                {this._fillBtn ? this.fillPicker : ''}
                {this._lineBtn ? this.linePicker : ''}
            </div>
        );
    }

    @computed get dashdStk() {  return this.selectedDoc?.stroke_dash || ''; } // prettier-ignore
    set dashdStk(value) {
        value && (this._lastDash = value);
        this.selectedDoc && (this.selectedDoc[DocData].stroke_dash = value ? this._lastDash : undefined);
    }
    @computed get widthStk() { return this.getField('stroke_width') || '1'; } // prettier-ignore
    set widthStk(value) {
        this.selectedDoc && (this.selectedDoc[DocData].stroke_width = Number(value));
    }
    @computed get markScal() { return Number(this.getField('stroke_markerScale') || '1'); } // prettier-ignore
    set markScal(value) {
        this.selectedDoc && (this.selectedDoc[DocData].stroke_markerScale = Number(value));
    }
    @computed get markHead() { return this.getField('stroke_startMarker') || ''; } // prettier-ignore
    set markHead(value) {
        this.selectedDoc && (this.selectedDoc[DocData].stroke_startMarker = value);
    }
    @computed get markTail() {  return this.getField('stroke_endMarker') || ''; } // prettier-ignore
    set markTail(value) {
        this.selectedDoc && (this.selectedDoc[DocData].stroke_endMarker = value);
    }

    regInput = (key: string, value: any, setter: (val: string) => {}) => (
        <div className="inputBox">
            <input className="inputBox-input" type="text" value={value} style={{ color: SnappingManager.userColor, backgroundColor: SnappingManager.userBackgroundColor }} onChange={e => setter(e.target.value)} />
            <div className="inputBox-button">
                <div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
                    <FontAwesomeIcon icon="caret-up" size="sm" />
                </div>
                <div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
                    <FontAwesomeIcon icon="caret-down" size="sm" />
                </div>
            </div>
        </div>
    );

    @action
    CloseAll = () => {
        this.openContexts = false;
        this.openLinks = false;
        this.openOptions = false;
        this.openTransform = false;
        this.openFields = false;
        this.openSharing = false;
        this.openLayout = false;
        this.openFilters = false;
    };

    @computed get widthAndDash() {
        return (
            // prettier-ignore
            <div className="widthAndDash">
                <div className="width">
                    {this.getNumber(
                        'Thickness',
                        '',
                        0,
                        Math.max(50, this.strokeThk),
                        this.strokeThk,
                        (val: number) => { !isNaN(val) && (this.strokeThk = val); },
                        50,
                        1
                    )}
                </div>
                <div className="width">
                    {this.getNumber(
                        'Arrow Scale',
                        '',
                        0,
                        Math.max(10, this.markScal),
                        this.markScal,
                        (val: number) => {  !isNaN(val) && (this.markScal = val); },
                        10,
                        1
                    )}
                </div>

                <div className="arrows">
                    <div className="arrows-head">
                        <div className="arrows-head-title">Arrow Head: </div>
                        <input
                            key="markHead"
                            className="arrows-head-input"
                            type="checkbox"
                            checked={this.markHead !== ''}
                            onChange={undoBatch(action(() => {  this.markHead = this.markHead ? '' : 'arrow';  }))}
                        />
                    </div>
                    <div className="arrows-tail">
                        <div className="arrows-tail-title">Arrow End: </div>
                        <input
                            key="markTail"
                            className="arrows-tail-input"
                            type="checkbox"
                            checked={this.markTail !== ''}
                            onChange={undoBatch(
                                action(() => { this.markTail = this.markTail ? '' : 'arrow'; })
                            )}
                        />
                    </div>
                </div>
                <div className="dashed">
                    <div className="dashed-title">Dashed Line:</div>
                    <input key="markHead" className="dashed-input" type="checkbox" checked={this.dashdStk === '2'} onChange={this.changeDash} />
                </div>
            </div>
        );
    }

    @undoBatch
    changeDash = () => {
        this.dashdStk = this.dashdStk === '2' ? '0' : '2';
    };

    @computed get appearanceEditor() {
        return (
            <div className="appearance-editor">
                {this.widthAndDash}
                {this.strokeAndFill}
            </div>
        );
    }

    _sliderBatch: UndoManager.Batch | undefined;
    setFinalNumber = () => {
        this._sliderBatch?.end();
    };
    getNumber = (label: string, unit: string, min: number, max: number, number: number, setNumber: any, autorange?: number, autorangeMinVal?: number) => (
        <div key={label + (this.selectedDoc?.title ?? '')}>
            <NumberInput formLabel={label} formLabelPlacement="left" type={Type.SEC} unit={unit} fillWidth color={this.color} number={number} setNumber={setNumber} min={min} max={max} />
            <Slider
                key={label}
                onPointerDown={() => {
                    this._sliderBatch = UndoManager.StartBatch('slider ' + label);
                }}
                multithumb={false}
                color={this.color}
                size={Size.XSMALL}
                min={min}
                max={max}
                autorangeMinVal={autorangeMinVal}
                autorange={autorange}
                number={number}
                unit={unit}
                decimals={1}
                setFinalNumber={this.setFinalNumber}
                setNumber={setNumber}
                fillWidth
            />
        </div>
    );

    setVal = (func: (doc: Doc, val: number) => void) => (val: number) => this.selectedDoc && !isNaN(val) && func(this.selectedDoc, val);
    @computed get transformEditor() {
        return (
            // prettier-ignore
            <div className="transform-editor">
                {!this.isStack ? null : this.getNumber('Gap', ' px', 0, 200, NumCast(this.selectedDoc!.gridGap), this.setVal((doc: Doc, val: number) => { doc.gridGap = val; })) }
                {!this.isStack ? null : this.getNumber('xMargin', ' px', 0, 500, NumCast(this.selectedDoc!.xMargin), this.setVal((doc: Doc, val: number) => { doc.xMargin = val; })) }
                {!this.isStack ? null : this.getNumber('yMargin', ' px', 0, 500, NumCast(this.selectedDoc!.yMargin), this.setVal((doc: Doc, val: number) => { doc.yMargin = val; })) }
                {!this.isGroup ? null : this.getNumber('Padding', ' px', 0, 500, NumCast(this.selectedDoc!.xPadding), this.setVal((doc: Doc, val: number) => { doc.xPadding = doc.yPadding = val; })) }
                {this.isInk ? this.controlPointsButton : null}
                {this.getNumber('Width', ' px', 0, Math.max(1000, this.shapeWid), this.shapeWid, this.setVal((doc: Doc, val:number) => {this.shapeWid = val}), 1000, 1)}
                {this.getNumber('Height', ' px', 0, Math.max(1000, this.shapeHgt), this.shapeHgt, this.setVal((doc: Doc, val:number) => {this.shapeHgt = val}), 1000, 1)}
                {this.getNumber('X', ' px', this.shapeXps - 500, this.shapeXps + 500, this.shapeXps, this.setVal((doc: Doc, val:number) =>  {this.shapeXps = val}), 1000)}
                {this.getNumber('Y', ' px', this.shapeYps - 500, this.shapeYps + 500, this.shapeYps, this.setVal((doc: Doc, val:number) => {this.shapeYps = val}), 1000)}
            </div>
        );
    }

    @computed get optionsSubMenu() {
        return (
            // prettier-ignore
            <PropertiesSection title="Options" isOpen={this.openOptions} setIsOpen={bool => { this.openOptions = bool; }} onDoubleClick={this.CloseAll}>
                <PropertiesButtons />
            </PropertiesSection>
        );
    }

    @computed get sharingSubMenu() {
        return (
            // prettier-ignore
            <PropertiesSection
                title="Sharing and Permissions"
                isOpen={this.openSharing}
                setIsOpen={bool => { this.openSharing = bool;  }}
                onDoubleClick={this.CloseAll}>
                <>
                    {/* <div className="propertiesView-buttonContainer"> */}
                    <div className="propertiesView-acls-checkbox">
                        Layout Permissions
                        <Checkbox
                            color="primary"
                            onChange={action(() => {
                                this.layoutDocAcls = !this.layoutDocAcls;
                            })}
                            checked={this.layoutDocAcls}
                        />
                    </div>
                    {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
                    <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
                        <FontAwesomeIcon icon="redo-alt"  size="1x" />
                    </button>
                    </Tooltip> */}
                    {/* </div> */}
                    {this.sharingTable}
                </>
            </PropertiesSection>
        );
    }

    /**
     * Updates this.filterDoc's currentFilter and saves the childFilters on the currentFilter
     */
    updateFilterDoc = (doc: Doc) => {
        if (this.selectedDoc) {
            if (doc === this.selectedDoc.currentFilter) return; // causes problems if you try to reapply the same doc
            const savedDocFilters = doc._childFiltersList;
            const currentDocFilters = this.selectedDoc._childFilters;
            this.selectedDoc._childFilters = new List<string>();
            (this.selectedDoc.currentFilter as Doc)._childFiltersList = currentDocFilters;
            this.selectedDoc.currentFilter = doc;
            doc._childFiltersList = new List<string>();
            this.selectedDoc._childFilters = savedDocFilters;

            const savedDocRangeFilters = doc._childFiltersByRangesList;
            const currentDocRangeFilters = this.selectedDoc._childFiltersByRanges;
            this.selectedDoc._childFiltersByRanges = new List<string>();
            (this.selectedDoc.currentFilter as Doc)._childFiltersByRangesList = currentDocRangeFilters;
            this.selectedDoc.currentFilter = doc;
            doc._childFiltersByRangesList = new List<string>();
            this.selectedDoc._childFiltersByRanges = savedDocRangeFilters;
        }
    };

    @computed get filtersSubMenu() {
        return (
            // prettier-ignore
            <PropertiesSection title="Filters" isOpen={this.openFilters} setIsOpen={bool => { this.openFilters = bool; }} onDoubleClick={this.CloseAll}>
                <div className="propertiesView-content filters" style={{ position: 'relative', height: 'auto' }}>
                    <FilterPanel Document={this.selectedDoc ?? Doc.ActiveDashboard!} />
                </div>
            </PropertiesSection>
        );
    }

    @computed get inkSubMenu() {
        return (
            // prettier-ignore
            <>
                <PropertiesSection title="Appearance" isOpen={this.openAppearance} setIsOpen={bool => { this.openAppearance = bool; }} onDoubleClick={this.CloseAll}>
                    {this.selectedLayoutDoc?.layout_isSvg ? this.appearanceEditor : null}
                </PropertiesSection>
                <PropertiesSection title="Transform" isOpen={this.openTransform} setIsOpen={bool => { this.openTransform = bool; }} onDoubleClick={this.CloseAll}>
                    {this.transformEditor}
                </PropertiesSection>
            </>
        );
    }

    @computed get fieldsSubMenu() {
        return (
            <PropertiesSection
                title="Fields & Tags"
                isOpen={this.openFields}
                setIsOpen={bool => {
                    this.openFields = bool;
                }}
                onDoubleClick={this.CloseAll}>
                <div className="propertiesView-content fields">{Doc.noviceMode ? this.noviceFields : this.expandedField}</div>
            </PropertiesSection>
        );
    }

    @computed get contextsSubMenu() {
        return (
            <PropertiesSection
                title="Other Contexts"
                isOpen={this.openContexts}
                setIsOpen={bool => {
                    this.openContexts = bool;
                }}
                onDoubleClick={this.CloseAll}>
                {this.contextCount > 0 ? this.contexts : 'There are no other contexts.'}
            </PropertiesSection>
        );
    }

    @computed get linksSubMenu() {
        return (
            <PropertiesSection
                title="Linked To"
                isOpen={this.openLinks}
                setIsOpen={bool => {
                    this.openLinks = bool;
                }}
                onDoubleClick={this.CloseAll}>
                {this.linkCount > 0 ? this.links : 'There are no current links.'}
            </PropertiesSection>
        );
    }

    @computed get layoutSubMenu() {
        return (
            <PropertiesSection
                title="Layout"
                isOpen={this.openLayout}
                setIsOpen={bool => {
                    this.openLayout = bool;
                }}
                onDoubleClick={this.CloseAll}>
                {this.layoutPreview}
            </PropertiesSection>
        );
    }

    @computed get description() {
        return Field.toString(this.selectedLink?.link_description as any as FieldType);
    }
    @computed get relationship() {
        return StrCast(this.selectedLink?.link_relationship);
    }
    @observable private relationshipButtonColor: string = '';

    // @action
    // handleDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.link_description = e.target.value; }
    // handleRelationshipChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.link_relationship = e.target.value; }

    handleDescriptionChange = undoable(
        action((value: string) => {
            if (this.selectedLink) {
                this.setDescripValue(value);
            }
        }),
        'change link description'
    );

    handlelinkRelationshipChange = undoable(
        action((value: string) => {
            if (this.selectedLink) {
                this.setlinkRelationshipValue(value);
            }
        }),
        'change link relationship'
    );

    @undoBatch
    setDescripValue = action((value: string) => {
        if (this.selectedLink) {
            this.selectedLink[DocData].link_description = value;
        }
    });

    @undoBatch
    setlinkRelationshipValue = action((value: string) => {
        if (this.selectedLink) {
            const prevRelationship = StrCast(this.selectedLink.link_relationship);
            this.selectedLink.link_relationship = value;
            Doc.GetProto(this.selectedLink).link_relationship = value;
            const linkRelationshipList = StrListCast(Doc.UserDoc().link_relationshipList);
            const linkRelationshipSizes = NumListCast(Doc.UserDoc().link_relationshipSizes);
            const linkColorList = StrListCast(Doc.UserDoc().link_ColorList);

            // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color
            if (!linkRelationshipList?.includes(value)) {
                linkRelationshipList.push(value);
                linkRelationshipSizes.push(1);
                const randColor = 'rgb(' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ')';
                linkColorList.push(randColor);
                // if the relationship is already in the list AND the new rel is different from the prev rel, update the rel sizes
            } else if (linkRelationshipList && value !== prevRelationship) {
                const index = linkRelationshipList.indexOf(value);
                // increment size of new relationship size
                if (index !== -1 && index < linkRelationshipSizes.length) {
                    const pvalue = linkRelationshipSizes[index];
                    linkRelationshipSizes[index] = pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue + 1;
                }
                // decrement the size of the previous relationship if it already exists (i.e. not default 'link' relationship upon link creation)
                if (linkRelationshipList.includes(prevRelationship)) {
                    const pindex = linkRelationshipList.indexOf(prevRelationship);
                    if (pindex !== -1 && pindex < linkRelationshipSizes.length) {
                        const pvalue = linkRelationshipSizes[pindex];
                        linkRelationshipSizes[pindex] = Math.max(0, pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue - 1);
                    }
                }
            }
            this.relationshipButtonColor = 'rgb(62, 133, 55)';
            setTimeout(action(() => { this.relationshipButtonColor = ''; }),  750); // prettier-ignore
            return true;
        }
        return undefined;
    });

    changeFollowBehavior = undoable((loc: Opt<string>) => {
        this.sourceAnchor && (this.sourceAnchor.followLinkLocation = loc);
    }, 'change follow behavior');

    @undoBatch
    changeAnimationBehavior = action((behavior: string) => {
        this.sourceAnchor && (this.sourceAnchor.followLinkAnimEffect = behavior);
    });

    @undoBatch
    changeEffectDirection = action((effect: PresEffectDirection) => {
        this.sourceAnchor && (this.sourceAnchor.followLinkAnimDirection = effect);
    });

    animationDirection = (direction: PresEffectDirection, icon: string, gridColumn: number, gridRow: number, opts: object) => {
        const lanch = this.sourceAnchor;
        const color = lanch?.followLinkAnimDirection === direction || (direction === PresEffectDirection.Center && !lanch?.followLinkAnimDirection) ? Colors.MEDIUM_BLUE : '';
        return (
            <Tooltip title={<div className="dash-tooltip">{direction}</div>}>
                <div
                    style={{ ...opts, border: direction === PresEffectDirection.Center ? `solid 2px ${color}` : undefined, borderRadius: '20%', cursor: 'pointer', gridColumn, gridRow, justifySelf: 'center', background: color, color: 'black' }}
                    onClick={() => this.changeEffectDirection(direction)}>
                    {icon ? <FontAwesomeIcon icon={icon as any} /> : null}
                </div>
            </Tooltip>
        );
    };

    onSelectOutDesc = () => {
        this.setDescripValue(this.description);
        document.getElementById('link_description_input')?.blur();
    };

    onDescriptionKey = () => {
        // if (e.key === 'Enter') {
        //     this.setDescripValue(this.description);
        //     document.getElementById('link_description_input')?.blur();
        // }
    };

    onSelectOutRelationship = () => {
        this.setlinkRelationshipValue(this.relationship);
        document.getElementById('link_relationship_input')?.blur();
    };

    onRelationshipKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
        if (e.key === 'Enter') {
            this.setlinkRelationshipValue(this.relationship);
            document.getElementById('link_relationship_input')?.blur();
        }
    };

    toggleLinkProp = (e: React.PointerEvent, prop: string) => {
        setupMoveUpEvents(
            this,
            e,
            returnFalse,
            emptyFunction,
            undoBatch(action(() => { this.selectedLink && (this.selectedLink[prop] = !this.selectedLink[prop]); })) // prettier-ignore
        );
    };

    @computed get destinationAnchor() {
        const ldoc = this.selectedLink;
        const lanch = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.Instance.currentLinkAnchor;
        if (ldoc && lanch) return Doc.getOppositeAnchor(ldoc, lanch) ?? lanch;
        return ldoc ? DocCast(ldoc.link_anchor_2) : ldoc;
    }

    @computed get sourceAnchor() {
        const selAnchor = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.Instance.currentLinkAnchor;

        return selAnchor ?? (this.selectedLink && this.destinationAnchor ? Doc.getOppositeAnchor(this.selectedLink, this.destinationAnchor) : this.selectedLink);
    }

    toggleAnchorProp = (e: React.PointerEvent, prop: string, anchor?: Doc, value: any = true, ovalue: any = false, cb: (val: any) => any = val => val) => {
        anchor &&
            setupMoveUpEvents(
                this,
                e,
                returnFalse,
                emptyFunction,
                undoBatch(action(() => {
                    anchor[prop] = anchor[prop] === value ? ovalue : value;
                    this.selectedDoc && cb(anchor[prop]);
                })) // prettier-ignore
            );
    };

    @computed
    get editRelationship() {
        return (
            <input
                style={{ color: SnappingManager.userColor, backgroundColor: SnappingManager.userBackgroundColor }}
                autoComplete="off"
                id="link_relationship_input"
                value={StrCast(this.selectedLink?.link_relationship)}
                onKeyDown={this.onRelationshipKey}
                onBlur={this.onSelectOutRelationship}
                onChange={e => this.handlelinkRelationshipChange(e.currentTarget.value)}
                className="text"
                type="text"
            />
        );
    }

    @computed
    get editDescription() {
        return (
            <textarea
                autoComplete="off"
                style={{ textAlign: 'left', color: SnappingManager.userColor, backgroundColor: SnappingManager.userBackgroundColor }}
                id="link_description_input"
                value={StrCast(this.selectedLink?.link_description)}
                onKeyDown={this.onDescriptionKey}
                onBlur={this.onSelectOutDesc}
                onChange={e => this.handleDescriptionChange(e.currentTarget.value)}
                className="text"
            />
        );
    }

    // Converts seconds to ms and updates presTransition
    setZoom = (number: String, change?: number) => {
        let scale = Number(number) / 100;
        if (change) scale += change;
        if (scale < 0.01) scale = 0.01;
        if (scale > 1) scale = 1;
        this.sourceAnchor && (this.sourceAnchor.followLinkZoomScale = scale);
    };

    @computed get linkProperties() {
        const zoom = Number((NumCast(this.sourceAnchor?.followLinkZoomScale, 1) * 100).toPrecision(3));
        const targZoom = this.sourceAnchor?.followLinkZoom;
        const indent = 30;
        const hasSelectedAnchor = this.selectedLink !== this.selectedDoc && Doc.Links(this.sourceAnchor).includes(this.selectedLink!);

        return (
            <>
                <div className="propertiesView-section">
                    <div className="propertiesView-input first" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
                        <p>Relationship</p>
                        {this.editRelationship}
                    </div>
                    <div className="propertiesView-input" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
                        <p>Description</p>
                        {this.editDescription}
                    </div>
                </div>
                {!hasSelectedAnchor ? null : (
                    <div className="propertiesView-section">
                        <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 84px)' }}>
                            <p>Follow by</p>
                            <select
                                style={{ color: SnappingManager.userColor, backgroundColor: SnappingManager.userBackgroundColor }}
                                onChange={e => this.changeFollowBehavior(e.currentTarget.value === 'Default' ? undefined : e.currentTarget.value)}
                                value={Cast(this.sourceAnchor?.followLinkLocation, 'string', null)}>
                                <option value={undefined}>Default</option>
                                <option value={OpenWhere.addLeft}>Opening in new left pane</option>
                                <option value={OpenWhere.addRight}>Opening in new right pane</option>
                                <option value={OpenWhere.replaceLeft}>Replacing left tab</option>
                                <option value={OpenWhere.replaceRight}>Replacing right tab</option>
                                <option value={OpenWhere.lightboxAlways}>Opening in lightbox always</option>
                                <option value={OpenWhere.lightbox}>Opening in lightbox if not visible</option>
                                <option value={OpenWhere.add}>Opening in new tab</option>
                                <option value={OpenWhere.replace}>Replacing current tab</option>
                                <option value={OpenWhere.inParent}>Opening in same collection</option>
                                {this.selectedLink?.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
                            </select>
                        </div>
                        <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 134px) 50px' }}>
                            <p>Animation</p>
                            <select
                                style={{ width: '100%', gridColumn: 2, color: SnappingManager.userColor, backgroundColor: SnappingManager.userBackgroundColor }}
                                onChange={e => this.changeAnimationBehavior(e.currentTarget.value)}
                                value={StrCast(this.sourceAnchor?.followLinkAnimEffect, 'default')}>
                                <option value="default">Default</option>
                                {[PresEffect.None, PresEffect.Expand, PresEffect.Lightspeed, PresEffect.Fade, PresEffect.Flip, PresEffect.Rotate, PresEffect.Bounce, PresEffect.Roll].map(effect => (
                                    <option key={effect.toString()} value={effect.toString()}>
                                        {effect.toString()}
                                    </option>
                                ))}
                            </select>
                            <div className="effectDirection" style={{ marginLeft: '10px', display: 'grid', width: 40, height: 36, gridColumn: 3, gridTemplateRows: '12px 12px 12px' }}>
                                {this.animationDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
                                {this.animationDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
                                {this.animationDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
                                {this.animationDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
                                {this.animationDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
                            </div>
                        </div>
                        {PresBox.inputter(
                            '0.1',
                            '0.1',
                            '10',
                            NumCast(this.sourceAnchor?.followLinkTransitionTime) / 1000,
                            true,
                            (val: string) =>
                                PresBox.SetTransitionTime(val, (timeInMS: number) => {
                                    this.sourceAnchor && (this.sourceAnchor.followLinkTransitionTime = timeInMS);
                                }),
                            indent
                        )}{' '}
                        <div
                            className="slider-headers"
                            style={{
                                display: 'grid',
                                justifyContent: 'space-between',
                                width: `calc(100% - ${indent * 2}px)`,
                                marginLeft: indent,
                                marginRight: indent,
                                gridTemplateColumns: 'auto auto',
                                borderTop: 'solid',
                            }}>
                            <div className="slider-text">Fast</div>
                            <div className="slider-text">Slow</div>
                        </div>{' '}
                        <div className="propertiesView-input inline">
                            <p>Play Target Audio</p>
                            {
                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                <button
                                    type="button"
                                    style={{ background: !this.sourceAnchor?.followLinkAudio ? '' : '#4476f7', borderRadius: 3 }}
                                    onPointerDown={e => this.toggleAnchorProp(e, 'followLinkAudio', this.sourceAnchor)}
                                    onClick={e => e.stopPropagation()}
                                    className="propertiesButton">
                                    <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
                                </button>
                            }
                        </div>
                        <div className="propertiesView-input inline">
                            <p>Play Target Video</p>
                            {
                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                <button
                                    type="button"
                                    style={{ background: !this.sourceAnchor?.followLinkVideo ? '' : '#4476f7', borderRadius: 3 }}
                                    onPointerDown={e => this.toggleAnchorProp(e, 'followLinkVideo', this.sourceAnchor)}
                                    onClick={e => e.stopPropagation()}
                                    className="propertiesButton">
                                    <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
                                </button>
                            }
                        </div>
                        <div className="propertiesView-input inline">
                            <p>Zoom Text Selections</p>
                            {
                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                <button
                                    type="button"
                                    style={{ background: !this.sourceAnchor?.followLinkZoomText ? '' : '#4476f7', borderRadius: 3 }}
                                    onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoomText', this.sourceAnchor)}
                                    onClick={e => e.stopPropagation()}
                                    className="propertiesButton">
                                    <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
                                </button>
                            }
                        </div>
                        <div className="propertiesView-input inline">
                            <p>Toggle Follow to Outer Context</p>
                            {
                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                <button
                                    type="button"
                                    style={{ background: !this.sourceAnchor?.followLinkToOuterContext ? '' : '#4476f7', borderRadius: 3 }}
                                    onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToOuterContext', this.sourceAnchor)}
                                    onClick={e => e.stopPropagation()}
                                    className="propertiesButton">
                                    <FontAwesomeIcon className="fa-icon" icon={faWindowMaximize as IconLookup} size="lg" />
                                </button>
                            }
                        </div>
                        <div className="propertiesView-input inline">
                            <p>Toggle Target (Show/Hide)</p>
                            {
                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                <button
                                    type="button"
                                    style={{ background: !this.sourceAnchor?.followLinkToggle ? '' : '#4476f7', borderRadius: 3 }}
                                    onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToggle', this.sourceAnchor)}
                                    onClick={e => e.stopPropagation()}
                                    className="propertiesButton">
                                    <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
                                </button>
                            }
                        </div>
                        <div className="propertiesView-input inline">
                            <p>Ease Transitions</p>
                            {
                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                <button
                                    type="button"
                                    style={{ background: this.sourceAnchor?.followLinkEase === 'linear' ? '' : '#4476f7', borderRadius: 3 }}
                                    onPointerDown={e => this.toggleAnchorProp(e, 'followLinkEase', this.sourceAnchor, 'ease', 'linear')}
                                    onClick={e => e.stopPropagation()}
                                    className="propertiesButton">
                                    <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
                                </button>
                            }
                        </div>
                        <div className="propertiesView-input inline">
                            <p>Capture Offset to Target</p>
                            {
                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                <button
                                    type="button"
                                    style={{ background: this.sourceAnchor?.followLinkXoffset === undefined ? '' : '#4476f7', borderRadius: 3 }}
                                    onPointerDown={e => {
                                        this.toggleAnchorProp(e, 'followLinkXoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.x) - NumCast(this.sourceAnchor?.x), undefined);
                                        this.toggleAnchorProp(e, 'followLinkYoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.y) - NumCast(this.sourceAnchor?.y), undefined);
                                    }}
                                    onClick={e => e.stopPropagation()}
                                    className="propertiesButton">
                                    <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
                                </button>
                            }
                        </div>
                        <div className="propertiesView-input inline">
                            <p>Center Target (no zoom)</p>
                            {
                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                <button
                                    type="button"
                                    style={{ background: this.sourceAnchor?.followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
                                    onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
                                    onClick={e => e.stopPropagation()}
                                    className="propertiesButton">
                                    <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
                                </button>
                            }
                        </div>
                        <div className="propertiesView-input inline" style={{ display: 'grid', gridTemplateColumns: '78px calc(100% - 108px) 50px' }}>
                            <p>Zoom %</p>
                            <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}>
                                <input className="presBox-input" style={{ width: '100%', color: SnappingManager.userColor, backgroundColor: SnappingManager.userBackgroundColor }} readOnly type="number" value={zoom} />
                                <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
                                    <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
                                        <FontAwesomeIcon icon="caret-up" />
                                    </div>
                                    <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
                                        <FontAwesomeIcon icon="caret-down" />
                                    </div>
                                </div>
                            </div>
                            {
                                // eslint-disable-next-line jsx-a11y/control-has-associated-label
                                <button
                                    type="button"
                                    style={{ background: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? '' : '#4476f7', borderRadius: 3, gridColumn: 3 }}
                                    onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
                                    onClick={e => e.stopPropagation()}
                                    className="propertiesButton">
                                    <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
                                </button>
                            }
                        </div>
                        {!targZoom ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
                        <div
                            className="slider-headers"
                            style={{
                                display: !targZoom ? 'none' : 'grid',
                                justifyContent: 'space-between',
                                width: `calc(100% - ${indent * 2}px)`,
                                marginLeft: indent,
                                marginRight: indent,
                                gridTemplateColumns: 'auto auto',
                                borderTop: 'solid',
                            }}>
                            <div className="slider-text">0%</div>
                            <div className="slider-text">100%</div>
                        </div>
                    </div>
                )}
            </>
        );
    }

    /**
     * Handles adding and removing members from the sharing panel
     */
    // handleUserChange = (selectedUser: string, add: boolean) => {
    //     if (!Doc.UserDoc().sidebarUsersDisplayed) Doc.UserDoc().sidebarUsersDisplayed = new Doc;
    //     DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => {
    //         sidebarUsersDisplayed![`display-${selectedUser}`] = add;
    //         !add && runInAction(() => this.selectedUser = "");
    //     });
    // }

    render() {
        const isNovice = Doc.noviceMode;
        if (!this.selectedDoc && !this.isPres) {
            return (
                <div className="propertiesView" style={{ width: this._props.width }}>
                    <div className="propertiesView-title" style={{ width: this._props.width }}>
                        No Document Selected
                    </div>
                </div>
            );
        }
        if (this.selectedDoc && !this.isPres) {
            return (
                <div
                    className="propertiesView"
                    style={{
                        background: SnappingManager.userBackgroundColor,
                        color: SnappingManager.userColor,
                        width: this._props.width,
                        minWidth: this._props.width,
                    }}>
                    <div className="propertiesView-propAndInfoGrouping">
                        <div className="propertiesView-sectionTitle" style={{ width: this._props.width }}>
                            Properties
                            <div className="propertiesView-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/properties')}>
                                <IconButton icon={<FontAwesomeIcon icon="info-circle" />} color={SnappingManager.userColor} />
                            </div>
                        </div>
                    </div>

                    <div className="propertiesView-name">{this.editableTitle}</div>
                    <div className="propertiesView-type"> {this.currentType} </div>
                    {/* {this.stylingSubMenu} */}
                    {this.fieldsSubMenu}
                    {this.optionsSubMenu}
                    {this.linksSubMenu}
                    {!this.selectedLink || !this.openLinks ? null : this.linkProperties}
                    {this.inkSubMenu}
                    {this.contextsSubMenu}
                    {isNovice ? null : this.sharingSubMenu}
                    {this.filtersSubMenu}
                    {isNovice ? null : this.layoutSubMenu}
                </div>
            );
        }
        if (this.isPres && PresBox.Instance) {
            const selectedItem: boolean = PresBox.Instance.selectedArray.size > 0;
            const type = [DocumentType.AUDIO, DocumentType.VID].includes(DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as any as DocumentType)
                ? (DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as any as DocumentType)
                : PresBox.targetRenderedDoc(PresBox.Instance.activeItem)?.type;
            return (
                <div className="propertiesView" style={{ width: this._props.width }}>
                    <div className="propertiesView-sectionTitle" style={{ width: this._props.width }}>
                        Presentation
                        <div className="propertiesView-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/features/trails/')}>
                            <IconButton icon={<FontAwesomeIcon icon="info-circle" />} color={SnappingManager.userColor} />
                        </div>
                    </div>
                    <div className="propertiesView-name" style={{ borderBottom: 0 }}>
                        {this.editableTitle}
                        <div className="propertiesView-presSelected">
                            <div className="propertiesView-selectedCount">{PresBox.Instance.selectedArray.size} selected</div>
                            <div className="propertiesView-selectedList">{PresBox.Instance.listOfSelected}</div>
                        </div>
                    </div>
                    {!selectedItem ? null : (
                        <div className="propertiesView-presentationTrails">
                            <div
                                className="propertiesView-presentationTrails-title"
                                onPointerDown={action(() => {
                                    this.openPresTransitions = !this.openPresTransitions;
                                })}
                                style={{
                                    color: SnappingManager.userColor,
                                    backgroundColor: this.openPresTransitions ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor,
                                }}>
                                &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon="rocket" /> &nbsp; Transitions
                                <div className="propertiesView-presentationTrails-title-icon">
                                    <FontAwesomeIcon icon={this.openPresTransitions ? 'caret-down' : 'caret-right'} size="lg" />
                                </div>
                            </div>
                            {this.openPresTransitions ? <div className="propertiesView-presentationTrails-content">{PresBox.Instance.transitionDropdown}</div> : null}
                        </div>
                    )}
                    {!selectedItem ? null : (
                        <div className="propertiesView-presentationTrails">
                            <div
                                className="propertiesView-presentationTrails-title"
                                onPointerDown={action(() => {
                                    this.openPresVisibilityAndDuration = !this.openPresVisibilityAndDuration;
                                })}
                                style={{
                                    color: SnappingManager.userColor,
                                    backgroundColor: this.openPresVisibilityAndDuration ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor,
                                }}>
                                &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon="rocket" /> &nbsp; Visibility
                                <div className="propertiesView-presentationTrails-title-icon">
                                    <FontAwesomeIcon icon={this.openPresVisibilityAndDuration ? 'caret-down' : 'caret-right'} size="lg" />
                                </div>
                            </div>
                            {this.openPresVisibilityAndDuration ? <div className="propertiesView-presentationTrails-content">{PresBox.Instance.visibilityDurationDropdown}</div> : null}
                        </div>
                    )}
                    {!selectedItem ? null : (
                        <div className="propertiesView-presentationTrails">
                            <div
                                className="propertiesView-presentationTrails-title"
                                onPointerDown={action(() => {
                                    this.openPresProgressivize = !this.openPresProgressivize;
                                })}
                                style={{
                                    color: SnappingManager.userColor,
                                    backgroundColor: this.openPresProgressivize ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor,
                                }}>
                                &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon="rocket" /> &nbsp; Progressivize
                                <div className="propertiesView-presentationTrails-title-icon">
                                    <FontAwesomeIcon icon={this.openPresProgressivize ? 'caret-down' : 'caret-right'} size="lg" />
                                </div>
                            </div>
                            {this.openPresProgressivize ? <div className="propertiesView-presentationTrails-content">{PresBox.Instance.progressivizeDropdown}</div> : null}
                        </div>
                    )}
                    {!selectedItem || (type !== DocumentType.VID && type !== DocumentType.AUDIO) ? null : (
                        <div className="propertiesView-presentationTrails">
                            <div
                                className="propertiesView-presentationTrails-title"
                                onPointerDown={action(() => {
                                    this.openSlideOptions = !this.openSlideOptions;
                                })}
                                style={{
                                    color: SnappingManager.userColor,
                                    backgroundColor: this.openSlideOptions ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor,
                                }}>
                                &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={type === DocumentType.AUDIO ? 'file-audio' : 'file-video'} /> &nbsp; {type === DocumentType.AUDIO ? 'Audio Options' : 'Video Options'}
                                <div className="propertiesView-presentationTrails-title-icon">
                                    <FontAwesomeIcon icon={this.openSlideOptions ? 'caret-down' : 'caret-right'} size="lg" />
                                </div>
                            </div>
                            {this.openSlideOptions ? <div className="propertiesView-presentationTrails-content">{PresBox.Instance.mediaOptionsDropdown}</div> : null}
                        </div>
                    )}
                </div>
            );
        }
        return null;
    }
}
