diff options
Diffstat (limited to 'src/client/views/nodes')
16 files changed, 421 insertions, 1103 deletions
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index e954d0484..7e8eef0a5 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -18,7 +18,7 @@ import { SearchBox } from '../search/SearchBox'; import { DashWebRTCVideo } from '../webcam/DashWebRTCVideo'; import { YoutubeBox } from './../../apis/youtube/YoutubeBox'; import { AudioBox } from './AudioBox'; -import { FontIconBox } from './button/FontIconBox'; +import { FontIconBox } from './FontIconBox/FontIconBox'; import { ColorBox } from './ColorBox'; import { ComparisonBox } from './ComparisonBox'; import { DataVizBox } from './DataVizBox/DataVizBox'; diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index f1627e1e1..b25540dd3 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -115,7 +115,10 @@ width: 100%; height: 100%; transition: inherit; - + display: flex; + justify-content: center; + align-items: center; + .sharingIndicator { height: 30px; width: 30px; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a4f5eb62b..6b47564a4 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1111,8 +1111,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps ); const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc; const background = StrCast( - SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.userColor, - Doc.UserDoc().layout_showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : 'rgba(0,0,0,0.4)' + SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, + Doc.UserDoc().layout_showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().headingColor) : 'rgba(0,0,0,0.4)' ); const sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', ''); const titleView = !showTitle ? null : ( @@ -1238,7 +1238,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps const renderDoc = this.renderDoc({ borderRadius: this.borderRounding, outline: highlighting && !this.borderRounding && !highlighting.highlightStroke ? `${highlighting.highlightColor} ${highlighting.highlightStyle} ${highlighting.highlightIndex}px` : 'solid 0px', - border: highlighting && this.borderRounding && highlighting.highlightStyle === 'dashed' ? `${highlighting.highlightStyle} ${highlighting.highlightColor} ${highlighting.highlightIndex}px` : undefined, + border: highlighting && this.borderRounding && highlighting.highlightStyle === 'dashed' ? `${highlighting.highlightStyle} ${highlighting.highlightColor} ${highlighting.highlightIndex}px` : undefined, boxShadow, clipPath: borderPath?.clipPath, }); diff --git a/src/client/views/nodes/button/ButtonInterface.ts b/src/client/views/nodes/FontIconBox/ButtonInterface.ts index 0aa2ac8e1..0aa2ac8e1 100644 --- a/src/client/views/nodes/button/ButtonInterface.ts +++ b/src/client/views/nodes/FontIconBox/ButtonInterface.ts diff --git a/src/client/views/nodes/button/FontIconBadge.scss b/src/client/views/nodes/FontIconBox/FontIconBadge.scss index 2ff5c651f..2ff5c651f 100644 --- a/src/client/views/nodes/button/FontIconBadge.scss +++ b/src/client/views/nodes/FontIconBox/FontIconBadge.scss diff --git a/src/client/views/nodes/button/FontIconBadge.tsx b/src/client/views/nodes/FontIconBox/FontIconBadge.tsx index b50588ce2..b50588ce2 100644 --- a/src/client/views/nodes/button/FontIconBadge.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBadge.tsx diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/FontIconBox/FontIconBox.scss index 9d9fa26b0..9d9fa26b0 100644 --- a/src/client/views/nodes/button/FontIconBox.scss +++ b/src/client/views/nodes/FontIconBox/FontIconBox.scss diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx new file mode 100644 index 000000000..2958fa737 --- /dev/null +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -0,0 +1,412 @@ +import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Button, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components'; +import { action, computed, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; +import { ScriptField } from '../../../../fields/ScriptField'; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; +import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; +import { SelectionManager } from '../../../util/SelectionManager'; +import { undoable, UndoManager } from '../../../util/UndoManager'; +import { ContextMenu } from '../../ContextMenu'; +import { DocComponent } from '../../DocComponent'; +import { EditableView } from '../../EditableView'; +import { Colors } from '../../global/globalEnums'; +import { StyleProp } from '../../StyleProvider'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { OpenWhere } from '../DocumentView'; +import { RichTextMenu } from '../formattedText/RichTextMenu'; +import './FontIconBox.scss'; +import { SelectedDocView } from '../../selectedDoc'; + +export enum ButtonType { + TextButton = 'textBtn', + MenuButton = 'menuBtn', + DropdownList = 'dropdownList', + DropdownButton = 'dropdownBtn', + ClickButton = 'clickBtn', + ToggleButton = 'toggleBtn', + ColorButton = 'colorBtn', + ToolButton = 'toolBtn', + MultiToggleButton = 'multiToggleBtn', + NumberSliderButton = 'numSliderBtn', + NumberDropdownButton = 'numDropdownBtn', + NumberInlineButton = 'numInlineBtn', + EditableText = 'editableText', +} + +export interface ButtonProps extends FieldViewProps { + type?: ButtonType; +} +@observer +export class FontIconBox extends DocComponent<ButtonProps>() { + public static LayoutString(fieldKey: string) { + return FieldView.LayoutString(FontIconBox, fieldKey); + } + @observable noTooltip = false; + showTemplate = (): void => { + const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null); + dragFactory && this.props.addDocTab(dragFactory, OpenWhere.addRight); + }; + dragAsTemplate = (): void => { + this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); + }; + useAsPrototype = (): void => { + this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)'); + }; + + specificContextMenu = (): void => { + if (!Doc.noviceMode) { + const cm = ContextMenu.Instance; + cm.addItem({ description: 'Show Template', event: this.showTemplate, icon: 'tag' }); + cm.addItem({ description: 'Use as Render Template', event: this.dragAsTemplate, icon: 'tag' }); + cm.addItem({ description: 'Use as Prototype', event: this.useAsPrototype, icon: 'tag' }); + } + }; + + static GetShowLabels() { + return BoolCast(Doc.UserDoc()._showLabel); + } + static SetShowLabels(show: boolean) { + Doc.UserDoc()._showLabel = show; + } + static GetRecognizeGestures() { + return BoolCast(Doc.UserDoc()._recognizeGestures); + } + static SetRecognizeGestures(show: boolean) { + Doc.UserDoc()._recognizeGestures = show; + } + + // Determining UI Specs + @computed get label() { + return StrCast(this.rootDoc.icon_label, StrCast(this.rootDoc.title)); + } + Icon = (color: string, iconFalse?: boolean) => { + let icon; + if (iconFalse ) { + icon = StrCast(this.dataDoc[this.fieldKey ?? 'iconFalse'] ?? this.dataDoc.icon, 'user') as any; + if (icon) return <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} /> + else return null + } + icon = StrCast(this.dataDoc[this.fieldKey ?? 'icon'] ?? this.dataDoc.icon, 'user') as any; + const trailsIcon = () => <img src={`/assets/${'presTrails.png'}`} style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? '0%' : '100%'})` }} />; + return !icon ? null : icon === 'pres-trail' ? trailsIcon() : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />; + }; + @computed get dropdown() { + return BoolCast(this.rootDoc.dropDownOpen); + } + @computed get buttonList() { + return StrListCast(this.rootDoc.btnList); + } + @computed get type() { + return StrCast(this.rootDoc.btnType); + } + + /** + * Types of buttons in dash: + * - Main menu button (LHS) + * - Tool button + * - Expandable button (CollectionLinearView) + * - Button inside of CollectionLinearView vs. outside of CollectionLinearView + * - Action button + * - Dropdown button + * - Color button + * - Dropdown list + * - Number button + **/ + + _batch: UndoManager.Batch | undefined = undefined; + /** + * Number button + */ + @computed get numberDropdown() { + let type: NumberDropdownType; + switch(this.type) { + case ButtonType.NumberDropdownButton: + type = 'dropdown' + break; + case ButtonType.NumberInlineButton: + type = 'input' + break; + case ButtonType.NumberSliderButton: + default: + type = 'slider' + break; + } + const numScript = (value?: number) => ScriptCast(this.rootDoc.script).script.run({ self: this.rootDoc, value, _readOnly_: value === undefined }); + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + // Script for checking the outcome of the toggle + const checkResult = Number(Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3))); + const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</div>; + + return <NumberDropdown + color={color} + numberDropdownType={type} + showPlusMinus={false} + tooltip={this.label} + type={Type.PRIM} + min={NumCast(this.rootDoc.numBtnMin, 0)} + max={NumCast(this.rootDoc.numBtnMax, 100)} + number={checkResult} + setNumber={undoable(value => numScript(value), `${this.rootDoc.title} button set from list`)} + fillWidth + /> + } + + /** + * Dropdown button + */ + @computed get dropdownButton() { + const active: string = StrCast(this.rootDoc.dropDownOpen); + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + return ( + <div + className={`menuButton ${this.type} ${active}`} + style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} + onClick={action(() => { + this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen; + this.noTooltip = this.rootDoc.dropDownOpen; + Doc.UnBrushAllDocs(); + })}> + {this.Icon(color)} + {!this.label || !FontIconBox.GetShowLabels() ? null : ( + <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> + {' '} + {this.label}{' '} + </div> + )} + <div className="menuButton-dropdown" style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}> + <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" /> + </div> + {this.rootDoc.dropDownOpen ? <div className="menuButton-dropdownBox">{/* DROPDOWN BOX CONTENTS */}</div> : null} + </div> + ); + } + + /** + * Dropdown list + */ + @computed get dropdownListButton() { + const active: string = StrCast(this.rootDoc.dropDownOpen); + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + + const script = ScriptCast(this.rootDoc.script); + + let noviceList: string[] = []; + let text: string | undefined; + let dropdown = true; + let getStyle: (val: string) => any = () => {}; + let icon: IconProp = 'caret-down'; + let isViewDropdown: boolean = script?.script.originalScript.startsWith('setView') + try { + if (isViewDropdown) { + const selectedDocs: Doc[] = SelectionManager.Docs(); + const selected = SelectionManager.Docs().lastElement(); + console.log('selected') + if (selected) { + if (StrCast(selected.type) === DocumentType.COL) { + text = StrCast(selected._type_collection); + } else { + console.log("doc selected", selected.title); + dropdown = false; + if (selectedDocs.length > 1) { + text = selectedDocs.length + " documents selected" + } else { + text = selected.type === DocumentType.RTF ? 'Text' : StrCast(selected.type); + icon = Doc.toIcon(selected); + } + return <Popup + icon={<FontAwesomeIcon size={'1x'} icon={icon} />} + text={text} + type={Type.TERT} + color={color} + popup={<SelectedDocView selectedDocs={selectedDocs}/>} + fillWidth + /> + } + } else { + dropdown = false; + return <Button + text={`None Selected`} + type={Type.TERT} + color={color} + fillWidth + inactive + /> + } + noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.NoteTaking]; + } else { + text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); + getStyle = (val: string) => { return { fontFamily: val } } + } + } catch (e) { + console.log(e); + } + + console.log("current item: ", text); + + // Get items to place into the list + const list: IListItemProps[] = this.buttonList + .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value)) + .map(value => ( + { + text: value.charAt(0).toUpperCase() + value.slice(1), + val: value, + style: getStyle(value), + onClick: undoable(() => script.script.run({ self: this.rootDoc, value }), value) + // shortcut: '#', + } + )); + + + return ( + <Dropdown + selectedVal={text} + setSelectedVal={undoable((val) => script.script.run({ self: this.rootDoc, val }), `dropdown select ${this.label}`)} + color={color} + type={isViewDropdown ? Type.TERT : Type.PRIM} + dropdownType={DropdownType.SELECT} + items={list} + tooltip={this.label} + fillWidth + /> + ) + } + + + @computed get colorScript() { + return ScriptCast(this.rootDoc.script); + } + + /** + * Color button + */ + @computed get colorButton() { + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + const curColor = this.colorScript?.script.run({ self: this.rootDoc, value: undefined, _readOnly_: true }).result ?? 'transparent'; + const tooltip: string = StrCast(this.rootDoc.toolTip); + + return ( + <ColorPicker + setSelectedColor={(value) => { + const s = this.colorScript; + s && undoable(() => s.script.run({ self: this.rootDoc, value: value, _readOnly_: false }).result, `Set ${tooltip} to ${value}`)(); + }} + selectedColor={curColor} + type={Type.PRIM} + color={color} + icon={this.Icon(color)!} + tooltip={tooltip} + label={this.label} + /> + ) + } + + @computed get toggleButton() { + // Determine the type of toggle button + const buttonText: string = StrCast(this.rootDoc.buttonText); + const tooltip: string = StrCast(this.rootDoc.toolTip); + + const script = ScriptCast(this.rootDoc.onClick); + const toggleStatus = script ? script.script.run({ self: this.rootDoc, value: undefined, _readOnly_: true }).result : false; + // Colors + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + + console.log(tooltip, toggleStatus); + return ( + <Toggle tooltip={`Toggle ${tooltip}`} toggleType={ToggleType.BUTTON} type={Type.PRIM} toggleStatus={toggleStatus} text={buttonText} color={color} icon={this.Icon(color)!} label={this.label}/> + ) + } + + /** + * Default + */ + @computed get defaultButton() { + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + const tooltip: string = StrCast(this.rootDoc.toolTip); + + return ( + <IconButton tooltip={tooltip} icon={this.Icon(color)!} label={this.label}/> + ) + } + + @computed get editableText() { + // Script for running the toggle + const script = ScriptCast(this.rootDoc.script); + // Function to run the script + const checkResult = script?.script.run({ value: '', _readOnly_: true }).result; + + const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ value, _readOnly_: false }).result; + + return <EditableText + editing={false} setEditing={(editing: boolean) => {}} + /> + + return ( + <div className="menuButton editableText"> + <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'lock'} /> + <div style={{ width: 'calc(100% - .875em)', paddingLeft: '4px' }}> + <EditableView GetValue={() => script?.script.run({ value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} /> + </div> + </div> + ); + } + + render() { + // determine dash button metadata + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + const tooltip: string = StrCast(this.rootDoc.toolTip); + + // TODO:glr Add label of button type + let button: JSX.Element = this.defaultButton; + // prettier-ignore + switch (this.type) { + case ButtonType.EditableText: + button = this.editableText; + break; + case ButtonType.DropdownList: + button = this.dropdownListButton; + break; + case ButtonType.ColorButton: + button = this.colorButton; + break; + case ButtonType.NumberDropdownButton: + case ButtonType.NumberInlineButton: + case ButtonType.NumberSliderButton: + button = this.numberDropdown; + break; + case ButtonType.DropdownButton: + button = this.dropdownButton; + break; + case ButtonType.ToggleButton: button = this.toggleButton; break; + case ButtonType.TextButton: + // Script for checking the outcome of the toggle + const script = ScriptCast(this.rootDoc.script); + const checkResult = script?.script.run({ _readOnly_: true }).result; + button = ( + <Button tooltip={tooltip} color={checkResult ?? backgroundColor} icon={this.Icon(color)!} text={StrCast(this.rootDoc.buttonText)} label={this.label}/> + ); + break; + case ButtonType.ClickButton: + case ButtonType.ToolButton: + button = ( + <IconButton tooltip={tooltip} color={color} icon={this.Icon(color)!} label={this.label}/> + ); + break; + case ButtonType.MenuButton: button = ( + <IconButton tooltip={tooltip} tooltipPlacement='right' size={Size.LARGE} color={color} icon={this.Icon(color)!} label={this.label}/> + ); + break; + } + + return button; + } +} diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx deleted file mode 100644 index a0b0caf98..000000000 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ /dev/null @@ -1,953 +0,0 @@ -import { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; -import { action, computed, observable, runInAction } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { ColorState, SketchPicker } from 'react-color'; -import { Doc, StrListCast } from '../../../../fields/Doc'; -import { Height, Width } from '../../../../fields/DocSymbols'; -import { InkTool } from '../../../../fields/InkField'; -import { ScriptField } from '../../../../fields/ScriptField'; -import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; -import { WebField } from '../../../../fields/URLField'; -import { GestureUtils } from '../../../../pen-gestures/GestureUtils'; -import { aggregateBounds, Utils } from '../../../../Utils'; -import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; -import { LinkManager } from '../../../util/LinkManager'; -import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; -import { SelectionManager } from '../../../util/SelectionManager'; -import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager'; -import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; -import { ContextMenu } from '../../ContextMenu'; -import { DocComponent } from '../../DocComponent'; -import { EditableView } from '../../EditableView'; -import { GestureOverlay } from '../../GestureOverlay'; -import { Colors } from '../../global/globalEnums'; -import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, SetActiveIsInkMask } from '../../InkingStroke'; -import { InkTranscription } from '../../InkTranscription'; -import { StyleProp } from '../../StyleProvider'; -import { FieldView, FieldViewProps } from '.././FieldView'; -import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentView'; -import { OpenWhere } from '../DocumentView'; -import { RichTextMenu } from '../formattedText/RichTextMenu'; -import { WebBox } from '../WebBox'; -import { FontIconBadge } from './FontIconBadge'; -import './FontIconBox.scss'; - -export enum ButtonType { - TextButton = 'textBtn', - MenuButton = 'menuBtn', - DropdownList = 'drpdownList', - DropdownButton = 'drpdownBtn', - ClickButton = 'clickBtn', - DoubleButton = 'dblBtn', - ToggleButton = 'tglBtn', - ColorButton = 'colorBtn', - ToolButton = 'toolBtn', - NumberSliderButton = 'numSliderBtn', - NumberDropdownButton = 'numDropdownBtn', - NumberInlineButton = 'numInlineBtn', - EditableText = 'editableText', -} - -export interface ButtonProps extends FieldViewProps { - type?: ButtonType; -} -@observer -export class FontIconBox extends DocComponent<ButtonProps>() { - public static LayoutString(fieldKey: string) { - return FieldView.LayoutString(FontIconBox, fieldKey); - } - @observable noTooltip = false; - showTemplate = (): void => { - const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null); - dragFactory && this.props.addDocTab(dragFactory, OpenWhere.addRight); - }; - dragAsTemplate = (): void => { - this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); - }; - useAsPrototype = (): void => { - this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)'); - }; - - specificContextMenu = (): void => { - if (!Doc.noviceMode) { - const cm = ContextMenu.Instance; - cm.addItem({ description: 'Show Template', event: this.showTemplate, icon: 'tag' }); - cm.addItem({ description: 'Use as Render Template', event: this.dragAsTemplate, icon: 'tag' }); - cm.addItem({ description: 'Use as Prototype', event: this.useAsPrototype, icon: 'tag' }); - } - }; - - static GetShowLabels() { - return BoolCast(Doc.UserDoc()._showLabel); - } - static SetShowLabels(show: boolean) { - Doc.UserDoc()._showLabel = show; - } - static GetRecognizeGestures() { - return BoolCast(Doc.UserDoc()._recognizeGestures); - } - static SetRecognizeGestures(show: boolean) { - Doc.UserDoc()._recognizeGestures = show; - } - - // Determining UI Specs - @computed get label() { - return StrCast(this.rootDoc.icon_label, StrCast(this.rootDoc.title)); - } - Icon = (color: string) => { - const icon = StrCast(this.dataDoc[this.fieldKey ?? 'icon'] ?? this.dataDoc.icon, 'user') as any; - const trailsIcon = () => <img src={`/assets/${'presTrails.png'}`} style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? '0%' : '100%'})` }} />; - return !icon ? null : icon === 'pres-trail' ? trailsIcon() : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />; - }; - @computed get dropdown() { - return BoolCast(this.rootDoc.dropDownOpen); - } - @computed get buttonList() { - return StrListCast(this.rootDoc.btnList); - } - @computed get type() { - return StrCast(this.rootDoc.btnType); - } - - /** - * Types of buttons in dash: - * - Main menu button (LHS) - * - Tool button - * - Expandable button (CollectionLinearView) - * - Button inside of CollectionLinearView vs. outside of CollectionLinearView - * - Action button - * - Dropdown button - * - Color button - * - Dropdown list - * - Number button - **/ - - _batch: UndoManager.Batch | undefined = undefined; - /** - * Number button - */ - @computed get numberSliderButton() { - const numScript = (value?: number) => ScriptCast(this.rootDoc.script).script.run({ self: this.rootDoc, value, _readOnly_: value === undefined }); - // Script for checking the outcome of the toggle - const checkResult = Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)); - const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</div>; - - const dropdown = ( - <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()}> - <input - className="menu-slider" - type="range" - step="1" - min={NumCast(this.rootDoc.numBtnMin, 0)} - max={NumCast(this.rootDoc.numBtnMax, 100)} - //readOnly={true} - value={checkResult} - onPointerDown={() => (this._batch = UndoManager.StartBatch('num slider changing'))} - onPointerUp={() => this._batch?.end()} - onChange={undoable(e => { - e.stopPropagation(); - numScript(Number(e.target.value)); - }, 'set num value')} - /> - </div> - ); - return ( - <div - className="menuButton numBtn slider" - onPointerDown={e => e.stopPropagation()} - onClick={action(() => { - this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen; - this.noTooltip = this.rootDoc.dropDownOpen; - Doc.UnBrushAllDocs(); - })}> - {checkResult} - {label} - {this.rootDoc.dropDownOpen ? dropdown : null} - </div> - ); - } - /** - * Number button - */ - @computed get numberDropdownButton() { - const numScript = (value?: number) => ScriptCast(this.rootDoc.script)?.script.run({ self: this.rootDoc, value, _readOnly_: value === undefined }); - - const checkResult = Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)); - - const items: number[] = []; - for (let i = 0; i < 100; i += 2) items.push(i); - - const list = items.map(value => { - return ( - <div - className="list-item" - key={`${value}`} - style={{ - backgroundColor: value.toString() === checkResult ? Colors.LIGHT_BLUE : undefined, - }} - onClick={undoable(value => numScript(value), `${this.rootDoc.title} button set from list`)}> - {value} - </div> - ); - }); - return ( - <div className="menuButton numBtn list"> - <div className="button" onClick={undoable(e => numScript(Number(checkResult) - 1), `${this.rootDoc.title} decrement value`)}> - <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon="minus" /> - </div> - <div - className={`button ${'number'}`} - onPointerDown={e => { - e.stopPropagation(); - e.preventDefault(); - }} - onClick={action(() => { - this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen; - this.noTooltip = this.rootDoc.dropDownOpen; - Doc.UnBrushAllDocs(); - })}> - <input style={{ width: 30 }} className="button-input" type="number" value={checkResult} readOnly={true} onChange={undoable(e => numScript(Number(e.target.value)), `${this.rootDoc.title} button set value`)} /> - </div> - <div className={`button`} onClick={undoable(e => numScript(Number(checkResult) + 1), `${this.rootDoc.title} increment value`)}> - <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'plus'} /> - </div> - - {this.rootDoc.dropDownOpen ? ( - <div> - <div className="menuButton-dropdownList" style={{ left: '25%' }}> - {list} - </div> - <div - className="dropbox-background" - onClick={action(e => { - e.stopPropagation(); - this.rootDoc.dropDownOpen = false; - this.noTooltip = false; - Doc.UnBrushAllDocs(); - })} - /> - </div> - ) : null} - </div> - ); - } - /** - * Number button - */ - @computed get numberInlineButton() { - return <div />; - } - - /** - * Dropdown button - */ - @computed get dropdownButton() { - const active: string = StrCast(this.rootDoc.dropDownOpen); - const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - return ( - <div - className={`menuButton ${this.type} ${active}`} - style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} - onClick={action(() => { - this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen; - this.noTooltip = this.rootDoc.dropDownOpen; - Doc.UnBrushAllDocs(); - })}> - {this.Icon(color)} - {!this.label || !FontIconBox.GetShowLabels() ? null : ( - <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> - {' '} - {this.label}{' '} - </div> - )} - <div className="menuButton-dropdown" style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}> - <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" /> - </div> - {this.rootDoc.dropDownOpen ? <div className="menuButton-dropdownBox">{/* DROPDOWN BOX CONTENTS */}</div> : null} - </div> - ); - } - - /** - * Dropdown list - */ - @computed get dropdownListButton() { - const active: string = StrCast(this.rootDoc.dropDownOpen); - const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - - const script = ScriptCast(this.rootDoc.script); - - let noviceList: string[] = []; - let text: string | undefined; - let dropdown = true; - let icon: IconProp = 'caret-down'; - try { - if (script?.script.originalScript.startsWith('setView')) { - const selected = SelectionManager.Docs().lastElement(); - if (selected) { - if (StrCast(selected.type) === DocumentType.COL) { - text = StrCast(selected._type_collection); - } else { - dropdown = false; - text = selected.type === DocumentType.RTF ? 'Text' : StrCast(selected.type); - icon = Doc.toIcon(selected); - } - } else { - dropdown = false; - icon = 'globe-asia'; - text = 'User Default'; - } - noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.NoteTaking]; - } else text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); - } catch (e) { - console.log(e); - } - - // Get items to place into the list - const list = this.buttonList - .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value)) - .map(value => ( - <div - className="list-item" - key={value} - style={{ - fontFamily: script.script.originalScript.startsWith('{ return setFont') ? value : undefined, - backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined, - }} - onClick={undoable(() => script.script.run({ self: this.rootDoc, value }), value)}> - {value[0].toUpperCase() + value.slice(1)} - </div> - )); - - const label = - !this.label || !FontIconBox.GetShowLabels() ? null : ( - <div className="fontIconBox-label" style={{ bottom: 0, position: 'absolute', color: color, backgroundColor: backgroundColor }}> - {this.label} - </div> - ); - - return ( - <div - className={`menuButton ${this.type} ${active}`} - style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : 'flex' }} - onClick={ - dropdown - ? action(() => { - this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen; - this.noTooltip = this.rootDoc.dropDownOpen; - Doc.UnBrushAllDocs(); - }) - : undefined - }> - {dropdown ? null : <FontAwesomeIcon style={{ marginLeft: 5 }} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />} - <div className="menuButton-dropdown-header">{text && text[0].toUpperCase() + text.slice(1)}</div> - {label} - {!dropdown ? null : ( - <div className="menuButton-dropDown"> - <FontAwesomeIcon icon={icon} color={color} size="sm" /> - </div> - )} - {this.rootDoc.dropDownOpen ? ( - <div> - <div className="menuButton-dropdownList" style={{ left: 0 }}> - {list} - </div> - <div - className="dropbox-background" - onClick={action(e => { - e.stopPropagation(); - this.rootDoc.dropDownOpen = false; - this.noTooltip = false; - Doc.UnBrushAllDocs(); - })} - /> - </div> - ) : null} - </div> - ); - } - - @observable colorPickerClosed: boolean = true; - @computed get colorScript() { - return ScriptCast(this.rootDoc.script); - } - - colorPicker = (curColor: string) => { - const change = (value: ColorState, ev: MouseEvent) => { - ev.preventDefault(); - ev.stopPropagation(); - const s = this.colorScript; - s && undoBatch(() => s.script.run({ self: this.rootDoc, value: Utils.colorString(value), _readOnly_: false }).result)(); - }; - const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']; - return <SketchPicker onChange={change as any /* SketchPicker passes the mouse event to the callback, but the type system doesn't know that */} color={curColor} presetColors={presets} />; - }; - /** - * Color button - */ - @computed get colorButton() { - const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const curColor = this.colorScript?.script.run({ self: this.rootDoc, value: undefined, _readOnly_: true }).result ?? 'transparent'; - - const label = - !this.label || !FontIconBox.GetShowLabels() ? null : ( - <div className="fontIconBox-label" style={{ color, backgroundColor }}> - {this.label} - </div> - ); - - return ( - <div - className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')} ${this.colorPickerClosed}`} - style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} - onClick={action(e => { - this.colorPickerClosed = !this.colorPickerClosed; - this.noTooltip = !this.colorPickerClosed; - setTimeout(() => Doc.UnBrushAllDocs()); - e.stopPropagation(); - })} - onPointerDown={e => e.stopPropagation()}> - {this.Icon(color)} - <div className="colorButton-color" style={{ backgroundColor: curColor }} /> - {label} - {/* {dropdownCaret} */} - {this.colorPickerClosed ? null : ( - <div> - <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onClick={e => e.stopPropagation()}> - {this.colorPicker(curColor)} - </div> - <div - className="dropbox-background" - onPointerDown={action(e => { - e.preventDefault(); - e.stopPropagation(); - this.colorPickerClosed = true; - this.noTooltip = false; - Doc.UnBrushAllDocs(); - })} - /> - </div> - )} - </div> - ); - } - - @computed get toggleButton() { - // Determine the type of toggle button - const switchToggle: boolean = BoolCast(this.rootDoc.switchToggle); - const buttonText: string = StrCast(this.rootDoc.buttonText); - // Colors - const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - - // Button label - const label = - !this.label || !FontIconBox.GetShowLabels() ? null : ( - <div className="fontIconBox-label" style={{ color, backgroundColor }}> - {this.label} - </div> - ); - - if (switchToggle) { - return ( - <div className={`menuButton ${this.type} ${'switch'}`}> - {buttonText ? buttonText : null} - <label className="switch"> - <input type="checkbox" checked={backgroundColor === Colors.MEDIUM_BLUE} /> - <span className="slider round" /> - </label> - </div> - ); - } else { - return ( - <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')}`} style={{ opacity: 1, backgroundColor, color }}> - {this.Icon(color)} - {label} - </div> - ); - } - } - - /** - * Default - */ - @computed get defaultButton() { - const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - return ( - <div className={`menuButton ${this.type}`} onContextMenu={this.specificContextMenu} style={{ backgroundColor: 'transparent', borderBottomLeftRadius: this.dropdown ? 0 : undefined }}> - <div className="menuButton-wrap"> - {this.Icon(color)} - {!this.label || !FontIconBox.GetShowLabels() ? null : ( - <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> - {' '} - {this.label}{' '} - </div> - )} - </div> - </div> - ); - } - - @computed get editableText() { - // Script for running the toggle - const script = ScriptCast(this.rootDoc.script); - // Function to run the script - const checkResult = script?.script.run({ value: '', _readOnly_: true }).result; - - const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ value, _readOnly_: false }).result; - return ( - <div className="menuButton editableText"> - <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'lock'} /> - <div style={{ width: 'calc(100% - .875em)', paddingLeft: '4px' }}> - <EditableView GetValue={() => script?.script.run({ value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} /> - </div> - </div> - ); - } - - render() { - const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const label = (noBackground: boolean = false) => - !this.label || !FontIconBox.GetShowLabels() ? null : ( - <div className="fontIconBox-label" style={{ color, backgroundColor: noBackground ? 'transparent' : backgroundColor }}> - {this.label} - </div> - ); - // TODO:glr Add label of button type - let button: JSX.Element = this.defaultButton; - - // prettier-ignore - switch (this.type) { - case ButtonType.EditableText: return this.editableText; - case ButtonType.DropdownList: button = this.dropdownListButton; break; - case ButtonType.ColorButton: button = this.colorButton; break; - case ButtonType.NumberDropdownButton: button = this.numberDropdownButton; break; - case ButtonType.NumberInlineButton: button = this.numberInlineButton; break; - case ButtonType.NumberSliderButton: button = this.numberSliderButton; break; - case ButtonType.DropdownButton: button = this.dropdownButton; break; - case ButtonType.ToggleButton: button = this.toggleButton; break; - case ButtonType.TextButton: - // Script for checking the outcome of the toggle - const script = ScriptCast(this.rootDoc.script); - const checkResult = script?.script.run({ _readOnly_: true }).result; - button = ( - <div className={`menuButton ${this.type}`} style={{ color, backgroundColor:checkResult ?? backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}> - {this.Icon(color)} - {StrCast(this.rootDoc.buttonText) ? <div className="button-text">{StrCast(this.rootDoc.buttonText)}</div> : null} - {label()} - </div> - ); - break; - case ButtonType.ClickButton: - case ButtonType.ToolButton: button = ( - <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')}`} style={{ backgroundColor, color, opacity: 1 }}> - {this.Icon(color)} - {label()} - </div> - ); - break; - case ButtonType.MenuButton: button = ( - <div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}> - {this.Icon(color)} - {label(true)} - <FontIconBadge value={Cast(this.Document.badgeValue, 'string', null)} /> - </div> - ); - break; - } - - return !this.layoutDoc.toolTip || this.noTooltip ? button : <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>{button}</Tooltip>; - } -} - -// toggle: Set overlay status of selected document -ScriptingGlobals.add(function setView(view: string) { - const selected = SelectionManager.Docs().lastElement(); - selected ? (selected._type_collection = view) : console.log('[FontIconBox.tsx] changeView failed'); -}); - -// toggle: Set overlay status of selected document -ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) { - const selectedViews = SelectionManager.Views(); - if (Doc.ActiveTool !== InkTool.None) { - if (checkResult) { - return ActiveFillColor(); - } - SetActiveFillColor(color ?? 'transparent'); - } else if (selectedViews.length) { - if (checkResult) { - const selView = selectedViews.lastElement(); - const fieldKey = selView.rootDoc.type === DocumentType.INK ? 'fillColor' : 'backgroundColor'; - const layoutFrameNumber = Cast(selView.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values - const contentFrameNumber = Cast(selView.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed - return CollectionFreeFormDocumentView.getStringValues(selView?.rootDoc, contentFrameNumber)[fieldKey] ?? 'transparent'; - } - selectedViews.forEach(dv => { - const fieldKey = dv.rootDoc.type === DocumentType.INK ? 'fillColor' : 'backgroundColor'; - const layoutFrameNumber = Cast(dv.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values - const contentFrameNumber = Cast(dv.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed - if (contentFrameNumber !== undefined) { - CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, { fieldKey: color }); - } else { - dv.dataDoc[fieldKey] = color; - } - }); - } else { - const selected = SelectionManager.Docs().length ? SelectionManager.Docs() : LinkManager.currentLink ? [LinkManager.currentLink] : []; - if (checkResult) { - return selected.lastElement()?._backgroundColor ?? 'transparent'; - } - selected.forEach(doc => (doc._backgroundColor = color)); - } -}); - -// toggle: Set overlay status of selected document -ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) { - if (checkResult) { - return Doc.SharingDoc().userColor; - } - Doc.SharingDoc().userColor = undefined; - Doc.GetProto(Doc.SharingDoc()).userColor = color; - Doc.UserDoc().layout_showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().layout_showTitle, 'author_date'); -}); - -// toggle: Set overlay status of selected document -ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { - const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; - if (checkResult) { - if (NumCast(selected?.Document.z) >= 1) return Colors.MEDIUM_BLUE; - return 'transparent'; - } - selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed'); -}); - -ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) { - const selected = SelectionManager.Docs().lastElement(); - // prettier-ignore - const map: Map<'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([ - ['grid', { - checkResult: (doc:Doc) => doc._freeform_backgroundGrid, - setDoc: (doc:Doc) => doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid, - }], - ['snaplines', { - checkResult: (doc:Doc) => doc._freeform_snapLines, - setDoc: (doc:Doc) => doc._freeform_snapLines = !doc._freeform_snapLines, - }], - ['viewAll', { - checkResult: (doc:Doc) => doc._freeform_fitContentsToBox, - setDoc: (doc:Doc) => doc._freeform_fitContentsToBox = !doc._freeform_fitContentsToBox, - }], - ['clusters', { - waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire - checkResult: (doc:Doc) => doc._freeform_useClusters, - setDoc: (doc:Doc) => doc._freeform_useClusters = !doc._freeform_useClusters, - }], - ['arrange', { - waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire - checkResult: (doc:Doc) => doc._autoArrange, - setDoc: (doc:Doc) => doc._autoArrange = !doc._autoArrange, - }], - ['flashcards', { - checkResult: (doc:Doc) => Doc.UserDoc().defaultToFlashcards, - setDoc: (doc:Doc) => Doc.UserDoc().defaultToFlashcards = !Doc.UserDoc().defaultToFlashcards, - }], - ]); - - if (checkResult) { - return map.get(attr)?.checkResult(selected) ? Colors.MEDIUM_BLUE : 'transparent'; - } - const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} }; - SelectionManager.Docs().map(dv => map.get(attr)?.setDoc(dv)); - setTimeout(() => batch.end(), 100); -}); -ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize', value: any, checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; - const selected = SelectionManager.Docs().lastElement(); - // prettier-ignore - const map: Map<'font'|'fontColor'|'highlight'|'fontSize', { checkResult: () => any; setDoc: () => void;}> = new Map([ - ['font', { - checkResult: () => RichTextMenu.Instance?.fontFamily, - setDoc: () => value && RichTextMenu.Instance.setFontFamily(value), - }], - ['highlight', { - checkResult: () =>(selected ?? Doc.UserDoc())._fontHighlight, - setDoc: () => value && RichTextMenu.Instance.setHighlight(value), - }], - ['fontColor', { - checkResult: () => RichTextMenu.Instance?.fontColor, - setDoc: () => value && RichTextMenu.Instance.setColor(value), - }], - ['fontSize', { - checkResult: () => RichTextMenu.Instance?.fontSize.replace('px', ''), - setDoc: () => { - if (typeof value === 'number') value = value.toString(); - if (value && Number(value).toString() === value) value += 'px'; - RichTextMenu.Instance.setFontSize(value); - }, - }], - ]); - - if (checkResult) { - return map.get(attr)?.checkResult(); - } - map.get(attr)?.setDoc?.(); -}); - -type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'underline' | 'left' | 'center' | 'right' | 'bullet' | 'decimal'; -type attrfuncs = [attrname, { checkResult: () => boolean; toggle: () => any }]; -ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?: boolean) { - const textView = RichTextMenu.Instance?.TextView; - const editorView = textView?.EditorView; - // prettier-ignore - const alignments:attrfuncs[] = (['left','right','center'] as ("left"|"center"|"right")[]).map((where) => - [ where, { checkResult: () =>(editorView ? (RichTextMenu.Instance.textAlign ===where): (Doc.UserDoc().textAlign ===where) ? true:false), - toggle: () => (editorView?.state ? RichTextMenu.Instance.align(editorView, editorView.dispatch, where):(Doc.UserDoc().textAlign = where))}]); - // prettier-ignore - const listings:attrfuncs[] = (['bullet','decimal'] as attrname[]).map(list => - [ list, { checkResult: () => (editorView ? RichTextMenu.Instance.getActiveListStyle() === list:false), - toggle: () => editorView?.state && RichTextMenu.Instance.changeListType(list) }]); - // prettier-ignore - const attrs:attrfuncs[] = [ - ['dictation', { checkResult: () => textView?._recording ? true:false, - toggle: () => textView && runInAction(() => (textView._recording = !textView._recording)) }], - ['noAutoLink',{ checkResult: () => (editorView ? RichTextMenu.Instance.noAutoLink : false), - toggle: () => editorView && RichTextMenu.Instance?.toggleNoAutoLinkAnchor()}], - ['bold', { checkResult: () => (editorView ? RichTextMenu.Instance.bold : (Doc.UserDoc().fontWeight === 'bold') ? true:false), - toggle: editorView ? RichTextMenu.Instance.toggleBold : () => (Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === 'bold' ? undefined : 'bold')}], - ['italics', { checkResult: () => (editorView ? RichTextMenu.Instance.italics : (Doc.UserDoc().fontStyle === 'italics') ? true:false), - toggle: editorView ? RichTextMenu.Instance.toggleItalics : () => (Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === 'italics' ? undefined : 'italics')}], - ['underline', { checkResult: () => (editorView ? RichTextMenu.Instance.underline : (Doc.UserDoc().textDecoration === 'underline') ? true:false), - toggle: editorView ? RichTextMenu.Instance.toggleUnderline : () => (Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === 'underline' ? undefined : 'underline') }]] - - const map = new Map(attrs.concat(alignments).concat(listings)); - if (checkResult) return map.get(charStyle)?.checkResult() ? Colors.MEDIUM_BLUE : 'transparent'; - map.get(charStyle)?.toggle(); -}); - -export function checkInksToGroup() { - // console.log("getting here to inks group"); - if (Doc.ActiveTool === InkTool.Write) { - CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => { - // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those - // find all inkDocs in ffView.unprocessedDocs that are within 200 pixels of each other - const inksToGroup = ffView.unprocessedDocs.filter(inkDoc => { - // console.log(inkDoc.x, inkDoc.y); - }); - }); - } -} - -export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) { - // TODO nda - if document being added to is a inkGrouping then we can just add to that group - if (Doc.ActiveTool === InkTool.Write) { - CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => { - // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those - const selected = ffView.unprocessedDocs; - // loop through selected an get the bound - const bounds: { x: number; y: number; width?: number; height?: number }[] = []; - - selected.map( - action(d => { - const x = NumCast(d.x); - const y = NumCast(d.y); - const width = d[Width](); - const height = d[Height](); - bounds.push({ x, y, width, height }); - }) - ); - - const aggregBounds = aggregateBounds(bounds, 0, 0); - const marqViewRef = ffView._marqueeViewRef.current; - - // set the vals for bounds in marqueeView - if (marqViewRef) { - marqViewRef._downX = aggregBounds.x; - marqViewRef._downY = aggregBounds.y; - marqViewRef._lastX = aggregBounds.r; - marqViewRef._lastY = aggregBounds.b; - } - - selected.map( - action(d => { - const dx = NumCast(d.x); - const dy = NumCast(d.y); - delete d.x; - delete d.y; - delete d.activeFrame; - delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection - delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection - // calculate pos based on bounds - if (marqViewRef?.Bounds) { - d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2; - d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2; - } - return d; - }) - ); - ffView.props.removeDocument?.(selected); - // TODO: nda - this is the code to actually get a new grouped collection - const newCollection = marqViewRef?.getCollection(selected, undefined, true); - if (newCollection) { - newCollection.height = newCollection[Height](); - newCollection.width = newCollection[Width](); - } - - // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs - newCollection && ffView.props.addDocument?.(newCollection); - // TODO: nda - will probably need to go through and only remove the unprocessed selected docs - ffView.unprocessedDocs = []; - - InkTranscription.Instance.transcribeInk(newCollection, selected, false); - }); - } - CollectionFreeFormView.collectionsWithUnprocessedInk.clear(); -} - -function setActiveTool(tool: InkTool | GestureUtils.Gestures, keepPrim: boolean, checkResult?: boolean) { - InkTranscription.Instance?.createInkGroup(); - if (checkResult) { - return (Doc.ActiveTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool - ? GestureOverlay.Instance?.KeepPrimitiveMode || ![GestureUtils.Gestures.Circle, GestureUtils.Gestures.Line, GestureUtils.Gestures.Rectangle].includes(tool as GestureUtils.Gestures) - ? Colors.MEDIUM_BLUE - : Colors.MEDIUM_BLUE_ALT - : 'transparent'; - } - runInAction(() => { - if (GestureOverlay.Instance) { - GestureOverlay.Instance.KeepPrimitiveMode = keepPrim; - } - if (Object.values(GestureUtils.Gestures).includes(tool as any)) { - if (GestureOverlay.Instance.InkShape === tool && !keepPrim) { - Doc.ActiveTool = InkTool.None; - GestureOverlay.Instance.InkShape = undefined; - } else { - Doc.ActiveTool = InkTool.Pen; - GestureOverlay.Instance.InkShape = tool as GestureUtils.Gestures; - } - } else if (tool) { - // pen or eraser - if (Doc.ActiveTool === tool && !GestureOverlay.Instance.InkShape && !keepPrim) { - Doc.ActiveTool = InkTool.None; - } else { - Doc.ActiveTool = tool as any; - GestureOverlay.Instance.InkShape = undefined; - } - } else { - Doc.ActiveTool = InkTool.None; - } - }); -} - -ScriptingGlobals.add(setActiveTool, 'sets the active ink tool mode'); - -// toggle: Set overlay status of selected document -ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', value: any, checkResult?: boolean) { - const selected = SelectionManager.Docs().lastElement(); - // prettier-ignore - const map: Map<'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', { checkResult: () => any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([ - ['inkMask', { - checkResult: () => ((selected?.type === DocumentType.INK ? BoolCast(selected.stroke_isInkMask) : ActiveIsInkMask()) ? Colors.MEDIUM_BLUE : 'transparent'), - setInk: (doc: Doc) => (doc.stroke_isInkMask = !doc.stroke_isInkMask), - setMode: () => selected?.type !== DocumentType.INK && SetActiveIsInkMask(!ActiveIsInkMask()), - }], - ['fillColor', { - checkResult: () => (selected?.type === DocumentType.INK ? StrCast(selected.fillColor) : ActiveFillColor() ?? "transparent"), - setInk: (doc: Doc) => (doc.fillColor = StrCast(value)), - setMode: () => SetActiveFillColor(StrCast(value)), - }], - [ 'strokeWidth', { - checkResult: () => (selected?.type === DocumentType.INK ? NumCast(selected.stroke_width) : ActiveInkWidth()), - setInk: (doc: Doc) => (doc.stroke_width = NumCast(value)), - setMode: () => SetActiveInkWidth(value.toString()), - }], - ['strokeColor', { - checkResult: () => (selected?.type === DocumentType.INK ? StrCast(selected.color) : ActiveInkColor()), - setInk: (doc: Doc) => (doc.color = String(value)), - setMode: () => SetActiveInkColor(StrCast(value)), - }], - ]); - - if (checkResult) { - return map.get(option)?.checkResult(); - } - map.get(option)?.setMode(); - SelectionManager.Docs() - .filter(doc => doc.type === DocumentType.INK) - .map(doc => map.get(option)?.setInk(doc)); -}); - -/** WEB - * webSetURL - **/ -ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) { - const selected = SelectionManager.Views().lastElement(); - if (selected?.rootDoc.type === DocumentType.WEB) { - if (checkResult) { - return StrCast(selected.rootDoc.data, Cast(selected.rootDoc.data, WebField, null)?.url?.href); - } - selected.ComponentView?.setData?.(url); - //selected.rootDoc.data = new WebField(url); - } -}); -ScriptingGlobals.add(function webForward(checkResult?: boolean) { - const selected = SelectionManager.Views().lastElement()?.ComponentView as WebBox; - if (checkResult) { - return selected?.forward(checkResult) ? undefined : 'lightGray'; - } - selected?.forward(); -}); -ScriptingGlobals.add(function webBack(checkResult?: boolean) { - const selected = SelectionManager.Views().lastElement()?.ComponentView as WebBox; - if (checkResult) { - return selected?.back(checkResult) ? undefined : 'lightGray'; - } - selected?.back(); -}); - -/** Schema - * toggleSchemaPreview - **/ -ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) { - const selected = SelectionManager.Docs().lastElement(); - if (checkResult && selected) { - const result: boolean = NumCast(selected.schema_previewWidth) > 0; - if (result) return Colors.MEDIUM_BLUE; - else return 'transparent'; - } else if (selected) { - if (NumCast(selected.schema_previewWidth) > 0) { - selected.schema_previewWidth = 0; - } else { - selected.schema_previewWidth = 200; - } - } -}); -ScriptingGlobals.add(function toggleSingleLineSchema(checkResult?: boolean) { - const selected = SelectionManager.Docs().lastElement(); - if (checkResult && selected) { - return NumCast(selected._schema_singleLine) > 0 ? Colors.MEDIUM_BLUE : 'transparent'; - } - if (selected) { - selected._schema_singleLine = !selected._schema_singleLine; - } -}); - -/** STACK - * groupBy - */ -ScriptingGlobals.add(function setGroupBy(key: string, checkResult?: boolean) { - SelectionManager.Docs().map(doc => (doc._text_fontFamily = key)); - const editorView = RichTextMenu.Instance.TextView?.EditorView; - if (checkResult) { - return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); - } - if (editorView) RichTextMenu.Instance.setFontFamily(key); - else Doc.UserDoc().fontFamily = key; -}); diff --git a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx deleted file mode 100644 index 74c3c563c..000000000 --- a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import React, { Component } from 'react'; -import { BoolCast, StrCast } from '../../../../../fields/Types'; -import { IButtonProps } from '../ButtonInterface'; -import { ColorState, SketchPicker } from 'react-color'; -import { ScriptField } from '../../../../../fields/ScriptField'; -import { Doc } from '../../../../../fields/Doc'; -import { FontIconBox } from '../FontIconBox'; - -export class ColorDropdown extends Component<IButtonProps> { - render() { - const active: string = StrCast(this.props.rootDoc.dropDownOpen); - - const script: string = StrCast(this.props.rootDoc.script); - const scriptCheck: string = script + '(undefined, true)'; - const boolResult = ScriptField.MakeScript(scriptCheck)?.script.run().result; - - const stroke: boolean = false; - // if (script === "setStrokeColor") { - // stroke = true; - // const checkWidth = ScriptField.MakeScript("setStrokeWidth(0, true)")?.script.run().result; - // const width = 20 + (checkWidth / 100) * 70; - // const height = 20 + (checkWidth / 100) * 70; - // strokeIcon = (<div style={{ borderRadius: "100%", width: width + '%', height: height + '%', backgroundColor: boolResult ? boolResult : "#FFFFFF" }} />); - // } - - const colorOptions: string[] = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb']; - - const colorBox = (func: (color: ColorState) => void) => <SketchPicker disableAlpha={!stroke} onChange={func} color={boolResult ? boolResult : '#FFFFFF'} presetColors={colorOptions} />; - const label = - !this.props.label || !FontIconBox.GetShowLabels() ? null : ( - <div className="fontIconBox-label" style={{ color: this.props.color, backgroundColor: this.props.backgroundColor, position: 'absolute' }}> - {this.props.label} - </div> - ); - - const dropdownCaret = ( - <div className="menuButton-dropDown" style={{ borderBottomRightRadius: active ? 0 : undefined }}> - <FontAwesomeIcon icon={'caret-down'} color={this.props.color} size="sm" /> - </div> - ); - - const click = (value: ColorState) => { - const hex: string = value.hex; - const s = ScriptField.MakeScript(script + '("' + hex + '", false)'); - if (s) { - s.script.run().result; - } - }; - return ( - <div - className={`menuButton ${this.props.type} ${active}`} - style={{ color: this.props.color, borderBottomLeftRadius: active ? 0 : undefined }} - onClick={() => (this.props.rootDoc.dropDownOpen = !this.props.rootDoc.dropDownOpen)} - onPointerDown={e => e.stopPropagation()}> - <FontAwesomeIcon className={`fontIconBox-icon-${this.props.type}`} icon={this.props.icon} color={this.props.color} /> - <div className="colorButton-color" style={{ backgroundColor: boolResult ? boolResult : '#FFFFFF' }} /> - {label} - {/* {dropdownCaret} */} - {this.props.rootDoc.dropDownOpen ? ( - <div> - <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()} onClick={e => e.stopPropagation()}> - {colorBox(click)} - </div> - <div - className="dropbox-background" - onClick={e => { - e.stopPropagation(); - this.props.rootDoc.dropDownOpen = false; - }} - /> - </div> - ) : null} - </div> - ); - } -} diff --git a/src/client/views/nodes/button/colorDropdown/index.ts b/src/client/views/nodes/button/colorDropdown/index.ts deleted file mode 100644 index 1147d6457..000000000 --- a/src/client/views/nodes/button/colorDropdown/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ColorDropdown';
\ No newline at end of file diff --git a/src/client/views/nodes/button/textButton/TextButton.tsx b/src/client/views/nodes/button/textButton/TextButton.tsx deleted file mode 100644 index 5d7d55863..000000000 --- a/src/client/views/nodes/button/textButton/TextButton.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import React, { Component } from 'react'; -import { BoolCast } from '../../../../../fields/Types'; -import { IButtonProps } from '../ButtonInterface'; - -export class TextButton extends Component<IButtonProps> { - render() { - const type = this.props.type; - // Determine the type of toggle button - const buttonText: boolean = BoolCast(this.props.rootDoc.switchToggle); - - return ( - <div - className={`menuButton ${this.props.type}`} - style={{ - opacity: 1, - backgroundColor: this.props.backgroundColor, - color: this.props.color, - }} - > - <FontAwesomeIcon - className={`fontIconBox-icon-${this.props.type}`} - icon={this.props.icon} - color={this.props.color} - /> - {this.props.label} - </div> - ); - } -} diff --git a/src/client/views/nodes/button/textButton/index.ts b/src/client/views/nodes/button/textButton/index.ts deleted file mode 100644 index 01d62eb7e..000000000 --- a/src/client/views/nodes/button/textButton/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './TextButton';
\ No newline at end of file diff --git a/src/client/views/nodes/button/toggleButton/ToggleButton.tsx b/src/client/views/nodes/button/toggleButton/ToggleButton.tsx deleted file mode 100644 index dca6487d8..000000000 --- a/src/client/views/nodes/button/toggleButton/ToggleButton.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import React, { Component } from 'react'; -import { BoolCast } from '../../../../../fields/Types'; -import { Colors } from '../../../global/globalEnums'; -import { IButtonProps } from '../ButtonInterface'; - -export class ToggleButton extends Component<IButtonProps> { - render() { - const type = this.props.type; - // Determine the type of toggle button - const switchToggle: boolean = BoolCast(this.props.rootDoc.switchToggle); - - if (switchToggle) { - return ( - <div className={`menuButton ${type} ${'switch'}`}> - <label className="switch"> - <input type="checkbox" - checked={this.props.backgroundColor === Colors.MEDIUM_BLUE} - /> - <span className="slider round"></span> - </label> - </div> - ); - } else { - return ( - <div className={`menuButton ${type}`} - style={{ opacity: 1, backgroundColor: this.props.backgroundColor, color: this.props.color }}> - <FontAwesomeIcon className={`fontIconBox-icon-${type}`} icon={this.props.icon} color={this.props.color} /> - {this.props.label} - </div> - ); - } - } -}
\ No newline at end of file diff --git a/src/client/views/nodes/button/toggleButton/index.ts b/src/client/views/nodes/button/toggleButton/index.ts deleted file mode 100644 index cdb9c527c..000000000 --- a/src/client/views/nodes/button/toggleButton/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ToggleButton';
\ No newline at end of file diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index ef1c3911c..56af67802 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -1025,7 +1025,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { }); return true; }; - childLayoutTemplate = () => (!this.isTreeOrStack ? undefined : DocCast(Doc.UserDoc().presElement)); + childLayoutTemplate = () => (!this.isTreeOrStack ? DocCast(Doc.UserDoc().presElement) : DocCast(Doc.UserDoc().presElement)); removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc); getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65); // listBox padding-left and pres-box-cont minHeight panelHeight = () => this.props.PanelHeight() - 40; |
