aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/button
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/button')
-rw-r--r--src/client/views/nodes/button/ButtonInterface.ts12
-rw-r--r--src/client/views/nodes/button/ButtonScripts.ts14
-rw-r--r--src/client/views/nodes/button/FontIconBadge.scss11
-rw-r--r--src/client/views/nodes/button/FontIconBadge.tsx37
-rw-r--r--src/client/views/nodes/button/FontIconBox.scss411
-rw-r--r--src/client/views/nodes/button/FontIconBox.tsx877
-rw-r--r--src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx75
-rw-r--r--src/client/views/nodes/button/colorDropdown/index.ts1
-rw-r--r--src/client/views/nodes/button/textButton/TextButton.tsx17
-rw-r--r--src/client/views/nodes/button/textButton/index.ts1
-rw-r--r--src/client/views/nodes/button/toggleButton/ToggleButton.tsx34
-rw-r--r--src/client/views/nodes/button/toggleButton/index.ts1
12 files changed, 1491 insertions, 0 deletions
diff --git a/src/client/views/nodes/button/ButtonInterface.ts b/src/client/views/nodes/button/ButtonInterface.ts
new file mode 100644
index 000000000..0aa2ac8e1
--- /dev/null
+++ b/src/client/views/nodes/button/ButtonInterface.ts
@@ -0,0 +1,12 @@
+import { Doc } from "../../../../fields/Doc";
+import { IconProp } from "@fortawesome/fontawesome-svg-core";
+import { ButtonType } from "./FontIconBox";
+
+export interface IButtonProps {
+ type: string | ButtonType;
+ rootDoc: Doc;
+ label: any;
+ icon: IconProp;
+ color: string;
+ backgroundColor: string;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/button/ButtonScripts.ts b/src/client/views/nodes/button/ButtonScripts.ts
new file mode 100644
index 000000000..bb4dd8bc9
--- /dev/null
+++ b/src/client/views/nodes/button/ButtonScripts.ts
@@ -0,0 +1,14 @@
+import { Scripting } from "../../../util/Scripting";
+import { SelectionManager } from "../../../util/SelectionManager";
+
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function changeView(view: string) {
+ const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+ selected ? selected.Document._viewType = view : console.log("[FontIconBox.tsx] changeView failed");
+});
+
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function toggleOverlay() {
+ const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+ selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("failed");
+}); \ No newline at end of file
diff --git a/src/client/views/nodes/button/FontIconBadge.scss b/src/client/views/nodes/button/FontIconBadge.scss
new file mode 100644
index 000000000..78f506e57
--- /dev/null
+++ b/src/client/views/nodes/button/FontIconBadge.scss
@@ -0,0 +1,11 @@
+.fontIconBadge {
+ background: red;
+ width: 15px;
+ height: 15px;
+ top: 8px;
+ display: block;
+ position: absolute;
+ right: 5;
+ border-radius: 50%;
+ text-align: center;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/button/FontIconBadge.tsx b/src/client/views/nodes/button/FontIconBadge.tsx
new file mode 100644
index 000000000..cf86b5e07
--- /dev/null
+++ b/src/client/views/nodes/button/FontIconBadge.tsx
@@ -0,0 +1,37 @@
+import { observer } from "mobx-react";
+import * as React from "react";
+import { AclPrivate, Doc, DocListCast } from "../../../../fields/Doc";
+import { GetEffectiveAcl } from "../../../../fields/util";
+import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils";
+import { DragManager } from "../../../util/DragManager";
+import "./FontIconBadge.scss";
+
+interface FontIconBadgeProps {
+ collection: Doc | undefined;
+}
+
+@observer
+export class FontIconBadge extends React.Component<FontIconBadgeProps> {
+ _notifsRef = React.createRef<HTMLDivElement>();
+
+ onPointerDown = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e,
+ (e: PointerEvent) => {
+ const dragData = new DragManager.DocumentDragData([this.props.collection!]);
+ DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y);
+ return true;
+ },
+ returnFalse, emptyFunction, false);
+ }
+
+ render() {
+ if (!(this.props.collection instanceof Doc)) return (null);
+ const length = DocListCast(this.props.collection.data).filter(d => GetEffectiveAcl(d) !== AclPrivate).length; // Object.keys(d).length).length; // filter out any documents that we can't read
+ return <div className="fontIconBadge-container" ref={this._notifsRef}>
+ <div className="fontIconBadge" style={length > 0 ? { "display": "initial" } : { "display": "none" }}
+ onPointerDown={this.onPointerDown} >
+ {length}
+ </div>
+ </div>;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss
new file mode 100644
index 000000000..079c767b9
--- /dev/null
+++ b/src/client/views/nodes/button/FontIconBox.scss
@@ -0,0 +1,411 @@
+@import "../../global/globalCssVariables";
+
+.menuButton {
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-size: 80%;
+ border-radius: $standard-border-radius;
+ transition: 0.15s;
+
+ .menuButton-wrap {
+ grid-column: 1;
+ justify-content: center;
+ align-items: center;
+ text-align: center;
+ }
+
+ .fontIconBox-label {
+ color: $white;
+ position: relative;
+ text-align: center;
+ font-size: 7px;
+ letter-spacing: normal;
+ background-color: inherit;
+ margin-top: 5px;
+ border-radius: 8px;
+ padding: 0;
+ width: 100%;
+ font-family: 'ROBOTO';
+ text-transform: uppercase;
+ font-weight: bold;
+ transition: 0.15s;
+
+
+ }
+
+ .fontIconBox-icon {
+ width: 80%;
+ height: 80%;
+ }
+
+ &.clickBtn {
+ cursor: pointer;
+
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.3) !important;
+ }
+
+ svg {
+ width: 50% !important;
+ height: 50%;
+ }
+ }
+
+ &.textBtn {
+ display: grid;
+ /* grid-row: auto; */
+ grid-auto-flow: column;
+ cursor: pointer;
+ width: 100%;
+ justify-content: center;
+ align-items: center;
+ justify-items: center;
+
+ &:hover {
+ filter:brightness(0.85) !important;
+ }
+ }
+
+ &.tglBtn {
+ cursor: pointer;
+
+ &.switch {
+ //TOGGLE
+
+ .switch {
+ position: relative;
+ display: inline-block;
+ width: 100%;
+ height: 25px;
+ margin: 0;
+ }
+
+ .switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+ }
+
+ .slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: lightgrey;
+ -webkit-transition: .4s;
+ transition: .4s;
+ }
+
+ .slider:before {
+ position: absolute;
+ content: "";
+ height: 21px;
+ width: 21px;
+ left: 2px;
+ bottom: 2px;
+ background-color: $white;
+ -webkit-transition: .4s;
+ transition: .4s;
+ }
+
+ input:checked+.slider {
+ background-color: $medium-blue;
+ }
+
+ input:focus+.slider {
+ box-shadow: 0 0 1px $medium-blue;
+ }
+
+ input:checked+.slider:before {
+ -webkit-transform: translateX(26px);
+ -ms-transform: translateX(26px);
+ transform: translateX(26px);
+ }
+
+ /* Rounded sliders */
+ .slider.round {
+ border-radius: $standard-border-radius;
+ }
+
+ .slider.round:before {
+ border-radius: $standard-border-radius;
+ }
+ }
+
+ svg {
+ width: 50% !important;
+ height: 50%;
+ }
+
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.3);
+ }
+ }
+
+ &.toolBtn {
+ cursor: pointer;
+ width: 100%;
+ border-radius: 100%;
+
+ svg {
+ width: 60% !important;
+ height: 60%;
+ }
+ }
+
+ &.menuBtn {
+ cursor: pointer !important;
+ border-radius: 0px;
+ flex-direction: column;
+
+ svg {
+ width: 45% !important;
+ height: 45%;
+ }
+
+ &:hover{
+ filter: brightness(0.85);
+ }
+ }
+
+
+
+ &.colorBtn {
+ color: black;
+ cursor: pointer;
+ flex-direction: column;
+ background: transparent;
+
+ .colorButton-color {
+ margin-top: 3px;
+ width: 80%;
+ height: 3px;
+ }
+
+ .menuButton-dropdownBox {
+ position: absolute;
+ width: fit-content;
+ height: fit-content;
+ color: black;
+ top: 100%;
+ left: 0;
+ z-index: 21;
+ background-color: #e3e3e3;
+ box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+ border-radius: 3px;
+ }
+
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.3) !important;
+ }
+ }
+
+ &.drpdownList {
+ width: 100%;
+ display: grid;
+ grid-auto-columns: 80px 20px;
+ justify-items: center;
+ font-family: 'Roboto';
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ font-size: 13;
+ font-weight: 600;
+ overflow: hidden;
+ cursor: pointer;
+ background: transparent;
+ align-content: center;
+ align-items: center;
+
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.3) !important;
+ }
+
+ .menuButton-dropdownList {
+ position: absolute;
+ width: 150px;
+ height: fit-content;
+ top: 100%;
+ z-index: 21;
+ background-color: $white;
+ box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+ padding: 1px;
+
+ .list-item {
+ color: $black;
+ width: 100%;
+ height: 25px;
+ font-weight: 400;
+ display: flex;
+ justify-content: left;
+ align-items: center;
+ padding-left: 5px;
+ }
+
+ .list-item:hover {
+ background-color: lightgrey;
+ }
+ }
+ }
+
+ &.numBtn {
+ cursor: pointer;
+ background: transparent;
+
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.3) !important;
+ }
+
+ &.slider {
+ color: $white;
+ cursor: pointer;
+ flex-direction: column;
+ background: transparent;
+
+ .menu-slider {
+ width: 100px;
+ }
+
+ .menuButton-dropdownBox {
+ position: absolute;
+ width: fit-content;
+ height: fit-content;
+ top: 100%;
+ z-index: 21;
+ background-color: #e3e3e3;
+ box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+ border-radius: $standard-border-radius;
+ }
+ }
+
+ .button {
+ width: 25%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ &.number {
+ width: 50%;
+
+ .button-input {
+ background: none;
+ border: none;
+ text-align: right;
+ width: 100%;
+ color: $white;
+ height: 100%;
+ text-align: center;
+ }
+
+ .button-input:focus {
+ outline: none;
+ }
+ }
+ }
+
+ &.list {
+ width: 100%;
+ justify-content: space-around;
+ border: $standard-border;
+
+ .menuButton-dropdownList {
+ position: absolute;
+ width: fit-content;
+ height: fit-content;
+ min-width: 50%;
+ max-height: 50vh;
+ overflow-y: scroll;
+ top: 100%;
+ z-index: 21;
+ background-color: $white;
+ box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+ padding: 1px;
+
+ .list-item {
+ color: $black;
+ width: 100%;
+ height: 25px;
+ font-weight: 400;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
+ .list-item:hover {
+ background-color: lightgrey;
+ }
+ }
+ }
+ }
+
+ &.editableText {
+ cursor: text;
+ display: flex;
+ flex-direction: row;
+ gap: 5px;
+ padding-left: 10px;
+ justify-content: flex-start;
+ color: black;
+ background-color: $light-gray;
+ padding: 5px;
+ padding-left: 10px;
+ width: 100%;
+ height: 100%;
+ }
+
+ &.drpDownBtn {
+ cursor: pointer;
+ background: transparent;
+ border: solid 0.5px grey;
+
+ &.true {
+ background: rgba(0, 0, 0, 0.3);
+ }
+
+ .menuButton-dropdownBox {
+ position: absolute;
+ width: 150px;
+ height: 250px;
+ top: 100%;
+ background-color: #e3e3e3;
+ box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+ border-radius: $standard-border-radius;
+ }
+ }
+
+ .menuButton-dropdown {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-size: 15px;
+ grid-column: 2;
+ border-radius: 0px 7px 7px 0px;
+ width: 13px;
+ height: 100%;
+ right: 0;
+ }
+
+ .menuButton-dropdown-header {
+ width: 100%;
+ font-weight: 300;
+ padding: 5px;
+ overflow: hidden;
+ font-size: 12px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ }
+
+ .dropbox-background {
+ width: 100vw;
+ height: 100vh;
+ top: 0;
+ z-index: 20;
+ left: 0;
+ background: transparent;
+ position: fixed;
+ }
+
+} \ No newline at end of file
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
new file mode 100644
index 000000000..33fa23805
--- /dev/null
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -0,0 +1,877 @@
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { ColorState, SketchPicker } from 'react-color';
+import { Doc, StrListCast, WidthSym, HeightSym } from '../../../../fields/Doc';
+import { InkTool } from '../../../../fields/InkField';
+import { createSchema, makeInterface } from '../../../../fields/Schema';
+import { ScriptField } from '../../../../fields/ScriptField';
+import { BoolCast, Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { WebField } from '../../../../fields/URLField';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { Scripting } from "../../../util/Scripting";
+import { SelectionManager } from '../../../util/SelectionManager';
+import { UndoManager, undoBatch } from '../../../util/UndoManager';
+import { CollectionViewType } from '../../collections/CollectionView';
+import { ContextMenu } from '../../ContextMenu';
+import { DocComponent } from '../../DocComponent';
+import { EditableView } from '../../EditableView';
+import { GestureOverlay } from '../../GestureOverlay';
+import { Colors } from '../../global/globalEnums';
+import { SetActiveInkColor, ActiveFillColor, SetActiveFillColor, ActiveInkWidth, ActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
+import { StyleProp } from '../../StyleProvider';
+import { FieldView, FieldViewProps } from '.././FieldView';
+import { RichTextMenu } from '../formattedText/RichTextMenu';
+import { Utils } from '../../../../Utils';
+import { IButtonProps } from './ButtonInterface';
+import { FontIconBadge } from './FontIconBadge';
+import './FontIconBox.scss';
+import { WebBox } from '../WebBox';
+const FontIconSchema = createSchema({
+ icon: "string",
+});
+
+export enum ButtonType {
+ TextButton = "textBtn",
+ MenuButton = "menuBtn",
+ DropdownList = "drpdownList",
+ DropdownButton = "drpdownBtn",
+ ClickButton = "clickBtn",
+ DoubleButton = "dblBtn",
+ ToggleButton = "tglBtn",
+ ColorButton = "colorBtn",
+ ToolButton = "toolBtn",
+ NumberButton = "numBtn",
+ EditableText = "editableText"
+}
+
+export enum NumButtonType {
+ Slider = "slider",
+ DropdownOptions = "list",
+ Inline = "inline"
+}
+
+export interface ButtonProps extends FieldViewProps {
+ type?: ButtonType;
+}
+
+type FontIconDocument = makeInterface<[typeof FontIconSchema]>;
+const FontIconDocument = makeInterface(FontIconSchema);
+@observer
+export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(FontIconDocument) {
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FontIconBox, fieldKey); }
+ showTemplate = (): void => {
+ const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
+ dragFactory && this.props.addDocTab(dragFactory, "add:right");
+ }
+ 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.UserDoc().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" });
+ }
+ }
+
+ // Determining UI Specs
+ @observable private label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
+ @observable private icon = StrCast(this.dataDoc.icon, "user") as any;
+ @observable private dropdown: boolean = BoolCast(this.rootDoc.dropDownOpen);
+ @observable private buttonList: string[] = StrListCast(this.rootDoc.btnList);
+ @observable private type = 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 numberButton() {
+ const numBtnType: string = StrCast(this.rootDoc.numBtnType);
+ const setValue = (value: number) => {
+ // Script for running the toggle
+ const script: string = StrCast(this.rootDoc.script) + "(" + value + ")";
+ ScriptField.MakeScript(script)?.script.run();
+ };
+
+ // Script for checking the outcome of the toggle
+ const checkScript: string = StrCast(this.rootDoc.script) + "(0, true)";
+ const checkResult: number = ScriptField.MakeScript(checkScript)?.script.run().result || 0;
+
+
+ if (numBtnType === NumButtonType.Slider) {
+ const dropdown =
+ <div
+ className="menuButton-dropdownBox"
+ onPointerDown={e => e.stopPropagation()}
+ >
+ <input type="range" step="1" min={NumCast(this.rootDoc.numBtnMin, 0)} max={NumCast(this.rootDoc.numBtnMax, 100)} value={checkResult}
+ className={"menu-slider"} id="slider"
+ onPointerDown={() => this._batch = UndoManager.StartBatch("presDuration")}
+ onPointerUp={() => this._batch?.end()}
+ onChange={e => { e.stopPropagation(); setValue(Number(e.target.value)); }}
+ />
+ </div>;
+ return (
+ <div
+ className={`menuButton ${this.type} ${numBtnType}`}
+ onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}
+ >
+ {checkResult}
+ {this.rootDoc.dropDownOpen ? dropdown : null}
+ </div>
+ );
+ } else if (numBtnType === NumButtonType.DropdownOptions) {
+ const items: number[] = [];
+ for (let i = 0; i < 100; i++) {
+ if (i % 2 === 0) {
+ items.push(i);
+ }
+ }
+ const list = items.map((value) => {
+ return <div className="list-item" key={`${value}`}
+ style={{
+ backgroundColor: value === checkResult ? Colors.LIGHT_BLUE : undefined
+ }}
+ onClick={() => setValue(value)}>
+ {value}
+ </div>;
+ });
+ return (
+ <div
+ className={`menuButton ${this.type} ${numBtnType}`}
+ >
+ <div className={`button`} onClick={action((e) => setValue(Number(checkResult) - 1))}>
+ <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)}
+ >
+ <input
+ style={{ width: 30 }}
+ className="button-input"
+ type="number"
+ value={checkResult}
+ onChange={action((e) => setValue(Number(e.target.value)))}
+ />
+ </div>
+ <div className={`button`} onClick={action((e) => setValue(Number(checkResult) + 1))}>
+ <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={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} />
+ </div> : null}
+
+ </div>
+ );
+ } else {
+ return (
+ <div>
+
+ </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)}>
+ <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+ {!this.label || !Doc.UserDoc()._showLabel ? (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: string = StrCast(this.rootDoc.script);
+
+ let noviceList: string[] = [];
+ let text: string | undefined;
+ let dropdown = true;
+ let icon: IconProp = "caret-down";
+
+ if (script === 'setView') {
+ const selected = SelectionManager.Docs().lastElement();
+ if (selected) {
+ if (StrCast(selected.type) === DocumentType.COL) {
+ text = StrCast(selected._viewType);
+ } 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];
+ } else if (script === 'setFont') {
+ const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+ text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+ noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia",
+ "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
+ }
+
+ // Get items to place into the list
+ const list = this.buttonList.map((value) => {
+ if (Doc.UserDoc().noviceMode && !noviceList.includes(value)) {
+ return;
+ }
+ const click = () => {
+ const s = ScriptField.MakeScript(script + '("' + value + '")');
+ if (s) {
+ s.script.run().result;
+ }
+ };
+ return <div className="list-item" key={`${value}`}
+ style={{
+ fontFamily: script === 'setFont' ? value : undefined,
+ backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined
+ }}
+ onClick={click}>
+ {value[0].toUpperCase() + value.slice(1)}
+ </div>;
+ });
+
+ const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+ <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
+ {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 ? () => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : 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={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} />
+ </div>
+ : null}
+ </div>
+ );
+ }
+
+ @observable colorPickerClosed: boolean = true;
+ @computed get colorScript() {
+ const script = StrCast(this.rootDoc.script);
+ return ScriptField.MakeScript(script + '(colValue, checkResult)', { colValue: "string", checkResult: "boolean" });
+ }
+
+ colorPicker = (curColor: string) => {
+ const change = (value: ColorState) => {
+ const s = this.colorScript;
+ s && undoBatch(() => s.script.run({ colValue: Utils.colorString(value), checkResult: false }).result)();
+ };
+ const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
+ '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
+ '#FFFFFF', '#f1efeb', "transparent"];
+ return <SketchPicker
+ onChange={change}
+ 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({ colValue: undefined, checkResult: true }).result ?? "transparent";
+
+ const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+ <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
+ {this.label}
+ </div>;
+
+ const dropdownCaret = <div
+ className="menuButton-dropDown"
+ style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
+ <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
+ </div>;
+ setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView
+ return (
+ <div className={`menuButton ${this.type} ${this.colorPickerClosed}`}
+ style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
+ onClick={action(() => this.colorPickerClosed = !this.colorPickerClosed)}
+ onPointerDown={e => e.stopPropagation()}>
+ <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+ <div className="colorButton-color" style={{ backgroundColor: curColor }} />
+ {label}
+ {/* {dropdownCaret} */}
+ {this.colorPickerClosed ? (null) :
+ <div>
+ <div className="menuButton-dropdownBox"
+ onPointerDown={e => e.stopPropagation()}
+ onClick={e => e.stopPropagation()}>
+ {this.colorPicker(curColor)}
+ </div>
+ <div className="dropbox-background" onClick={action((e) => {
+ e.stopPropagation(); this.colorPickerClosed = true;
+ })} />
+ </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 || !Doc.UserDoc()._showLabel ? (null) :
+ <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
+ {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"></span>
+ </label>
+ </div>
+ );
+ } else {
+ return (
+ <div className={`menuButton ${this.type}`}
+ style={{ opacity: 1, backgroundColor, color }}>
+ <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={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);
+ const active: string = StrCast(this.rootDoc.dropDownOpen);
+ return (
+ <div className={`menuButton ${this.type}`} onContextMenu={this.specificContextMenu}
+ style={{ backgroundColor: "transparent", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}>
+ <div className="menuButton-wrap">
+ <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={"black"} size={"sm"} />
+ {!this.label || !Doc.UserDoc()._showLabel ? (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: string = StrCast(this.rootDoc.script);
+
+ // Script for checking the outcome of the toggle
+ const checkScript: string = StrCast(this.rootDoc.script) + "('', true)";
+
+ // Function to run the script
+ const checkResult = ScriptField.MakeScript(checkScript)?.script.run().result;
+
+ const setValue = (value: string, shiftDown?: boolean): boolean => {
+ ScriptField.MakeScript(script + "('" + value + "')")?.script.run();
+ return true;
+ };
+ return (
+ <div className="menuButton editableText">
+ <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} />
+ <div style={{ width: "calc(100% - .875em)", paddingLeft: "4px" }}>
+ <EditableView GetValue={() => checkResult} SetValue={setValue} 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 = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+ <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
+ {this.label}
+ </div>;
+
+ const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
+ <div className="fontIconBox-label" style={{ color: color, backgroundColor: "transparent" }}>
+ {this.label}
+ </div>;
+
+ const buttonProps: IButtonProps = {
+ type: this.type,
+ rootDoc: this.rootDoc,
+ label: label,
+ backgroundColor: backgroundColor,
+ icon: this.icon,
+ color: color
+ };
+
+ const buttonText = StrCast(this.rootDoc.buttonText);
+
+ // TODO:glr Add label of button type
+ let button = this.defaultButton;
+
+ switch (this.type) {
+ case ButtonType.TextButton:
+ button = (
+ <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
+ <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+ {buttonText ?
+ <div className="button-text">
+ {buttonText}
+ </div>
+ : null}
+ {label}
+ </div>
+ );
+ // button = <TextButton {...buttonProps}></TextButton>
+ break;
+ case ButtonType.EditableText:
+ button = this.editableText;
+ break;
+ case ButtonType.NumberButton:
+ button = this.numberButton;
+ break;
+ case ButtonType.DropdownButton:
+ button = this.dropdownButton;
+ break;
+ case ButtonType.DropdownList:
+ button = this.dropdownListButton;
+ break;
+ case ButtonType.ColorButton:
+ button = this.colorButton;
+ break;
+ case ButtonType.ToolButton:
+ button = (
+ <div className={`menuButton ${this.type}`} style={{ opacity: 1, backgroundColor: backgroundColor, color: color }}>
+ <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+ {label}
+ </div>
+ );
+ break;
+ case ButtonType.ToggleButton:
+ button = this.toggleButton;
+ // button = <ToggleButton {...buttonProps}></ToggleButton>
+ break;
+ case ButtonType.ClickButton:
+ button = (
+ <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: 1 }}>
+ <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+ {label}
+ </div>
+ );
+ break;
+ case ButtonType.MenuButton:
+ const trailsIcon = <img src={`/assets/${"presTrails.png"}`}
+ style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})` }} />;
+ button = (
+ <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor }}>
+ {this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />}
+ {menuLabel}
+ <FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} />
+ </div >
+ );
+ break;
+ default:
+ break;
+ }
+
+ return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? button :
+ <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>
+ {button}
+ </Tooltip>;
+ }
+}
+
+
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function setView(view: string) {
+ const selected = SelectionManager.Docs().lastElement();
+ selected ? selected._viewType = view : console.log("[FontIconBox.tsx] changeView failed");
+});
+
+
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: boolean) {
+ const selected = SelectionManager.Docs().lastElement();
+ if (checkResult) {
+ return selected?._backgroundColor ?? "transparent";
+ }
+ if (selected) selected._backgroundColor = color;
+});
+
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function setHeaderColor(color?: string, checkResult?: boolean) {
+ if (checkResult) {
+ return Doc.SharingDoc().userColor;
+ }
+ Doc.SharingDoc().userColor = undefined;
+ Doc.GetProto(Doc.SharingDoc()).userColor = color;
+ Doc.UserDoc().showTitle = color === "transparent" ? undefined : StrCast(Doc.UserDoc().showTitle, "creationDate");
+});
+
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
+ const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+ if (checkResult && selected) {
+ if (NumCast(selected.Document.z) >= 1) return Colors.MEDIUM_BLUE;
+ return "transparent";
+ }
+ selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed");
+});
+
+/** TEXT
+ * setFont
+ * setFontSize
+ * toggleBold
+ * toggleUnderline
+ * toggleItalic
+ * setAlignment
+ * toggleBold
+ * toggleItalic
+ * toggleUnderline
+ **/
+
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function setFont(font: string, checkResult?: boolean) {
+ SelectionManager.Docs().map(doc => doc._fontFamily = font);
+ const editorView = RichTextMenu.Instance.TextView?.EditorView;
+ if (checkResult) {
+ return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+ }
+ if (editorView) RichTextMenu.Instance.setFontFamily(font);
+ else Doc.UserDoc().fontFamily = font;
+});
+
+Scripting.addGlobal(function getActiveTextInfo(info: "family" | "size" | "color" | "highlight") {
+ const editorView = RichTextMenu.Instance.TextView?.EditorView;
+ const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection();
+ switch (info) {
+ case "family": return style?.activeFamilies[0];
+ case "size": return style?.activeSizes[0];
+ case "color": return style?.activeColors[0];
+ case "highlight": return style?.activeHighlights[0];
+ }
+});
+
+Scripting.addGlobal(function setAlignment(align: "left" | "right" | "center", checkResult?: boolean) {
+ const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+ if (checkResult) {
+ let active: string;
+ if (editorView) {
+ active = editorView?.state && RichTextMenu.Instance.getActiveAlignment();
+ } else {
+ active = StrCast(Doc.UserDoc().textAlign);
+ }
+ if (active === align) return Colors.MEDIUM_BLUE;
+ return "transparent";
+ }
+ SelectionManager.Docs().map(doc => doc.textAlign = align);
+ switch (align) {
+ case "left":
+ editorView?.state && RichTextMenu.Instance.alignLeft(editorView, editorView.dispatch);
+ break;
+ case "center":
+ editorView?.state && RichTextMenu.Instance.alignCenter(editorView, editorView.dispatch);
+ break;
+ case "right":
+ editorView?.state && RichTextMenu.Instance.alignRight(editorView, editorView.dispatch);
+ break;
+ default:
+ break;
+ }
+ Doc.UserDoc().textAlign = align;
+});
+
+Scripting.addGlobal(function setBulletList(mapStyle: "bullet" | "decimal", checkResult?: boolean) {
+ const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+ if (checkResult) {
+ const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle();
+ if (active === mapStyle) return Colors.MEDIUM_BLUE;
+ return "transparent";
+ }
+ if (editorView) {
+ const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle();
+ editorView?.state && RichTextMenu.Instance.changeListType(
+ editorView.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? "" : mapStyle }));
+ }
+});
+
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean) {
+ const editorView = RichTextMenu.Instance.TextView?.EditorView;
+
+ if (checkResult) {
+ return editorView ? RichTextMenu.Instance.fontColor : Doc.UserDoc().fontColor;
+ }
+
+ if (editorView) color && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch);
+ else Doc.UserDoc().fontColor = color;
+});
+
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: boolean) {
+ const selected = SelectionManager.Docs().lastElement();
+ const editorView = RichTextMenu.Instance.TextView?.EditorView;
+
+ if (checkResult) {
+ return (selected ?? Doc.UserDoc())._fontHighlight;
+ }
+ if (selected) {
+ selected._fontColor = color;
+ if (color) {
+ editorView?.state && RichTextMenu.Instance.setHighlight(color, editorView, editorView?.dispatch);
+ }
+ }
+ Doc.UserDoc()._fontHighlight = color;
+});
+
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function setFontSize(size: string | number, checkResult?: boolean) {
+ if (typeof size === "number") size = size.toString();
+ if (size && Number(size).toString() === size) size += "px";
+ const editorView = RichTextMenu.Instance.TextView?.EditorView;
+ if (checkResult) {
+ return (editorView ? RichTextMenu.Instance.fontSize : StrCast(Doc.UserDoc().fontSize, "10px")).replace("px", "");
+ }
+ if (editorView) RichTextMenu.Instance.setFontSize(size);
+ else Doc.UserDoc()._fontSize = size;
+});
+
+Scripting.addGlobal(function toggleBold(checkResult?: boolean) {
+ const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+ if (checkResult) {
+ return (editorView ? RichTextMenu.Instance.bold : Doc.UserDoc().fontWeight === "bold") ? Colors.MEDIUM_BLUE : "transparent";
+ }
+ if (editorView) RichTextMenu.Instance?.toggleBold();
+ else Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === "bold" ? undefined : "bold";
+});
+
+Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) {
+ const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+ if (checkResult) {
+ return (editorView ? RichTextMenu.Instance.underline : Doc.UserDoc().textDecoration === "underline") ? Colors.MEDIUM_BLUE : "transparent";
+ }
+ if (editorView) RichTextMenu.Instance?.toggleUnderline();
+ else Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === "underline" ? undefined : "underline";
+});
+
+Scripting.addGlobal(function toggleItalic(checkResult?: boolean) {
+ const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+ if (checkResult) {
+ return (editorView ? RichTextMenu.Instance.italics : Doc.UserDoc().fontStyle === "italics") ? Colors.MEDIUM_BLUE : "transparent";
+ }
+ if (editorView) RichTextMenu.Instance?.toggleItalics();
+ else Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === "italics" ? undefined : "italics";
+});
+
+
+
+
+/** INK
+ * setActiveInkTool
+ * setStrokeWidth
+ * setStrokeColor
+ **/
+
+Scripting.addGlobal(function setActiveInkTool(tool: string, checkResult?: boolean) {
+ if (checkResult) {
+ return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
+ Colors.MEDIUM_BLUE : "transparent";
+ }
+ if (["circle", "square", "line"].includes(tool)) {
+ if (GestureOverlay.Instance.InkShape === tool) {
+ Doc.UserDoc().activeInkTool = InkTool.None;
+ GestureOverlay.Instance.InkShape = InkTool.None;
+ } else {
+ Doc.UserDoc().activeInkTool = InkTool.Pen;
+ GestureOverlay.Instance.InkShape = tool;
+ }
+ } else if (tool) { // pen
+ if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) {
+ Doc.UserDoc().activeInkTool = InkTool.None;
+ } else {
+ Doc.UserDoc().activeInkTool = tool;
+ GestureOverlay.Instance.InkShape = "";
+ }
+ } else {
+ Doc.UserDoc().activeInkTool = InkTool.None;
+ }
+});
+
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function setFillColor(color?: string, checkResult?: boolean) {
+ const selected = SelectionManager.Docs().lastElement();
+ if (checkResult) {
+ if (selected?.type === DocumentType.INK) {
+ return StrCast(selected.fillColor);
+ }
+ return ActiveFillColor();
+ }
+ SetActiveFillColor(StrCast(color));
+ SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.fillColor = color);
+});
+
+Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean) {
+ if (checkResult) {
+ const selected = SelectionManager.Docs().lastElement();
+ if (selected?.type === DocumentType.INK) {
+ return NumCast(selected.strokeWidth);
+ }
+ return ActiveInkWidth();
+ }
+ SetActiveInkWidth(width.toString());
+ SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.strokeWidth = Number(width));
+});
+
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function setStrokeColor(color?: string, checkResult?: boolean) {
+ if (checkResult) {
+ const selected = SelectionManager.Docs().lastElement();
+ if (selected?.type === DocumentType.INK) {
+ return StrCast(selected.color);
+ }
+ return ActiveInkColor();
+ }
+ SetActiveInkColor(StrCast(color));
+ SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.color = String(color));
+});
+
+
+/** WEB
+ * webSetURL
+ **/
+Scripting.addGlobal(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 as WebBox).submitURL(url);
+ //selected.rootDoc.data = new WebField(url);
+ }
+});
+Scripting.addGlobal(function webForward() {
+ const selected = SelectionManager.Views().lastElement();
+ if (selected?.rootDoc.type === DocumentType.WEB) {
+ (selected.ComponentView as WebBox).forward();
+ }
+});
+Scripting.addGlobal(function webBack() {
+ const selected = SelectionManager.Views().lastElement();
+ if (selected?.rootDoc.type === DocumentType.WEB) {
+ (selected.ComponentView as WebBox).back();
+ }
+});
+
+
+/** Schema
+ * toggleSchemaPreview
+ **/
+Scripting.addGlobal(function toggleSchemaPreview(checkResult?: boolean) {
+ const selected = SelectionManager.Docs().lastElement();
+ if (checkResult && selected) {
+ const result: boolean = NumCast(selected.schemaPreviewWidth) > 0;
+ if (result) return Colors.MEDIUM_BLUE;
+ else return "transparent";
+ }
+ else if (selected) {
+ if (NumCast(selected.schemaPreviewWidth) > 0) {
+ selected.schemaPreviewWidth = 200;
+ } else {
+ selected.schemaPreviewWidth = 0;
+ }
+ }
+});
+
+/** STACK
+ * groupBy
+ */
+Scripting.addGlobal(function setGroupBy(key: string, checkResult?: boolean) {
+ SelectionManager.Docs().map(doc => doc._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;
+}); \ No newline at end of file
diff --git a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
new file mode 100644
index 000000000..235495250
--- /dev/null
+++ b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
@@ -0,0 +1,75 @@
+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';
+
+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 || !Doc.UserDoc()._showLabel ? (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>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/button/colorDropdown/index.ts b/src/client/views/nodes/button/colorDropdown/index.ts
new file mode 100644
index 000000000..1147d6457
--- /dev/null
+++ b/src/client/views/nodes/button/colorDropdown/index.ts
@@ -0,0 +1 @@
+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
new file mode 100644
index 000000000..e18590a95
--- /dev/null
+++ b/src/client/views/nodes/button/textButton/TextButton.tsx
@@ -0,0 +1,17 @@
+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>);
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/button/textButton/index.ts b/src/client/views/nodes/button/textButton/index.ts
new file mode 100644
index 000000000..01d62eb7e
--- /dev/null
+++ b/src/client/views/nodes/button/textButton/index.ts
@@ -0,0 +1 @@
+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
new file mode 100644
index 000000000..dca6487d8
--- /dev/null
+++ b/src/client/views/nodes/button/toggleButton/ToggleButton.tsx
@@ -0,0 +1,34 @@
+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
new file mode 100644
index 000000000..cdb9c527c
--- /dev/null
+++ b/src/client/views/nodes/button/toggleButton/index.ts
@@ -0,0 +1 @@
+export * from './ToggleButton'; \ No newline at end of file