aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2023-10-26 11:31:15 -0400
committerbobzel <zzzman@gmail.com>2023-10-26 11:31:15 -0400
commit51cad21a358e17c1f8e609d1d3f077960922fc38 (patch)
tree62e00b55baa68953857da921c59782e58e1fe00c /src/client/views/nodes
parentbbdba27c743a871c51ff99f52a3d348fdd5d2faf (diff)
enabled different title colors per doc, not just per user. added support for screen space doc titles, and for proper title clipping when borderRadius is set. added dropdown for setting title field to display and tweaked editableView to enable ellipsis for overfow
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/DocumentView.scss6
-rw-r--r--src/client/views/nodes/DocumentView.tsx131
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss1
3 files changed, 103 insertions, 35 deletions
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index b25540dd3..874723895 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -24,6 +24,7 @@
width: 100%;
height: 100%;
border-radius: inherit;
+ overflow: hidden; // need this so that title will be clipped when borderRadius is set
// transition: outline 0.3s linear;
// background: $white; //overflow: hidden;
@@ -118,7 +119,7 @@
display: flex;
justify-content: center;
align-items: center;
-
+
.sharingIndicator {
height: 30px;
width: 30px;
@@ -185,6 +186,7 @@
text-overflow: ellipsis;
white-space: pre;
position: absolute;
+ display: flex; // this allows title field dropdown to be inline with editable title
}
.documentView-titleWrapper-hover {
@@ -214,7 +216,7 @@
.documentView-node:hover {
> .documentView-styleWrapper {
> .documentView-titleWrapper-hover {
- display: inline-block;
+ display: flex;
}
// > .documentView-contentsView {
// opacity: 0.5;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c2355e4f7..4a43ffe3e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -17,7 +17,7 @@ import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils';
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { DocServer } from '../../DocServer';
-import { Docs, DocUtils } from '../../documents/Documents';
+import { DocOptions, Docs, DocUtils, FInfo } from '../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
import { Networking } from '../../Network';
import { DictationManager } from '../../util/DictationManager';
@@ -54,6 +54,8 @@ import { PinProps, PresBox } from './trails/PresBox';
import React = require('react');
import { KeyValueBox } from './KeyValueBox';
import { LinkBox } from './LinkBox';
+import { FilterPanel } from '../FilterPanel';
+import { Dropdown, DropdownType, Type } from 'browndash-components';
const { Howl } = require('howler');
interface Window {
@@ -143,7 +145,7 @@ export interface DocComponentView {
annotationKey?: string;
getTitle?: () => string;
getCenter?: (xf: Transform) => { X: number; Y: number };
- screenBounds?: () => { left: number; top: number; right: number; bottom: number; center?:{X:number, Y:number} };
+ screenBounds?: () => { left: number; top: number; right: number; bottom: number; center?: { X: number; Y: number } };
ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
snapPt?: (pt: { X: number; Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number; Y: number }; distance: number };
@@ -622,7 +624,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (this.props.Document === Doc.ActiveDashboard) {
e.stopPropagation();
e.preventDefault();
- alert((e.target as any)?.closest?.('*.lm_content') ? "You can't perform this move most likely because you don't have permission to modify the destination." : 'Linking to document tabs not yet supported. Drop link on document content.');
+ alert(
+ (e.target as any)?.closest?.('*.lm_content')
+ ? "You can't perform this move most likely because you didn't drag the document's title bar to enable embedding in a different document."
+ : 'Linking to document tabs not yet supported. Drop link on document content.'
+ );
return true;
}
const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData;
@@ -1068,6 +1074,41 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
};
captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption');
+ @observable _changingTitleField = false;
+ @observable _dropDownInnerWidth = 0;
+ fieldsDropdown = (inputOptions: string[], dropdownWidth: number, placeholder: string, onChange: (val: string | number) => void, onClose: () => void) => {
+ const filteredOptions = new Set(inputOptions);
+ const scaling = this.titleHeight / 30; /* height of Dropdown */
+ Object.entries(DocOptions)
+ .filter(opts => opts[1].filterable)
+ .forEach((pair: [string, FInfo]) => filteredOptions.add(pair[0]));
+ filteredOptions.add(StrCast(this.layoutDoc.layout_showTitle));
+ const options = Array.from(filteredOptions)
+ .filter(f => f)
+ .map(facet => ({ val: facet, text: facet }));
+ return (
+ <div style={{ width: dropdownWidth }}>
+ <div
+ ref={action((r: any) => r && (this._dropDownInnerWidth = Number(getComputedStyle(r).width.replace('px', ''))))}
+ onPointerDown={action(e => (this._changingTitleField = true))}
+ style={{ width: 'max-content', transformOrigin: 'left', transform: `scale(${scaling})` }}>
+ <Dropdown
+ activeChanged={action(isOpen => !isOpen && (this._changingTitleField = false))}
+ selectedVal={placeholder}
+ setSelectedVal={onChange}
+ color={SettingsManager.userColor}
+ background={SettingsManager.userVariantColor}
+ type={Type.TERT}
+ closeOnSelect={true}
+ dropdownType={DropdownType.SELECT}
+ items={options}
+ width={100}
+ fillWidth
+ />
+ </div>
+ </div>
+ );
+ };
@computed get innards() {
TraceMobx();
const showTitle = this.layout_showTitle?.split(':')[0];
@@ -1095,8 +1136,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
</div>
);
const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc;
- const background = StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.userBackgroundColor));
+ const background = StrCast(
+ this.layoutDoc.layout_headingColor,
+ StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(this.layoutDoc.layout_headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.userBackgroundColor)))
+ );
+ const dropdownWidth = this._titleRef.current?._editing || this._changingTitleField ? Math.max(10, (this._dropDownInnerWidth * this.titleHeight) / 30) : 0;
const sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', '');
+ // displays a 'title' at the top of a document. The title contents default to the 'title' field, but can be changed to one or more fields by
+ // setting layout_showTitle using the format: field1[;field2[...][:hover]]
+ // from the UI, this is done by clicking the title field and prefixin the format with '#'. eg., #field1[;field2;...][:hover]
const titleView = !showTitle ? null : (
<div
className={`documentView-titleWrapper${showTitleHover ? '-hover' : ''}`}
@@ -1104,39 +1152,58 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
style={{
position: this.headerMargin ? 'relative' : 'absolute',
height: this.titleHeight,
- width: !this.headerMargin ? `calc(${sidebarWidthPercent || 100}% - 18px)` : (sidebarWidthPercent || 100) + '%', // leave room for annotation button
+ width: 100 - sidebarWidthPercent + '%',
color: background === 'transparent' ? SettingsManager.userColor : lightOrDark(background),
background,
pointerEvents: (!this.disableClickScriptFunc && this.onClickHandler) || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
}}>
- <EditableView
- ref={this._titleRef}
- contents={showTitle
- .split(';')
- .map(field => field.trim())
- .map(field => targetDoc[field]?.toString())
- .join('\\')}
- display={'block'}
- fontSize={10}
- GetValue={() => {
- return showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle;
- }}
- SetValue={undoBatch((input: string) => {
- if (input?.startsWith('#')) {
- if (this.rootDoc.layout_showTitle) {
- this.rootDoc._layout_showTitle = input?.substring(1) ? input.substring(1) : undefined;
- } else if (!this.props.layout_showTitle) {
- Doc.UserDoc().layout_showTitle = input?.substring(1) ? input.substring(1) : 'author_date';
+ {!dropdownWidth
+ ? null
+ : this.fieldsDropdown(
+ [],
+ dropdownWidth,
+ StrCast(this.layoutDoc.layout_showTitle).split(':')[0],
+ action((field: string | number) => {
+ if (this.rootDoc.layout_showTitle) {
+ this.rootDoc._layout_showTitle = field;
+ } else if (!this.props.layout_showTitle) {
+ Doc.UserDoc().layout_showTitle = field;
+ }
+ this._changingTitleField = false;
+ }),
+ action(() => (this._changingTitleField = false))
+ )}
+ <div
+ style={{
+ width: `calc(100% - ${dropdownWidth}px)`,
+ minWidth: '100px',
+ color: this._titleRef.current?._editing || this._changingTitleField ? 'black' : undefined,
+ background: this._titleRef.current?._editing || this._changingTitleField ? 'yellow' : undefined,
+ }}>
+ <EditableView
+ ref={this._titleRef}
+ contents={showTitle
+ .split(';')
+ .map(field => targetDoc[field.trim()]?.toString())
+ .join(' \\ ')}
+ display="block"
+ oneLine={true}
+ fontSize={(this.titleHeight / 15) * 10}
+ GetValue={() => (showTitle.split(';').length !== 1 ? '#' + showTitle : Field.toKeyValueString(this.rootDoc, showTitle.split(';')[0]))}
+ SetValue={undoBatch((input: string) => {
+ if (input?.startsWith('#')) {
+ if (this.rootDoc.layout_showTitle) {
+ this.rootDoc._layout_showTitle = input?.substring(1);
+ } else if (!this.props.layout_showTitle) {
+ Doc.UserDoc().layout_showTitle = input?.substring(1) ?? 'author_date';
+ }
+ } else if (showTitle && !showTitle.includes('Date') && showTitle !== 'author') {
+ KeyValueBox.SetField(targetDoc, showTitle, input);
}
- } else {
- var value = input.replace(new RegExp(showTitle + '='), '') as string | number;
- if (showTitle !== 'title' && Number(value).toString() === value) value = Number(value);
- if (showTitle.includes('Date') || showTitle === 'author') return true;
- Doc.SetInPlace(targetDoc, showTitle, value, true);
- }
- return true;
- })}
- />
+ return true;
+ })}
+ />
+ </div>
</div>
);
return this.props.hideTitle || (!showTitle && !this.layout_showCaption) ? (
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 6765e1dea..818c0cbe7 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -37,7 +37,6 @@ audiotag:hover {
background: inherit;
padding: 0;
border-width: 0px;
- border-radius: inherit;
border-color: $medium-gray;
box-sizing: border-box;
background-color: inherit;