aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormehekj <mehek.jethani@gmail.com>2023-03-15 18:15:18 -0400
committermehekj <mehek.jethani@gmail.com>2023-03-15 18:15:18 -0400
commit61ce7b4d434aff7e642d2af17b8643297f99e4a3 (patch)
tree0562ddcab46ef1c10996baaada7001c083dea820 /src
parent504c8046c13dfe77316e42943e48c017d67c2ad8 (diff)
column drag in progress
Diffstat (limited to 'src')
-rw-r--r--src/client/util/DragManager.ts14
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss112
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx40
-rw-r--r--src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx21
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx16
-rw-r--r--src/client/views/nodes/RecordingBox/RecordingView.tsx177
6 files changed, 197 insertions, 183 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index a56f87075..1b413c73b 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -149,10 +149,14 @@ export namespace DragManager {
linkDragView: DocumentView;
}
export class ColumnDragData {
- constructor(colKey: SchemaHeaderField) {
- this.colKey = colKey;
+ // constructor(colKey: SchemaHeaderField) {
+ // this.colKey = colKey;
+ // }
+ // colKey: SchemaHeaderField;
+ constructor(colIndex: number) {
+ this.colIndex = colIndex;
}
- colKey: SchemaHeaderField;
+ colIndex: number;
}
// used by PDFs,Text,Image,Video,Web to conditionally (if the drop completes) create a text annotation when dragging the annotate button from the AnchorMenu when a text/region selection has been made.
// this is pretty clunky and should be rethought out using linkDrag or DocumentDrag
@@ -256,8 +260,8 @@ export namespace DragManager {
}
// drags a column from a schema view
- export function StartColumnDrag(ele: HTMLElement, dragData: ColumnDragData, downX: number, downY: number, options?: DragOptions) {
- StartDrag([ele], dragData, downX, downY, options);
+ export function StartColumnDrag(ele: HTMLElement[], dragData: ColumnDragData, downX: number, downY: number, options?: DragOptions) {
+ StartDrag(ele, dragData, downX, downY, options);
}
export function SetSnapLines(horizLines: number[], vertLines: number[]) {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 5eb5cc86d..1853fb589 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -8,6 +8,7 @@
.schema-table {
background-color: $white;
+ cursor: default;
.schema-column-menu,
.schema-filter-menu {
@@ -80,68 +81,70 @@
align-self: center;
}
}
+ }
- .schema-header-row {
- justify-content: flex-end;
+ .schema-preview-divider {
+ height: 100%;
+ background: black;
+ cursor: ew-resize;
+ }
+}
- .row-menu {
- display: flex;
- justify-content: flex-end;
- }
+.schema-header-row {
+ cursor: grab;
+ justify-content: flex-end;
- .schema-column-header {
- font-weight: bold;
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: center;
- padding: 0;
- z-index: 1;
-
- .schema-column-title {
- flex-grow: 2;
- margin: 5px;
- }
+ .row-menu {
+ display: flex;
+ justify-content: flex-end;
+ }
+}
- .schema-header-menu {
- margin: 5px;
- }
+.schema-column-header {
+ background-color: $light-gray;
+ font-weight: bold;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0;
+ z-index: 1;
- .schema-column-resizer {
- height: 100%;
- width: 3px;
- cursor: ew-resize;
+ .schema-column-title {
+ flex-grow: 2;
+ margin: 5px;
+ }
- &:hover {
- background-color: $light-blue;
- }
- }
+ .schema-header-menu {
+ margin: 5px;
+ }
- .schema-column-resizer.right {
- margin-left: 5px;
- align-self: flex-end;
- }
+ .schema-column-resizer {
+ height: 100%;
+ width: 3px;
+ cursor: ew-resize;
- .schema-column-resizer.left {
- margin-right: 5px;
- align-self: flex-start;
- }
- }
+ &:hover {
+ background-color: $light-blue;
}
+ }
- .schema-header-menu {
- display: flex;
- flex-direction: row;
- }
+ .schema-column-resizer.right {
+ margin-left: 5px;
+ align-self: flex-end;
}
- .schema-preview-divider {
- height: 100%;
- background: black;
- cursor: ew-resize;
+ .schema-column-resizer.left {
+ margin-right: 5px;
+ align-self: flex-start;
}
}
+.schema-header-menu {
+ display: flex;
+ flex-direction: row;
+}
+
.schema-row-wrapper {
// max-height: 70px;
overflow: hidden;
@@ -158,17 +161,18 @@
height: 100%;
// max-height: 70px;
overflow: auto;
+}
- .schema-column-header,
- .schema-table-cell,
- .row-menu {
- border: 1px solid $medium-gray;
- padding: 5px;
- overflow: hidden;
- }
+.schema-column-header,
+.schema-table-cell,
+.row-menu {
+ border: 1px solid $medium-gray;
+ padding: 5px;
+ overflow: hidden;
}
.schema-row {
+ cursor: default;
justify-content: flex-end;
background: white;
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index e1c2d989f..b0114a226 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -54,6 +54,7 @@ export class CollectionSchemaView extends CollectionSubView() {
@observable _lastSelectedRow: number | undefined;
@observable _selectedDocs: ObservableSet = new ObservableSet<Doc>();
@observable _rowEles: ObservableMap = new ObservableMap<Doc, HTMLDivElement>();
+ @observable _colEles: HTMLDivElement[] = [];
@observable _isDragging: boolean = false;
@observable _displayColumnWidths: number[] | undefined;
@observable _columnMenuIndex: number | undefined;
@@ -301,23 +302,17 @@ export class CollectionSchemaView extends CollectionSubView() {
this.layoutDoc.columnWidths = new List<number>(currWidths);
};
- // @action
- // dragColumn = (e: any, index: number) => {
- // e.stopPropagation();
- // e.preventDefault();
- // const rect = e.target.getBoundingClientRect();
- // if (e.clientX < rect.x) {
- // if (index < 1) return true;
- // this.swapColumns(index - 1, index);
- // return true;
- // }
- // if (e.clientX > rect.x + rect.width) {
- // if (index === this.columnKeys.length) return true;
- // this.swapColumns(index, index + 1);
- // return true;
- // }
- // return false;
- // };
+ @action
+ dragColumn = (e: PointerEvent, index: number) => {
+ const dragData = new DragManager.ColumnDragData(index);
+ const dragEles = [this._colEles[index]];
+ this.childDocs.forEach(doc => {
+ dragEles.push(this._rowEles.get(doc).children[1].children[index]);
+ });
+ DragManager.StartColumnDrag(dragEles, dragData, e.x, e.y);
+
+ return true;
+ };
@action
addRowRef = (doc: Doc, ref: HTMLDivElement) => {
@@ -325,6 +320,15 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@action
+ setColRef = (index: number, ref: HTMLDivElement) => {
+ if (this._colEles.length <= index) {
+ this._colEles.push(ref);
+ } else {
+ this._colEles[index] = ref;
+ }
+ };
+
+ @action
addDocToSelection = (doc: Doc, extendSelection: boolean, index: number) => {
this._selectedDocs.add(doc);
const rowDocView = DocumentManager.Instance.getDocumentView(doc);
@@ -842,6 +846,8 @@ export class CollectionSchemaView extends CollectionSubView() {
removeColumn={this.removeColumn}
resizeColumn={this.startResize}
openContextMenu={this.openContextMenu}
+ dragColumn={this.dragColumn}
+ setColRef={this.setColRef}
/>
);
})}
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index 42626697a..e648356f4 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -26,8 +26,9 @@ export interface SchemaColumnHeaderProps {
setSort: (field: string, desc: boolean) => void;
removeColumn: (index: number) => void;
resizeColumn: (e: any, index: number, left: boolean) => void;
- // dragColumn: (e: any, index: number) => boolean;
+ dragColumn: (e: any, index: number) => boolean;
openContextMenu: (x: number, y: number, index: number) => void;
+ setColRef: (index: number, ref: HTMLDivElement) => void;
}
@observer
@@ -47,16 +48,20 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
}
};
- // @action
- // onPointerDown = (e: React.PointerEvent) => {
- // e.stopPropagation();
-
- // setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction);
- // };
+ @action
+ onPointerDown = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction);
+ };
render() {
return (
- <div className="schema-column-header" style={{ width: this.props.columnWidths[this.props.columnIndex] }}>
+ <div
+ className="schema-column-header"
+ style={{ width: this.props.columnWidths[this.props.columnIndex] }}
+ onPointerDown={this.onPointerDown}
+ ref={(col: HTMLDivElement | null) => {
+ col && this.props.setColRef(this.props.columnIndex, col);
+ }}>
<div className="schema-column-resizer left" onPointerDown={e => this.props.resizeColumn(e, this.props.columnIndex, true)}></div>
<div className="schema-column-title">{this.fieldKey}</div>
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 43b7da544..91b292b28 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -48,13 +48,15 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
};
return (
- <div className="schema-table-cell" style={{ width: this.props.columnWidth, pointerEvents: this.props.isRowActive() ? 'all' : 'none' }}>
- <EditableView
- contents={<FieldView {...props} />}
- height={'auto'}
- GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
- SetValue={(value: string) => KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value)}
- />
+ <div className="schema-table-cell" style={{ width: this.props.columnWidth }}>
+ <div className="schemacell-edit-wrapper" style={{ cursor: this.props.isRowActive() ? 'text' : 'default' }}>
+ <EditableView
+ contents={<FieldView {...props} />}
+ GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
+ SetValue={(value: string) => KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value)}
+ editing={this.props.isRowActive() ? undefined : false}
+ />
+ </div>
</div>
);
}
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index ec5917b9e..6efe62e0b 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -1,32 +1,31 @@
import * as React from 'react';
-import "./RecordingView.scss";
-import { useEffect, useRef, useState } from "react";
-import { ProgressBar } from "./ProgressBar"
+import './RecordingView.scss';
+import { useEffect, useRef, useState } from 'react';
+import { ProgressBar } from './ProgressBar';
import { MdBackspace } from 'react-icons/md';
import { FaCheckCircle } from 'react-icons/fa';
-import { IconContext } from "react-icons";
+import { IconContext } from 'react-icons';
import { Networking } from '../../../Network';
import { Upload } from '../../../../server/SharedMediaTypes';
import { returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
import { Presentation, TrackMovements } from '../../../util/TrackMovements';
export interface MediaSegment {
- videoChunks: any[],
- endTime: number,
- startTime: number,
- presentation?: Presentation,
+ videoChunks: any[];
+ endTime: number;
+ startTime: number;
+ presentation?: Presentation;
}
interface IRecordingViewProps {
- setResult: (info: Upload.AccessPathInfo, presentation?: Presentation) => void
- setDuration: (seconds: number) => void
- id: string
+ setResult: (info: Upload.AccessPathInfo, presentation?: Presentation) => void;
+ setDuration: (seconds: number) => void;
+ id: string;
}
const MAXTIME = 100000;
export function RecordingView(props: IRecordingViewProps) {
-
const [recording, setRecording] = useState(false);
const recordingTimerRef = useRef<number>(0);
const [recordingTimer, setRecordingTimer] = useState(0); // unit is 0.01 second
@@ -46,19 +45,16 @@ export function RecordingView(props: IRecordingViewProps) {
const [finished, setFinished] = useState<boolean>(false);
const [trackScreen, setTrackScreen] = useState<boolean>(false);
-
-
const DEFAULT_MEDIA_CONSTRAINTS = {
video: {
width: 1280,
height: 720,
-
},
audio: {
echoCancellation: true,
noiseSuppression: true,
- sampleRate: 44100
- }
+ sampleRate: 44100,
+ },
};
useEffect(() => {
@@ -71,12 +67,11 @@ export function RecordingView(props: IRecordingViewProps) {
const videoFiles = videos.map((vid, i) => new File(vid.videoChunks, `segvideo${i}.mkv`, { type: vid.videoChunks[0].type, lastModified: Date.now() }));
// upload the segments to the server and get their server access paths
- const serverPaths: string[] = (await Networking.UploadFilesToServer(videoFiles))
- .map(res => (res.result instanceof Error) ? '' : res.result.accessPaths.agnostic.server)
+ const serverPaths: string[] = (await Networking.UploadFilesToServer(videoFiles)).map(res => (res.result instanceof Error ? '' : res.result.accessPaths.agnostic.server));
// concat the segments together using post call
const result: Upload.AccessPathInfo | Error = await Networking.PostToServer('/concatVideos', serverPaths);
- !(result instanceof Error) ? props.setResult(result, concatPres || undefined) : console.error("video conversion failed");
+ !(result instanceof Error) ? props.setResult(result, concatPres || undefined) : console.error('video conversion failed');
})();
}
}, [videos]);
@@ -87,7 +82,9 @@ export function RecordingView(props: IRecordingViewProps) {
}, [finished]);
// check if the browser supports media devices on first load
- useEffect(() => { if (!navigator.mediaDevices) alert('This browser does not support getUserMedia.'); }, []);
+ useEffect(() => {
+ if (!navigator.mediaDevices) alert('This browser does not support getUserMedia.');
+ }, []);
useEffect(() => {
let interval: any = null;
@@ -102,24 +99,24 @@ export function RecordingView(props: IRecordingViewProps) {
}, [recording]);
useEffect(() => {
- setVideoProgressHelper(recordingTimer)
+ setVideoProgressHelper(recordingTimer);
recordingTimerRef.current = recordingTimer;
}, [recordingTimer]);
const setVideoProgressHelper = (progress: number) => {
const newProgress = (progress / MAXTIME) * 100;
setProgress(newProgress);
- }
+ };
const startShowingStream = async (mediaConstraints = DEFAULT_MEDIA_CONSTRAINTS) => {
const stream = await navigator.mediaDevices.getUserMedia(mediaConstraints);
- videoElementRef.current!.src = "";
+ videoElementRef.current!.src = '';
videoElementRef.current!.srcObject = stream;
videoElementRef.current!.muted = true;
return stream;
- }
+ };
const record = async () => {
// don't need to start a new stream every time we start recording a new segment
@@ -145,29 +142,28 @@ export function RecordingView(props: IRecordingViewProps) {
const nextVideo = {
videoChunks,
endTime: recordingTimerRef.current,
- startTime: videos?.lastElement()?.endTime || 0
+ startTime: videos?.lastElement()?.endTime || 0,
};
// depending on if a presenation exists, add it to the video
const presentation = TrackMovements.Instance.yieldPresentation();
- setVideos(videos => [...videos, (presentation != null && trackScreen) ? { ...nextVideo, presentation } : nextVideo]);
+ setVideos(videos => [...videos, presentation != null && trackScreen ? { ...nextVideo, presentation } : nextVideo]);
}
// reset the temporary chunks
videoChunks = [];
setRecording(false);
- }
+ };
videoRecorder.current.start(200);
- }
-
+ };
// if this is called, then we're done recording all the segments
const finish = (e: React.PointerEvent) => {
e.stopPropagation();
// call stop on the video recorder if active
- videoRecorder.current?.state !== "inactive" && videoRecorder.current?.stop();
+ videoRecorder.current?.state !== 'inactive' && videoRecorder.current?.stop();
// end the streams (audio/video) to remove recording icon
const stream = videoElementRef.current!.srcObject;
@@ -178,94 +174,91 @@ export function RecordingView(props: IRecordingViewProps) {
// this will call upon progessbar to update videos to be in the correct order
setFinished(true);
- }
+ };
const pause = (e: React.PointerEvent) => {
e.stopPropagation();
// if recording, then this is just a new segment
- videoRecorder.current?.state === "recording" && videoRecorder.current.stop();
- }
+ videoRecorder.current?.state === 'recording' && videoRecorder.current.stop();
+ };
const start = (e: React.PointerEvent) => {
- setupMoveUpEvents({}, e, returnTrue, returnFalse, e => {
- // start recording if not already recording
- if (!videoRecorder.current || videoRecorder.current.state === "inactive") record();
-
- return true; // cancels propagation to documentView to avoid selecting it.
- }, false, false);
- }
+ setupMoveUpEvents(
+ {},
+ e,
+ returnTrue,
+ returnFalse,
+ e => {
+ // start recording if not already recording
+ if (!videoRecorder.current || videoRecorder.current.state === 'inactive') record();
+
+ return true; // cancels propagation to documentView to avoid selecting it.
+ },
+ false,
+ false
+ );
+ };
const undoPrevious = (e: React.PointerEvent) => {
e.stopPropagation();
setDoUndo(prev => !prev);
- }
+ };
- const handleOnTimeUpdate = () => { playing && setVideoProgressHelper(videoElementRef.current!.currentTime); };
+ const handleOnTimeUpdate = () => {
+ playing && setVideoProgressHelper(videoElementRef.current!.currentTime);
+ };
const millisecondToMinuteSecond = (milliseconds: number) => {
const toTwoDigit = (digit: number) => {
- return String(digit).length == 1 ? "0" + digit : digit
- }
+ return String(digit).length == 1 ? '0' + digit : digit;
+ };
const minutes = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((milliseconds % (1000 * 60)) / 1000);
- return toTwoDigit(minutes) + " : " + toTwoDigit(seconds);
- }
+ return toTwoDigit(minutes) + ' : ' + toTwoDigit(seconds);
+ };
return (
<div className="recording-container">
<div className="video-wrapper">
- <video id={`video-${props.id}`}
- autoPlay
- muted
- onTimeUpdate={() => handleOnTimeUpdate()}
- ref={videoElementRef}
- />
+ <video id={`video-${props.id}`} autoPlay muted onTimeUpdate={() => handleOnTimeUpdate()} ref={videoElementRef} />
<div className="recording-sign">
<span className="dot" />
<p className="timer">{millisecondToMinuteSecond(recordingTimer * 10)}</p>
</div>
<div className="controls">
-
<div className="controls-inner-container">
- <div className="record-button-wrapper">
- {recording ?
- <button className="stop-button" onPointerDown={pause} /> :
- <button className="record-button" onPointerDown={start} />
- }
- </div>
-
- {!recording && (videos.length > 0 ?
-
- <div className="options-wrapper video-edit-wrapper">
- <IconContext.Provider value={{ color: "grey", className: "video-edit-buttons", style: { display: canUndo ? 'inherit' : 'none' } }}>
- <MdBackspace onPointerDown={undoPrevious} />
- </IconContext.Provider>
- <IconContext.Provider value={{ color: "#cc1c08", className: "video-edit-buttons" }}>
- <FaCheckCircle onPointerDown={finish} />
- </IconContext.Provider>
- </div>
-
- : <div className="options-wrapper track-screen-wrapper">
- <label className="track-screen">
- <input type="checkbox" checked={trackScreen} onChange={(e) => { setTrackScreen(e.target.checked) }} />
- <span className="checkmark"></span>
- Track Screen
- </label>
- </div>)}
-
+ <div className="record-button-wrapper">{recording ? <button className="stop-button" onPointerDown={pause} /> : <button className="record-button" onPointerDown={start} />}</div>
+
+ {!recording &&
+ (videos.length > 0 ? (
+ <div className="options-wrapper video-edit-wrapper">
+ <IconContext.Provider value={{ color: 'grey', className: 'video-edit-buttons', style: { display: canUndo ? 'inherit' : 'none' } }}>
+ <MdBackspace onPointerDown={undoPrevious} />
+ </IconContext.Provider>
+ <IconContext.Provider value={{ color: '#cc1c08', className: 'video-edit-buttons' }}>
+ <FaCheckCircle onPointerDown={finish} />
+ </IconContext.Provider>
+ </div>
+ ) : (
+ <div className="options-wrapper track-screen-wrapper">
+ <label className="track-screen">
+ <input
+ type="checkbox"
+ checked={trackScreen}
+ onChange={e => {
+ setTrackScreen(e.target.checked);
+ }}
+ />
+ <span className="checkmark"></span>
+ Track Screen
+ </label>
+ </div>
+ ))}
</div>
-
</div>
- <ProgressBar
- videos={videos}
- setVideos={setVideos}
- orderVideos={orderVideos}
- progress={progress}
- recording={recording}
- doUndo={doUndo}
- setCanUndo={setCanUndo}
- />
+ <ProgressBar videos={videos} setVideos={setVideos} orderVideos={orderVideos} progress={progress} recording={recording} doUndo={doUndo} setCanUndo={setCanUndo} />
</div>
- </div>)
-} \ No newline at end of file
+ </div>
+ );
+}