Portals, Timers, Observers & Validity
Demo of Portals, Timers (auto-close modal), Intersection Observer, and Form Validity.
Features Used
- Portal
- Timer
- Intersection Observer
- Form validity
- Conditional styling
How to Run
npm install @constela/core @constela/runtimenpx constela run advanced-features.jsonSource Code
json
{
"version": "1.0",
"state": {
"showModal": {
"type": "boolean",
"initial": false
},
"sectionVisible": {
"type": "boolean",
"initial": false
},
"email": {
"type": "string",
"initial": ""
},
"countdown": {
"type": "number",
"initial": 3
},
"timerId": {
"type": "number",
"initial": 0
},
"submitted": {
"type": "boolean",
"initial": false
}
},
"actions": [
{
"name": "openModal",
"steps": [
{
"do": "set",
"target": "showModal",
"value": {
"expr": "lit",
"value": true
}
},
{
"do": "set",
"target": "countdown",
"value": {
"expr": "lit",
"value": 3
}
},
{
"do": "interval",
"ms": {
"expr": "lit",
"value": 1000
},
"action": "tickCountdown",
"result": "timerId"
}
]
},
{
"name": "tickCountdown",
"steps": [
{
"do": "update",
"target": "countdown",
"operation": "decrement"
},
{
"do": "if",
"condition": {
"expr": "bin",
"op": "<=",
"left": {
"expr": "state",
"name": "countdown"
},
"right": {
"expr": "lit",
"value": 0
}
},
"then": [
{
"do": "set",
"target": "showModal",
"value": {
"expr": "lit",
"value": false
}
},
{
"do": "clearTimer",
"target": {
"expr": "state",
"name": "timerId"
}
}
]
}
]
},
{
"name": "closeModal",
"steps": [
{
"do": "clearTimer",
"target": {
"expr": "state",
"name": "timerId"
}
},
{
"do": "set",
"target": "showModal",
"value": {
"expr": "lit",
"value": false
}
}
]
},
{
"name": "onSectionVisible",
"steps": [
{
"do": "set",
"target": "sectionVisible",
"value": {
"expr": "lit",
"value": true
}
}
]
},
{
"name": "setEmail",
"steps": [
{
"do": "set",
"target": "email",
"value": {
"expr": "var",
"name": "value"
}
}
]
},
{
"name": "submit",
"steps": [
{
"do": "set",
"target": "submitted",
"value": {
"expr": "lit",
"value": true
}
},
{
"do": "delay",
"ms": {
"expr": "lit",
"value": 3000
},
"then": [
{
"do": "set",
"target": "submitted",
"value": {
"expr": "lit",
"value": false
}
}
]
}
]
}
],
"view": {
"kind": "element",
"tag": "div",
"props": {
"style": {
"expr": "lit",
"value": "font-family: system-ui, sans-serif; padding: 16px;"
}
},
"children": [
{
"kind": "element",
"tag": "h1",
"props": {
"style": {
"expr": "lit",
"value": "margin: 0 0 8px 0; font-size: 24px;"
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "lit",
"value": "Portals, Timers, Observers & Validity"
}
}
]
},
{
"kind": "element",
"tag": "p",
"props": {
"style": {
"expr": "lit",
"value": "color: #666; margin: 0 0 24px 0;"
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "lit",
"value": "Demonstrating Portals, Observers, and Form Validity."
}
}
]
},
{
"kind": "element",
"tag": "div",
"props": {
"style": {
"expr": "lit",
"value": "margin-bottom: 24px; padding: 16px; background: #f5f5f5; border-radius: 8px;"
}
},
"children": [
{
"kind": "element",
"tag": "h2",
"props": {
"style": {
"expr": "lit",
"value": "font-size: 18px; margin: 0 0 12px 0; color: #333;"
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "lit",
"value": "1. Portals & Timers"
}
}
]
},
{
"kind": "element",
"tag": "p",
"props": {
"style": {
"expr": "lit",
"value": "color: #666; margin: 0 0 12px 0; font-size: 14px;"
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "lit",
"value": "Render content outside the component tree. Modal auto-closes in 3 seconds."
}
}
]
},
{
"kind": "element",
"tag": "button",
"props": {
"style": {
"expr": "lit",
"value": "padding: 8px 16px; background: #0070f3; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;"
},
"onClick": {
"event": "click",
"action": "openModal"
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "lit",
"value": "Open Modal"
}
}
]
}
]
},
{
"kind": "element",
"tag": "div",
"props": {
"style": {
"expr": "cond",
"if": {
"expr": "state",
"name": "sectionVisible"
},
"then": {
"expr": "lit",
"value": "margin-bottom: 24px; padding: 16px; background: #e0f2fe; border-radius: 8px; transition: all 0.5s; transform: translateY(0); opacity: 1;"
},
"else": {
"expr": "lit",
"value": "margin-bottom: 24px; padding: 16px; background: #f5f5f5; border-radius: 8px; transition: all 0.5s; transform: translateY(20px); opacity: 0.5;"
}
},
"onIntersect": {
"event": "intersect",
"action": "onSectionVisible"
}
},
"children": [
{
"kind": "element",
"tag": "h2",
"props": {
"style": {
"expr": "lit",
"value": "font-size: 18px; margin: 0 0 12px 0; color: #333;"
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "lit",
"value": "2. Observers"
}
}
]
},
{
"kind": "element",
"tag": "p",
"props": {
"style": {
"expr": "lit",
"value": "color: #666; margin: 0; font-size: 14px;"
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "lit",
"value": "This section uses Intersection Observer. Status: "
}
},
{
"kind": "element",
"tag": "span",
"props": {
"style": {
"expr": "cond",
"if": {
"expr": "state",
"name": "sectionVisible"
},
"then": {
"expr": "lit",
"value": "font-weight: bold; color: #16a34a;"
},
"else": {
"expr": "lit",
"value": "font-weight: bold; color: #666;"
}
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "cond",
"if": {
"expr": "state",
"name": "sectionVisible"
},
"then": {
"expr": "lit",
"value": "Visible"
},
"else": {
"expr": "lit",
"value": "Not visible"
}
}
}
]
}
]
}
]
},
{
"kind": "element",
"tag": "div",
"props": {
"style": {
"expr": "lit",
"value": "margin-bottom: 24px; padding: 16px; background: #f5f5f5; border-radius: 8px;"
}
},
"children": [
{
"kind": "element",
"tag": "h2",
"props": {
"style": {
"expr": "lit",
"value": "font-size: 18px; margin: 0 0 12px 0; color: #333;"
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "lit",
"value": "3. Form Validity"
}
}
]
},
{
"kind": "element",
"tag": "p",
"props": {
"style": {
"expr": "lit",
"value": "color: #666; margin: 0 0 12px 0; font-size: 14px;"
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "lit",
"value": "Real-time form validation using validity expressions."
}
}
]
},
{
"kind": "element",
"tag": "div",
"props": {
"style": {
"expr": "lit",
"value": "display: flex; flex-direction: column; gap: 8px;"
}
},
"children": [
{
"kind": "element",
"tag": "input",
"ref": "emailInput",
"props": {
"type": {
"expr": "lit",
"value": "email"
},
"placeholder": {
"expr": "lit",
"value": "Enter your email"
},
"style": {
"expr": "lit",
"value": "padding: 8px 12px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; color: #333;"
},
"value": {
"expr": "state",
"name": "email"
},
"onInput": {
"event": "input",
"action": "setEmail",
"payload": {
"expr": "var",
"name": "value"
}
}
}
},
{
"kind": "if",
"condition": {
"expr": "bin",
"op": "&&",
"left": {
"expr": "state",
"name": "email"
},
"right": {
"expr": "not",
"operand": {
"expr": "validity",
"ref": "emailInput",
"property": "valid"
}
}
},
"then": {
"kind": "element",
"tag": "p",
"props": {
"style": {
"expr": "lit",
"value": "color: #dc2626; font-size: 12px; margin: 0;"
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "lit",
"value": "Please enter a valid email address"
}
}
]
}
},
{
"kind": "element",
"tag": "button",
"props": {
"style": {
"expr": "cond",
"if": {
"expr": "validity",
"ref": "emailInput",
"property": "valid"
},
"then": {
"expr": "lit",
"value": "padding: 8px 16px; background: #0070f3; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;"
},
"else": {
"expr": "lit",
"value": "padding: 8px 16px; background: #ccc; color: #666; border: none; border-radius: 4px; cursor: not-allowed; font-size: 14px;"
}
},
"disabled": {
"expr": "not",
"operand": {
"expr": "validity",
"ref": "emailInput",
"property": "valid"
}
},
"onClick": {
"event": "click",
"action": "submit"
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "lit",
"value": "Submit"
}
}
]
}
]
}
]
},
{
"kind": "if",
"condition": {
"expr": "state",
"name": "showModal"
},
"then": {
"kind": "portal",
"target": "body",
"children": [
{
"kind": "element",
"tag": "div",
"props": {
"style": {
"expr": "lit",
"value": "position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 1000;"
}
},
"children": [
{
"kind": "element",
"tag": "div",
"props": {
"style": {
"expr": "lit",
"value": "background: white; padding: 24px; border-radius: 8px; max-width: 400px; width: 90%;"
}
},
"children": [
{
"kind": "element",
"tag": "h3",
"props": {
"style": {
"expr": "lit",
"value": "margin: 0 0 16px 0; font-size: 18px; color: #333;"
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "lit",
"value": "Portal Modal"
}
}
]
},
{
"kind": "element",
"tag": "p",
"props": {
"style": {
"expr": "lit",
"value": "color: #666; margin: 0 0 16px 0;"
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "lit",
"value": "This modal is rendered via a Portal. Auto-closes in "
}
},
{
"kind": "text",
"value": {
"expr": "state",
"name": "countdown"
}
},
{
"kind": "text",
"value": {
"expr": "lit",
"value": " seconds."
}
}
]
},
{
"kind": "element",
"tag": "button",
"props": {
"style": {
"expr": "lit",
"value": "padding: 8px 16px; background: #0070f3; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;"
},
"onClick": {
"event": "click",
"action": "closeModal"
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "lit",
"value": "Close"
}
}
]
}
]
}
]
}
]
}
},
{
"kind": "if",
"condition": {
"expr": "state",
"name": "submitted"
},
"then": {
"kind": "element",
"tag": "div",
"props": {
"style": {
"expr": "lit",
"value": "position: fixed; bottom: 24px; right: 24px; background: #16a34a; color: white; padding: 16px 24px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); font-size: 14px; z-index: 1000;"
}
},
"children": [
{
"kind": "text",
"value": {
"expr": "lit",
"value": "Form submitted successfully!"
}
}
]
}
}
]
}
}