import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Icon, Tooltip } from '@material-ui/core'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; import { RichTextField } from '../../fields/RichTextField'; import { BoolCast, ScriptCast, StrCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; import { Utils } from '../../Utils'; import { DocUtils } from '../documents/Documents'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { IsFollowLinkScript } from '../util/LinkFollower'; import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; import { undoable, undoBatch } from '../util/UndoManager'; import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; import { DocumentView, OpenWhere } from './nodes/DocumentView'; import { pasteImageBitmap } from './nodes/WebBoxRenderer'; import './PropertiesButtons.scss'; import React = require('react'); import { JsxElement } from 'typescript'; import { FaThumbtack } from 'react-icons/fa'; import { AiOutlineApple } from 'react-icons/ai'; const higflyout = require('@hig/flyout'); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; enum UtilityButtonState { Default, OpenRight, OpenExternally, } @observer export class PropertiesButtons extends React.Component<{}, {}> { @observable public static Instance: PropertiesButtons; @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || SelectionManager.Views().lastElement()?.rootDoc; } @computed get selectedTabView() { return !SelectionManager.SelectedSchemaDoc() && SelectionManager.Views().lastElement()?.topMost; } propertyToggleBtn = (label: string, property: string, tooltip: (on?: any) => string, icon: (on: boolean) => any, onClick?: (dv: Opt, doc: Doc, property: string) => void, useUserDoc?: boolean) => { const targetDoc = useUserDoc ? Doc.UserDoc() : this.selectedDoc; const onPropToggle = (dv: Opt, doc: Doc, prop: string) => ((dv?.layoutDoc || doc)[prop] = (dv?.layoutDoc || doc)[prop] ? false : true); return !targetDoc ? null : ( {tooltip(targetDoc?.[property])} } placement="top">
e.stopPropagation()} onClick={undoable(() => { if (SelectionManager.Views().length > 1) { SelectionManager.Views().forEach(dv => (onClick ?? onPropToggle)(dv, dv.rootDoc, property)); } else if (targetDoc) (onClick ?? onPropToggle)(undefined, targetDoc, property); }, property)}> {icon((BoolCast(targetDoc?.[property])) as any)} {/* */}
{label}
); }; @computed get lockButton() { return this.propertyToggleBtn( 'No\xA0Drag', '_lockedPosition', on => `${on ? 'Unlock' : 'Lock'} position to prevent dragging`, on => // on => 'thumbtack' ); } // @computed get maskButton() { // return this.propertyToggleBtn( // 'Mask', // 'stroke_isInkMask', // on => (on ? 'Make plain ink' : 'Make highlight mask'), // on => 'paint-brush', // (dv, doc) => InkingStroke.toggleMask(dv?.layoutDoc || doc) // ); // } // @computed get hideImageButton() { // return this.propertyToggleBtn( // 'Background', // '_hideImage', // on => (on ? 'Show Image' : 'Show Background'), // on => 'portrait' // ); // } // @computed get clustersButton() { // return this.propertyToggleBtn( // 'Clusters', // '_freeform_useClusters', // on => `${on ? 'Hide' : 'Show'} clusters`, // on => 'braille' // ); // } // @computed get panButton() { // return this.propertyToggleBtn( // 'Lock\xA0View', // '_lockedTransform', // on => `${on ? 'Unlock' : 'Lock'} panning of view`, // on => 'lock' // ); // } // @computed get forceActiveButton() { // return this.propertyToggleBtn( // 'Active', // '_forceActive', // on => `${on ? 'Select to activate' : 'Contents always active'} `, // on => 'eye' // ); // } // @computed get fitContentButton() { // return this.propertyToggleBtn( // 'View All', // '_freeform_fitContentsToBox', // on => `${on ? "Don't" : 'Do'} fit content to container visible area`, // on => 'object-group' // ); // } // // this implments a container pattern by marking the targetDoc (collection) as a lightbox // // that always fits its contents to its container and that hides all other documents when // // a link is followed that targets a 'lightbox' destination // @computed get isLightboxButton() { // return this.propertyToggleBtn( // 'Lightbox', // 'isLightbox', // on => `${on ? 'Set' : 'Remove'} lightbox flag`, // on => 'window-restore', // onClick => { // SelectionManager.Views().forEach(dv => { // const containerDoc = dv.rootDoc; // //containerDoc.followAllLinks = // // containerDoc.noShadow = // // containerDoc.disableDocBrushing = // // containerDoc._forceActive = // //containerDoc._freeform_fitContentsToBox = // containerDoc._isLightbox = !containerDoc._isLightbox; // //containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined; // const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]); // //dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' }); // containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.layout_linkDisplay = false))); // }); // } // ); // } // @computed get layout_fitWidthButton() { // return this.propertyToggleBtn( // 'Fit\xA0Width', // '_layout_fitWidth', // on => `${on ? "Don't" : 'Do'} fit content to width of container`, // on => 'arrows-alt-h' // ); // } // @computed get captionButton() { // return this.propertyToggleBtn( // 'Caption', // '_layout_showCaption', // on => `${on ? 'Hide' : 'Show'} caption footer`, // on => 'closed-captioning', // (dv, doc) => ((dv?.rootDoc || doc)._layout_showCaption = (dv?.rootDoc || doc)._layout_showCaption === undefined ? 'caption' : undefined) // ); // } // @computed get chromeButton() { // return this.propertyToggleBtn( // 'Controls', // '_chromeHidden', // on => `${on ? 'Show' : 'Hide'} editing UI`, // on => 'edit', // (dv, doc) => ((dv?.rootDoc || doc)._chromeHidden = !(dv?.rootDoc || doc)._chromeHidden) // ); // } // @computed get titleButton() { // return this.propertyToggleBtn( // 'Title', // '_layout_showTitle', // on => 'Switch between title styles', // on => 'text-width', // (dv, doc) => { // const tdoc = dv?.rootDoc || doc; // const newtitle = !tdoc._layout_showTitle ? 'title' : tdoc._layout_showTitle === 'title' ? 'title:hover' : ''; // tdoc._layout_showTitle = newtitle; // } // ); // } // @computed get layout_autoHeightButton() { // return this.propertyToggleBtn( // 'Auto\xA0Size', // '_layout_autoHeight', // on => `Automatical vertical sizing to show all content`, // on => 'arrows-alt-v' // ); // } // @computed get gridButton() { // return this.propertyToggleBtn( // 'Grid', // '_freeform_backgroundGrid', // on => `Display background grid in collection`, // on => 'border-all' // ); // } // @computed get groupButton() { // return this.propertyToggleBtn( // 'Group', // 'isGroup', // on => `Display collection as a Group`, // on => 'object-group', // (dv, doc) => { // doc.isGroup = !doc.isGroup; // doc.forceActive = doc.isGroup; // } // ); // } // @computed get freezeThumb() { // return this.propertyToggleBtn( // 'FreezeThumb', // '_thumb-frozen', // on => `${on ? 'Freeze' : 'Unfreeze'} thumbnail`, // on => 'snowflake', // (dv, doc) => { // if (doc['thumb-frozen']) doc['thumb-frozen'] = undefined; // else { // document.body.focus(); // so that we can access the clipboard without an error // setTimeout(() => // pasteImageBitmap((data_url: any, error: any) => { // error && console.log(error); // data_url && Utils.convertDataUri(data_url, doc[Id] + '-thumb-frozen', true).then(returnedfilename => (doc['thumb-frozen'] = new ImageField(returnedfilename))); // }) // ); // } // } // ); // } // @computed get snapButton() { // return this.propertyToggleBtn( // 'Snap\xA0Lines', // 'freeform_snapLines', // on => `Display snapping lines when objects are dragged`, // on => 'th', // undefined, // true // ); // } // @computed // get onClickButton() { // return !this.selectedDoc ? null : ( // Choose onClick behavior} placement="top"> //
//
// //
e.stopPropagation()}> // //
//
//
//
onclick
//
//
// ); // } // @computed // get perspectiveButton() { // return !this.selectedDoc ? null : ( // Choose view perspective} placement="top"> //
//
// //
e.stopPropagation()}> // //
//
//
//
Perspective
//
//
// ); // } @undoBatch handlePerspectiveChange = (e: any) => { this.selectedDoc && (this.selectedDoc._type_collection = e.target.value); SelectionManager.Views() .filter(dv => dv.docView) .map(dv => dv.docView!) .forEach(docView => (docView.layoutDoc._type_collection = e.target.value)); }; @undoBatch @action handleOptionChange = (onClick: string) => { SelectionManager.Views() .filter(dv => dv.docView) .map(dv => dv.docView!) .forEach(docView => { const linkButton = IsFollowLinkScript(docView.props.Document.onClick); docView.noOnClick(); switch (onClick) { case 'enterPortal': docView.makeIntoPortal(); break; case 'toggleDetail': docView.setToggleDetail(); break; case 'linkInPlace': docView.toggleFollowLink(false, false); docView.props.Document.followLinkLocation = linkButton ? OpenWhere.lightbox : undefined; break; case 'linkOnRight': docView.toggleFollowLink(false, false); docView.props.Document.followLinkLocation = linkButton ? OpenWhere.addRight : undefined; break; } }); }; @undoBatch editOnClickScript = () => { if (SelectionManager.Views().length) SelectionManager.Views().forEach(dv => DocUtils.makeCustomViewClicked(dv.rootDoc, undefined, 'onClick')); else this.selectedDoc && DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, 'onClick'); }; @computed get onClickFlyout() { const buttonList = [ ['nothing', 'Select Document'], ['enterPortal', 'Enter Portal'], ['toggleDetail', 'Toggle Detail'], ['linkInPlace', 'Open Link in Lightbox'], ['linkOnRight', 'Open Link on Right'], ]; const list = buttonList.map(value => { const click = () => this.handleOptionChange(value[0]); const linkButton = IsFollowLinkScript(this.selectedDoc.onClick); const followLoc = this.selectedDoc._followLinkLocation; const linkedToLightboxView = () => LinkManager.Links(this.selectedDoc).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); let active = false; // prettier-ignore switch (value[0]) { case 'linkInPlace': active = linkButton && followLoc === OpenWhere.lightbox && !linkedToLightboxView(); break; case 'linkOnRight': active = linkButton && followLoc === OpenWhere.addRight; break; case 'enterPortal': active = linkButton && this.selectedDoc._followLinkLocation === OpenWhere.lightbox && linkedToLightboxView(); break; case 'toggleDetail':active = ScriptCast(this.selectedDoc.onClick)?.script.originalScript.includes('toggleDetail'); break; case 'nothing': active = !linkButton && this.selectedDoc.onClick === undefined;break; } return (
{value[1]}
); }); return (
{list}
{Doc.noviceMode ? null : (
{' '} Edit onClick Script
)}
); } @computed get onPerspectiveFlyout() { const excludedViewTypes = [CollectionViewType.Invalid, CollectionViewType.Docking, CollectionViewType.Pile, CollectionViewType.StackedTimeline, CollectionViewType.Linear]; const makeLabel = (value: string, label: string) => (
); return (
{Object.values(CollectionViewType) .filter(type => !excludedViewTypes.includes(type)) .map(type => makeLabel(type, type))}
); } render() { const layoutField = this.selectedDoc?.[Doc.LayoutFieldKey(this.selectedDoc)]; const isText = layoutField instanceof RichTextField; const isInk = layoutField instanceof InkField; const isImage = layoutField instanceof ImageField; const isMap = this.selectedDoc?.type === DocumentType.MAP; const isCollection = this.selectedDoc?.type === DocumentType.COL; //TODO: will likely need to create separate note-taking view type here const isStacking = this.selectedDoc?._type_collection === CollectionViewType.Stacking || this.selectedDoc?._type_collection === CollectionViewType.NoteTaking; const isFreeForm = this.selectedDoc?._type_collection === CollectionViewType.Freeform; const isTree = this.selectedDoc?._type_collection === CollectionViewType.Tree; const isTabView = this.selectedTabView; const toggle = (ele: JSX.Element | null, style?: React.CSSProperties) => (
{' '} {ele}{' '}
); const isNovice = Doc.noviceMode; return !this.selectedDoc ? null : (
{/* {toggle(this.titleButton)} {toggle(this.captionButton)} */} {toggle(this.lockButton)} {/* {toggle(this.onClickButton)} {toggle(this.layout_fitWidthButton)} {toggle(this.freezeThumb)} {toggle(this.forceActiveButton)} {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })} {toggle(this.isLightboxButton, { display: !isFreeForm && !isMap ? 'none' : '' })} {toggle(this.layout_autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })} {toggle(this.maskButton, { display: !isInk ? 'none' : '' })} {toggle(this.hideImageButton, { display: !isImage ? 'none' : '' })} {toggle(this.chromeButton, { display: !isCollection || isNovice ? 'none' : '' })} {toggle(this.gridButton, { display: !isCollection ? 'none' : '' })} {toggle(this.groupButton, { display: isTabView || !isCollection ? 'none' : '' })} {toggle(this.snapButton, { display: !isCollection ? 'none' : '' })} {toggle(this.clustersButton, { display: !isFreeForm ? 'none' : '' })} {toggle(this.panButton, { display: !isFreeForm ? 'none' : '' })} {toggle(this.perspectiveButton, { display: !isCollection || isNovice ? 'none' : '' })} */}
); } }