Why is {object promise} popping up when a function returns a number? I need it to return data


I am not sure why “[object promise]” pops up in this case. I have tried to get help but can’t figure it out.

let inputFile = convertToFile() + ' <-- Data';

And then here is the function.

async function convertToFile() {
    const fileInput = document.getElementById('filedata');
    const file = fileInput.files[0];
    const reader = new FileReader();

    return new Promise((resolve, reject) => {
        reader.onload = function(event) {
            const buffer = event.target.result;
            const hexString = Array.from(new Uint8Array(buffer))
                .map(byte => byte.toString(16).padStart(2, '0'))
                .join('');
            resolve(hexString);
            console.log(hexString);
        };

        reader.onerror = function() {
            reject(new Error('Error reading file'));
        };

        if (file) {
            reader.readAsArrayBuffer(file);
        } else {
            reject(new Error('No file selected'));
        }
    });
}

I am not sure why this doesn’t work but it should return the text. Please help I am desperate.

I tried to replace it with another function and it worked fine. The problem is it returns “[object promise]”

Furthermore, I have also tried “await”

let inputFile = await convertToFile() + ' <-- Data';

Why my input is not reseting? {react-router-dom}

I’m currently working on getting my reset button to effectively reset the search functionality. While the button itself is working as intended, I’m encountering an issue where the text input field doesn’t reset along with it. Strangely, both “query,” “searchText,” and “inputValue” seem to retain their values, and I’m struggling to find a solution to reset them as well. Any assistance on resolving this would be greatly appreciated. Thanks!

//This is my reset function
const onResetSearch = () => {
    event.preventDefault();
   setBusco(false);
   setSearchParams({});
   console.log(query, searchText, inputValue);
   onResetForm();
   navigate("/search")}

const [searchParams, setSearchParams] = useSearchParams(); //esto se hace asi nomas
const query = useMemo(() => searchParams.get("q"), [searchParams.get("q")]); 
const heroes = useMemo(() => (query ? getHeroesByName(query) : []), [query]); 
const { searchText, onInputChange, onResetForm } = useForm({searchText: query || ""});

const inputValue = searchText.trim();
const onSearchSubmit = (event) => {
event.preventDefault();
if (inputValue.length < 1 || !inputValue) return;
setSearchParams({ q: inputValue });};

<button onClick={onResetSearch}}
className="btn btn-outline-danger mt-2">Reset</button>

When I search something (https://i.stack.imgur.com/JQCAr.png)
When I press the reset button (https://i.stack.imgur.com/uCxpt.jpg)
You can see that superman is still there in the input

horizontal slide button does not work with grid display

I’m working on a website, and want a horizontal carrousel function. I’m using the grid display, and after many tutorials I found a suitable way to implement this. For some reason this js does not work well with the grid display. I’m a novice and do not now much about java script.

As I mentioned, I played around with different ways to horizontal scroll. I realized that many of these implementations does not really work with grid, but I don’t want to rewrite my entire code for this.

Rotate the child elements when the parent element is being rotated

I am trying to rotate the parent element along with all the child elements.

Few things to note:-

  • All the child elements are not appended in the parent element. However, their positions are in relative to parent element.
  • Their distances and angles should stay the same from the parent left and top position (x,y) while being rotated. So, if the parent element is rotated to +90° (clockwise), all the child elements should behave as if they were appended inside the parent element. i.e., they should get rotated to +90°. (see example below)

Initial Position

Initial Position

90° Rotated Position

Rotated to 90° clockwise

I have looked at other multiple questions and answers. They do describe this scenario but I’m not able to figure out what is the main issue in my code. This is the code I have.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Rotate and Move Elements</title>
    <style>
      body {
        margin: 0;
        height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
        background-color: #f0f0f0;
      }

      #parentElement {
        position: absolute;
        width: 200px;
        height: 200px;
        background-color: #3498db;
        top: 231px;
        left: 960px;
      }

      .childElement {
        position: absolute;
        width: 20px;
        height: 20px;
        background-color: #e74c3c;
        transform-origin: center center;
      }
    </style>
  </head>
  <body>
    <div id="parentElement"></div>
    <div id="0" class="childElement" style="top: 200px; left: 900px"></div>
    <div id="1" class="childElement" style="top: 250px; left: 1000px"></div>
    <div id="2" class="childElement" style="top: 300px; left: 1050px"></div>
    <div id="3" class="childElement" style="top: 350px; left: 1100px"></div>

    <script>
      document.addEventListener("DOMContentLoaded", function () {
        const parentElement = document.getElementById("parentElement");
        const childElements = document.querySelectorAll(".childElement");

        let isDragging = false;
        let initialMouseAngle = 0;
        let initialRotation = 0;
        let initialDistance = {};

        function getMouseAngle(e) {
          const rect = parentElement.getBoundingClientRect();
          const centerX = rect.left + rect.width / 2;
          const centerY = rect.top + rect.height / 2;
          const mouseX = e.clientX - centerX;
          const mouseY = e.clientY - centerY;
          return Math.atan2(mouseY, mouseX) * (180 / Math.PI);
        }

        function getRotationDegrees() {
          const transformValue = window.getComputedStyle(parentElement).getPropertyValue("transform");
          const matrix = new DOMMatrixReadOnly(transformValue);
          return Math.atan2(matrix.b, matrix.a) * (180 / Math.PI);
        }

        function startDrag(e) {
          isDragging = true;
          initialMouseAngle = getMouseAngle(e);
          initialRotation = getRotationDegrees();
          childElements.forEach(child => {
            const parentRect = parentElement.getBoundingClientRect();
            const childRect = child.getBoundingClientRect();
            initialDistance[child.id] = Math.hypot(childRect.left - parentRect.left, childRect.top - parentRect.top);
          });
          console.log(initialDistance);
        }
        function handleDrag(e) {
          if (isDragging) {
            const currentMouseAngle = getMouseAngle(e);
            const rotationChange = currentMouseAngle - initialMouseAngle;
            const newRotation = initialRotation + rotationChange;
            const parentRect = parentElement.getBoundingClientRect();
            const angleInRadians = newRotation * (Math.PI / 180);
            parentElement.style.transform = `rotate(${newRotation}deg)`;

            childElements.forEach(child => {
              const distance = initialDistance[child.id];
              const newX = parentRect.left + distance * Math.cos(angleInRadians);
              const newY = parentRect.top + distance * Math.sin(angleInRadians);
              console.log(newX + newY);
              child.style.left = `${newX}px`;
              child.style.top = `${newY}px`;
              child.style.transform = `rotate(${newRotation}deg)`;

              // console.log("newX", newX, "newY", newY, distance);
            });
          }
        }

        function stopDrag() {
          isDragging = false;
        }

        document.addEventListener("mousedown", startDrag);
        document.addEventListener("mouseup", stopDrag);
        document.addEventListener("mousemove", handleDrag);
      });
    </script>
  </body>
</html>

I cant make the canvas background transparent, I dont know whether its bcz of canvas or the globe or particles… See the below js and picture

Here is my HTML, CSS, JAVASCRIPT (THREE.JS) code.
Can you please analyze the code and check for my desired output that i put in my title.

( I don’t know what to write of 220 characters minimum so i typing this, please neglect this braced content)

//**HTMLCODE**

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <link rel="stylesheet" href="/src/index.css" />
    <script type="importmap">
      {
        "imports": {
          "three": "https://unpkg.com/[email protected]/build/three.module.js",
          "three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
        }
      }
    </script>
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>EXAMPLE</title>
  </head>
  <body>
    <div id="root"></div>
    <div class="globe-render" id="globe-render">
      <script type="module" src="./globe.js"></script>
    </div>
  </body>
</html>

**//CSS CODE:**

@tailwind base;
@tailwind components;
@tailwind utilities;
body {
  background-color: #000f14;
  margin: 0;
  position: relative;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
    "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
    monospace;
}

#globe-render {
  position: absolute;
  top: -20%;
  left: 20%;
  background-color: rgb(255, 255, 255);
}
#myCanvas {
  position: absolute;
  background-color: green;
}


//JS CODE
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { UnrealBloomPass } from "three/addons/postprocessing/UnrealBloomPass.js";

//Radius define
let ParticleSurfaceLayer = 7.5;
let GlobeRadius = 6.6;

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
const renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
const container = document.querySelector(".globe-render");
renderer.setClearColor(0x000000,0);
container.appendChild(renderer.domElement);


// Group for globe and particle system
const group = new THREE.Group();
scene.add(group);

// Bloom pass for the globe
const renderScene = new RenderPass(scene, camera);
const globeBloomPass = new UnrealBloomPass(
  new THREE.Vector2(window.innerWidth, window.innerHeight),
  1.5,
  0.4,
  0.85
);
globeBloomPass.threshold = 0;
globeBloomPass.strength = 2;
globeBloomPass.radius = 0;

const globeComposer = new EffectComposer(renderer);
globeComposer.setSize(window.innerWidth, window.innerHeight);
globeComposer.addPass(renderScene);
globeComposer.addPass(globeBloomPass);

// Bloom pass for particles
const particleComposer = new EffectComposer(renderer);
particleComposer.setSize(window.innerWidth, window.innerHeight);
particleComposer.addPass(new RenderPass(group, camera)); // Assuming 'group' contains particles
const particleBloomPass = new UnrealBloomPass(
  new THREE.Vector2(window.innerWidth, window.innerHeight),
  1.5,
  0.4,
  0.85
);

particleBloomPass.threshold = 0;
particleBloomPass.strength = 1.3;
particleBloomPass.radius = 0.8;
particleComposer.addPass(particleBloomPass);

// Update size function
function updateSize() {
  const newWidth = window.innerWidth;
  const newHeight = window.innerHeight;

  camera.aspect = newWidth / newHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(newWidth, newHeight);

  const sphereRadius = Math.min(newWidth, newHeight) * 0.1;
  globe.geometry = new THREE.SphereGeometry(sphereRadius, 32, 32);

  const particleSizeMin = Math.min(newWidth, newHeight) * 0.001;
  const particleSizeMax = Math.min(newWidth, newHeight) * 0.004;

  group.children.forEach((particle) => {
    const randomSize = THREE.MathUtils.randFloat(
      particleSizeMin,
      particleSizeMax
    );
    particle.scale.set(randomSize, randomSize, randomSize);
  });

  // Update composer sizes
  globeComposer.setSize(newWidth, newHeight);
  particleComposer.setSize(newWidth, newHeight);
}

//orbit controls
const controls = new OrbitControls(camera, renderer.domElement);
const loader = new THREE.TextureLoader();
controls.enableZoom = false;
// controls.enabled=false

// Initial setup
////////
const geometry = new THREE.SphereGeometry(GlobeRadius, 80, 80);
const material1 = new THREE.MeshBasicMaterial({
  map: loader.load("./8k_earth_nightmap_underlayer.jpg"),
  //transparent: true,
  opacity: 1,
});
const material2 = new THREE.MeshBasicMaterial({
  color: 0xff047e,
  transparent: true,
  opacity: 0.1,
});
const multimaterial = [material1, material2];
const globe = new THREE.Mesh(geometry, material1);
globe.layers.set(1);
group.add(globe);

// Particle System
const particleCount = 600;
const color = new THREE.Color("#fc2414");

for (let i = 0; i < particleCount; i++) {
  // ... (same as your code)

  const phi = Math.random() * Math.PI * 2;
  const theta = Math.random() * Math.PI - Math.PI / 2;
  const randomradius = 0.01 + Math.random() * 0.04;

  const radius = ParticleSurfaceLayer; // Radius of the sphere

  const x = radius * Math.cos(theta) * Math.cos(phi);
  const y = radius * Math.cos(theta) * Math.sin(phi);
  const z = radius * Math.sin(theta);

  const particleGeometry = new THREE.SphereGeometry(randomradius, 30, 25); // Initial size
  const particleMaterial = new THREE.MeshBasicMaterial({
    color: "#00FFFF",
  });
  const particle = new THREE.Mesh(particleGeometry, particleMaterial);

  particle.position.set(x, y, z);
  particle.layers.set(1);
  group.add(particle);
}

const ambientLight = new THREE.AmbientLight(0xffffff, 100);
scene.add(ambientLight);

// Camera position
camera.position.z = 15;

// Rotation animation
const rotationSpeed = 0.001;

// Animation function
const animate = function () {
  requestAnimationFrame(animate);

  group.rotation.y += rotationSpeed;

  // Render globe with bloom effect
  camera.layers.set(1);
  globeComposer.render();

  // Render particles with bloom effect
  particleComposer.render();
};
///////orbit controls///*
let isDragging = false;
let originalRotation = group.rotation.y;

// Event listener for mouse down
renderer.domElement.addEventListener("mousedown", () => {
  isDragging = true;
});

// Event listener for mouse up
renderer.domElement.addEventListener("mouseup", () => {
  isDragging = false;
  // Reset the rotation to its original position
  group.rotation.y = originalRotation;
});

// Event listener for mouse leave (in case mouse leaves the canvas while dragging)
renderer.domElement.addEventListener("mouseleave", () => {
  if (isDragging) {
    isDragging = false;
    // Reset the rotation to its original position
    group.rotation.y = originalRotation;
  }
});

// Handle window resize
window.addEventListener("resize", updateSize);

// Start animation
animate();

const canvas = document.querySelector("canvas");
canvas.id = "myCanvas";
canvas.classList.add("myCanvasClass");

Screen Shot of my rendered page

I tried setClearColor in js, transparent in the css block for canvas.
I want the canvas convert from black background to transparent. Please some one help this.

DragulaJs – Drag from a specific button only

I have a div that has several cards inside them, I want to use dragula to allow setting the order of the cards by drag and drop. However I only want to allow dragging from a specific button only.

I tried setting the button as the container but that only moves the button. If I set the card as the container, the whole card is draggable from anywhere.

HTML

<div id="container">
    <div class="card">
        <figure>
            <a href="scent/giorgio-armani-acqua-di-gio"><img src="img/perfumes/armani-acqua-di-gio.jpg"></a>
            <button type="button" class="btn icon drag-handle"><span class="material-icons">drag_indicator</span></button>
        </figure>
        <div class="content">
            <p>Acqua Di Gio</p>
            <button type="button" class="btn text remove-slot">Remove</button>
        </div>
    </div>
    ...
    ...
</div>  

JS

const dragulaV = dragula({
  revertOnSpill: true
});
document.querySelectorAll('#container .drag-handle').forEach(el => {
    dragulaV.containers.push(el);
});

screenshot

To summarize: I want to move .card and I only want to trigger it with .drag-handle.

Infinite recursion in fluid physics algorithm – why and how to fix it?

In attempting to design an algorithm for simulating fluid spread and dissipation, I have encountered a problem caused by the branching recursive function spreadAboutCenter (see MCVE) entering an infinite cycle.

I had thought I’d covered every base case, but evidently not. Testing the original version of the script in my Minecraft Bedrock addon caused an Event 41 unexpected restart. After that, I added the depth <= 4 constraint to the ‘fluid drying up’ functions to prevent future crashes, and revised the quaternary part of the recursive function to include a stricter set of conditions.

Specifically, when determining whether to perform the recursive step in each of the 4 directions, there is now an extra check to ensure the next block will be further away from any nearby fluid source blocks than is the current block. This should prevent the recursive call to the adjacent block from triggering a circular recursive call back to the original block, as they can’t both be further away.

// Recursive calls on the 4 horizontally adjacent blocks
// Only proceed in a given direction if either no nearby sources were found, or else the adjacent block in that direction will be further from any/all sources than mB is (along either the x or the z axis).
// Not being able to move back in the direction of a source should prevent an infinite cycle of recursive calls from being generated (which seems to have been the problem with the previous version of this algorithm).
for (let i = 0; i < adjacents.length; i++) {
    if (adjacents[i] !== undefined) {
        let adjName = adjacents[i].cell.textContent;
        if (adjName.includes("m") && (!sourceCoords || isMoreDistantFromAll(adjacents[i], mB, sourceCoords))) {
            setTimeout(function() { return spreadAboutCenter(adjacents[i], x, z, recursionDepth + 1); }, miasmaSpreadInterval);
        }
    }
}

But, something is still causing unexpectedly high numbers of recursive calls to be generated in certain situations. (Now, this will only trigger the depth limit condition and print the “OVERFLOW” message.) I know that the problem must originate in the above for loop, because adding the line console.log("Depth " + recursionDepth); inside the loop causes Depth 5 to be printed before the overflow message is displayed.

I’ve looked over each base case multiple times and still can’t see what it is I’m missing.
Any help identifying the exact cause and how to fix it is greatly appreciated.


Background: The code in the snippet below is supposed to recreate Minecraft’s fluid spreading and dissipation physics: it is intended to be used with a custom fluid, called ‘miasma,’ which I’m planning on including as part of a Bedrock Edition Addon. The script will go in main.js. Miasma is a glowing purple liquid with a viscosity in between that of water and lava, which can be placed and picked up with a bucket.

  • When a miasma source block is placed, using a full bucket, it will branch out into a descending “+” shape if it is not above a solid surface, and if it is above solid blocks, it will spread out in increasingly shallower levels around the source, until it reaches the maximum spread level of 3.

  • When a miasma source block is picked back up using an empty bucket, the source gets replaced with air, and, starting from the newly empty center, each ring of surrounding miasma blocks gets shallower by 1 spread level until all have evaporated (i.e. reached spread level 3 and then been replaced by air).

  • Non-source miasma blocks cannot be picked up.

You can easily observe one of the problematic cases (causing the “OVERFLOW” message to be logged) by recreating the configuration in the image below (you’ll only need the second layer, which is the top right quadrant), then picking up the “m0” block next to the cursor with an empty bucket:
Problematic Configuration Image

var tDiv = document.getElementById("tableDiv");
var tbl = document.createElement("table");
tbl.id = "chunk";
// References: https://stackoverflow.com/questions/8302166/dynamic-creation-of-table-with-dom and https://stackoverflow.com/questions/3319683/add-id-to-dynamically-created-div
var row;
var tCell;
var innerTbl;
var row2;
var tCell2;
for (var r = 0; r < 2; r++) {
  row = document.createElement("tr");
  tbl.appendChild(row);
  for (var c = 0; c < 2; c++) {
    tCell = document.createElement("td");
    innerTbl = document.createElement("table");
    tCell.appendChild(innerTbl);
    row.appendChild(tCell);
    for (var r2 = 0; r2 < 16; r2++) {
      row2 = document.createElement("tr");
      innerTbl.appendChild(row2);
      for (var c2 = 0; c2 < 16; c2++) {
        tCell2 = document.createElement("td");
        if (r + c === 0) {
          tCell2.textContent = "S";
        } else {
          tCell2.textContent = "A";
        }
        row2.appendChild(tCell2);
      }
    }
  }
}
tDiv.appendChild(tbl);

const bounds = {
  vMin: -1,
  vMax: 4,
  hMin: -1,
  hMax: 16
}; // Values are exclusive: valid block coordinates must be > min and < max
const miasmaSpreadInterval = 1000; // 1000 ms = 1 second
const testArea = document.getElementById("chunk");

const layers = [
  testArea.children.item(0).children.item(0).children.item(0),
  testArea.children.item(0).children.item(1).children.item(0),
  testArea.children.item(1).children.item(0).children.item(0),
  testArea.children.item(1).children.item(1).children.item(0)
];
var blocks = [];
var Block = function(x, y, z, cell) {
  this.x = x;
  this.y = y;
  this.z = z;
  this.cell = cell;
};
for (let y = 0; y < layers.length; y++) { // Can't use 'var' here, because that would assign all array elements based on the *last* values of x, y and z
  blocks[y] = [];
  for (let x = 0; x < 16; x++) {
    blocks[y][x] = [];
    for (let z = 0; z < 16; z++) {
      blocks[y][x][z] = new Block(x, y, z, layers[y].children.item(x).children.item(z));
      blocks[y][x][z].cell.addEventListener("click", function() {
        playerInteractWithBlock(blocks[y][x][z]);
      });
    }
  }
}

function getBlockAbove(b) {
  if (b.y + 1 < bounds.vMax) {
    return blocks[b.y + 1][b.x][b.z];
  }
}

function getBlockBelow(b) {
  if (b.y - 1 > bounds.vMin) {
    return blocks[b.y - 1][b.x][b.z];
  }
}

function getBlockNorthOf(b) {
  if (b.x - 1 > bounds.hMin) {
    return blocks[b.y][b.x - 1][b.z];
  }
}

function getBlockSouthOf(b) {
  if (b.x + 1 < bounds.hMax) {
    return blocks[b.y][b.x + 1][b.z];
  }
}

function getBlockEastOf(b) {
  if (b.z + 1 < bounds.hMax) {
    return blocks[b.y][b.x][b.z + 1];
  }
}

function getBlockWestOf(b) {
  if (b.z - 1 > bounds.hMin) {
    return blocks[b.y][b.x][b.z - 1];
  }
}

// selectedItem can be "full bucket" or "empty bucket"
var player = {
  selectedItem: "full bucket",
  miasmaBucketCooldown: false
};
var selectedLabel = document.getElementById("selected");
document.getElementById("full").addEventListener("click", function() {
  player.selectedItem = "full bucket";
  selected.textContent = "Full Bucket";
});
document.getElementById("empty").addEventListener("click", function() {
  player.selectedItem = "empty bucket";
  selected.textContent = "Empty Bucket";
});

/* Miasma Block Variants: m0 (source), m1 (spread level 1), m2 (spread level 2), m3 (spread level 3), mv (vertically flowing), m0v (vertically flowing source) */
/* Other Possible Blocks: S (solid) and A (air) */

// Miasma flowing, spreading, and source-forming mechanics
function spreadMiasma(mB) {
  const mBName = mB.cell.textContent;
  if (!mBName.includes("m")) { // It's not a miasma block, and so can't spread
    return;
  } else {
    const adjacents = [getBlockNorthOf(mB), getBlockSouthOf(mB), getBlockEastOf(mB), getBlockWestOf(mB)];
    const belowB = getBlockBelow(mB);
    var belowBName;
    if (belowB !== undefined) {
      belowBName = belowB.cell.textContent;
    }
    // VERTICAL FLOW (from miasma block of any numbered depth, or vertically flowing)
    // Replace air or other miasma block variants with the vertically flowing miasma block
    if (belowBName !== undefined && (belowBName === "A" || (belowBName.includes("m") && !belowBName.includes("v"))) && (mB.y - 1) > bounds.vMin) {
      if (belowBName === "m0") {
        // Convert source block to vertically flowing source block
        belowB.cell.textContent = "m0v";
      } else {
        belowB.cell.textContent = "mv";
      }
      setTimeout(function() {
        return spreadMiasma(belowB);
      }, miasmaSpreadInterval); // Recursive call for newly placed vertical block under current block
    }

    // Vertically flowing miasma won't spread horizontally unless it is directly above a non-air, non-miasma block
    if (mBName.includes("v")) {
      if (belowBName !== undefined && belowBName !== "A" && !belowBName.includes("m")) {
        // LET, not VAR! Function-scoped variables and closures don't get along well :D
        for (let i = 0; i < adjacents.length; i++) {
          if (adjacents[i] !== undefined) {
            let adjName = adjacents[i].cell.textContent;
            // If the flowing miasma is above anything other than air or vertically flowing miasma, spread lvl 1 miasma to each horizontally adjacent air or higher-spread-level miasma block
            if (adjName === "A" || (adjName.includes("m") && Number(adjName[adjName.length - 1]) > 1)) {
              adjacents[i].cell.textContent = "m1";
              setTimeout(function() {
                return spreadMiasma(adjacents[i]);
              }, miasmaSpreadInterval);
            }
          }
        }
      }
    } else {
      const aboveB = getBlockAbove(mB);
      var aboveBName;
      if (aboveB !== undefined) {
        aboveBName = aboveB.cell.textContent;
      }
      // SOURCE BLOCK CONVERSION
      // Any miasma block adjacent (including vertically) to two or more source blocks is converted to a source block (= m0 or m0v)
      var adjacentSourceBlocks = ((aboveBName === "m0" || aboveBName === "m0v") && (belowBName === "m0" || belowBName === "m0v")) ? 2 : (((aboveBName === "m0" || aboveBName === "m0v") || (belowBName === "m0" || belowBName === "m0v")) ? 1 : 0);
      for (let i = 0; i < adjacents.length; i++) {
        if (adjacents[i] !== undefined) {
          let adjName = adjacents[i].cell.textContent;
          if (adjName === "m0" || adjName === "m0v") {
            adjacentSourceBlocks++;
          }
        }
      }
      if (adjacentSourceBlocks >= 2) {
        if (aboveBName !== undefined && aboveBName.includes("m")) {
          mB.cell.textContent = "m0v";
        } else {
          mB.cell.textContent = "m0";
        }
      }

      // HORIZONTAL SPREADING
      // Shallowest depth possible is "end:miasma_3" block
      const lvl = Number(mBName[mBName.length - 1]);
      // Convert source block to vertically flowing source block if there is another miasma block above it
      if (lvl === 0) {
        if (aboveBName !== undefined && aboveBName.includes("m")) {
          mB.cell.textContent = "m0v";
        }
      }
      if (lvl < 3) {
        // LET, not VAR! (Same reason as above)
        for (let i = 0; i < adjacents.length; i++) {
          if (adjacents[i] !== undefined && getBlockBelow(adjacents[i]) !== undefined) {
            let adjName = adjacents[i].cell.textContent;
            let belowAdjName = getBlockBelow(adjacents[i]).cell.textContent;
            // Only source blocks will spread horizontally in air, to prevent a giant cone from forming if every level could spread.
            // Why test for >= instead of just >, when this means blocks will be re-set to the same thing? Because otherwise, blocks adjacent to placed sources won't be evaluated to be converted to new source blocks.
            if (((belowAdjName !== undefined && belowAdjName !== "A" && !belowAdjName.includes("m")) || lvl === 0) && (adjName === "A" || (adjName.includes("m") && !adjName.includes("v") && Number(adjName[adjName.length - 1]) >= (lvl + 1)))) {
              adjacents[i].cell.textContent = "m" + (lvl + 1);
              setTimeout(function() {
                return spreadMiasma(adjacents[i]);
              }, miasmaSpreadInterval);
            }
          }
        }
      }
    }
  }
}


// Prevent excessive resource demands in case of an unexpectedly high number of recursive calls being generated
var numberOfRecursiveCalls = 0;
var maxDepth = 0; // Only increment this for horizontal spreading, not vertical, because recursion in the vertical direction is linear and not branching
var overflowed = false; // If this becomes true, the spreadAboutCenter function will exit early.

// DEBUGGING: remove this later! --------------------------------------------------------------------------
// It's OK to modify and access these global variables from different threads, because JS is guaranteed not to have simultaneous-access issues.
function progressReport() {
  console.log("# of total recursive calls: " + numberOfRecursiveCalls);
  console.log("Maximum depth reached: " + maxDepth);
  if (overflowed) {
    console.log("OVERFLOW");
  }
}
setInterval(progressReport, 3000); // Every 3 seconds
// --------------------------------------------------------------------------------------------------------

// Return type of this function will vary.
// If any sources/vertical blocks are located within the specified distance, it will return an array of objects containing those blocks' x and z coordinates. (This evaluates to *true* in conditional statements.)
// Otherwise, it will return false.
function isSourceOrVerticalWithin4Blocks(x, y, z) {
  var coords = [];
  for (var xAway = -3; xAway < 4; xAway++) {
    for (var zAway = -3; zAway < 4; zAway++) {
      if ((Math.abs(xAway) + Math.abs(zAway) < 4) && ((x + xAway) > bounds.hMin && (z + zAway) > bounds.hMin && (x + xAway) < bounds.hMax && (z + zAway) < bounds.hMax)) {
        const bName = blocks[y][x + xAway][z + zAway].cell.textContent;
        if (bName.includes("m0") || bName === "mv") {
          coords.push({
            x: x + xAway,
            z: z + zAway
          });
        }
      }
    }
  }
  return (coords.length) ? coords : false;
}

// 'next' and 'current' should be Block object references; 'comparisons' should be an array of objects of the form {x: Number, z: Number}
// Returns true if the distance of the x- and z-coordinates provided for 'next' is greater than that for 'current' from all the pairs of coordinates in the array, and false otherwise.
function isMoreDistantFromAll(next, current, comparisons) {
  var compared = {};
  for (var i = 0; i < comparisons.length; i++) {
    // If 'next' is further than 'current' from the source in a given direction, the value for that direction will be *positive*. If it is an equal distance away, the value will be *0*. If it is closer, the value will be *negative*.
    compared.x = Math.abs(next.x - comparisons[i].x) - Math.abs(current.x - comparisons[i].x);
    compared.z = Math.abs(next.z - comparisons[i].z) - Math.abs(current.z - comparisons[i].z);
    // If 'next' is closer than 'current' to the source *in either direction*, OR 'next' is *not* further than 'current' in *at least one* direction, return false
    if ((compared.x < 0) || (compared.z < 0) || (compared.x + compared.z === 0)) {
      return false;
    }
  }
  return true;
}

// Starting at the lowest-spread-level (i.e. greatest depth) miasma block in each puddle, decrease its level by 1 increment per second, and do the same for each miasma block horizontally adjacent to it after a 1-second delay.
// Only dissipate numbered miasma blocks which remain disconnected from any source or vertical miasma blocks.
function spreadAboutCenter(mB, x, z, recursionDepth) {
  numberOfRecursiveCalls++;
  var xDist = Math.abs(mB.x - x);
  var zDist = Math.abs(mB.z - z);
  if (recursionDepth > maxDepth) {
    maxDepth = recursionDepth;
  }
  if (recursionDepth > 4) {
    overflowed = true;
    return;
  } else if (xDist < 3 && zDist < 3 && (xDist + zDist < 3)) { // Dist is one less than it would be for a source (so, 3 instead of 4), because the minimum spread level of the center blocks for this function is 1, not 0
    var blockName = mB.cell.textContent;
    // Somehow, this function got called on something other than a numbered, non-source miasma block.
    if (blockName.includes("v") || !blockName.includes("m") || blockName === "m0") {
      return;
    } else {
      var sourceCoords = isSourceOrVerticalWithin4Blocks(mB.x, mB.y, mB.z);

      // Replace highest spread level miasma block with air (unless there's another source or vertical miasma block nearby)
      if (blockName === "m3" && !sourceCoords) {
        mB.cell.textContent = "A";
      }
      // Replace any other spread level miasma block with the block 1 spread level greater than it
      else {
        const adjacents = [getBlockNorthOf(mB), getBlockSouthOf(mB), getBlockEastOf(mB), getBlockWestOf(mB)];
        var lvl = Number(blockName[blockName.length - 1]);

        // Don't dissipate the puddle if there's another source or vertical miasma block nearby
        if (!sourceCoords) {
          mB.cell.textContent = "m" + (lvl + 1);
          // Recursive call on original block
          setTimeout(function() {
            return spreadAboutCenter(mB, x, z, recursionDepth + 1);
          }, miasmaSpreadInterval);
        }

        // Recursive calls on the 4 horizontally adjacent blocks
        // Only proceed in a given direction if either no nearby sources were found, or else the adjacent block in that direction will be further from any/all sources than mB is (along either the x or the z axis).
        // Not being able to move back in the direction of a source should prevent an infinite cycle of recursive calls from being generated (which seems to have been the problem with the previous version of this algorithm).
        for (let i = 0; i < adjacents.length; i++) {
          if (adjacents[i] !== undefined) {
            let adjName = adjacents[i].cell.textContent;
            if (adjName.includes("m") && (!sourceCoords || isMoreDistantFromAll(adjacents[i], mB, sourceCoords))) {
              setTimeout(function() {
                return spreadAboutCenter(adjacents[i], x, z, recursionDepth + 1);
              }, miasmaSpreadInterval);
            }
          }
        }
      }
    }
  }
}

// Miasma dissipating when the source is removed: any ground-level numbered miasma block that is not within 4 blocks of a source (or of a flowing block under a source) has its spread level increase by 1
// per spreadInterval, starting with the deepest (or topmost) block; any vertically flowing block (and any miasma block floating in air) will disappear instantly if not connected vertically to a source.
function dissipateMiasma(mB, x, z, recursionDepth) {
  numberOfRecursiveCalls++;
  if (recursionDepth > maxDepth) {
    maxDepth = recursionDepth;
  }
  if (recursionDepth > 4) {
    overflowed = true;
    return;
  } else {
    const mBName = mB.cell.textContent;
    // If a source is encountered, do not continue. Also don't check any blocks that are not some variant of miasma, or are more than 4 blocks away from the current center.
    // When a source is removed with a bucket, the center is initialized to the coords of the removed source. When a vertical flowing block is encountered, the center is reset to that block's x and z coords.
    var xDist = Math.abs(mB.x - x);
    var zDist = Math.abs(mB.z - z);
    if ((xDist < 4 && zDist < 4 && (xDist + zDist < 4)) && mBName.includes("m") && !mBName.includes("m0")) {
      const adjacents = [getBlockNorthOf(mB), getBlockSouthOf(mB), getBlockEastOf(mB), getBlockWestOf(mB)];
      var mB_above = getBlockAbove(mB);
      var mB_below = getBlockBelow(mB);
      var aboveBName;
      var belowBName;
      if (mB_above !== undefined) {
        aboveBName = mB_above.cell.textContent;
      }
      if (mB_below !== undefined) {
        belowBName = mB_below.cell.textContent;
      }

      if (mBName === "mv") {
        // There is another non-source miasma block above mB: perform *instant* recursive call upwards, and do not increment the depth
        if (aboveBName !== undefined && aboveBName === "mv" || (aboveBName.includes("m") && !aboveBName.includes("m0"))) {
          dissipateMiasma(mB_above, mB_above.x, mB_above.z, recursionDepth);
        }
        // mB is the topmost flowing block -- replace it with air and perform *delayed* recursive step downwards if block below is not a source
        else if (aboveBName !== undefined && !aboveBName.includes("m")) {
          mB.cell.textContent = "A";
          // If mB is directly above a flowing source, replace the flowing source block with a still source block and exit the function
          if (belowBName !== undefined && belowBName === "m0v") {
            mB_below.cell.textContent = "m0";
            return;
          }
          // The block below mB is a non-source block: either "mv" or a non-miasma block
          else if (belowBName !== undefined && belowBName !== "m0") {
            // If the block under mB is not miasma, perform *delayed* recursive steps on the 4 adjacent blocks, without changing the center coordinates
            if (!belowBName.includes("m")) {
              for (let i = 0; i < adjacents.length; i++) {
                if (adjacents[i] !== undefined) {
                  setTimeout(function() {
                    return dissipateMiasma(adjacents[i], x, z, recursionDepth + 1);
                  }, miasmaSpreadInterval);
                }
              }
            } else {
              // If it is miasma, perform a *delayed* recursive step on the block below mB, and do not increment the depth
              setTimeout(function() {
                return dissipateMiasma(mB_below, mB_below.x, mB_below.z, recursionDepth);
              }, miasmaSpreadInterval);
            }
          }
        }
      }

      // All numbered miasma blocks
      else if (mBName.includes("m")) {
        var lvl = Number(mBName[mBName.length - 1]);
        var neighbor;
        var hasLessSpreadNeighbor = false;

        for (let i = 0; i < adjacents.length; i++) {
          if (adjacents[i] !== undefined) {
            neighbor = adjacents[i];
            nName = neighbor.cell.textContent;
            // Encountered a miasma source block (either "m0" or "m0v") connected to mB: do not continue.
            if (nName.includes("m0")) {
              return;
            }
            // Encountered vertically flowing miasma or a lower-spread-level miasma block: perform *instant* recursive step horizontally, without changing the center coordinates, and update boolean.
            // USE <, NOT <=, or else it will be an infinite cycle!
            else if (nName === "mv" || (nName.includes("m") && Number(nName[nName.length - 1]) < lvl)) {
              hasLessSpreadNeighbor = true;
              dissipateMiasma(neighbor, x, z, recursionDepth + 1);
            }
          }
        }
        // If no adjacent block has a lower spread level, then mB must be cut off from all source blocks.
        if (!hasLessSpreadNeighbor) {
          // If mB is floating, replace it with air (no delay is required here, since there was already a delay from the vertical base block).
          if (belowBName === "A" || belowBName === "mv") {
            mB.cell.textContent = "A";
            // If the block below the dissipated one is flowing miasma, that should also dissipate after a delay. Reset the center coordinates to the vertically flowing block's coordinates.
            if (belowBName === "mv") {
              setTimeout(function() {
                return dissipateMiasma(mB_below, mB_below.x, mB_below.z, recursionDepth + 1);
              }, miasmaSpreadInterval);
            }
          }
          // Ground-level (or above a miasma source block)
          else {
            // The current block should begin to evaporate (as it is the lowest-spread-level block in its group, the 'center' about which the miasma puddle should dry up)
            spreadAboutCenter(mB, mB.x, mB.z, 0);
          }
        }
      }
    }
  }
}

function playerInteractWithBlock(b) {
  if (player.selectedItem === "full bucket" && b.cell.textContent !== "S" && !player.miasmaBucketCooldown) {
    setTimeout(function() {
      b.cell.textContent = "m0";
    }, 1);
    setTimeout(function() {
      spreadMiasma(b);
    }, miasmaSpreadInterval);

    // Start bucket use cooldown
    player.miasmaBucketCooldown = true;
    setTimeout(function() {
      player.miasmaBucketCooldown = false;
    }, 1000);
  } else if (player.selectedItem === "empty bucket" && (b.cell.textContent === "m0" || b.cell.textContent === "m0v") && !player.miasmaBucketCooldown) {
    // Remove miasma block
    setTimeout(function() {
      b.cell.textContent = "A";
    }, 1);
    // Invoke spreading mechanics on lowest-spread-level miasma block adjacent to the new air block
    var leastSpread = {
      lvl: 4
    }; // Starts 1 level ABOVE the highest possible spread level for miasma. If it is still 4 after the loop, that means there was no adjacent miasma.
    const adjacents = [getBlockNorthOf(b), getBlockSouthOf(b), getBlockEastOf(b), getBlockWestOf(b), getBlockAbove(b), getBlockBelow(b)];
    for (let i = 0; i < adjacents.length; i++) {
      if (adjacents[i] !== undefined) {
        let adjName = adjacents[i].cell.textContent;
        // Here, count vertically flowing miasma blocks as if they were lvl 0, since both sources and vertically flowing blocks spread lvl 1 to adjacents.
        if (adjName.includes("m") && (adjName === "mv" || Number(adjName[adjName.length - 1]) < leastSpread.lvl)) {
          leastSpread.lvl = ((adjName === "mv") || (adjName === "m0v")) ? 0 : Number(adjName[adjName.length - 1]);
          leastSpread.block = adjacents[i];
        }
        // Determine whether to invoke decay mechanics on each adjacent miasma block that is not a source block
        if (adjName.includes("m") && !adjName.includes("m0")) {
          setTimeout(function() {
            return dissipateMiasma(adjacents[i], adjacents[i].x, adjacents[i].z, 0);
          }, miasmaSpreadInterval);
        }
      }
    }
    if (leastSpread.block !== undefined) {
      setTimeout(function() {
        return spreadMiasma(leastSpread.block);
      }, miasmaSpreadInterval);
    }

    // Start bucket use cooldown
    player.miasmaBucketCooldown = true;
    setTimeout(function() {
      player.miasmaBucketCooldown = false;
    }, 1000);
  }
}
table {
  font-size: 10px;
}

table {
  td {
    table {
      td {
        width: 12px;
        height: 12px;
      }
    }
  }
}

#bucket {
  width: 400px;
  height: 50px;
}

#empty {
  background-color: lightgray;
  color: black;
}

#full {
  background-color: purple;
  color: white;
}

#selected {
  color: green;
}
<div id="bucket">
  <p><span id="full"> Full Bucket </span>&#160;&#160;&#160;&#160;&#160;<span id="empty"> Empty Bucket </span>&#160;&#160;&#160;&#160;&#160;Selected: <span id="selected">Full Bucket</span></p>
</div>
<div id="tableDiv"></div>

Service Worker with Next.js /app directory v14

I’m working on a Next.js (v13 or later) project using the /app directory architecture. I’d like to implement a service worker to enhance my website’s performance and offline capabilities, but I’m having trouble finding resources specifically addressing this setup.

Here’s what I need help with:

  • Registration: How do I correctly register the service worker in my app/page.js file?
  • Service Worker Logic: What’s the best approach to write the core service worker code (service-worker.js) for caching strategies and potential offline support within the /app directory structure?
  • Considerations: Are there any Next.js-specific considerations or quirks I should be aware of when implementing a service worker in the /app directory?

Things I’ve tried:

  • I’ve searched for tutorials and guides, but most seem to focus on older Next.js versions or don’t address the /app directory.
  • I understand the basics of service workers in general, but I’m unsure how to adapt it to Next.js’s newer architecture.

I have a website on mobile that keeps zooming in when users tap on the keyboard to quickly. How do I fix it so it no longer zooms, but stays static?

my website (http://thewordshift.com) on mobile I have this issue where if some one double clicks quickly it zooms in. however I don’t have this issue on desktop. is there any code I can implement to make it static

I have edited the css and js to be more mobile friendly but still have the same problem

Difference between getBoundingClientRect().top and clientY?

What is the difference between getBoundingClientRect().top and clientY?

getBoundingClientRect().top gives the position of element relative to viewport.
clientY (from any event at target node) gives position relative to the viewport, not including any scroll offset.

From my quick test the difference in px is not zero.

HLS.js get video size from codec

The video size reported by the current level always returns 0. I guess this just reads whatever info is in MetaData good or bad.

  var level = hls.levels[hls.currentLevel];
  console.log("width: "+level.width); // 0
  console.log("height: "+level.height); // 0

Is there a way to get the video size that is reported by the codec when playing?

how do I executing JS loop returning multiple queries with Dataform

I have the following JS file in my dataform repo:

const values = ["4", "5", "6"];

values.forEach(value => {
  operate("operation" + value,{tags: ["test_operation_tag"]}).queries(
  ctx => `INSERT INTO table (test) VALUES (${value})`
 );
});

It return 3 operations with 3 different names (operation4, operation5, etc…).
As you can see in my code I’m adding a tag but it doesn’t appear as an option for execution. How can I execute the 3 operations returned by this JS script? Do I have to create a dedicated Workflow configurations and select each operations manually?

Dependant dynamically created select(dropdown) fields that run their .change() on page load

I haven’t been in the active programming game for some time so forgive me if the description of my problem is poorly worded.

I have select forms that are generated (using flask) from a database. The database table pre-populates the forms where a value exists, otherwise the code i’m trying to figure out will be used to fill in those blanks by selecting some dependant dropdowns (their values also come from a DB table).

I have ~11 rows of dynamically created ‘s in two columns. one column is dependant on the other. i did manage to get that working using this:

the left column of selects are named: “sel_dstColumn_XXX” where XXX is the row ID in the database.
the right column of selects are named: “sel_dstTable_XXX” and both XXX’s are the same per row. sel_dstColumn_XXX is dependant on sel_dstTable_XXX and works using this .change() below.

  $("select[id^=sel_dstTable]").change(function () {
    table = this.value;
    my_s = this.id.split('_')[2];
    $.get("/get_columns/" + table, function (data, status) {
      var selID = "select[id=sel_dstColumn_" + my_s + "]";
      $(selID).html("");
      data.forEach(function (table) {
        $(selID).append("<option>" + table + "</option>");
      });
    });
  });

i think the code above has the same issues as my code that’s not working but its ok because i’m only working with a single field unlike further down.

the python portion is simple and looks like this for the $.get() url:

@app.route('/get_columns/<value>')
def get_columns(value):
    if value in all_tables:
        return jsonify(all_tables[value])
    return ''

I would like to run this for all the selects with the name sel_dstTable_XXX on page load to pre-populate the dependant selects based on what (if any) values are pulled from the database. I’m trying this below (duplicate code but i dont know how to call the .change() for all the proper forms.. it doesn’t work due to the variable “my_s” not properly passing into the $.get() function. i’m not understand why my_s produces the results below in the console screenshot.

this is what i’m currently working with. it’s mostly duplicate code from my .change() function. i’m not sure how to just call that change function for each select on startup though.

  $(function () {
    $("select[name^=sel_dstTable_]").each(function () {
      table = this.value;
      my_s = this.id.split('_')[2];
      console.log("expected variable my_s: " + my_s);
      $.get("/get_columns/" + table, function (data, status) {
        console.log("actual variable my_s:" + my_s);
# PROBLEM ON THE LINE ABOVE reading my_s, i only get the last row ID (22), instead of 12, 13, 14, 15... etc produced when looping through the .each().
        // var selID = "select[name=sel_dstColumn_" + my_s + "]";
        // $(selID).html("");
        // data.forEach(function (table) {
        //   $(selID).append("<option>" + table + "</option>");
        // });
      });
    });
  });

console output

Thank you so much for any help – i’ve been fighting this all week. I hope this description makes sense.

rendering 2nd layer of nested array in vue 3 within each component

Good day guys

I’d like to seek advise on accessing the 2nd element layer of my nested array to loop through a templated component making it a dynamic card through a year, btw I use laravel vue just incase you need to know

my desired template

----------------------------|
|Component                  |
|---------------------------|
|Title header               |
|---------------------------|
|Event Name  |date |remarks |
-----------------------------

I reffered to this as my guide for the array Nested arrays of objects and v-for

then afterwards i was able to rendered or display the first layer which is the title of my component but the 2nd layer which the data seems to return an empty, but when i try to dump the whole array it returns me the right data

Sample Dump

[
  {
    "event_name": "New Year",
    "month": "01",
    "day": 1,
    "status": "enabled",
    "date": "2024-01-1"
  }
]

here’s my sample codes

Structure of the code/component

title-> first loop to render the 1st element of array (title) using v-for
data->  2nd render the rest of the data loop using v-for inside the first loop

script

<script setup>
import MainContent from '../../Components/MainContent.vue'
import { reactive } from 'vue'

 const props = defineProps({
  title: String,
  january:Array,
  february:Array,
  december:Array,
  department_category:Array
})
const months = [
        {
            name:"January",
            data:props.january
        },
        {
            name:"February",
            data:props.february
            
        }
    ]
</script>

Vue Component

 <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4 mb-4">
                 <!-- Loop through Whole Year -->
                 <div v-for="value in months" :key="value.id"  class="bg-white rounded-md border border-gray-100 p-6 shadow-md shadow-black/5">
                    <div class="flex justify-between mb-4 items-start">
                        <div class="font-medium">{{ value.name}}</div>
                            <div class="dropdown">
                                <button type="button" class="dropdown-toggle text-gray-400 hover:text-gray-600"><i class="ri-more-fill"></i></button>
                                <ul class="dropdown-menu shadow-md shadow-black/5 z-30 hidden py-1.5 rounded-md bg-white border border-gray-100 w-full max-w-[140px]">
                                    <li>
                                        <a href="#" class="flex items-center text-[13px] py-1.5 px-4 text-gray-600 hover:text-blue-500 hover:bg-gray-50">Profile</a>
                                    </li>
                                    <li>
                                        <a href="#" class="flex items-center text-[13px] py-1.5 px-4 text-gray-600 hover:text-blue-500 hover:bg-gray-50">Settings</a>
                                    </li>
                                    <li>
                                        <a href="#" class="flex items-center text-[13px] py-1.5 px-4 text-gray-600 hover:text-blue-500 hover:bg-gray-50">Logout</a>
                                    </li>
                                </ul>
                            </div>
                    </div>
                    <div class="overflow-x-auto">
                        <table class="w-full min-w-[540px]">
                            <thead>
                                <tr>
                                    <th class="text-[12px] uppercase tracking-wide font-medium text-gray-400 py-2 px-4 bg-gray-50 text-left rounded-tl-md rounded-bl-md">Event Name</th>
                                    <th class="text-[12px] uppercase tracking-wide font-medium text-gray-400 py-2 px-4 bg-gray-50 text-left">Date</th>
                                    <th class="text-[12px] uppercase tracking-wide font-medium text-gray-400 py-2 px-4 bg-gray-50 text-left">Remarks</th>
                                </tr>
                            </thead>
                            <tbody>
                                <tr v-for="event in [value.data]" :key="event.id">
                                    <td class="py-2 px-4 border-b border-b-gray-50">
                                        <div class="flex items-center">
                                            <img src="https://placehold.co/32x32" alt="" class="w-8 h-8 rounded object-cover block">
                                            <a href="#" class="text-gray-600 text-sm font-medium hover:text-blue-500 ml-2 truncate">{{ event.event_name}}</a>
                                        </div>
                                    </td>
                                    <td class="py-2 px-4 border-b border-b-gray-50">
                                        <span class="text-[13px] font-medium text-gray-400">{{ event.date }}</span>
                                    </td>
                                    <td class="py-2 px-4 border-b border-b-gray-50">
                                        <span class="text-[13px] font-medium text-gray-400">
                                            <button class="ml-2 btn btn-xs btn-accent">Update</button>
                                            <button class="ml-2 btn btn-xs btn-error">Remove</button>
                                        </span>
                                    </td>
                                </tr>



                            </tbody>
                        </table>
                    </div>

My Current Output

----------------------------|
|Component                  |
|---------------------------|
|January                    |
|---------------------------|
|//empty     |//empty       |
-----------------------------



    ----------------------------|
    |Component                  |
    |---------------------------|
    |February                   |
    |---------------------------|
    |//empty     |//empty       |
    -----------------------------