diff options
author | Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> | 2024-08-25 04:26:12 -0400 |
---|---|---|
committer | Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> | 2024-08-25 04:26:12 -0400 |
commit | f5c2f5bc8710a965a0b52e2e414f62a2a459a94f (patch) | |
tree | 558c2125c3bd4e3cfb004552691cd482b98bfab0 /src | |
parent | 35724d24821132ab5c6df398447db4e9760155cb (diff) |
border work
Diffstat (limited to 'src')
-rw-r--r-- | src/client/documents/Documents.ts | 2 | ||||
-rw-r--r-- | src/client/views/ContextMenu.scss | 35 | ||||
-rw-r--r-- | src/client/views/ContextMenu.tsx | 118 | ||||
-rw-r--r-- | src/client/views/ContextMenuItem.tsx | 12 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/PropertiesView.tsx | 10 | ||||
-rw-r--r-- | src/client/views/StyleProvider.tsx | 37 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 37 |
8 files changed, 191 insertions, 66 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 751fe6d91..aecc79189 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -256,6 +256,8 @@ export class DocumentOptions { layout_hideContextMenu?: BOOLt = new BoolInfo('whether the context menu can be shown'); layout_borderRounding?: string; _layout_borderRounding?: STRt = new StrInfo('amount of rounding to document view corners'); + _layout_borderWidth?: NUMt = new NumInfo('Hey now', false); + _layout_borderColor?: STRt = new StrInfo('No no', false); _layout_modificationDate?: DATEt = new DateInfo('last modification date of doc layout', false); _layout_nativeDimEditable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers', false); _layout_reflowVertical?: BOOLt = new BoolInfo('permit vertical resizing with content "reflow"'); diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index 232362c5c..ff6ad526e 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -165,3 +165,38 @@ border: solid black 1px; border-radius: 5px; } + +.contextMenu-borderMenu { + display: flex; + flex-direction: column; + align-items: flex-start; + width: 222px; + height: 300px; + background-color: white; + border: solid 1px black; + + .top-bar { + height: 20px; + width: 100%; + display: flex; + + .close-menu { + margin-top: 0; + margin-bottom: 0; + margin-right: 0; + padding: 0; + margin-left: auto; + z-index: 999999999; + width: 20px; + height: 20px; + color: black; + background-color: transparent; + } + } + + .bottom-box{ + .width-selector{ + + } + } +} diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index de985263d..9472fb95c 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -5,11 +5,17 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, IReactionDisposer, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { DivHeight, DivWidth } from '../../ClientUtils'; +import { DivHeight, DivWidth, returnFalse, setupMoveUpEvents } from '../../ClientUtils'; import { SnappingManager } from '../util/SnappingManager'; import './ContextMenu.scss'; import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from './ContextMenuItem'; import { ObservableReactComponent } from './ObservableReactComponent'; +import { ColorResult, SketchPicker } from 'react-color'; +import { DocumentView } from './nodes/DocumentView'; +import { Doc } from '../../fields/Doc'; +import { undoable } from '../util/UndoManager'; +import { NumCast, StrCast } from '../../fields/Types'; +import { emptyFunction } from '../../Utils'; @observer export class ContextMenu extends ObservableReactComponent<{}> { @@ -38,6 +44,8 @@ export class ContextMenu extends ObservableReactComponent<{}> { @observable _mouseX: number = -1; @observable _mouseY: number = -1; @observable _shouldDisplay: boolean = false; + @observable _displayColorPicker: boolean = false; + @observable _selectorWidth: number = 0; constructor(props: any) { super(props); @@ -116,6 +124,10 @@ export class ContextMenu extends ObservableReactComponent<{}> { this._defaultItem = item; } + @action setColorPickerDisplay = (display: boolean) => { + this._displayColorPicker = display; + } + static readonly buffer = 20; get pageX() { return this._pageX + this._width > window.innerWidth - ContextMenu.buffer ? window.innerWidth - ContextMenu.buffer - this._width : Math.max(0, this._pageX); @@ -206,45 +218,79 @@ export class ContextMenu extends ObservableReactComponent<{}> { _searchRef = React.createRef<HTMLInputElement>(); // bcz: we shouldn't need this, since we set autoFocus on the <input> tag, but for some reason we do... + get colorPicker() { + const doc = DocumentView.Selected().lastElement().Document; + const layoutDoc = doc ? Doc.Layout(doc) : doc; + + return ( + <div className='contextMenu-borderMenu' style={{position: 'absolute', right: `${this._pageX}`, top: `${this._pageY}`}}> + <div className='top-bar'> + <button className='close-menu' onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, + undoable(clickEv => { + clickEv.stopPropagation(); + clickEv.preventDefault(); + this.setColorPickerDisplay(false); + }, 'close border menu'))}> + <FontAwesomeIcon icon='minus'/> + </button> + </div> + <SketchPicker + onChange={undoable( + action((col: ColorResult) => layoutDoc._layout_borderColor = col.hex), + 'set stroke color property' + )} + presetColors={[]} + color={StrCast(layoutDoc._layout_borderColor)} + /> + <div className='bottom-box'> + <input className='width-selector' type="range" min="1" max="100" value={this._selectorWidth} id="myRange" onChange={e => {layoutDoc._layout_borderWidth = e.target.value; this._selectorWidth = Number(e.target.value); console.log(layoutDoc._layout_borderWidth)}}/> + </div> + </div> + ); + } + render() { this.itemsNeedSearch && setTimeout(() => this._searchRef.current?.focus()); return ( - <div - className="contextMenu-cont" - ref={action((r: any) => { - if (r) { - this._width = DivWidth(r); - this._height = DivHeight(r); - } - this._searchRef.current?.focus(); - })} - style={{ - display: this._display ? '' : 'none', - left: this.pageX, - ...(this._yRelativeToTop ? { top: Math.max(0, this.pageY) } : { bottom: this.pageY }), - background: SnappingManager.userBackgroundColor, - color: SnappingManager.userColor, - }}> - {!this.itemsNeedSearch ? null : ( - <span className="search-icon"> - <span className="icon-background"> - <FontAwesomeIcon icon="search" size="lg" /> + <div> + {this._displayColorPicker ? this.colorPicker : null} + <div + className="contextMenu-cont" + ref={action((r: any) => { + if (r) { + this._width = DivWidth(r); + this._height = DivHeight(r); + } + this._searchRef.current?.focus(); + })} + style={{ + display: this._display ? '' : 'none', + left: this.pageX, + ...(this._yRelativeToTop ? { top: Math.max(0, this.pageY) } : { bottom: this.pageY }), + background: SnappingManager.userBackgroundColor, + color: SnappingManager.userColor, + }}> + {!this.itemsNeedSearch ? null : ( + <span className="search-icon"> + <span className="icon-background"> + <FontAwesomeIcon icon="search" size="lg" /> + </span> + <input + ref={this._searchRef} + style={{ color: 'black' }} + className="contextMenu-item contextMenu-description search" + type="text" + placeholder="Filter Menu..." + value={this._searchString} + onKeyDown={this.onKeyDown} + onChange={this.onChange} + // eslint-disable-next-line jsx-a11y/no-autofocus + autoFocus + /> </span> - <input - ref={this._searchRef} - style={{ color: 'black' }} - className="contextMenu-item contextMenu-description search" - type="text" - placeholder="Filter Menu..." - value={this._searchString} - onKeyDown={this.onKeyDown} - onChange={this.onChange} - // eslint-disable-next-line jsx-a11y/no-autofocus - autoFocus - /> - </span> - )} - {this.menuItems} + )} + {this.menuItems} + </div> </div> ); } diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index eb1030eec..782077fd6 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -5,8 +5,14 @@ import { action, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { SnappingManager } from '../util/SnappingManager'; -import { UndoManager } from '../util/UndoManager'; +import { UndoManager, undoable } from '../util/UndoManager'; import { ObservableReactComponent } from './ObservableReactComponent'; +import { ColorPicker, Type } from 'browndash-components'; +import { DocumentView } from './nodes/DocumentView'; +import { Doc } from '../../fields/Doc'; +import { StrCast } from '../../fields/Types'; +import { ColorResult, SketchPicker } from 'react-color'; +import color from 'color'; export interface OriginalMenuProps { description: string; @@ -14,6 +20,7 @@ export interface OriginalMenuProps { undoable?: boolean; icon: IconProp | JSX.Element; // maybe should be optional (icon?) closeMenu?: () => void; + colorPicker?: boolean; } export interface SubmenuProps { @@ -24,6 +31,7 @@ export interface SubmenuProps { addDivider?: boolean; icon: IconProp; // maybe should be optional (icon?) closeMenu?: () => void; + colorPicker?: boolean; } export type ContextMenuProps = OriginalMenuProps | SubmenuProps; @@ -101,7 +109,7 @@ export class ContextMenuItem extends ObservableReactComponent<ContextMenuProps & render() { if ('event' in this._props) { return ( - <div className={'contextMenu-item' + (this._props.selected ? ' contextMenu-itemSelected' : '')} onPointerDown={this.handleEvent}> + <div className={'contextMenu-item' + (this._props.selected ? ' contextMenu-itemSelected' : '')} onPointerDown={this.handleEvent} > {this._props.icon ? <span className="contextMenu-item-icon-background">{this.isJSXElement(this._props.icon) ? this._props.icon : <FontAwesomeIcon icon={this._props.icon} size="sm" />}</span> : null} <div className="contextMenu-description">{this._props.description.replace(':', '')}</div> <div diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d4467e536..9c350afc0 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -88,6 +88,7 @@ export class MainView extends ObservableReactComponent<{}> { public static Live: boolean = false; private _docBtnRef = React.createRef<HTMLDivElement>(); + @observable private _keepContextMenuOpen: boolean = false; @observable private _windowWidth: number = 0; @observable private _windowHeight: number = 0; @observable private _dashUIWidth: number = 0; // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row) @@ -590,7 +591,10 @@ export class MainView extends ObservableReactComponent<{}> { if (typeof targets[i].className === 'object') targClass = targets[i + 1].className.toString(); else break; } - !targClass.includes('contextMenu') && ContextMenu.Instance.closeMenu(); + if (!targClass.includes('contextMenu')) { + console.log(targClass) + ContextMenu.Instance.closeMenu(); + } !['timeline-menu-desc', 'timeline-menu-item', 'timeline-menu-input'].includes(targClass) && TimelineMenu.Instance.closeMenu(); } }); diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 024db82a4..775e15772 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -847,6 +847,12 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps set colorFil(value) { this.selectedDoc && (this.selectedDoc[DocData].fillColor = value || undefined); } // prettier-ignore @computed get colorStk() { return StrCast(this.selectedDoc?.[DocData].color); } // prettier-ignore set colorStk(value) { this.selectedDoc && (this.selectedDoc[DocData].color = value || undefined); } // prettier-ignore + @computed get borderColor() { + const doc = this.selectedDoc; + const layoutDoc = doc ? Doc.Layout(doc) : doc; + return StrCast(layoutDoc); + } + set borderColor(value) { this.selectedDoc && (this.selectedDoc[DocData].color = value || undefined); } // prettier-ignore colorButton(value: string, type: string, setter: () => void) { return ( @@ -898,6 +904,10 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps return this.colorPicker(this.colorStk, (color: string) => { this.colorStk = color; }); // prettier-ignore } + @computed get borderColorPicker() { + return this.colorPicker(this.colorStk, (color: string) => { this.colorStk = color; }); // prettier-ignore + } + @computed get strokeAndFill() { return ( <div> diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 8c100f238..3a48ec957 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -55,14 +55,25 @@ export function styleFromLayoutString(doc: Doc, props: FieldViewProps, scale: nu return style; } -export function wavyBorderPath(pw: number, ph: number, inset: number = 0.05) { - return `M ${pw * 0.5} ${ph * inset} C ${pw * 0.6} ${ph * inset} ${pw * (1 - 2 * inset)} 0 ${pw * (1 - inset)} ${ph * inset} C ${pw} ${ph * (2 * inset)} ${pw * (1 - inset)} ${ph * 0.25} ${pw * (1 - inset)} ${ph * 0.3} C ${ - pw * (1 - inset) - } ${ph * 0.4} ${pw} ${ph * (1 - 2 * inset)} ${pw * (1 - inset)} ${ph * (1 - inset)} C ${pw * (1 - 2 * inset)} ${ph} ${pw * 0.6} ${ph * (1 - inset)} ${pw * 0.5} ${ph * (1 - inset)} C ${pw * 0.3} ${ph * (1 - inset)} ${pw * (2 * inset)} ${ph} ${ - pw * inset - } ${ph * (1 - inset)} C 0 ${ph * (1 - 2 * inset)} ${pw * inset} ${ph * 0.8} ${pw * inset} ${ph * 0.75} C ${pw * inset} ${ph * 0.7} 0 ${ph * (2 * inset)} ${pw * inset} ${ph * inset} C ${pw * (2 * inset)} 0 ${pw * 0.25} ${ph * inset} ${ - pw * 0.5 - } ${ph * inset}`; +export function wavyBorderPath(doc: Doc, pw: number, ph: number, inset: number = 0) { + const layoutDoc = doc ? Doc.Layout(doc) : doc; + const width = pw * inset; + const height = ph * inset; + + const radius = Math.min(Number(StrCast(layoutDoc._layout_borderRounding).replace('px', '')), (pw - 2 * width) / 2, (ph - 2 * height) / 2); + + return ` + M ${width + radius} ${height} + L ${pw - width - radius} ${height} + A ${radius} ${radius} 0 0 1 ${pw - width} ${height + radius} + L ${pw - width} ${ph - height - radius} + A ${radius} ${radius} 0 0 1 ${pw - width - radius} ${ph - height} + L ${width + radius} ${ph - height} + A ${radius} ${radius} 0 0 1 ${width} ${ph - height - radius} + L ${width} ${height + radius} + A ${radius} ${radius} 0 0 1 ${width + radius} ${height} + Z + `; } let _filterOpener: () => void; @@ -189,10 +200,12 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & const rounding = StrCast(doc?.[fieldKey + 'borderRounding'], StrCast(doc?.layout_borderRounding, doc?._type_collection === CollectionViewType.Pile ? '50%' : '')); return (doc?.[StrCast(doc?.layout_fieldKey)] instanceof Doc || doc?.isTemplateDoc) ? StrCast(doc._layout_borderRounding,rounding) : rounding; } + // Doc.IsComicStyle(doc) && + // renderDepth && + // !doc?.layout_isSvg && + //case StyleProp. case StyleProp.BorderPath: { - const borderPath = Doc.IsComicStyle(doc) && - renderDepth && - !doc?.layout_isSvg && { path: wavyBorderPath(PanelWidth?.() || 0, PanelHeight?.() || 0), fill: wavyBorderPath(PanelWidth?.() || 0, PanelHeight?.() || 0, 0.08), width: 3 }; + const borderPath = doc?.type === 'image' && { path: wavyBorderPath(doc, NumCast(doc._width), NumCast(doc._height), NumCast(layoutDoc?._layout_borderWidth)/2), fill: wavyBorderPath(doc, NumCast(doc._width), NumCast(doc._height), 0.08), width: 3 }; return !borderPath ? null : { @@ -200,7 +213,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & jsx: ( <div key="border2" className="documentView-customBorder" style={{ pointerEvents: 'none' }}> <svg style={{ overflow: 'visible', height: '100%' }} viewBox={`0 0 ${PanelWidth?.()} ${PanelHeight?.()}`}> - <path d={borderPath.path} style={{ stroke: 'black', fill: 'transparent', strokeWidth: borderPath.width }} /> + <path d={borderPath.path} style={{ stroke: StrCast(layoutDoc?._layout_borderColor), fill: 'transparent', strokeWidth: `${StrCast(layoutDoc?._layout_borderWidth)}px` }} /> </svg> </div> ), diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 8edb37acc..fe6d529ba 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -634,6 +634,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document moreItems.push({ description: 'Make View of Metadata Field', event: () => Doc.MakeMetadataFieldTemplate(this.Document, this._props.TemplateDataDocument), icon: 'concierge-bell' }); moreItems.push({ description: `${this.Document._chromeHidden ? 'Show' : 'Hide'} Chrome`, event: () => { this.Document._chromeHidden = !this.Document._chromeHidden; }, icon: 'project-diagram' }); // prettier-ignore moreItems.push({ description: 'Copy ID', event: () => ClientUtils.CopyText(Doc.globalServerPath(this.Document)), icon: 'fingerprint' }); + moreItems.push({ description: 'Add border', event: () => ContextMenu.Instance.setColorPickerDisplay(true), icon: 'square', colorPicker: true }); } } @@ -944,22 +945,28 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document return ( // eslint-disable-next-line jsx-a11y/click-events-have-key-events + // <div style={{position: 'relative'}}> + // <div className='docView-border-top' style={{width: `${this.layoutDoc._width}px`, height: '100px', backgroundColor: 'black'}}/> + // <div className='docView-border-right' style={{position: 'absolute', right: '-10px', width: '10px', height: `${this.layoutDoc._height}px`, backgroundColor: 'black'}}/> + // <div className='docView-border-left' style={{position: 'absolute', left: '-10px', width: '10px', height: `${this.layoutDoc._height}px`, backgroundColor: 'black'}}/> + // <div className='docView-border-bottom' style={{position: 'absolute', bottom: '-10px', height: '100px', width: `${this.layoutDoc._width}px`, backgroundColor: 'black'}}/> <div - className={`${DocumentView.ROOT_DIV} docView-hack`} - ref={this._mainCont} - onContextMenu={this.onContextMenu} - onPointerDown={this.onPointerDown} - onClick={SnappingManager.ExploreMode ? this.onBrowseClick : this.onClick} - onPointerEnter={() => (!SnappingManager.IsDragging || SnappingManager.CanEmbed) && Doc.BrushDoc(this.Document)} - onPointerOver={() => (!SnappingManager.IsDragging || SnappingManager.CanEmbed) && Doc.BrushDoc(this.Document)} - onPointerLeave={e => !isParentOf(this._contentDiv, document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y)) && Doc.UnBrushDoc(this.Document)} - style={{ - borderRadius: this.borderRounding, - pointerEvents: this._pointerEvents === 'visiblePainted' ? 'none' : this._pointerEvents, // visible painted means that the underlying doc contents are irregular and will process their own pointer events (otherwise, the contents are expected to fill the entire doc view box so we can handle pointer events here) - }}> - {this._componentView?.isUnstyledView?.() || this.Document.type === DocumentType.CONFIG ? renderDoc : DocumentViewInternal.AnimationEffect(renderDoc, this.Document[Animation], this.Document)} - {borderPath?.jsx} - </div> + className={`${DocumentView.ROOT_DIV} docView-hack`} + ref={this._mainCont} + onContextMenu={this.onContextMenu} + onPointerDown={this.onPointerDown} + onClick={SnappingManager.ExploreMode ? this.onBrowseClick : this.onClick} + onPointerEnter={() => (!SnappingManager.IsDragging || SnappingManager.CanEmbed) && Doc.BrushDoc(this.Document)} + onPointerOver={() => (!SnappingManager.IsDragging || SnappingManager.CanEmbed) && Doc.BrushDoc(this.Document)} + onPointerLeave={e => !isParentOf(this._contentDiv, document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y)) && Doc.UnBrushDoc(this.Document)} + style={{ + borderRadius: this.borderRounding, + pointerEvents: this._pointerEvents === 'visiblePainted' ? 'none' : this._pointerEvents, // visible painted means that the underlying doc contents are irregular and will process their own pointer events (otherwise, the contents are expected to fill the entire doc view box so we can handle pointer events here) + }}> + {this._componentView?.isUnstyledView?.() || this.Document.type === DocumentType.CONFIG ? renderDoc : DocumentViewInternal.AnimationEffect(renderDoc, this.Document[Animation], this.Document)} + {borderPath?.jsx} + </div> + // </div> ); } |