aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/DocumentView.tsx5
-rw-r--r--src/client/views/nodes/FieldView.tsx4
-rw-r--r--src/client/views/nodes/FontIconBox/FontIconBox.tsx21
-rw-r--r--src/client/views/nodes/IconTagBox.scss26
-rw-r--r--src/client/views/nodes/IconTagBox.tsx92
-rw-r--r--src/client/views/nodes/ImageBox.tsx3
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx3
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts4
8 files changed, 144 insertions, 14 deletions
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4c357cf45..758e70508 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -67,6 +67,7 @@ export interface DocumentViewProps extends FieldViewSharedProps {
hideCaptions?: boolean;
contentPointerEvents?: Property.PointerEvents | undefined; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
dontCenter?: 'x' | 'y' | 'xy';
+ showTags?: boolean;
childHideDecorationTitle?: boolean;
childHideResizeHandles?: boolean;
childDragAction?: dropActionType; // allows child documents to be dragged out of collection without holding the embedKey or dragging the doc decorations title bar.
@@ -1126,6 +1127,10 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
@observable public static CurrentlyPlaying: DocumentView[] = []; // audio or video media views that are currently playing
@observable public TagPanelHeight = 0;
+ @computed get showTags() {
+ return this.Document._layout_showTags || this._props.showTags;
+ }
+
@computed private get shouldNotScale() {
return (this.layout_fitWidth && !this.nativeWidth) || this.ComponentView?.isUnstyledView?.();
}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index c269c7bcb..683edba16 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable react/no-unused-prop-types */
-/* eslint-disable react/require-default-props */
import { Property } from 'csstype';
import { computed } from 'mobx';
import { observer } from 'mobx-react';
@@ -21,6 +19,7 @@ export type FocusFuncType = (doc: Doc, options: FocusViewOptions) => Opt<number>
// eslint-disable-next-line no-use-before-define
export type StyleProviderFuncType = (
doc: Opt<Doc>,
+ // eslint-disable-next-line no-use-before-define
props: Opt<FieldViewProps>,
property: string
) =>
@@ -65,6 +64,7 @@ export interface FieldViewSharedProps {
containerViewPath?: () => DocumentView[];
fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document
isGroupActive?: () => string | undefined; // is this document part of a group that is active
+ // eslint-disable-next-line no-use-before-define
setContentViewBox?: (view: ViewBoxInterface<FieldViewProps>) => void; // called by rendered field's viewBox so that DocumentView can make direct calls to the viewBox
PanelWidth: () => number;
PanelHeight: () => number;
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 7a09ad9e2..cb0c4d188 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -192,7 +192,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
} else {
text = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result as string;
// text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
- getStyle = (val: string) => ({ fontFamily: val });
+ if (this.Document.title === 'Font') getStyle = (val: string) => ({ fontFamily: val }); // bcz: major hack to style the font dropdown items --- needs to become part of the dropdown's metadata
}
// Get items to place into the list
@@ -266,26 +266,31 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
// Colors
const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string;
const items = DocListCast(this.dataDoc.data);
- const multiDoc = this.Document;
+ const selectedItems = items.filter(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, value: undefined, _readOnly_: true }).result).map(item => StrCast(item.toolType));
return (
<MultiToggle
tooltip={`Toggle ${tooltip}`}
type={Type.PRIM}
color={color}
- onPointerDown={e => script && !toggleStatus && setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => script.run({ this: multiDoc, value: undefined, _readOnly_: false }))}
+ multiSelect={true}
+ onPointerDown={e => script && !toggleStatus && setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => script.run({ this: this.Document, value: undefined, _readOnly_: false }))}
isToggle={script ? true : false}
toggleStatus={toggleStatus}
//background={SnappingManager.userBackgroundColor}
label={this.label}
- items={DocListCast(this.dataDoc.data).map(item => ({
+ items={items.map(item => ({
icon: <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={StrCast(item.icon) as IconProp} color={color} />,
tooltip: StrCast(item.toolTip),
val: StrCast(item.toolType),
}))}
- selectedVal={StrCast(items.find(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, value: undefined, _readOnly_: true }).result)?.toolType ?? StrCast(multiDoc.toolType))}
- setSelectedVal={(val: string | number) => {
- const itemDoc = items.find(item => item.toolType === val);
- itemDoc && ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, value: val, _readOnly_: false });
+ selectedItems={selectedItems}
+ onSelectionChange={(val: (string | number) | (string | number)[], added: boolean) => {
+ // note: the multitoggle is telling us whether the selection was toggled on or off, but we ignore this since we know the state of all the buttons
+ // and control it through the selectedItems prop. Therefore, the callback script will have to re-determine the toggle information.
+ // it would be better to pas the 'added' flag to the callback script, but our script generator from currentUserUtils makes it hard to define
+ // arbitrary parameter variables (but it could be done as a special case or with additional effort when creating the sript)
+ const itemsChanged = items.filter(item => (val instanceof Array ? val.includes(item.toolType as string | number) : item.toolType === val));
+ itemsChanged.forEach(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, _added_: added, itemDoc, _readOnly_: false }));
}}
/>
);
diff --git a/src/client/views/nodes/IconTagBox.scss b/src/client/views/nodes/IconTagBox.scss
new file mode 100644
index 000000000..90cc06092
--- /dev/null
+++ b/src/client/views/nodes/IconTagBox.scss
@@ -0,0 +1,26 @@
+@import '../global/globalCssVariables.module.scss';
+
+.card-button-container {
+ display: flex;
+ position: relative;
+ pointer-events: none;
+ background-color: rgb(218, 218, 218);
+ border-radius: 50px;
+ align-items: center;
+ gap: 5px;
+ padding-left: 5px;
+ padding-right: 5px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+
+ button {
+ pointer-events: auto;
+ width: 20px;
+ height: 20px;
+ margin: auto;
+ padding: 0;
+ border-radius: 50%;
+ background-color: $dark-gray;
+ background-color: transparent;
+ }
+}
diff --git a/src/client/views/nodes/IconTagBox.tsx b/src/client/views/nodes/IconTagBox.tsx
new file mode 100644
index 000000000..8faf8ffa5
--- /dev/null
+++ b/src/client/views/nodes/IconTagBox.tsx
@@ -0,0 +1,92 @@
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@mui/material';
+import { computed, makeObservable } from 'mobx';
+import { observer } from 'mobx-react';
+import React from 'react';
+import { returnFalse, setupMoveUpEvents } from '../../../ClientUtils';
+import { emptyFunction } from '../../../Utils';
+import { Doc } from '../../../fields/Doc';
+import { StrCast } from '../../../fields/Types';
+import { undoable } from '../../util/UndoManager';
+import { ObservableReactComponent } from '../ObservableReactComponent';
+import { TagItem } from '../TagsView';
+import { DocumentView } from './DocumentView';
+import './IconTagBox.scss';
+
+export interface IconTagProps {
+ Views: DocumentView[];
+ IsEditing: boolean;
+}
+
+/**
+ * Renders the icon tags that rest under the document. The icons rendered are determined by the values of
+ * each icon in the userdoc.
+ */
+@observer
+export class IconTagBox extends ObservableReactComponent<IconTagProps> {
+ constructor(props: IconTagProps) {
+ super(props);
+ makeObservable(this);
+ }
+
+ @computed get View() { return this._props.Views.lastElement(); } // prettier-ignore
+ @computed get currentScale() { return this.View?.screenToLocalScale(); } // prettier-ignore
+
+ /**
+ * Sets or removes the specified tag
+ * @param tag tag name (should begin with '#')
+ * @param state flag to add or remove the metadata
+ */
+ setIconTag = undoable((tag: string, state: boolean) => {
+ this._props.Views.forEach(view => {
+ state && TagItem.addTagToDoc(view.dataDoc, tag);
+ !state && TagItem.removeTagFromDoc(view.dataDoc, tag);
+ });
+ }, 'toggle card tag');
+
+ /**
+ * Returns a renderable version of the button Doc that is colorized to indicate
+ * whether the doc has the associated tag set on it or not.
+ * @param doc doc to test
+ * @param key metadata icon button
+ * @returns an icon for the metdata button
+ */
+ getButtonIcon = (doc: Doc, key: Doc): JSX.Element => {
+ const icon = StrCast(key.icon) as IconProp;
+ const tag = StrCast(key.toolType);
+ const isActive = TagItem.docHasTag(doc, tag);
+ const color = isActive ? '#4476f7' : '#323232'; // TODO should use theme colors
+
+ return <FontAwesomeIcon icon={icon} style={{ color, height: '20px', width: '20px' }} />;
+ };
+
+ /**
+ * Renders the buttons to customize sorting depending on which group the card belongs to and the amount of total groups
+ */
+ render() {
+ const buttons = Doc.MyFilterHotKeys
+ .map(key => ({ key, tag: StrCast(key.toolType) }))
+ .filter(({ tag }) => this._props.IsEditing || TagItem.docHasTag(this.View.Document, tag) || (DocumentView.Selected.length === 1 && this.View.IsSelected))
+ .map(({ key, tag }) => (
+ <Tooltip key={tag} title={<div className="dash-tooltip">Click to add/remove this card from the {tag} group</div>}>
+ <button
+ type="button"
+ onPointerDown={e =>
+ setupMoveUpEvents(this, e, returnFalse, emptyFunction, clickEv => {
+ this.setIconTag(tag, !TagItem.docHasTag(this.View.Document, tag));
+ clickEv.stopPropagation();
+ })
+ }>
+ {this.getButtonIcon(this.View.Document, key)}
+ </button>
+ </Tooltip>
+ )); // prettier-ignore
+
+ return !buttons.length ? null : (
+ <div className="card-button-container" style={{ fontSize: '50px' }}>
+ {buttons}
+ </div>
+ );
+ }
+}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index be3525544..aa4376bb2 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -368,7 +368,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
TraceMobx();
const backColor = DashColor((this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string) ?? Colors.WHITE);
- const backAlpha = backColor.red() === 0 && backColor.green() === 0 && backColor.blue() === 0 ? backColor.alpha() : 1;
+ // allow use case where the image is transparent when the alpha value is to smallest possible value from UI (alpha = 1 out of 255)
+ const backAlpha = backColor.alpha() < 0.015 && backColor.alpha() > 0 ? backColor.alpha() : 1;
const srcpath = this.layoutDoc.hideImage ? '' : this.paths[0];
const fadepath = this.layoutDoc.hideImage ? '' : this.paths.lastElement();
const { nativeWidth, nativeHeight /* , nativeOrientation */ } = this.nativeSize;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 343e255dc..1ccc6e502 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -275,6 +275,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
ele.append(contents);
}
this._selectionHTML = ele?.innerHTML;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
/* empty */
}
@@ -561,7 +562,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const draggedDoc = dragData.droppedDocuments.lastElement();
let added: Opt<boolean>;
const dropAction = dragData.dropAction || dragData.userDropAction;
- if ([AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl)) {
+ if ([AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl) && !dragData.draggedDocuments.includes(this.Document)) {
// replace text contents when dragging with Alt
if (de.altKey) {
const fieldKey = Doc.LayoutFieldKey(draggedDoc);
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index e0d6c7c05..f58434906 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -404,9 +404,9 @@ export class RichTextRules {
if (!tags.includes(tag)) {
tags.push(tag);
this.Document[DocData].tags = new List<string>(tags);
- this.Document[DocData].showTags = true;
+ this.Document._layout_showTags = true;
}
- const fieldView = state.schema.nodes.dashField.create({ fieldKey: '#' + tag });
+ const fieldView = state.schema.nodes.dashField.create({ fieldKey: tag.startsWith('@') ? tag.replace(/^@/, '') : '#' + tag });
return state.tr
.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end)))
.replaceSelectionWith(fieldView, true)