import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, Field, FieldType } from '../../../fields/Doc'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxBaseComponent } from '../DocComponent'; import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProp'; import { FieldView, FieldViewProps } from './FieldView'; import BigText from './LabelBigText'; import './LabelBox.scss'; @observer export class LabelBox extends ViewBoxBaseComponent() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LabelBox, fieldKey); } public static LayoutStringWithTitle(fieldStr: string, label?: string) { return !label ? LabelBox.LayoutString(fieldStr) : ``; // e.g., "" } private dropDisposer?: DragManager.DragDropDisposer; private _timeout: any; constructor(props: FieldViewProps) { super(props); makeObservable(this); } componentDidMount() { this._props.setContentViewBox?.(this); } componentWillUnMount() { this._timeout && clearTimeout(this._timeout); } @computed get Title() { return Field.toString(this.dataDoc[this.fieldKey] as FieldType) || StrCast(this.Document.title); } protected createDropTarget = (ele: HTMLDivElement) => { this.dropDisposer?.(); if (ele) { this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.Document); } }; get paramsDoc() { return Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? this.dataDoc : this.layoutDoc; } specificContextMenu = (): void => { const funcs: ContextMenuProps[] = []; !Doc.noviceMode && funcs.push({ description: 'Clear Script Params', event: () => { const params = Cast(this.paramsDoc['onClick-paramFieldKeys'], listSpec('string'), []); params?.forEach(p => { this.paramsDoc[p] = undefined; }); }, icon: 'trash', }); funcs.length && ContextMenu.Instance.addItem({ description: 'OnClick...', noexpand: true, subitems: funcs, icon: 'mouse-pointer' }); }; @undoBatch drop = (e: Event, de: DragManager.DropEvent) => { const { docDragData } = de.complete; const params = Cast(this.paramsDoc['onClick-paramFieldKeys'], listSpec('string'), []); const missingParams = params?.filter(p => !this.paramsDoc[p]); if (docDragData && missingParams?.includes((e.target as any).textContent)) { this.paramsDoc[(e.target as any).textContent] = new List(docDragData.droppedDocuments.map((d, i) => (d.onDragStart ? docDragData.draggedDocuments[i] : d))); e.stopPropagation(); return true; } return false; }; @observable _mouseOver = false; @computed get hoverColor() { return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor); } getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { if (!pinProps) return this.Document; const anchor = Docs.Create.ConfigDocument({ title: StrCast(this.Document.title), annotationOn: this.Document }); if (anchor) { if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; // addAsAnnotation && this.addDocument(anchor); PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}) } }, this.Document); return anchor; } return anchor; }; fitTextToBox = ( r: any ): | NodeJS.Timeout | { rotateText: null; fontSizeFactor: number; minimumFontSize: number; maximumFontSize: number; limitingDimension: string; horizontalAlign: string; verticalAlign: string; textAlign: string; singleLine: boolean; whiteSpace: string; } => { const singleLine = BoolCast(this.layoutDoc._singleLine, true); const params = { rotateText: null, fontSizeFactor: 1, minimumFontSize: NumCast(this.layoutDoc._label_minFontSize, 8), maximumFontSize: NumCast(this.layoutDoc._label_maxFontSize, 1000), limitingDimension: 'both', horizontalAlign: 'center', verticalAlign: 'center', textAlign: 'center', singleLine, whiteSpace: singleLine ? 'nowrap' : 'pre-wrap', }; this._timeout = undefined; if (!r) return params; if (!r.offsetHeight || !r.offsetWidth) { this._timeout = setTimeout(() => this.fitTextToBox(r)); return this._timeout; } const parent = r.parentNode; const parentStyle = parent.style; parentStyle.display = ''; parentStyle.alignItems = ''; r.setAttribute('style', ''); r.style.width = singleLine ? '' : '100%'; r.style.textOverflow = 'ellipsis'; r.style.overflow = 'hidden'; BigText(r, params); return params; }; // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")") render() { const boxParams = this.fitTextToBox(null); // this causes mobx to trigger re-render when data changes const params = Cast(this.paramsDoc['onClick-paramFieldKeys'], listSpec('string'), []); const missingParams = params?.filter(p => !this.paramsDoc[p]); params?.map(p => DocListCast(this.paramsDoc[p])); // bcz: really hacky form of prefetching ... const label = this.Title; return (
{ this._mouseOver = false; })} // eslint-disable-next-line jsx-a11y/mouse-events-have-key-events onMouseOver={action(() => { this._mouseOver = true; })} ref={this.createDropTarget} onContextMenu={this.specificContextMenu} style={{ boxShadow: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BoxShadow) }}>
this.fitTextToBox(r))}> {label.startsWith('#') ? null : label.replace(/([^a-zA-Z])/g, '$1\u200b')}
{!missingParams?.length ? null : missingParams.map(m => (
{m}
))}
); } } Docs.Prototypes.TemplateMap.set(DocumentType.LABEL, { layout: { view: LabelBox, dataField: 'title' }, options: { acl: '', _singleLine: true, _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true }, }); Docs.Prototypes.TemplateMap.set(DocumentType.BUTTON, { layout: { view: LabelBox, dataField: 'title' }, options: { acl: '', _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true }, });