aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/FieldView.tsx
blob: 5b47dd91d495d2312b9a899b10f1337f3ea92d2f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import { observer } from 'mobx-react';
import * as React from 'react';
import { DateField } from '../../../fields/DateField';
import { Doc, Field, Opt } from '../../../fields/Doc';
import { List } from '../../../fields/List';
import { ScriptField } from '../../../fields/ScriptField';
import { WebField } from '../../../fields/URLField';
import { dropActionType } from '../../util/DragManager';
import { Transform } from '../../util/Transform';
import { ViewBoxInterface } from '../DocComponent';
import { CollectionFreeFormDocumentView } from './CollectionFreeFormDocumentView';
import { DocumentView, OpenWhere } from './DocumentView';
import { PinProps } from './trails';
import { computed } from 'mobx';

export interface FocusViewOptions {
    willPan?: boolean; // determines whether to pan to target document
    willZoomCentered?: boolean; // determines whether to zoom in on target document.  if zoomScale is 0, this just centers the document
    zoomScale?: number; // percent of containing frame to zoom into document
    zoomTime?: number;
    didMove?: boolean; // whether a document was changed during the showDocument process
    docTransform?: Transform; // when a document can't be panned and zoomed within its own container (say a group), then we need to continue to move up the render hierarchy to find something that can pan and zoom.  when this happens the docTransform must accumulate all the transforms of each level of the hierarchy
    instant?: boolean; // whether focus should happen instantly (as opposed to smooth zoom)
    preview?: boolean; // whether changes should be previewed by the componentView or written to the document
    effect?: Doc; // animation effect for focus
    noSelect?: boolean; // whether target should be selected after focusing
    playAudio?: boolean; // whether to play audio annotation on focus
    playMedia?: boolean; // whether to play start target videos
    openLocation?: OpenWhere; // where to open a missing document
    zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections
    toggleTarget?: boolean; // whether to toggle target on and off
    anchorDoc?: Doc; // doc containing anchor info to apply at end of focus to target doc
    easeFunc?: 'linear' | 'ease'; // transition method for scrolling
}
export type FocusFuncType = (doc: Doc, options: FocusViewOptions) => Opt<number>;
export type StyleProviderFuncType = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => any;
//
// these properties get assigned through the render() method of the DocumentView when it creates this node.
// However, that only happens because the properties are "defined" in the markup for the field view.
// See the LayoutString method on each field view :   ImageBox, FormattedTextBox, etc.
//
export interface FieldViewSharedProps {
    Document: Doc;
    TemplateDataDocument?: Doc;
    LayoutTemplateString?: string;
    LayoutTemplate?: () => Opt<Doc>;
    renderDepth: number;
    scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
    xPadding?: number;
    yPadding?: number;
    dontRegisterView?: boolean;
    dropAction?: dropActionType;
    dragAction?: dropActionType;
    forceAutoHeight?: boolean;
    ignoreAutoHeight?: boolean;
    disableBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
    hideClickBehaviors?: boolean; // whether to suppress menu item options for changing click behaviors
    ignoreUsePath?: boolean; // ignore the usePath field for selecting the fieldKey (eg., on text docs)
    CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView;
    containerViewPath?: () => DocumentView[];
    fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document
    isGroupActive?: () => string | undefined; // is this document part of a group that is active
    setContentViewBox?: (view: ViewBoxInterface) => any; // called by rendered field's viewBox so that DocumentView can make direct calls to the viewBox
    PanelWidth: () => number;
    PanelHeight: () => number;
    isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
    isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
    childFilters: () => string[];
    childFiltersByRanges: () => string[];
    styleProvider: Opt<StyleProviderFuncType>;
    setTitleFocus?: () => void;
    focus: FocusFuncType;
    onClickScript?: () => ScriptField;
    onDoubleClickScript?: () => ScriptField;
    onPointerDownScript?: () => ScriptField;
    onPointerUpScript?: () => ScriptField;
    onBrowseClickScript?: () => ScriptField | undefined;
    onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined;
    layout_fitWidth?: (doc: Doc) => boolean | undefined;
    searchFilterDocs: () => Doc[];
    layout_showTitle?: () => string;
    whenChildContentsActiveChanged: (isActive: boolean) => void;
    rootSelected?: () => boolean; // whether the root of a template has been selected
    addDocTab: (doc: Doc, where: OpenWhere) => boolean;
    filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
    addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean;
    removeDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean;
    moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) => boolean;
    pinToPres: (document: Doc, pinProps: PinProps) => void;
    ScreenToLocalTransform: () => Transform;
    bringToFront?: (doc: Doc, sendToBack?: boolean) => void;
    waitForDoubleClickToClick?: () => 'never' | 'always' | undefined;
    defaultDoubleClick?: () => 'default' | 'ignore' | undefined;
    pointerEvents?: () => Opt<string>;
}

/**
 * FieldView specific props that are not shared with DocumentView props
 *  */
export interface FieldViewProps extends FieldViewSharedProps {
    DocumentView?: () => DocumentView;
    fieldKey: string;
    isSelected: () => boolean;
    select: (ctrlPressed: boolean, shiftPress?: boolean) => void;
    docViewPath: () => DocumentView[];
    setHeight?: (height: number) => void;
    NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal

    // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
    // See currentUserUtils headerTemplate for examples of creating text boxes from html which set some of these fields
    // Also, see InkingStroke for examples of creating text boxes from render() methods which set some of these fields
    backgroundColor?: string;
    color?: string;
    height?: number;
    width?: number;
    dontSelectOnLoad?: boolean; // suppress selecting (e.g.,. text box) when loaded (and mark as not being associated with scrollTop document field)
    noSidebar?: boolean;
    dontScale?: boolean;
}

@observer
export class FieldView extends React.Component<FieldViewProps> {
    public static LayoutString(fieldType: { name: string }, fieldStr: string) {
        return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "<ImageBox {...props} fieldKey={'data'} />"
    }
    @computed get fieldval() {
        return this.props.Document[this.props.fieldKey];
    }

    render() {
        const field = this.fieldval;
        // prettier-ignore
        if (field instanceof Doc)  return <p> <b>{field.title?.toString()}</b></p>;
        if (field === undefined) return <p>{'<null>'}</p>;
        if (field instanceof DateField) return <p>{field.date.toLocaleString()}</p>;
        if (field instanceof List) return <div> {field.map(f => Field.toString(f)).join(', ')} </div>;
        if (field instanceof WebField) return <p>{Field.toString(field.url.href)}</p>;
        if (!(field instanceof Promise)) return <p>{Field.toString(field)}</p>;
        return <p> {'Waiting for server...'} </p>;
    }
}