aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/ScriptingRepl.tsx191
-rw-r--r--src/client/views/StyleProvider.tsx108
-rw-r--r--src/client/views/collections/CollectionMenu.tsx73
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx181
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx2
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss2
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx53
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts14
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx56
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts19
-rw-r--r--src/client/views/webcam/DashWebRTCVideo.scss82
-rw-r--r--src/client/views/webcam/DashWebRTCVideo.tsx76
-rw-r--r--src/client/views/webcam/WebCamLogic.js292
13 files changed, 410 insertions, 739 deletions
diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx
index acf0ecff4..ba2e22b3b 100644
--- a/src/client/views/ScriptingRepl.tsx
+++ b/src/client/views/ScriptingRepl.tsx
@@ -1,3 +1,6 @@
+/* eslint-disable react/no-array-index-key */
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
@@ -12,6 +15,36 @@ import { OverlayView } from './OverlayView';
import './ScriptingRepl.scss';
import { DocumentIconContainer } from './nodes/DocumentIcon';
+interface replValueProps {
+ scrollToBottom: () => void;
+ value: any;
+ name?: string;
+}
+@observer
+export class ScriptingValueDisplay extends ObservableReactComponent<replValueProps> {
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
+ render() {
+ const val = this._props.name ? this._props.value[this._props.name] : this._props.value;
+ const title = (name: string) => (
+ <>
+ {this._props.name ? <b>{this._props.name} : </b> : <> </>}
+ {name}
+ </>
+ );
+ if (typeof val === 'object') {
+ // eslint-disable-next-line no-use-before-define
+ return <ScriptingObjectDisplay scrollToBottom={this._props.scrollToBottom} value={val} name={this._props.name} />;
+ }
+ if (typeof val === 'function') {
+ return <div className="scriptingObject-leaf">{title('[Function]')}</div>;
+ }
+ return <div className="scriptingObject-leaf">{title(String(val))}</div>;
+ }
+}
interface ReplProps {
scrollToBottom: () => void;
value: { [key: string]: any };
@@ -37,7 +70,7 @@ export class ScriptingObjectDisplay extends ObservableReactComponent<ReplProps>
const name = (proto && proto.constructor && proto.constructor.name) || String(val);
const title = (
<>
- {this.props.name ? <b>{this._props.name} : </b> : <></>}
+ {this.props.name ? <b>{this._props.name} : </b> : null}
{name}
</>
);
@@ -50,53 +83,23 @@ export class ScriptingObjectDisplay extends ObservableReactComponent<ReplProps>
{title} (+{Object.keys(val).length})
</div>
);
- } else {
- return (
- <div className="scriptingObject-open">
- <div>
- <span onClick={this.toggle} className="scriptingObject-icon">
- <FontAwesomeIcon icon="caret-down" size="sm" />
- </span>
- {title}
- </div>
- <div className="scriptingObject-fields">
- {Object.keys(val).map(key => (
- <ScriptingValueDisplay {...this._props} name={key} />
- ))}
- </div>
- </div>
- );
}
- }
-}
-
-interface replValueProps {
- scrollToBottom: () => void;
- value: any;
- name?: string;
-}
-@observer
-export class ScriptingValueDisplay extends ObservableReactComponent<replValueProps> {
- constructor(props: any) {
- super(props);
- makeObservable(this);
- }
-
- render() {
- const val = this._props.name ? this._props.value[this._props.name] : this._props.value;
- const title = (name: string) => (
- <>
- {this._props.name ? <b>{this._props.name} : </b> : <> </>}
- {name}
- </>
+ return (
+ <div className="scriptingObject-open">
+ <div>
+ <span onClick={this.toggle} className="scriptingObject-icon">
+ <FontAwesomeIcon icon="caret-down" size="sm" />
+ </span>
+ {title}
+ </div>
+ <div className="scriptingObject-fields">
+ {Object.keys(val).map(key => (
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ <ScriptingValueDisplay {...this._props} name={key} />
+ ))}
+ </div>
+ </div>
);
- if (typeof val === 'object') {
- return <ScriptingObjectDisplay scrollToBottom={this._props.scrollToBottom} value={val} name={this._props.name} />;
- } else if (typeof val === 'function') {
- const name = '[Function]';
- return <div className="scriptingObject-leaf">{title('[Function]')}</div>;
- }
- return <div className="scriptingObject-leaf">{title(String(val))}</div>;
}
}
@@ -119,47 +122,45 @@ export class ScriptingRepl extends ObservableReactComponent<{}> {
private args: any = {};
- getTransformer = (): Transformer => {
- return {
- transformer: context => {
- const knownVars: { [name: string]: number } = {};
- const usedDocuments: number[] = [];
- ScriptingGlobals.getGlobals().forEach((global: any) => (knownVars[global] = 1));
- return root => {
- function visit(node: ts.Node) {
- let skip = false;
- if (ts.isIdentifier(node)) {
- if (ts.isParameter(node.parent)) {
- skip = true;
- knownVars[node.text] = 1;
- }
+ getTransformer = (): Transformer => ({
+ transformer: context => {
+ const knownVars: { [name: string]: number } = {};
+ const usedDocuments: number[] = [];
+ ScriptingGlobals.getGlobals().forEach((global: any) => {
+ knownVars[global] = 1;
+ });
+ return root => {
+ function visit(nodeIn: ts.Node) {
+ if (ts.isIdentifier(nodeIn)) {
+ if (ts.isParameter(nodeIn.parent)) {
+ knownVars[nodeIn.text] = 1;
}
- node = ts.visitEachChild(node, visit, context);
+ }
+ const node = ts.visitEachChild(nodeIn, visit, context);
- if (ts.isIdentifier(node)) {
- const isntPropAccess = !ts.isPropertyAccessExpression(node.parent) || node.parent.expression === node;
- const isntPropAssign = !ts.isPropertyAssignment(node.parent) || node.parent.name !== node;
- if (ts.isParameter(node.parent)) {
- // delete knownVars[node.text];
- } else if (isntPropAccess && isntPropAssign && !(node.text in knownVars) && !(node.text in globalThis)) {
- const match = node.text.match(/d([0-9]+)/);
- if (match) {
- const m = parseInt(match[1]);
- usedDocuments.push(m);
- } else {
- return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('args'), node);
- // ts.createPropertyAccess(ts.createIdentifier('args'), node);
- }
+ if (ts.isIdentifier(node)) {
+ const isntPropAccess = !ts.isPropertyAccessExpression(node.parent) || node.parent.expression === node;
+ const isntPropAssign = !ts.isPropertyAssignment(node.parent) || node.parent.name !== node;
+ if (ts.isParameter(node.parent)) {
+ // delete knownVars[node.text];
+ } else if (isntPropAccess && isntPropAssign && !(node.text in knownVars) && !(node.text in globalThis)) {
+ const match = node.text.match(/d([0-9]+)/);
+ if (match) {
+ const m = parseInt(match[1]);
+ usedDocuments.push(m);
+ } else {
+ return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('args'), node);
+ // ts.createPropertyAccess(ts.createIdentifier('args'), node);
}
}
-
- return node;
}
- return ts.visitNode(root, visit);
- };
- },
- };
- };
+
+ return node;
+ }
+ return ts.visitNode(root, visit);
+ };
+ },
+ });
@action
onKeyDown = (e: React.KeyboardEvent) => {
@@ -168,14 +169,16 @@ export class ScriptingRepl extends ObservableReactComponent<{}> {
case 'Enter': {
e.stopPropagation();
const docGlobals: { [name: string]: any } = {};
- DocumentManager.Instance.DocumentViews.forEach((dv, i) => (docGlobals[`d${i}`] = dv.Document));
+ DocumentManager.Instance.DocumentViews.forEach((dv, i) => {
+ docGlobals[`d${i}`] = dv.Document;
+ });
const globals = ScriptingGlobals.makeMutableGlobalsCopy(docGlobals);
const script = CompileScript(this.commandString, { typecheck: false, addReturn: true, editable: true, params: { args: 'any' }, transformer: this.getTransformer(), globals });
if (!script.compiled) {
this.commands.push({ command: this.commandString, result: script.errors });
return;
}
- const result = undoable(() => script.run({ args: this.args }, e => this.commands.push({ command: this.commandString, result: e.toString() })), 'run:' + this.commandString)();
+ const result = undoable(() => script.run({ args: this.args }, () => this.commands.push({ command: this.commandString, result: e.toString() })), 'run:' + this.commandString)();
if (result.success) {
this.commands.push({ command: this.commandString, result: result.result });
this.commandsHistory.push(this.commandString);
@@ -260,18 +263,16 @@ export class ScriptingRepl extends ObservableReactComponent<{}> {
return (
<div className="scriptingRepl-outerContainer">
<div className="scriptingRepl-commandsContainer" style={{ background: SettingsManager.userBackgroundColor }} ref={this.commandsRef}>
- {this.commands.map(({ command, result }, i) => {
- return (
- <div className="scriptingRepl-resultContainer" style={{ background: SettingsManager.userBackgroundColor }} key={i}>
- <div className="scriptingRepl-commandString" style={{ background: SettingsManager.userBackgroundColor }}>
- {command || <br />}
- </div>
- <div className="scriptingRepl-commandResult" style={{ background: SettingsManager.userBackgroundColor }}>
- {<ScriptingValueDisplay scrollToBottom={this.maybeScrollToBottom} value={result} />}
- </div>
+ {this.commands.map(({ command, result }, i) => (
+ <div className="scriptingRepl-resultContainer" style={{ background: SettingsManager.userBackgroundColor }} key={i}>
+ <div className="scriptingRepl-commandString" style={{ background: SettingsManager.userBackgroundColor }}>
+ {command || <br />}
+ </div>
+ <div className="scriptingRepl-commandResult" style={{ background: SettingsManager.userBackgroundColor }}>
+ <ScriptingValueDisplay scrollToBottom={this.maybeScrollToBottom} value={result} />
</div>
- );
- })}
+ </div>
+ ))}
</div>
<input
className="scriptingRepl-commandInput"
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 75f1a7d80..3697aa010 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -49,6 +49,7 @@ export enum StyleProp {
TitleHeight = 'titleHeight', // Height of Title area
ShowTitle = 'layout_showTitle', // whether to display a title on a Document (optional :hover suffix)
BorderPath = 'customBorder', // border path for document view
+ FontColor = 'fontColor', // color o tet
FontSize = 'fontSize', // size of text font
FontFamily = 'fontFamily', // font family of text
FontWeight = 'fontWeight', // font weight of text
@@ -109,13 +110,35 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
const layoutDoc = doc ? Doc.Layout(doc) : doc;
const isOpen = property.includes(':open');
const boxBackground = property.includes(':box');
- const fieldKey = props?.fieldKey ? props.fieldKey + '_' : isCaption ? 'caption_' : '';
- const isInk = () => layoutDoc?._layout_isSvg && !props?.LayoutTemplateString;
+ const {
+ fieldKey: fieldKeyProp,
+ styleProvider,
+ pointerEvents,
+ isGroupActive,
+ isDocumentActive,
+ containerViewPath,
+ childFilters,
+ hideCaptions,
+ // eslint-disable-next-line camelcase
+ layout_showTitle,
+ childFiltersByRanges,
+ renderDepth,
+ docViewPath,
+ DocumentView,
+ LayoutTemplateString,
+ disableBrushing,
+ NativeDimScaling,
+ PanelWidth,
+ PanelHeight,
+ } = props || {}; // extract props that are not shared between fieldView and documentView props.
+ const fieldKey = fieldKeyProp ? fieldKeyProp + '_' : isCaption ? 'caption_' : '';
+ const isInk = () => layoutDoc?._layout_isSvg && !LayoutTemplateString;
const lockedPosition = () => doc && BoolCast(doc._lockedPosition);
- const titleHeight = () => props?.styleProvider?.(doc, props, StyleProp.TitleHeight);
- const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor + ':nonTransparent' + (isNonTransparentLevel + 1));
- const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity);
- const layoutShowTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle);
+ const titleHeight = () => styleProvider?.(doc, props, StyleProp.TitleHeight);
+ const backgroundCol = () => styleProvider?.(doc, props, StyleProp.BackgroundColor + ':nonTransparent' + (isNonTransparentLevel + 1));
+ const color = () => styleProvider?.(doc, props, StyleProp.Color);
+ const opacity = () => styleProvider?.(doc, props, StyleProp.Opacity);
+ const layoutShowTitle = () => styleProvider?.(doc, props, StyleProp.ShowTitle);
// prettier-ignore
switch (property.split(':')[0]) {
case StyleProp.TreeViewIcon: {
@@ -137,7 +160,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
}
case StyleProp.Highlighting:
if (doc && (Doc.IsSystem(doc) || doc.type === DocumentType.FONTICON)) return undefined;
- if (doc && !doc.layout_disableBrushing && !props?.disableBrushing) {
+ if (doc && !doc.layout_disableBrushing && !disableBrushing) {
const selected = Array.from(doc?.[DocViews]??[]).filter(dv => dv.IsSelected).length;
const highlightIndex = Doc.GetBrushHighlightStatus(doc) || (selected ? Doc.DocBrushStatus.selfBrushed : 0);
const highlightColor = ['transparent', 'rgb(68, 118, 247)', selected ? "black" : 'rgb(68, 118, 247)', 'orange', 'lightBlue'][highlightIndex];
@@ -152,26 +175,27 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
}
}
return undefined;
- case StyleProp.DocContents:return undefined;
- case StyleProp.WidgetColor:return isAnnotated ? Colors.LIGHT_BLUE : 'dimgrey';
- case StyleProp.Opacity: return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : doc?.text_inlineAnnotations ? 0 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null));
- case StyleProp.FontSize: return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(Doc.UserDoc().fontSize));
- case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(Doc.UserDoc().fontFamily));
- case StyleProp.FontWeight: return StrCast(doc?.[fieldKey + 'fontWeight'], StrCast(Doc.UserDoc().fontWeight));
- case StyleProp.FillColor: return StrCast(doc?._fillColor, StrCast(doc?.fillColor, StrCast(doc?.backgroundColor, 'transparent')));
- case StyleProp.ShowCaption:return props?.hideCaptions || doc?._type_collection === CollectionViewType.Carousel ? undefined: StrCast(doc?._layout_showCaption);
- case StyleProp.TitleHeight:return Math.min(4,(props?.DocumentView?.().screenToViewTransform().Scale ?? 1)) * NumCast(Doc.UserDoc().headerHeight,30);
+ case StyleProp.DocContents: return undefined;
+ case StyleProp.WidgetColor: return isAnnotated ? Colors.LIGHT_BLUE : 'dimgrey';
+ case StyleProp.Opacity: return LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : doc?.text_inlineAnnotations ? 0 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null));
+ case StyleProp.FontColor: return StrCast(doc?.[fieldKey + 'fontColor'], StrCast(Doc.UserDoc().fontColor, color()));
+ case StyleProp.FontSize: return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(Doc.UserDoc().fontSize));
+ case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(Doc.UserDoc().fontFamily));
+ case StyleProp.FontWeight: return StrCast(doc?.[fieldKey + 'fontWeight'], StrCast(Doc.UserDoc().fontWeight));
+ case StyleProp.FillColor: return StrCast(doc?._fillColor, StrCast(doc?.fillColor, StrCast(doc?.backgroundColor, 'transparent')));
+ case StyleProp.ShowCaption: return hideCaptions || doc?._type_collection === CollectionViewType.Carousel ? undefined: StrCast(doc?._layout_showCaption);
+ case StyleProp.TitleHeight: return Math.min(4,(DocumentView?.().screenToViewTransform().Scale ?? 1)) * NumCast(Doc.UserDoc().headerHeight,30);
case StyleProp.ShowTitle:
return (
(doc &&
- !(props?.DocumentView?.().ComponentView instanceof CollectionSchemaView) &&
- !props?.LayoutTemplateString &&
+ !(DocumentView?.().ComponentView instanceof CollectionSchemaView) &&
+ !LayoutTemplateString &&
!doc.presentation_targetDoc &&
- !props?.LayoutTemplateString?.includes(KeyValueBox.name) &&
- props?.layout_showTitle?.() !== '' &&
+ !LayoutTemplateString?.includes(KeyValueBox.name) &&
+ layout_showTitle?.() !== '' &&
StrCast(
doc._layout_showTitle,
- props?.layout_showTitle?.() ||
+ layout_showTitle?.() ||
(!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.FUNCPLOT, DocumentType.LABEL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as any)
? doc.author === ClientUtils.CurrentUserEmail()
? StrCast(Doc.UserDoc().layout_showTitle)
@@ -195,15 +219,15 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
}
case StyleProp.BorderPath: {
const borderPath = Doc.IsComicStyle(doc) &&
- props?.renderDepth &&
- !doc?.layout_isSvg && { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, 0.08), width: 3 };
+ renderDepth &&
+ !doc?.layout_isSvg && { path: wavyBorderPath(PanelWidth?.() || 0, PanelHeight?.() || 0), fill: wavyBorderPath(PanelWidth?.() || 0, PanelHeight?.() || 0, 0.08), width: 3 };
return !borderPath
? null
: {
clipPath: `path('${borderPath.path}')`,
jsx: (
<div key="border2" className="documentView-customBorder" style={{ pointerEvents: 'none' }}>
- <svg style={{ overflow: 'visible', height: '100%' }} viewBox={`0 0 ${props.PanelWidth()} ${props.PanelHeight()}`}>
+ <svg style={{ overflow: 'visible', height: '100%' }} viewBox={`0 0 ${PanelWidth?.()} ${PanelHeight?.()}`}>
<path d={borderPath.path} style={{ stroke: 'black', fill: 'transparent', strokeWidth: borderPath.width }} />
</svg>
</div>
@@ -251,13 +275,13 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
? undefined
: doc?._type_collection === CollectionViewType.Stacking ?
(Colors.DARK_GRAY)
- : Cast((props?.renderDepth || 0) > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground, 'string') ?? (Colors.MEDIUM_GRAY));
+ : Cast((renderDepth || 0) > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground, 'string') ?? (Colors.MEDIUM_GRAY));
break;
// if (doc._type_collection !== CollectionViewType.Freeform && doc._type_collection !== CollectionViewType.Time) return "rgb(62,62,62)";
default: docColor = docColor || (Colors.WHITE);
}
- if (isNonTransparent && isNonTransparentLevel < 9 && (!docColor || docColor === 'transparent') && doc?.embedContainer && props?.styleProvider) {
- return props.styleProvider(DocCast(doc.embedContainer), props, StyleProp.BackgroundColor+":nonTransparent"+(isNonTransparentLevel+1));
+ if (isNonTransparent && isNonTransparentLevel < 9 && (!docColor || docColor === 'transparent') && doc?.embedContainer && styleProvider) {
+ return styleProvider(DocCast(doc.embedContainer), props, StyleProp.BackgroundColor+":nonTransparent"+(isNonTransparentLevel+1));
}
return (docColor && !doc) ? DashColor(docColor).fade(0.5).toString() : docColor;
}
@@ -271,7 +295,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
doc?.layout_boxShadow,
doc?._type_collection === CollectionViewType.Pile
? '4px 4px 10px 2px'
- : lockedPosition() || doc?.isGroup || props?.LayoutTemplateString
+ : lockedPosition() || doc?.isGroup || LayoutTemplateString
? undefined // groups have no drop shadow -- they're supposed to be "invisible". LayoutString's imply collection is being rendered as something else (e.g., title of a Slide)
: `${Colors.DARK_GRAY} ${StrCast(doc.layout_boxShadow, '0.2vw 0.2vw 0.8vw')}`
);
@@ -282,10 +306,10 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
default:
return doc.z
? `#9c9396 ${StrCast(doc?.layout_boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow
- : props?.containerViewPath?.().lastElement()?.Document._freeform_useClusters
- ? `${backgroundCol()} ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (props?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
+ : containerViewPath?.().lastElement()?.Document._freeform_useClusters
+ ? `${backgroundCol()} ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
: NumCast(doc.group, -1) !== -1
- ? `gray ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (props?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
+ ? `gray ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
: lockedPosition()
? undefined // if it's a background & has a cluster color, make the shadow spread really big
: fieldKey.includes('_inline') // if doc is an inline document in a text box
@@ -296,14 +320,14 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
}
}
case StyleProp.PointerEvents:
- if (StrCast(doc?.pointerEvents) && !props?.LayoutTemplateString?.includes(KeyValueBox.name)) return StrCast(doc!.pointerEvents); // honor pointerEvents field (set by lock button usually) if it's not a keyValue view of the Doc
- if (props?.LayoutTemplateString?.includes(KeyValueBox.name)) return 'all';
+ if (StrCast(doc?.pointerEvents) && !LayoutTemplateString?.includes(KeyValueBox.name)) return StrCast(doc!.pointerEvents); // honor pointerEvents field (set by lock button usually) if it's not a keyValue view of the Doc
+ if (LayoutTemplateString?.includes(KeyValueBox.name)) return 'all';
if (SnappingManager.ExploreMode || doc?.layout_unrendered) return isInk() ? 'visiblePainted' : 'all';
- if (props?.pointerEvents?.() === 'none') return 'none';
+ if (pointerEvents?.() === 'none') return 'none';
if (opacity() === 0) return 'none';
- if (props?.isGroupActive?.() ) return isInk() ? 'visiblePainted': (doc?.
+ if (isGroupActive?.() ) return isInk() ? 'visiblePainted': (doc?.
isGroup )? undefined: 'all'
- if (props?.isDocumentActive?.()) return isInk() ? 'visiblePainted' : 'all';
+ if (isDocumentActive?.()) return isInk() ? 'visiblePainted' : 'all';
return undefined; // fixes problem with tree view elements getting pointer events when the tree view is not active
case StyleProp.Decorations: {
const lock = () => doc?.pointerEvents !== 'none' ? null : (
@@ -312,7 +336,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
</div>
);
const paint = () => !doc?.onPaint ? null : (
- <div className={`styleProvider-paint${props?.DocumentView?.().IsSelected ? "-selected":""}`} onClick={e => togglePaintView(e, doc, props)}>
+ <div className={`styleProvider-paint${DocumentView?.().IsSelected ? "-selected":""}`} onClick={e => togglePaintView(e, doc, props)}>
<FontAwesomeIcon icon='pen' size="lg" />
</div>
);
@@ -321,7 +345,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
const showFilterIcon =
StrListCast(doc?._childFilters).length || StrListCast(doc?._childFiltersByRanges).length
? 'green' // #18c718bd' //'hasFilter'
- : props?.childFilters?.().filter(f => ClientUtils.IsRecursiveFilter(f) && f !== ClientUtils.noDragDocsFilter).length || props?.childFiltersByRanges().length
+ : childFilters?.().filter(f => ClientUtils.IsRecursiveFilter(f) && f !== ClientUtils.noDragDocsFilter).length || childFiltersByRanges?.().length
? 'orange' // 'inheritsFilter'
: undefined;
return !showFilterIcon ? null : (
@@ -353,7 +377,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
"this view inherits filters from one of its parents"}
color={SettingsManager.userColor}
background={showFilterIcon}
- items={[ ...(dashView ? [dashView]: []), ...(props?.docViewPath?.()??[])]
+ items={[ ...(dashView ? [dashView]: []), ...(docViewPath?.()??[])]
.filter(dv => StrListCast(dv?.Document.childFilters).length || StrListCast(dv?.Document.childRangeFilters).length)
.map(dv => ({
text: StrCast(dv?.Document.title),
@@ -365,9 +389,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
);
};
const audio = () => {
- const audioAnnoState = (doc: Doc) => StrCast(doc.audioAnnoState, AudioAnnoState.stopped);
- const audioAnnosCount = (doc: Doc) => StrListCast(doc[fieldKey + 'audioAnnotations']).length;
- if (!doc || props?.renderDepth === -1 || !audioAnnosCount(doc)) return null;
+ const audioAnnoState = (audioDoc: Doc) => StrCast(audioDoc.audioAnnoState, AudioAnnoState.stopped);
+ const audioAnnosCount = (audioDoc: Doc) => StrListCast(audioDoc[fieldKey + 'audioAnnotations']).length;
+ if (!doc || renderDepth === -1 || !audioAnnosCount(doc)) return null;
const audioIconColors: { [key: string]: string } = { playing: 'green', stopped: 'blue' };
return (
<Tooltip title={<div>{StrListCast(doc[fieldKey + 'audioAnnotations_text']).lastElement()}</div>}>
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 5a509128d..6dba9e155 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -1,3 +1,9 @@
+/* eslint-disable jsx-a11y/label-has-associated-control */
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+/* eslint-disable jsx-a11y/control-has-associated-label */
+/* eslint-disable react/no-unused-class-component-methods */
+/* eslint-disable react/sort-comp */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
import { Toggle, ToggleType, Type } from 'browndash-components';
@@ -312,8 +318,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
}
private get _buttonizableCommands() {
switch (this.props.type) {
- default:
- return this._doc_commands;
case CollectionViewType.Freeform:
return this._freeform_commands;
case CollectionViewType.Tree:
@@ -332,6 +336,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
return this._freeform_commands;
case CollectionViewType.Carousel3D:
return this._freeform_commands;
+ default:
+ return this._doc_commands;
}
}
private _commandRef = React.createRef<HTMLInputElement>();
@@ -345,11 +351,13 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
@undoBatch
viewChanged = (e: React.ChangeEvent) => {
const target = this.document !== Doc.MyLeftSidebarPanel ? this.document : DocCast(this.document.proto);
- target._type_collection = e.target.selectedOptions[0].value;
+ target._type_collection = (e.target as any).selectedOptions[0].value;
};
commandChanged = (e: React.ChangeEvent) => {
- runInAction(() => (this._currentKey = e.target.selectedOptions[0].value));
+ runInAction(() => {
+ this._currentKey = (e.target as any).selectedOptions[0].value;
+ });
};
@action closeViewSpecs = () => {
@@ -367,7 +375,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
@undoBatch
@action
protected drop(e: Event, de: DragManager.DropEvent): boolean {
- const docDragData = de.complete.docDragData;
+ const { docDragData } = de.complete;
if (docDragData?.draggedDocuments.length) {
this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => c.immediate(docDragData.draggedDocuments || []));
e.stopPropagation();
@@ -420,11 +428,11 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
<div className="collectionViewBaseChrome-template" ref={this.createDropTarget}>
<Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
<div className="commandEntry-outerDiv" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
- <button className={'antimodeMenu-button'}>
+ <button type="button" className="antimodeMenu-button">
<FontAwesomeIcon icon="bullseye" size="lg" />
</button>
<select className="collectionViewBaseChrome-cmdPicker" onPointerDown={stopPropagation} onChange={this.commandChanged} value={this._currentKey}>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={'empty'} value={''} />
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key="empty" value="" />
{this._buttonizableCommands?.map(cmd => (
<option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={cmd.title} value={cmd.title}>
{cmd.title}
@@ -471,23 +479,19 @@ export class CollectionNoteTakingViewChrome extends React.Component<CollectionVi
if (docs instanceof Doc) {
const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('author_date') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_'));
return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
- } else {
- const keys = new Set<string>();
- docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
- const noviceKeys = Array.from(keys).filter(
- key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('author_date') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_')
- );
- return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
}
+ const keys = new Set<string>();
+ docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
+ const noviceKeys = Array.from(keys).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('author_date') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_'));
+ return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
}
if (docs instanceof Doc) {
return Object.keys(docs).filter(key => key.toLowerCase().indexOf(val) > -1);
- } else {
- const keys = new Set<string>();
- docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
- return Array.from(keys).filter(key => key.toLowerCase().indexOf(val) > -1);
}
+ const keys = new Set<string>();
+ docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
+ return Array.from(keys).filter(key => key.toLowerCase().indexOf(val) > -1);
};
@action
@@ -497,9 +501,7 @@ export class CollectionNoteTakingViewChrome extends React.Component<CollectionVi
getSuggestionValue = (suggestion: string) => suggestion;
- renderSuggestion = (suggestion: string) => {
- return <p>{suggestion}</p>;
- };
+ renderSuggestion = (suggestion: string) => <p>{suggestion}</p>;
onSuggestionFetch = async ({ value }: { value: string }) => {
const sugg = await this.getKeySuggestions(value);
@@ -587,12 +589,16 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
}
componentDidMount() {
- runInAction(() => (this.resize = this.props.docView.props.PanelWidth() < 700));
+ runInAction(() => {
+ this.resize = this.props.docView.props.PanelWidth() < 700;
+ });
// listener to reduce text on chrome resize (panel resize)
this.resizeListenerDisposer = reaction(
() => this.panelWidth,
- newValue => (this.resize = newValue < 700)
+ newValue => {
+ this.resize = newValue < 700;
+ }
);
}
@@ -608,7 +614,10 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
* Sets the value of `numCols` on the grid's Document to the value entered.
*/
onNumColsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- if (e.currentTarget.valueAsNumber > 0) undoBatch(() => (this.document.gridNumCols = e.currentTarget.valueAsNumber))();
+ if (e.currentTarget.valueAsNumber > 0)
+ undoBatch(() => {
+ this.document.gridNumCols = e.currentTarget.valueAsNumber;
+ })();
};
/**
@@ -637,7 +646,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
onIncrementButtonClick = () => {
this.clicked = true;
this.entered && (this.document.gridNumCols as number)--;
- undoBatch(() => (this.document.gridNumCols = this.numCols + 1))();
+ undoBatch(() => {
+ this.document.gridNumCols = this.numCols + 1;
+ })();
this.entered = false;
};
@@ -648,7 +659,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
this.clicked = true;
if (this.numCols > 1 && !this.decrementLimitReached) {
this.entered && (this.document.gridNumCols as number)++;
- undoBatch(() => (this.document.gridNumCols = this.numCols - 1))();
+ undoBatch(() => {
+ this.document.gridNumCols = this.numCols - 1;
+ })();
if (this.numCols === 1) this.decrementLimitReached = true;
}
this.entered = false;
@@ -741,7 +754,13 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
<label className="flexLabel">{this.resize ? 'Flex' : 'Flexible'}</label>
</span>
- <button onClick={() => (this.document.gridResetLayout = true)}>{!this.resize ? 'Reset' : <FontAwesomeIcon icon="redo-alt" size="1x" />}</button>
+ <button
+ type="button"
+ onClick={() => {
+ this.document.gridResetLayout = true;
+ }}>
+ {!this.resize ? 'Reset' : <FontAwesomeIcon icon="redo-alt" size="1x" />}
+ </button>
</div>
);
}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 46bf56dc8..ee79812a1 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable no-restricted-syntax */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Popup, PopupTrigger, Type } from 'browndash-components';
import { ObservableMap, action, computed, makeObservable, observable, observe } from 'mobx';
@@ -31,6 +32,7 @@ import { CollectionSubView } from '../CollectionSubView';
import './CollectionSchemaView.scss';
import { SchemaColumnHeader } from './SchemaColumnHeader';
import { SchemaRowBox } from './SchemaRowBox';
+
const { SCHEMA_NEW_NODE_HEIGHT } = require('../../global/globalCssVariables.module.scss'); // prettier-ignore
export const FInfotoColType: { [key: string]: ColumnType } = {
@@ -90,12 +92,11 @@ export class CollectionSchemaView extends CollectionSubView() {
@computed get _selectedDocs() {
const selected = SelectionManager.Docs.filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.Document));
if (!selected.length) {
- for (const sel of SelectionManager.Docs) {
- const contextPath = DocumentManager.GetContextPath(sel, true);
- if (contextPath.includes(this.Document)) {
- const parentInd = contextPath.indexOf(this.Document);
- return parentInd < contextPath.length - 1 ? [contextPath[parentInd + 1]] : [];
- }
+ // if no schema doc is directly selected, test if a child of a schema doc is selected (such as in the preview window)
+ const childOfSchemaDoc = SelectionManager.Docs.find(sel => DocumentManager.GetContextPath(sel, true).includes(this.Document));
+ if (childOfSchemaDoc) {
+ const contextPath = DocumentManager.GetContextPath(childOfSchemaDoc, true);
+ return [contextPath[contextPath.indexOf(childOfSchemaDoc) - 1]]; // the schema doc that is "selected" by virtue of one of its children being selected
}
}
return selected;
@@ -158,7 +159,9 @@ export class CollectionSchemaView extends CollectionSubView() {
Object.keys(proto).forEach(action(key => // check if any of its keys are new, and add them
!this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo("-no description-", key === 'author'))))));
break;
- case 'update': //let oldValue = change.oldValue; // fill this in if the entire child list will ever be reassigned with a new list
+ case 'update': // let oldValue = change.oldValue; // fill this in if the entire child list will ever be reassigned with a new list
+ break;
+ default:
}
},
true
@@ -187,7 +190,7 @@ export class CollectionSchemaView extends CollectionSubView() {
if (this._selectedDocs.includes(newDoc)) {
SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc));
} else {
- this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1);
+ this.addDocToSelection(newDoc, e.shiftKey);
this._selectedCell && (this._selectedCell[0] = newDoc);
this.scrollToDoc(newDoc, {});
}
@@ -206,7 +209,7 @@ export class CollectionSchemaView extends CollectionSubView() {
const newDoc = this.sortedDocs.docs[firstIndex - 1];
if (this._selectedDocs.includes(newDoc)) SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc));
else {
- this.addDocToSelection(newDoc, e.shiftKey, firstIndex - 1);
+ this.addDocToSelection(newDoc, e.shiftKey);
this._selectedCell && (this._selectedCell[0] = newDoc);
this.scrollToDoc(newDoc, {});
}
@@ -235,7 +238,9 @@ export class CollectionSchemaView extends CollectionSubView() {
}
case 'Escape': {
this.deselectCell();
+ break;
}
+ default:
}
}
};
@@ -254,7 +259,7 @@ export class CollectionSchemaView extends CollectionSubView() {
this.addNewKey(newKey, defaultVal);
}
- let currKeys = [...this.columnKeys];
+ const currKeys = [...this.columnKeys];
currKeys[index] = newKey;
this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
};
@@ -271,13 +276,16 @@ export class CollectionSchemaView extends CollectionSubView() {
const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0);
this.layoutDoc.schema_columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)));
- let currKeys = this.columnKeys.slice();
+ const currKeys = this.columnKeys.slice();
currKeys.splice(0, 0, key);
this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
};
@action
- addNewKey = (key: string, defaultVal: any) => this.childDocs.forEach(doc => (doc[DocData][key] = defaultVal));
+ addNewKey = (key: string, defaultVal: any) =>
+ this.childDocs.forEach(doc => {
+ doc[DocData][key] = defaultVal;
+ });
@undoBatch
removeColumn = (index: number) => {
@@ -287,7 +295,7 @@ export class CollectionSchemaView extends CollectionSubView() {
const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0);
this.layoutDoc.schema_columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)));
- let currKeys = this.columnKeys.slice();
+ const currKeys = this.columnKeys.slice();
currKeys.splice(index, 1);
this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
};
@@ -295,7 +303,7 @@ export class CollectionSchemaView extends CollectionSubView() {
@action
startResize = (e: any, index: number) => {
this._displayColumnWidths = this.storedColumnWidths;
- setupMoveUpEvents(this, e, (e, delta) => this.resizeColumn(e, index), this.finishResize, emptyFunction);
+ setupMoveUpEvents(this, e, moveEv => this.resizeColumn(moveEv, index), this.finishResize, emptyFunction);
};
@action
@@ -334,11 +342,11 @@ export class CollectionSchemaView extends CollectionSubView() {
@undoBatch
moveColumn = (fromIndex: number, toIndex: number) => {
- let currKeys = this.columnKeys.slice();
+ const currKeys = this.columnKeys.slice();
currKeys.splice(toIndex, 0, currKeys.splice(fromIndex, 1)[0]);
this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
- let currWidths = this.storedColumnWidths.slice();
+ const currWidths = this.storedColumnWidths.slice();
currWidths.splice(toIndex, 0, currWidths.splice(fromIndex, 1)[0]);
this.layoutDoc.schema_columnWidths = new List<number>(currWidths);
};
@@ -352,7 +360,7 @@ export class CollectionSchemaView extends CollectionSubView() {
document.removeEventListener('pointermove', this.highlightDropColumn);
document.addEventListener('pointermove', this.highlightDropColumn);
- let stopHighlight = (e: PointerEvent) => {
+ const stopHighlight = () => {
document.removeEventListener('pointermove', this.highlightDropColumn);
document.removeEventListener('pointerup', stopHighlight);
};
@@ -405,7 +413,7 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@action
- addDocToSelection = (doc: Doc, extendSelection: boolean, index: number) => {
+ addDocToSelection = (doc: Doc, extendSelection: boolean) => {
const rowDocView = DocumentManager.Instance.getDocumentView(doc);
if (rowDocView) SelectionManager.SelectView(rowDocView, extendSelection);
};
@@ -420,19 +428,25 @@ export class CollectionSchemaView extends CollectionSubView() {
const endRow = Math.max(lastSelectedRow, index);
for (let i = startRow; i <= endRow; i++) {
const currDoc = this.sortedDocs.docs[i];
- if (!this._selectedDocs.includes(currDoc)) this.addDocToSelection(currDoc, true, i);
+ if (!this._selectedDocs.includes(currDoc)) this.addDocToSelection(currDoc, true);
}
};
@action
- selectCell = (doc: Doc, index: number) => (this._selectedCell = [doc, index]);
+ selectCell = (doc: Doc, index: number) => {
+ this._selectedCell = [doc, index];
+ };
@action
- deselectCell = () => (this._selectedCell = undefined);
+ deselectCell = () => {
+ this._selectedCell = undefined;
+ };
sortedSelectedDocs = () => this.sortedDocs.docs.filter(doc => this._selectedDocs.includes(doc));
- setDropIndex = (index: number) => (this._closestDropIndex = index);
+ setDropIndex = (index: number) => {
+ this._closestDropIndex = index;
+ };
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
if (de.complete.columnDragData) {
@@ -476,7 +490,7 @@ export class CollectionSchemaView extends CollectionSubView() {
onDividerDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, emptyFunction);
@action
- onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
+ onDividerMove = (e: PointerEvent) => {
const nativeWidth = this._previewRef!.getBoundingClientRect();
const minWidth = 40;
const maxWidth = 1000;
@@ -506,37 +520,76 @@ export class CollectionSchemaView extends CollectionSubView() {
const rect = found.getBoundingClientRect();
const localRect = this.ScreenToLocalBoxXf().transformBounds(rect.left, rect.top, rect.width, rect.height);
if (localRect.y < this.rowHeightFunc() || localRect.y + localRect.height > this._props.PanelHeight()) {
- let focusSpeed = options.zoomTime ?? 50;
+ const focusSpeed = options.zoomTime ?? 50;
smoothScroll(focusSpeed, this._tableContentRef!, localRect.y + this._tableContentRef!.scrollTop - this.rowHeightFunc(), options.easeFunc);
return focusSpeed;
}
}
+ return undefined;
};
@computed get fieldDefaultInput() {
switch (this._newFieldType) {
case ColumnType.Number:
- return <input type="number" name="" id="" value={this._newFieldDefault ?? 0} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />;
+ return (
+ <input
+ type="number"
+ name=""
+ id=""
+ value={this._newFieldDefault ?? 0}
+ onPointerDown={e => e.stopPropagation()}
+ onChange={action(e => {
+ this._newFieldDefault = e.target.value;
+ })}
+ />
+ );
case ColumnType.Boolean:
return (
<>
- <input type="checkbox" name="" id="" value={this._newFieldDefault} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.checked))} />
+ <input
+ type="checkbox"
+ name=""
+ id=""
+ value={this._newFieldDefault}
+ onPointerDown={e => e.stopPropagation()}
+ onChange={action(e => {
+ this._newFieldDefault = e.target.checked;
+ })}
+ />
{this._newFieldDefault ? 'true' : 'false'}
</>
);
case ColumnType.String:
- return <input type="text" name="" id="" value={this._newFieldDefault ?? ''} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />;
+ return (
+ <input
+ type="text"
+ name=""
+ id=""
+ value={this._newFieldDefault ?? ''}
+ onPointerDown={e => e.stopPropagation()}
+ onChange={action(e => {
+ this._newFieldDefault = e.target.value;
+ })}
+ />
+ );
+ default:
+ return undefined;
}
}
onSearchKeyDown = (e: React.KeyboardEvent) => {
switch (e.key) {
case 'Enter':
- this._menuKeys.length > 0 && this._menuValue.length > 0 ? this.setKey(this._menuKeys[0]) : action(() => (this._makeNewField = true))();
+ this._menuKeys.length > 0 && this._menuValue.length > 0
+ ? this.setKey(this._menuKeys[0])
+ : action(() => {
+ this._makeNewField = true;
+ })();
break;
case 'Escape':
this.closeColumnMenu();
break;
+ default:
}
};
@@ -568,7 +621,9 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@action
- closeColumnMenu = () => (this._columnMenuIndex = undefined);
+ closeColumnMenu = () => {
+ this._columnMenuIndex = undefined;
+ };
@action
openFilterMenu = (index: number) => {
@@ -577,7 +632,9 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@action
- closeFilterMenu = () => (this._filterColumnIndex = undefined);
+ closeFilterMenu = () => {
+ this._filterColumnIndex = undefined;
+ };
openContextMenu = (x: number, y: number, index: number) => {
this.closeColumnMenu();
@@ -607,7 +664,7 @@ export class CollectionSchemaView extends CollectionSubView() {
this._menuKeys = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase()));
};
- getFieldFilters = (field: string) => StrListCast(this.Document._childFilters).filter(filter => filter.split(Doc.FilterSep)[0] == field);
+ getFieldFilters = (field: string) => StrListCast(this.Document._childFilters).filter(filter => filter.split(Doc.FilterSep)[0] === field);
removeFieldFilters = (field: string) => {
this.getFieldFilters(field).forEach(filter => Doc.setDocFilter(this.Document, field, filter.split(Doc.FilterSep)[1], 'remove'));
@@ -619,11 +676,14 @@ export class CollectionSchemaView extends CollectionSubView() {
case 'Escape':
this.closeFilterMenu();
break;
+ default:
}
};
@action
- updateFilterSearch = (e: React.ChangeEvent<HTMLInputElement>) => (this._filterSearchValue = e.target.value);
+ updateFilterSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this._filterSearchValue = e.target.value;
+ };
@computed get newFieldMenu() {
return (
@@ -632,7 +692,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<input
type="radio"
name="newFieldType"
- checked={this._newFieldType == ColumnType.Number}
+ checked={this._newFieldType === ColumnType.Number}
onChange={action(() => {
this._newFieldType = ColumnType.Number;
this._newFieldDefault = 0;
@@ -644,7 +704,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<input
type="radio"
name="newFieldType"
- checked={this._newFieldType == ColumnType.Boolean}
+ checked={this._newFieldType === ColumnType.Boolean}
onChange={action(() => {
this._newFieldType = ColumnType.Boolean;
this._newFieldDefault = false;
@@ -656,7 +716,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<input
type="radio"
name="newFieldType"
- checked={this._newFieldType == ColumnType.String}
+ checked={this._newFieldType === ColumnType.String}
onChange={action(() => {
this._newFieldType = ColumnType.String;
this._newFieldDefault = '';
@@ -668,7 +728,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<div className="schema-key-warning">{this._newFieldWarning}</div>
<div
className="schema-column-menu-button"
- onPointerDown={action(e => {
+ onPointerDown={action(() => {
if (this.documentKeys.includes(this._menuValue)) {
this._newFieldWarning = 'Field already exists';
} else if (this._menuValue.length === 0) {
@@ -733,7 +793,7 @@ export class CollectionSchemaView extends CollectionSubView() {
}
@computed get renderColumnMenu() {
- const x = this._columnMenuIndex! == -1 ? 0 : this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._columnMenuIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth);
+ const x = this._columnMenuIndex! === -1 ? 0 : this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._columnMenuIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth);
return (
<div className="schema-column-menu" style={{ left: x, minWidth: CollectionSchemaView._minColWidth }}>
<input className="schema-key-search-input" type="text" onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} />
@@ -817,7 +877,7 @@ export class CollectionSchemaView extends CollectionSubView() {
: [...this.childDocs].sort((docA, docB) => {
const aStr = Field.toString(docA[field] as FieldType);
const bStr = Field.toString(docB[field] as FieldType);
- var out = 0;
+ let out = 0;
if (aStr < bStr) out = -1;
if (aStr > bStr) out = 1;
if (desc) out *= -1;
@@ -835,7 +895,7 @@ export class CollectionSchemaView extends CollectionSubView() {
render() {
return (
<div className="collectionSchemaView" ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)} onDrop={this.onExternalDrop.bind(this)}>
- <div ref={this._menuTarget} style={{ background: 'red', top: 0, left: 0, position: 'absolute', zIndex: 10000 }}></div>
+ <div ref={this._menuTarget} style={{ background: 'red', top: 0, left: 0, position: 'absolute', zIndex: 10000 }} />
<div
className="schema-table"
style={{ width: `calc(100% - ${this.previewWidth}px)` }}
@@ -851,7 +911,7 @@ export class CollectionSchemaView extends CollectionSubView() {
placement="right"
background={SettingsManager.userBackgroundColor}
color={SettingsManager.userColor}
- toggle={<FontAwesomeIcon onPointerDown={e => this.openColumnMenu(-1, true)} icon="plus" />}
+ toggle={<FontAwesomeIcon onPointerDown={() => this.openColumnMenu(-1, true)} icon="plus" />}
trigger={PopupTrigger.CLICK}
type={Type.TERT}
isOpen={this._columnMenuIndex !== -1 ? false : undefined}
@@ -860,6 +920,7 @@ export class CollectionSchemaView extends CollectionSubView() {
</div>
{this.columnKeys.map((key, index) => (
<SchemaColumnHeader
+ // eslint-disable-next-line react/no-array-index-key
key={index}
columnIndex={index}
columnKeys={this.columnKeys}
@@ -879,28 +940,42 @@ export class CollectionSchemaView extends CollectionSubView() {
</div>
{this._columnMenuIndex !== undefined && this._columnMenuIndex !== -1 && this.renderColumnMenu}
{this._filterColumnIndex !== undefined && this.renderFilterMenu}
- <CollectionSchemaViewDocs schema={this} childDocs={this.sortedDocsFunc} rowHeight={this.rowHeightFunc} setRef={(ref: HTMLDivElement | null) => (this._tableContentRef = ref)} />
+ {
+ // eslint-disable-next-line no-use-before-define
+ <CollectionSchemaViewDocs
+ schema={this}
+ childDocs={this.sortedDocsFunc}
+ rowHeight={this.rowHeightFunc}
+ setRef={(ref: HTMLDivElement | null) => {
+ this._tableContentRef = ref;
+ }}
+ />
+ }
{this.layoutDoc.chromeHidden ? null : (
<div className="schema-add">
<EditableView
GetValue={returnEmptyString}
SetValue={undoable(value => (value ? this.addRow(Docs.Create.TextDocument(value, { title: value, _layout_autoHeight: true })) : false), 'add text doc')}
placeholder={"Type text to create note or ':' to create specific type"}
- contents={'+ New Node'}
+ contents="+ New Node"
menuCallback={this.menuCallback}
height={CollectionSchemaView._newNodeInputHeight}
/>
</div>
)}
</div>
- {this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown}></div>}
+ {this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown} />}
{this.previewWidth > 0 && (
- <div style={{ width: `${this.previewWidth}px` }} ref={ref => (this._previewRef = ref)}>
+ <div
+ style={{ width: `${this.previewWidth}px` }}
+ ref={ref => {
+ this._previewRef = ref;
+ }}>
{Array.from(this._selectedDocs).lastElement() && (
<DocumentView
Document={Array.from(this._selectedDocs).lastElement()}
fitContentsToBox={returnTrue}
- dontCenter={'y'}
+ dontCenter="y"
onClickScriptDisable="always"
focus={emptyFunction}
defaultDoubleClick={returnIgnore}
@@ -945,7 +1020,10 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
<div className="schema-table-content" ref={this.props.setRef} style={{ height: `calc(100% - ${CollectionSchemaView._newNodeInputHeight + this.props.rowHeight()}px)` }}>
{this.props.childDocs().docs.map((doc: Doc, index: number) => (
<div key={doc[Id]} className="schema-row-wrapper" style={{ height: this.props.rowHeight() }}>
- <CollectionSchemaViewDoc doc={doc} schema={this.props.schema} index={index} rowHeight={this.props.rowHeight} />
+ {
+ // eslint-disable-next-line no-use-before-define
+ <CollectionSchemaViewDoc doc={doc} schema={this.props.schema} index={index} rowHeight={this.props.rowHeight} />
+ }
</div>
))}
</div>
@@ -977,6 +1055,7 @@ class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaV
return (
<DocumentView
key={this._props.doc[Id]}
+ // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props.schema._props}
containerViewPath={this._props.schema.childContainerViewPath}
LayoutTemplate={this._props.schema._props.childLayoutTemplate}
@@ -996,14 +1075,14 @@ class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaV
searchFilterDocs={this._props.schema.searchFilterDocs}
rootSelected={this._props.schema.rootSelected}
ScreenToLocalTransform={this.screenToLocalXf}
- dragWhenActive={true}
+ dragWhenActive
isDocumentActive={this._props.schema._props.childDocumentsActive?.() ? this._props.schema._props.isDocumentActive : this._props.schema.isContentActive}
isContentActive={emptyFunction}
whenChildContentsActiveChanged={this._props.schema._props.whenChildContentsActiveChanged}
- hideDecorations={true}
- hideTitle={true}
- hideDocumentButtonBar={true}
- hideLinkAnchors={true}
+ hideDecorations
+ hideTitle
+ hideDocumentButtonBar
+ hideLinkAnchors
layout_fitWidth={returnTrue}
/>
);
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 832e18b68..cc4b5b67f 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -21,7 +21,6 @@ import { CollectionSchemaView } from '../collections/collectionSchema/Collection
import { SchemaRowBox } from '../collections/collectionSchema/SchemaRowBox';
import { PresElementBox } from './trails/PresElementBox';
import { SearchBox } from '../search/SearchBox';
-import { DashWebRTCVideo } from '../webcam/DashWebRTCVideo';
import { AudioBox } from './AudioBox';
import { ComparisonBox } from './ComparisonBox';
import { DataVizBox } from './DataVizBox/DataVizBox';
@@ -248,7 +247,6 @@ export class DocumentContentsView extends ObservableReactComponent<DocumentConte
PresElementBox,
SearchBox,
FunctionPlotBox,
- DashWebRTCVideo,
LinkAnchorBox,
InkingStroke,
LinkBox,
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 3a1a72910..99b4a84fc 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -349,7 +349,7 @@ footnote::before {
touch-action: none;
span {
font-family: inherit;
- background-color: inherit;
+ // background-color: inherit; // intended to allow texts to inherit background from list container, but this prevents css highlights e.,g highlight text from others
display: inline; // needs to be inline for search highlighting to appear
// display: contents; // BUT needs to be 'contents' to avoid Chrome bug where extra space is added above and <ol> lists when inside a prosemirror span
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 99a2f4ab9..ba37c3265 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -65,6 +65,7 @@ import { FootnoteView } from './FootnoteView';
import './FormattedTextBox.scss';
import { findLinkMark, FormattedTextBoxComment } from './FormattedTextBoxComment';
import { buildKeymap, updateBullets } from './ProsemirrorExampleTransfer';
+// eslint-disable-next-line import/extensions
import { removeMarkWithAttrs } from './prosemirrorPatches';
import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
import { RichTextRules } from './RichTextRules';
@@ -291,7 +292,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
});
};
AnchorMenu.Instance.Highlight = undoable((color: string) => {
- this._editorView?.state && RichTextMenu.Instance?.setHighlight(color);
+ this._editorView?.state && RichTextMenu.Instance?.setFontField(color, 'fontHighlight');
return undefined;
}, 'highlght text');
AnchorMenu.Instance.onMakeAnchor = () => this.getAnchor(true);
@@ -637,8 +638,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const pos = view.posAtCoords({ left: de.x, top: de.y })?.pos;
pos && view.dispatch(view.state.tr.insert(pos, node));
added = !!pos; // pos will be null if you don't drop onto an actual text location
- } catch (e) {
- console.log('Drop failed', e);
+ } catch (err) {
+ console.log('Drop failed', err);
added = false;
} finally {
this._inDrop = false;
@@ -778,7 +779,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
this,
e,
this.sidebarMove,
- (e, movement, isClick) => !isClick && batch.end(),
+ (moveEv, movement, isClick) => !isClick && batch.end(),
() => {
this.toggleSidebar();
batch.end();
@@ -1301,7 +1302,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
this._disposers.selected = reaction(
() => this._props.rootSelected?.(),
action(selected => {
- // selected && setTimeout(() => this.prepareForTyping());
+ this.prepareForTyping();
if (FormattedTextBox._globalHighlights.has('Bold Text')) {
// eslint-disable-next-line operator-assignment
this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed
@@ -1498,8 +1499,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
- } else if (curText && !FormattedTextBox.DontSelectInitialText) {
- selectAll(this._editorView.state, this._editorView?.dispatch);
+ } else if (!FormattedTextBox.DontSelectInitialText) {
+ const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
+ selectAll(this._editorView.state, (tx: Transaction) => {
+ this._editorView?.dispatch(tx.deleteSelection().addStoredMark(mark));
+ });
this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
} else {
const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
@@ -1526,17 +1530,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
// add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
prepareForTyping = () => {
- if (!this._editorView) return;
- const docDefaultMarks = [
- ...(Doc.UserDoc().fontColor !== 'transparent' && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []),
- ...(Doc.UserDoc().fontStyle === 'italics' ? [schema.mark(schema.marks.em)] : []),
- ...(Doc.UserDoc().textDecoration === 'underline' ? [schema.mark(schema.marks.underline)] : []),
- ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { fontFamily: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontFamily) })] : []),
- ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontSize) })] : []),
- ...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []),
- ...[schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) })],
- ];
- this._editorView?.dispatch(this._editorView?.state.tr.setStoredMarks(docDefaultMarks));
+ if (this._editorView) {
+ const { text, paragraph } = schema.nodes;
+ const selNode = this._editorView.state.selection.$anchor.node();
+ if (this._editorView.state.selection.from === 1 && this._editorView.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) {
+ const docDefaultMarks = [schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) })];
+ this._editorView.state.selection.empty && this._editorView.state.selection.from === 1 && this._editorView?.dispatch(this._editorView?.state.tr.setStoredMarks(docDefaultMarks).removeStoredMark(schema.marks.pFontColor));
+ }
+ }
};
componentWillUnmount() {
@@ -1601,10 +1602,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const state = this.EditorView?.state;
if (state && this.ProseRef?.children[0].className.includes('-focused') && this._props.isContentActive() && !e.button) {
if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu();
- let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
- for (let target = e.target as any; target && !target.dataset?.targethrefs; target = target.parentElement);
- while (target && !target.dataset?.targethrefs) target = target.parentElement;
- FormattedTextBoxComment.update(this, this.EditorView!, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true');
+ let clickTarget = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
+ for (let { target } = e as any; target && !target.dataset?.targethrefs; target = target.parentElement);
+ while (clickTarget && !clickTarget.dataset?.targethrefs) clickTarget = clickTarget.parentElement;
+ FormattedTextBoxComment.update(this, this.EditorView!, undefined, clickTarget?.dataset?.targethrefs, clickTarget?.dataset.linkdoc, clickTarget?.dataset.nopreview === 'true');
}
};
@action
@@ -1724,9 +1725,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
if (!(this.EditorView?.state.selection instanceof NodeSelection)) {
this.autoLink();
if (this._editorView?.state.tr) {
- const tr = stordMarks?.reduce((tr, m) => {
- tr.addStoredMark(m);
- return tr;
+ const tr = stordMarks?.reduce((tr2, m) => {
+ tr2.addStoredMark(m);
+ return tr2;
}, this._editorView.state.tr);
tr && this._editorView.dispatch(tr);
}
@@ -2038,7 +2039,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
};
_oldWheel: any;
@computed get fontColor() {
- return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
+ return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontColor);
}
@computed get fontSize() {
return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontSize);
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 073ed91c3..5d448d40e 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -7,7 +7,7 @@ import { liftTarget } from 'prosemirror-transform';
import { EditorView } from 'prosemirror-view';
import { ClientUtils } from '../../../../ClientUtils';
import { Utils } from '../../../../Utils';
-import { AclAdmin, AclAugment, AclEdit } from '../../../../fields/DocSymbols';
+import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSymbols';
import { GetEffectiveAcl } from '../../../../fields/util';
import { Docs } from '../../../documents/Documents';
import { RTFMarkup } from '../../../util/RTFMarkup';
@@ -48,11 +48,12 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
}
const canEdit = (state: any) => {
- switch (GetEffectiveAcl(props.TemplateDataDocument)) {
+ const permissions = GetEffectiveAcl(props.TemplateDataDocument ?? props.Document[DocData]);
+ switch (permissions) {
case AclAugment:
{
const prevNode = state.selection.$cursor.nodeBefore;
- const prevUser = !prevNode ? ClientUtils.CurrentUserEmail() : prevNode.marks[prevNode.marks.length - 1].attrs.userid;
+ const prevUser = !prevNode ? ClientUtils.CurrentUserEmail() : prevNode.marks.lastElement()?.attrs.userid;
if (prevUser !== ClientUtils.CurrentUserEmail()) {
return false;
}
@@ -278,7 +279,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
dispatch(updateBullets(tx, schema));
if (view.state.selection.$anchor.node(-1)?.type === schema.nodes.list_item) {
// gets rid of an extra paragraph when joining two list items together.
- joinBackward(view.state, (tx: Transaction) => view.dispatch(tx));
+ joinBackward(view.state, (tx2: Transaction) => view.dispatch(tx2));
}
})
) {
@@ -344,7 +345,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
!splitBlockKeepMarks(state, (tx3: Transaction) => {
const tonode = tx3.selection.$to.node();
if (tx3.selection.to && tx3.doc.nodeAt(tx3.selection.to - 1)) {
- const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks);
+ const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks).setStoredMarks(marks || []);
dispatch(tx4);
}
@@ -365,7 +366,8 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
// Command to create a blank space
bind('Space', () => {
- if (props.TemplateDataDocument && ![AclAdmin, AclAugment, AclEdit].includes(GetEffectiveAcl(props.TemplateDataDocument))) return true;
+ const editDoc = props.TemplateDataDocument ?? props.Document[DocData];
+ if (editDoc && ![AclAdmin, AclAugment, AclEdit].includes(GetEffectiveAcl(editDoc))) return true;
return false;
});
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 6108383c2..6c12b9991 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -10,7 +10,6 @@ import { EditorView } from 'prosemirror-view';
import * as React from 'react';
import { Doc } from '../../../../fields/Doc';
import { BoolCast, Cast, StrCast } from '../../../../fields/Types';
-import { numberRange } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
import { LinkManager } from '../../../util/LinkManager';
import { SelectionManager } from '../../../util/SelectionManager';
@@ -147,12 +146,13 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const { activeHighlights } = active;
const refDoc = SelectionManager.Views.lastElement()?.layoutDoc ?? Doc.UserDoc();
const refField = (pfx => (pfx ? pfx + '_' : ''))(SelectionManager.Views.lastElement()?.LayoutFieldKey);
+ const refVal = (field: string, dflt: string) => StrCast(refDoc[refField + field], StrCast(Doc.UserDoc()[field], dflt));
this._activeListType = this.getActiveListStyle();
this._activeAlignment = this.getActiveAlignment();
- this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document._text_fontFamily, StrCast(refDoc[refField + 'fontFamily'], 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various';
- this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, StrCast(refDoc[refField + 'fontSize'], '10px')) : activeSizes[0];
- this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, StrCast(refDoc[refField + 'fontColor'], 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...';
+ this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document._text_fontFamily, refVal('fontFamily', 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various';
+ this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, refVal('fontSize', '10px')) : activeSizes[0];
+ this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, refVal('fontColor', 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...';
this._activeHighlightColor = !activeHighlights.length ? '' : activeHighlights.length > 0 ? String(activeHighlights[0]) : '...';
// update link in current selection
@@ -161,12 +161,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
setMark = (mark: Mark, state: EditorState, dispatch: any, dontToggle: boolean = false) => {
if (mark) {
- const liFirst = numberRange(state.selection.$from.depth + 1).find(i => state.selection.$from.node(i)?.type === state.schema.nodes.list_item);
- const liTo = numberRange(state.selection.$to.depth + 1).find(i => state.selection.$to.node(i)?.type === state.schema.nodes.list_item);
- const olFirst = numberRange(state.selection.$from.depth + 1).find(i => state.selection.$from.node(i)?.type === state.schema.nodes.ordered_list);
- const nodeOl = (liFirst && liTo && state.selection.$from.node(liFirst) !== state.selection.$to.node(liTo) && olFirst) || (!liFirst && !liTo && olFirst);
- const fromRange = numberRange(state.selection.from).reverse();
- const newPos = nodeOl ? fromRange.find(i => state.doc.nodeAt(i)?.type === state.schema.nodes.ordered_list) ?? state.selection.from : state.selection.from;
+ const newPos = state.selection.$anchor.node()?.type === schema.nodes.ordered_list ? state.selection.from : state.selection.from;
const node = (state.selection as NodeSelection).node ?? (newPos >= 0 ? state.doc.nodeAt(newPos) : undefined);
if (node?.type === schema.nodes.ordered_list || node?.type === schema.nodes.list_item) {
const hasMark = node.marks.some(m => m.type === mark.type);
@@ -174,18 +169,16 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const addAnyway = node.marks.filter(m => m.type === mark.type && Object.keys(m.attrs).some(akey => m.attrs[akey] !== mark.attrs[akey]));
const markup = state.tr.setNodeMarkup(newPos, node.type, node.attrs, hasMark && !addAnyway ? otherMarks : [...otherMarks, mark]);
dispatch(updateBullets(markup, state.schema));
- } else {
- const state = this.view?.state;
- if (state) {
- const { tr } = state;
- if (dontToggle) {
- tr.addMark(state.selection.from, state.selection.to, mark);
- dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(state.selection.from), tr.doc.resolve(state.selection.to)))); // bcz: need to redo the selection because ctrl-a selections disappear otherwise
- } else {
- toggleMark(mark.type, mark.attrs)(state, dispatch);
- }
+ } else if (state) {
+ const { tr } = state;
+ if (dontToggle) {
+ tr.addMark(state.selection.from, state.selection.to, mark);
+ dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(state.selection.from), tr.doc.resolve(state.selection.to)))); // bcz: need to redo the selection because ctrl-a selections disappear otherwise
+ } else {
+ toggleMark(mark.type, mark.attrs)(state, dispatch);
}
}
+ this.updateMenu(this.view, undefined, undefined, this.layoutDoc);
}
};
@@ -242,7 +235,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
m.type === state.schema.marks.pFontFamily && activeFamilies.add(m.attrs.fontFamily);
m.type === state.schema.marks.pFontColor && activeColors.add(m.attrs.fontColor);
m.type === state.schema.marks.pFontSize && activeSizes.add(m.attrs.fontSize);
- m.type === state.schema.marks.pFontHighlight && activeHighlights.add(String(m.attrs.fontHigh));
+ m.type === state.schema.marks.pFontHighlight && activeHighlights.add(String(m.attrs.fontHighlight));
});
} else if (SelectionManager.Views.some(dv => dv.ComponentView instanceof EquationBox)) {
SelectionManager.Views.forEach(dv => StrCast(dv.Document._text_fontSize) && activeSizes.add(StrCast(dv.Document._text_fontSize)));
@@ -359,18 +352,21 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
setFontField = (value: string, fontField: 'fontSize' | 'fontFamily' | 'fontColor' | 'fontHighlight') => {
if (this.view) {
- if (this.view.state.selection.from === 1 && this.view.state.selection.empty && (!this.view.state.doc.nodeAt(1) || !this.view.state.doc.nodeAt(1)?.marks.some(m => m.type.name === value))) {
+ const { text, paragraph } = this.view.state.schema.nodes;
+ const selNode = this.view.state.selection.$anchor.node();
+ if (this.view.state.selection.from === 1 && this.view.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) {
this.TextView.dataDoc[this.TextView.fieldKey + `_${fontField}`] = value;
this.view.focus();
- } else {
- const attrs: { [key: string]: string } = {};
- attrs[fontField] = value;
- const fmark = this.view?.state.schema.marks['pF' + fontField.substring(1)].create(attrs);
- this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true);
- this.view.focus();
}
- } else Doc.UserDoc()[fontField] = value;
- this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
+ const attrs: { [key: string]: string } = {};
+ attrs[fontField] = value;
+ const fmark = this.view?.state.schema.marks['pF' + fontField.substring(1)].create(attrs);
+ this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true);
+ this.view.focus();
+ } else {
+ Doc.UserDoc()[fontField] = value;
+ this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
+ }
};
// TODO: remove doesn't work
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 184487b7d..5bf942218 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -3,6 +3,7 @@ import { listItem, orderedList } from 'prosemirror-schema-list';
import { ParagraphNodeSpec, toParagraphDOM, getParagraphNodeAttrs } from './ParagraphNodeSpec';
import { DocServer } from '../../../DocServer';
import { Doc, Field, FieldType } from '../../../../fields/Doc';
+import { schema } from './schema_rts';
const blockquoteDOM: DOMOutputSpec = ['blockquote', 0];
const hrDOM: DOMOutputSpec = ['hr'];
@@ -353,7 +354,7 @@ export const nodes: { [index: string]: NodeSpec } = {
},
{
style: 'list-style-type=disc',
- getAttrs(dom: any) {
+ getAttrs() {
return { mapStyle: 'bullet' };
},
},
@@ -373,10 +374,10 @@ export const nodes: { [index: string]: NodeSpec } = {
],
toDOM(node: Node) {
const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : '';
- const fhigh = (found => (found ? `background-color: ${found};` : ''))(node.marks.find(m => m.type.name === 'marker')?.attrs.highlight);
- const fsize = (found => (found ? `font-size: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontSize')?.attrs.fontSize);
- const ffam = (found => (found ? `font-family: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontFamily')?.attrs.family);
- const fcol = (found => (found ? `color: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontColor')?.attrs.color);
+ const fhigh = (found => (found ? `background-color: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontHighlight)?.attrs.fontHighlight);
+ const fsize = (found => (found ? `font-size: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontSize)?.attrs.fontSize);
+ const ffam = (found => (found ? `font-family: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontFamily)?.attrs.fontFamily);
+ const fcol = (found => (found ? `color: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontColor)?.attrs.fontColor);
const marg = node.attrs.indent ? `margin-left: ${node.attrs.indent};` : '';
if (node.attrs.mapStyle === 'bullet') {
return [
@@ -421,10 +422,10 @@ export const nodes: { [index: string]: NodeSpec } = {
},
],
toDOM(node: Node) {
- const fhigh = (found => (found ? `background-color: ${found};` : ''))(node.marks.find(m => m.type.name === 'marker')?.attrs.highlight);
- const fsize = (found => (found ? `font-size: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontSize')?.attrs.fontSize);
- const ffam = (found => (found ? `font-family: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontFamily')?.attrs.family);
- const fcol = (found => (found ? `color: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontColor')?.attrs.color);
+ const fhigh = (found => (found ? `background-color: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontHighlight)?.attrs.fontHighlight);
+ const fsize = (found => (found ? `font-size: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontSize)?.attrs.fontSize);
+ const ffam = (found => (found ? `font-family: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontFamily)?.attrs.fontFamily);
+ const fcol = (found => (found ? `color: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontColor)?.attrs.fontColor);
const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : '';
return [
'li',
diff --git a/src/client/views/webcam/DashWebRTCVideo.scss b/src/client/views/webcam/DashWebRTCVideo.scss
deleted file mode 100644
index 5744ebbcd..000000000
--- a/src/client/views/webcam/DashWebRTCVideo.scss
+++ /dev/null
@@ -1,82 +0,0 @@
-@import '../global/globalCssVariables.module.scss';
-
-.webcam-cont {
- background: whitesmoke;
- color: grey;
- border-radius: 15px;
- box-shadow: #9c9396 0.2vw 0.2vw 0.4vw;
- border: solid #bbbbbbbb 5px;
- pointer-events: all;
- display: flex;
- flex-direction: column;
- overflow: hidden;
-
- .webcam-header {
- height: 50px;
- text-align: center;
- text-transform: uppercase;
- letter-spacing: 2px;
- font-size: 16px;
- width: 100%;
- margin-top: 20px;
- }
-
- .videoContainer {
- position: relative;
- width: calc(100% - 20px);
- height: 100%;
- /* border: 10px solid red; */
- margin-left: 10px;
- }
-
- .buttonContainer {
- display: flex;
- width: calc(100% - 20px);
- height: 50px;
- justify-content: center;
- text-align: center;
- /* border: 1px solid black; */
- margin-left: 10px;
- margin-top: 0;
- margin-bottom: 15px;
- }
-
- #roomName {
- outline: none;
- border-radius: inherit;
- border: 1px solid #bbbbbbbb;
- margin: 10px;
- padding: 10px;
- }
-
- .side {
- width: 25%;
- height: 20%;
- position: absolute;
- /* top: 65%; */
- z-index: 2;
- right: 0px;
- bottom: 18px;
- }
-
- .main {
- position: absolute;
- width: 100%;
- height: 100%;
- /* top: 20%; */
- align-self: center;
- }
-
- .videoButtons {
- border-radius: 50%;
- height: 30px;
- width: 30px;
- display: flex;
- justify-content: center;
- align-items: center;
- justify-self: center;
- align-self: center;
- margin: 5px;
- border: 1px solid black;
- }
-}
diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx
deleted file mode 100644
index 4e984f3d6..000000000
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import { IconLookup } from '@fortawesome/fontawesome-svg-core';
-import { faPhoneSlash, faSync } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc } from '../../../fields/Doc';
-import { InkTool } from '../../../fields/InkField';
-import { SnappingManager } from '../../util/SnappingManager';
-import '../../views/nodes/WebBox.scss';
-import { FieldView, FieldViewProps } from '../nodes/FieldView';
-import './DashWebRTCVideo.scss';
-import { hangup, initialize, refreshVideos } from './WebCamLogic';
-
-/**
- * This models the component that will be rendered, that can be used as a doc that will reflect the video cams.
- */
-@observer
-export class DashWebRTCVideo extends React.Component<FieldViewProps> {
- private roomText: HTMLInputElement | undefined;
- @observable remoteVideoAdded: boolean = false;
-
- @action
- changeUILook = () => (this.remoteVideoAdded = true);
-
- /**
- * Function that submits the title entered by user on enter press.
- */
- private onEnterKeyDown = (e: React.KeyboardEvent) => {
- if (e.keyCode === 13) {
- const submittedTitle = this.roomText!.value;
- this.roomText!.value = '';
- this.roomText!.blur();
- initialize(submittedTitle, this.changeUILook);
- }
- };
-
- public static LayoutString(fieldKey: string) {
- return FieldView.LayoutString(DashWebRTCVideo, fieldKey);
- }
-
- onClickRefresh = () => refreshVideos();
-
- onClickHangUp = () => hangup();
-
- render() {
- const content = (
- <div className="webcam-cont" style={{ width: '100%', height: '100%' }}>
- <div className="webcam-header">DashWebRTC</div>
- <input id="roomName" type="text" placeholder="Enter room name" ref={e => (this.roomText = e!)} onKeyDown={this.onEnterKeyDown} />
- <div className="videoContainer">
- <video id="localVideo" className={'RTCVideo' + (this.remoteVideoAdded ? ' side' : ' main')} autoPlay playsInline muted ref={e => {}}></video>
- <video id="remoteVideo" className="RTCVideo main" autoPlay playsInline ref={e => {}}></video>
- </div>
- <div className="buttonContainer">
- <div className="videoButtons" style={{ background: 'red' }} onClick={this.onClickHangUp}>
- <FontAwesomeIcon icon={faPhoneSlash as IconLookup} color="white" />
- </div>
- <div className="videoButtons" style={{ background: 'green' }} onClick={this.onClickRefresh}>
- <FontAwesomeIcon icon={faSync as IconLookup} color="white" />
- </div>
- </div>
- </div>
- );
-
- const frozen = !this.props.isSelected() || SnappingManager.IsResizing;
- const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !SnappingManager.IsResizing ? '-interactive' : '');
-
- return (
- <>
- <div className={classname}>{content}</div>
- {!frozen ? null : <div className="webBox-overlay" />}
- </>
- );
- }
-}
diff --git a/src/client/views/webcam/WebCamLogic.js b/src/client/views/webcam/WebCamLogic.js
deleted file mode 100644
index 5f6202bc8..000000000
--- a/src/client/views/webcam/WebCamLogic.js
+++ /dev/null
@@ -1,292 +0,0 @@
-'use strict';
-import io from "socket.io-client";
-
-var socket;
-var isChannelReady = false;
-var isInitiator = false;
-var isStarted = false;
-var localStream;
-var pc;
-var remoteStream;
-var turnReady;
-var room;
-
-export function initialize(roomName, handlerUI) {
-
- var pcConfig = {
- 'iceServers': [{
- 'urls': 'stun:stun.l.google.com:19302'
- }]
- };
-
- // Set up audio and video regardless of what devices are present.
- var sdpConstraints = {
- offerToReceiveAudio: true,
- offerToReceiveVideo: true
- };
-
- /////////////////////////////////////////////
-
- room = roomName;
-
- socket = io.connect(`${window.location.protocol}//${window.location.hostname}:4321`);
-
- if (room !== '') {
- socket.emit('create or join', room);
- console.log('Attempted to create or join room', room);
- }
-
- socket.on('created', function (room) {
- console.log('Created room ' + room);
- isInitiator = true;
- });
-
- socket.on('full', function (room) {
- console.log('Room ' + room + ' is full');
- });
-
- socket.on('join', function (room) {
- console.log('Another peer made a request to join room ' + room);
- console.log('This peer is the initiator of room ' + room + '!');
- isChannelReady = true;
- });
-
- socket.on('joined', function (room) {
- console.log('joined: ' + room);
- isChannelReady = true;
- });
-
- socket.on('log', function (array) {
- console.log.apply(console, array);
- });
-
- ////////////////////////////////////////////////
-
-
- // This client receives a message
- socket.on('message', function (message) {
- console.log('Client received message:', message);
- if (message === 'got user media') {
- maybeStart();
- } else if (message.type === 'offer') {
- if (!isInitiator && !isStarted) {
- maybeStart();
- }
- pc.setRemoteDescription(new RTCSessionDescription(message));
- doAnswer();
- } else if (message.type === 'answer' && isStarted) {
- pc.setRemoteDescription(new RTCSessionDescription(message));
- } else if (message.type === 'candidate' && isStarted) {
- var candidate = new RTCIceCandidate({
- sdpMLineIndex: message.label,
- candidate: message.candidate
- });
- pc.addIceCandidate(candidate);
- } else if (message === 'bye' && isStarted) {
- handleRemoteHangup();
- }
- });
-
- ////////////////////////////////////////////////////
-
- var localVideo = document.querySelector('#localVideo');
- var remoteVideo = document.querySelector('#remoteVideo');
-
- const gotStream = (stream) => {
- console.log('Adding local stream.');
- localStream = stream;
- localVideo.srcObject = stream;
- sendMessage('got user media');
- if (isInitiator) {
- maybeStart();
- }
- }
-
-
- navigator.mediaDevices.getUserMedia({
- audio: true,
- video: true
- })
- .then(gotStream)
- .catch(function (e) {
- alert('getUserMedia() error: ' + e.name);
- });
-
-
-
- var constraints = {
- video: true
- };
-
- console.log('Getting user media with constraints', constraints);
-
- const requestTurn = (turnURL) => {
- var turnExists = false;
- for (var i in pcConfig.iceServers) {
- if (pcConfig.iceServers[i].urls.substr(0, 5) === 'turn:') {
- turnExists = true;
- turnReady = true;
- break;
- }
- }
- if (!turnExists) {
- console.log('Getting TURN server from ', turnURL);
- // No TURN server. Get one from computeengineondemand.appspot.com:
- var xhr = new XMLHttpRequest();
- xhr.onreadystatechange = function () {
- if (xhr.readyState === 4 && xhr.status === 200) {
- var turnServer = JSON.parse(xhr.responseText);
- console.log('Got TURN server: ', turnServer);
- pcConfig.iceServers.push({
- 'urls': 'turn:' + turnServer.username + '@' + turnServer.turn,
- 'credential': turnServer.password
- });
- turnReady = true;
- }
- };
- xhr.open('GET', turnURL, true);
- xhr.send();
- }
- }
-
-
-
-
- if (location.hostname !== 'localhost') {
- requestTurn(
- `${window.location.origin}/corsProxy/${encodeURIComponent("https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913")}`
- );
- }
-
- const maybeStart = () => {
- console.log('>>>>>>> maybeStart() ', isStarted, localStream, isChannelReady);
- if (!isStarted && typeof localStream !== 'undefined' && isChannelReady) {
- console.log('>>>>>> creating peer connection');
- createPeerConnection();
- pc.addStream(localStream);
- isStarted = true;
- console.log('isInitiator', isInitiator);
- if (isInitiator) {
- doCall();
- }
- }
- };
-
- window.onbeforeunload = function () {
- sendMessage('bye');
- };
-
- /////////////////////////////////////////////////////////
-
- const createPeerConnection = () => {
- try {
- pc = new RTCPeerConnection(null);
- pc.onicecandidate = handleIceCandidate;
- pc.onaddstream = handleRemoteStreamAdded;
- pc.onremovestream = handleRemoteStreamRemoved;
- console.log('Created RTCPeerConnnection');
- } catch (e) {
- console.log('Failed to create PeerConnection, exception: ' + e.message);
- alert('Cannot create RTCPeerConnection object.');
- return;
- }
- }
-
- const handleIceCandidate = (event) => {
- console.log('icecandidate event: ', event);
- if (event.candidate) {
- sendMessage({
- type: 'candidate',
- label: event.candidate.sdpMLineIndex,
- id: event.candidate.sdpMid,
- candidate: event.candidate.candidate
- });
- } else {
- console.log('End of candidates.');
- }
- }
-
- const handleCreateOfferError = (event) => {
- console.log('createOffer() error: ', event);
- }
-
- const doCall = () => {
- console.log('Sending offer to peer');
- pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
- }
-
- const doAnswer = () => {
- console.log('Sending answer to peer.');
- pc.createAnswer().then(
- setLocalAndSendMessage,
- onCreateSessionDescriptionError
- );
- }
-
- const setLocalAndSendMessage = (sessionDescription) => {
- pc.setLocalDescription(sessionDescription);
- console.log('setLocalAndSendMessage sending message', sessionDescription);
- sendMessage(sessionDescription);
- }
-
- const onCreateSessionDescriptionError = (error) => {
- trace('Failed to create session description: ' + error.toString());
- }
-
-
-
- const handleRemoteStreamAdded = (event) => {
- console.log('Remote stream added.');
- remoteStream = event.stream;
- remoteVideo.srcObject = remoteStream;
- handlerUI();
-
- };
-
- const handleRemoteStreamRemoved = (event) => {
- console.log('Remote stream removed. Event: ', event);
- }
-}
-
-export function hangup() {
- console.log('Hanging up.');
- stop();
- sendMessage('bye');
- if (localStream) {
- localStream.getTracks().forEach(track => track.stop());
- }
-}
-
-function stop() {
- isStarted = false;
- if (pc) {
- pc.close();
- }
- pc = null;
-}
-
-function handleRemoteHangup() {
- console.log('Session terminated.');
- stop();
- isInitiator = false;
- if (localStream) {
- localStream.getTracks().forEach(track => track.stop());
- }
-}
-
-function sendMessage(message) {
- console.log('Client sending message: ', message);
- socket.emit('message', message, room);
-};
-
-export function refreshVideos() {
- var localVideo = document.querySelector('#localVideo');
- var remoteVideo = document.querySelector('#remoteVideo');
- if (localVideo) {
- localVideo.srcObject = localStream;
- }
- if (remoteVideo) {
- remoteVideo.srcObject = remoteStream;
- }
-
-} \ No newline at end of file