diff options
Diffstat (limited to 'src/client/views/nodes/LabelBox.tsx')
-rw-r--r-- | src/client/views/nodes/LabelBox.tsx | 223 |
1 files changed, 90 insertions, 133 deletions
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index f80ff5f94..d33d12603 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -1,21 +1,17 @@ -import { action, computed, makeObservable, observable } from 'mobx'; +import { Property } from 'csstype'; +import { action, computed, makeObservable, trace } 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 * as textfit from 'textfit'; +import { Field, FieldType } from '../../../fields/Doc'; +import { BoolCast, 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 @@ -23,28 +19,15 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LabelBox, fieldKey); } - public static LayoutStringWithTitle(fieldStr: string, label?: string) { - return !label ? LabelBox.LayoutString(fieldStr) : `<LabelBox fieldKey={'${fieldStr}'} label={'${label}'} {...props} />`; // e.g., "<ImageBox {...props} fieldKey={"data} />" - } private dropDisposer?: DragManager.DragDropDisposer; - private _timeout: any; + private _timeout: NodeJS.Timeout | undefined; + _divRef: HTMLDivElement | null = null; 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) { @@ -52,44 +35,27 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() { } }; - get paramsDoc() { - return Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? this.dataDoc : this.layoutDoc; + @computed get Title() { + return Field.toString(this.dataDoc[this.fieldKey] as FieldType) || StrCast(this.Document.title); } - 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' }); - }; + @computed get backgroundColor() { + return this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) as string; + } - @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<Doc>(docDragData.droppedDocuments.map((d, i) => (d.onDragStart ? docDragData.draggedDocuments[i] : d))); - e.stopPropagation(); - return true; - } + componentDidMount() { + this._props.setContentViewBox?.(this); + } + componentWillUnMount() { + this._timeout && clearTimeout(this._timeout); + } + + specificContextMenu = (): void => {}; + + drop = (/* e: Event, de: DragManager.DropEvent */) => { 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 }); @@ -104,101 +70,92 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() { }; 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', + 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, }; - this._timeout = undefined; - if (!r) return params; - if (!r.offsetHeight || !r.offsetWidth) { - this._timeout = setTimeout(() => this.fitTextToBox(r)); - return this._timeout; + 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); } - 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; + return textfitParams; }; - // (!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; + trace(); + const boxParams = this.fitTextToBox(undefined); // this causes mobx to trigger re-render when data changes + const label = this.Title.startsWith('#') ? null : this.Title; return ( - <div - className="labelBox-outerDiv" - onMouseLeave={action(() => { - 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) }}> + <div key={label?.length} className="labelBox-outerDiv" ref={this.createDropTarget} onContextMenu={this.specificContextMenu} style={{ boxShadow: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BoxShadow) as string }}> <div className="labelBox-mainButton" style={{ - backgroundColor: this.hoverColor, - fontSize: StrCast(this.layoutDoc._text_fontSize), + backgroundColor: this.backgroundColor, + // fontSize: StrCast(this.layoutDoc._text_fontSize), color: StrCast(this.layoutDoc._color), fontFamily: StrCast(this.layoutDoc._text_fontFamily) || 'inherit', letterSpacing: StrCast(this.layoutDoc.letterSpacing), - textTransform: StrCast(this.layoutDoc.textTransform) as any, + textTransform: StrCast(this.layoutDoc.textTransform) as Property.TextTransform, paddingLeft: NumCast(this.layoutDoc._xPadding), paddingRight: NumCast(this.layoutDoc._xPadding), paddingTop: NumCast(this.layoutDoc._yPadding), paddingBottom: NumCast(this.layoutDoc._yPadding), width: this._props.PanelWidth(), height: this._props.PanelHeight(), - whiteSpace: 'singleLine' in boxParams && boxParams.singleLine ? 'pre' : 'pre-wrap', + whiteSpace: 'multiLine' in boxParams && boxParams.multiLine ? 'pre-wrap' : 'pre', }}> - <span style={{ width: 'singleLine' in boxParams ? '' : '100%' }} ref={action((r: any) => this.fitTextToBox(r))}> - {label.startsWith('#') ? null : label.replace(/([^a-zA-Z])/g, '$1\u200b')} - </span> - </div> - <div className="labelBox-fieldKeyParams"> - {!missingParams?.length - ? null - : missingParams.map(m => ( - <div key={m} className="labelBox-missingParam"> - {m} - </div> - ))} + <div + style={{ + width: this._props.PanelWidth() - 2 * NumCast(this.layoutDoc._xPadding), + height: this._props.PanelHeight() - 2 * NumCast(this.layoutDoc._yPadding), + outline: 'unset !important', + }} + onKeyDown={action(e => { + 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} + </div> </div> </div> ); |