diff options
author | bobzel <zzzman@gmail.com> | 2025-01-28 12:02:37 -0500 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2025-01-28 12:02:37 -0500 |
commit | 519a28c4ef5b5a70d29377c9baed50b455459ebd (patch) | |
tree | d637caf13fa0dc9b167b9ec45715915202a92bf5 | |
parent | a78901127cd8af401146df47595442c4d8a696f2 (diff) |
card view cleanup. moved 'pile' into shiftclick on Perspective dropdown. fixed tags sorting.
-rw-r--r-- | packages/components/src/components/Dropdown/Dropdown.tsx | 371 | ||||
-rw-r--r-- | packages/components/src/components/ListBox/ListBox.tsx | 90 | ||||
-rw-r--r-- | packages/components/src/components/ListItem/ListItem.tsx | 197 | ||||
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 4 | ||||
-rw-r--r-- | src/client/views/animationtimeline/Timeline.tsx | 14 | ||||
-rw-r--r-- | src/client/views/collections/CollectionCardDeckView.tsx | 3 | ||||
-rw-r--r-- | src/client/views/global/globalScripts.ts | 36 | ||||
-rw-r--r-- | src/client/views/nodes/FontIconBox/FontIconBox.tsx | 9 |
8 files changed, 313 insertions, 411 deletions
diff --git a/packages/components/src/components/Dropdown/Dropdown.tsx b/packages/components/src/components/Dropdown/Dropdown.tsx index d9fec5e9d..0953f412c 100644 --- a/packages/components/src/components/Dropdown/Dropdown.tsx +++ b/packages/components/src/components/Dropdown/Dropdown.tsx @@ -1,31 +1,32 @@ -import React, { useEffect, useState } from 'react' -import { FaCaretDown, FaCaretLeft, FaCaretRight, FaCaretUp } from 'react-icons/fa' -import { Popup, PopupTrigger } from '..' -import { Colors, IGlobalProps, Placement, Type, getFontSize, getHeight, isDark , getFormLabelSize } from '../../global' -import { IconButton } from '../IconButton' -import { ListBox } from '../ListBox' -import { IListItemProps, ListItem } from '../ListItem' -import './Dropdown.scss' -import { Tooltip } from '@mui/material' +import React, { useState } from 'react'; +import { FaCaretDown, FaCaretLeft, FaCaretRight, FaCaretUp } from 'react-icons/fa'; +import { Popup, PopupTrigger } from '..'; +import { Colors, IGlobalProps, Placement, Type, getFontSize, getHeight, isDark, getFormLabelSize } from '../../global'; +import { IconButton } from '../IconButton'; +import { ListBox } from '../ListBox'; +import { IListItemProps, ListItem } from '../ListItem'; +import './Dropdown.scss'; +import { Tooltip } from '@mui/material'; export enum DropdownType { - SELECT = "select", - CLICK = "click" + SELECT = 'select', + CLICK = 'click', } export interface IDropdownProps extends IGlobalProps { - items: IListItemProps[] - placement?: Placement - dropdownType: DropdownType - title?: string - closeOnSelect?: boolean; - iconProvider?: (active:boolean, placement?:Placement) => JSX.Element, - selectedVal?: string, - setSelectedVal?: (val: string | number) => unknown, - maxItems?: number, - uppercase?: boolean, - activeChanged?: (isOpen:boolean) => void, - onItemDown?: (e:React.PointerEvent, val:number | string) => boolean, // returns whether to select item + items: IListItemProps[]; + placement?: Placement; + dropdownType: DropdownType; + title?: string; + toolTip?: string; + closeOnSelect?: boolean; + iconProvider?: (active: boolean, placement?: Placement) => JSX.Element; + selectedVal?: string; + setSelectedVal?: (val: string | number, e?: React.MouseEvent) => unknown; + maxItems?: number; + uppercase?: boolean; + activeChanged?: (isOpen: boolean) => void; + onItemDown?: (e: React.PointerEvent, val: number | string) => boolean; // returns whether to select item } /** @@ -37,189 +38,159 @@ export interface IDropdownProps extends IGlobalProps { * Look at: import Select from "react-select"; */ export const Dropdown = (props: IDropdownProps) => { - const { - size, - height, - maxItems, - items, - dropdownType, - selectedVal, - setSelectedVal, - iconProvider, - placement = 'bottom-start', - tooltip, - tooltipPlacement = 'top', - inactive, - color = Colors.MEDIUM_BLUE, - background, - closeOnSelect, - title = "Dropdown", - type, - width, - formLabel, - formLabelPlacement, - fillWidth = true, - onItemDown, - uppercase - } = props + const { + size, + height, + maxItems, + items, + dropdownType, + selectedVal, + toolTip, + setSelectedVal, + iconProvider, + placement = 'bottom-start', + tooltip, + tooltipPlacement = 'top', + inactive, + color = Colors.MEDIUM_BLUE, + background, + closeOnSelect, + title = 'Dropdown', + type, + width, + formLabel, + formLabelPlacement, + fillWidth = true, + onItemDown, + uppercase, + } = props; - const [active, setActive] = useState<boolean>(false) - const itemsMap = new Map(); - items.forEach((item) => { - itemsMap.set(item.val, item) - }) + const [active, setActive] = useState<boolean>(false); + const itemsMap = new Map(); + items.forEach(item => { + itemsMap.set(item.val, item); + }); - const getBorderColor = (): Colors | string | undefined => { - switch(type){ - case Type.PRIM: - return undefined; - case Type.SEC: - return color; - case Type.TERT: - if (active) return color; - else return color; - } - } + const getBorderColor = (): Colors | string | undefined => { + switch (type) { + case Type.PRIM: + return undefined; + case Type.SEC: + return color; + case Type.TERT: + if (active) return color; + else return color; + } + }; - const defaultProperties: React.CSSProperties = { - height: getHeight(height, size), - width: fillWidth ? '100%' : width, - fontWeight: 500, - fontSize: getFontSize(size), - fontFamily: 'sans-serif', - textTransform: uppercase ? 'uppercase' : undefined, - borderColor: getBorderColor(), - background, - color: color && background? color : type == (Type.TERT) ? isDark(color) ? Colors.WHITE : Colors.BLACK : color - } + const defaultProperties: React.CSSProperties = { + height: getHeight(height, size), + width: fillWidth ? '100%' : width, + fontWeight: 500, + fontSize: getFontSize(size), + fontFamily: 'sans-serif', + textTransform: uppercase ? 'uppercase' : undefined, + borderColor: getBorderColor(), + background, + color: color && background ? color : type == Type.TERT ? (isDark(color) ? Colors.WHITE : Colors.BLACK) : color, + }; - const backgroundProperties: React.CSSProperties = { - background: background ?? color - } + const backgroundProperties: React.CSSProperties = { + background: background ?? color, + }; - const getCaretDirection = (active: boolean, placement:Placement = 'left'): JSX.Element => { - if (iconProvider) return iconProvider(active, placement); - switch (placement) { - case 'bottom': - if (active) return <FaCaretUp/> - return <FaCaretDown/> - case 'right': - if (active) return <FaCaretLeft/> - return <FaCaretRight/> - case 'top': - if (active) return <FaCaretDown/> - return <FaCaretUp/> - default: - if (active) return <FaCaretUp/> - return <FaCaretDown/> - } - } + const getCaretDirection = (active: boolean, placement: Placement = 'left'): JSX.Element => { + if (iconProvider) return iconProvider(active, placement); + switch (placement) { + case 'bottom': + if (active) return <FaCaretUp />; + return <FaCaretDown />; + case 'right': + if (active) return <FaCaretLeft />; + return <FaCaretRight />; + case 'top': + if (active) return <FaCaretDown />; + return <FaCaretUp />; + default: + if (active) return <FaCaretUp />; + return <FaCaretDown />; + } + }; - const getToggle = () => { - switch (dropdownType) { - case DropdownType.SELECT: - return ( - <div - className={`dropdown-toggle${!selectedVal?"-mini":""} ${type} ${inactive && 'inactive'}`} - style={{...defaultProperties, height: getHeight(height, size), width: width }} - > - {selectedVal && ( - <ListItem - size={size} - {...itemsMap.get(selectedVal)} - style={{ color: defaultProperties.color, background: defaultProperties.background}} - inactive - /> - )} - <div className="toggle-caret"> - <IconButton + const getToggle = () => { + switch (dropdownType) { + case DropdownType.SELECT: + return ( + <div className={`dropdown-toggle${!selectedVal ? '-mini' : ''} ${type} ${inactive && 'inactive'}`} style={{ ...defaultProperties, height: getHeight(height, size), width: width }}> + {selectedVal && <ListItem size={size} {...itemsMap.get(selectedVal)} style={{ color: defaultProperties.color, background: defaultProperties.background }} inactive />} + <div className="toggle-caret"> + <IconButton size={size} icon={getCaretDirection(active, placement)} color={defaultProperties.color} inactive /> + </div> + <div className={`background ${active && 'active'}`} style={{ ...backgroundProperties }} /> + </div> + ); + case DropdownType.CLICK: + default: + return ( + <div className={`dropdown-toggle${!selectedVal ? '-mini' : ''} ${type} ${inactive && 'inactive'}`} style={{ ...defaultProperties, height: getHeight(height, size), width: width }}> + <ListItem val="title" text={title} size={size} style={{ color: defaultProperties.color, background: defaultProperties.backdropFilter }} inactive /> + <div className="toggle-caret"> + <IconButton size={size} icon={getCaretDirection(active, placement)} color={defaultProperties.color} inactive /> + </div> + <div className={`background ${active && 'active'}`} style={{ ...backgroundProperties }} /> + </div> + ); + } + }; + + const setActiveChanged = (active: boolean) => { + setActive(active); + props.activeChanged?.(active); + }; + + const dropdown: JSX.Element = ( + <div className="dropdown-container"> + <Popup + toggle={ + <Tooltip disableInteractive={true} arrow={true} placement={tooltipPlacement} title={toolTip || (itemsMap.get(selectedVal)?.text ?? title)}> + {getToggle()} + </Tooltip> + } + placement={placement} + tooltip={tooltip} + tooltipPlacement={tooltipPlacement} + trigger={PopupTrigger.CLICK} + isOpen={active} + setOpen={setActiveChanged} size={size} - icon={getCaretDirection(active,placement)} - color={defaultProperties.color} - inactive - /> - </div> - <div className={`background ${active && 'active'}`} style={{...backgroundProperties}}/> - </div> - ) - case DropdownType.CLICK: - default: - return ( - <div - className={`dropdown-toggle${!selectedVal?"-mini":""} ${type} ${inactive && 'inactive'}`} - style={{...defaultProperties, height: getHeight(height, size), width: width }} - > - <ListItem val='title' - text={title} - size={size} - style={{ color: defaultProperties.color, background: defaultProperties.backdropFilter}} - inactive + fillWidth={true} + color={color} + popup={ + <ListBox + maxItems={maxItems} + items={items} + color={color} + onItemDown={onItemDown} + selectedVal={selectedVal} + setSelectedVal={(val, e) => { + setSelectedVal?.(val, e); + closeOnSelect && setActive(false); + }} + size={size} + /> + } /> - <div className="toggle-caret"> - <IconButton - size={size} - icon={getCaretDirection(active,placement)} - color={defaultProperties.color} - inactive - /> - </div> - <div className={`background ${active && 'active'}`} style={{...backgroundProperties}}/> - </div> - ) - } - } - - const setActiveChanged = (active:boolean) => { - setActive(active); - props.activeChanged?.(active); - } - - const dropdown: JSX.Element = - ( - <div - className="dropdown-container" - > - <Popup - toggle={ - <Tooltip disableInteractive={true} arrow={true} placement={tooltipPlacement} title={itemsMap.get(selectedVal) ? itemsMap.get(selectedVal).text : title}> - {getToggle()} - </Tooltip> - } - placement={placement} - tooltip={tooltip} - tooltipPlacement={tooltipPlacement} - trigger={PopupTrigger.CLICK} - isOpen={active} - setOpen={setActiveChanged} - size={size} - fillWidth={true} - color={color} - popup={ - <ListBox - maxItems={maxItems} - items={items} - color={color} - onItemDown={onItemDown} - selectedVal={selectedVal} - setSelectedVal={val => { - setSelectedVal?.(val); - closeOnSelect && setActive(false); - }} - size={size} - /> - } - /> - </div> - ) + </div> + ); - return ( - formLabel ? - <div className={`form-wrapper ${formLabelPlacement}`} -style={{ width: fillWidth ? '100%' : undefined}}> - <div className={'formLabel'} style={{fontSize: getFormLabelSize(size)}}>{formLabel}</div> - {dropdown} - </div> - : - dropdown - ) -} + return formLabel ? ( + <div className={`form-wrapper ${formLabelPlacement}`} style={{ width: fillWidth ? '100%' : undefined }}> + <div className={'formLabel'} style={{ fontSize: getFormLabelSize(size) }}> + {formLabel} + </div> + {dropdown} + </div> + ) : ( + dropdown + ); +}; diff --git a/packages/components/src/components/ListBox/ListBox.tsx b/packages/components/src/components/ListBox/ListBox.tsx index abdfd38f3..aa5eb6b44 100644 --- a/packages/components/src/components/ListBox/ListBox.tsx +++ b/packages/components/src/components/ListBox/ListBox.tsx @@ -1,15 +1,15 @@ -import React, { ReactText } from 'react' -import { IListItemProps, ListItem } from '../ListItem' -import './ListBox.scss' -import { Colors, IGlobalProps, isDark , getFormLabelSize } from '../../global' +import React from 'react'; +import { IListItemProps, ListItem } from '../ListItem'; +import './ListBox.scss'; +import { Colors, IGlobalProps } from '../../global'; export interface IListBoxProps extends IGlobalProps { - items: IListItemProps[] - filter?: string - selectedVal?: string | number - setSelectedVal?: (val: string | number) => unknown - maxItems?: number - onItemDown?: (e:React.PointerEvent, val:number|string) => void + items: IListItemProps[]; + filter?: string; + selectedVal?: string | number; + setSelectedVal?: (val: string | number, e?: React.MouseEvent) => unknown; + maxItems?: number; + onItemDown?: (e: React.PointerEvent, val: number | string) => void; } /** @@ -21,56 +21,24 @@ export interface IListBoxProps extends IGlobalProps { * Look at: import Select from "react-select"; */ export const ListBox = (props: IListBoxProps) => { - const { - items, - selectedVal, - setSelectedVal, - filter, - onItemDown, - color = Colors.MEDIUM_BLUE - } = props + const { items, selectedVal, setSelectedVal, filter, onItemDown, color = Colors.MEDIUM_BLUE } = props; - const getListItem = ( - item: IListItemProps, - ind: number, - selected: boolean - ): JSX.Element => { + const getListItem = (item: IListItemProps, ind: number, selected: boolean): JSX.Element => { + return <ListItem key={ind} ind={ind} onItemDown={onItemDown} selected={selected} color={color} setSelectedVal={setSelectedVal} onClick={item.onClick} {...item} />; + }; + const itemElements: JSX.Element[] = []; + items.forEach((item, ind) => { + if (filter) { + if (filter.toLowerCase() === item.text?.substring(0, filter.length).toLowerCase()) { + itemElements.push(getListItem(item, ind, item.val === selectedVal)); + } + } else { + itemElements.push(getListItem(item, ind, item.val === selectedVal)); + } + }); return ( - <ListItem - key={ind} - ind={ind} - onItemDown={onItemDown} - selected={selected} - color={color} - setSelectedVal={setSelectedVal} - onClick={item.onClick} - {...item} - /> - ) - } - let itemElements: JSX.Element[] = [] - items.forEach((item, ind) => { - if (filter) { - if ( - filter.toLowerCase() === - item.text?.substring(0, filter.length).toLowerCase() - ) { - itemElements.push( - getListItem(item, ind, item.val === selectedVal) - ) - } - } else { - itemElements.push( - getListItem(item, ind, item.val === selectedVal) - ) - } - }) - return ( - <div - className="listBox-container" - style={{ color: color }} - > - {itemElements} - </div> - ) -} + <div className="listBox-container" style={{ color: color }}> + {itemElements} + </div> + ); +}; diff --git a/packages/components/src/components/ListItem/ListItem.tsx b/packages/components/src/components/ListItem/ListItem.tsx index d76c84b3e..e04c6fbee 100644 --- a/packages/components/src/components/ListItem/ListItem.tsx +++ b/packages/components/src/components/ListItem/ListItem.tsx @@ -1,25 +1,25 @@ -import React, { useState } from 'react' -import * as fa from 'react-icons/fa' -import { getFontSize, IGlobalProps, Type , getFormLabelSize, getHeight } from '../../global' -import { Size } from '../../global/globalEnums' -import { IconButton } from '../IconButton' -import { ListBox } from '../ListBox' -import { Popup, PopupTrigger } from '../Popup' -import './ListItem.scss' +import React, { useState } from 'react'; +import * as fa from 'react-icons/fa'; +import { getFontSize, IGlobalProps, Type, getHeight } from '../../global'; +import { Size } from '../../global/globalEnums'; +import { IconButton } from '../IconButton'; +import { ListBox } from '../ListBox'; +import { Popup, PopupTrigger } from '../Popup'; +import './ListItem.scss'; export interface IListItemProps extends IGlobalProps { - ind?: number - text?: string - val: string | number - icon?: JSX.Element - description?: string - shortcut?: string - items?: IListItemProps[] - selected?: boolean - setSelectedVal?: (val: string | number) => unknown - onClick?: () => void - onItemDown?: (e:React.PointerEvent, val:string| number) => void - uppercase?: boolean + ind?: number; + text?: string; + val: string | number; + icon?: JSX.Element; + description?: string; + shortcut?: string; + items?: IListItemProps[]; + selected?: boolean; + setSelectedVal?: (val: string | number, e?: React.MouseEvent) => unknown; + onClick?: () => void; + onItemDown?: (e: React.PointerEvent, val: string | number) => void; + uppercase?: boolean; } /** @@ -31,104 +31,67 @@ export interface IListItemProps extends IGlobalProps { * Look at: import Select from "react-select"; */ export const ListItem = (props: IListItemProps) => { - const { - ind, - val, - description, - text, - shortcut, - items, - icon, - selected, - setSelectedVal, - onClick, - onItemDown, - inactive, - size = Size.SMALL, - style, - color, - background, - uppercase - } = props + const { val, description, text, shortcut, items, icon, selected, setSelectedVal, onClick, onItemDown, inactive, size = Size.SMALL, style, color, background, uppercase } = props; - const [isHovered, setIsHovered] = useState<boolean>(false); + const [isHovered, setIsHovered] = useState<boolean>(false); - let listItem:JSX.Element = ( - <div - tabIndex={-1} - className="listItem-container" - onPointerDown={(e) => onItemDown?.(e, val) && setSelectedVal?.(val)} - onClick={(e: React.MouseEvent) => { - if (!items) { - !inactive && onClick?.() - !inactive && onClick && e.stopPropagation() - setSelectedVal?.(val) - } - }} - style={{ - minHeight: getHeight(undefined, size), - userSelect: 'none', - ...style - }} - onPointerEnter={() => { - setIsHovered(true) - }} - onPointerLeave={() => { - setIsHovered(false) - }} - > - <div className="listItem-top"> - <div className="content" - style={{ - fontSize: getFontSize(size), - color: style?.color ? style.color : color - }}> - {icon} - <div className="text" style={{ - textTransform: uppercase ? 'uppercase' : undefined - }}>{text}</div> + const listItem: JSX.Element = ( + <div + tabIndex={-1} + className="listItem-container" + onPointerDown={e => onItemDown?.(e, val) && setSelectedVal?.(val, e)} + onClick={(e: React.MouseEvent) => { + if (!items) { + !inactive && onClick?.(); + !inactive && onClick && e.stopPropagation(); + setSelectedVal?.(val, e); + } + }} + style={{ + minHeight: getHeight(undefined, size), + userSelect: 'none', + ...style, + }} + onPointerEnter={() => { + setIsHovered(true); + }} + onPointerLeave={() => { + setIsHovered(false); + }}> + <div className="listItem-top"> + <div + className="content" + style={{ + fontSize: getFontSize(size), + color: style?.color ? style.color : color, + }}> + {icon} + <div + className="text" + style={{ + textTransform: uppercase ? 'uppercase' : undefined, + }}> + {text} + </div> + </div> + {shortcut && !inactive && ( + <div className="shortcut" color={style?.color ? style.color : color}> + {shortcut} + </div> + )} + {items && !inactive && <IconButton type={Type.PRIM} size={Size.SMALL} icon={<fa.FaCaretRight />} color={style?.color ? style.color : color} background={background} inactive />} + </div> + {description && !inactive && <div className="listItem-description">{description}</div>} + <div + className="listItem-background" + style={{ + background: background ? background : style?.color ? style.color : color, + filter: selected ? 'opacity(0.3)' : isHovered && !inactive ? 'opacity(0.2)' : 'opacity(0)', + }} + /> </div> - {shortcut && !inactive && ( - <div - className="shortcut" - color={style?.color ? style.color : color} - > - {shortcut} - </div> - )} - {items && !inactive && ( - <IconButton - type={Type.PRIM} - size={Size.SMALL} - icon={<fa.FaCaretRight/>} - color={style?.color ? style.color : color} - background={background} - inactive - /> - )} - </div> - {description && !inactive && ( - <div className="listItem-description">{description}</div> - )} - <div className="listItem-background" - style={{ - background: background ? background : style?.color ? style.color : color, - filter: selected ? 'opacity(0.3)' : isHovered && !inactive ? 'opacity(0.2)' : 'opacity(0)' - }} - /> - </div> -) + ); - if (items && !inactive) return <Popup - placement={'right'} - toggle={listItem} - color={color} - background={background} - trigger={PopupTrigger.CLICK} - popup={ - <ListBox color={color} background={background} items={items}/> - } - fillWidth={true} - /> - else return <>{listItem}</> -} + if (items && !inactive) return <Popup placement={'right'} toggle={listItem} color={color} background={background} trigger={PopupTrigger.CLICK} popup={<ListBox color={color} background={background} items={items} />} fillWidth={true} />; + else return <>{listItem}</>; +}; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b41fd09dc..7c36a82f2 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -694,10 +694,8 @@ pie title Minerals in my tap water { title: "Type", icon:"eye", toolTip:"Sort by document type", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"docType", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, { title: "Color", icon:"palette", toolTip:"Sort by document color", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"color", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, { title: "Tags", icon:"bolt", toolTip:"Sort by document's tags", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"tag", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, - { title: "Pile", icon:"layer-group", toolTip:"View the cards as a pile in the free form view", btnType: ButtonType.ClickButton, expertMode: false, toolType:"pile", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, { title: "Chat Popup",icon:"lightbulb", toolTip:"Toggle the chat popup's visibility", width: 45, btnType: ButtonType.ToggleButton, expertMode: false, toolType:"toggle-chat",funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'} }, { title: "Show Tags", icon:"id-card", toolTip:"Toggle tag annotation panel", width: 45, btnType: ButtonType.ToggleButton, expertMode: false, toolType:"toggle-tags",funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'} }, - { title: "Sort", icon: "sort" , toolTip: "Manage sort order / lock status", btnType: ButtonType.MultiToggleButton, toolType:"alignment", ignoreClick: true, subMenu: [ { title: "Ascending", toolTip: "Sort the cards in ascending order", btnType: ButtonType.ToggleButton, icon: "sort-up", toolType:"up", ignoreClick: true, scripts: {onClick: '{ return showFreeform(this.toolType, _readOnly_);}'} }, @@ -814,7 +812,7 @@ pie title Minerals in my tap water CollectionViewType.Masonry, CollectionViewType.Multicolumn, CollectionViewType.Multirow, CollectionViewType.Linear, CollectionViewType.Map, CollectionViewType.NoteTaking, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.Calendar, CollectionViewType.Grid, CollectionViewType.Tree, CollectionViewType.Time, ]), - title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: '{ return setView(value, _readOnly_); }'}}, + title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: '{ return setView(value, shiftKey, _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: "Doc Titlebar Color", btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'} }, { title: "Template",icon: "scroll", toolTip: "Default Note Template",btnType: ButtonType.ToggleButton, expertMode: false, toolType:DocumentType.RTF, scripts: { onClick: '{ return setDefaultTemplate(_readOnly_); }'} }, diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index d9ff21035..15683ebf2 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -16,6 +16,7 @@ import { RegionHelpers } from './Region'; import './Timeline.scss'; import { TimelineOverview } from './TimelineOverview'; import { Track } from './Track'; +import { Id } from '../../../fields/FieldSymbols'; /** * Timeline class controls most of timeline functions besides individual region and track mechanism. Main functions are @@ -56,7 +57,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> { private DEFAULT_CONTAINER_HEIGHT: number = 330; private MIN_CONTAINER_HEIGHT: number = 205; - constructor(props: any) { + constructor(props: FieldViewProps) { super(props); makeObservable(this); } @@ -89,7 +90,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> { */ @computed private get children(): Doc[] { - const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this._props.Document.type) as any); + const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this._props.Document.type) as unknown as DocumentType); if (annotatedDoc) { return DocListCast(this._props.Document[Doc.LayoutFieldKey(this._props.Document) + '_annotations']); } @@ -272,9 +273,9 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> { * for displaying time to standard min:sec */ @action - toReadTime = (time: number): string => { - time = time / 1000; - const inSeconds = Math.round(time * 100) / 100; + toReadTime = (timeIn: number): string => { + const timeSecs = timeIn / 1000; + const inSeconds = Math.round(timeSecs * 100) / 100; const min = Math.floor(inSeconds / 60); const sec = Math.round((inSeconds % 60) * 100) / 100; @@ -552,6 +553,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> { <div key="timeline_trackbox" className="trackbox" ref={this._trackbox} style={{ width: `${this._totalLength}px` }}> {[...this.children, this._props.Document].map(doc => ( <Track + key={doc[Id]} ref={ref => this.mapOfTracks.push(ref)} timeline={this} animatedDoc={doc} @@ -570,7 +572,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> { <div className="currentTime">Current: {this.getCurrentTime()}</div> <div key="timeline_title" className="title-container" ref={this._titleContainer}> {[...this.children, this._props.Document].map(doc => ( - <div style={{ height: `${this._titleHeight}px` }} className="datapane" onPointerOver={() => Doc.BrushDoc(doc)} onPointerOut={() => Doc.UnBrushDoc(doc)}> + <div key={doc[Id]} style={{ height: `${this._titleHeight}px` }} className="datapane" onPointerOver={() => Doc.BrushDoc(doc)} onPointerOut={() => Doc.UnBrushDoc(doc)}> <p>{StrCast(doc.title)}</p> </div> ))} diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index 800eb4914..1bca68846 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import * as CSS from 'csstype'; import { ClientUtils, DashColor, imageUrlToBase64, returnFalse, returnNever, returnZero, setupMoveUpEvents } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; -import { Doc, DocListCast, Opt } from '../../../fields/Doc'; +import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc'; import { Animation, DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; @@ -314,6 +314,7 @@ export class CollectionCardView extends CollectionSubView() { case cardSortings.Chat: return [NumCast(docA.chatIndex, 9999), NumCast(docB.chatIndex,9999)]; case cardSortings.Time: return [DateCast(docA.author_date)?.date ?? Date.now(), DateCast(docB.author_date)?.date ?? Date.now()]; case cardSortings.Color:return [DashColor(StrCast(docA.backgroundColor)).hsv().hue(), DashColor(StrCast(docB.backgroundColor)).hsv().hue()]; + case cardSortings.Tag: return [StrListCast(docA.tags).join(""), StrListCast(docB.tags).join("")]; } })(); //prettier-ignore return (typeA < typeB ? -1 : typeA > typeB ? 1 : 0) * (isDesc ? 1 : -1); diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index 1738802b7..542417531 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -48,10 +48,20 @@ ScriptingGlobals.add(function IsNoneSelected() { // toggle: Set overlay status of selected document // eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function setView(view: string, getSelected: boolean) { - if (getSelected) return DocumentView.SelectedDocs(); - const selected = DocumentView.SelectedDocs().lastElement(); - selected ? (selected._type_collection = view) : console.log('[FontIconBox.tsx] changeView failed'); +ScriptingGlobals.add(function setView(view: string, shiftKey: boolean, checkResult?: boolean) { + if (checkResult) return DocumentView.SelectedDocs(); + const selected = DocumentView.Selected().lastElement(); + if (selected) { + if (shiftKey) { + const newCol = Doc.MakeEmbedding(selected.Document); + newCol._type_collection = view; + selected._props.addDocTab?.(newCol, OpenWhere.addRight); + } else { + selected.Document._type_collection = view; + } + } else { + console.log('[FontIconBox.tsx] changeView failed'); + } return undefined; }); @@ -141,7 +151,7 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function showFreeform( - attr: 'flashcards' | 'hcenter' | 'vcenter' | 'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'chat' | 'up' | 'down' | 'pile' | 'toggle-chat' | 'toggle-tags' | 'tag', + attr: 'flashcards' | 'hcenter' | 'vcenter' | 'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'chat' | 'up' | 'down' | 'toggle-chat' | 'toggle-tags' | 'tag', checkResult?: boolean, persist?: boolean ) { @@ -152,7 +162,7 @@ ScriptingGlobals.add(function showFreeform( } // prettier-ignore - const map: Map<'flashcards' | 'hcenter' | 'vcenter' | 'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'chat' | 'up' | 'down' | 'pile' | 'toggle-chat' | 'toggle-tags' | 'tag', + const map: Map<'flashcards' | 'hcenter' | 'vcenter' | 'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'chat' | 'up' | 'down'| 'toggle-chat' | 'toggle-tags' | 'tag', { waitForRender?: boolean; checkResult: (doc: Doc) => boolean; @@ -242,20 +252,6 @@ ScriptingGlobals.add(function showFreeform( doc.showChildTags = !doc.showChildTags; }, }], - ['pile', { - checkResult: (doc: Doc) => doc._type_collection == CollectionViewType.Freeform, - setDoc: (doc: Doc, dv: DocumentView) => { - const newCol = Docs.Create.CarouselDocument(DocListCast(doc[Doc.LayoutFieldKey(doc)]), { - title: doc.title + "_carousel", - _width: 250, - _height: 200, - _layout_fitWidth: false, - _layout_autoHeight: true, - childFilters: new List<string>(StrListCast(doc.childFilters)) - }); - dv._props.addDocTab?.(newCol, OpenWhere.addRight); - }, - }], ]); if (checkResult) { diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 60b2a7519..f58862028 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -160,6 +160,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { buttonList, jsx: undefined, selectedVal: script(), + toolTip: 'Set text font', getStyle: (val: string) => ({ fontFamily: val }), }; }; @@ -174,6 +175,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { buttonList: buttonList.filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value as CollectionViewType)), getStyle: undefined, selectedVal: StrCast(selected[0]._type_collection), + toolTip: 'change view type (press Shift to add as a new view)', } : { jsx: selected.length ? ( @@ -205,11 +207,11 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { @computed get dropdownListButton() { const script = ScriptCast(this.Document.script); const selectedFunc = () => script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result as string; - const { buttonList, selectedVal, getStyle, jsx } = (() => { + const { buttonList, selectedVal, getStyle, jsx, toolTip } = (() => { switch (this.Document.title) { case 'Font': return this.handleFontDropdown(selectedFunc, this.buttonList); case 'Perspective': return this.handleViewDropdown(script, this.buttonList); - default: return { buttonList: this.buttonList, selectedVal: selectedFunc(), jsx: undefined, getStyle: undefined }; + default: return { buttonList: this.buttonList, selectedVal: selectedFunc(), toolTip: undefined, jsx: undefined, getStyle: undefined }; } // prettier-ignore })(); if (jsx) return jsx; @@ -225,9 +227,10 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { return ( <Dropdown selectedVal={selectedVal} - setSelectedVal={undoable(value => script.script.run({ this: this.Document, value }), `dropdown select ${this.label}`)} + setSelectedVal={undoable((value, e) => script.script.run({ this: this.Document, value, shiftKey: e.shiftKey }), `dropdown select ${this.label}`)} color={SnappingManager.userColor} background={SnappingManager.userVariantColor} + toolTip={toolTip} type={Type.TERT} closeOnSelect={false} dropdownType={DropdownType.SELECT} |