diff --git a/ivette/src/dome/src/renderer/frame/sidebars.js b/ivette/src/dome/src/renderer/frame/sidebars.js deleted file mode 100644 index a87d5dbca068ea5aed36ec777aa32c0e7ccf616b..0000000000000000000000000000000000000000 --- a/ivette/src/dome/src/renderer/frame/sidebars.js +++ /dev/null @@ -1,213 +0,0 @@ -// -------------------------------------------------------------------------- -// --- SideBars -// -------------------------------------------------------------------------- - -/** - @packageDocumentation - @module dome/frame/sidebars -*/ - -import React from 'react' ; -import { useFlipSettings } from 'dome'; -import { Badge } from 'dome/controls/icons' ; -import { Label } from 'dome/controls/labels' ; - -import './style.css' ; - -const SideBarContext = React.createContext(); - -// -------------------------------------------------------------------------- -// --- SideBar Container -// -------------------------------------------------------------------------- - -/** - @summary Container for sidebar items. - @property {Elements} [children] - Side bar items - @property {string} [settings] - Side bar items settings - @property {string} [selection] - Current item selection - @property {function} [onSelection] - Selection callback - @property {function} [onContextMenu] - Context Menu callback - @description - - When a base settings is set on the sidebar, all contained - sections and items are attributed derived settings based on their identifiers. - - The global selection state and callback are also propagated to the side bar items. - */ -export const SideBar = ({ children , ...props }) => ( - <div className="dome-xSideBar dome-color-frame"> - <SideBarContext.Provider value={props}> - {children} - </SideBarContext.Provider> - </div> -); - -const makeSettings = ( globalSettings, { settings, label, id } ) => { - if (settings) return settings ; - if (globalSettings) { - let localId = id || label ; - if (localId) - return globalSettings + '.' + localId ; - } - return undefined ; -}; - -// -------------------------------------------------------------------------- -// --- Badges Specifications -// -------------------------------------------------------------------------- - -const makeBadge = ((element) => { - switch(typeof(element)) { - case 'number': - case 'string': - return <Badge value={element}/>; - default: - return element ; - } -}); - -// -------------------------------------------------------------------------- -// --- SideBar Section Hide/Show Button -// -------------------------------------------------------------------------- - -const HideShow = (props) => ( - <label className='dome-xSideBarSection-hideshow dome-text-label' - onClick={props.onClick} > - {props.visible ? 'Hide' : 'Show'} - </label> -); - -// -------------------------------------------------------------------------- -// --- SideBar Section -// -------------------------------------------------------------------------- - -const disableAll = (children) => - React.Children.map( children , (elt) => React.cloneElement( elt , { disabled: true } ) ); - -/** - @summary Sidebar Section. - @property {string} [id] - Section identifier (used for derived settings) - @property {string} label - **Section label** - @property {string} [title] - Section short description (label tooltip) - @property {boolean} [enabled] - Enabled section (by default) - @property {boolean} [disabled] - Disabled section (not by default) - @property {boolean} [unfold] - Fold/unfold local state (default to local state) - @property {string} [settings] - Fold/unfold window settings (local state) - @property {boolean} [defaultUnfold] - Fold/unfold default state (default is `true`) - @property {Badge} [summary] - Badge summary (when content is hidden) - @property {Item[]} children - Content items - @description - - Unless specified, sections can be hidden on click. When items in the section have badge(s) - it is highly recommended to provide a badge summary to be displayed - when the content is hidden. - - Sections with no items are not displayed. -*/ -export function Section(props) { - - const context = React.useContext( SideBarContext ); - const [ state, flipState ] = useFlipSettings( - makeSettings(context.settings,props), props.defaultUnfold - ); - const { enabled=true, disabled=false, unfold, children } = props ; - - if (React.Children.count(children) == 0) return null; - - const dimmed = context.disabled || disabled || !enabled ; - const foldable = unfold === undefined ; - const visible = foldable ? state : unfold ; - const onClick = foldable ? flipState : undefined ; - const maxHeight = visible ? 'max-content' : 0 ; - const subContext = Object.assign( {}, context, { disabled: dimmed } ); - - return ( - <div className='dome-xSideBarSection'> - <div className='dome-xSideBarSection-title' title={props.label}> - <Label className={ dimmed ? 'dome-disabled' : '' } label={props.label} /> - {!visible && React.Children.map(props.summary,makeBadge)} - {foldable && <HideShow visible={visible} onClick={onClick}/>} - </div> - <div className='dome-xSideBarSection-content' style={{ maxHeight }}> - <SideBarContext.Provider value={subContext}> - {children} - </SideBarContext.Provider> - </div> - </div> - ); -} - -// -------------------------------------------------------------------------- -// --- SideBar Items -// -------------------------------------------------------------------------- - -/** - @summary Sidebar Section Items. - @property {string} [id] - Item identifier - @property {string} [label] - **Item label** - @property {string} [icon] - Item icon - @property {string} [title] - Item tooltip - @property {boolean} [enabled] - Enabled item (default is `true`) - @property {boolean} [disabled] - Disabled item (default is `false`) - @property {boolean} [selected] - Item selection state (default is `SideBar` selection) - @property {function} [onSelection] - Selection callback - @property {function} [onContextMenu] - Context Menu callback - @property {Badge} [badge] - Badge element(s) - @property {React.Children} [children] - Item additional content - @description - - The item will be highlighted if selected _via_ the `selection` property of the - englobing sidebar. The `onSelection` and `onContextMenu` callbacks are invoked with the item identifier. - Context menu callback also triggers the selection callback (first). - In case callbacks are defined from the englobing sidebar, both are invoked. - - Badges can be single or multiple [[Badge]] values. - They are displayed stacked on the right edge of the item. - -**/ -export function Item(props) -{ - const { selection, - disabled: disabledSection, - onSelection: ctxtOnSelect, - onContextMenu: ctxtOnPopup - } = React.useContext(SideBarContext); - const { id, selected, - disabled=false, enabled=true, - onSelection:itemOnSelect, - onContextMenu:itemOnPopup - } = props ; - const isDisabled = disabled || !enabled || disabled ; - const isSelected = selected !== undefined ? selected : (selection && id && (selection === id)) ; - const onClick = isDisabled ? undefined : () => { - itemOnSelect && itemOnSelect(id); - ctxtOnSelect && ctxtOnSelect(id); - }; - const onContextMenu = isDisabled ? undefined : () => { - onClick(); - itemOnPopup && itemOnPopup(id); - ctxtOnPopup && ctxtOnPopup(id); - }; - const classes = 'dome-xSideBarItem' - + ( isSelected ? ' dome-active' : ' dome-inactive' ) - + ( isDisabled ? ' dome-disabled' : '' ); - return ( - <div className={classes} title={props.title} - onContextMenu={onContextMenu} - onClick={onClick}> - <Label icon={props.icon} label={props.label} /> - {props.children} - {React.Children.map(props.badge,makeBadge)} - </div> - ); -} - -// -------------------------------------------------------------------------- - -import { register } from 'dome/misc/register' ; - -register( SideBar, 'DOME_SIDEBAR' ); -register( Section, 'DOME_SIDEBAR_ITEM' ); -register( Item, 'DOME_SIDEBAR_ITEM' ); - -// -------------------------------------------------------------------------- diff --git a/ivette/src/dome/src/renderer/frame/sidebars.tsx b/ivette/src/dome/src/renderer/frame/sidebars.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2137d570c17cf7e78dc12fbef89f533492d6d292 --- /dev/null +++ b/ivette/src/dome/src/renderer/frame/sidebars.tsx @@ -0,0 +1,197 @@ +// -------------------------------------------------------------------------- +// --- SideBars +// -------------------------------------------------------------------------- + +/** + @packageDocumentation + @module dome/frame/sidebars +*/ + +import React from 'react'; +import { useFlipSettings } from 'dome'; +import { Badge } from 'dome/controls/icons'; +import { Label } from 'dome/controls/labels'; +import { classes } from 'dome/misc/utils'; + +import './style.css'; + +// -------------------------------------------------------------------------- +// --- SideBar Container +// -------------------------------------------------------------------------- + +export interface SideBarProps { + className?: string; + style?: React.CSSProperties; + children?: React.ReactNode; +} + +/** + Container for sidebar items. + */ +export function SideBar(props: SideBarProps) { + const className = classes( + 'dome-xSideBar', + 'dome-color-frame', + props.className, + ); + return ( + <div className={className} style={props.style}> + {props.children} + </div> + ); +} + +// -------------------------------------------------------------------------- +// --- Badges Specifications +// -------------------------------------------------------------------------- + +export type BadgeElt = undefined | null | string | number | React.ReactNode; +export type Badge = BadgeElt | BadgeElt[]; + +const makeBadgeElt = (elt: BadgeElt): React.ReactNode => { + if (elt === undefined || elt === null) return null; + switch (typeof (elt)) { + case 'number': + case 'string': + return <Badge value={elt} />; + default: + return elt; + } +}; + +const makeBadge = (elt: Badge): React.ReactNode => { + if (Array.isArray(elt)) + return elt.map(makeBadgeElt); + return makeBadgeElt(elt); +}; + +// -------------------------------------------------------------------------- +// --- SideBar Section Hide/Show Button +// -------------------------------------------------------------------------- + +const HideShow = (props: { onClick: () => void; visible: boolean }) => ( + <label + className="dome-xSideBarSection-hideshow dome-text-label" + onClick={props.onClick} + > + {props.visible ? 'Hide' : 'Show'} + </label> +); + +// -------------------------------------------------------------------------- +// --- SideBar Section +// -------------------------------------------------------------------------- + +export interface SectionProps { + /** Section label. */ + label: string; + /** Section tooltip description. */ + title?: string; + /** Hide/Show window settings. */ + settings?: string; + /** Controlled Fold/Unfold state. */ + unfold?: boolean; + /** Initial unfold state (default is `true`). */ + defaultUnfold?: boolean; + /** Enabled sections are made visible. */ + enabled?: boolean; + /** Disabled sections are made unvisible. */ + disabled?: boolean; + /** Badge summary (only visible when folded). */ + summary?: Badge; + /** Section contents. */ + children?: React.ReactNode; +} + +/** + Sidebar Section. + + Unless specified, sections can be hidden on click. + When items in the section have badge(s) + it is highly recommended to provide a badge summary to be displayed + when the content is hidden. + + Sections with no items are not displayed. +*/ +export function Section(props: SectionProps) { + + const [state, flipState] = useFlipSettings( + props.settings, + props.defaultUnfold, + ); + + const { enabled = true, disabled = false, children } = props; + if (disabled || !enabled || React.Children.count(children) === 0) + return null; + const { unfold } = props; + const foldable = unfold === undefined; + const visible = unfold ?? state; + const maxHeight = visible ? 'max-content' : 0; + + return ( + <div className="dome-xSideBarSection"> + <div className="dome-xSideBarSection-title" title={props.label}> + <Label label={props.label} /> + {!visible && makeBadge(props.summary)} + {foldable && <HideShow visible={visible} onClick={flipState} />} + </div> + <div className="dome-xSideBarSection-content" style={{ maxHeight }}> + {children} + </div> + </div> + ); +} + +// -------------------------------------------------------------------------- +// --- SideBar Items +// -------------------------------------------------------------------------- + +export interface ItemProps { + /** Item icon. */ + icon?: string; + /** Item label. */ + label?: string; + /** Item tooltip text. */ + title?: string; + /** Badge. */ + badge?: Badge; + /** Enabled item. */ + enabled?: boolean; + /** Disabled item (dimmed). */ + disabled?: boolean; + /** Selection state. */ + selected?: boolean; + /** Selection callback. */ + onSelection?: () => void; + /** Right-click callback. */ + onContextMenu?: () => void; + /** Other item elements. */ + children?: React.ReactNode; +} + +/** Sidebar Items. */ +export function Item(props: ItemProps) { + const { selected = false, disabled = false, enabled = true } = props; + const isDisabled = disabled || !enabled; + const onClick = isDisabled ? undefined : props.onSelection; + const onContextMenu = isDisabled ? undefined : props.onContextMenu; + const className = classes( + 'dome-xSideBarItem', + selected ? 'dome-active' : 'dome-inactive', + isDisabled && 'dome-disabled', + ); + return ( + <div + className={className} + title={props.title} + onContextMenu={onContextMenu} + onClick={onClick} + > + <Label icon={props.icon} label={props.label} /> + {props.children} + {makeBadge(props.badge)} + </div> + ); +} + +// -------------------------------------------------------------------------- diff --git a/ivette/src/frama-c/LabViews.tsx b/ivette/src/frama-c/LabViews.tsx index 54b4bc4f0e835b149b6282756ee93020b8affdd8..080572b69274cc5cbf2d2a14854e13b7131283e3 100644 --- a/ivette/src/frama-c/LabViews.tsx +++ b/ivette/src/frama-c/LabViews.tsx @@ -476,11 +476,14 @@ function CustomViews({ settings, shape, setShape, views: libViews }: any) { placeholder="View Name" autoFocus value={label} + title={title} onChange={RENAMED} /> ); return ( - <Item key={id} id={id} icon="DISPLAY" title={title} label={FIELD} /> + <Item key={id} icon="DISPLAY"> + {FIELD} + </Item> ); } const FLAGS = []; @@ -488,13 +491,12 @@ function CustomViews({ settings, shape, setShape, views: libViews }: any) { return ( <Item key={id} - id={id} icon="DISPLAY" label={label} title={title} selected={id && current === id} - onSelection={SELECT} - onContextMenu={POPUP} + onSelection={() => SELECT(id)} + onContextMenu={() => POPUP(id)} > {FLAGS.map((icn) => ( <Icon @@ -529,6 +531,7 @@ function CustomViews({ settings, shape, setShape, views: libViews }: any) { const DRAGOVERLAY = { className: 'labview-stock' }; function CustomGroup({ + settings, dnd, shape, setDragging, id: sectionId, title: sectionTitle, @@ -557,7 +560,8 @@ function CustomGroup({ }; return ( <Section - id={sectionId} + key={sectionId} + settings={settings && `${settings}.${sectionId}`} label={sectionLabel} title={sectionTitle} defaultUnfold={sectionId === 'groups.frama-c'} @@ -594,7 +598,7 @@ function CustomizePanel( }); return ( - <SideBar settings={settingFolds}> + <SideBar> <CustomViews key="views" settings={settingViews} @@ -604,6 +608,7 @@ function CustomizePanel( /> {groups.map((g) => ( <CustomGroup + settings={settingFolds} key={g.id} id={g.id} label={g.label}