mirror of
https://github.com/rio-labs/rio.git
synced 2026-01-06 05:09:43 -06:00
add auto_focus parameter (and refactor TS)
This commit is contained in:
@@ -63,6 +63,7 @@ import { ThemeContextSwitcherComponent } from "./components/themeContextSwitcher
|
||||
import { TooltipComponent } from "./components/tooltip";
|
||||
import { WebviewComponent } from "./components/webview";
|
||||
import { GraphEditorComponent } from "./components/graphEditor/graphEditor";
|
||||
import { KeyboardFocusableComponent } from "./components/keyboardFocusableComponent";
|
||||
|
||||
const COMPONENT_CLASSES = {
|
||||
"Button-builtin": ButtonComponent,
|
||||
@@ -334,10 +335,7 @@ export function updateComponentStates(
|
||||
component.updateElement(deltaState, latentComponents);
|
||||
|
||||
// Update the component's state
|
||||
component.state = {
|
||||
...component.state,
|
||||
...deltaState,
|
||||
};
|
||||
Object.assign(component.state, deltaState);
|
||||
}
|
||||
|
||||
// Notify the parents of all elements whose `_grow_` changed to update their
|
||||
@@ -397,11 +395,6 @@ export function recursivelyDeleteComponent(component: ComponentBase): void {
|
||||
component.element.remove();
|
||||
}
|
||||
|
||||
function canHaveKeyboardFocus(instance: ComponentBase): boolean {
|
||||
// @ts-expect-error
|
||||
return typeof instance.grabKeyboardFocus === "function";
|
||||
}
|
||||
|
||||
function restoreKeyboardFocus(
|
||||
focusedComponent: ComponentBase,
|
||||
latentComponents: Set<ComponentBase>
|
||||
@@ -415,7 +408,7 @@ function restoreKeyboardFocus(
|
||||
// itself might be about to die.
|
||||
let rootComponent = getRootComponent();
|
||||
let current = focusedComponent;
|
||||
let winner: ComponentBase | null = null;
|
||||
let winner: KeyboardFocusableComponent | null = null;
|
||||
|
||||
while (current !== rootComponent) {
|
||||
// If this component is dead, no child of it can get the keyboard focus
|
||||
@@ -425,7 +418,10 @@ function restoreKeyboardFocus(
|
||||
|
||||
// If we don't currently know of a focusable (and live) component, check
|
||||
// if this one fits the bill
|
||||
else if (winner === null && canHaveKeyboardFocus(current)) {
|
||||
else if (
|
||||
winner === null &&
|
||||
current instanceof KeyboardFocusableComponent
|
||||
) {
|
||||
winner = current;
|
||||
}
|
||||
|
||||
@@ -434,7 +430,6 @@ function restoreKeyboardFocus(
|
||||
|
||||
// We made it to the root. Do we have a winner?
|
||||
if (winner !== null) {
|
||||
// @ts-expect-error
|
||||
winner.grabKeyboardFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import { applySwitcheroo } from "../designApplication";
|
||||
import { ColorSet, ComponentId } from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { RippleEffect } from "../rippleEffect";
|
||||
import { markEventAsHandled } from "../eventHandling";
|
||||
import { getAllocatedHeightInPx, getAllocatedWidthInPx } from "../utils";
|
||||
|
||||
type AbstractButtonState = ComponentState & {
|
||||
shape?: "pill" | "rounded" | "rectangle" | "circle";
|
||||
style?: "major" | "minor" | "colored-text" | "plain-text";
|
||||
color?: ColorSet;
|
||||
content?: ComponentId;
|
||||
is_sensitive?: boolean;
|
||||
shape: "pill" | "rounded" | "rectangle" | "circle";
|
||||
style: "major" | "minor" | "colored-text" | "plain-text";
|
||||
color: ColorSet;
|
||||
content: ComponentId;
|
||||
is_sensitive: boolean;
|
||||
};
|
||||
|
||||
abstract class AbstractButtonComponent extends ComponentBase {
|
||||
declare state: Required<AbstractButtonState>;
|
||||
|
||||
abstract class AbstractButtonComponent extends ComponentBase<AbstractButtonState> {
|
||||
// This is the element with the `rio-button` class. The subclass is
|
||||
// responsible for creating it (by calling `createButtonElement()`).
|
||||
protected buttonElement: HTMLElement;
|
||||
@@ -66,7 +64,7 @@ abstract class AbstractButtonComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: AbstractButtonState,
|
||||
deltaState: DeltaState<AbstractButtonState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
@@ -133,8 +131,6 @@ export type ButtonState = AbstractButtonState & {
|
||||
};
|
||||
|
||||
export class ButtonComponent extends AbstractButtonComponent {
|
||||
declare state: Required<ButtonState>;
|
||||
|
||||
createElement(): HTMLElement {
|
||||
this.buttonElement = this.createButtonElement();
|
||||
this.buttonElement.role = "button";
|
||||
@@ -148,8 +144,6 @@ export type IconButtonState = AbstractButtonState & {
|
||||
};
|
||||
|
||||
export class IconButtonComponent extends AbstractButtonComponent {
|
||||
declare state: Required<IconButtonState>;
|
||||
|
||||
private resizeObserver: ResizeObserver;
|
||||
|
||||
protected createElement(): HTMLElement {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { applyIcon } from "../designApplication";
|
||||
import { markEventAsHandled } from "../eventHandling";
|
||||
|
||||
@@ -7,17 +7,15 @@ const CALENDAR_HEIGHT = 17.8;
|
||||
|
||||
export type CalendarState = ComponentState & {
|
||||
_type_: "Calendar-builtin";
|
||||
selectedYear?: number;
|
||||
selectedMonth?: number; // [1, 12]
|
||||
selectedDay?: number; // [1, ...]
|
||||
monthNamesLong?: Array<string>;
|
||||
dayNamesLong?: Array<string>;
|
||||
firstDayOfWeek?: number;
|
||||
selectedYear: number;
|
||||
selectedMonth: number; // [1, 12]
|
||||
selectedDay: number; // [1, ...]
|
||||
monthNamesLong: Array<string>;
|
||||
dayNamesLong: Array<string>;
|
||||
firstDayOfWeek: number;
|
||||
};
|
||||
|
||||
export class CalendarComponent extends ComponentBase {
|
||||
declare state: Required<CalendarState>;
|
||||
|
||||
export class CalendarComponent extends ComponentBase<CalendarState> {
|
||||
// Internal HTML Elements
|
||||
private prevYearButton: HTMLElement;
|
||||
private prevMonthButton: HTMLElement;
|
||||
@@ -104,7 +102,7 @@ export class CalendarComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: CalendarState,
|
||||
deltaState: DeltaState<CalendarState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
import { applySwitcheroo } from "../designApplication";
|
||||
import { ColorSet, ComponentId } from "../dataModels";
|
||||
import { RippleEffect } from "../rippleEffect";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { markEventAsHandled } from "../eventHandling";
|
||||
|
||||
export type CardState = ComponentState & {
|
||||
_type_: "Card-builtin";
|
||||
content?: ComponentId;
|
||||
corner_radius?: number | [number, number, number, number];
|
||||
reportPress?: boolean;
|
||||
ripple?: boolean;
|
||||
elevate_on_hover?: boolean;
|
||||
colorize_on_hover?: boolean;
|
||||
color?: ColorSet;
|
||||
content: ComponentId;
|
||||
corner_radius: number | [number, number, number, number];
|
||||
reportPress: boolean;
|
||||
ripple: boolean;
|
||||
elevate_on_hover: boolean;
|
||||
colorize_on_hover: boolean;
|
||||
color: ColorSet;
|
||||
};
|
||||
|
||||
export class CardComponent extends ComponentBase {
|
||||
declare state: Required<CardState>;
|
||||
|
||||
export class CardComponent extends ComponentBase<CardState> {
|
||||
// If this card has a ripple effect, this is the ripple instance. `null`
|
||||
// otherwise.
|
||||
private rippleInstance: RippleEffect | null = null;
|
||||
@@ -46,7 +44,7 @@ export class CardComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: CardState,
|
||||
deltaState: DeltaState<CardState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { applyIcon } from "../designApplication";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type CheckboxState = ComponentState & {
|
||||
_type_: "Checkbox-builtin";
|
||||
is_on?: boolean;
|
||||
is_sensitive?: boolean;
|
||||
is_on: boolean;
|
||||
is_sensitive: boolean;
|
||||
};
|
||||
|
||||
export class CheckboxComponent extends ComponentBase {
|
||||
declare state: Required<CheckboxState>;
|
||||
|
||||
export class CheckboxComponent extends ComponentBase<CheckboxState> {
|
||||
private checkboxElement: HTMLInputElement;
|
||||
private borderElement: HTMLElement;
|
||||
private checkElement: HTMLElement;
|
||||
@@ -52,7 +50,7 @@ export class CheckboxComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: CheckboxState,
|
||||
deltaState: DeltaState<CheckboxState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { ComponentId } from "../dataModels";
|
||||
|
||||
export type ClassContainerState = ComponentState & {
|
||||
_type_: "ClassContainer-builtin";
|
||||
content?: ComponentId | null;
|
||||
classes?: string[];
|
||||
content: ComponentId | null;
|
||||
classes: string[];
|
||||
};
|
||||
|
||||
export class ClassContainerComponent extends ComponentBase {
|
||||
declare state: Required<ClassContainerState>;
|
||||
|
||||
export class ClassContainerComponent extends ComponentBase<ClassContainerState> {
|
||||
createElement(): HTMLElement {
|
||||
return document.createElement("div");
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: ClassContainerState,
|
||||
deltaState: DeltaState<ClassContainerState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
// This import decides which languages are supported by `highlight.js`. See
|
||||
// their docs for details:
|
||||
@@ -11,15 +11,6 @@ import { setClipboard } from "../utils";
|
||||
import { applyIcon } from "../designApplication";
|
||||
import { markEventAsHandled } from "../eventHandling";
|
||||
|
||||
export type CodeBlockState = ComponentState & {
|
||||
_type_: "CodeBlock-builtin";
|
||||
code?: string;
|
||||
language?: string | null;
|
||||
show_controls?: boolean;
|
||||
scroll_code_x?: "never" | "auto" | "always";
|
||||
scroll_code_y?: "never" | "auto" | "always";
|
||||
};
|
||||
|
||||
/// Contains additional aliases for languages that are not recognized by
|
||||
/// highlight.js
|
||||
const languageAliases: { [key: string]: string } = {
|
||||
@@ -133,16 +124,23 @@ export function convertDivToCodeBlock(
|
||||
}
|
||||
}
|
||||
|
||||
export class CodeBlockComponent extends ComponentBase {
|
||||
declare state: Required<CodeBlockState>;
|
||||
export type CodeBlockState = ComponentState & {
|
||||
_type_: "CodeBlock-builtin";
|
||||
code: string;
|
||||
language: string | null;
|
||||
show_controls: boolean;
|
||||
scroll_code_x: "never" | "auto" | "always";
|
||||
scroll_code_y: "never" | "auto" | "always";
|
||||
};
|
||||
|
||||
export class CodeBlockComponent extends ComponentBase<CodeBlockState> {
|
||||
createElement(): HTMLElement {
|
||||
const element = document.createElement("div");
|
||||
return element;
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: CodeBlockState,
|
||||
deltaState: DeltaState<CodeBlockState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import hljs from "highlight.js/lib/common";
|
||||
import { componentsByElement, componentsById } from "../componentManagement";
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { applyIcon } from "../designApplication";
|
||||
|
||||
export type CodeExplorerState = ComponentState & {
|
||||
_type_: "CodeExplorer-builtin";
|
||||
source_code?: string;
|
||||
build_result?: ComponentId;
|
||||
line_indices_to_component_keys?: (string | number | null)[];
|
||||
style?: "horizontal" | "vertical";
|
||||
source_code: string;
|
||||
build_result: ComponentId;
|
||||
line_indices_to_component_keys: (string | number | null)[];
|
||||
style: "horizontal" | "vertical";
|
||||
};
|
||||
|
||||
export class CodeExplorerComponent extends ComponentBase {
|
||||
declare state: Required<CodeExplorerState>;
|
||||
|
||||
export class CodeExplorerComponent extends ComponentBase<CodeExplorerState> {
|
||||
private sourceCodeElement: HTMLElement;
|
||||
private arrowElement: HTMLElement;
|
||||
private buildResultElement: HTMLElement;
|
||||
@@ -62,7 +60,7 @@ export class CodeExplorerComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: CodeExplorerState,
|
||||
deltaState: DeltaState<CodeExplorerState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import { Color } from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { hsvToRgb, rgbToHsv, rgbToHex, rgbaToHex } from "../colorConversion";
|
||||
import { markEventAsHandled } from "../eventHandling";
|
||||
|
||||
export type ColorPickerState = ComponentState & {
|
||||
_type_: "ColorPicker-builtin";
|
||||
color?: Color;
|
||||
pick_opacity?: boolean;
|
||||
color: Color;
|
||||
pick_opacity: boolean;
|
||||
};
|
||||
|
||||
export class ColorPickerComponent extends ComponentBase {
|
||||
declare state: Required<ColorPickerState>;
|
||||
|
||||
export class ColorPickerComponent extends ComponentBase<ColorPickerState> {
|
||||
private colorSquare: HTMLElement;
|
||||
private squareKnob: HTMLElement;
|
||||
|
||||
@@ -116,7 +114,7 @@ export class ColorPickerComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: ColorPickerState,
|
||||
deltaState: DeltaState<ColorPickerState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
@@ -357,7 +355,7 @@ export class ColorPickerComponent extends ComponentBase {
|
||||
|
||||
setFromUserHex(event: Event) {
|
||||
// Try to parse the value
|
||||
let color = this.lenientlyParseColorHex(event.target.value);
|
||||
let color = this.lenientlyParseColorHex(this.selectedColorLabel.value);
|
||||
|
||||
// Invalid color
|
||||
if (color === null) {
|
||||
@@ -375,7 +373,7 @@ export class ColorPickerComponent extends ComponentBase {
|
||||
this.matchComponentToSelectedHsv();
|
||||
|
||||
// Deselect the text input
|
||||
event.target.blur();
|
||||
this.selectedColorLabel.blur();
|
||||
|
||||
// Send the final color to the frontend
|
||||
this.sendMessageToBackend({
|
||||
|
||||
@@ -20,39 +20,38 @@ import { devToolsConnector } from "../app";
|
||||
export type ComponentState = {
|
||||
// The component type's unique id. Crucial so the client knows what kind of
|
||||
// component to spawn.
|
||||
_type_?: string;
|
||||
readonly _type_: string;
|
||||
// Debugging information. Useful both for developing rio itself, and also
|
||||
// displayed to developers in Rio's dev tools
|
||||
_python_type_?: string;
|
||||
_python_type_: string;
|
||||
// Debugging information
|
||||
_key_?: string | number | null;
|
||||
_key_: string | number | null;
|
||||
// How much space to leave on the left, top, right, bottom
|
||||
_margin_?: [number, number, number, number];
|
||||
_margin_: [number, number, number, number];
|
||||
// Explicit size request, if any
|
||||
_min_size_?: [number, number];
|
||||
_min_size_: [number, number];
|
||||
// Maximum size, if any
|
||||
// MAX-SIZE-BRANCH _max_size_?: [number | null, number | null];
|
||||
// Alignment of the component within its parent, if any
|
||||
_align_?: [number | null, number | null];
|
||||
_align_: [number | null, number | null];
|
||||
// Scrolling behavior
|
||||
// SCROLLING-REWORK _scroll_?: [RioScrollBehavior, RioScrollBehavior];
|
||||
// Whether the component would like to receive additional space if there is
|
||||
// any left over
|
||||
_grow_?: [boolean, boolean];
|
||||
_grow_: [boolean, boolean];
|
||||
// Debugging information: The dev tools may not display components to the
|
||||
// developer if they're considered internal
|
||||
_rio_internal_?: boolean;
|
||||
_rio_internal_: boolean;
|
||||
};
|
||||
|
||||
/// Base class for all components
|
||||
///
|
||||
/// Note: Components that can have the keyboard focus must also implement a
|
||||
/// `grabKeyboardFocus(): void` method.
|
||||
export abstract class ComponentBase {
|
||||
id: ComponentId;
|
||||
element: HTMLElement;
|
||||
export type DeltaState<S extends ComponentState> = Omit<Partial<S>, "_type_">;
|
||||
|
||||
state: Required<ComponentState>;
|
||||
/// Base class for all components
|
||||
export abstract class ComponentBase<S extends ComponentState = ComponentState> {
|
||||
readonly id: ComponentId;
|
||||
readonly element: HTMLElement;
|
||||
|
||||
readonly state: S;
|
||||
|
||||
// Reference to the parent component. If the component is about to be
|
||||
// removed from the component tree (i.e. it's in `latent-components`), this
|
||||
@@ -72,7 +71,7 @@ export abstract class ComponentBase {
|
||||
private centerScrollElement: HTMLElement | null = null;
|
||||
private innerScrollElement: HTMLElement | null = null;
|
||||
|
||||
constructor(id: ComponentId, state: Required<ComponentState>) {
|
||||
constructor(id: ComponentId, state: S) {
|
||||
this.id = id;
|
||||
this.state = state;
|
||||
|
||||
@@ -110,7 +109,7 @@ export abstract class ComponentBase {
|
||||
/// The `element` parameter is identical to `this.element`. It's passed as
|
||||
/// an argument because it's more efficient than calling `this.element`.
|
||||
updateElement(
|
||||
deltaState: ComponentState,
|
||||
deltaState: DeltaState<S>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
if (deltaState._min_size_ !== undefined) {
|
||||
@@ -551,15 +550,12 @@ export abstract class ComponentBase {
|
||||
});
|
||||
}
|
||||
|
||||
_setStateDontNotifyBackend(deltaState: object): void {
|
||||
_setStateDontNotifyBackend(deltaState: DeltaState<S>): void {
|
||||
// Trigger an update
|
||||
this.updateElement(deltaState, null as any as Set<ComponentBase>);
|
||||
|
||||
// Set the state
|
||||
this.state = {
|
||||
...this.state,
|
||||
...deltaState,
|
||||
};
|
||||
Object.assign(this.state, deltaState);
|
||||
|
||||
// Notify the dev tools, if any
|
||||
if (devToolsConnector !== null) {
|
||||
@@ -569,7 +565,7 @@ export abstract class ComponentBase {
|
||||
}
|
||||
}
|
||||
|
||||
setStateAndNotifyBackend(deltaState: object): void {
|
||||
setStateAndNotifyBackend(deltaState: DeltaState<S>): void {
|
||||
// Set the state. This also updates the component
|
||||
this._setStateDontNotifyBackend(deltaState);
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { devToolsConnector } from "../app";
|
||||
import { applyIcon } from "../designApplication";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type ComponentPickerState = ComponentState & {
|
||||
_type_: "ComponentPicker-builtin";
|
||||
};
|
||||
|
||||
export class ComponentPickerComponent extends ComponentBase {
|
||||
export class ComponentPickerComponent extends ComponentBase<ComponentPickerState> {
|
||||
protected createElement(): HTMLElement {
|
||||
let element = document.createElement("div");
|
||||
element.classList.add("rio-component-picker");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { componentsById } from "../componentManagement";
|
||||
import { applyIcon } from "../designApplication";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { Highlighter } from "../highlighter";
|
||||
import {
|
||||
getDisplayableChildren,
|
||||
@@ -12,12 +12,10 @@ import { findComponentUnderMouse } from "../utils";
|
||||
|
||||
export type ComponentTreeState = ComponentState & {
|
||||
_type_: "ComponentTree-builtin";
|
||||
component_id?: number;
|
||||
component_id: number;
|
||||
};
|
||||
|
||||
export class ComponentTreeComponent extends ComponentBase {
|
||||
declare state: Required<ComponentTreeState>;
|
||||
|
||||
export class ComponentTreeComponent extends ComponentBase<ComponentTreeState> {
|
||||
private highlighter = new Highlighter();
|
||||
|
||||
private nodesByComponent: WeakMap<ComponentBase, HTMLElement> =
|
||||
@@ -54,7 +52,7 @@ export class ComponentTreeComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: ComponentTreeState,
|
||||
deltaState: DeltaState<ComponentTreeState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
@@ -344,7 +342,7 @@ export class ComponentTreeComponent extends ComponentBase {
|
||||
|
||||
private nodeNeedsRebuild(
|
||||
component: ComponentBase,
|
||||
deltaState: ComponentState
|
||||
deltaState: DeltaState<ComponentState>
|
||||
): boolean {
|
||||
if ("key" in deltaState) {
|
||||
return true;
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { RippleEffect } from "../rippleEffect";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { ComponentId } from "../dataModels";
|
||||
|
||||
export type CustomListItemState = ComponentState & {
|
||||
_type_: "CustomListItem-builtin";
|
||||
content?: ComponentId;
|
||||
pressable?: boolean;
|
||||
content: ComponentId;
|
||||
pressable: boolean;
|
||||
};
|
||||
|
||||
export class CustomListItemComponent extends ComponentBase {
|
||||
declare state: Required<CustomListItemState>;
|
||||
|
||||
export class CustomListItemComponent extends ComponentBase<CustomListItemState> {
|
||||
// If this item has a ripple effect, this is the ripple instance. `null`
|
||||
// otherwise.
|
||||
private rippleInstance: RippleEffect | null = null;
|
||||
@@ -22,7 +20,7 @@ export class CustomListItemComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: CustomListItemState,
|
||||
deltaState: DeltaState<CustomListItemState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { setDevToolsConnector } from "../app";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { ComponentTreeComponent } from "./componentTree";
|
||||
|
||||
export type DevToolsConnectorState = ComponentState & {
|
||||
_type_: "DevToolsConnector-builtin";
|
||||
};
|
||||
|
||||
export class DevToolsConnectorComponent extends ComponentBase {
|
||||
declare state: Required<DevToolsConnectorState>;
|
||||
|
||||
export class DevToolsConnectorComponent extends ComponentBase<DevToolsConnectorState> {
|
||||
// If component tree components exists, they register here
|
||||
public componentTreeComponent: ComponentTreeComponent | null = null;
|
||||
|
||||
|
||||
@@ -5,19 +5,17 @@ import {
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { FullscreenPositioner, PopupManager } from "../popupManager";
|
||||
import { callRemoteMethodDiscardResponse } from "../rpc";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type DialogContainerState = ComponentState & {
|
||||
_type_: "DialogContainer-builtin";
|
||||
content?: ComponentId;
|
||||
owning_component_id?: ComponentId;
|
||||
is_modal?: boolean;
|
||||
is_user_closable?: boolean;
|
||||
content: ComponentId;
|
||||
owning_component_id: ComponentId;
|
||||
is_modal: boolean;
|
||||
is_user_closable: boolean;
|
||||
};
|
||||
|
||||
export class DialogContainerComponent extends ComponentBase {
|
||||
declare state: Required<DialogContainerState>;
|
||||
|
||||
export class DialogContainerComponent extends ComponentBase<DialogContainerState> {
|
||||
private contentContainer: HTMLElement;
|
||||
|
||||
// Dialogs are displayed via a popup manager. While this isn't strictly
|
||||
@@ -104,7 +102,7 @@ export class DialogContainerComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: DialogContainerState,
|
||||
deltaState: DeltaState<DialogContainerState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
import { pixelsPerRem } from "../app";
|
||||
import { commitCss } from "../utils";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { ColorSet, ComponentId } from "../dataModels";
|
||||
import { applySwitcheroo } from "../designApplication";
|
||||
import { markEventAsHandled } from "../eventHandling";
|
||||
|
||||
export type DrawerState = ComponentState & {
|
||||
_type_: "Drawer-builtin";
|
||||
anchor?: ComponentId;
|
||||
content?: ComponentId;
|
||||
side?: "left" | "right" | "top" | "bottom";
|
||||
is_modal?: boolean;
|
||||
is_open?: boolean;
|
||||
is_user_openable?: boolean;
|
||||
color?: ColorSet;
|
||||
anchor: ComponentId;
|
||||
content: ComponentId;
|
||||
side: "left" | "right" | "top" | "bottom";
|
||||
is_modal: boolean;
|
||||
is_open: boolean;
|
||||
is_user_openable: boolean;
|
||||
color: ColorSet;
|
||||
};
|
||||
|
||||
export class DrawerComponent extends ComponentBase {
|
||||
declare state: Required<DrawerState>;
|
||||
|
||||
export class DrawerComponent extends ComponentBase<DrawerState> {
|
||||
private anchorContainer: HTMLElement;
|
||||
private contentOuterContainer: HTMLElement;
|
||||
private contentInnerContainer: HTMLElement;
|
||||
@@ -74,7 +72,7 @@ export class DrawerComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: DrawerState,
|
||||
deltaState: DeltaState<DrawerState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, DeltaState } from "./componentBase";
|
||||
import { applyIcon } from "../designApplication";
|
||||
import { InputBox, InputBoxStyle } from "../inputBox";
|
||||
import { markEventAsHandled } from "../eventHandling";
|
||||
import { DropdownPositioner, PopupManager } from "../popupManager";
|
||||
|
||||
export type DropdownState = ComponentState & {
|
||||
_type_: "Dropdown-builtin";
|
||||
optionNames?: string[];
|
||||
label?: string;
|
||||
accessibility_label?: string;
|
||||
style?: InputBoxStyle;
|
||||
selectedName?: string;
|
||||
is_sensitive?: boolean;
|
||||
is_valid?: boolean;
|
||||
};
|
||||
import {
|
||||
KeyboardFocusableComponent,
|
||||
KeyboardFocusableComponentState,
|
||||
} from "./keyboardFocusableComponent";
|
||||
|
||||
const SELECT_OPTION_EVENT = DropdownPositioner.USE_MOBILE_MODE
|
||||
? "click"
|
||||
: "pointerdown";
|
||||
|
||||
export class DropdownComponent extends ComponentBase {
|
||||
declare state: Required<DropdownState>;
|
||||
export type DropdownState = KeyboardFocusableComponentState & {
|
||||
_type_: "Dropdown-builtin";
|
||||
optionNames: string[];
|
||||
label: string;
|
||||
accessibility_label: string;
|
||||
style: InputBoxStyle;
|
||||
selectedName: string;
|
||||
is_sensitive: boolean;
|
||||
is_valid: boolean;
|
||||
};
|
||||
|
||||
export class DropdownComponent extends KeyboardFocusableComponent<DropdownState> {
|
||||
private inputBox: InputBox;
|
||||
private hiddenOptionsElement: HTMLElement;
|
||||
private popupElement: HTMLElement;
|
||||
@@ -468,7 +470,7 @@ export class DropdownComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: DropdownState,
|
||||
deltaState: DeltaState<DropdownState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { applyIcon } from "../designApplication";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type BuildFailedState = ComponentState & {
|
||||
_type_: "BuildFailed-builtin";
|
||||
export type ErrorPlaceholderState = ComponentState & {
|
||||
_type_: "ErrorPlaceholder-builtin";
|
||||
error_summary: string;
|
||||
error_details: string;
|
||||
};
|
||||
|
||||
export class ErrorPlaceholderComponent extends ComponentBase {
|
||||
declare state: Required<BuildFailedState>;
|
||||
|
||||
export class ErrorPlaceholderComponent extends ComponentBase<ErrorPlaceholderState> {
|
||||
private iconElement: HTMLElement;
|
||||
private summaryElement: HTMLElement;
|
||||
private detailsElement: HTMLElement;
|
||||
@@ -55,7 +53,7 @@ export class ErrorPlaceholderComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: BuildFailedState,
|
||||
deltaState: DeltaState<ErrorPlaceholderState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { applyIcon, applySwitcheroo } from "../designApplication";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { RippleEffect } from "../rippleEffect";
|
||||
import { markEventAsHandled } from "../eventHandling";
|
||||
import { ColorSetName, ComponentId } from "../dataModels";
|
||||
@@ -113,19 +113,17 @@ function getFileIcon(filename: string): string {
|
||||
|
||||
type FilePickerAreaState = ComponentState & {
|
||||
_type_: "FilePickerArea-builtin";
|
||||
child_text?: string | null;
|
||||
child_component?: ComponentId | null;
|
||||
file_types?: string[];
|
||||
multiple?: boolean;
|
||||
files?: {
|
||||
child_text: string | null;
|
||||
child_component: ComponentId | null;
|
||||
file_types: string[];
|
||||
multiple: boolean;
|
||||
files: {
|
||||
id: string;
|
||||
name: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export class FilePickerAreaComponent extends ComponentBase {
|
||||
declare state: Required<FilePickerAreaState>;
|
||||
|
||||
export class FilePickerAreaComponent extends ComponentBase<FilePickerAreaState> {
|
||||
private fileInput: HTMLInputElement;
|
||||
private iconElement: HTMLElement;
|
||||
private childContentContainer: HTMLElement;
|
||||
@@ -285,7 +283,7 @@ export class FilePickerAreaComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: FilePickerAreaState,
|
||||
deltaState: DeltaState<FilePickerAreaState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { componentsById } from "../componentManagement";
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type FlowState = ComponentState & {
|
||||
_type_: "FlowContainer-builtin";
|
||||
children?: ComponentId[];
|
||||
row_spacing?: number;
|
||||
column_spacing?: number;
|
||||
justify?: "left" | "center" | "right" | "justify" | "grow";
|
||||
children: ComponentId[];
|
||||
row_spacing: number;
|
||||
column_spacing: number;
|
||||
justify: "left" | "center" | "right" | "justify" | "grow";
|
||||
};
|
||||
|
||||
export class FlowComponent extends ComponentBase {
|
||||
declare state: Required<FlowState>;
|
||||
|
||||
export class FlowComponent extends ComponentBase<FlowState> {
|
||||
private innerElement: HTMLElement;
|
||||
|
||||
createElement(): HTMLElement {
|
||||
@@ -27,7 +25,7 @@ export class FlowComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: FlowState,
|
||||
deltaState: DeltaState<FlowState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ComponentId } from "../dataModels";
|
||||
import { Debouncer } from "../debouncer";
|
||||
import { callRemoteMethodDiscardResponse } from "../rpc";
|
||||
import { getAllocatedHeightInPx, getAllocatedWidthInPx } from "../utils";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
let notifyBackendOfWindowSizeChange = new Debouncer({
|
||||
callback: (width: number, height: number) => {
|
||||
@@ -26,9 +26,7 @@ export type FundamentalRootComponentState = ComponentState & {
|
||||
dev_tools: ComponentId | null;
|
||||
};
|
||||
|
||||
export class FundamentalRootComponent extends ComponentBase {
|
||||
declare state: Required<FundamentalRootComponentState>;
|
||||
|
||||
export class FundamentalRootComponent extends ComponentBase<FundamentalRootState> {
|
||||
private userRootContainer: HTMLElement;
|
||||
public userOverlaysContainer: HTMLElement;
|
||||
|
||||
@@ -127,7 +125,7 @@ export class FundamentalRootComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: FundamentalRootComponentState,
|
||||
deltaState: DeltaState<FundamentalRootComponentState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ComponentId } from "../../dataModels";
|
||||
import { ComponentBase, ComponentState } from "../componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "../componentBase";
|
||||
import { NodeInputComponent } from "../nodeInput";
|
||||
import {
|
||||
AugmentedConnectionState,
|
||||
@@ -22,12 +22,10 @@ import { CuttingConnectionStrategy } from "./cuttingConnectionStrategy";
|
||||
|
||||
export type GraphEditorState = ComponentState & {
|
||||
_type_: "GraphEditor-builtin";
|
||||
children?: ComponentId[];
|
||||
children: ComponentId[];
|
||||
};
|
||||
|
||||
export class GraphEditorComponent extends ComponentBase {
|
||||
declare state: Required<GraphEditorState>;
|
||||
|
||||
export class GraphEditorComponent extends ComponentBase<GraphEditorState> {
|
||||
private htmlChild: HTMLElement;
|
||||
public svgChild: SVGSVGElement;
|
||||
|
||||
@@ -83,7 +81,7 @@ export class GraphEditorComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: GraphEditorState,
|
||||
deltaState: DeltaState<GraphEditorState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { componentsById } from "../componentManagement";
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { range, zip } from "../utils";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
type GridChildPosition = {
|
||||
row: number;
|
||||
@@ -12,15 +12,13 @@ type GridChildPosition = {
|
||||
|
||||
export type GridState = ComponentState & {
|
||||
_type_: "Grid-builtin";
|
||||
_children?: ComponentId[];
|
||||
_child_positions?: GridChildPosition[];
|
||||
row_spacing?: number;
|
||||
column_spacing?: number;
|
||||
_children: ComponentId[];
|
||||
_child_positions: GridChildPosition[];
|
||||
row_spacing: number;
|
||||
column_spacing: number;
|
||||
};
|
||||
|
||||
export class GridComponent extends ComponentBase {
|
||||
declare state: Required<GridState>;
|
||||
|
||||
export class GridComponent extends ComponentBase<GridState> {
|
||||
createElement(): HTMLElement {
|
||||
let element = document.createElement("div");
|
||||
element.classList.add("rio-grid");
|
||||
@@ -28,7 +26,7 @@ export class GridComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: GridState,
|
||||
deltaState: DeltaState<GridState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { textStyleToCss } from "../cssUtils";
|
||||
|
||||
export type HeadingListItemState = ComponentState & {
|
||||
_type_: "HeadingListItem-builtin";
|
||||
text?: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
export class HeadingListItemComponent extends ComponentBase {
|
||||
declare state: Required<HeadingListItemState>;
|
||||
|
||||
export class HeadingListItemComponent extends ComponentBase<HeadingListItemState> {
|
||||
createElement(): HTMLElement {
|
||||
// Create the element
|
||||
let element = document.createElement("div");
|
||||
@@ -23,7 +21,7 @@ export class HeadingListItemComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: HeadingListItemState,
|
||||
deltaState: DeltaState<HeadingListItemState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { ComponentId } from "../dataModels";
|
||||
|
||||
export type HighLevelComponentState = ComponentState & {
|
||||
_type_: "HighLevelComponent-builtin";
|
||||
_child_?: ComponentId;
|
||||
_child_: ComponentId;
|
||||
};
|
||||
|
||||
export class HighLevelComponent extends ComponentBase {
|
||||
declare state: Required<HighLevelComponentState>;
|
||||
|
||||
export class HighLevelComponent extends ComponentBase<HighLevelState> {
|
||||
createElement(): HTMLElement {
|
||||
let element = document.createElement("div");
|
||||
element.classList.add("rio-high-level-component");
|
||||
@@ -16,7 +14,7 @@ export class HighLevelComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: HighLevelComponentState,
|
||||
deltaState: DeltaState<HighLevelComponentState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
RadialGradientFill,
|
||||
SolidFill,
|
||||
} from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { applyIcon, applyFillToSVG } from "../designApplication";
|
||||
|
||||
export type IconState = ComponentState & {
|
||||
@@ -22,9 +22,7 @@ export type IconState = ComponentState & {
|
||||
| "dim";
|
||||
};
|
||||
|
||||
export class IconComponent extends ComponentBase {
|
||||
declare state: Required<IconState>;
|
||||
|
||||
export class IconComponent extends ComponentBase<IconState> {
|
||||
private svgElement: SVGSVGElement;
|
||||
|
||||
createElement(): HTMLElement {
|
||||
@@ -34,7 +32,7 @@ export class IconComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: IconState,
|
||||
deltaState: DeltaState<IconState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { applyIcon } from "../designApplication";
|
||||
import { getAllocatedHeightInPx, getAllocatedWidthInPx } from "../utils";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
const FILL_MODE_TO_OBJECT_FIT = {
|
||||
fit: "contain",
|
||||
@@ -10,16 +10,14 @@ const FILL_MODE_TO_OBJECT_FIT = {
|
||||
|
||||
export type ImageState = ComponentState & {
|
||||
_type_: "Image-builtin";
|
||||
fill_mode?: keyof typeof FILL_MODE_TO_OBJECT_FIT;
|
||||
imageUrl?: string;
|
||||
reportError?: boolean;
|
||||
corner_radius?: [number, number, number, number];
|
||||
accessibility_description?: string;
|
||||
fill_mode: keyof typeof FILL_MODE_TO_OBJECT_FIT;
|
||||
imageUrl: string;
|
||||
reportError: boolean;
|
||||
corner_radius: [number, number, number, number];
|
||||
accessibility_description: string;
|
||||
};
|
||||
|
||||
export class ImageComponent extends ComponentBase {
|
||||
declare state: Required<ImageState>;
|
||||
|
||||
export class ImageComponent extends ComponentBase<ImageState> {
|
||||
private imageElement: HTMLImageElement;
|
||||
private isLoading: boolean = false;
|
||||
private resizeObserver: ResizeObserver;
|
||||
@@ -49,7 +47,7 @@ export class ImageComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: ImageState,
|
||||
deltaState: DeltaState<ImageState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, DeltaState } from "./componentBase";
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { markEventAsHandled } from "../eventHandling";
|
||||
import {
|
||||
KeyboardFocusableComponent,
|
||||
KeyboardFocusableComponentState,
|
||||
} from "./keyboardFocusableComponent";
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values
|
||||
const HARDWARE_KEY_MAP = {
|
||||
@@ -454,7 +458,7 @@ const SOFTWARE_KEY_MAP = {
|
||||
ColorF1Green: "color-f1-green",
|
||||
ColorF2Yellow: "color-f2-yellow",
|
||||
ColorF3Blue: "color-f3-blue",
|
||||
ColorF4Grey: "color-f4-grey",
|
||||
ColorF4Grey: "color-f4-gray",
|
||||
ColorF5Brown: "color-f5-brown",
|
||||
ClosedCaptionToggle: "closed-caption-toggle",
|
||||
Dimmer: "dimmer",
|
||||
@@ -686,17 +690,15 @@ function encodeEvent(event: KeyboardEvent): EncodedEvent {
|
||||
|
||||
type KeyCombination = SoftwareKey | SoftwareKey[];
|
||||
|
||||
export type KeyEventListenerState = ComponentState & {
|
||||
export type KeyEventListenerState = KeyboardFocusableComponentState & {
|
||||
_type_: "KeyEventListener-builtin";
|
||||
content?: ComponentId;
|
||||
reportKeyDown?: KeyCombination[] | true;
|
||||
reportKeyUp?: KeyCombination[] | true;
|
||||
reportKeyPress?: KeyCombination[] | true;
|
||||
content: ComponentId;
|
||||
reportKeyDown: KeyCombination[] | true;
|
||||
reportKeyUp: KeyCombination[] | true;
|
||||
reportKeyPress: KeyCombination[] | true;
|
||||
};
|
||||
|
||||
export class KeyEventListenerComponent extends ComponentBase {
|
||||
declare state: Required<KeyEventListenerState>;
|
||||
|
||||
export class KeyEventListenerComponent extends KeyboardFocusableComponent<KeyEventListenerState> {
|
||||
private keyDownCombinations: Map<string, KeyCombination> | true;
|
||||
private keyUpCombinations: Map<string, KeyCombination> | true;
|
||||
private keyPressCombinations: Map<string, KeyCombination> | true;
|
||||
@@ -709,7 +711,7 @@ export class KeyEventListenerComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: KeyEventListenerState,
|
||||
deltaState: DeltaState<KeyEventListenerState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
@@ -804,10 +806,6 @@ export class KeyEventListenerComponent extends ComponentBase {
|
||||
...encodedEvent,
|
||||
});
|
||||
}
|
||||
|
||||
grabKeyboardFocus(): void {
|
||||
this.element.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function keyCombinationsMapFromDeltaState(
|
||||
|
||||
45
frontend/code/components/keyboardFocusableComponent.ts
Normal file
45
frontend/code/components/keyboardFocusableComponent.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
|
||||
export type KeyboardFocusableComponentState = ComponentState & {
|
||||
auto_focus: boolean;
|
||||
};
|
||||
|
||||
/// Base class for components that can receive keyboard focus. What this class
|
||||
/// does:
|
||||
/// - Enforces the presence of `auto_focus` in the component state
|
||||
/// - Focuses the component on mount if `auto_focus` is true
|
||||
///
|
||||
/// There are also some other places where this class is used:
|
||||
/// - In `updateComponentStates`, the keyboard focus is automatically moved to a
|
||||
/// `KeyboardFocusableComponent` if the focused component dies
|
||||
/// - The `setKeyboardFocus` RPC function only works with these components
|
||||
export abstract class KeyboardFocusableComponent<
|
||||
S extends KeyboardFocusableComponentState = KeyboardFocusableComponentState,
|
||||
> extends ComponentBase<S> {
|
||||
constructor(id: ComponentId, state: S) {
|
||||
super(id, state);
|
||||
|
||||
// `.focus()` may not work on the initial page load (it's probably
|
||||
// blocked unless there's user interaction like a click), so we'll
|
||||
// use the `autofocus` attribute.
|
||||
if (state.auto_focus) {
|
||||
let element = this.getElementForKeyboardFocus();
|
||||
element.autofocus = true;
|
||||
}
|
||||
|
||||
// `autofocus` doesn't work in dialogs (probably because they open with
|
||||
// a delay), so we'll add our own delay.
|
||||
setTimeout(() => {
|
||||
this.grabKeyboardFocus();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
public grabKeyboardFocus(): void {
|
||||
this.getElementForKeyboardFocus().focus();
|
||||
}
|
||||
|
||||
protected getElementForKeyboardFocus(): HTMLElement {
|
||||
return this.element;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { componentsById } from "../componentManagement";
|
||||
import { getDisplayableChildren } from "../devToolsTreeWalk";
|
||||
import { Highlighter } from "../highlighter";
|
||||
@@ -9,13 +9,11 @@ import { getAllocatedHeightInPx, getAllocatedWidthInPx } from "../utils";
|
||||
|
||||
export type LayoutDisplayState = ComponentState & {
|
||||
_type_: "LayoutDisplay-builtin";
|
||||
component_id?: number;
|
||||
max_requested_height?: number;
|
||||
component_id: number;
|
||||
max_requested_height: number;
|
||||
};
|
||||
|
||||
export class LayoutDisplayComponent extends ComponentBase {
|
||||
declare state: Required<LayoutDisplayState>;
|
||||
|
||||
export class LayoutDisplayComponent extends ComponentBase<LayoutDisplayState> {
|
||||
// Represents the target component's parent. It matches the aspect ratio of
|
||||
// the parent and is centered within this component.
|
||||
parentElement: HTMLElement;
|
||||
@@ -99,7 +97,7 @@ export class LayoutDisplayComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: LayoutDisplayState,
|
||||
deltaState: DeltaState<LayoutDisplayState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -7,13 +7,13 @@ import {
|
||||
OnlyResizeObserver,
|
||||
zip,
|
||||
} from "../utils";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type LinearContainerState = ComponentState & {
|
||||
_type_: "Row-builtin" | "Column-builtin";
|
||||
children?: ComponentId[];
|
||||
spacing?: number;
|
||||
proportions?: "homogeneous" | number[] | null;
|
||||
children: ComponentId[];
|
||||
spacing: number;
|
||||
proportions: "homogeneous" | number[] | null;
|
||||
};
|
||||
|
||||
// The size of the invisible spacer element. It must be large enough to account
|
||||
@@ -23,9 +23,7 @@ export type LinearContainerState = ComponentState & {
|
||||
// its job.)
|
||||
const PROPORTIONS_SPACER_SIZE = 50;
|
||||
|
||||
export abstract class LinearContainer extends ComponentBase {
|
||||
declare state: Required<LinearContainerState>;
|
||||
|
||||
export abstract class LinearContainer extends ComponentBase<LinearContainerState> {
|
||||
index: 0 | 1; // 0 for Rows, 1 for Columns
|
||||
sizeAttribute: "width" | "height"; // 'width' for Rows, 'height' for Columns
|
||||
|
||||
@@ -73,7 +71,7 @@ export abstract class LinearContainer extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: LinearContainerState,
|
||||
deltaState: DeltaState<LinearContainerState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { hijackLinkElement } from "../utils";
|
||||
import { applyIcon } from "../designApplication";
|
||||
|
||||
export type LinkState = ComponentState & {
|
||||
_type_: "Link-builtin";
|
||||
child_text?: string | null;
|
||||
child_component?: ComponentId | null;
|
||||
icon?: string | null;
|
||||
open_in_new_tab?: boolean;
|
||||
child_text: string | null;
|
||||
child_component: ComponentId | null;
|
||||
icon: string | null;
|
||||
open_in_new_tab: boolean;
|
||||
targetUrl: string;
|
||||
};
|
||||
|
||||
export class LinkComponent extends ComponentBase {
|
||||
declare state: Required<LinkState>;
|
||||
|
||||
export class LinkComponent extends ComponentBase<LinkState> {
|
||||
createElement(): HTMLElement {
|
||||
let element = document.createElement("a");
|
||||
element.classList.add("rio-link");
|
||||
@@ -25,7 +23,7 @@ export class LinkComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: LinkState,
|
||||
deltaState: DeltaState<LinkState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { componentsByElement, componentsById } from "../componentManagement";
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { CustomListItemComponent } from "./customListItem";
|
||||
import { HeadingListItemComponent } from "./headingListItem";
|
||||
import { SeparatorListItemComponent } from "./separatorListItem";
|
||||
|
||||
export type ListViewState = ComponentState & {
|
||||
_type_: "ListView-builtin";
|
||||
children?: ComponentId[];
|
||||
children: ComponentId[];
|
||||
};
|
||||
|
||||
export class ListViewComponent extends ComponentBase {
|
||||
declare state: Required<ListViewState>;
|
||||
|
||||
export class ListViewComponent extends ComponentBase<ListViewState> {
|
||||
createElement(): HTMLElement {
|
||||
let element = document.createElement("div");
|
||||
element.classList.add("rio-list-view");
|
||||
@@ -20,7 +18,7 @@ export class ListViewComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: ListViewState,
|
||||
deltaState: DeltaState<ListViewState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { micromark } from "micromark";
|
||||
|
||||
// This import decides which languages are supported by `highlight.js`. See
|
||||
@@ -12,13 +12,13 @@ import { convertDivToCodeBlock } from "./codeBlock";
|
||||
|
||||
export type MarkdownState = ComponentState & {
|
||||
_type_: "Markdown-builtin";
|
||||
text?: string;
|
||||
default_language?: null | string;
|
||||
selectable?: boolean;
|
||||
justify?: "left" | "right" | "center" | "justify";
|
||||
overflow?: "nowrap" | "wrap" | "ellipsize";
|
||||
scroll_code_x?: "never" | "auto" | "always";
|
||||
scroll_code_y?: "never" | "auto" | "always";
|
||||
text: string;
|
||||
default_language: null | string;
|
||||
selectable: boolean;
|
||||
justify: "left" | "right" | "center" | "justify";
|
||||
overflow: "nowrap" | "wrap" | "ellipsize";
|
||||
scroll_code_x: "never" | "auto" | "always";
|
||||
scroll_code_y: "never" | "auto" | "always";
|
||||
};
|
||||
|
||||
// Convert a Markdown string to HTML and render it in the given div.
|
||||
@@ -120,9 +120,7 @@ function hijackLocalLinks(div: HTMLElement): void {
|
||||
}
|
||||
}
|
||||
|
||||
export class MarkdownComponent extends ComponentBase {
|
||||
declare state: Required<MarkdownState>;
|
||||
|
||||
export class MarkdownComponent extends ComponentBase<MarkdownState> {
|
||||
createElement(): HTMLElement {
|
||||
const element = document.createElement("div");
|
||||
element.classList.add("rio-markdown");
|
||||
@@ -130,7 +128,7 @@ export class MarkdownComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: MarkdownState,
|
||||
deltaState: DeltaState<MarkdownState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -2,20 +2,24 @@ import { fillToCss } from "../cssUtils";
|
||||
import { applyIcon } from "../designApplication";
|
||||
import { AnyFill } from "../dataModels";
|
||||
import { sleep } from "../utils";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, DeltaState } from "./componentBase";
|
||||
import { markEventAsHandled } from "../eventHandling";
|
||||
import {
|
||||
KeyboardFocusableComponent,
|
||||
KeyboardFocusableComponentState,
|
||||
} from "./keyboardFocusableComponent";
|
||||
|
||||
export type MediaPlayerState = ComponentState & {
|
||||
export type MediaPlayerState = KeyboardFocusableComponentState & {
|
||||
_type_: "MediaPlayer-builtin";
|
||||
loop?: boolean;
|
||||
autoplay?: boolean;
|
||||
controls?: boolean;
|
||||
muted?: boolean;
|
||||
volume?: number;
|
||||
mediaUrl?: string;
|
||||
loop: boolean;
|
||||
autoplay: boolean;
|
||||
controls: boolean;
|
||||
muted: boolean;
|
||||
volume: number;
|
||||
mediaUrl: string;
|
||||
background: AnyFill;
|
||||
reportError?: boolean;
|
||||
reportPlaybackEnd?: boolean;
|
||||
reportError: boolean;
|
||||
reportPlaybackEnd: boolean;
|
||||
};
|
||||
|
||||
const OVERLAY_TIMEOUT = 2000;
|
||||
@@ -58,9 +62,7 @@ async function hasAudio(element: HTMLMediaElement): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
export class MediaPlayerComponent extends ComponentBase {
|
||||
declare state: Required<MediaPlayerState>;
|
||||
|
||||
export class MediaPlayerComponent extends KeyboardFocusableComponent<MediaPlayerState> {
|
||||
private mediaPlayer: HTMLVideoElement;
|
||||
private altDisplay: HTMLElement;
|
||||
private controls: HTMLElement;
|
||||
@@ -566,7 +568,7 @@ export class MediaPlayerComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: MediaPlayerState,
|
||||
deltaState: DeltaState<MediaPlayerState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
@@ -849,10 +851,6 @@ export class MediaPlayerComponent extends ComponentBase {
|
||||
markEventAsHandled(event);
|
||||
}
|
||||
|
||||
grabKeyboardFocus(): void {
|
||||
this.element.focus();
|
||||
}
|
||||
|
||||
private _onError(event: string | Event): void {
|
||||
this.sendMessageToBackend({
|
||||
type: "error",
|
||||
@@ -874,4 +872,8 @@ export class MediaPlayerComponent extends ComponentBase {
|
||||
this.mediaPlayer.src = "";
|
||||
this.mediaPlayer.load();
|
||||
}
|
||||
|
||||
protected override getElementForKeyboardFocus(): HTMLElement {
|
||||
return this.mediaPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { pixelsPerRem } from "../app";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { DragHandler } from "../eventHandling";
|
||||
import { tryGetComponentByElement } from "../componentManagement";
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { findComponentUnderMouse } from "../utils";
|
||||
|
||||
@@ -20,7 +19,7 @@ function eventMousePositionToString(event: MouseEvent): object {
|
||||
|
||||
export type MouseEventListenerState = ComponentState & {
|
||||
_type_: "MouseEventListener-builtin";
|
||||
content?: ComponentId;
|
||||
content: ComponentId;
|
||||
reportPress: boolean;
|
||||
reportMouseDown: boolean;
|
||||
reportMouseUp: boolean;
|
||||
@@ -32,9 +31,7 @@ export type MouseEventListenerState = ComponentState & {
|
||||
reportDragEnd: boolean;
|
||||
};
|
||||
|
||||
export class MouseEventListenerComponent extends ComponentBase {
|
||||
declare state: Required<MouseEventListenerState>;
|
||||
|
||||
export class MouseEventListenerComponent extends ComponentBase<MouseEventListenerState> {
|
||||
private _dragHandler: DragHandler | null = null;
|
||||
|
||||
createElement(): HTMLElement {
|
||||
@@ -44,7 +41,7 @@ export class MouseEventListenerComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: MouseEventListenerState,
|
||||
deltaState: DeltaState<MouseEventListenerState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import { markEventAsHandled } from "../eventHandling";
|
||||
import { InputBox, InputBoxStyle } from "../inputBox";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, DeltaState } from "./componentBase";
|
||||
import {
|
||||
KeyboardFocusableComponent,
|
||||
KeyboardFocusableComponentState,
|
||||
} from "./keyboardFocusableComponent";
|
||||
|
||||
export type MultiLineTextInputState = ComponentState & {
|
||||
export type MultiLineTextInputState = KeyboardFocusableComponentState & {
|
||||
_type_: "MultiLineTextInput-builtin";
|
||||
text?: string;
|
||||
label?: string;
|
||||
accessibility_label?: string;
|
||||
style?: InputBoxStyle;
|
||||
is_sensitive?: boolean;
|
||||
is_valid?: boolean;
|
||||
auto_adjust_height?: boolean;
|
||||
text: string;
|
||||
label: string;
|
||||
accessibility_label: string;
|
||||
style: InputBoxStyle;
|
||||
is_sensitive: boolean;
|
||||
is_valid: boolean;
|
||||
auto_adjust_height: boolean;
|
||||
};
|
||||
|
||||
export class MultiLineTextInputComponent extends ComponentBase {
|
||||
declare state: Required<MultiLineTextInputState>;
|
||||
|
||||
export class MultiLineTextInputComponent extends KeyboardFocusableComponent<MultiLineTextInputState> {
|
||||
private inputBox: InputBox;
|
||||
|
||||
createElement(): HTMLElement {
|
||||
@@ -79,7 +81,7 @@ export class MultiLineTextInputComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: MultiLineTextInputState,
|
||||
deltaState: DeltaState<MultiLineTextInputState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
@@ -122,7 +124,7 @@ export class MultiLineTextInputComponent extends ComponentBase {
|
||||
textarea.style.minHeight = `${textarea.scrollHeight}px`;
|
||||
}
|
||||
|
||||
grabKeyboardFocus(): void {
|
||||
this.inputBox.focus();
|
||||
protected override getElementForKeyboardFocus(): HTMLElement {
|
||||
return this.inputBox.inputElement;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { Color } from "../dataModels";
|
||||
import { colorToCssString } from "../cssUtils";
|
||||
|
||||
@@ -9,9 +9,7 @@ export type NodeInputState = ComponentState & {
|
||||
key: string;
|
||||
};
|
||||
|
||||
export class NodeInputComponent extends ComponentBase {
|
||||
declare state: Required<NodeInputState>;
|
||||
|
||||
export class NodeInputComponent extends ComponentBase<NodeInputState> {
|
||||
textElement: HTMLElement;
|
||||
circleElement: HTMLElement;
|
||||
|
||||
@@ -39,7 +37,7 @@ export class NodeInputComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: NodeInputState,
|
||||
deltaState: DeltaState<NodeInputState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { Color } from "../dataModels";
|
||||
import { colorToCssString } from "../cssUtils";
|
||||
|
||||
@@ -9,9 +9,7 @@ export type NodeOutputState = ComponentState & {
|
||||
key: string;
|
||||
};
|
||||
|
||||
export class NodeOutputComponent extends ComponentBase {
|
||||
declare state: Required<NodeOutputState>;
|
||||
|
||||
export class NodeOutputComponent extends ComponentBase<NodeOutputState> {
|
||||
textElement: HTMLElement;
|
||||
circleElement: HTMLElement;
|
||||
|
||||
@@ -39,7 +37,7 @@ export class NodeOutputComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: NodeOutputState,
|
||||
deltaState: DeltaState<NodeOutputState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { FullscreenPositioner, PopupManager } from "../popupManager";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type OverlayState = ComponentState & {
|
||||
_type_: "Overlay-builtin";
|
||||
content?: ComponentId;
|
||||
content: ComponentId;
|
||||
};
|
||||
|
||||
export class OverlayComponent extends ComponentBase {
|
||||
declare state: Required<OverlayState>;
|
||||
|
||||
export class OverlayComponent extends ComponentBase<OverlayState> {
|
||||
private overlayContentElement: HTMLElement;
|
||||
private popupManager: PopupManager;
|
||||
|
||||
@@ -42,7 +40,7 @@ export class OverlayComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: OverlayState,
|
||||
deltaState: DeltaState<OverlayState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { fillToCss } from "../cssUtils";
|
||||
import { AnyFill } from "../dataModels";
|
||||
import { getAllocatedHeightInPx, getAllocatedWidthInPx } from "../utils";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
type PlotlyType = any;
|
||||
|
||||
@@ -19,12 +19,10 @@ type PlotState = ComponentState & {
|
||||
_type_: "Plot-builtin";
|
||||
plot: PlotlyPlot | MatplotlibPlot;
|
||||
background: AnyFill | null;
|
||||
corner_radius?: [number, number, number, number];
|
||||
corner_radius: [number, number, number, number];
|
||||
};
|
||||
|
||||
export class PlotComponent extends ComponentBase {
|
||||
declare state: Required<PlotState>;
|
||||
|
||||
export class PlotComponent extends ComponentBase<PlotState> {
|
||||
// I know this abstraction looks like overkill, but plotly does so much
|
||||
// stuff with a time delay (loading plotly, setTimeout, resizeObserver, ...)
|
||||
// that it's just a giant mess of race conditions if it's not all
|
||||
@@ -38,7 +36,7 @@ export class PlotComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: PlotState,
|
||||
deltaState: DeltaState<PlotState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { pixelsPerRem } from "../app";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { DragHandler } from "../eventHandling";
|
||||
import { tryGetComponentByElement } from "../componentManagement";
|
||||
import { ComponentId } from "../dataModels";
|
||||
@@ -7,7 +7,7 @@ import { findComponentUnderMouse } from "../utils";
|
||||
|
||||
export type PointerEventListenerState = ComponentState & {
|
||||
_type_: "PointerEventListener-builtin";
|
||||
content?: ComponentId;
|
||||
content: ComponentId;
|
||||
reportPress: boolean;
|
||||
reportPointerDown: boolean;
|
||||
reportPointerUp: boolean;
|
||||
@@ -19,9 +19,7 @@ export type PointerEventListenerState = ComponentState & {
|
||||
reportDragEnd: boolean;
|
||||
};
|
||||
|
||||
export class PointerEventListenerComponent extends ComponentBase {
|
||||
declare state: Required<PointerEventListenerState>;
|
||||
|
||||
export class PointerEventListenerComponent extends ComponentBase<PointerEventListenerState> {
|
||||
private _dragHandler: DragHandler | null = null;
|
||||
|
||||
createElement(): HTMLElement {
|
||||
@@ -31,7 +29,7 @@ export class PointerEventListenerComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: PointerEventListenerState,
|
||||
deltaState: DeltaState<PointerEventListenerState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { applySwitcheroo } from "../designApplication";
|
||||
import { ColorSet, ComponentId } from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import {
|
||||
DesktopDropdownPositioner,
|
||||
getPositionerByName,
|
||||
@@ -11,14 +11,14 @@ import { componentsById } from "../componentManagement";
|
||||
|
||||
export type PopupState = ComponentState & {
|
||||
_type_: "Popup-builtin";
|
||||
anchor?: ComponentId;
|
||||
content?: ComponentId;
|
||||
is_open?: boolean;
|
||||
anchor: ComponentId;
|
||||
content: ComponentId;
|
||||
is_open: boolean;
|
||||
modal: boolean;
|
||||
user_closable: boolean;
|
||||
color?: ColorSet | "none";
|
||||
corner_radius?: number | [number, number, number, number];
|
||||
position?:
|
||||
color: ColorSet | "none";
|
||||
corner_radius: number | [number, number, number, number];
|
||||
position:
|
||||
| "auto"
|
||||
| "left"
|
||||
| "top"
|
||||
@@ -27,13 +27,11 @@ export type PopupState = ComponentState & {
|
||||
| "center"
|
||||
| "fullscreen"
|
||||
| "dropdown";
|
||||
alignment?: number;
|
||||
gap?: number;
|
||||
alignment: number;
|
||||
gap: number;
|
||||
};
|
||||
|
||||
export class PopupComponent extends ComponentBase {
|
||||
declare state: Required<PopupState>;
|
||||
|
||||
export class PopupComponent extends ComponentBase<PopupState> {
|
||||
private popupContentElement: HTMLElement;
|
||||
private popupScrollerElement: HTMLElement;
|
||||
|
||||
@@ -77,7 +75,7 @@ export class PopupComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: PopupState,
|
||||
deltaState: DeltaState<PopupState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import { ColorSet } from "../dataModels";
|
||||
import { applySwitcheroo } from "../designApplication";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type ProgressBarState = ComponentState & {
|
||||
_type_: "ProgressBar-builtin";
|
||||
progress?: number | null;
|
||||
color?: ColorSet;
|
||||
rounded?: boolean;
|
||||
progress: number | null;
|
||||
color: ColorSet;
|
||||
rounded: boolean;
|
||||
};
|
||||
|
||||
export class ProgressBarComponent extends ComponentBase {
|
||||
declare state: Required<ProgressBarState>;
|
||||
|
||||
export class ProgressBarComponent extends ComponentBase<ProgressBarState> {
|
||||
fillElement: HTMLElement;
|
||||
|
||||
createElement(): HTMLElement {
|
||||
@@ -31,7 +29,7 @@ export class ProgressBarComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: ProgressBarState,
|
||||
deltaState: DeltaState<ProgressBarState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { applySwitcheroo } from "../designApplication";
|
||||
import { ColorSet } from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type ProgressCircleState = ComponentState & {
|
||||
_type_: "ProgressCircle-builtin";
|
||||
progress?: number | null;
|
||||
color?: ColorSet;
|
||||
progress: number | null;
|
||||
color: ColorSet;
|
||||
};
|
||||
|
||||
export class ProgressCircleComponent extends ComponentBase {
|
||||
declare state: Required<ProgressCircleState>;
|
||||
|
||||
export class ProgressCircleComponent extends ComponentBase<ProgressCircleState> {
|
||||
createElement(): HTMLElement {
|
||||
let element = document.createElement("div");
|
||||
|
||||
@@ -24,7 +22,7 @@ export class ProgressCircleComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: ProgressCircleState,
|
||||
deltaState: DeltaState<ProgressCircleState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import { Color, ComponentId, AnyFill } from "../dataModels";
|
||||
import { colorToCssString, fillToCss } from "../cssUtils";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { RippleEffect } from "../rippleEffect";
|
||||
|
||||
export type RectangleState = ComponentState & {
|
||||
_type_: "Rectangle-builtin";
|
||||
content?: ComponentId | null;
|
||||
transition_time?: number;
|
||||
cursor?: string;
|
||||
ripple?: boolean;
|
||||
content: ComponentId | null;
|
||||
transition_time: number;
|
||||
cursor: string;
|
||||
ripple: boolean;
|
||||
|
||||
fill?: AnyFill;
|
||||
stroke_color?: Color;
|
||||
stroke_width?: number;
|
||||
corner_radius?: [number, number, number, number];
|
||||
shadow_color?: Color;
|
||||
shadow_radius?: number;
|
||||
shadow_offset_x?: number;
|
||||
shadow_offset_y?: number;
|
||||
fill: AnyFill;
|
||||
stroke_color: Color;
|
||||
stroke_width: number;
|
||||
corner_radius: [number, number, number, number];
|
||||
shadow_color: Color;
|
||||
shadow_radius: number;
|
||||
shadow_offset_x: number;
|
||||
shadow_offset_y: number;
|
||||
|
||||
hover_fill?: AnyFill | null;
|
||||
hover_stroke_color?: Color | null;
|
||||
hover_stroke_width?: number | null;
|
||||
hover_corner_radius?: [number, number, number, number] | null;
|
||||
hover_shadow_color?: Color | null;
|
||||
hover_shadow_radius?: number | null;
|
||||
hover_shadow_offset_x?: number | null;
|
||||
hover_shadow_offset_y?: number | null;
|
||||
hover_fill: AnyFill | null;
|
||||
hover_stroke_color: Color | null;
|
||||
hover_stroke_width: number | null;
|
||||
hover_corner_radius: [number, number, number, number] | null;
|
||||
hover_shadow_color: Color | null;
|
||||
hover_shadow_radius: number | null;
|
||||
hover_shadow_offset_x: number | null;
|
||||
hover_shadow_offset_y: number | null;
|
||||
};
|
||||
|
||||
function numberToRem(num: number): string {
|
||||
@@ -71,9 +71,7 @@ function cursorToCSS(cursor: string): string {
|
||||
return CURSOR_MAP[cursor];
|
||||
}
|
||||
|
||||
export class RectangleComponent extends ComponentBase {
|
||||
declare state: Required<RectangleState>;
|
||||
|
||||
export class RectangleComponent extends ComponentBase<RectangleState> {
|
||||
// If this rectangle has a ripple effect, this is the ripple instance.
|
||||
// `null` otherwise.
|
||||
private rippleInstance: RippleEffect | null = null;
|
||||
@@ -85,7 +83,7 @@ export class RectangleComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: RectangleState,
|
||||
deltaState: DeltaState<RectangleState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -2,22 +2,20 @@ import { textStyleToCss } from "../cssUtils";
|
||||
import { applyIcon } from "../designApplication";
|
||||
import { ComponentId, TextStyle } from "../dataModels";
|
||||
import { commitCss } from "../utils";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { RippleEffect } from "../rippleEffect";
|
||||
|
||||
let HEADER_PADDING: number = 0.3;
|
||||
|
||||
export type RevealerState = ComponentState & {
|
||||
_type_: "Revealer-builtin";
|
||||
header?: string | null;
|
||||
content?: ComponentId;
|
||||
header_style?: "heading1" | "heading2" | "heading3" | "text" | TextStyle;
|
||||
header: string | null;
|
||||
content: ComponentId;
|
||||
header_style: "heading1" | "heading2" | "heading3" | "text" | TextStyle;
|
||||
is_open: boolean;
|
||||
};
|
||||
|
||||
export class RevealerComponent extends ComponentBase {
|
||||
declare state: Required<RevealerState>;
|
||||
|
||||
export class RevealerComponent extends ComponentBase<RevealerState> {
|
||||
private headerElement: HTMLElement;
|
||||
private labelElement: HTMLElement;
|
||||
private arrowElement: HTMLElement;
|
||||
@@ -93,7 +91,7 @@ export class RevealerComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: RevealerState,
|
||||
deltaState: DeltaState<RevealerState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type ScrollContainerState = ComponentState & {
|
||||
_type_: "ScrollContainer-builtin";
|
||||
content?: ComponentId;
|
||||
scroll_x?: "never" | "auto" | "always";
|
||||
scroll_y?: "never" | "auto" | "always";
|
||||
initial_x?: number;
|
||||
initial_y?: number;
|
||||
reserve_space_y?: boolean;
|
||||
sticky_bottom?: boolean;
|
||||
content: ComponentId;
|
||||
scroll_x: "never" | "auto" | "always";
|
||||
scroll_y: "never" | "auto" | "always";
|
||||
initial_x: number;
|
||||
initial_y: number;
|
||||
reserve_space_y: boolean;
|
||||
sticky_bottom: boolean;
|
||||
};
|
||||
|
||||
export class ScrollContainerComponent extends ComponentBase {
|
||||
declare state: Required<ScrollContainerState>;
|
||||
|
||||
export class ScrollContainerComponent extends ComponentBase<ScrollContainerState> {
|
||||
private scrollerElement: HTMLElement;
|
||||
private childContainer: HTMLElement;
|
||||
private scrollAnchor: HTMLElement;
|
||||
@@ -59,7 +57,7 @@ export class ScrollContainerComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: ScrollContainerState,
|
||||
deltaState: DeltaState<ScrollContainerState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import { tryGetComponentByElement } from "../componentManagement";
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { setClipboard } from "../utils";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type ScrollTargetState = ComponentState & {
|
||||
_type_: "ScrollTarget-builtin";
|
||||
id?: string;
|
||||
content?: ComponentId | null;
|
||||
copy_button_content?: ComponentId | null;
|
||||
copy_button_text?: string | null;
|
||||
copy_button_spacing?: number;
|
||||
id: string;
|
||||
content: ComponentId | null;
|
||||
copy_button_content: ComponentId | null;
|
||||
copy_button_text: string | null;
|
||||
copy_button_spacing: number;
|
||||
};
|
||||
|
||||
export class ScrollTargetComponent extends ComponentBase {
|
||||
declare state: Required<ScrollTargetState>;
|
||||
|
||||
export class ScrollTargetComponent extends ComponentBase<ScrollTargetState> {
|
||||
childContainerElement: HTMLElement;
|
||||
buttonContainerElement: HTMLElement;
|
||||
|
||||
@@ -42,7 +40,7 @@ export class ScrollTargetComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: ScrollTargetState,
|
||||
deltaState: DeltaState<ScrollTargetState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Color } from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { colorToCssString } from "../cssUtils";
|
||||
|
||||
export type SeparatorState = ComponentState & {
|
||||
@@ -8,9 +8,7 @@ export type SeparatorState = ComponentState & {
|
||||
color: Color;
|
||||
};
|
||||
|
||||
export class SeparatorComponent extends ComponentBase {
|
||||
declare state: Required<SeparatorState>;
|
||||
|
||||
export class SeparatorComponent extends ComponentBase<SeparatorState> {
|
||||
createElement(): HTMLElement {
|
||||
let element = document.createElement("div");
|
||||
element.classList.add("rio-separator");
|
||||
@@ -18,7 +16,7 @@ export class SeparatorComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: SeparatorState,
|
||||
deltaState: DeltaState<SeparatorState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type SeparatorListItemState = ComponentState & {
|
||||
_type_: "SeparatorListItem-builtin";
|
||||
};
|
||||
|
||||
export class SeparatorListItemComponent extends ComponentBase {
|
||||
declare state: Required<SeparatorListItemState>;
|
||||
|
||||
export class SeparatorListItemComponent extends ComponentBase<SeparatorListItemState> {
|
||||
createElement(): HTMLElement {
|
||||
let element = document.createElement("div");
|
||||
element.classList.add("rio-separator-list-item");
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import { applySwitcheroo } from "../designApplication";
|
||||
import { markEventAsHandled } from "../eventHandling";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type SliderState = ComponentState & {
|
||||
_type_: "Slider-builtin";
|
||||
minimum?: number;
|
||||
maximum?: number;
|
||||
value?: number;
|
||||
step?: number;
|
||||
is_sensitive?: boolean;
|
||||
show_values?: boolean;
|
||||
ticks?: (number | string | [number, string])[] | boolean;
|
||||
minimum: number;
|
||||
maximum: number;
|
||||
value: number;
|
||||
step: number;
|
||||
is_sensitive: boolean;
|
||||
show_values: boolean;
|
||||
ticks: (number | string | [number, string])[] | boolean;
|
||||
};
|
||||
|
||||
export class SliderComponent extends ComponentBase {
|
||||
declare state: Required<SliderState>;
|
||||
|
||||
export class SliderComponent extends ComponentBase<SliderState> {
|
||||
private innerElement: HTMLElement;
|
||||
private minValueElement: HTMLElement;
|
||||
private maxValueElement: HTMLElement;
|
||||
@@ -138,7 +136,7 @@ export class SliderComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: SliderState,
|
||||
deltaState: DeltaState<SliderState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { easeIn, easeInOut, easeOut } from "../easeFunctions";
|
||||
import { ComponentId } from "../dataModels";
|
||||
|
||||
@@ -7,15 +7,13 @@ const progressBarFadeDuration = 0.2;
|
||||
|
||||
export type SlideshowState = ComponentState & {
|
||||
_type_: "Slideshow-builtin";
|
||||
children?: ComponentId[];
|
||||
linger_time?: number;
|
||||
pause_on_hover?: boolean;
|
||||
corner_radius?: [number, number, number, number];
|
||||
children: ComponentId[];
|
||||
linger_time: number;
|
||||
pause_on_hover: boolean;
|
||||
corner_radius: [number, number, number, number];
|
||||
};
|
||||
|
||||
export class SlideshowComponent extends ComponentBase {
|
||||
declare state: Required<SlideshowState>;
|
||||
|
||||
export class SlideshowComponent extends ComponentBase<SlideshowState> {
|
||||
private childContainer: HTMLElement;
|
||||
private progressBar: HTMLElement;
|
||||
|
||||
@@ -70,7 +68,7 @@ export class SlideshowComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: SlideshowState,
|
||||
deltaState: DeltaState<SlideshowState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type StackState = ComponentState & {
|
||||
_type_: "Stack-builtin";
|
||||
children?: ComponentId[];
|
||||
children: ComponentId[];
|
||||
};
|
||||
|
||||
export class StackComponent extends ComponentBase {
|
||||
declare state: Required<StackState>;
|
||||
|
||||
export class StackComponent extends ComponentBase<StackState> {
|
||||
createElement(): HTMLElement {
|
||||
let element = document.createElement("div");
|
||||
element.classList.add("rio-stack");
|
||||
@@ -16,7 +14,7 @@ export class StackComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: StackState,
|
||||
deltaState: DeltaState<StackState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { applyIcon } from "../designApplication";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type SwitchState = ComponentState & {
|
||||
_type_: "Switch-builtin";
|
||||
is_on?: boolean;
|
||||
is_sensitive?: boolean;
|
||||
is_on: boolean;
|
||||
is_sensitive: boolean;
|
||||
};
|
||||
|
||||
export class SwitchComponent extends ComponentBase {
|
||||
declare state: Required<SwitchState>;
|
||||
|
||||
export class SwitchComponent extends ComponentBase<SwitchState> {
|
||||
createElement(): HTMLElement {
|
||||
let element = document.createElement("div");
|
||||
element.classList.add("rio-switch");
|
||||
@@ -37,7 +35,7 @@ export class SwitchComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: SwitchState,
|
||||
deltaState: DeltaState<SwitchState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { componentsById } from "../componentManagement";
|
||||
import { commitCss } from "../utils";
|
||||
|
||||
export type SwitcherState = ComponentState & {
|
||||
_type_: "Switcher-builtin";
|
||||
content?: ComponentId | null;
|
||||
transition_time?: number;
|
||||
content: ComponentId | null;
|
||||
transition_time: number;
|
||||
};
|
||||
|
||||
export class SwitcherComponent extends ComponentBase {
|
||||
declare state: Required<SwitcherState>;
|
||||
|
||||
export class SwitcherComponent extends ComponentBase<SwitcherState> {
|
||||
private activeChildContainer: HTMLElement | null = null;
|
||||
private resizerElement: HTMLElement | null = null;
|
||||
private idOfCurrentAnimation: number = 0;
|
||||
@@ -24,7 +22,7 @@ export class SwitcherComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: SwitcherState,
|
||||
deltaState: DeltaState<SwitcherState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { ColorSet } from "../dataModels";
|
||||
import { applyIcon, applySwitcheroo } from "../designApplication";
|
||||
import { MappingTween } from "../tweens/mappingTweens";
|
||||
@@ -13,18 +13,16 @@ import {
|
||||
|
||||
export type SwitcherBarState = ComponentState & {
|
||||
_type_: "SwitcherBar-builtin";
|
||||
names?: string[];
|
||||
icons?: (string | null)[] | null;
|
||||
color?: ColorSet;
|
||||
orientation?: "horizontal" | "vertical";
|
||||
spacing?: number;
|
||||
names: string[];
|
||||
icons: (string | null)[] | null;
|
||||
color: ColorSet;
|
||||
orientation: "horizontal" | "vertical";
|
||||
spacing: number;
|
||||
allow_none: boolean;
|
||||
selectedName?: string | null;
|
||||
selectedName: string | null;
|
||||
};
|
||||
|
||||
export class SwitcherBarComponent extends ComponentBase {
|
||||
declare state: Required<SwitcherBarState>;
|
||||
|
||||
export class SwitcherBarComponent extends ComponentBase<SwitcherBarState> {
|
||||
private innerElement: HTMLElement; // Used for alignment
|
||||
private markerElement: HTMLElement; // Highlights the selected item
|
||||
private backgroundOptionsElement: HTMLElement; // Displays all options
|
||||
@@ -259,7 +257,7 @@ export class SwitcherBarComponent extends ComponentBase {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
buildContent(deltaState: SwitcherBarState): HTMLElement {
|
||||
buildContent(deltaState: DeltaState<SwitcherBarState>): HTMLElement {
|
||||
let result = document.createElement("div");
|
||||
result.classList.add("rio-switcher-bar-options");
|
||||
result.style.gap = `${this.state.spacing}rem`;
|
||||
@@ -299,7 +297,7 @@ export class SwitcherBarComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: SwitcherBarState,
|
||||
deltaState: DeltaState<SwitcherBarState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { colorToCssString } from "../cssUtils";
|
||||
import { Color } from "../dataModels";
|
||||
import { markEventAsHandled } from "../eventHandling";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
type TableValue = number | string;
|
||||
|
||||
@@ -11,23 +10,21 @@ type TableStyle = {
|
||||
width: number;
|
||||
height: number;
|
||||
|
||||
fontColor?: Color;
|
||||
backgroundColor?: Color;
|
||||
italic?: boolean;
|
||||
fontWeight?: "normal" | "bold";
|
||||
fontColor: Color;
|
||||
backgroundColor: Color;
|
||||
italic: boolean;
|
||||
fontWeight: "normal" | "bold";
|
||||
};
|
||||
|
||||
type TableState = ComponentState & {
|
||||
_type_: "Table-builtin";
|
||||
show_row_numbers?: boolean;
|
||||
headers?: string[] | null;
|
||||
columns?: TableValue[][];
|
||||
styling?: TableStyle[];
|
||||
show_row_numbers: boolean;
|
||||
headers: string[] | null;
|
||||
columns: TableValue[][];
|
||||
styling: TableStyle[];
|
||||
};
|
||||
|
||||
export class TableComponent extends ComponentBase {
|
||||
declare state: Required<TableState>;
|
||||
|
||||
export class TableComponent extends ComponentBase<TableState> {
|
||||
private dataWidth: number;
|
||||
private dataHeight: number;
|
||||
|
||||
@@ -66,7 +63,7 @@ export class TableComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: TableState,
|
||||
deltaState: DeltaState<TableState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -6,35 +6,33 @@ import {
|
||||
TextStyle,
|
||||
} from "../dataModels";
|
||||
import { textfillToCss, textStyleToCss } from "../cssUtils";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type TextState = ComponentState & {
|
||||
_type_: "Text-builtin";
|
||||
text?: string;
|
||||
selectable?: boolean;
|
||||
style?: "heading1" | "heading2" | "heading3" | "text" | "dim" | TextStyle;
|
||||
justify?: "left" | "right" | "center" | "justify";
|
||||
overflow?: "nowrap" | "wrap" | "ellipsize";
|
||||
text: string;
|
||||
selectable: boolean;
|
||||
style: "heading1" | "heading2" | "heading3" | "text" | "dim" | TextStyle;
|
||||
justify: "left" | "right" | "center" | "justify";
|
||||
overflow: "nowrap" | "wrap" | "ellipsize";
|
||||
|
||||
font?: string | null;
|
||||
fill?:
|
||||
font: string | null;
|
||||
fill:
|
||||
| Color
|
||||
| SolidFill
|
||||
| LinearGradientFill
|
||||
| ImageFill
|
||||
| null
|
||||
| "not given";
|
||||
font_size?: number | null;
|
||||
italic?: boolean | null;
|
||||
font_weight?: "normal" | "bold" | null;
|
||||
underlined?: boolean | null;
|
||||
strikethrough?: boolean | null;
|
||||
all_caps?: boolean | null;
|
||||
font_size: number | null;
|
||||
italic: boolean | null;
|
||||
font_weight: "normal" | "bold" | null;
|
||||
underlined: boolean | null;
|
||||
strikethrough: boolean | null;
|
||||
all_caps: boolean | null;
|
||||
};
|
||||
|
||||
export class TextComponent extends ComponentBase {
|
||||
declare state: Required<TextState>;
|
||||
|
||||
export class TextComponent extends ComponentBase<TextState> {
|
||||
private inner: HTMLElement;
|
||||
|
||||
createElement(): HTMLElement {
|
||||
@@ -48,7 +46,7 @@ export class TextComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: TextState,
|
||||
deltaState: DeltaState<TextState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,30 +1,36 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, DeltaState } from "./componentBase";
|
||||
import { Debouncer } from "../debouncer";
|
||||
import { InputBox, InputBoxStyle } from "../inputBox";
|
||||
import { markEventAsHandled } from "../eventHandling";
|
||||
import {
|
||||
KeyboardFocusableComponent,
|
||||
KeyboardFocusableComponentState,
|
||||
} from "./keyboardFocusableComponent";
|
||||
|
||||
export type TextInputState = ComponentState & {
|
||||
export type TextInputState = KeyboardFocusableComponentState & {
|
||||
_type_: "TextInput-builtin";
|
||||
text?: string;
|
||||
label?: string;
|
||||
accessibility_label?: string;
|
||||
style?: InputBoxStyle;
|
||||
prefix_text?: string;
|
||||
suffix_text?: string;
|
||||
is_secret?: boolean;
|
||||
is_sensitive?: boolean;
|
||||
is_valid?: boolean;
|
||||
text: string;
|
||||
label: string;
|
||||
accessibility_label: string;
|
||||
style: InputBoxStyle;
|
||||
prefix_text: string;
|
||||
suffix_text: string;
|
||||
is_secret: boolean;
|
||||
is_sensitive: boolean;
|
||||
is_valid: boolean;
|
||||
};
|
||||
|
||||
export class TextInputComponent extends ComponentBase {
|
||||
declare state: Required<TextInputState>;
|
||||
|
||||
export class TextInputComponent extends KeyboardFocusableComponent<TextInputState> {
|
||||
private inputBox: InputBox;
|
||||
private onChangeLimiter: Debouncer;
|
||||
|
||||
createElement(): HTMLElement {
|
||||
this.inputBox = new InputBox();
|
||||
|
||||
if (this.state.auto_focus) {
|
||||
this.inputBox.inputElement.autofocus = true;
|
||||
}
|
||||
|
||||
let element = this.inputBox.outerElement;
|
||||
|
||||
// Create a rate-limited function for notifying the backend of changes.
|
||||
@@ -115,7 +121,7 @@ export class TextInputComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: TextInputState,
|
||||
deltaState: DeltaState<TextInputState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
@@ -159,7 +165,7 @@ export class TextInputComponent extends ComponentBase {
|
||||
}
|
||||
}
|
||||
|
||||
grabKeyboardFocus(): void {
|
||||
this.inputBox.focus();
|
||||
protected override getElementForKeyboardFocus(): HTMLElement {
|
||||
return this.inputBox.inputElement;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { applySwitcheroo } from "../designApplication";
|
||||
import { ColorSet, ComponentId } from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type ThemeContextSwitcherState = ComponentState & {
|
||||
_type_: "ThemeContextSwitcher-builtin";
|
||||
content?: ComponentId;
|
||||
color?: ColorSet;
|
||||
content: ComponentId;
|
||||
color: ColorSet;
|
||||
};
|
||||
|
||||
export class ThemeContextSwitcherComponent extends ComponentBase {
|
||||
declare state: Required<ThemeContextSwitcherState>;
|
||||
|
||||
export class ThemeContextSwitcherComponent extends ComponentBase<ThemeContextSwitcherState> {
|
||||
createElement(): HTMLElement {
|
||||
let element = document.createElement("div");
|
||||
element.classList.add("rio-single-container");
|
||||
@@ -18,7 +16,7 @@ export class ThemeContextSwitcherComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: ThemeContextSwitcherState,
|
||||
deltaState: DeltaState<ThemeContextSwitcherState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
import { getPositionerByName, PopupManager } from "../popupManager";
|
||||
|
||||
export type TooltipState = ComponentState & {
|
||||
_type_: "Tooltip-builtin";
|
||||
anchor?: ComponentId;
|
||||
_tip_component?: ComponentId | null;
|
||||
position?: "auto" | "left" | "top" | "right" | "bottom";
|
||||
gap?: number;
|
||||
anchor: ComponentId;
|
||||
_tip_component: ComponentId | null;
|
||||
position: "auto" | "left" | "top" | "right" | "bottom";
|
||||
gap: number;
|
||||
};
|
||||
|
||||
export class TooltipComponent extends ComponentBase {
|
||||
declare state: Required<TooltipState>;
|
||||
|
||||
export class TooltipComponent extends ComponentBase<TooltipState> {
|
||||
private popupElement: HTMLElement;
|
||||
private popupManager: PopupManager;
|
||||
|
||||
@@ -51,7 +49,7 @@ export class TooltipComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: TooltipState,
|
||||
deltaState: DeltaState<TooltipState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { ComponentBase, ComponentState } from "./componentBase";
|
||||
import { ComponentBase, ComponentState, DeltaState } from "./componentBase";
|
||||
|
||||
export type WebviewState = ComponentState & {
|
||||
_type_: "Webview-builtin";
|
||||
content?: string; // Url or Html code
|
||||
enable_pointer_events?: boolean;
|
||||
resize_to_fit_content?: boolean;
|
||||
content: string; // Url or Html code
|
||||
enable_pointer_events: boolean;
|
||||
resize_to_fit_content: boolean;
|
||||
};
|
||||
|
||||
export class WebviewComponent extends ComponentBase {
|
||||
declare state: Required<WebviewState>;
|
||||
|
||||
export class WebviewComponent extends ComponentBase<WebviewState> {
|
||||
private iframe: HTMLIFrameElement | null = null;
|
||||
private resizeObserver: ResizeObserver | null = null;
|
||||
private isInitialized = false;
|
||||
@@ -21,7 +19,7 @@ export class WebviewComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
updateElement(
|
||||
deltaState: WebviewState,
|
||||
deltaState: DeltaState<WebviewState>,
|
||||
latentComponents: Set<ComponentBase>
|
||||
): void {
|
||||
super.updateElement(deltaState, latentComponents);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { goingAway, pixelsPerRem } from "./app";
|
||||
import { componentsById, updateComponentStates } from "./componentManagement";
|
||||
import { KeyboardFocusableComponent } from "./components/keyboardFocusableComponent";
|
||||
import {
|
||||
requestFileUpload,
|
||||
registerFont,
|
||||
@@ -386,8 +387,10 @@ export async function processMessageReturnResponse(
|
||||
|
||||
case "setKeyboardFocus":
|
||||
let component = componentsById[message.params.component_id]!;
|
||||
// @ts-expect-error
|
||||
component.grabKeyboardFocus();
|
||||
|
||||
if (component instanceof KeyboardFocusableComponent) {
|
||||
component.grabKeyboardFocus();
|
||||
}
|
||||
|
||||
response = null;
|
||||
break;
|
||||
|
||||
@@ -9,7 +9,7 @@ from uniserde import JsonDoc
|
||||
import rio
|
||||
|
||||
from .. import utils
|
||||
from .fundamental_component import FundamentalComponent
|
||||
from .keyboard_focusable_components import KeyboardFocusableFundamentalComponent
|
||||
|
||||
__all__ = [
|
||||
"Dropdown",
|
||||
@@ -39,7 +39,7 @@ class DropdownChangeEvent(t.Generic[T]):
|
||||
|
||||
|
||||
@t.final
|
||||
class Dropdown(FundamentalComponent, t.Generic[T]):
|
||||
class Dropdown(KeyboardFocusableFundamentalComponent, t.Generic[T]):
|
||||
"""
|
||||
A dropdown menu for selecting from one of several options.
|
||||
|
||||
@@ -248,9 +248,9 @@ class Dropdown(FundamentalComponent, t.Generic[T]):
|
||||
)
|
||||
|
||||
# Trigger the event
|
||||
assert not isinstance(
|
||||
self.selected_value, utils.NotGiven
|
||||
), self.selected_value
|
||||
assert not isinstance(self.selected_value, utils.NotGiven), (
|
||||
self.selected_value
|
||||
)
|
||||
|
||||
await self.call_event_handler(
|
||||
self.on_change, DropdownChangeEvent(self.selected_value)
|
||||
|
||||
@@ -10,10 +10,7 @@ import rio
|
||||
from .. import inspection, utils
|
||||
from .component import Component
|
||||
|
||||
__all__ = [
|
||||
"FundamentalComponent",
|
||||
"KeyboardFocusableFundamentalComponent",
|
||||
]
|
||||
__all__ = ["FundamentalComponent"]
|
||||
|
||||
|
||||
JAVASCRIPT_SOURCE_TEMPLATE = """
|
||||
@@ -181,19 +178,3 @@ class FundamentalComponent(Component):
|
||||
|
||||
if not was_already_dirty:
|
||||
self.session._dirty_components.discard(self)
|
||||
|
||||
|
||||
class KeyboardFocusableFundamentalComponent(FundamentalComponent):
|
||||
"""
|
||||
## Metadata
|
||||
|
||||
`public`: False
|
||||
"""
|
||||
|
||||
async def grab_keyboard_focus(self) -> None:
|
||||
"""
|
||||
## Metadata
|
||||
|
||||
`public`: False
|
||||
"""
|
||||
await self.session._remote_set_keyboard_focus(self._id_)
|
||||
|
||||
@@ -8,7 +8,7 @@ from uniserde import Jsonable
|
||||
|
||||
import rio
|
||||
|
||||
from .fundamental_component import KeyboardFocusableFundamentalComponent
|
||||
from .keyboard_focusable_components import KeyboardFocusableFundamentalComponent
|
||||
|
||||
__all__ = [
|
||||
"KeyEventListener",
|
||||
@@ -438,7 +438,7 @@ SoftwareKey = t.Literal[
|
||||
"color-f1-green",
|
||||
"color-f2-yellow",
|
||||
"color-f3-blue",
|
||||
"color-f4-grey",
|
||||
"color-f4-gray",
|
||||
"color-f5-brown",
|
||||
"closed-caption-toggle",
|
||||
"dimmer",
|
||||
@@ -569,7 +569,7 @@ SoftwareKey = t.Literal[
|
||||
ModifierKey = t.Literal["alt", "control", "meta", "shift"]
|
||||
KeyCombination = SoftwareKey | tuple[ModifierKey | SoftwareKey, ...]
|
||||
|
||||
_MODIFIERS = ("control", "shift", "alt", "meta")
|
||||
_MODIFIERS: tuple[ModifierKey, ...] = t.get_args(ModifierKey)
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
|
||||
47
rio/components/keyboard_focusable_components.py
Normal file
47
rio/components/keyboard_focusable_components.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import abc
|
||||
import dataclasses
|
||||
|
||||
from .component import Component
|
||||
from .fundamental_component import FundamentalComponent
|
||||
|
||||
__all__ = [
|
||||
"KeyboardFocusableComponent",
|
||||
"KeyboardFocusableFundamentalComponent",
|
||||
]
|
||||
|
||||
|
||||
class KeyboardFocusableComponent(Component, abc.ABC):
|
||||
"""
|
||||
## Attributes
|
||||
|
||||
`auto_focus`: Whether this component should receive the keyboard focus when
|
||||
it is created.
|
||||
|
||||
## Metadata
|
||||
|
||||
`public`: False
|
||||
"""
|
||||
|
||||
_: dataclasses.KW_ONLY
|
||||
auto_focus: bool = False
|
||||
|
||||
@abc.abstractmethod
|
||||
async def grab_keyboard_focus(self) -> None:
|
||||
"""
|
||||
## Metadata
|
||||
|
||||
`public`: False
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class KeyboardFocusableFundamentalComponent(
|
||||
KeyboardFocusableComponent, FundamentalComponent
|
||||
):
|
||||
async def grab_keyboard_focus(self) -> None:
|
||||
"""
|
||||
## Metadata
|
||||
|
||||
`public`: False
|
||||
"""
|
||||
await self.session._remote_set_keyboard_focus(self._id_)
|
||||
@@ -9,7 +9,7 @@ import rio
|
||||
|
||||
from .. import assets, color, fills
|
||||
from ..utils import EventHandler
|
||||
from .fundamental_component import KeyboardFocusableFundamentalComponent
|
||||
from .keyboard_focusable_components import KeyboardFocusableFundamentalComponent
|
||||
|
||||
__all__ = ["MediaPlayer"]
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from uniserde import JsonDoc
|
||||
|
||||
import rio
|
||||
|
||||
from .fundamental_component import KeyboardFocusableFundamentalComponent
|
||||
from .keyboard_focusable_components import KeyboardFocusableFundamentalComponent
|
||||
|
||||
__all__ = [
|
||||
"MultiLineTextInput",
|
||||
|
||||
@@ -7,7 +7,7 @@ import imy.docstrings
|
||||
|
||||
import rio
|
||||
|
||||
from .component import Component
|
||||
from .keyboard_focusable_components import KeyboardFocusableComponent
|
||||
|
||||
__all__ = [
|
||||
"NumberInput",
|
||||
@@ -83,7 +83,7 @@ class NumberInputFocusEvent:
|
||||
|
||||
|
||||
@t.final
|
||||
class NumberInput(Component):
|
||||
class NumberInput(KeyboardFocusableComponent):
|
||||
"""
|
||||
Like `TextInput`, but specifically for inputting numbers.
|
||||
|
||||
@@ -217,8 +217,21 @@ class NumberInput(Component):
|
||||
on_gain_focus: rio.EventHandler[NumberInputFocusEvent] = None
|
||||
on_lose_focus: rio.EventHandler[NumberInputFocusEvent] = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self._text_input = None
|
||||
def __post_init__(self):
|
||||
self._text_input = rio.TextInput(
|
||||
text=self._formatted_value(),
|
||||
label=self.label,
|
||||
style=self.style,
|
||||
prefix_text=self.prefix_text,
|
||||
suffix_text=self.suffix_text,
|
||||
is_sensitive=self.is_sensitive,
|
||||
is_valid=self.is_valid,
|
||||
accessibility_label=self.accessibility_label,
|
||||
auto_focus=self.auto_focus,
|
||||
on_confirm=self._on_confirm,
|
||||
on_gain_focus=self._on_gain_focus,
|
||||
on_lose_focus=self._on_lose_focus,
|
||||
)
|
||||
|
||||
def _try_set_value(self, raw_value: str) -> bool:
|
||||
"""
|
||||
@@ -348,27 +361,7 @@ class NumberInput(Component):
|
||||
return f"{integer_part_with_sep}{self.session._decimal_separator}{frac_str}"
|
||||
|
||||
def build(self) -> rio.Component:
|
||||
# Build the component
|
||||
self._text_input = rio.TextInput(
|
||||
text=self._formatted_value(),
|
||||
label=self.label,
|
||||
style=self.style,
|
||||
prefix_text=self.prefix_text,
|
||||
suffix_text=self.suffix_text,
|
||||
is_sensitive=self.is_sensitive,
|
||||
is_valid=self.is_valid,
|
||||
accessibility_label=self.accessibility_label,
|
||||
on_confirm=self._on_confirm,
|
||||
on_gain_focus=self._on_gain_focus,
|
||||
on_lose_focus=self._on_lose_focus,
|
||||
)
|
||||
return self._text_input
|
||||
|
||||
async def grab_keyboard_focus(self) -> None:
|
||||
"""
|
||||
## Metadata
|
||||
|
||||
`public`: False
|
||||
"""
|
||||
if self._text_input is not None:
|
||||
await self._text_input.grab_keyboard_focus()
|
||||
await self._text_input.grab_keyboard_focus()
|
||||
|
||||
@@ -59,6 +59,9 @@ class SwitcherBar(FundamentalComponent, t.Generic[T]):
|
||||
`names`: The list of names to display for each value. If `None`, the
|
||||
string representation of each value is used.
|
||||
|
||||
`icons`: Optionally, a list of icons that should be displayed next to the
|
||||
names.
|
||||
|
||||
`color`: The color of the switcher bar.
|
||||
|
||||
`orientation`: The orientation of the switcher bar.
|
||||
@@ -170,11 +173,6 @@ class SwitcherBar(FundamentalComponent, t.Generic[T]):
|
||||
# SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never",
|
||||
# SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never",
|
||||
):
|
||||
"""
|
||||
## Parameters
|
||||
|
||||
`icons`: The list of icons to display along with with each name.
|
||||
"""
|
||||
super().__init__(
|
||||
key=key,
|
||||
margin=margin,
|
||||
|
||||
60
rio/components/tabs.py
Normal file
60
rio/components/tabs.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import typing as t
|
||||
|
||||
import typing_extensions as te
|
||||
|
||||
from .component import Component
|
||||
|
||||
|
||||
class Tab(t.TypedDict):
|
||||
name: str
|
||||
icon: te.NotRequired[str]
|
||||
content: Component
|
||||
|
||||
|
||||
class Tabs(Component):
|
||||
tabs: tuple[Tab, ...]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*tabs: Tab,
|
||||
key: str | int | None = None,
|
||||
margin: float | None = None,
|
||||
margin_x: float | None = None,
|
||||
margin_y: float | None = None,
|
||||
margin_left: float | None = None,
|
||||
margin_top: float | None = None,
|
||||
margin_right: float | None = None,
|
||||
margin_bottom: float | None = None,
|
||||
min_width: float = 0,
|
||||
min_height: float = 0,
|
||||
# MAX-SIZE-BRANCH max_width: float | None = None,
|
||||
# MAX-SIZE-BRANCH max_height: float | None = None,
|
||||
grow_x: bool = False,
|
||||
grow_y: bool = False,
|
||||
align_x: float | None = None,
|
||||
align_y: float | None = None,
|
||||
# SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never",
|
||||
# SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never",
|
||||
):
|
||||
super().__init__(
|
||||
key=key,
|
||||
margin=margin,
|
||||
margin_x=margin_x,
|
||||
margin_y=margin_y,
|
||||
margin_left=margin_left,
|
||||
margin_top=margin_top,
|
||||
margin_right=margin_right,
|
||||
margin_bottom=margin_bottom,
|
||||
min_width=min_width,
|
||||
min_height=min_height,
|
||||
# MAX-SIZE-BRANCH max_width=max_width,
|
||||
# MAX-SIZE-BRANCH max_height=max_height,
|
||||
grow_x=grow_x,
|
||||
grow_y=grow_y,
|
||||
align_x=align_x,
|
||||
align_y=align_y,
|
||||
# SCROLLING-REWORK scroll_x=scroll_x,
|
||||
# SCROLLING-REWORK scroll_y=scroll_y,
|
||||
)
|
||||
|
||||
self.tabs = tabs
|
||||
@@ -7,7 +7,7 @@ import imy.docstrings
|
||||
|
||||
import rio
|
||||
|
||||
from .fundamental_component import KeyboardFocusableFundamentalComponent
|
||||
from .keyboard_focusable_components import KeyboardFocusableFundamentalComponent
|
||||
|
||||
__all__ = [
|
||||
"TextInput",
|
||||
|
||||
@@ -258,8 +258,14 @@ class PendingAttributeBinding:
|
||||
|
||||
def __getattr__(self, name: str):
|
||||
operation = f".{name}"
|
||||
self._warn_about_incorrect_usage(operation)
|
||||
raise AttributeError(self._get_error_message(operation))
|
||||
|
||||
def __getitem__(self, item: object):
|
||||
operation = f"[{item!r}]"
|
||||
self._warn_about_incorrect_usage(operation)
|
||||
raise TypeError(self._get_error_message(operation))
|
||||
|
||||
def __add__(self, other):
|
||||
self._warn_about_incorrect_usage("+")
|
||||
return NotImplemented
|
||||
|
||||
Reference in New Issue
Block a user