aboutsummaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/client/documents/Documents.ts2
-rw-r--r--src/client/util/CurrentUserUtils.ts2
-rw-r--r--src/client/util/SettingsManager.tsx15
-rw-r--r--src/client/views/DocumentDecorations.tsx1
-rw-r--r--src/client/views/EditableView.scss2
-rw-r--r--src/client/views/EditableView.tsx17
-rw-r--r--src/client/views/FilterPanel.tsx110
-rw-r--r--src/client/views/StyleProvider.tsx5
-rw-r--r--src/client/views/global/globalScripts.ts15
-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
12 files changed, 201 insertions, 106 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 161aba6e1..5c913513a 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -444,6 +444,8 @@ export class DocumentOptions {
userBackgroundColor?: STRt = new StrInfo('background color associated with a Dash user (seen in header fields of shared documents)');
userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)');
}
+
+export const DocOptions = new DocumentOptions();
export namespace Docs {
export let newAccount: boolean = false;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index dc988b04d..591bc7430 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -707,7 +707,7 @@ export class CurrentUserUtils {
CollectionViewType.Grid, CollectionViewType.NoteTaking]),
title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}},
{ title: "Pin", icon: "map-pin", toolTip: "Pin View to Trail", btnType: ButtonType.ClickButton, expertMode: false, width: 30, scripts: { onClick: 'pinWithView(altKey)'}, funcs: {hidden: "IsNoneSelected()"}},
- { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, expertMode: true, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}},
+ { title: "Header", icon: "heading", toolTip: "Doc Titlebar Color", btnType: ButtonType.ColorButton, expertMode: true, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'} },
{ title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, width: 30, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}}, // Only when a document is selected
{ title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'}, scripts: { onClick: '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform
{ title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 30, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index dc852596f..f75322905 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -7,7 +7,7 @@ import { BsGoogle } from 'react-icons/bs';
import { FaFillDrip, FaPalette } from 'react-icons/fa';
import { Doc } from '../../fields/Doc';
import { DashVersion } from '../../fields/DocSymbols';
-import { BoolCast, Cast, StrCast } from '../../fields/Types';
+import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types';
import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { DocServer } from '../DocServer';
@@ -269,6 +269,19 @@ export class SettingsManager extends React.Component<{}> {
size={Size.XSMALL}
color={SettingsManager.userColor}
/>
+ <Group formLabel="Title Height">
+ <NumberDropdown
+ number={NumCast(Doc.UserDoc().headerHeight, 30)}
+ color={SettingsManager.userColor}
+ numberDropdownType={'slider'}
+ min={6}
+ max={60}
+ step={2}
+ type={Type.TERT}
+ unit={'px'}
+ setNumber={val => console.log('GOT: ' + (Doc.UserDoc().headerHeight = val))}
+ />
+ </Group>
</div>
);
}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index f40d2ae8b..9dafb12fb 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -944,6 +944,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
key="rad"
className="documentDecorations-borderRadius"
style={{
+ opacity: 0.5,
background: `${this._isRounding ? Colors.MEDIUM_BLUE : SettingsManager.userColor}`,
transform: `translate(${radiusHandleLocation ?? 0}px, ${(radiusHandleLocation ?? 0) + (this._showNothing ? 0 : this._titleHeight)}px)`,
}}
diff --git a/src/client/views/EditableView.scss b/src/client/views/EditableView.scss
index f7c03caf9..27b260450 100644
--- a/src/client/views/EditableView.scss
+++ b/src/client/views/EditableView.scss
@@ -3,7 +3,7 @@
overflow-wrap: break-word;
word-wrap: break-word;
hyphens: auto;
- overflow: auto;
+ overflow: hidden;
height: 100%;
min-width: 20;
text-overflow: ellipsis;
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index ca4ffaf3a..ed7c544fc 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -283,11 +283,24 @@ export class EditableView extends React.Component<EditableProps> {
<div
className={`editableView-container-editing${this.props.oneLine ? '-oneLine' : ''}`}
ref={this._ref}
- style={{ display: this.props.display, textOverflow: this.props.overflow, minHeight: '10px', whiteSpace: this.props.oneLine ? 'nowrap' : 'pre-line', height: this.props.height, maxHeight: this.props.maxHeight }}
+ style={{
+ display: this.props.display, //
+ textOverflow: this.props.overflow,
+ minHeight: '10px',
+ whiteSpace: this.props.oneLine ? 'nowrap' : 'pre-line',
+ height: this.props.height,
+ maxHeight: this.props.maxHeight,
+ }}
//onPointerDown={this.stopPropagation}
onClick={this.onClick}
placeholder={this.props.placeholder}>
- <span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize }}>{this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()}</span>
+ <span
+ style={{
+ fontStyle: this.props.fontStyle,
+ fontSize: this.props.fontSize,
+ }}>
+ {this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()}
+ </span>
</div>
);
}
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 0df88f970..cb5c9b085 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -7,7 +7,7 @@ import { CiCircleRemove } from 'react-icons/ci';
import Select from 'react-select';
import { Doc, DocListCast, Field, LinkedTo, StrListCast } from '../../fields/Doc';
import { RichTextField } from '../../fields/RichTextField';
-import { DocumentOptions, FInfo } from '../documents/Documents';
+import { DocOptions, DocumentOptions, FInfo } from '../documents/Documents';
import { DocumentManager } from '../util/DocumentManager';
import { UserOptions } from '../util/GroupManager';
import { SearchUtil } from '../util/SearchUtil';
@@ -18,6 +18,7 @@ import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components';
import { SettingsManager } from '../util/SettingsManager';
import { Id } from '../../fields/FieldSymbols';
import { List } from '../../fields/List';
+import { emptyFunction } from '../../Utils';
interface filterProps {
rootDoc: Doc;
@@ -25,8 +26,6 @@ interface filterProps {
@observer
export class FilterPanel extends React.Component<filterProps> {
- private _documentOptions: DocumentOptions = new DocumentOptions();
-
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(FilterPanel, fieldKey);
}
@@ -51,12 +50,10 @@ export class FilterPanel extends React.Component<filterProps> {
if (targetDoc) {
SearchUtil.foreachRecursiveDoc([this.targetDoc], (depth, doc) => allDocs.add(doc));
}
- console.log('this is all Docs' + Array.from(allDocs));
return Array.from(allDocs);
}
@computed get _allFacets() {
- // trace();
const noviceReqFields = ['author', 'tags', 'text', 'type', LinkedTo];
const noviceLayoutFields: string[] = []; //["_layout_curPage"];
const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
@@ -68,11 +65,8 @@ export class FilterPanel extends React.Component<filterProps> {
.filter(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode)
.sort();
- // console.log('THIS IS HERE ' + Doc.UserDoc().color + 'space ' + Doc.UserDoc().color);
noviceFields.forEach(key => sortedKeys.splice(sortedKeys.indexOf(key), 1));
- console.log('this is novice fields ' + noviceFields + 'and this is sorted Keys ' + sortedKeys);
-
return [...noviceFields, ...sortedKeys];
}
@@ -206,13 +200,7 @@ export class FilterPanel extends React.Component<filterProps> {
*/
@action
- facetClick = (facetHeader: string) => {
- // just when someone chooses a facet
-
- this._selectedFacetHeaders.add(facetHeader);
-
- return;
- };
+ facetClick = (facetHeader: string) => this._selectedFacetHeaders.add(facetHeader);
@action
sortingCurrentFacetValues = (facetHeader: string) => {
@@ -260,57 +248,59 @@ export class FilterPanel extends React.Component<filterProps> {
return nonNumbers / facetValues.length > 0.1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2));
};
- render() {
- let filteredOptions: string[] = ['author', 'tags', 'text', 'acl-Guest', ...this._allFacets.filter(facet => facet[0] === facet.charAt(0).toUpperCase())];
+ @computed get fieldsDropdown() {
+ const filteredOptions = ['author', 'tags', 'text', 'acl-Guest', ...this._allFacets.filter(facet => facet[0] === facet.charAt(0).toUpperCase())];
- Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => {
- if (pair[1].filterable) {
- filteredOptions.push(pair[0]);
- }
- });
+ Object.entries(DocOptions)
+ .filter(opts => opts[1].filterable)
+ .forEach((pair: [string, FInfo]) => filteredOptions.push(pair[0]));
+ const options = filteredOptions.map(facet => ({ value: facet, label: facet }));
- let options = filteredOptions.map(facet => ({ value: facet, label: facet }));
+ return (
+ <Select
+ styles={{
+ control: (baseStyles, state) => ({
+ ...baseStyles,
+ color: SettingsManager.userColor,
+ background: SettingsManager.userBackgroundColor,
+ }),
+ placeholder: (baseStyles, state) => ({
+ ...baseStyles,
+ color: SettingsManager.userColor,
+ background: SettingsManager.userBackgroundColor,
+ }),
+ input: (baseStyles, state) => ({
+ ...baseStyles,
+ color: SettingsManager.userColor,
+ background: SettingsManager.userBackgroundColor,
+ }),
+ option: (baseStyles, state) => ({
+ ...baseStyles,
+ color: SettingsManager.userColor,
+ background: !state.isFocused ? SettingsManager.userBackgroundColor : SettingsManager.userVariantColor,
+ }),
+ menuList: (baseStyles, state) => ({
+ ...baseStyles,
+ backgroundColor: SettingsManager.userBackgroundColor,
+ }),
+ }}
+ placeholder={'add a filter'}
+ options={options}
+ isMulti={false}
+ onChange={val => this.facetClick((val as UserOptions).value)}
+ onKeyDown={e => e.stopPropagation()}
+ //onMenuClose={onClose}
+ value={null}
+ closeMenuOnSelect={true}
+ />
+ );
+ }
+ render() {
return (
<div className="filterBox-treeView">
<div className="filterBox-select">
- <div style={{ width: '100%' }}>
- <Select
- styles={{
- control: (baseStyles, state) => ({
- ...baseStyles,
- color: SettingsManager.userColor,
- background: SettingsManager.userBackgroundColor,
- }),
- placeholder: (baseStyles, state) => ({
- ...baseStyles,
- color: SettingsManager.userColor,
- background: SettingsManager.userBackgroundColor,
- }),
- input: (baseStyles, state) => ({
- ...baseStyles,
- color: SettingsManager.userColor,
- background: SettingsManager.userBackgroundColor,
- }),
- option: (baseStyles, state) => ({
- ...baseStyles,
- color: SettingsManager.userColor,
- background: !state.isFocused ? SettingsManager.userBackgroundColor : SettingsManager.userVariantColor,
- }),
- menuList: (baseStyles, state) => ({
- ...baseStyles,
- backgroundColor: SettingsManager.userBackgroundColor,
- }),
- }}
- placeholder="Add a filter..."
- options={options}
- isMulti={false}
- onChange={val => this.facetClick((val as UserOptions).value)}
- onKeyDown={e => e.stopPropagation()}
- value={null}
- closeMenuOnSelect={true}
- />
- </div>
+ <div style={{ width: '100%' }}>{this.fieldsDropdown}</div>
{/* THE FOLLOWING CODE SHOULD BE DEVELOPER FOR BOOLEAN EXPRESSION (AND / OR) */}
{/* <div className="filterBox-select-bool">
<select className="filterBox-selection" onChange={action(e => this.targetDoc && (this.targetDoc._childFilters_boolean = (e.target as any).value))} defaultValue={StrCast(this.targetDoc?.childFilters_boolean)}>
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 48c96d064..5ff743a30 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -90,6 +90,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
const boxBackground = property.includes(':box');
const fieldKey = props?.fieldKey ? props.fieldKey + '_' : isCaption ? 'caption_' : '';
const lockedPosition = () => doc && BoolCast(doc._lockedPosition);
+ const titleHeight = () => props?.styleProvider?.(doc, props, StyleProp.TitleHeight);
const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor);
const color = () => props?.styleProvider?.(doc, props, StyleProp.Color);
const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity);
@@ -137,7 +138,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
case StyleProp.FontWeight: return StrCast(doc?.[fieldKey + 'fontWeight'], StrCast(doc?._text_fontWeight, StrCast(Doc.UserDoc().fontWeight)));
case StyleProp.FillColor: return StrCast(doc?._fillColor, StrCast(doc?.fillColor, 'transparent'));
case StyleProp.ShowCaption:return doc?._type_collection === CollectionViewType.Carousel || props?.hideCaptions ? undefined : StrCast(doc?._layout_showCaption);
- case StyleProp.TitleHeight:return 15;
+ case StyleProp.TitleHeight:return (props?.ScreenToLocalTransform().Scale ?? 1)*(props?.NativeDimScaling?.()??1) * NumCast(Doc.UserDoc().headerHeight,30)
case StyleProp.ShowTitle:
return (
(doc &&
@@ -189,7 +190,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
doc?.type === DocumentType.LABEL) &&
layout_showTitle() &&
!StrCast(doc?.layout_showTitle).includes(':hover')
- ? 15
+ ? titleHeight()
: 0;
case StyleProp.BackgroundColor: {
if (DocumentView.LastPressedSidebarBtn === doc) return SettingsManager.userColor; // hack to indicate active menu panel item
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index d8ed93bd7..ced11447c 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -68,11 +68,18 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
// toggle: Set overlay status of selected document
ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
if (checkResult) {
- return Doc.SharingDoc().headingColor;
+ return SelectionManager.Views().length ? StrCast(SelectionManager.Docs().lastElement().layout_headingColor) : Doc.SharingDoc().headingColor;
+ }
+ if (SelectionManager.Views().length) {
+ SelectionManager.Docs().forEach(doc => {
+ Doc.GetProto(doc).layout_headingColor = color;
+ doc.layout_showTitle = color === 'transparent' ? undefined : StrCast(doc.layout_showTitle, 'title');
+ });
+ } else {
+ Doc.SharingDoc().headingColor = undefined;
+ Doc.GetProto(Doc.SharingDoc()).headingColor = color;
+ Doc.UserDoc().layout_showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().layout_showTitle, 'author_date');
}
- Doc.SharingDoc().headingColor = undefined;
- Doc.GetProto(Doc.SharingDoc()).headingColor = color;
- Doc.UserDoc().layout_showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().layout_showTitle, 'author_date');
});
// toggle: Set overlay status of selected document
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;