Unused variable in for each

I have this function in my TS project:

  size(): number {
    let size = 0;
    for (const _ of this) {
      size++;
    }

    return size;
  }

While I want to continue using a for each to count the elements I get following eslint error:

'_' is assigned a value but never used

In other languages like ruby, prefixing with an underscore would fix this. In this case that doesn’t work.

sql error, Saying theres no relations between tables but I cant figure out why

I have a database for a DnD builder webpage.

We want to make an application that when the user selects a radio on the webpage it will pull the corresponding information from the database and display the compiled data on the webpage so that the user can see the end result of however many choices without too much busywork.

The Database is sql, and I’m currently working on the models for it with Sequelize.

However, the models are currently the trouble.
I’m extremely inexperienced, so I fear this mistake is a very simple one. Because this is the error code being thrown out on my console.

length: 105,
severity: 'ERROR',
code: '42P01',
detail: undefined,
hint: undefined,
position: undefined,
internalPosition: undefined,
internalQuery: undefined,
where: undefined,
schema: undefined,
table: undefined,
column: undefined,
dataType: undefined,
routine: 'RangeVarGetRelidExtended',
sql: 'ALTER TABLE "Race"  ADD FOREIGN KEY ("subrace_id") REFERENCES "Subrace" ("id");',       
parameters: undefined
ubrace_id") REFERENCES "Subrace" ("id");',
sql: 'ALTER TABLE "Race"  ADD FOREIGN KEY ("subrace_id") REFERENCES "Subrace" ("id");',
parameters: {}                                 race_id") REFERENCES "Subrace" ("id");',
> }

This is the subrace model

const  { Model, DataTypes } = require('sequelize');

const sequelize = require('../config/connection.js');


class Subrace extends Model {}

Subrace.init(
    {
        id: {
         type: DataTypes.INTEGER,
         allowNull: false,
         primaryKey: true,
        },
        name: {
            type: DataTypes.STRING
        },
        skills_id: {
            type: DataTypes.INTEGER,
            references: {
                model: 'Skills',
                key: 'id',
                unique: false,
            },
        },
    }, 
    {
        sequelize,
        timestamps: false,
        modelName: 'Subrace'
    }
    


);

module.exports = Subrace; 

This is the race model

const  { Model, DataTypes } = require('sequelize');

const sequelize = require('../config/connection.js');


class Race extends Model {}

Race.init(
    {
        id: {
         type: DataTypes.INTEGER,
         allowNull: false,
         primaryKey: true,
        },
        name: {
            type: DataTypes.STRING,
            allowNull: false,
        },
        subrace_id: {
            type: DataTypes.INTEGER,
            references: {
                model: 'Subrace',
                key: 'id'
            },
        },
        skills_id: {
            type: DataTypes.INTEGER,
            references: {
                model: 'Skills',
                key: 'id'
            },
        },
    }, 
    {
        sequelize,
        timestamps: false,
        freezeTableName: true,
        modelName: 'Race'
    }
    


);

module.exports = Race; 


I think the error is coming in in how I’ve made the index.

Race.hasOne(Subrace, {
  foreignKey: "subrace_id",
});
Subrace.belongsTo(Race, {
  foreignKey: "race_id",
});

This is presently how they’re coded in. If I’ve overlooked any code or information that’s important or helpful please tell me.

I’ve looked for syntax errors and tried moving the code structure around. I’d heard if they’re made in the wrong order that can be the cause of this issue.

Save additional fields values from WooCommerce checkout Blocks

I’m developing a Payment Gateway plugin for WooCommerce Blocks (Gutenberg). I need to get a custom checkout field in the address block.
I’ve managed to do so, but now I’m stuck on how I can pass the values from the frontend to the backend.

The last 3 fields are custom:
print of addicional fields

The code that generates the fields is the following:

(function () {
document.addEventListener('DOMContentLoaded', function() {
// Helper function to create a custom field
function createCustomField(options) {
const { formId, fieldId, labelText, validationMessage, inputName } = options;
const form = document.getElementById(formId);

            if (form && !document.getElementById(fieldId)) {
                const customFieldWrapper = document.createElement('div');
                customFieldWrapper.classList.add('wc-block-components-text-input');
                customFieldWrapper.classList.add(`wc-block-components-address-form__${fieldId}`);
    
                const label = document.createElement('label');
                label.setAttribute('for', fieldId);
                label.textContent = labelText;
    
                const input = document.createElement('input');
                input.type = 'text';
                input.id = fieldId;
                input.name = inputName; // Set the name attribute
                input.ariaLabel = labelText;
                input.required = true;
    
                input.addEventListener('focus', function () {
                    customFieldWrapper.classList.add('is-active');
                });
    
                input.addEventListener('blur', function () {
                    const fieldValue = this.value.trim();
    
                    if (!fieldValue) {
                        customFieldWrapper.classList.add('has-error');
                        customFieldWrapper.classList.remove('is-active');
    
                        let warningDiv = document.getElementById(`warning-div-${fieldId}`);
                        if (warningDiv) {
                            warningDiv.remove();
                        }
    
                        warningDiv = document.createElement('div');
                        warningDiv.classList.add("wc-block-components-validation-error");
                        warningDiv.id = `warning-div-${fieldId}`;
    
                        const warningText = document.createElement('p');
                        warningText.textContent = validationMessage;
                        warningDiv.appendChild(warningText);
    
                        customFieldWrapper.appendChild(warningDiv);
                    } else {
                        customFieldWrapper.classList.remove('has-error');
                        const warningDiv = document.getElementById(`warning-div-${fieldId}`);
                        if (warningDiv) {
                            warningDiv.remove();
                        }
                    }
                });
    
                customFieldWrapper.appendChild(label);
                customFieldWrapper.appendChild(input);
                form.appendChild(customFieldWrapper);
            }
        }
    
        const observer = new MutationObserver(function() {
            createCustomField({
                formId: 'billing',
                fieldId: 'billing-neighborhood',
                labelText: 'Bairro',
                validationMessage: 'Por favor preencha o Bairro',
                inputName: 'billing_neighborhood' // Added name attribute
            });
    
            createCustomField({
                formId: 'billing',
                fieldId: 'billing-street-number',
                labelText: 'NΓΊmero',
                validationMessage: 'Por favor preencha o NΓΊmero',
                inputName: 'billing_number' // Added name attribute
            });
    
            createCustomField({
                formId: 'billing',
                fieldId: 'billing-cpf',
                labelText: 'CPF',
                validationMessage: 'Por favor preencha o CPF',
                inputName: 'billing_cpf' // Added name attribute
            });
        });
    
        observer.observe(document.body, { childList: true, subtree: true });
    });

})();

I’m trying to get the fields on my main plugin file (plugin-name.php) this way but it’s failing to retrieve anything…

//WooCommerce Blocks
add_action('woocommerce_store_api_checkout_update_order_meta', 'save_custom_checkout_fields', 10, 2);
function save_custom_checkout_fields($order) {
$logger = wc_get_logger();
$logger->info('Entered on woocommerce_store_api_checkout_update_order_meta', array( 'source' => 'aditum-pix-meta' ));
$logger->info(wc_print_r( $order, true ), array( 'source' => 'aditum-pix-meta' ));

    if (isset($_POST['billing_neighborhood'])) {
        $neighborhood = sanitize_text_field($_POST['billing_neighborhood']);
        $order->update_meta_data('_billing_neighborhood', $neighborhood);
        $logger->info('Billing Neighborhood: ' . $neighborhood, array( 'source' => 'aditum-pix-meta' ));
    }
    
    if (isset($_POST['billing_street_number'])) {
        $street_number = sanitize_text_field($_POST['billing_street_number']);
        $order->update_meta_data('_billing_street_number', $street_number);
        $logger->info('Billing Street Number: ' . $street_number, array( 'source' => 'aditum-pix-meta' ));
    }
    
    if (isset($_POST['billing_cpf'])) {
        $cpf = sanitize_text_field($_POST['billing_cpf']);
        $order->update_meta_data('_billing_cpf', $cpf);
        $logger->info('Billing CPF: ' . $cpf, array( 'source' => 'aditum-pix-meta' ));
    }

}

When the log prints the $order, that’s what the billing object is filled with:

[billing] => Array
                (
                    [first_name] => asd
                    [last_name] => asdasd
                    [company] => 
                    [address_1] => asd
                    [address_2] => 123
                    [city] => asd
                    [state] => SP
                    [postcode] => 03530-110
                    [country] => BR
                    [email] => [email protected]
                    [phone] => 
                )

Alternative to Headless JS

I am trying to write a React Native app that runs some processing every 15 minutes. To be specific: I want fetch a status from an open 3rd party API and create a notification if that status has changed.

I thought I could use a React Native module and Headless JS to do this:

  1. Use a native module to create a Work Manager:

a. JS Side:

import { NativeModules } from "react-native";

const { WorkerModule } = NativeModules;

WorkerModule.configureWorker();

b. Native Side:

class WorkerModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {  
  
    var context = reactContext.getApplicationContext()  
  
    override fun getName() = "WorkerModule"  
  
    @ReactMethod fun configureWorker() {  
        val periodicRequest = PeriodicWorkRequest  
            .Builder(MyWorker::class.java, 15, TimeUnit.MINUTES)  
            .setInitialDelay(10, TimeUnit.SECONDS)  
            .build()  
  
        WorkManager  
            .getInstance(this.context)  
            .enqueueUniquePeriodicWork("unique_work_name", ExistingPeriodicWorkPolicy.UPDATE, periodicRequest)  
    }  
}

  1. Register a javascript function with Headless JS (as per HeadlessJS docs)

a. JS Side:

AppRegistry.registerHeadlessTask("myBackgroundFetchFunction", (...args) => {
    console.log('woo! im a react native background javascript task')
    // do my fetch and notification logic in javascript
});

b. Native Side

class JsTaskService : HeadlessJsTaskService() {  
    override fun getTaskConfig(intent: Intent): HeadlessJsTaskConfig? {  
        return intent.extras?.let {  
            HeadlessJsTaskConfig(  
                "myBackgroundFetchFunction",  
                null,
                5000, // timeout for the task  
                true // optional: defines whether or not the task is allowed in foreground.  
                // Default is false            )  
        }  
    }  
}
  1. Use a native worker to call my javascript function using Headless JS:
class MyWorker(context: Context, private val workParams: WorkerParameters) : Worker(context, workParams) {  
    override fun doWork(): Result {  
        Log.d("MyWorker", "im a background task running in kotlin")
        val service = Intent(applicationContext, JsTaskService::class.java);  
  
        HeadlessJsTaskService.acquireWakeLockNow(applicationContext)  
    
        applicationContext.startForegroundService(service) 

        // or for older version of android:
        // applicationContext.startService(service) 
        return Result.success()  
    }  
}
  1. Register the service in the AndroidManifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">  
  ...
  <application 
      android:name=".MainApplication" 
      android:label="@string/app_name" 
      android:icon="@mipmap/ic_launcher" 
      android:roundIcon="@mipmap/ic_launcher_round" 
      android:allowBackup="true" 
      android:theme="@style/AppTheme"
  >      
    ...    
    <service android:name=".....JsTaskService" />  
    <service        
        android:name="androidx.work.impl.foreground.SystemForegroundService"  
        android:foregroundServiceType="shortService" />  
  </application>
</manifest>

However on my device running Android 14 I’m getting errors when trying to run the line applicationContext.startForegroundService(service).

android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false

I understand that this is because on Android 14 it is no longer possible to start a foreground service from background services.

I have also seen that Headless JS is not being actively supported or recommended by the React Native team and is not used internally at Meta


Questions:

  1. What alternatives do we have to run javascript code in the background of our React Native applications?
  2. Are there any examples around that can be followed and allow javascript functions to run in the background of a React Native app on the latest versions of Android 14 SDK version 34+?

How to force JavaScript fetch api to request page in desktop mode?

I use the JavaScript fetch api to download json files from Reddit’s public json endpoints. Recently, Reddit has made their json endpoints respond differently based on whether the browser is in desktop mode or mobile mode. If the browser is in mobile mode, my app now receives bogus data from Reddit in most cases, whereas in desktop mode my app receives correct data.

How can I force the fetch() call to make the request in desktop mode?

What are the ways I can optimize math heavy operations in JS?

I’m using a Javascript ES6 Class to store an instance of a polygon object. Within this object I define functions to calculate contour coordinates based on a long physics equation. In my constructor I probably have over 200 lines declaring variables to be used later in the contour functions themselves. There’s plenty of logs, square roots, powers, etc… The contour functions themselves are mathematically heavy as well.

What are the ways I can optimize heavy math like this? I was thinking possibly to do the work asynchronous, but I am unsure if that would have any real benefit.

As is non-asynchronously the polygons render extremely quickly. I attempted to structure the code asynchronously, however, I was unsure how to make the variable declaration in my constructor asynchronous or if there would be any real benefit.

Any advice appreciated πŸ™‚

Parameter encoded with encodeURIComponent, how to get it in a Python Bottle server?

Let’s say I send a request with JavaScript:

fetch("/query?q=" + encodeURIComponent("c'est un chΓ’teau? Yes & no!"));

to a Python Bottle server:

from bottle import Bottle, request
app = Bottle("")
@app.route("/query")
def query():
    q = request.query.get("q")   # how to decode it?
    print(q)
app.run()    

How to decode request.query.get("q") so that it extracts the encodeURIComponent encoding? In this example, the ? and & are correctly decoded, but the Γ’ is not: print(q) gives c'est un chÒteau? Yes & no!

Why is there a line going through a part of the circle?

I am making a collision system but the intersections of a partial circle are not working. There is a line that is going through the edge of the circle can anyone help?
I am using canvas as well.

This is so you can see the output

let world = {}
let cnvs, ctx, cnvs2, ctx2, cnvs3, ctx3;
let n;

function distance(x1, y1, x2, y2) {
  // Calculating distance 
  return Math.sqrt(Math.pow(x2 - x1, 2) +
    Math.pow(y2 - y1, 2) * 1.0);
}

function mod(x, y) {
  let output = x;
  while (output >= y) {
    output -= y
  }
  return output
}

function intersecting(n, t, r, e) {
  return intersect(n.x, n.y, t.x, t.y, r.x, r.y, e.x, e.y)
}

function intersect(x1, y1, x2, y2, x3, y3, x4, y4) {

  // Check if none of the lines are of length 0
  if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) {
    return false
  }

  denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))

  // Lines are parallel
  if (denominator === 0) {
    return false
  }

  let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator
  let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator

  // is the intersection along the segments
  if (ua < 0 || ua > 1 || ub < 0 || ub > 1) {
    return false
  }

  // Return a object with the x and y coordinates of the intersection
  let x = x1 + ua * (x2 - x1)
  let y = y1 + ua * (y2 - y1)

  return {
    x,
    y
  }
}

function inteceptCircleLineSeg(circle, line, check2, noN) {
  if (!n) n = 0
  var a, b, c, d, u1, u2, ret, retP1, retP2, v1, v2;
  v1 = {};
  v2 = {};
  v1.x = line.x2 - line.x;
  v1.y = line.y2 - line.y;
  v2.x = line.x - circle.x;
  v2.y = line.y - circle.y;
  b = (v1.x * v2.x + v1.y * v2.y);
  c = 2 * (v1.x * v1.x + v1.y * v1.y);
  b *= -2;
  d = Math.sqrt(b * b - 2 * c * (v2.x * v2.x + v2.y * v2.y - circle.radius * circle.radius));

  if (isNaN(d)) { // no intercept
    return false;
  }
  u1 = (b - d) / c; // these represent the unit distance of point one and two on the line
  u2 = (b + d) / c;
  retP1 = {}; // return points
  retP2 = {}
  ret = []; // return array
  if (u1 <= 1 && u1 >= 0) { // add point if on the line segment
    retP1.x = line.x + v1.x * u1;
    retP1.y = line.y + v1.y * u1;
    ret[0] = retP1;
  } else {
    //if (!check2) world.drawBackup.push({type:'arc',x:line.x + v1.x * u1,y:line.y + v1.y * u1,radius:5,start:0,end:2*Math.PI,lineWidth:1})
  }
  if (u2 <= 1 && u2 >= 0) { // second add point if on the line segment
    retP2.x = line.x + v1.x * u2;
    retP2.y = line.y + v1.y * u2;
    ret[ret.length] = retP2;
  } else {
    //if (!check2) world.drawBackup.push({type:'arc',x:line.x + v1.x * u2,y:line.y + v1.y * u2,radius:15,start:0,end:2*Math.PI,lineWidth:1})
  }
  if (ret.length === 0) {
    return false;
  }
  if (!check2) {
    if (mod(circle.end, 2 * Math.PI) !== circle.start) {
      let partial = false;
      for (let i = circle.end; i < circle.end + circle.start; i += 0.01) {
        if (u1 <= 1 && u1 >= 0) {
          if (inteceptCircleLineSeg({
              radius: 1,
              x: Math.cos(i) * circle.radius + circle.x,
              y: Math.sin(i) * circle.radius + circle.y,
              start: 0,
              end: Math.PI
            }, {
              x: line.x,
              y: line.y,
              x2: retP1.x,
              y2: retP1.y
            }, true)) {
            ret[0].x = line.x + v1.x * u2
            ret[0].y = line.y + v1.y * u2
            partial = true
            let intersection = intersecting({
              x: Math.cos(circle.end) * (circle.radius + 0.5) + circle.x,
              y: Math.sin(circle.end) * (circle.radius + 0.5) + circle.y
            }, {
              x: Math.cos(circle.start) * (circle.radius + 0.5) + circle.x,
              y: Math.sin(circle.start) * (circle.radius + 0.5) + circle.y
            }, {
              x: line.x,
              y: line.y
            }, {
              x: ret[0].x,
              y: ret[0].y
            })
            if (!intersection) {
              return false
            }
          }
        } else if (u2 <= 1 && u2 >= 0) {
          if (inteceptCircleLineSeg({
              radius: 1,
              x: Math.cos(i) * circle.radius + circle.x,
              y: Math.sin(i) * circle.radius + circle.y,
              start: 0,
              end: Math.PI
            }, {
              x: line.x,
              y: line.y,
              x2: retP2.x,
              y2: retP2.y
            }, true)) {
            ret[0].x = line.x + v1.x * u1
            ret[0].y = line.y + v1.y * u1
            partial = true
            let intersection = intersecting({
              x: Math.cos(circle.end) * (circle.radius + 0.5) + circle.x,
              y: Math.sin(circle.end) * (circle.radius + 0.5) + circle.y
            }, {
              x: Math.cos(circle.start) * (circle.radius + 0.5) + circle.x,
              y: Math.sin(circle.start) * (circle.radius + 0.5) + circle.y
            }, {
              x: line.x,
              y: line.y
            }, {
              x: ret[0].x,
              y: ret[0].y
            })
            if (!intersection) {
              return false
            }
          }
        }
      }
      if (partial) {
        if (circle.fill) {
          let intersection = intersecting({
            x: Math.cos(circle.end) * (circle.radius + 0.5) + circle.x,
            y: Math.sin(circle.end) * (circle.radius + 0.5) + circle.y
          }, {
            x: Math.cos(circle.start) * (circle.radius + 0.5) + circle.x,
            y: Math.sin(circle.start) * (circle.radius + 0.5) + circle.y
          }, {
            x: line.x,
            y: line.y
          }, {
            x: ret[0].x,
            y: ret[0].y
          })
          if (intersection) {
            ret[0].x = intersection.x
            ret[0].y = intersection.y
          }
        }
      }
    }
  }
  return ret;
}
world.mouseX = 0;
world.mouseY = 0;

function addEvent(el, ev, func) {
  el.addEventListener(ev, func, false)
}

function removeEvent(el, ev, func) {
  el.removeEventListener(ev, func)
}
world.cubeList = [];
world.cubeNum = 0;
world.drawBackup = [];

world.GetMouseCoords = function(e) {
  var posx = 0;
  var posy = 0;
  if (!e) var e = window.event;
  if (e.pageX || e.pageY) {
    posx = e.pageX;
    posy = e.pageY;
  } else if (e.clientX || e.clientY) {
    posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
    posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
  }

  world.mouseX = posx * cnvs.width / document.documentElement.clientWidth
  world.mouseY = posy * cnvs.height / document.documentElement.clientHeight
}
world.cube = function(x, y, z, texture) {
  this.id = world.cubeNum;
  this.x = cnvs.width / 2 + (x)
  this.y = cnvs.height / 2 + (y)
  this.texture = texture;
  world.cubeList[world.cubeNum] = this;
  world.cubeNum++
}
world.drawList = []
world.drawLength = 0;
world.setupDraw = function(info) {
  ctx.lineWidth = info.lineWidth
  if (info.type !== 'group') ctx.beginPath()
  if (info.type === 'arc') {
    ctx.arc(info.x, info.y, info.radius, info.start, info.end)
    this.start = info.start;
    this.end = info.end;
    this.x = info.x;
    this.y = info.y;
    this.radius = info.radius;
  } else if (info.type === 'line') {
    ctx.moveTo(info.x, info.y)
    ctx.lineTo(info.x2, info.y2)
    this.x = info.x;
    this.y = info.y;
    this.x2 = info.x2;
    this.y2 = info.y2;
  } else if (info.type === 'group') {
    let lineMoveTo = 1;
    for (let i = 0; i < info.items.length; i++) {
      let me = info.items[i];
      if (lineMoveTo === 1) ctx.beginPath();
      if (me.type === 'line') {
        if (lineMoveTo === 1) {
          ctx.moveTo(me.x, me.y)
        } else {
          ctx.lineTo(me.x, me.y)
        }
      } else if (me.type === 'arc') {
        ctx.arc(me.x, me.y, me.radius, me.start, me.end)
      } else if (me.type === 'closePath') {
        ctx.closePath()
        lineMoveTo = 1;
      }
      if (me.type !== 'closePath') lineMoveTo = 0;
    }
    this.items = info.items
  }
  if (info.type === 'arc') {
    this.fill = info.fill
    if (info.fill) ctx.fill()
  }
  ctx.stroke()
  this.lineWidth = info.lineWidth;
  this.id = world.drawLength;
  this.type = info.type;
  world.drawList[world.drawLength] = this;
  world.drawLength++
}
world.drawCam = function(cam) {
  n = 0
  ctx.lineWidth = 1;
  let xList = [];
  let yList = [];
  for (let i = cam.i; i < cam.i + 1; i += 0.01) {
    ctx.lineWidth = 1;
    ctx.beginPath();
    ctx.moveTo(cam.x, cam.y);
    let raduis = 600;
    let x = cam.x + Math.cos(i) * raduis
    let y = cnvs.height / 2 + Math.sin(i) * raduis
    let t = inteceptCircleLineSeg({
      radius: 1,
      x: world.mouseX,
      y: world.mouseY,
      start: 0,
      end: Math.PI * 2,
      fill: false
    }, {
      x: cam.x,
      y: cam.y,
      x2: x,
      y2: y
    }, true)
    if (t) ctx.lineWidth = 4
    for (let i2 = 0; i2 < world.drawList.length; i2++) {
      let me = world.drawList[i2];
      if (me.type === 'arc') {
        let touching;
        touching = inteceptCircleLineSeg({
          radius: me.radius,
          x: me.x,
          y: me.y,
          start: me.start,
          end: me.end,
          fill: me.fill
        }, {
          x: cam.x,
          y: cam.y,
          x2: x,
          y2: y
        })
        if (touching) {
          x = touching[0].x;
          y = touching[0].y;
        }
      } else if (me.type === 'line') {
        let touching;
        touching = intersecting({
          x: cam.x,
          y: cam.y
        }, {
          x: x,
          y: y
        }, {
          x: me.x,
          y: me.y
        }, {
          x: me.x2,
          y: me.y2
        })
        if (touching) {
          x = touching.x;
          y = touching.y;
        }
      } else if (me.type === 'group') {
        for (let i3 = 0; i3 < me.items.length; i3++) {
          let me2 = me.items[i3];
          if (me2.type === 'arc') {
            let touching;
            touching = inteceptCircleLineSeg({
              radius: me2.radius,
              x: me2.x,
              y: me2.y,
              start: me2.start,
              end: me2.end,
              fill: false
            }, {
              x: cam.x,
              y: cam.y,
              x2: x,
              y2: y
            })
            if (touching) {
              x = touching[0].x;
              y = touching[0].y;
            }
            if (i3 + 1 < me.items.length) {
              let me3 = me.items[i3 + 1];
              if (me3.type !== 'closePath') {
                let x2 = me3.x;
                let y2 = me3.y;
                if (me3.type === 'arc') {
                  x2 = me3.x + Math.cos(me3.start) * me3.radius;
                  y2 = me3.y + Math.sin(me3.start) * me3.radius;
                }
                touching = intersecting({
                  x: cam.x,
                  y: cam.y
                }, {
                  x: x,
                  y: y
                }, {
                  x: me2.x + me2.radius,
                  y: me2.y
                }, {
                  x: x2,
                  y: y2
                })
                if (touching) {
                  x = touching.x;
                  y = touching.y;
                }
              }
            }
          } else if (me2.type === 'line') {
            if (i3 + 1 < me.items.length) {
              let me3 = me.items[i3 + 1];
              if (me3.type !== 'closePath') {
                let touching;
                let x2 = me3.x;
                let y2 = me3.y;
                if (me3.type === 'arc') {
                  x2 = me3.x + Math.cos(me3.start) * me3.radius;
                  y2 = me3.y + Math.sin(me3.start) * me3.radius;
                }
                touching = intersecting({
                  x: cam.x,
                  y: cam.y
                }, {
                  x: x,
                  y: y
                }, {
                  x: me2.x,
                  y: me2.y
                }, {
                  x: x2,
                  y: y2
                })
                if (touching) {
                  x = touching.x;
                  y = touching.y;
                }
              }
            }
          }
        }
      }
    }
    for (let i2 = 0; i2 < world.drawList.length; i2++) {
      let me = world.drawList[i2];
      if (me.type === 'line') {
        let touching;
        touching = intersecting({
          x: cam.x,
          y: cam.y
        }, {
          x: x,
          y: y
        }, {
          x: me.x,
          y: me.y
        }, {
          x: me.x2,
          y: me.y2
        })
        if (touching) {
          x = touching.x;
          y = touching.y;
        }
      } else if (me.type === 'group') {
        for (let i3 = 0; i3 < me.items.length; i3++) {
          let me2 = me.items[i3];
          if (me2.type === 'arc') {
            if (i3 + 1 < me.items.length) {
              let me3 = me.items[i3 + 1];
              if (me3.type !== 'closePath') {
                let x2 = me3.x;
                let y2 = me3.y;
                if (me3.type === 'arc') {
                  x2 = me3.x + Math.cos(me3.start) * me3.radius;
                  y2 = me3.y + Math.sin(me3.start) * me3.radius;
                }
                touching = intersecting({
                  x: cam.x,
                  y: cam.y
                }, {
                  x: x,
                  y: y
                }, {
                  x: me2.x + me2.radius,
                  y: me2.y
                }, {
                  x: x2,
                  y: y2
                })
                if (touching) {
                  x = touching.x;
                  y = touching.y;
                }
              }
            }
          } else if (me2.type === 'line') {
            if (i3 + 1 < me.items.length) {
              let me3 = me.items[i3 + 1];
              if (me3.type !== 'closePath') {
                let touching;
                let x2 = me3.x;
                let y2 = me3.y;
                if (me3.type === 'arc') {
                  x2 = me3.x + Math.cos(me3.start) * (me3.radius);
                  y2 = me3.y + Math.sin(me3.start) * (me3.radius);
                }
                touching = intersecting({
                  x: cam.x,
                  y: cam.y
                }, {
                  x: x,
                  y: y
                }, {
                  x: me2.x,
                  y: me2.y
                }, {
                  x: x2,
                  y: y2
                })
                if (touching) {
                  x = touching.x;
                  y = touching.y;
                }
              }
            }
          }
        }
      }
    }
    ctx.lineTo(x, y);
    ctx.stroke();
    xList.push(x)
    yList.push(y)
  }
  ctx2.beginPath();
  ctx2.moveTo(xList[0], yList[0])
  for (let i = 1; i < xList.length; i++) {
    ctx2.lineTo(xList[i], yList[i])
  }
  ctx2.stroke();
  while (world.drawBackup.length > 0) {
    world.draw(world.drawBackup[0])
    let drawBackup = [];
    for (let i = 1; i < world.drawBackup.length; i++) {
      drawBackup.push(world.drawBackup[i])
    }
    world.drawBackup = drawBackup;
  }
}
world.cam = {}
world.draw = function(info) {
  ctx.lineWidth = info.lineWidth
  if (info.type !== 'group') ctx.beginPath()
  if (info.type === 'arc') {
    ctx.arc(info.x, info.y, info.radius, info.start, info.end)
  } else if (info.type === 'line') {
    ctx.moveTo(info.x, info.y)
    ctx.lineTo(info.x2, info.y2)
  } else if (info.type === 'group') {
    let lineMoveTo = 1;
    for (let i = 0; i < info.items.length; i++) {
      let me = info.items[i];
      if (lineMoveTo === 1) ctx.beginPath();
      if (me.type === 'line') {
        if (lineMoveTo === 1) {
          ctx.moveTo(me.x, me.y)
        } else {
          ctx.lineTo(me.x, me.y)
        }
      } else if (me.type === 'arc') {
        ctx.arc(me.x, me.y, me.radius, me.start, me.end)
      } else if (me.type === 'closePath') {
        ctx.closePath()
        lineMoveTo = 1;
      }
      if (me.type !== 'closePath') lineMoveTo = 0;
    }
  }
  if (info.type === 'arc') {
    if (info.fill) ctx.fill()
  }
  ctx.stroke()
}
world.runDraw = function(i) {
  ctx.clearRect(0, 0, cnvs.width, cnvs.height)
  ctx2.clearRect(0, 0, cnvs.width, cnvs.height)
  ctx3.clearRect(0, 0, cnvs.width, cnvs.height)
  ctx.strokeStyle = "blue";
  ctx.fillStyle = "blue";
  ctx2.strokeStyle = "blue";
  ctx2.fillStyle = "blue";
  ctx3.strokeStyle = "blue";
  ctx3.fillStyle = "blue";
  for (let i2 = 0; i2 < world.drawList.length; i2++) {
    world.draw(world.drawList[i2])
  }
  world.drawCam(world.cam)
}

function setup() {
  new world.setupDraw({
    type: 'arc',
    x: cnvs.width / 2,
    y: cnvs.height / 2,
    radius: 500,
    start: 0,
    end: 2 * Math.PI,
    lineWidth: 8,
    fill: false
  })
  new world.setupDraw({
    type: 'line',
    x: cnvs.width / 2 + Math.sin(1) * 500,
    y: cnvs.height / 2 + Math.cos(1) * 500,
    x2: cnvs.width / 2 + Math.sin(5) * 500,
    y2: cnvs.height / 2 + Math.cos(5) * 500,
    lineWidth: 8
  })
  new world.setupDraw({
    type: 'line',
    x: cnvs.width / 2 + Math.sin(2) * 500,
    y: cnvs.height / 2 + Math.cos(2) * 500,
    x2: cnvs.width / 2 + Math.sin(4) * 500,
    y2: cnvs.height / 2 + Math.cos(4) * 500,
    lineWidth: 8
  })
  new world.setupDraw({
    type: 'group',
    lineWidth: 1,
    items: [{
      type: 'line',
      x: cnvs.width / 2 - 200,
      y: 0
    }, {
      type: 'line',
      x: cnvs.width / 2 - 200,
      y: cnvs.height
    }, {
      type: 'arc',
      x: cnvs.width / 2 - 200,
      y: cnvs.height / 2 - 200,
      radius: 50,
      start: 5,
      end: 2 * Math.PI
    }]
  })
  addEvent(cnvs, 'mousemove', function(e) {
    world.GetMouseCoords(e)
    //$('#output').text(world.mouseX)
  })
  addEvent(cnvs, 'mousedown', function(e) {
    world.GetMouseCoords(e)
    world.cam.x = world.mouseX
    world.cam.y = world.mouseY
  })
  world.cam = {
    i: 4,
    x: (cnvs.width / 2),
    y: (cnvs.height / 2)
  }
  run()
}

function run() {
  world.runDraw(world.cam.i)
  //world.cam.i+=0.01
  setTimeout(() => {
    run()
  }, 1)
}
window.onload = async function() {
  cnvs = document.getElementById("canvas");
  cnvs2 = document.getElementById("canvas2");
  cnvs3 = document.getElementById("canvas3");
  cnvs.width = 1000;
  cnvs.height = 1000;
  cnvs.style.background = "black";
  cnvs2.width = 1000;
  cnvs2.height = 1000;
  cnvs2.style.background = "black";
  cnvs3.width = 1000;
  cnvs3.height = 1000;
  cnvs3.style.background = "black";
  ctx = cnvs.getContext('2d')
  ctx2 = cnvs2.getContext('2d')
  ctx3 = cnvs3.getContext('2d')
  setup()
  resize()
  window.addEventListener('resize', function(e) {
    resize()
  });
}

function resize() {
  let w = document.documentElement.clientWidth;
  let h = document.documentElement.clientHeight;
  cnvs.style.position = 'absolute';
  cnvs2.style.position = 'absolute';
  cnvs3.style.position = 'absolute';
  cnvs.style.top = '0px'
  cnvs.style.left = '0px'
  cnvs2.style.top = h + 'px'
  cnvs2.style.left = '0px'
  cnvs3.style.top = (h * 2) + 'px'
  cnvs3.style.left = '0px'
  cnvs.style.width = w + "px";
  cnvs.style.height = h + "px";
  cnvs2.style.width = w + "px";
  cnvs2.style.height = h + "px";
  cnvs3.style.width = w + "px";
  cnvs3.style.height = h + "px";
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<canvas id="canvas"></canvas>
<canvas id="canvas2"></canvas>
<canvas id="canvas3"></canvas>
<p id="output" style="position:absolute;top:700px;color:white;"></p>

Do functions within a component remain in memory even after execution ends?

I have a question about the code below Nextjs.

The handleContactClick function is expected to be deleted from memory when the UserDetail component function ends, so I thought it was necessary to use useCallback, but it runs normally.
Are functions within a component maintained in memory even after execution ends?

'use client';

import UserProfileCard from './user-profile-card';

export default function UserDetail() {
  const user = {
    profilePicture: 'profile/1.png',
    name: 'John Doe',
    email: '[email protected]',
  };

  const handleContactClick = () => {
    window.alert('Contact button clicked!');
  };

  return (
    <>
      <h1>user profile</h1>
      <UserProfileCard user={user} onContactClick={handleContactClick} />
    </>
  );
}

jquery UI Calendar date range showing too many months

I have the jquery UI code where i am trying to display 6 months of the current year starting from this months onwards till next 5 months so a total of 6 months, but right now, it goes beyond the those 6 months and showing too many months.

so probably it has two issies:

  1. It shows the range of months but it is showing 6 times the 6 months so a total of 6*6 = 36 months which is wrong.
  2. when i click the next /prev arrows it is hising the jquery UI calendar instead of navigating between the dates.

Here is below y code and also the JSFiddle link

HTML First

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Date and Time Range Selector</title>
    <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
    <style>
        .date-time-range-selector {
            display: flex;
            flex-direction: column;
            align-items: center;
            margin: 20px;
        }
        .calendar-container {
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
        }
        .month-container {
            margin: 10px;
        }
        .time-slider {
            width: 200px;
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <div class="date-time-range-selector">
        <label for="dateTimeRange">Select Date and Time Range:</label>
        <input type="text" id="dateTimeRange" readonly>
        <div id="calendar-popup" style="display: none;">
            <div class="calendar-container"></div>
        </div>
    </div>
    
</body>
</html>

and here is the JS Code

$(function() {
    const $input = $("#dateTimeRange");
    const $calendarPopup = $("#calendar-popup");
    const $calendarContainer = $calendarPopup.find(".calendar-container");
    
    let startDate, endDate, startTime, endTime;

    function updateInputValue() {
        if (startDate && endDate && startTime && endTime) {
            $input.val(`${startDate.toLocaleDateString()} ${startTime} - ${endDate.toLocaleDateString()} ${endTime}`);
        } else if (startDate && startTime) {
            $input.val(`${startDate.toLocaleDateString()} ${startTime} - Select end date`);
        } else {
            $input.val('');
        }
    }

    function createCalendar(date) {
        const $monthContainer = $("<div>").addClass("month-container");
        const $calendar = $("<div>").datepicker({
            defaultDate: date,
            numberOfMonths: [2,3],
            showOtherMonths: true,
            selectOtherMonths: true,
            beforeShowDay: function(date) {
                const isInRange = startDate && endDate && date >= startDate && date <= endDate;
                const isStart = startDate && date.getTime() === startDate.getTime();
                const isEnd = endDate && date.getTime() === endDate.getTime();
                let cssClass = '';
                if (isStart) cssClass = 'in-range';
                if (isEnd) cssClass = 'in-range';
                if (isInRange) cssClass = 'in-range';
                return [true, cssClass];
            },
            onSelect: function(selectedDate, instance) {
                const selected = new Date(instance.selectedYear, instance.selectedMonth, instance.selectedDay);
                if (!startDate || (startDate && endDate) || (startDate && selected < startDate)) {
                    startDate = selected;
                    endDate = null;
                    startTime = '00:00';
                    endTime = null;
                } else {
                    endDate = selected;
                    endTime = '23:59';
                }
                updateInputValue();
                $calendarContainer.find(".ui-datepicker").datepicker("refresh");
            }
        });
        
        const $timeSlider = $("<div>").addClass("time-slider").slider({
            range: true,
            min: 0,
            max: 1440,
            step: 15,
            values: [540, 1020], // 9:00 AM to 5:00 PM
            create: function() {
                const $handles = $(this).find('.ui-slider-handle');
                $handles.each(function(index) {
                    const $tooltip = $('<div class="ui-slider-tooltip">').appendTo(this);
                    $(this).data('tooltip', $tooltip);
                });
                updateTooltips($(this));
            },
            slide: function(event, ui) {
                startTime = minutesToTime(ui.values[0]);
                endTime = minutesToTime(ui.values[1]);
                updateInputValue();
                updateTooltips($(this));
            }
        });

        $monthContainer.append($calendar, $timeSlider);
        return $monthContainer;
    }

    function updateTooltips($slider) {
        const values = $slider.slider('values');
        $slider.find('.ui-slider-handle').each(function(index) {
            const $tooltip = $(this).data('tooltip');
            $tooltip.text(minutesToTime(values[index]));
        });
    }

    function minutesToTime(minutes) {
        const hours = Math.floor(minutes / 60);
        const mins = minutes % 60;
        return `${hours.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}`;
    }

    $input.on("click", function() {
        if ($calendarContainer.children().length === 0) {
            const today = new Date();
            const monthsToShow = 6;
            const startDateOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);

            $calendarContainer.empty();

            for (let i = 0; i < monthsToShow; i++) {
                const date = new Date(startDateOfMonth.getFullYear(), startDateOfMonth.getMonth() + i, 1);
                const $monthContainer = createCalendar(date);
                $calendarContainer.append($monthContainer);

                const $calendar = $monthContainer.find(".ui-datepicker");

                // Attach event handlers manually
                $calendar.find(".ui-datepicker-prev, .ui-datepicker-next").off("click").on("click", function(e) {
                    e.preventDefault();
                    e.stopPropagation();
                    const isNext = $(this).hasClass("ui-datepicker-next");
                    $calendar.datepicker(isNext ? "nextMonth" : "prevMonth");
                });
            }
        }
        $calendarPopup.show();
    });

    $(document).on("click", function(event) {
        if (!$(event.target).closest(".date-time-range-selector").length && 
            !$(event.target).closest(".ui-datepicker-header").length) {
            $calendarPopup.hide();
        }
    });

    // Add clear button
    const $clearButton = $("<button>").text("Clear").click(function() {
        startDate = null;
        endDate = null;
        startTime = null;
        endTime = null;
        updateInputValue();
        $calendarContainer.find(".ui-datepicker").datepicker("refresh");
        $calendarPopup.hide();
    });
    $calendarPopup.append($clearButton);
});

and here is the CSS Code

.ui-datepicker .in-range a,
.ui-datepicker .range-start a,
.ui-datepicker .range-end a {
    background-color: #FF0000;
    color: #FFFFFF;
}

.ui-datepicker .range-start a,
.ui-datepicker .range-end a {
    font-weight: bold;
}

.ui-datepicker-other-month {
    visibility: visible;
}

.ui-datepicker-today {
    background: none !important;
}

and now this is my fiddle

https://jsfiddle.net/c2twojve/

and here is the image how it appears now

enter image description here

How to check whether a value exists in a nested object recursively

Unable to create a fully recursive function that checks if a value exists in a nested object

tried Implementing the code in JavaScript(ES6+) but only managed to do it semi-recursive with a for loop to check the contents of key at a time

function contains(obj, value) {
  for (var key in obj) {
    if (typeof obj[key] === "object") {
      return contains(obj[key], value);
    }

    if (obj[key] === value) {
      return true;
    }
  }
  return false;
}

Please explain the time complexity of below LeetCode solutions in JavaScript

How below code have two different complexities?

I am solving one question of leetcode and while solving I have written one solution which is comparing if a part of string is palindrome or not. But when comparing with customized code of Time Complexity O(N*N) then it exceeds the given time limit (TLE).

But when I check equality of two strings after creating substrings of them then it does not give TLE even if the time complexity is O(N*N), N for outer loop and N for creating substring and comparing them.

Question:

214. Shortest Palindrome

You are given a string s. You can convert s to a palindrome by adding characters in front of it.

Return the shortest palindrome you can find by performing this
transformation.

Example 1:

Input: s = “aacecaaa”

Output: “aaacecaaa”

Example 2:

Input: s = “abcd”

Output: “dcbabcd”

Solution that exceeds the time limit:

var shortestPalindrome = function(s) {
   const n = s.length;
   const rev = s.split("").toReversed().join("");

   let f = (i) => {
       for(let j = 0; j < n-i; j++) {
           if(rev[i+j] != s[j]) {
               return false;
           }
       }

       return true;
   }

   for(let i = 0; i < n; i++) { \ this looping for n times
       if(f(i)) { \ roughly taking n time for comparison
           return rev.substr(0, i) + s;
       }
   }

   return "";
};

Working solution:

var shortestPalindrome = function(s) {
   const n = s.length;
   const rev = s.split("").toReversed().join("");

   for(let i = 0; i < n; i++) { \ this looping for n times
       if(rev.substr(i) == s.substr(0, n-i)) { \ this line must be taking O(N) right?
           return rev.substr(0, i) + s;
       }
   }

   return "";
};

Please explain how these two solutions are different.

splitting js code base into multiple folders inside assets folder with webpack-encore symfony bundler

I am working on a full stack symfony (v7) project in which i’m using Webpack-encore bundle to bundle and compile different assets

the config in bootstrap.js says the following:

import { startStimulusApp } from '@symfony/stimulus-bridge';

const app = startStimulusApp(
    require.context(
        '@symfony/stimulus-bridge/lazy-controller-loader!./controllers',
        true,
        /.[jt]sx?$/
    )
);
// register any custom, 3rd party controllers here
// app.register('some_controller_name', SomeImportedController);

i’ve created a ‘js/’ folder in which i want to include all js related stuffs (matter to have an organized js code base) but when i import stuffs from /js/log.js, for example, webpack watching process from terminal says:

 ERROR  Failed to compile with 1 errors                                                                                                                                                                         

Module build failed: Module not found:
"./assets/app.js" contains a reference to the file "js/log.js".

i tried to make a workaround with the regex in the bootstrap.js to track all js files in any XXX folder in /assets folder
but nothing works

i appreciate any help/suggestions

here the tree of the project so far:

.
β”œβ”€β”€ assets
β”‚   β”œβ”€β”€ app.js
β”‚   β”œβ”€β”€ bootstrap.js
β”‚   β”œβ”€β”€ controllers
β”‚   β”‚   └── hello_controller.js
β”‚   β”œβ”€β”€ controllers.json
β”‚   β”œβ”€β”€ js
β”‚   β”‚   └── log.js
β”‚   └── styles
β”‚       └── app.css
β”œβ”€β”€ bin
β”‚   β”œβ”€β”€ console
β”‚   └── phpunit
β”œβ”€β”€ composer.json
β”œβ”€β”€ composer.lock
β”œβ”€β”€ config
β”œβ”€β”€ migrations
β”œβ”€β”€ package.json
β”œβ”€β”€ package-lock.json
β”œβ”€β”€ phpstan.neon
β”œβ”€β”€ phpunit.xml.dist
β”œβ”€β”€ public
β”‚   β”œβ”€β”€ build
β”‚   └── index.php
β”œβ”€β”€ README.md
β”œβ”€β”€ src
β”œβ”€β”€ symfony.lock
β”œβ”€β”€ templates
β”œβ”€β”€ tests
β”œβ”€β”€ translations
└── webpack.config.js

How to add class “active” on element when another element is clicked?

So I have a layout with 4 different tabs, when each tab is clicked, it populates information below it. Right now, when I click on the arrow, it moves up or down based on which tab you’re on. but I want the arrows to move when the entire tab is clicked on, not just the arrow. How do I set that up? Would I set an event listener up on the tab-button, to add the the class active to the dropdown-trigger ?

Here is my code:

//Tab toggle for full page modal
const workMarketToggle = document.querySelector("#tab-toggle--workmarket");
const wmButton = document.querySelector("button");
const tabs = document.querySelector(".wrapper");
const tabButton = document.querySelectorAll(
  ".tab-button, .toggle-text, .toggle-img"
);
const contents = document.querySelectorAll(".content");
tabs &&
  tabs.addEventListener("click", (e) => {
    const button = e.target.closest("button");
    if (!button) return;

    contents.forEach((content) => content.classList.remove("active"));
    tabButton.forEach((btn) => btn.classList.remove("active"));

    button.classList.add("active");
    const element = document.getElementById(button.dataset.id);
    element.classList.add("active");
  });

// Caret Dropdown for tab toggle in Full Page Modal
const caretDropdown = document.querySelectorAll(
  "#dropdown-trigger .caret-dropdown"
);
const buttonNavigation = document.querySelector(".buttonWrapper");
buttonNavigation.addEventListener("click", handleClick);
function handleClick(e) {
  if (e.target.matches("#dropdown-trigger .caret-dropdown")) {
    caretDropdown.forEach((dropdown) => dropdown.classList.remove("active"));
    e.target.classList.add("active");
  }
}
#tab-toggle--workmarket .container,
#tab-toggle--profservices .container {
  margin: 30px auto;
}

#tab-toggle--workmarket #tab1,
#tab-toggle--workmarket #tab2,
#tab-toggle--workmarket #tab3,
#tab-toggle--workmarket #tab4,
#tab-toggle--workmarket #tab5,
#tab-toggle--workmarket #tab6,
#tab-toggle--profservices #tab1,
#tab-toggle--profservices #tab2,
#tab-toggle--profservices #tab3,
#tab-toggle--profservices #tab4,
#tab-toggle--profservices #tab5,
#tab-toggle--profservices #tab6 {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  gap: 50px;
  padding-top: 50px;
}

#tab-toggle--workmarket .wrapper,
#tab-toggle--profservices .wrapper {
  max-width: 1330px;
  margin: auto;
  border-radius: 10px;
}

#tab-toggle--profservices .buttonWrapper {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  background-color: #20347d;
  border-radius: 10px;
  height: 81px;
  max-width: 848px;
  margin: 0 auto;
  position: relative;
  z-index: 12;
}

#tab-toggle--profservices .no-bg {
  background-color: #eff5ff;
  height: auto;
}

#tab-toggle--profservices .contentWrapper {
  max-width: 1220px;
  margin: 0 auto;
}

#tab-toggle--workmarket button.tab-button,
#tab-toggle--profservices button.tab-button {
  font-family: var(--font-family-base);
  color: #fff;
}

.tab-button.tab-button-img {
  background-color: #eff5ff !important;
  height: 100% !important;
}

#tab-toggle--profservices button.tab-button {
  border: none;
  padding: 10px;
  background-color: #20347d;
  color: #fff;
  font-size: 18px;
  cursor: pointer;
  transition: 0.5s;
  border-radius: 10px;
  width: 202px;
  height: 61px;
  margin: 0 20px;
}

#tab-toggle--workmarket button:hover,
#tab-toggle--profservices button:hover {
  background-color: #d5e3ff;
}

#tab-toggle--workmarket button.active,
#tab-toggle--profservices button.active {
  background-color: white;
  margin: 0 20px;
}

#tab-toggle--workmarket button:hover,
#tab-toggle--workmarket button.active,
#tab-toggle--profservices button:hover,
#tab-toggle--profservices button.active {
  color: #000;
}

#tab-toggle--profservices button:hover,
#tab-toggle--profservices button.active {
  width: 202px;
  color: #33478c;
}

#tab-toggle--workmarket .active,
#tab-toggle--profservices .active {
  background-color: #f3f4f6;
}

#tab-toggle--workmarket .content,
#tab-toggle--profservices .content {
  display: none;
  padding: 10px 20px;
}

#tab-toggle--profservices .content-regular.active {
  display: flex;
  justify-content: center;
  padding: 70px 20px;
  align-items: center;
  gap: 50px;
  background-color: #fff;
  border-radius: 10px;
  margin: 0px;
  box-shadow: rgba(0, 0, 0, 0.14) 0px 3px 15px;
}

#tab-toggle--profservices .content.active {
  display: flex;
  justify-content: center;
  padding: 70px 20px;
  align-items: center;
  gap: 50px;
  background-color: #fff;
  border-radius: 10px;
  margin: -30px;
  box-shadow: rgba(0, 0, 0, 0.14) 0px 3px 15px;
}
#dropdown-trigger {
  display: block;
}
.caret-dropdown {
  cursor: pointer;
  top: 50%;
  transform: translateY(-50%);
  display: inline-block;
  height: 10px;
  left: 15px;
  margin-top: 2px;
  position: relative;
  text-align: left;
  transition: 0.4s ease;
  transform: rotate(0);
  width: 13px;
}
.caret-dropdown:after,
.caret-dropdown:before {
  background-color: transparent;
  border-bottom: 11px solid #444;
  box-sizing: content-box;
  content: "";
  display: inline-block;
  height: 8px;
  left: 0;
  position: absolute;
  top: 0;
  transition: 0.4s ease;
  width: 3px;
}
.caret-dropdown:before {
  transform: rotate(-135deg);
}
.caret-dropdown:after {
  transform: rotate(135deg);
}
.caret-dropdown.active {
  transform: rotate(0);
  transform: translate(0, -6px);
}
.caret-dropdown.active:before {
  transform: rotate(-45deg);
}
.caret-dropdown.active:after {
  transform: rotate(45deg);
}
<!-------- TAB TOGGLE SECTION -------->
<div class="bg-lightblue">
  <div id="tab-toggle--profservices">
    <div class="wrapper">
      <div class="buttonWrapper no-bg gap-100">
        <button class="tab-button tab-button-img active" data-id="implementation">

          <img src="#" width="150" class="toggle-img" />
          <h3 class="blue toggle-text">Implementation</h3>
             <a href="#" id="dropdown-trigger"
                  ><span class="caret-dropdown"></span
                ></a>
        </button>
        <button class="tab-button tab-button-img" data-id="advisory">
   
          <img
            src="#" width="150" class="toggle-img" >
          <h3 class="blue toggle-text">Advisory</h3>
              <a href="#" id="dropdown-trigger"
                  ><span class="caret-dropdown"></span
                ></a>
        </button>
        <button class="tab-button tab-button-img" data-id="integration">
          
                <img
                  src="#"
                  width="150"
                  class="toggle-img"
                />
                <h3 class="blue toggle-text">Integration</h3>
                    <a href="#" id="dropdown-trigger"
                  ><span class="caret-dropdown"></span
                ></a>
              </button>
        <button class="tab-button tab-button-img" data-id="transformation">
      
                <img
                  src="#"
                  width="150"
                  class="toggle-img"
                />
                <h3 class="blue toggle-text">Transformation</h3>
                    <a href="#" id="dropdown-trigger"
                  ><span class="caret-dropdown"></span
                ></a>
              </button>
      </div>
      <div class="contentWrapper">
        <div class="content content-regular active" id="implementation">
          <div class="pf-two-col-1">
            <p class="deep-red wfn-caps bold pb-0 mb-0">Info 1</p>
            <h2 class="pt-0 mt-0">
              information here
            </h2>
            <ul class="profservices">
              <li>
                list 1
              </li>
              <li>list 2</li>
              <li>
                list 3
              </li>
            </ul>
          </div>
          <div class="pf-two-col-2">
            <img src="#" class="pf-img-col2" skiplazy="" />
          </div>
        </div>
        <div class="content" id="advisory">
          <div class="pf-two-col-1">
            <p class="deep-red wfn-caps bold pb-0 mb-0">info 2</p>
            <h2 class="pt-0 mt-0">
              information here
            </h2>
            <ul class="profservices">
              <li>
                list 1
              </li>
              <li>list 2</li>
              <li>
                list 3
              </li>
            </ul>
          </div>
          <div class="pf-two-col-2">
            <img src="#" class="pf-img-col2" skiplazy="" />
          </div>
        </div>
        <div class="content" id="integration">
          <div class="pf-two-col-1">
            <p class="deep-red wfn-caps bold pb-0 mb-0">info 3</p>
            <h2 class="pt-0 mt-0">
              information here
            </h2>
            <ul class="profservices">
              <li>
                info 1
              </li>
              <li>
                info 2
              </li>
              <li>
                info 3
              </li>
            </ul>
          </div>
          <div class="pf-two-col-2">
            <img src="#" class="pf-img-col2" skiplazy="" />
          </div>
        </div>
        <div class="content" id="transformation">
          <div class="pf-two-col-1">
            <p class="deep-red wfn-caps bold pb-0 mb-0">info 4</p>
            <h2 class="pt-0 mt-0">
              information here
            </h2>
            <ul class="profservices">
              <li>
                list 1
              </li>
              <li>list 2</li>
              <li>
                list 3
              </li>
            </ul>
          </div>
          <div class="pf-two-col-2">
            <img src="#" class="pf-img-col2" skiplazy="" />
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

Component does not redraw on keypress in React

I have a group of components added to the parent as subcomponents. Each of those sub components has a number of variables and are contained in a JSON. When I use the arrow keys I can update the location of one of the children (Desks) but that update will not be drawn on the page until I refresh.

What can I do to force an update on every keypress? The problem is, I believe, because I am using objects so the address of each object is not changes and a redraw is not triggered.

I have tried using Math.random() in the state to force a redraw but with no success.

How can this be modified to allow a redraw to take place on every keypress?

/* eslint-disable no-use-before-define */
import React, { useState, useRef, useContext, useEffect, useCallback } from 'react';
import { useQuery } from 'react-query';
import useKeypress from 'react-use-keypress';

import { Loading } from '../Elements/LoadingComponent';
import { Row, Col } from 'react-bootstrap';
import AddDesks from '../Elements/Layout/AddDesks';
import Status from '../Elements/Layout/Status';
import { Desk } from '../Elements/Layout/Desks';
import { SiteMapContext } from '../../App';

import {
    Layout_Get_Sites,
    Layout_Get_Desk_Types,
    Layout_Update_Desk_Data,
    Layout_Create_Desk,
    Layout_Set_UserImage,
} from '../../network/Layout_Creator';
import '../../shared/styles/layout.css';

const LayoutMap = (props) => {
    const mapRef = useRef();
    const mapContainer = useRef();
    const desksRef = useRef([]);
    const siteMap = useContext(SiteMapContext);
    let roles = siteMap.siteMapData.user.roles;
    const isAdmin = roles.indexOf('admin') + roles.indexOf('system') > 0
    let siteCount = 0;

    const [mapScale, setMapScale] = useState();
    const [unused, rerender] = useState(Math.random());
    const [desks, setDesks] = useState({ past: [], present: [], future: [] });
    const [currentDesk, setDesk] = useState({ index: -1, desk: null });
    const [currentMap, setCurrentMap] = useState(-1);
    const [deskTypes, setDeskTypes] = useState(-1);
    const [maps, setMaps] = useState({ maps: [] });
    const [editMode, setEditMode] = useState(false);
    //const [deskComponents, setDeskComponents] = useState(null)
    const [mapImage, setMapImage] = useState(`data:image/jpeg;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==`);
    const { isLoading: areMapsLoading, refetch } = useQuery('LayoutMaps', () => Layout_Get_Sites(siteCount),
        {
            staleTime: Infinity,
            refetchInterval: false,
            refetchOnMount: false,
            refetchOnReconnect: false,
            refetchOnWindowFocus: false,
            onSuccess: (data) => {
                let map = data.maps;
                if (map) {
                    let newMaps = [...maps.maps, map];
                    setMaps({ maps: newMaps })
                    siteCount = newMaps.length;

                }
                if (!data.done) refetch();

            },
        })

    useEffect(() => {
        const loadDeskTypes = async () => {
            let dt = await Layout_Get_Desk_Types();
            setDeskTypes(dt);
        }
        window.addEventListener('resize', updateSize);
        loadDeskTypes();
        return () => {
            window.removeEventListener('resize', updateSize);
        }
    }, [])



    useEffect(() => {
        var img = `data:image/jpeg;base64,${maps.maps[currentMap]?.SiteBackground ?? 'R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='}`
        setMapImage(img);
    }, [currentMap])

    useEffect(() => {

        setDesks({ ...desks, present: maps.maps[currentMap]?.desks });
        var img = `data:image/jpeg;base64,${maps.maps[currentMap]?.SiteBackground ?? 'R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='}`
        setMapImage(img);
    }, [editMode])

    useEffect(() => {
        setDesks({ ...desks, present: maps.maps[currentMap]?.desks });
    }, [mapScale]);


    const updateSize = () => {
       //TODO Not implemented yet
    }

    //called when location Select is used
    const changeMap = target => {
        console.log("Layout.changeMap");
        setDesk({ index: -1, desk: null });
        const map = parseInt(target.value);
        setCurrentMap(map);
        setEditMode(false);
    }


    const clickMap = () => {
        clearCurrent();
        setDesk({ index: -1, desk: null });
    }

    const createDesk = async (newDesk) => {
        Layout_Create_Desk(newDesk)
            .then(data => {
                let newDesks = data.desks;
                setDesks({ past: desks.present, present: newDesks, future: [] });
            });
    }

    const updateDesk = (desk) => {
        let id = desk.id;
        let index = desks.present.findIndex(d => d.id === id);
        let presentDesks = desks.present;
        let newDesks = presentDesks;
        newDesks[index] = desk;
        setDesks({ past: presentDesks, present: newDesks, future: [] });
        setDesk({ index: index, desk: desk })
        Layout_Update_Desk_Data(desk);
    }

    const setUserImage = (file) => {
        Layout_Set_UserImage(file, currentDesk)
    }


    const clearCurrent = () => {
        for (let i = 0; i < desksRef.current.length; i++) {
            if (desksRef.current[i] && desksRef.current[i].img().style) {
                desksRef.current[i].img().style.border = "none";
            }
        };
    }

    const setCurrentDesk = (index) => {
        clearCurrent();

        let desk = desksRef.current[index];
        desk.img().style.border = '2px solid red';
        setDesk({ index: index, desk: desk });
    }


    const buildMapOption = (site, index) => {
        var ret = <option value={index} key={site.id}>{site.SiteName}</option>
        return ret;
    }

    const buildMapSelector = () => {
        var ret =
            <select onChange={(e) => changeMap(e.target)} value={currentMap}>
                <option id='-1'>Select Site...</option>
                {
                    maps.maps.map((site, index) => buildMapOption(site, index))
                }
            </select>
        return ret;
    }

    const getScale = () => {
        const site = maps.maps[currentMap] //the id on the select
        if (!site) return;
        let map = mapRef.current;

        let rect = map?.getBoundingClientRect();

        let mapWidth = rect.width;
        let mapHeight = rect.height;
        let scaleW = (mapWidth && site?.wMM) ? (mapWidth / site.wMM) : 1;
        let scaleH = (mapHeight && site?.hMM) ? (mapHeight / site.hMM) : 1;
        setMapScale({ height: scaleH, width: scaleW });
    }

    const buildDesk = (desk, index) => {
        const res =
            <Desk key={desk.id}
                desk={desk}
                isEditing={editMode}
                desks={desks.present}
                scale={mapScale}
                deskTypes={deskTypes}
                currentDesk={currentDesk.index}
                fns={DeskFns}
                ref={el => desksRef.current[index] = el}
                index={index}
                
                rdm={Math.random()}
            />
        return res;
    }
    
    useKeypress(['ArrowLeft', 'ArrowUp', 'ArrowRight', 'ArrowDown'], (e) => {
    //const keyDown = useCallback((e) => {
        e.preventDefault();
        if (currentDesk.index > -1 && editMode === true) {

            var key = e.key;

            const desk = desks.present[currentDesk.index];
            switch (key) {
                case 'ArrowLeft': //left
                    desk.x = Math.round(desk.x - (5 / mapScale.width));
                    break;
                case 'ArrowUp': //up
                    desk.y = Math.round(desk.y - (5 / mapScale.height));
                    break;
                case 'ArrowRight': //right
                    desk.x = Math.round(desk.x + (5 / mapScale.width));
                    break;
                case 'ArrowDown'://down
                    desk.y = Math.round(desk.y + (5 / mapScale.height));
                    break;
                default: break;
            }
            updateDesk(desk);
            desksRef.current[currentDesk.index].redraw(desk.x, desk.y);
            rerender(Math.random())
            
        }
    }, [currentDesk, editMode]);


    const showStatus = () => {
        return (currentDesk.index > -1) ? <Status currentDesk={currentDesk} desks={desks.present} />
            : null
    }

    const AddDesksFns = {
        Update_Desklist: setDesks,
        Create_Desk: createDesk
    }

    const DeskFns = {
        Update_Desklist: setDesks,
        Update_Desk_Data: updateDesk,
        Set_UserImage: setUserImage,
        Set_Current: setCurrentDesk,
        Get_Current: () =>  currentDesk > -1 ? desks.present[currentDesk].id : -1 ,

        //redraw: redraw,
    }
    //onKeyDown = { keyDown }
    //    Press_Key: keyDown,
    
    return (
        <div>
            <Row>
                <Col sm={1}>
                    {(isAdmin && !areMapsLoading) ?
                        <span style={{ 'whiteSpace': 'nowrap', 'backgroundColor': '#ccc', 'visibility': `${currentMap !== -1 ? 'visible' : 'hidden'}` }}>
                            <label htmlFor='layoutEditSelect'>Edit</label>&nbsp;
                            <input id='layoutEditSelect' type='checkbox'
                                onClick={async (e) => { setEditMode(e.target.checked); }}
                            />
                        </span>
                        : <span></span>
                    }
                </Col>
                <Col sm={10}>
                    {(editMode && currentDesk != null) ?
                        <div style={{ position: 'absolute', top: '100px', left: '300px' }}>

                            <Row>
                                <Col>Left</Col><Col>{Math.round(currentDesk.x)}</Col>
                                <Col>Height</Col><Col>{currentDesk.height}</Col>
                            </Row>
                            <Row>
                                <Col>Top</Col><Col>{Math.round(currentDesk.y)}</Col>
                                <Col>Width</Col><Col>{currentDesk.width}</Col>
                            </Row>
                            <Row>
                                <Col>Rotation</Col>
                                <Col>{currentDesk.rotation}&deg;</Col>
                            </Row>

                        </div>
                        : ''
                    }
                </Col>
            </Row>
            <Row>
                <Col sm={1}>
                    <Row>
                        <Col>
                            {!areMapsLoading ?
                                buildMapSelector()
                                :
                                <Loading title="Map Data" />
                            }
                        </Col>
                        <Col>
                            <AddDesks
                                maps={maps}
                                deskTypes={deskTypes}
                                editMode={editMode}
                                scale={mapScale}
                                fns={AddDesksFns}

                            />

                        </Col>
                    </Row>
                </Col>
                <Col sm={10} id="Layout_Map_Container" ref={mapContainer} >
                    {/* <BuildMap map={maps.maps[props.maps.current]} fns={props.MapFunctions} scale={props.scale} /> */}
                    {/*onClick={clickDesk}*/}
                    <div ref={mapContainer}
                        style={{ width: '100%', height: `${window.height}px`, position: 'relative' }}
                        onClick={() => clickMap()}
                        onLoad={() => { getScale(); clickMap(); } /*If map is reset, set current desk to null*/}
                        className='map'
                        id="Layout_SiteMap_Img"
                    >
                        {/**/}
                        <img src={mapImage}
                            style={{ width: '100%', position: 'absolute' }}
                            alt={`Layout plan for ${maps.maps[currentMap]?.currentSite?.SiteName}`}
                            ref={mapRef}
                            id="floorMap"
                            onLoad={getScale}
                        />
                        <React.Fragment>
                            {//deskComponents
                                mapScale? maps.maps[currentMap]?.desks.map((desk, index) => buildDesk(desk, index)):''
                            }
                        </React.Fragment>
                    </div>

                </Col>
            </Row >
            {showStatus()}
        </div>
    )
}

export default LayoutMap;
}
import React, { forwardRef, useState, useImperativeHandle, useRef, useEffect } from 'react';
import { useQuery } from 'react-query';
import { Row, Col } from 'react-bootstrap';
import { Droppable } from '../Droppable';
import ContentEditable from 'react-contenteditable';
import Draggable from 'react-draggable';

import {
    Layout_Get_Desk_Types,

} from '../../../network/Layout_Creator';

export const Desk = forwardRef(({ desk, isEditing, scale, deskTypes, fns, index }, ref) => {

    
    const imgRef = useRef(null);
    const [rotation, setRotation] = useState(desk?.rotation ?? 0);
    const [top, setTop] = useState(0);
    const [left, setLeft] = useState(0);
    const [unused, rerender] = useState(Math.random())
    const [size, setSize] = useState(() => {

        let deskImg = null;
        var dImg = deskTypes?.find(dsk => dsk.deskType === desk.deskType);

        //desk top * scale gives top in MM, multiply by map scale
        let top = parseInt(desk.y * scale.height);
        const left = parseInt(desk.x * scale.width);
        let width = 0;
        let height = 0;
        try {
            if (dImg) {
                deskImg = dImg.deskImage;
                
                width = parseInt(dImg.wMM * scale.width);
                height = parseInt(dImg.hMM * scale.height);
            }

            let imgStyle = {
                boxSizing: "border-box",
                width: `${width}px`,
                height: `${height}px`,
            }
            //position: 'absolute'

            const url = `data:image/jpeg;base64,${deskImg}`;
            setTop(top ?? 0);
            setLeft(left ?? 0);
            return { url: url, style: imgStyle };
        }
        catch (ex) {
            console.log(ex);
        }

        //return base coords if full data not available
        return { left: left, top: top };
    });


    
    const handleImageClick = (e) => {
    e.stopPropagation();
    fns.Set_Current(index);
};

const handleImageDoubleClick = () => {
    if (isEditing) {

        setRotation((prevRotation) => {
            let newRotation = prevRotation + 90
            fns.Update_Desk_Data({ ...desk, rotation: newRotation })
            return newRotation;
        });
    }
};

const handleDrag = (e, ui) => {

    // Handle drag functionality
    const { x, y } = ui;
    //console.log("Moving ", ui)
    // Update image position
};

const handleDragComplete = (e, { lastX, lastY }) => {

    //convert back to a MM basis
    let newX = Math.round(lastX / scale.width);
    let newY = Math.round(lastY / scale.height);

    fns.Update_Desk_Data({ ...desk, x: newX, y: newY })
    /*
   deltaX: 0
   deltaY: 0
   lastX: 444
   lastY: 206
9       x:444
   y: 206
   */
}

//const handleKey = (e) => {
//    e.preventDefault()
//    fns.Press_Key(e, index);    //}


useImperativeHandle(ref, () => ({
    redraw: (x, y) => {
        setLeft(Math.round(x * scale.width));
        setTop(Math.round(y * scale.height));
        rerender(Math.random())
    },
    img: () => imgRef.current
}));


return (

    <Draggable
        key={desk.id} // **Force re-render when desk.id changes**
        defaultPosition={{ x: left, y: top }}
        onDrag={handleDrag}
        onStop={handleDragComplete}
        disabled={!isEditing} // Ensure editing state is passed correctly
    >
        <div >

            <img ref={imgRef}
                alt={`{DeskID:${desk.id} }`}
                src={size.url}
                style={{
                    ...size.style,
                    position: 'absolute',
                    transform: `rotate(${rotation}deg)`,
                    cursor: 'move',
                }}
                onClick={(e) => handleImageClick(e)} // Ensure clicks are handled
                onDoubleClick={handleImageDoubleClick} // Ensure double clicks are handled
            />
        </div>
    </Draggable>

)
});


https://codesandbox.io/p/sandbox/xenodochial-pine-t6ccl8