import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; import { Property } from 'csstype'; import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import * as textfit from 'textfit'; import { returnFalse, setupMoveUpEvents } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; import { Field, FieldType } from '../../../fields/Doc'; import { BoolCast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { ViewBoxBaseComponent } from '../DocComponent'; import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProp'; import { FieldView, FieldViewProps } from './FieldView'; import './LabelBox.scss'; @observer export class LabelBox extends ViewBoxBaseComponent() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LabelBox, fieldKey); } private dropDisposer?: DragManager.DragDropDisposer; private _timeout: NodeJS.Timeout | undefined; @observable private _editLabel = false; _divRef: HTMLDivElement | null = null; constructor(props: FieldViewProps) { super(props); makeObservable(this); } protected createDropTarget = (ele: HTMLDivElement) => { this.dropDisposer?.(); if (ele) { this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.Document); } }; @computed get Title() { return Field.toString(this.dataDoc[this.fieldKey] as FieldType) || StrCast(this.Document.title); } @computed get backgroundColor() { return this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) as string; } @computed get answerIcon() { return ( {StrCast(this.Document.quiz)} }>
); } @computed get editAnswer() { return ( {this._editLabel ? 'save' : 'edit correct answer'} }>
setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, () => this.editLabelAnswer())}>
); } editLabelAnswer = () => { // when click the pencil, set the text to the quiz content. when click off, set the quiz text to that and set textbox to nothing. if (!this._editLabel) { this.dataDoc.title = StrCast(this.Document.quiz); } else { this.Document.quiz = this.Title; this.dataDoc.title = ''; } this._editLabel = !this._editLabel; }; componentDidMount() { this._props.setContentViewBox?.(this); } componentWillUnMount() { this._timeout && clearTimeout(this._timeout); } specificContextMenu = (): void => {}; drop = (/* e: Event, de: DragManager.DropEvent */) => { return false; }; 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: HTMLElement | null | undefined ): { minFontSize: number; maxFontSize: number; multiLine: boolean; alignHoriz: boolean; alignVert: boolean; detectMultiLine: boolean; } => { this._timeout && clearTimeout(this._timeout); const textfitParams = { minFontSize: NumCast(this.layoutDoc._label_minFontSize, 1), maxFontSize: NumCast(this.layoutDoc._label_maxFontSize, 100), multiLine: BoolCast(this.layoutDoc._singleLine, true) ? false : true, alignHoriz: true, alignVert: true, detectMultiLine: true, }; if (r) { if (!r.offsetHeight || !r.offsetWidth) { console.log("CAN'T FIT TO EMPTY BOX"); this._timeout && clearTimeout(this._timeout); this._timeout = setTimeout(() => this.fitTextToBox(r)); return textfitParams; } textfit(r, textfitParams); } return textfitParams; }; render() { TraceMobx(); const boxParams = this.fitTextToBox(undefined); // this causes mobx to trigger re-render when data changes const label = this.Title.startsWith('#') ? null : this.Title; return (
{ // this.hoverFlip(undefined); // }} >
{ e.stopPropagation(); })} onKeyUp={action(e => { e.stopPropagation(); // if (e.key === 'Enter') { this.dataDoc[this.fieldKey] = this._divRef?.innerText ?? ''; setTimeout(() => this._props.select(false)); // } })} onBlur={() => { this.dataDoc[this.fieldKey] = this._divRef?.innerText ?? ''; }} contentEditable={this._props.onClickScript?.() ? false : true} ref={r => { this._divRef = r; this.fitTextToBox(r); if (this._props.isSelected() && this._divRef) { const range = document.createRange(); range.setStart(this._divRef, this._divRef.childNodes.length); range.setEnd(this._divRef, this._divRef.childNodes.length); const sel = window.getSelection(); sel?.removeAllRanges(); sel?.addRange(range); } }}> {label}
{this.Document.showQuiz ? this.answerIcon : null} {this.Document.showQuiz ? this.editAnswer : null}
); } } 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 }, });