Documentation
Component Composition Features
The composition system follows a functional programming approach, allowing you to build components by composing various features together.
Overview
The composition system is based on the concept of Higher-Order Functions (HOF) that enhance a base component with additional capabilities. Each feature is implemented as a function that takes a configuration object and returns another function that applies the feature to a component.
The general pattern is:
const withFeature = (config) => (component) => {
// Enhance component with feature
return enhancedComponent;
};
Features can be composed using the pipe
function:
const component = pipe(
createBase,
withElement(elementConfig),
withEvents(),
withText(config),
withIcon(config),
withLifecycle()
)(config);
Core Features
withEvents
Adds event handling capabilities to a component. It creates an event emitter that allows the component to emit and listen for custom events.
withEvents(): <T extends BaseComponent>(component: T) => T & EventComponent
Provided Capabilities:
on(event: string, handler: Function)
: Subscribe to an eventoff(event: string, handler: Function)
: Unsubscribe from an eventemit(event: string, data?: any)
: Emit an event with optional data
const component = pipe(
createBase,
withEvents()
)(config);
component.on('click', (data) => console.log('Clicked:', data));
component.emit('click', { x: 10, y: 20 });
withText
Adds text management to a component. Creates a text manager that handles setting and getting text content.
withText<T extends TextConfig>(config: T): <C extends ElementComponent>(component: C) => C & TextComponent
Configuration Options:
text
: Initial text contentprefix
: CSS class prefixcomponentName
: Component name for class generation
text.setText(text: string)
: Set the text contenttext.getText()
: Get the current text contenttext.getElement()
: Get the text DOM element
const component = pipe(
createBase,
withElement(elementConfig),
withText({ text: 'Initial text' })
)(config);
component.text.setText('Updated text');
withIcon
Adds icon management to a component. Creates an icon manager that handles setting and getting icon content.
withIcon<T extends IconConfig>(config: T): <C extends ElementComponent>(component: C) => C & IconComponent
Configuration Options:
icon
: Icon HTML content (typically SVG)iconPosition
: Position of the icon ('start' or 'end')iconSize
: Size of the iconprefix
: CSS class prefixcomponentName
: Component name for class generation
icon.setIcon(html: string)
: Set the icon HTML contenticon.getIcon()
: Get the current icon HTML contenticon.getElement()
: Get the icon DOM element
const component = pipe(
createBase,
withElement(elementConfig),
withIcon({
icon: '<svg>...</svg>',
iconPosition: 'start'
})
)(config);
component.icon.setIcon('<svg>...</svg>');
withVariant
Adds a variant class to a component. Useful for implementing different visual styles (filled, outlined, etc.).
withVariant<T extends VariantConfig>(config: T): <C extends ElementComponent>(component: C) => C
Configuration Options:
variant
: Variant name (e.g., 'filled', 'outlined')prefix
: CSS class prefixcomponentName
: Component name for class generation
const component = pipe(
createBase,
withElement(elementConfig),
withVariant({ variant: 'outlined' })
)(config);
withSize
Adds a size class to a component. Useful for implementing different size variants (small, medium, large).
withSize<T extends SizeConfig>(config: T): <C extends ElementComponent>(component: C) => C
Configuration Options:
size
: Size name (e.g., 'small', 'medium', 'large')prefix
: CSS class prefixcomponentName
: Component name for class generation
const component = pipe(
createBase,
withElement(elementConfig),
withSize({ size: 'large' })
)(config);
withPosition
Adds positioning functionality to a component. Useful for positioning components relative to each other.
withPosition<T extends PositionConfig>(config: T): <C extends ElementComponent>(component: C) => C & PositionComponent
Configuration Options:
position
: Position value (e.g., 'start', 'end', 'top', 'bottom')prefix
: CSS class prefixcomponentName
: Component name for class generation
LEFT
,RIGHT
,TOP
,BOTTOM
START
,END
,CENTER
position.setPosition(newPosition: string)
: Change the positionposition.getPosition()
: Get the current position
const component = pipe(
createBase,
withElement(elementConfig),
withPosition({ position: 'start' })
)(config);
component.position.setPosition('end');
withRipple
Adds Material Design ripple effect to a component. Creates a ripple controller that handles mounting and unmounting ripple effects.
withRipple<T extends RippleFeatureConfig>(config: T): <C extends ElementComponent>(component: C) => C & RippleComponent
Configuration Options:
ripple
: Whether to enable ripple effectrippleConfig
: Detailed configuration for the ripple effect
- duration
: Duration of the ripple animation in milliseconds
- timing
: Timing function for the animation
- opacity
: Opacity values for the ripple
ripple.mount(element: HTMLElement)
: Mount ripple effect to an elementripple.unmount(element: HTMLElement)
: Unmount ripple effect from an element
const component = pipe(
createBase,
withElement(elementConfig),
withRipple({
ripple: true,
rippleConfig: {
duration: 300,
timing: 'ease-out'
}
})
)(config);
withInput
Creates an input element and adds it to a component. Useful for checkbox, radio, and other input components.
withInput<T extends InputConfig>(config: T): <C extends ElementComponent>(component: C) => C & InputComponent
Configuration Options:
name
: Input name attributechecked
: Initial checked staterequired
: Whether input is requireddisabled
: Whether input is disabledvalue
: Input value attributelabel
orariaLabel
: Accessibility label
input
: HTML input elementgetValue()
: Get the current input valuesetValue(value: string)
: Set the input value
const component = pipe(
createBase,
withElement(elementConfig),
withInput({
name: 'agreement',
checked: false,
required: true
})
)(config);
component.setValue('accepted');
withCheckable
Adds checked state management to a component with an input. Manages visual state and event emission for checked changes.
withCheckable<T extends CheckableConfig>(config: T): <C extends InputComponent>(component: C) => C & CheckableComponent
Configuration Options:
checked
: Initial checked state
checkable.check()
: Check the componentcheckable.uncheck()
: Uncheck the componentcheckable.toggle()
: Toggle the checked statecheckable.isChecked()
: Get the current checked state
const component = pipe(
createBase,
withElement(elementConfig),
withInput({ name: 'agreement' }),
withCheckable({ checked: false })
)(config);
component.checkable.toggle();
withStyle
Adds style classes to a component based on configuration. Useful for applying multiple style aspects at once.
withStyle<T extends StyleConfig>(config: T): <C extends ElementComponent>(component: C) => C
Configuration Options:
variant
: Variant style to applysize
: Size style to apply
const component = pipe(
createBase,
withElement(elementConfig),
withStyle({
variant: 'outlined',
size: 'large'
})
)(config);
withTextInput
Enhances a component with text input functionality. Useful for textfields, search inputs, etc.
withTextInput<T extends TextInputConfig>(config: T): <C extends ElementComponent>(component: C) => C & TextInputComponent
Configuration Options:
type
: Input type (text, password, etc.)multiline
: Whether to use textarea instead of inputname
: Input name attributerequired
: Whether input is requireddisabled
: Whether input is disabledmaxLength
: Maximum allowed lengthpattern
: Input validation patternautocomplete
: Autocomplete settingvalue
: Initial input value
input
: HTML input or textarea elementsetValue(value: string)
: Set the input valuegetValue()
: Get the current input valuesetAttribute(name: string, value: string)
: Set an attributegetAttribute(name: string)
: Get an attributeremoveAttribute(name: string)
: Remove an attribute
const component = pipe(
createBase,
withElement(elementConfig),
withTextInput({
type: 'password',
required: true,
maxLength: 20
})
)(config);
component.setValue('secure123');
withTextLabel
Adds a text label to a component. Useful for form controls, switches, etc.
withTextLabel<T extends TextLabelConfig>(config: T): <C extends ElementComponent>(component: C) => C & LabelComponent
Configuration Options:
label
: Label text contentlabelPosition
: Position of the label ('start' or 'end')prefix
: CSS class prefixcomponentName
: Component name for class generation
label.setText(text: string)
: Set the label textlabel.getText()
: Get the current label textlabel.getElement()
: Get the label DOM element
const component = pipe(
createBase,
withElement(elementConfig),
withTextLabel({
label: 'Agree to terms',
labelPosition: 'end'
})
)(config);
component.label.setText('Accept all terms and conditions');
withTrack
Adds track and thumb elements to a component. Useful for sliders, switches, and other track-based components.
withTrack<T extends TrackConfig>(config: T): <C extends ElementComponent>(component: C) => C & TrackComponent
Configuration Options:
prefix
: CSS class prefixcomponentName
: Component name for class generationicon
: Custom icon HTML or 'none'
track
: Track DOM elementthumb
: Thumb DOM element
const component = pipe(
createBase,
withElement(elementConfig),
withTrack({
prefix: 'mtrl',
componentName: 'switch'
})
)(config);
withBadge
Adds badge functionality to a component. Creates and configures a badge component attached to the main component.
withBadge<T extends BadgeConfig>(config: T): <C extends ElementComponent>(component: C) => C & BadgeComponent
Configuration Options:
badge
: Badge content to display (string or number)badgeConfig
: Detailed configuration for the badge
- variant
: Badge style variant
- color
: Badge color
- size
: Badge size
- position
: Badge position
- max
: Maximum number to display before showing "+"
prefix
: CSS class prefix
badge
: Badge component instance
const component = pipe(
createBase,
withElement(elementConfig),
withBadge({
badge: 5,
badgeConfig: {
position: 'top-right',
color: 'error'
}
})
)(config);
State Management Features
withDisabled
Adds disabled state management to a component. Manages both visual disabled state and functionality disabling.
withDisabled<T extends DisabledConfig>(config: T): <C extends ElementComponent>(component: C) => C & DisabledComponent
Configuration Options:
disabled
: Whether the component is initially disabledcomponentName
: Component name for class generation
disabled.enable()
: Enable the componentdisabled.disable()
: Disable the componentdisabled.toggle()
: Toggle the disabled statedisabled.isDisabled()
: Get the current disabled state
const component = pipe(
createBase,
withElement(elementConfig),
withDisabled({ disabled: false })
)(config);
component.disabled.disable();
withLifecycle
Adds lifecycle management to a component. Tracks component mounting state and provides cleanup functionality.
withLifecycle(): <T extends ElementComponent>(component: T) => T & LifecycleComponent
Provided Capabilities:
lifecycle.onMount(handler: () => void)
: Register mount event handlerlifecycle.onUnmount(handler: () => void)
: Register unmount event handlerlifecycle.mount()
: Mount the componentlifecycle.unmount()
: Unmount the componentlifecycle.isMounted()
: Check if component is mountedlifecycle.destroy()
: Destroy the component and clean up resources
const component = pipe(
createBase,
withElement(elementConfig),
withLifecycle()
)(config);
component.lifecycle.onMount(() => console.log('Component mounted'));
component.lifecycle.mount();
Enhanced Event Handling
withEnhancedEvents
Adds enhanced event handling capabilities to a component. Provides more powerful event management features than the basic withEvents
.
withEnhancedEvents(target?: HTMLElement): <C extends ElementComponent>(component: C) => C & EnhancedEventComponent
Parameters:
target
: Optional custom event target (defaults to component.element)
events.on(event: string, handler: Function)
: Add event listenerevents.off(event: string, handler: Function)
: Remove event listenerevents.addListeners(listeners: Record<string, Function>)
: Add multiple listenersevents.removeListeners(listeners: Record<string, Function>)
: Remove multiple listenersevents.once(event: string, handler: Function)
: Add one-time event handlerevents.destroy()
: Clean up all event listenerson(event: string, handler: Function)
: Shorthand for events.onoff(event: string, handler: Function)
: Shorthand for events.off
const component = pipe(
createBase,
withElement(elementConfig),
withEnhancedEvents()
)(config);
component.events.addListeners({
'click': (e) => console.log('Clicked'),
'focus': (e) => console.log('Focused')
});
component.events.once('hover', (e) => console.log('First hover only'));
Performance Features
withThrottle
Adds throttled event handling capabilities to a component. Limits how often an event handler can be called.
withThrottle(config: ThrottleConfig): <C extends ElementComponent>(component: C) => C & ThrottleComponent
Configuration Options:
throttledEvents
: Record of events with throttle settings
- handler
: Event handler function
- wait
: Throttle interval in milliseconds
- options
: Throttle options (leading, trailing)
addThrottledEvent(event, handler, wait, options)
: Add throttled eventremoveThrottledEvent(event)
: Remove throttled event
const component = pipe(
createBase,
withElement(elementConfig),
withThrottle({
throttledEvents: {
'scroll': {
handler: handleScroll,
wait: 100,
options: { trailing: true }
}
}
})
)(config);
component.addThrottledEvent('resize', handleResize, 200);
withDebounce
Adds debounced event handling capabilities to a component. Delays calling the event handler until after a specified wait time.
withDebounce(config: DebounceConfig): <C extends ElementComponent>(component: C) => C & DebounceComponent
Configuration Options:
debouncedEvents
: Record of events with debounce settings
- handler
: Event handler function
- wait
: Debounce delay in milliseconds
- options
: Debounce options (leading, maxWait)
addDebouncedEvent(event, handler, wait, options)
: Add debounced eventremoveDebouncedEvent(event)
: Remove debounced event
const component = pipe(
createBase,
withElement(elementConfig),
withDebounce({
debouncedEvents: {
'input': {
handler: handleInput,
wait: 300
}
}
})
)(config);
component.addDebouncedEvent('search', handleSearch, 500);
Gesture Features
withGestures
Adds comprehensive gesture recognition capabilities to a component. Handles various touch gestures like tap, swipe, pinch, etc.
withGestures(config: GestureFeatureConfig): <C extends ElementComponent>(component: C) => C & GesturesComponent
Configuration Options:
enableGestures
: Whether to enable gesture recognition initiallygestureHandlers
: Initial gesture event handlers- Other gesture-specific options (swipeThreshold, etc.)
gestures
: Gesture manager instanceonGesture(eventType, handler)
: Add gesture event handleroffGesture(eventType, handler)
: Remove gesture event handlerisGestureSupported(gestureType)
: Check if gesture is supportedenableGestures()
: Enable gesture recognitiondisableGestures()
: Disable gesture recognition
const component = pipe(
createBase,
withElement(elementConfig),
withGestures({
gestureHandlers: {
'tap': handleTap,
'swipeleft': handleSwipeLeft
}
})
)(config);
component.onGesture('pinch', handlePinch);
Individual Gesture Features
For more lightweight gesture handling, you can use specific gesture features:
withTapGesture
withTapGesture(config: TapGestureConfig): <C extends ElementComponent>(component: C) => C & TapGestureComponent
Provided Capabilities:
onTap(handler)
,offTap(handler)
enableTap()
,disableTap()
withSwipeGesture
withSwipeGesture(config: SwipeGestureConfig): <C extends ElementComponent>(component: C) => C & SwipeGestureComponent
Provided Capabilities:
onSwipe(handler)
,onSwipeLeft(handler)
,onSwipeRight(handler)
, etc.offSwipe(handler)
,offSwipeLeft(handler)
, etc.enableSwipe()
,disableSwipe()
withLongPressGesture
withLongPressGesture(config: LongPressGestureConfig): <C extends ElementComponent>(component: C) => C & LongPressGestureComponent
Provided Capabilities:
onLongPress(handler)
,offLongPress(handler)
enableLongPress()
,disableLongPress()
withPanGesture
withPanGesture(config: PanGestureConfig): <C extends ElementComponent>(component: C) => C & PanGestureComponent
Provided Capabilities:
onPanStart(handler)
,onPan(handler)
,onPanEnd(handler)
offPanStart(handler)
,offPan(handler)
,offPanEnd(handler)
enablePan()
,disablePan()
withPinchGesture
withPinchGesture(config: PinchGestureConfig): <C extends ElementComponent>(component: C) => C & PinchGestureComponent
Provided Capabilities:
onPinch(handler)
,onPinchStart(handler)
,onPinchEnd(handler)
offPinch(handler)
,offPinchStart(handler)
,offPinchEnd(handler)
enablePinch()
,disablePinch()
isPinchSupported()
withRotateGesture
withRotateGesture(config: RotateGestureConfig): <C extends ElementComponent>(component: C) => C & RotateGestureComponent
Provided Capabilities:
onRotate(handler)
,onRotateStart(handler)
,onRotateEnd(handler)
offRotate(handler)
,offRotateStart(handler)
,offRotateEnd(handler)
enableRotate()
,disableRotate()
isRotateSupported()
Feature Composition Best Practices
- Order Matters: Some features depend on others. For example,
withCheckable
requireswithInput
to be applied first. - Base Components First: Always start with
createBase
andwithElement
before adding other features. - State Management Later: Apply state management features like
withDisabled
andwithLifecycle
after UI features. - Lifecycle Last:
withLifecycle
is typically applied last to ensure proper cleanup of all added features. - Avoid Redundancy: Don't apply multiple features that provide the same capability. For example, don't use both
withEvents
andwithEnhancedEvents
. - Performance Considerations: For high-frequency events, consider using
withThrottle
orwithDebounce
. - Gesture Handling: Use the specific gesture features (
withTapGesture
, etc.) for better performance if you only need one gesture type.
Component API Pattern
When creating components, follow this pattern to ensure a consistent API:
- Use feature composition to build the component internally
- Create a public API that exposes only the necessary methods
- Use method chaining for a fluent interface
- Implement proper cleanup through the lifecycle feature
Example:
// Internal implementation
const createComponent = (config) => {
try {
const component = pipe(
createBase,
withElement(elementConfig),
withEvents(),
withText(config),
withVariant(config),
withDisabled(config),
withLifecycle()
)(config);
// Expose public API
return withAPI(getApiConfig(component))(component);
} catch (error) {
console.error('Component creation error:', error);
throw new Error(`Failed to create component: ${error.message}`);
}
};
This ensures a clean and consistent public API while keeping the internal implementation flexible and modular.