Appearance
Component JavaScript
This guide explains how to create interactive JavaScript components in LamaPress.
Table of Contents
- Overview
- Component Structure
- Automatic Loading
- Component Methods
- Accessing Core Instances
- Component Examples
- Best Practices
- Related Documentation
Overview
JavaScript components in LamaPress are automatically loaded when an element has a data-component attribute. Components export default classes with lifecycle methods for initialization, event binding, animations, and cleanup.
Component Structure
Basic Component
Create components/{type}/{name}/index.js:
javascript
import { app } from '@src/js/core/app'
export default class MyComponent {
constructor(element) {
this.element = element
this.init()
}
init = () => {
// Initialization logic
}
bindEvents = () => {
// Event listeners
}
enter = () => {
// Reveal animations
}
destroy = () => {
// Cleanup
}
}Component Lifecycle
Components follow this lifecycle:
- constructor() - Called when component is created
- init() - Setup logic (called from constructor)
- bindEvents() - Attach event listeners (call in init)
- enter() - Reveal animations (called automatically)
- destroy() - Cleanup (called on page transitions)
Automatic Loading
data-component Attribute
Add data-component to HTML element:
php
<div class="ll-block--accordion" data-component="blocks/accordion">
<!-- Component content -->
</div>Format: {type}/{name}
Examples:
data-component="blocks/accordion"data-component="sections/hero_basic"data-component="parts/button"
Component Registration
Components are automatically:
- Detected via
data-componentattribute - Loaded from
components/{type}/{name}/index.js - Instantiated with element as parameter
- Registered in component instance map
Component Methods
constructor()
Called when component is created:
javascript
constructor(element) {
this.element = element
this.init()
}Parameters:
element- DOM element withdata-componentattribute
init()
Setup logic for component:
javascript
init = () => {
this.setupElements()
this.bindEvents()
}Common tasks:
- Cache DOM elements
- Initialize variables
- Call
bindEvents()
bindEvents()
Attach event listeners:
javascript
bindEvents = () => {
this.button?.addEventListener('click', this.handleClick)
app.instances.get('eventManager').addScopeToEvent('scroll', 'page')
}Best practice: Always call from init().
enter()
Reveal animations (called automatically):
javascript
enter = () => {
gsap.from(this.element, {
opacity: 0,
y: 20,
duration: 1,
})
}Note: Called after component is rendered.
destroy()
Cleanup on page transitions:
javascript
destroy = () => {
this.timeline?.kill()
this.button?.removeEventListener('click', this.handleClick)
}Cleanup tasks:
- Kill GSAP timelines
- Remove event listeners
- Clear intervals/timeouts
Accessing Core Instances
Access core instances via app.instances:
javascript
import { app } from '@src/js/core/app'
export default class MyComponent {
init = () => {
const scroller = app.instances.get('scroller')
const device = app.instances.get('device')
const router = app.instances.get('router')
}
}Available instances:
scroller- Scroll managementdevice- Device detectionrouter- Page routingeventManager- Event systempage- Current page instance
Component Examples
Simple Interactive Component
javascript
import { app } from '@src/js/core/app'
export default class Accordion {
constructor(element) {
this.element = element
this.allowMultiple = this.element.getAttribute('data-multiple') === 'true'
this.items = [...this.element.querySelectorAll('[data-component="blocks/accordion_item"]')]
}
closeItems = (itemsToExclude = []) => {
const itemsToClose = this.items.filter((item) => !itemsToExclude.includes(item))
itemsToClose.map((item) => {
app.instances.get('page').instances.get(item).close()
})
}
}Component with Animation
javascript
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/all'
export default class AnimatedSection {
constructor(element) {
this.element = element
this.init()
}
init = () => {
this.title = this.element.querySelector('.js-title')
this.content = this.element.querySelector('.js-content')
gsap.set([this.title, this.content], {
opacity: 0,
y: 20,
})
}
enter = () => {
this.timeline = gsap.timeline({
scrollTrigger: {
trigger: this.element,
start: 'top 80%',
},
})
this.timeline.to(this.title, {
opacity: 1,
y: 0,
duration: 0.6,
})
this.timeline.to(this.content, {
opacity: 1,
y: 0,
duration: 0.6,
}, '-=0.3')
}
destroy = () => {
this.timeline?.kill()
ScrollTrigger.getAll().forEach(trigger => {
if (trigger.vars.trigger === this.element) {
trigger.kill()
}
})
}
}Component with Event Listeners
javascript
import { app } from '@src/js/core/app'
export default class VideoPlayer {
constructor(element) {
this.element = element
this.init()
}
init = () => {
this.video = this.element.querySelector('video')
this.playButton = this.element.querySelector('.js-play')
this.pauseButton = this.element.querySelector('.js-pause')
this.bindEvents()
}
bindEvents = () => {
this.playButton?.addEventListener('click', this.play)
this.pauseButton?.addEventListener('click', this.pause)
this.video?.addEventListener('ended', this.onEnded)
}
play = () => {
this.video?.play()
}
pause = () => {
this.video?.pause()
}
onEnded = () => {
// Handle video end
}
destroy = () => {
this.playButton?.removeEventListener('click', this.play)
this.pauseButton?.removeEventListener('click', this.pause)
this.video?.removeEventListener('ended', this.onEnded)
}
}Best Practices
1. Use Arrow Functions
Use arrow functions for methods to preserve this:
javascript
// Good
handleClick = () => {
// this.element is accessible
}
// Avoid
handleClick() {
// this might be lost
}2. Clean Up in destroy()
Always clean up in destroy():
javascript
destroy = () => {
this.timeline?.kill()
this.button?.removeEventListener('click', this.handleClick)
clearInterval(this.interval)
}3. Access Other Components
Access other component instances:
javascript
const otherComponent = app.instances.get('page').instances.get(element)Related Documentation
- JavaScript Architecture - JavaScript system
- Animations GSAP - GSAP patterns
- Core Instances - Core instance APIs
- Component System - Component structure
Next Steps:
- Review JavaScript Architecture for system overview
- Check Animations GSAP for animation patterns
- See Core Instances for available APIs