JupyterLab React-based UI components library providing icons, forms, buttons, and widgets for consistent interface development.
Helper functions and utilities for styling, DOM manipulation, positioning, and React integration. These utilities provide common functionality needed when building JupyterLab applications and widgets.
Functions for managing and combining CSS class names in a clean, efficient way.
/**
* Combine CSS class names, filtering out falsy values
* @param classes - Array of class names, objects, or falsy values
* @returns Single combined class string
*/
function classes(
...classes: (string | false | undefined | null | { [className: string]: any })[]
): string;
/**
* Combine CSS class names removing duplicates
* @param classes - Array of class names, objects, or falsy values
* @returns Single combined class string without duplicates
*/
function classesDedupe(
...classes: (string | false | undefined | null | { [className: string]: any })[]
): string;Usage Examples:
import { classes, classesDedupe } from '@jupyterlab/ui-components';
// Basic class combination
const buttonClass = classes(
'jp-Button',
isActive && 'jp-mod-active',
isDisabled && 'jp-mod-disabled',
customClass
);
// Conditional classes with objects
const widgetClass = classes(
'jp-Widget',
{
'jp-mod-hidden': !visible,
'jp-mod-focused': hasFocus,
'jp-mod-current': isCurrent
},
additionalClasses
);
// Remove duplicates when combining from multiple sources
const combinedClass = classesDedupe(
baseClasses,
themeClasses,
stateClasses,
userClasses
);
// Use in React components
function MyButton({ active, disabled, className }: ButtonProps) {
return (
<button
className={classes(
'my-button',
active && 'active',
disabled && 'disabled',
className
)}
>
Click me
</button>
);
}
// Complex conditional styling
const iconClass = classes(
'icon',
size === 'small' && 'icon-small',
size === 'large' && 'icon-large',
{
'icon-spin': spinning,
'icon-disabled': !enabled,
'icon-primary': variant === 'primary',
'icon-secondary': variant === 'secondary'
}
);Functions for working with DOM elements and converting between DOM and React patterns.
/**
* Convert DOM element attributes to React props
* @param elem - DOM element to extract attributes from
* @param options - Options for conversion
* @returns Object with React-compatible props
*/
function getReactAttrs(
elem: Element,
options?: { ignore?: string[] }
): { [key: string]: string | null };
/**
* Find tree item element in DOM hierarchy
* @param el - Element to start search from
* @returns Tree item element or null if not found
*/
function getTreeItemElement(el: HTMLElement): HTMLElement | null;Usage Examples:
import { getReactAttrs, getTreeItemElement } from '@jupyterlab/ui-components';
// Convert DOM element to React props
function domElementToReact(element: Element) {
const props = getReactAttrs(element, {
ignore: ['style', 'class'] // Ignore these attributes
});
return (
<div {...props}>
Converted from DOM element
</div>
);
}
// Handle tree interactions
function handleTreeClick(event: MouseEvent) {
const target = event.target as HTMLElement;
const treeItem = getTreeItemElement(target);
if (treeItem) {
const itemId = treeItem.dataset.itemId;
console.log('Clicked tree item:', itemId);
// Handle tree item selection
selectTreeItem(itemId);
}
}
// Widget that uses DOM utilities
class InteractiveWidget extends Widget {
onAfterAttach() {
super.onAfterAttach();
// Set up tree interaction handlers
this.node.addEventListener('click', (event) => {
const treeItem = getTreeItemElement(event.target as HTMLElement);
if (treeItem) {
this.handleTreeItemClick(treeItem);
}
});
}
private handleTreeItemClick(element: HTMLElement) {
// Convert DOM attributes to work with
const attrs = getReactAttrs(element);
console.log('Tree item attributes:', attrs);
}
}
// Extract data attributes
function extractDataAttributes(element: Element) {
const attrs = getReactAttrs(element);
const dataAttrs: { [key: string]: string } = {};
Object.entries(attrs).forEach(([key, value]) => {
if (key.startsWith('data-') && value !== null) {
dataAttrs[key] = value;
}
});
return dataAttrs;
}Utility for positioning hover elements and tooltips relative to anchor elements.
/**
* HoverBox positioning utilities
*/
type OutOfViewDisplay = 'hidden-inside' | 'hidden-outside' | 'stick-inside' | 'stick-outside';
namespace HoverBox {
interface IOptions {
/** Anchor element or position to attach to */
anchor: IAnchor;
/** Host element that contains the positioned element */
host: HTMLElement;
/** Maximum height for the positioned element */
maxHeight: number;
/** Minimum height for the positioned element */
minHeight: number;
/** Element to be positioned */
node: HTMLElement;
/** Positioning offsets */
offset?: {
horizontal?: number;
vertical?: { above?: number; below?: number };
};
/** Vertical positioning preference */
privilege?: 'above' | 'below' | 'forceAbove' | 'forceBelow';
/** Custom style overrides */
style?: CSSStyleDeclaration;
/** Behavior when element goes out of view */
outOfViewDisplay?: {
top?: OutOfViewDisplay;
bottom?: OutOfViewDisplay;
left?: OutOfViewDisplay;
right?: OutOfViewDisplay;
};
/** Fixed size for the element */
size?: { width: number; height: number; };
}
/** Anchor position interface */
interface IAnchor extends Pick<DOMRect, 'left' | 'right' | 'top' | 'bottom'> {}
/**
* Set geometry for positioned element
* @param options - Positioning configuration
*/
function setGeometry(options: IOptions): void;
}Usage Examples:
import { HoverBox } from '@jupyterlab/ui-components';
// Create tooltip positioning
function createTooltip(anchorElement: HTMLElement, content: string) {
const tooltip = document.createElement('div');
tooltip.className = 'tooltip';
tooltip.textContent = content;
tooltip.style.position = 'absolute';
tooltip.style.zIndex = '1000';
document.body.appendChild(tooltip);
// Position tooltip relative to anchor
const anchorRect = anchorElement.getBoundingClientRect();
HoverBox.setGeometry({
anchor: {
left: anchorRect.left,
right: anchorRect.right,
top: anchorRect.top,
bottom: anchorRect.bottom
},
host: document.body,
node: tooltip,
maxHeight: 200,
minHeight: 20,
privilege: 'above',
offset: {
vertical: { above: 5, below: 5 },
horizontal: 0
}
});
return tooltip;
}
// Advanced positioning for dropdown
class DropdownWidget extends Widget {
private _dropdown: HTMLElement;
constructor() {
super();
this._dropdown = this.createDropdown();
}
showDropdown() {
const buttonRect = this.node.getBoundingClientRect();
HoverBox.setGeometry({
anchor: {
left: buttonRect.left,
right: buttonRect.right,
top: buttonRect.top,
bottom: buttonRect.bottom
},
host: document.body,
node: this._dropdown,
maxHeight: 300,
minHeight: 50,
privilege: 'below',
offset: {
vertical: { below: 2 }
},
outOfViewDisplay: {
bottom: 'stick',
top: 'stick'
}
});
this._dropdown.style.display = 'block';
}
hideDropdown() {
this._dropdown.style.display = 'none';
}
private createDropdown(): HTMLElement {
const dropdown = document.createElement('div');
dropdown.className = 'dropdown-menu';
dropdown.style.position = 'absolute';
dropdown.style.display = 'none';
dropdown.style.zIndex = '1000';
document.body.appendChild(dropdown);
return dropdown;
}
}
// Context menu positioning
function showContextMenu(event: MouseEvent, items: MenuItem[]) {
const menu = document.createElement('div');
menu.className = 'context-menu';
items.forEach(item => {
const menuItem = document.createElement('div');
menuItem.className = 'context-menu-item';
menuItem.textContent = item.label;
menuItem.onclick = item.action;
menu.appendChild(menuItem);
});
document.body.appendChild(menu);
// Position at mouse location
HoverBox.setGeometry({
anchor: {
left: event.clientX,
right: event.clientX,
top: event.clientY,
bottom: event.clientY
},
host: document.body,
node: menu,
maxHeight: 400,
minHeight: 30,
privilege: 'below',
outOfViewDisplay: {
right: 'stick',
bottom: 'stick'
}
});
}Interface for managing element references in React components.
/**
* Interface for element reference properties
*/
interface IElementRefProps<E extends HTMLElement> {
/** Callback function to receive element reference */
elementRef?: (ref: E | null) => void;
}
/**
* Default style class constant
*/
const DEFAULT_STYLE_CLASS = 'jp-DefaultStyle';Usage Examples:
import { IElementRefProps, DEFAULT_STYLE_CLASS } from '@jupyterlab/ui-components';
// Component with element reference
interface CustomInputProps extends IElementRefProps<HTMLInputElement> {
placeholder?: string;
value?: string;
onChange?: (value: string) => void;
}
function CustomInput({ elementRef, placeholder, value, onChange }: CustomInputProps) {
const handleRef = (element: HTMLInputElement | null) => {
if (element) {
element.classList.add(DEFAULT_STYLE_CLASS);
}
elementRef?.(element);
};
return (
<input
ref={handleRef}
placeholder={placeholder}
value={value}
onChange={(e) => onChange?.(e.target.value)}
className="custom-input"
/>
);
}
// Use with element reference
function ParentComponent() {
const [inputElement, setInputElement] = useState<HTMLInputElement | null>(null);
const focusInput = () => {
inputElement?.focus();
};
return (
<div>
<CustomInput
elementRef={setInputElement}
placeholder="Enter text..."
/>
<button onClick={focusInput}>Focus Input</button>
</div>
);
}
// Widget that uses element references
class FormWidget extends ReactWidget {
private _formElement: HTMLFormElement | null = null;
protected render() {
return (
<form
ref={(el) => this._formElement = el}
className={DEFAULT_STYLE_CLASS}
>
<CustomInput
elementRef={(el) => console.log('Input element:', el)}
placeholder="Widget input"
/>
</form>
);
}
submitForm() {
if (this._formElement) {
this._formElement.requestSubmit();
}
}
}
// Generic element ref hook pattern
function useElementRef<T extends HTMLElement>() {
const [element, setElement] = useState<T | null>(null);
const ref = useCallback((el: T | null) => {
if (el) {
el.classList.add(DEFAULT_STYLE_CLASS);
}
setElement(el);
}, []);
return [element, ref] as const;
}Utilities for applying consistent styling to DOM elements and their children.
/**
* Namespace for DOM node styling utilities
*/
namespace Styling {
/**
* Style a node and its child elements with default tag names
* @param node - Base DOM element to style
* @param className - Optional CSS class to add to styled nodes
*/
function styleNode(node: HTMLElement, className?: string): void;
/**
* Style a node and elements with a specific tag name
* @param node - Base DOM element to style
* @param tagName - HTML tag name to target for styling
* @param className - Optional CSS class to add to styled nodes
*/
function styleNodeByTag(node: HTMLElement, tagName: string, className?: string): void;
/**
* Wrap select elements with custom styling container
* @param select - Select element to wrap
* @param multiple - Whether select allows multiple selections
*/
function wrapSelect(select: HTMLSelectElement, multiple?: boolean): void;
}Usage Examples:
import { Styling } from '@jupyterlab/ui-components';
// Style all form elements in a container
function styleFormContainer(container: HTMLElement) {
// Apply default styling to common form elements
Styling.styleNode(container, 'custom-form-style');
// This will add 'jp-mod-styled' class to all:
// - select elements
// - textarea elements
// - input elements
// - button elements
}
// Style specific element types
function styleSpecificElements(container: HTMLElement) {
// Style only input elements
Styling.styleNodeByTag(container, 'input', 'styled-input');
// Style only buttons
Styling.styleNodeByTag(container, 'button', 'styled-button');
// Style textareas with special class
Styling.styleNodeByTag(container, 'textarea', 'styled-textarea');
}
// Widget that applies styling on render
class StyledFormWidget extends Widget {
onAfterAttach() {
super.onAfterAttach();
// Apply JupyterLab styling to all form elements
Styling.styleNode(this.node);
}
addFormField(fieldType: string, className?: string) {
const field = document.createElement(fieldType);
this.node.appendChild(field);
// Style the new field specifically
Styling.styleNodeByTag(this.node, fieldType, className);
}
}
// Custom select wrapper usage
function createStyledSelect(options: string[], multiple = false) {
const select = document.createElement('select');
select.multiple = multiple;
options.forEach(option => {
const optionElement = document.createElement('option');
optionElement.value = option;
optionElement.textContent = option;
select.appendChild(optionElement);
});
// Wrap with custom styling - handled automatically by styleNode
const container = document.createElement('div');
container.appendChild(select);
Styling.styleNode(container);
return container;
}
// Apply styling to dynamically created content
function createStyledDialog(content: HTMLElement) {
const dialog = document.createElement('div');
dialog.className = 'dialog-container';
dialog.appendChild(content);
// Ensure all form controls are properly styled
Styling.styleNode(dialog, 'dialog-form');
return dialog;
}
// Utility for styling imported HTML content
function styleImportedContent(htmlContent: string) {
const container = document.createElement('div');
container.innerHTML = htmlContent;
// Style all form elements in the imported content
Styling.styleNode(container);
return container;
}Additional type definitions and constants used throughout the utilities.
// SVG type definitions (from svg.d.ts)
declare module '*.svg' {
const value: string;
export default value;
}
// React render element type
type ReactRenderElement = React.ReactElement<any> | null;
// Common interfaces and types used across utilities
interface IChangedArgs<T, U, K extends string> {
name: K;
oldValue: T;
newValue: U;
}Usage Examples:
// Import SVG files as strings
import iconSvg from './my-icon.svg';
// Create LabIcon from imported SVG
const myIcon = new LabIcon({
name: 'my-app:my-icon',
svgstr: iconSvg
});
// Use changed args in signals
class CounterModel extends VDomModel {
private _count = 0;
get count(): number {
return this._count;
}
set count(value: number) {
const oldValue = this._count;
this._count = value;
// Emit change signal with typed args
this.stateChanged.emit({
name: 'count',
oldValue,
newValue: value
} as IChangedArgs<number, number, 'count'>);
}
}
// Utility functions that work with React elements
function isValidRenderElement(element: any): element is ReactRenderElement {
return element === null || React.isValidElement(element);
}
function renderIfValid(element: ReactRenderElement) {
if (isValidRenderElement(element)) {
return ReactDOM.render(element, container);
}
return null;
}tessl i tessl/npm-jupyterlab--ui-components@4.4.0evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10