From 168bfb4a226970153b488a6a4de4e6286def3663 Mon Sep 17 00:00:00 2001 From: yzamir Date: Mon, 2 Oct 2023 14:08:03 +0300 Subject: [PATCH] Refactor led renderers Signed-off-by: yzamir --- public/index.html | 2 +- public/js/app.js | 2 +- public/js/components/index.js | 2 +- public/js/components/led-array.js | 55 +++++---- public/js/components/led-memory-table.js | 136 +++++++++++++++++++++++ public/js/components/led-table.js | 91 --------------- 6 files changed, 173 insertions(+), 115 deletions(-) create mode 100644 public/js/components/led-memory-table.js delete mode 100644 public/js/components/led-table.js diff --git a/public/index.html b/public/index.html index 0c48db4..1c2b22b 100644 --- a/public/index.html +++ b/public/index.html @@ -69,7 +69,7 @@

Memory

- + diff --git a/public/js/app.js b/public/js/app.js index 5469340..48e7b41 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -8,7 +8,7 @@ import compile from './src/compiler.mjs'; // Virtual machine const memorySize = 256; const vm = new VM(memorySize); -const runningStepTimeMs = 100; +const runningStepTimeMs = 50; let runningSteps = 0; let runningInterval; diff --git a/public/js/components/index.js b/public/js/components/index.js index 1c6af00..281ffc0 100644 --- a/public/js/components/index.js +++ b/public/js/components/index.js @@ -5,7 +5,7 @@ import './led-indicator.js'; import './led-switch.js'; import './led-array.js'; import './led-switch-array.js'; -import './led-table.js'; +import './led-memory-table.js'; import './led-register-table.js'; import './code-editor.js'; import './hex-value.js'; diff --git a/public/js/components/led-array.js b/public/js/components/led-array.js index b319b10..129beac 100644 --- a/public/js/components/led-array.js +++ b/public/js/components/led-array.js @@ -1,9 +1,12 @@ -import { html } from '../html.js'; - +/* eslint-disable no-underscore-dangle */ class LedArray extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); + this._value = null; + this._ledIndicators = []; + this._width = parseInt(this.getAttribute('width') || '8', 10); + this._color = this.getAttribute('color') || 'green'; } connectedCallback() { @@ -14,30 +17,40 @@ class LedArray extends HTMLElement { return ['value']; } - attributeChangedCallback(name) { - if (name === 'value') { + attributeChangedCallback(name, oldValue, newValue) { + if (name === 'value' && oldValue !== newValue) { + this._value = newValue; this.render(); } } render() { - const width = parseInt(this.getAttribute('width') || '8', 10); - const color = this.getAttribute('color') || 'green'; - const value = parseInt(this.getAttribute('value'), 10) || 0; - const binaryString = value.toString(2).padStart(width, '0'); - - this.shadowRoot.innerHTML = html` - -
- ${binaryString.split('').map((bit) => ``).join('')} -
- `; + const value = parseInt(this._value, 10) || 0; + const binaryString = value.toString(2).padStart(this._width, '0'); + + // Create the led-container if it doesn't exist + let ledContainer = this.shadowRoot.querySelector('.led-container'); + if (!ledContainer) { + ledContainer = document.createElement('div'); + ledContainer.className = 'led-container'; + this.shadowRoot.appendChild(ledContainer); + ledContainer.style.display = 'flex'; + ledContainer.style.alignItems = 'center'; + ledContainer.style.gap = '5px'; + } + + // Update or create led-indicator elements + binaryString.split('').forEach((bit, index) => { + let ledIndicator = this._ledIndicators[index]; + if (!ledIndicator) { + ledIndicator = document.createElement('led-indicator'); + this._ledIndicators[index] = ledIndicator; + + ledContainer.appendChild(ledIndicator); + ledIndicator.setAttribute('color', this._color); + } + ledIndicator.setAttribute('value', bit === '1'); + }); } } diff --git a/public/js/components/led-memory-table.js b/public/js/components/led-memory-table.js new file mode 100644 index 0000000..292c257 --- /dev/null +++ b/public/js/components/led-memory-table.js @@ -0,0 +1,136 @@ +/* eslint-disable class-methods-use-this */ +/* eslint-disable no-underscore-dangle */ +/* eslint-disable no-restricted-syntax */ +import { html } from '../html.js'; + +class LedMemoryTable extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: 'open' }); + this._indexesToRender = []; + + this._prevValueArray = null; + this._prevIndicatorArray = null; + + this._data = JSON.parse(this.getAttribute('data') || '[]'); + + this._tableElement = document.createElement('table'); + this.shadowRoot.appendChild(this._tableElement); + + this._style = document.createElement('style'); + this._style.innerHTML = html` + table { + border-collapse: collapse; + } + td { + padding: 2px 6px; + text-align: left; + font-weight: bold; + } + .address { + cursor: pointer; + } + .address:hover { + color: #f0f0f0; + } + .label { + color: #D4AF37; + } + .decimal { + color: #D3D3D3; + } + `; + this.shadowRoot.appendChild(this._style); + } + + buildArrays(data) { + return data.reduce((acc, item) => { + acc[0].push(item.value); + acc[1].push(item.indicator); + return acc; + }, [[], []]); + } + + connectedCallback() { + this.render(); + } + + static get observedAttributes() { + return ['data']; + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name === 'data' && oldValue !== newValue) { + const newData = JSON.parse(newValue || '[]'); + this._indexesToRender = this.getIndexesToRender(newData); + + if (this._indexesToRender.length > 0 || this._data.length !== newData.length) { + [this._prevValueArray, this._prevIndicatorArray] = this.buildArrays(newData); + this._data = newData; + + this.render(); + } + } + } + + getIndexesToRender(newData) { + const indexesToRender = []; + + if (!this._data) { + for (let i = 0; i < newData.length; i++) { + indexesToRender.push(i); + } + } else { + const newLength = newData.length; + const prevLength = this._data.length; + + for (let i = 0; i < newLength; i++) { + if (!this._prevIndicatorArray || newData[i].indicator !== this._prevIndicatorArray[i] || newData[i].value !== this._prevValueArray[i]) { + indexesToRender.push(i); + } + } + + // If the new data is shorter, add all remaining indexes from the old data + if (newLength < prevLength) { + for (let i = newLength; i < prevLength; i++) { + indexesToRender.push(i); + } + } + } + + return indexesToRender; + } + + render() { + const table = this._tableElement; + + for (const i of this._indexesToRender) { + const item = this._data[i]; + let row = table.rows[i]; + + if (!row) { + row = table.insertRow(); + } + + row.innerHTML = this.getRowStaticContent(item); + } + } + + getRowStaticContent(item) { + const address = `0x${item.address.toString(16).toUpperCase().padStart(2, '0')}`; + const valueHex = `0x${item.value.toString(16).toUpperCase().padStart(2, '0')}`; + const valueDecimal = `${item.value.toString(10).padStart(3, '0')}`; + + return html` + ${address} + + + ${valueHex} + ${item.label || ''} + ${item.opCode ? `${item.opCode}` : `${item.oprand || ''}`} + ${valueDecimal} + `; + } +} + +customElements.define('led-memory-table', LedMemoryTable); diff --git a/public/js/components/led-table.js b/public/js/components/led-table.js deleted file mode 100644 index 76e183f..0000000 --- a/public/js/components/led-table.js +++ /dev/null @@ -1,91 +0,0 @@ -/* eslint-disable no-restricted-syntax */ -import { html } from '../html.js'; - -class LedTable extends HTMLElement { - constructor() { - super(); - this.attachShadow({ mode: 'open' }); - this.handleClick = this.handleClick.bind(this); - } - - connectedCallback() { - this.render(); - } - - static get observedAttributes() { - return ['data']; - } - - attributeChangedCallback(name) { - if (name === 'data') { - this.render(); - } - } - - handleClick(event, value) { - event.stopPropagation(); - - const addressClickEvent = new CustomEvent('tableClick', { - detail: { value }, - bubbles: true, - composed: true, - }); - this.dispatchEvent(addressClickEvent); - } - - render() { - const data = JSON.parse(this.getAttribute('data') || '[]'); - let tableContent = ` - - - `; - - for (const item of data) { - const address = `0x${item.address.toString(16).toUpperCase().padStart(2, '0')}`; - const value = `0x${item.value.toString(16).toUpperCase().padStart(2, '0')}`; - const valueDecimal = `${item.value.toString(10).padStart(3, '0')}`; - - tableContent += html` - - - - - - - ${item.opCode ? `` : ``} - - - `; - } - - tableContent += '
${address}${value}${item.label || ''}${item.opCode}${item.oprand || ''}${valueDecimal}
'; - this.shadowRoot.innerHTML = tableContent; - - for (const item of data) { - this.shadowRoot.querySelector(`#address-${item.address}`).addEventListener('click', (event) => this.handleClick(event, item.address)); - } - } -} - -customElements.define('led-table', LedTable);