diff --git a/ivette/src/dome/renderer/frame/panel.tsx b/ivette/src/dome/renderer/frame/panel.tsx index b1851a3e9f5abf508a068b925ee6b0bda6a7ba16..e5301212fdac427340f47914e02f6e6bd6c7eecf 100644 --- a/ivette/src/dome/renderer/frame/panel.tsx +++ b/ivette/src/dome/renderer/frame/panel.tsx @@ -32,34 +32,44 @@ @module dome/frame/Panel */ -import { Label } from 'dome/controls/labels'; import React from 'react'; import { classes } from 'dome/misc/utils'; +import { Hbox } from 'dome/layout/boxes'; /* --------------------------------------------------------------------------*/ /* --- Panel Container */ /* --------------------------------------------------------------------------*/ +export type PanelPosition = 'top' | 'bottom' | 'left' | 'right'; + interface PanelProps { + /** Additional class. */ className?: string; - show?: boolean; - position?: 'left' | 'right' + /** Position to displayed the panel. Default 'tr' */ + position?: PanelPosition; + /** Defaults to `true`. */ + visible?: boolean; + /** Defaults to `true`. */ + display?: boolean; + /** Panel children. */ children: JSX.Element[]; } export const Panel = (props: PanelProps): JSX.Element => { - const { show = true, className, position } = props; + const { visible = true, display = true, + className, position = 'right' } = props; const classNames = classes( 'dome-xPanel', - position === 'left' ? 'dome-xPanel-left' : 'dome-xPanel-right', - show ? 'dome-xPanel-open' : 'dome-xPanel-close', + 'dome-xPanel-'+position, + visible ? 'dome-xPanel-open' : 'dome-xPanel-close', + !display && 'dome-control-erased', className, ); return ( <div className={classNames}> - {props.children} + {props.children.map((elt, k) => <Hbox key={k}>{elt}</Hbox>)} </div> ); }; @@ -68,77 +78,38 @@ export const Panel = (props: PanelProps): JSX.Element => { /* --- Panel List */ /* --------------------------------------------------------------------------*/ export interface ElementProps { - label: string; - onClickName?: () => void; - content: JSX.Element; + /** Selection state. */ + selected?: boolean; + /** Selection callback. */ + onSelection?: () => void; + /** Item element. */ + children?: JSX.Element; } -const Element = (props: ElementProps): JSX.Element => { - const { label, onClickName, content } = props; +export function Element(props: ElementProps): JSX.Element { + const { selected = true, onSelection, children } = props; - const nameClasse = classes( - 'dome-xPanel-element-name', - onClickName && "action" + const classNames = classes( + 'dome-xPanel-element', + selected ? 'dome-active' : 'dome-inactive', ); - return ( - <div className='dome-xPanel-element'> - <div - className={nameClasse} - onClick={() => { if(onClickName) onClickName(); }} - >{label}</div> - <div className='dome-xPanel-element-content'> - {content} - </div> + <div + className={classNames} + onClick={onSelection} + > + {children} </div> ); -}; +} interface ListElementProps { - list: ElementProps[]; + children: JSX.Element[]; } export function ListElement(props: ListElementProps): JSX.Element { return ( <div className='dome-xPanel-list'> - { props.list.map((elt, k) => <Element - key={k} - {...elt} - />) - } - </div> - ); -} - -/* --------------------------------------------------------------------------*/ -/* --- Panel Text */ -/* --------------------------------------------------------------------------*/ -interface TextProps { - label: string; - content?: string | JSX.Element; -} - -export function Text(props: TextProps): JSX.Element { - return ( - <div className='dome-xPanel-text'> - <Label> - {props.label} - </Label> - {props.content} - </div> - ); -} - -/* --------------------------------------------------------------------------*/ -/* ---Panel Button */ -/* --------------------------------------------------------------------------*/ -interface ActionsProps { - children: React.ReactNode; -} - -export function Actions(props: ActionsProps): JSX.Element { - return ( - <div className='dome-xPanel-actions'> {props.children} </div> ); diff --git a/ivette/src/dome/renderer/frame/style.css b/ivette/src/dome/renderer/frame/style.css index 448cd5618b31c41915dce4a23bf4934d3213ce5e..c4dd8a880576ffc1ee883344afac7302cc74de82 100644 --- a/ivette/src/dome/renderer/frame/style.css +++ b/ivette/src/dome/renderer/frame/style.css @@ -593,24 +593,34 @@ input:checked + .dome-xSwitch-slider:before { /* -------------------------------------------------------------------------- */ .dome-xPanel { position: absolute; - top:0; - max-width: 350px; - max-height: 100%; - overflow-y: scroll; z-index: 50; - background-color: var(--background-interaction); + background-color: var(--background-intense); padding: 10px 5px; transition: transform .5s ease-in-out, opacity .5s; + button:hover, + button:hover label { + cursor: pointer; + } + &::-webkit-scrollbar { display: none; } &.dome-xPanel-open { - transform: translateX(0px); + transform: translate(0); opacity: 1; } + &.dome-xPanel-left, + &.dome-xPanel-right { + top: 5px; + padding-top: -5px; + max-height: calc(100% - 10px); + max-width: 350px; + overflow-y: auto; + } + &.dome-xPanel-left { - border-radius: 0 20px 20px 0; + border-radius: 0 10px 10px 0; &.dome-xPanel-close { transform: translateX(-350px); @@ -620,7 +630,7 @@ input:checked + .dome-xSwitch-slider:before { &.dome-xPanel-right { right: 0; - border-radius: 20px 0 0 20px; + border-radius: 10px 0 0 10px; &.dome-xPanel-close { transform: translateX(350px); @@ -628,45 +638,55 @@ input:checked + .dome-xSwitch-slider:before { } } - .dome-xPanel-text { - color: var(--text); - padding-bottom: 3px; + &.dome-xPanel-top, + &.dome-xPanel-bottom { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + width: calc(100% - 10px); + left:5px; + max-height: 200px; + overflow-x: scroll; + + label, + .dome-xPanel-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + } } - .dome-xPanel-actions { - display: flex; + &.dome-xPanel-top { + overflow-x: auto; + border-radius: 0 0 10px 10px; + + &.dome-xPanel-close { + transform: translateY(-350px); + opacity: 0; + } + } + + &.dome-xPanel-bottom { + bottom: 0; + border-radius: 10px 10px 0 0; - button:hover, - button:hover label { - cursor: pointer; + &.dome-xPanel-close { + transform: translateY(350px); + opacity: 0; } } .dome-xPanel-list { .dome-xPanel-element { margin:5px; + padding: 2px 10px; background-color: var(--background); - overflow-x: hidden; - border-radius: 0 10px 0 10px; + border-radius: 5px; - .dome-xPanel-element-name { + &.dome-active { background-color: var(--background-profound); - font-weight: bold; - width: 100%; - padding: 5px 10px; - - &.action:hover { - cursor: pointer; - background-color: var(--background-profound-hover); - } - } - - .dome-xPanel-element-content { - padding: 4px; - - &:has(*){ - padding: 10px; - } } } } diff --git a/ivette/src/sandbox/panel.tsx b/ivette/src/sandbox/panel.tsx new file mode 100644 index 0000000000000000000000000000000000000000..407bbde1701091ad5ee99319568eb8448419295e --- /dev/null +++ b/ivette/src/sandbox/panel.tsx @@ -0,0 +1,182 @@ +/* ************************************************************************ */ +/* */ +/* This file is part of Frama-C. */ +/* */ +/* Copyright (C) 2007-2024 */ +/* CEA (Commissariat à l'énergie atomique et aux énergies */ +/* alternatives) */ +/* */ +/* you can redistribute it and/or modify it under the terms of the GNU */ +/* Lesser General Public License as published by the Free Software */ +/* Foundation, version 2.1. */ +/* */ +/* It is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU Lesser General Public License for more details. */ +/* */ +/* See the GNU Lesser General Public License version 2.1 */ +/* for more details (enclosed in the file licenses/LGPLv2.1). */ +/* */ +/* ************************************************************************ */ + +/* -------------------------------------------------------------------------- */ +/* --- Sandbox Testing of Panel --- */ +/* --- Only appears in DEVEL mode. --- */ +/* -------------------------------------------------------------------------- */ + +import React from 'react'; +import * as Dome from 'dome'; +import { IconButton } from 'dome/controls/buttons'; +import { registerSandbox, TitleBar } from 'ivette'; +import { + Panel, ListElement, Element, PanelPosition +} from 'dome/frame/panel'; +import { Button, ButtonGroup } from 'dome/frame/toolbars'; +import { Icon } from 'dome/controls/icons'; +import './style.css'; +import { Label } from 'dome/controls/labels'; + +/* -------------------------------------------------------------------------- */ +/* --- Use Panel --- */ +/* -------------------------------------------------------------------------- */ +function UsePanel(): JSX.Element { + const [position, setPosition] = React.useState<PanelPosition>('right'); + const [visible, flipVisible] = Dome.useFlipState(true); + const [selected, setSelected] = React.useState(1); + const [list, setList] = React.useState({ + flip: true, + list: [ + { id: 1, children: <div>item 1</div> }, + { id: 2, children: <div>item 2</div> }, + { id: 3, children: <div>item 3</div> }, + { id: 4, children: <div>item 4</div> }, + ] + } + ); + + return ( + <> + <TitleBar> + <IconButton + icon='ANGLE.RIGHT' + title="Change position of Panel." + className={'sandbox-panel-button-position-'+position} + onClick={() => setPosition((val) => { + return val === 'right' ? 'bottom' : + val === 'bottom' ? 'left' : + val === 'left' ? 'top' : + 'right'; + }) + } + /> + + <IconButton + icon="SIDEBAR" + title={"show or hide the panel"} + onClick={flipVisible} + /> + </TitleBar> + <div style={{ position: 'relative', height: '100%' }}> + <Panel visible={visible} position={position}> + <Label + label="label" + title="Text Component" + icon="CODE" + > + Content of the Text component. + </Label> + + <Label> + <ButtonGroup> + <Button + label='left' + selected={position === 'left'} + onClick={() => setPosition('left')} /> + <Button + label='top' + selected={position === 'top'} + onClick={() => setPosition('top')} /> + <Button + label='right' + selected={position === 'right'} + onClick={() => setPosition('right')} /> + <Button + label='bottom' + selected={position === 'bottom'} + onClick={() => setPosition('bottom')} /> + </ButtonGroup> + <Button + icon='SIDEBAR' + title="This button is always selected when the panel is visible." + selected={visible} + onClick={() => flipVisible()} /> + </Label> + + <Label + label="List icon" + title="Text Component" + > + <Icon id="WARNING"/> + <Icon id="SETTINGS"/> + <Icon id="RELOAD"/> + <Icon id="DOWNLOAD"/> + <Icon id="LOCK"/> + </Label> + + <Label + label="Add/remove element" + title="Text Component" + > + <Button + icon='PLUS' + title="This button add an element t the end of the list." + onClick={() => setList((elt) => { + const newId = elt.list.length + 1; + const newChild = "item "+newId; + elt.list.push({ + id: newId, children: <div>{newChild}</div> + }); + return { flip: !elt.flip, list: elt.list }; + } + )} + /> + <Button + icon='MINUS' + title="This button remove the last element of the list." + onClick={() => setList((elt) => { + elt.list.pop(); + return { flip: !elt.flip, list: elt.list }; + } + )} + /> + </Label> + <ListElement> + { list.list.map((elt, k) => + <Element + key={k} + selected={selected === elt.id} + onSelection={() => setSelected(elt.id)} + > + {elt.children} + </Element> + ) + } + </ListElement> + </Panel> + </div> + </> + ); +} + +/* -------------------------------------------------------------------------- */ +/* --- Sandbox --- */ +/* -------------------------------------------------------------------------- */ + +registerSandbox({ + id: 'sandbox.panel', + label: 'Panel', + children: <UsePanel />, +}); + +/* -------------------------------------------------------------------------- */ diff --git a/ivette/src/sandbox/style.css b/ivette/src/sandbox/style.css new file mode 100644 index 0000000000000000000000000000000000000000..6263e25d48a2fd23a96c0c5432952ade1cc67abf --- /dev/null +++ b/ivette/src/sandbox/style.css @@ -0,0 +1,19 @@ +/* -------------------------------------------------------------------------- */ +/* --- Sandbox Testing CSS --- */ +/* -------------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* --- Sandbox Panel --- */ +/* -------------------------------------------------------------------------- */ +.sandbox-panel-button-position-right { + transform: rotate(0); +} +.sandbox-panel-button-position-bottom { + transform: rotate(90deg); +} +.sandbox-panel-button-position-left { + transform: rotate(180deg); +} +.sandbox-panel-button-position-top { + transform: rotate(270deg); +}