@plotinus/matrix-package-calculator
Version:
Calculator components for the Matrix framework - basic arithmetic operations with UI
316 lines (282 loc) • 12.3 kB
JavaScript
// Component source templates for the calculator package
export const componentSources = {
// Calculator component sources
calculatorCommunication: `class CalculatorComponent extends window.Matrix.BaseCommunicationComponent {
static dslTag = 'calculator';
static isMatrixComponent = true;
constructor(id, eventBus) {
super(id, eventBus);
this.calculatorState = {
display: '0',
currentValue: 0,
previousValue: null,
pendingOperation: null,
waitingForNewValue: false,
history: []
};
}
startExecution() {
console.log(\`Calculator \${this.id} started\`);
this.setState(this.calculatorState);
this.emitEvent('CalculatorReady', { calculatorId: this.id });
}
numberInputHandler(data) {
console.log(\`Calculator \${this.id}: Number input: \${data.digit}\`);
if (this.calculatorState.waitingForNewValue) {
this.calculatorState.display = data.digit.toString();
this.calculatorState.currentValue = data.digit;
this.calculatorState.waitingForNewValue = false;
} else {
if (this.calculatorState.display === '0') {
this.calculatorState.display = data.digit.toString();
} else {
this.calculatorState.display += data.digit.toString();
}
this.calculatorState.currentValue = parseFloat(this.calculatorState.display);
}
this.setState(this.calculatorState);
this.emitEvent('DisplayUpdated', {
calculatorId: this.id,
display: this.calculatorState.display,
value: this.calculatorState.currentValue
});
}
operationHandler(data) {
console.log(\`Calculator \${this.id}: Operation: \${data.operation}\`);
if (this.calculatorState.pendingOperation && !this.calculatorState.waitingForNewValue) {
// Complete pending operation first
this.performCalculation({
id: \`calc-\${Date.now()}\`,
operation: this.calculatorState.pendingOperation,
operand: this.calculatorState.currentValue,
timestamp: Date.now()
});
} else {
this.calculatorState.previousValue = this.calculatorState.currentValue;
}
this.calculatorState.pendingOperation = data.operation;
this.calculatorState.waitingForNewValue = true;
this.setState(this.calculatorState);
this.emitEvent('OperationSet', {
calculatorId: this.id,
operation: data.operation,
display: this.calculatorState.display
});
}
equalsHandler() {
console.log(\`Calculator \${this.id}: Equals pressed\`);
if (this.calculatorState.pendingOperation && this.calculatorState.previousValue !== null) {
this.performCalculation({
id: \`calc-\${Date.now()}\`,
operation: this.calculatorState.pendingOperation,
operand: this.calculatorState.currentValue,
timestamp: Date.now()
});
this.calculatorState.pendingOperation = null;
this.calculatorState.previousValue = null;
this.calculatorState.waitingForNewValue = true;
this.setState(this.calculatorState);
this.emitEvent('EqualsComplete', {
calculatorId: this.id,
result: this.calculatorState.display
});
}
}
clearHandler() {
console.log(\`Calculator \${this.id}: Clear pressed\`);
this.calculatorState = {
display: '0',
currentValue: 0,
previousValue: null,
pendingOperation: null,
waitingForNewValue: false,
history: [...this.calculatorState.history]
};
const clearCalculation = {
id: \`clear-\${Date.now()}\`,
operation: 'clear',
timestamp: Date.now()
};
this.calculatorState.history.push(clearCalculation);
this.setState(this.calculatorState);
this.emitEvent('CalculatorCleared', {
calculatorId: this.id,
display: this.calculatorState.display
});
}
performCalculation(calculation) {
let result = this.calculatorState.currentValue;
if (this.calculatorState.previousValue !== null && calculation.operand !== undefined) {
switch (calculation.operation) {
case 'add':
result = this.calculatorState.previousValue + calculation.operand;
break;
case 'subtract':
result = this.calculatorState.previousValue - calculation.operand;
break;
case 'multiply':
result = this.calculatorState.previousValue * calculation.operand;
break;
case 'divide':
if (calculation.operand === 0) {
this.calculatorState.display = 'Error';
this.emitEvent('CalculationError', {
calculatorId: this.id,
error: 'Division by zero',
calculation
});
return this.calculatorState.currentValue;
}
result = this.calculatorState.previousValue / calculation.operand;
break;
}
}
this.calculatorState.currentValue = result;
this.calculatorState.display = result.toString();
this.calculatorState.history.push(calculation);
return result;
}
}`,
calculatorPresentation: `class CalculatorPresentationElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
bindTo(commComponent) {
this.commComponent = commComponent;
// Subscribe to state changes
if (commComponent.eventBus) {
const stateKey = \`cmp:\${commComponent.id}:_stateChanged\`;
commComponent.eventBus.on(stateKey, () => this.render());
}
this.render();
}
render() {
const state = this.commComponent?.state || { display: '0' };
this.shadowRoot.innerHTML = \`
<style>
:host {
display: block;
font-family: 'Courier New', monospace;
}
.calculator {
border: 2px solid #ff6b35;
padding: 16px;
margin: 8px;
border-radius: 12px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
box-shadow: 0 8px 16px rgba(0,0,0,0.2);
max-width: 300px;
}
.display {
background: #000;
color: #00ff00;
font-size: 24px;
text-align: right;
padding: 12px;
margin-bottom: 16px;
border-radius: 6px;
border: 2px inset #333;
min-height: 40px;
display: flex;
align-items: center;
justify-content: flex-end;
}
.buttons {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
}
button {
background: linear-gradient(145deg, #f0f0f0, #d0d0d0);
border: 2px outset #ddd;
padding: 16px;
font-size: 18px;
font-weight: bold;
border-radius: 8px;
cursor: pointer;
transition: all 0.1s;
}
button:hover {
background: linear-gradient(145deg, #e0e0e0, #c0c0c0);
transform: translateY(-1px);
}
button:active {
border: 2px inset #ddd;
transform: translateY(0);
}
.operator {
background: linear-gradient(145deg, #ff6b35, #f4511e);
color: white;
}
.operator:hover {
background: linear-gradient(145deg, #f4511e, #e64a19);
}
.equals {
background: linear-gradient(145deg, #4caf50, #45a049);
color: white;
grid-column: span 2;
}
.equals:hover {
background: linear-gradient(145deg, #45a049, #3d8b40);
}
.clear {
background: linear-gradient(145deg, #f44336, #d32f2f);
color: white;
}
.clear:hover {
background: linear-gradient(145deg, #d32f2f, #c62828);
}
.zero {
grid-column: span 2;
}
h3 {
color: white;
text-align: center;
margin-top: 0;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
</style>
<div class="calculator">
<h3>Calculator: \${this.commComponent?.id}</h3>
<div class="display">\${state.display || '0'}</div>
<div class="buttons">
<button class="clear" onclick="this.getRootNode().host.clearCalculator()">C</button>
<button class="operator" onclick="this.getRootNode().host.inputOperation('divide')">÷</button>
<button class="operator" onclick="this.getRootNode().host.inputOperation('multiply')">×</button>
<button class="operator" onclick="this.getRootNode().host.inputOperation('subtract')">−</button>
<button onclick="this.getRootNode().host.inputNumber(7)">7</button>
<button onclick="this.getRootNode().host.inputNumber(8)">8</button>
<button onclick="this.getRootNode().host.inputNumber(9)">9</button>
<button class="operator" onclick="this.getRootNode().host.inputOperation('add')">+</button>
<button onclick="this.getRootNode().host.inputNumber(4)">4</button>
<button onclick="this.getRootNode().host.inputNumber(5)">5</button>
<button onclick="this.getRootNode().host.inputNumber(6)">6</button>
<button class="equals" onclick="this.getRootNode().host.calculateEquals()" style="grid-row: span 2;">=</button>
<button onclick="this.getRootNode().host.inputNumber(1)">1</button>
<button onclick="this.getRootNode().host.inputNumber(2)">2</button>
<button onclick="this.getRootNode().host.inputNumber(3)">3</button>
<button class="zero" onclick="this.getRootNode().host.inputNumber(0)">0</button>
<button onclick="this.getRootNode().host.inputNumber('.')">.</button>
</div>
</div>
\`;
}
inputNumber(digit) {
if (typeof digit === 'string' && digit === '.') {
console.log('Decimal point not yet implemented');
return;
}
this.commComponent?.numberInputHandler({ digit: Number(digit) });
}
inputOperation(operation) {
this.commComponent?.operationHandler({ operation });
}
calculateEquals() {
this.commComponent?.equalsHandler();
}
clearCalculator() {
this.commComponent?.clearHandler();
}
}`
};