From a665eac7bb86f60dee52eba751cbdd9f276ea6d9 Mon Sep 17 00:00:00 2001
From: rlazarini <remi.lazarini@cea.fr>
Date: Wed, 18 Sep 2024 08:57:30 +0200
Subject: [PATCH] [Ivette] add sandbox to the panel + new position top and
 bottom

---
 ivette/src/dome/renderer/frame/panel.tsx |  99 +++++-------
 ivette/src/dome/renderer/frame/style.css |  90 ++++++-----
 ivette/src/sandbox/panel.tsx             | 182 +++++++++++++++++++++++
 ivette/src/sandbox/style.css             |  19 +++
 4 files changed, 291 insertions(+), 99 deletions(-)
 create mode 100644 ivette/src/sandbox/panel.tsx
 create mode 100644 ivette/src/sandbox/style.css

diff --git a/ivette/src/dome/renderer/frame/panel.tsx b/ivette/src/dome/renderer/frame/panel.tsx
index b1851a3e9f5..e5301212fda 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 448cd5618b3..c4dd8a88057 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 00000000000..407bbde1701
--- /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 00000000000..6263e25d48a
--- /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);
+}
-- 
GitLab