Skip to content
Snippets Groups Projects
Commit 5d606805 authored by Loïc Correnson's avatar Loïc Correnson
Browse files

[ivette] sanitize component properties

parent c7f999ba
No related branches found
No related tags found
No related merge requests found
...@@ -26,17 +26,17 @@ import { DefineElement, RenderElement } from 'dome/layout/dispatch'; ...@@ -26,17 +26,17 @@ import { DefineElement, RenderElement } from 'dome/layout/dispatch';
import './style-labview.css'; import './style-labview.css';
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// --- Library // --- Library Class
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
const UPDATE = new Dome.Event('labview.library');
class Library { class Library {
modified: boolean; modified: boolean;
virtual: {}; virtual: {};
collection: {}; collection: {};
items: any[]; items: any[];
static update = new Dome.Event('labview.library');
constructor() { constructor() {
this.modified = false; this.modified = false;
this.virtual = {}; this.virtual = {};
...@@ -49,7 +49,7 @@ class Library { ...@@ -49,7 +49,7 @@ class Library {
this.collection = { ...this.virtual }; this.collection = { ...this.virtual };
this.items = _.sortBy(this.collection, ['order', 'id']); this.items = _.sortBy(this.collection, ['order', 'id']);
this.modified = false; this.modified = false;
Library.update.emit(); UPDATE.emit();
} }
} }
...@@ -90,6 +90,21 @@ class Library { ...@@ -90,6 +90,21 @@ class Library {
} }
// --------------------------------------------------------------------------
// --- Global Consolidated Library
// --------------------------------------------------------------------------
const LIBRARY = new Library();
function useLibrary() {
const libRef = React.useRef(LIBRARY);
// Hot Reload detection
if (LIBRARY.updateFrom(libRef.current)) {
libRef.current = LIBRARY;
}
return LIBRARY;
}
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// --- Library Components // --- Library Components
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
...@@ -103,111 +118,136 @@ const getItemId = ...@@ -103,111 +118,136 @@ const getItemId =
const getItems = const getItems =
(items: any[], fd: string) => items.filter((item) => isItemId(fd, item.id)); (items: any[], fd: string) => items.filter((item) => isItemId(fd, item.id));
const LibraryManager = React.createContext(undefined); interface GroupContext {
group?: string;
order?: number[];
}
const useLibraryItem = (fd: string, { id, ...props }: any) => { const GROUP = React.createContext<GroupContext>({});
const context = React.useContext(LibraryManager);
function useLibraryItem(fd: string, { id, ...props }: any): GroupContext {
const context = React.useContext(GROUP);
React.useEffect(() => { React.useEffect(() => {
if (context) { const { group, order } = context;
const { group, order, library }: any = context; const itemId = `${fd}.${id}`;
const itemId = `${fd}.${id}`; LIBRARY.addItem(itemId, group, order ?? [], props);
library.addItem(itemId, group, order, props); return () => LIBRARY.removeItem(itemId);
return () => library.removeItem(itemId);
}
return undefined;
}); });
return context; return context;
}; }
const Rankify = /* --------------------------------------------------------------------------*/
({ library, group, order, children }: any) => { /* --- Rankifyier ---*/
let rank = 0; /* --------------------------------------------------------------------------*/
const rankify = (elt: any) => {
rank += 1; interface RankifyProps {
const context: any = { group, order: [...order, rank], library }; group: string | undefined;
return ( order: number[] | undefined;
<LibraryManager.Provider value={context}> children: React.ReactNode | undefined;
{elt} }
</LibraryManager.Provider>
); function Rankify(props: RankifyProps) {
}; const { group, order = [], children } = props;
let rank = 0;
const rankify = (elt: any) => {
rank += 1;
return ( return (
<> <GROUP.Provider value={{ group, order: [...order, rank] }}>
{React.Children.map(children, rankify)} {elt}
</> </GROUP.Provider>
); );
}; };
return (
<>
{React.Children.map(children, rankify)}
</>
);
}
const HIDDEN = { display: 'none' }; function UseLibrary(props: { children?: React.ReactNode }) {
return (
<div className="dome-phantom">
<Rankify group={undefined} order={[]}>
{props.children}
</Rankify>
</div>
);
}
const UseLibrary = ({ library, children }: any) => ( /* --------------------------------------------------------------------------*/
<div style={HIDDEN}> /* --- Fragments ---*/
<Rankify library={library} order={[]}> /* --------------------------------------------------------------------------*/
{children}
</Rankify> export interface FragmentProps {
</div> group?: string;
); rank?: number;
children?: React.ReactNode;
}
/** /**
@summary Ordered collection of LabView Components. Ordered collection of LabView Components.
@description
Renderers its children in the specified order.
Otherwise, elements are ordered by `rank` and `id`. Otherwise, elements are ordered by `rank` and `id`.
*/ */
export const Fragment = ({ group, children }: any) => { export function Fragment(props: FragmentProps) {
const context: any = React.useContext(LibraryManager); const { group, rank, children } = props;
const context = React.useContext(GROUP);
if (!context) return null; const base = context.order ?? [];
return ( return (
<Rankify <Rankify
group={group || context.group} group={group ?? context.group}
order={context.order} order={rank === undefined ? base : [...base, rank]}
library={context.library}
> >
{children} {children}
</Rankify> </Rankify>
); );
}; }
/* --------------------------------------------------------------------------*/
/* --- Groups ---*/
/* --------------------------------------------------------------------------*/
export interface ItemProps {
/** Identifier. */
id: string;
/** Displayed name. */
label: string;
/** Tooltip description. */
title?: string;
/** Contents. */
children?: React.ReactNode;
}
/** /**
@summary Group of LabView Components.
@property {string} id - group identifier
@property {string} label - displayed name
@property {string} [title] - description tooltip
@property {React.Children} [children] - group content
@description
Defines a group of components. The components rendered Defines a group of components. The components rendered
_inside_ its content are implicitely affected to this group, _inside_ its content are implicitely affected to this group,
unless specified. The group content are also rendered unless specified. The group content are also rendered
in their specified order. For nested collections of components, in their specified order. For nested collections of components,
use `<Fragment/>` instead of `<React.Fragment/>` to specify order. use `<Fragment/>` instead of `<React.Fragment/>` to specify order.
*/ */
export const Group = ({ children, ...props }: any) => { export function Group(props: ItemProps) {
const context: any = useLibraryItem('groups', props); const { children, ...group } = props;
const context = useLibraryItem('groups', group);
return ( return (
<Rankify <Rankify
group={props.id} group={props.id}
order={context.order} order={context.order ?? []}
library={context.library}
> >
{children} {children}
</Rankify> </Rankify>
); );
}; }
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// --- Views // --- Views
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
export interface ViewProps extends ItemProps {
/** Use this view by default. */
defaultView?: boolean;
}
/** /**
@summary Layout of LabView Components. Layout of LabView Components.
@property {string} id - view identifier
@property {string} label - displayed name
@property {string} [title] - description tooltip
@property {boolean} [defaultView] - use this view by default
@property {GridContent} children - grid content of the view
@description
Defines a predefined layout of components. The view is organized Defines a predefined layout of components. The view is organized
into a GridContent, which must _only_ consists of: into a GridContent, which must _only_ consists of:
- `<GridHbox>…</GridHbox>` an horizontal grid of `GridContent` elements; - `<GridHbox>…</GridHbox>` an horizontal grid of `GridContent` elements;
...@@ -220,53 +260,69 @@ export const Group = ({ children, ...props }: any) => { ...@@ -220,53 +260,69 @@ export const Group = ({ children, ...props }: any) => {
import { GridItem, GridHbox, GridVbox } from 'dome/layout/grids'; import { GridItem, GridHbox, GridVbox } from 'dome/layout/grids';
``` ```
*/ */
export const View = (props: any) => { export function View(props: ViewProps) {
useLibraryItem('views', props); useLibraryItem('views', props);
return null; return null;
}; }
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// --- Components // --- Components
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
export interface ComponentProps extends ItemProps {
/** Group attachment (defaults to group context) */
group?: string;
/** Ordering index (defaults to fragment context). */
rank?: number;
}
/** /**
@summary LabView Component. LabView Component.
@property {string} id - component identifier
@property {string} label - displayed name
@property {number} [rank] - ordering index
@property {string} [group] - attachment group
@property {string} [title] - description tooltip
@property {React.Children} children - component rendering elements
@description
Defines a component and its content when incorporated inside a view. Defines a component and its content when incorporated inside a view.
Unless specified, the component will be implicitely attached Unless specified, the component will be implicitely attached
to the current enclosing group. The `rank` property can be used to the current enclosing group. The `rank` property can be used
for adjusting component ordering (see also `<Fragment/>` and `<Group/>`). for adjusting component ordering (see also `<Fragment/>` and `<Group/>`).
*/ */
export const Component = (props: any) => { export function Component(props: ComponentProps) {
useLibraryItem('components', props); useLibraryItem('components', props);
return null; return null;
}; }
const TitleContext: any = React.createContext(undefined); interface TitleContext {
id?: string;
label?: string;
title?: string;
}
const TITLE = React.createContext<TitleContext>({});
export interface TitleBarProps {
/*
@property {string} [icon] - displayed icon
@property {string} [label] - displayed name
@property {string} [title] - description tooltip
@property {React.Children} children - additional components to render
*/
/** Displayed icon. */
icon?: string;
/** Displayed name (when mounted). */
label?: string;
/** Tooltip description (when mounted). */
title?: string;
/** TitleBar additional components (stacked to right). */
children?: React.ReactNode;
}
/** /**
@summary LabView Component's title bar. LabView Component's title bar.
@property {string} [icon] - displayed icon Defines an alternative component title bar in current context.
@property {string} [label] - displayed name Default values are taken from the associated component.
@property {string} [title] - description tooltip
@property {React.Children} children - additional components to render
@description
Defines an alternative component title bar.
If specified, the icon, label and title properties are rendered in an
`<Label/>` component.
By default, the component title bar is labelled according to the component
properties.
*/ */
export const TitleBar = ({ icon, label, title, children }: any) => { export const TitleBar = ({ icon, label, title, children }: any) => {
const context: any = React.useContext(TitleContext); const context = React.useContext(TITLE);
const { id } = context;
if (!id) return null;
return ( return (
<DefineElement id={`labview.title.${context.id}`}> <DefineElement id={`labview.title.${id}`}>
<Label <Label
className="labview-handle" className="labview-handle"
icon={icon} icon={icon}
...@@ -355,9 +411,9 @@ const makeGridItem = (customize: any, onClose: any) => (comp: any) => { ...@@ -355,9 +411,9 @@ const makeGridItem = (customize: any, onClose: any) => (comp: any) => {
</Hfill> </Hfill>
{CLOSING} {CLOSING}
</Hbox> </Hbox>
<TitleContext.Provider value={{ id, label, title }}> <TITLE.Provider value={{ id, label, title }}>
<Catch label={id}>{children}</Catch> <Catch label={id}>{children}</Catch>
</TitleContext.Provider> </TITLE.Provider>
</Vfill> </Vfill>
</Grids.GridItem> </Grids.GridItem>
); );
...@@ -595,7 +651,7 @@ function CustomGroup({ ...@@ -595,7 +651,7 @@ function CustomGroup({
function CustomizePanel( function CustomizePanel(
{ dnd, settings, library, shape, setShape, setDragging }: any, { dnd, settings, library, shape, setShape, setDragging }: any,
) { ) {
Dome.useUpdate(Library.update); Dome.useUpdate(UPDATE);
const { items } = library; const { items } = library;
const views = getItems(items, 'views'); const views = getItems(items, 'views');
const groups = getItems(items, 'groups'); const groups = getItems(items, 'groups');
...@@ -640,20 +696,6 @@ function CustomizePanel( ...@@ -640,20 +696,6 @@ function CustomizePanel(
); );
} }
// --------------------------------------------------------------------------
// --- Global Consolidated Library
// --------------------------------------------------------------------------
const LIBRARY = new Library();
function useLibrary() {
const libRef = React.useRef(LIBRARY);
// Hot Reload detection
if (LIBRARY.updateFrom(libRef.current)) {
libRef.current = LIBRARY;
}
return LIBRARY;
}
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// --- LabView Container // --- LabView Container
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
...@@ -679,7 +721,7 @@ export function LabView(props: any) { ...@@ -679,7 +721,7 @@ export function LabView(props: any) {
const settingPanel = settings && `${settings}.panel`; const settingPanel = settings && `${settings}.panel`;
// Hooks & State // Hooks & State
Dome.useUpdate( Dome.useUpdate(
Library.update, UPDATE,
Dome.windowSettings, Dome.windowSettings,
Dome.globalSettings, Dome.globalSettings,
); );
...@@ -700,7 +742,7 @@ export function LabView(props: any) { ...@@ -700,7 +742,7 @@ export function LabView(props: any) {
// Make view // Make view
return ( return (
<> <>
<UseLibrary library={lib}> <UseLibrary>
{children} {children}
</UseLibrary> </UseLibrary>
<RSplit margin={120} settings={settingSplit} unfold={customize}> <RSplit margin={120} settings={settingSplit} unfold={customize}>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment