bfa-dryer-design/static/hmi-blocks.js

278 lines
12 KiB
JavaScript
Raw Normal View History

/* BFA Banana Dryer — Custom HMI Blocks for GrapesJS */
function registerHMIBlocks(editor) {
const bm = editor.BlockManager;
const dm = editor.DomComponents;
// ── Shared styles ──
const cardBase = {
'display': 'flex', 'flex-direction': 'column', 'align-items': 'center', 'justify-content': 'center',
'gap': '6px', 'padding': '16px', 'border-radius': '10px', 'cursor': 'pointer',
'min-height': '120px', 'width': '48%', 'font-family': 'Segoe UI, system-ui, sans-serif',
'border': '1px solid #1e2a45', 'transition': 'background 0.15s', 'box-sizing': 'border-box'
};
// ══════════════════════════════════════════
// TEMPERATURE CARD
// ══════════════════════════════════════════
dm.addType('temp-card', {
model: {
defaults: {
tagName: 'div',
draggable: true,
droppable: false,
attributes: {'data-type': 'temp'},
traits: [
{type: 'text', name: 'label', label: 'Name', changeProp: true},
{type: 'color', name: 'temp_color', label: 'Temp Color', changeProp: true},
{type: 'number', name: 'setpoint', label: 'Setpoint (°C)', changeProp: true, min: 0, max: 200},
{type: 'text', name: 'linked_id', label: 'Linked Control ID', changeProp: true},
],
label: 'Heat Input',
temp_color: '#ff4444',
setpoint: 130,
linked_id: '',
components: [
{tagName: 'div', attributes: {'data-role': 'name'}, content: 'Heat Input',
style: {'font-size': '20px', 'color': '#7a8baa'}},
{tagName: 'div', attributes: {'data-role': 'value'}, content: '-- °C',
style: {'font-size': '36px', 'font-weight': '700', 'color': '#ff4444'}},
{tagName: 'div', attributes: {'data-role': 'bar'},
style: {'width': '80%', 'height': '6px', 'background': '#1a2240', 'border-radius': '3px', 'overflow': 'hidden'},
components: [{tagName: 'div', style: {'width': '50%', 'height': '100%', 'background': '#ff4444', 'border-radius': '3px'}}]},
{tagName: 'div', attributes: {'data-role': 'sp'}, content: 'SP: 130 °C',
style: {'font-size': '14px', 'color': '#4a5670'}},
],
style: {...cardBase, 'background': '#131a2b'},
}
}
});
bm.add('temp-card', {
label: '🌡 Temperature',
category: 'HMI Controls',
content: {type: 'temp-card'},
attributes: {class: 'gjs-fonts gjs-f-b1'}
});
// ══════════════════════════════════════════
// MOTOR CARD
// ══════════════════════════════════════════
dm.addType('motor-card', {
model: {
defaults: {
tagName: 'div',
draggable: true,
droppable: false,
attributes: {'data-type': 'motor'},
traits: [
{type: 'text', name: 'label', label: 'Name', changeProp: true},
{type: 'number', name: 'speed_sp', label: 'Speed SP (%)', changeProp: true, min: 0, max: 100, step: 5},
{type: 'color', name: 'active_color', label: 'Active Color', changeProp: true},
{type: 'color', name: 'inactive_color', label: 'Inactive Color', changeProp: true},
{type: 'select', name: 'animation', label: 'Animation', changeProp: true,
options: [{value:'none'},{value:'fan'},{value:'agitator'},{value:'conveyor'},{value:'pulse'},{value:'vibrate'},{value:'glow'},{value:'blink'},{value:'flow'}]},
{type: 'text', name: 'linked_id', label: 'Linked Control ID', changeProp: true},
],
label: 'Motor',
speed_sp: 50,
active_color: '#00c853',
inactive_color: '#ff1744',
animation: 'none',
linked_id: '',
components: [
{tagName: 'div', style: {'display': 'flex', 'gap': '10px', 'align-items': 'center'},
components: [
{tagName: 'span', attributes: {'data-role': 'name'}, content: 'Motor',
style: {'font-size': '24px', 'font-weight': '600', 'color': '#e8ecf4'}},
{tagName: 'span', attributes: {'data-role': 'speed'}, content: '50%',
style: {'font-size': '24px', 'color': '#7a8baa'}},
]},
{tagName: 'div', attributes: {'data-role': 'state'}, content: 'OFF',
style: {'font-size': '36px', 'font-weight': '700', 'color': '#e8ecf4'}},
],
style: {...cardBase, 'background': '#ff1744'},
}
}
});
bm.add('motor-card', {
label: '⚙ Motor / Speed',
category: 'HMI Controls',
content: {type: 'motor-card'},
});
// ══════════════════════════════════════════
// OUTPUT CARD
// ══════════════════════════════════════════
dm.addType('output-card', {
model: {
defaults: {
tagName: 'div',
draggable: true,
droppable: false,
attributes: {'data-type': 'output'},
traits: [
{type: 'text', name: 'label', label: 'Name', changeProp: true},
{type: 'color', name: 'active_color', label: 'Active Color', changeProp: true},
{type: 'color', name: 'inactive_color', label: 'Inactive Color', changeProp: true},
{type: 'text', name: 'linked_id', label: 'Linked Control ID', changeProp: true},
],
label: 'Output',
active_color: '#00c853',
inactive_color: '#ff1744',
linked_id: '',
components: [
{tagName: 'div', attributes: {'data-role': 'name'}, content: 'Output',
style: {'font-size': '20px', 'text-align': 'center', 'color': '#e8ecf4'}},
{tagName: 'div', attributes: {'data-role': 'state'}, content: 'OFF',
style: {'font-size': '28px', 'font-weight': '700', 'color': '#e8ecf4'}},
],
style: {...cardBase, 'background': '#ff1744'},
}
}
});
bm.add('output-card', {
label: '⚡ Output / Switch',
category: 'HMI Controls',
content: {type: 'output-card'},
});
// ══════════════════════════════════════════
// BURNER CARD
// ══════════════════════════════════════════
dm.addType('burner-card', {
model: {
defaults: {
tagName: 'div',
draggable: true,
droppable: false,
attributes: {'data-type': 'burner'},
traits: [
{type: 'text', name: 'label', label: 'Name', changeProp: true},
{type: 'color', name: 'active_color', label: 'Active Color', changeProp: true},
{type: 'color', name: 'inactive_color', label: 'Inactive Color', changeProp: true},
],
label: 'Burner',
active_color: '#ffab00',
inactive_color: '#ff1744',
components: [
{tagName: 'div', attributes: {'data-role': 'name'}, content: 'Burner',
style: {'font-size': '24px', 'color': '#e8ecf4'}},
{tagName: 'div', attributes: {'data-role': 'state'}, content: 'OFF',
style: {'font-size': '32px', 'font-weight': '700', 'color': '#e8ecf4'}},
],
style: {...cardBase, 'background': '#ff1744'},
}
}
});
bm.add('burner-card', {
label: '🔥 Burner',
category: 'HMI Controls',
content: {type: 'burner-card'},
});
// ══════════════════════════════════════════
// AUTOMATION CARD
// ══════════════════════════════════════════
dm.addType('automation-card', {
model: {
defaults: {
tagName: 'div',
draggable: true,
droppable: false,
attributes: {'data-type': 'automation'},
traits: [
{type: 'text', name: 'label', label: 'Name', changeProp: true},
{type: 'color', name: 'active_color', label: 'Active Color', changeProp: true},
{type: 'color', name: 'inactive_color', label: 'Inactive Color', changeProp: true},
],
label: 'Automation',
active_color: '#00c853',
inactive_color: '#ff1744',
components: [
{tagName: 'div', attributes: {'data-role': 'name'}, content: 'Automation',
style: {'font-size': '24px', 'font-weight': '700', 'color': '#e8ecf4'}},
{tagName: 'div', attributes: {'data-role': 'rules'}, content: 'Click to configure',
style: {'font-size': '12px', 'color': '#7a8baa'}},
],
style: {...cardBase, 'background': '#ff1744', 'width': '100%'},
}
}
});
bm.add('automation-card', {
label: '🤖 Automation',
category: 'HMI Controls',
content: {type: 'automation-card'},
});
// ══════════════════════════════════════════
// GAUGE (arc)
// ══════════════════════════════════════════
bm.add('gauge', {
label: '📊 Gauge',
category: 'HMI Controls',
content: `<div style="width:150px;height:150px;display:flex;align-items:center;justify-content:center;position:relative" data-type="gauge">
<svg viewBox="0 0 100 100" width="120" height="120">
<circle cx="50" cy="50" r="42" fill="none" stroke="#1a2240" stroke-width="6"/>
<circle cx="50" cy="50" r="42" fill="none" stroke="#2d7ff9" stroke-width="6"
stroke-dasharray="264" stroke-dashoffset="132" transform="rotate(-90 50 50)" stroke-linecap="round"/>
<text x="50" y="50" text-anchor="middle" dominant-baseline="central" fill="#e8ecf4" font-size="18" font-weight="700">50%</text>
</svg>
</div>`,
});
// ══════════════════════════════════════════
// LAYOUT HELPERS
// ══════════════════════════════════════════
bm.add('page-container', {
label: '📄 Page Container',
category: 'Layout',
content: `<div style="display:flex;flex-wrap:wrap;gap:8px;padding:8px;width:100%;height:100%;align-content:flex-start;background:#0a0e17" data-type="page">
</div>`,
});
bm.add('tab-bar', {
label: '📑 Tab Bar',
category: 'Layout',
content: `<div style="display:flex;height:50px;background:#0d1220;border-bottom:1px solid #1e2a45;width:100%">
<div style="flex:1;display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:600;color:#2d7ff9;border-bottom:3px solid #2d7ff9;cursor:pointer">PAGE 1</div>
<div style="flex:1;display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:600;color:#7a8baa;cursor:pointer">PAGE 2</div>
<div style="flex:1;display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:600;color:#7a8baa;cursor:pointer">PAGE 3</div>
</div>`,
});
bm.add('top-bar', {
label: '📌 Top Bar',
category: 'Layout',
content: `<div style="display:flex;align-items:center;justify-content:space-between;height:48px;padding:0 20px;background:#111827;border-bottom:1px solid #1e2a45;width:100%">
<div style="display:flex;align-items:center;gap:16px">
<span style="font-size:20px;font-weight:700;color:#e8ecf4">BFA BANANA DRYER</span>
<span style="font-size:14px;color:#7a8baa">SAE Engineering</span>
</div>
<span style="font-size:16px;font-weight:600;color:#ff1744">RS485 OFFLINE</span>
</div>`,
});
bm.add('text-label', {
label: '📝 Text Label',
category: 'Layout',
content: `<div style="font-size:18px;color:#e8ecf4;padding:8px;text-align:center">Label Text</div>`,
});
bm.add('divider', {
label: ' Divider',
category: 'Layout',
content: `<div style="width:100%;height:1px;background:#1e2a45;margin:8px 0"></div>`,
});
bm.add('spacer', {
label: '⬜ Spacer',
category: 'Layout',
content: `<div style="width:100%;height:20px"></div>`,
});
}