import { computed } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, DocListCast, StrListCast } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { List } from '../../fields/List'; import { NumCast, StrCast } from '../../fields/Types'; import { emptyFunction, OmitKeys, returnAll, returnOne, returnTrue, returnZero } from '../../Utils'; import { Docs, DocUtils } from '../documents/Documents'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { Transform } from '../util/Transform'; import { CollectionStackingView } from './collections/CollectionStackingView'; import { FieldViewProps } from './nodes/FieldView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { SearchBox } from './search/SearchBox'; import './SidebarAnnos.scss'; import { StyleProp } from './StyleProvider'; import React = require('react'); interface ExtraProps { fieldKey: string; layoutDoc: Doc; rootDoc: Doc; dataDoc: Doc; // usePanelWidth: boolean; showSidebar: boolean; nativeWidth: number; whenChildContentsActiveChanged: (isActive: boolean) => void; ScreenToLocalTransform: () => Transform; sidebarAddDocument: (doc: Doc | Doc[], suffix: string) => boolean; removeDocument: (doc: Doc | Doc[], suffix: string) => boolean; moveDocument: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean, annotationKey?: string) => boolean; } @observer export class SidebarAnnos extends React.Component { constructor(props: Readonly) { super(props); // this.props.dataDoc[this.sidebarKey] = new List(); // bcz: can't do this here. it blows away existing things and isn't a robust solution for making sure the field exists -- instead this should happen when the document is created and/or shared } _stackRef = React.createRef(); @computed get allMetadata() { const keys = new Set(); DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => SearchBox.documentKeys(doc).forEach(key => keys.add(key))); return Array.from(keys.keys()) .filter(key => key[0]) .filter(key => key[0] !== '_' && key[0] === key[0].toUpperCase()) .sort(); } @computed get allUsers() { const keys = new Set(); DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author))); return Array.from(keys.keys()).sort(); } get filtersKey() { return '_' + this.sidebarKey + '-docFilters'; } anchorMenuClick = (anchor: Doc) => { const startup = StrListCast(this.props.rootDoc.docFilters) .map(filter => filter.split(':')[0]) .join(' '); const target = Docs.Create.TextDocument(startup, { title: '-note-', annotationOn: this.props.rootDoc, _width: 200, _height: 50, _fitWidth: true, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize), _fontFamily: StrCast(Doc.UserDoc().fontFamily), }); FormattedTextBox.SelectOnLoad = target[Id]; FormattedTextBox.DontSelectInitialText = true; this.allMetadata.map(tag => (target[tag] = tag)); DocUtils.MakeLink({ doc: anchor }, { doc: target }, 'inline comment:comment on'); this.addDocument(target); this._stackRef.current?.focusDocument(target); }; makeDocUnfiltered = (doc: Doc) => { if (DocListCast(this.props.rootDoc[this.sidebarKey]).includes(doc)) { if (this.props.layoutDoc[this.filtersKey]) { this.props.layoutDoc[this.filtersKey] = new List(); } return true; } return false; }; get sidebarKey() { return this.props.fieldKey + '-sidebar'; } filtersHeight = () => 38; screenToLocalTransform = () => this.props .ScreenToLocalTransform() .translate(Doc.NativeWidth(this.props.dataDoc), 0) .scale(this.props.NativeDimScaling?.() || 1); // panelWidth = () => !this.props.layoutDoc._showSidebar ? 0 : // this.props.usePanelWidth ? this.props.PanelWidth() : // (NumCast(this.props.layoutDoc.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.layoutDoc.nativeWidth); panelWidth = () => !this.props.showSidebar ? 0 : this.props.layoutDoc.type === DocumentType.RTF || this.props.layoutDoc.type === DocumentType.MAP ? this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) : ((NumCast(this.props.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth()) / NumCast(this.props.nativeWidth); panelHeight = () => this.props.PanelHeight() - this.filtersHeight(); addDocument = (doc: Doc | Doc[]) => this.props.sidebarAddDocument(doc, this.sidebarKey); moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey); removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey); docFilters = () => [...StrListCast(this.props.layoutDoc._docFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])]; showTitle = () => 'title'; setHeightCallback = (height: number) => this.props.setHeight?.(height + this.filtersHeight()); render() { const renderTag = (tag: string) => { const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:check`); return (
Doc.setDocFilter(this.props.rootDoc, tag, tag, 'check', true, this.sidebarKey, e.shiftKey)}> {tag}
); }; const renderMeta = (tag: string) => { const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:exists`); return (
Doc.setDocFilter(this.props.rootDoc, tag, tag, 'exists', true, this.sidebarKey, e.shiftKey)}> {tag}
); }; const renderUsers = (user: string) => { const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`author:${user}:check`); return (
Doc.setDocFilter(this.props.rootDoc, 'author', user, 'check', true, this.sidebarKey, e.shiftKey)}> {user}
); }; // TODO: Calculation of the topbar is hardcoded and different for text nodes - it should all be the same and all be part of SidebarAnnos return !this.props.showSidebar ? null : (
e.stopPropagation()}> {this.allUsers.map(renderUsers)} {this.allMetadata.map(renderMeta)}
); } }