Jitter problem with differential line growth

I’m trying to create a smooth animation using differential line growth.
I can have a snake on the screen grow and fill the space which I want.

It’s made up out of a number of nodes with two forces working on them (cohesion and separation)

It grows and moves as expected but it never settles down, once it fills the space certain particles (usually ones in a straight line) will jitter and jump up and down at a high speed.

I’m sure this is a common problem but I can’t figure out how to fix it.
I would like the snake to settle once it’s fully filled the space but need to fix the jittering first.

here is all the relevant code which will recreate the problem if you press play.
https://editor.p5js.org/spaciousmind/sketches/wNp-8jyop

How to display each part of a comma-separated string on a new line (or table) in HTML using JavaScript?

I’m trying to split a comma-separated string in JavaScript and display each part on a new line within an HTML page. I have the following code:

<head>
    <script>
        const testString = "Hello,World";
    </script>
</head>
<body>
    <div id="output"></div>

    <script>
        const parts = testString.split(',');
        document.getElementById('output').innerHTML = `
            <p>${parts[0]}</p>
            <p>${parts[1]}</p>
        `;
    </script>
</body>

It works in some HTML editors but not others.

I want to make it more dynamic, so it can handle any number of comma-separated values. How can I modify this code to iterate over the split parts and display each one on a new line or all values in a new row in a table, regardless of how many parts there are?

It should work in HTMLFiddle.

how to grind farcaster

how to grind farcaster

to someoen tell medvsaf
affdsfafaasfefaehow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefaehow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefaehow to grind farcasterhow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefaehow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefae

to someoen tell medvsaf
affdsfafaasfefaehow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefaehow to grind farcasterhow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefaehow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefae

to someoen tell medvsaf
affdsfafaasfefae
to someoen tell medvsaf
affdsfafaasfefaehow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefaehow to grind farcasterhow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefaehow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefae

to someoen tell medvsaf
affdsfafaasfefae
to someoen tell medvsaf
affdsfafaasfefaehow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefaehow to grind farcasterhow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefaehow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefae

to someoen tell medvsaf
affdsfafaasfefae
to someoen tell medvsaf
to someoen tell medvsaf
affdsfafaasfefaehow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefaehow to grind farcasterhow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefaehow to grind farcaster

to someoen tell medvsaf
affdsfafaasfefae

to someoen tell medvsaf
affdsfafaasfefaewdfrwsefs
affdsfafaasfefae

How to programmatically zoom in on a webpage and improve resolution (like Ctrl + does)?

In some webpages where there is a <canvas> element, when i tried every single methode of mking the browser bigger, the page bigger… I did found some methodes that will make everything big, but the I check the canvas element it’s like it still the same resolution just got bigger, and in some cases it even stay in the same size. But when I try manual Keyboard shortcut Ctrl +, the page zooms in adn the canvas element get a good resolution even if its bigger (i don’t know why, maybe it got reloaded in the backend).
I also tried the built-in Keyboard actions in Playwright Python, but seems to do nothing I don’t know why.
So after that I used PyAutoGui to simullate the user clicking Ctrl +, but the disadvantage of this solution is the PyAutoGui will zoom in in whatever page your on, no matter if its chrome or no, so you need to stay on chrome when executing the scrapping code.
Is there a reliable way to programmatically trigger the same effect as Ctrl + —specifically one that improves the resolution of elements—using automation tools like Playwright, or perhaps through browser APIs?

Parallax Image Responsive Resize

I have a parallax page that works (well enough for now) on computer monitors: the images heights are 100vh and the width is 100%. When I look at the same page on a phone the parallax itself is working, but the images heights are no longer 100vh, instead they are quite small. The 100vh height is priority, so I don’t mind if the sides of the images get cut off, but I can’t seem to figure out how to do it.
Here’s what I have been working with:

<div class="hppblock">
  <img src="/Collection.jpg" data-speed="2" class="img-parallax">
  <h1>Collection</h1>
</div>
<div class="hppblock">
  <img src="/Single.jpg" data-speed="1.5" class="img-parallax">
  <h1>Bespoke Gallery</h1>
</div>
<div class="hppblock">
  <img src="/Detail.jpg" data-speed="1" class="img-parallax">
   <h1>Detail</h1>
</div>

With this JS (though I don’t this is affecting the issue one way or the other)

$('.img-parallax').each(function(){
  var img = $(this);
  var imgParent = $(this).parent();
  function parallaxImg () {
    var speed = img.data('speed');
    var imgY = imgParent.offset().top;
    var winY = $(this).scrollTop();
    var winH = $(this).height();
    var parentH = imgParent.innerHeight();
    var winBottom = winY + winH;

    if (winBottom > imgY && winY < imgY + parentH) {
          var imgBottom = ((winBottom - imgY) * speed);
          var imgTop = winH + parentH;
          var imgPercent = ((imgBottom / imgTop) * 100) + (50 - (speed * 50));
    }
    img.css({
      top: imgPercent + '%',
      transform: 'translate(-50%, -' + imgPercent + '%)'
    });
  }
  $(document).on({
    scroll: function () {
      parallaxImg();
    }, ready: function () {
      parallaxImg();
    }
  });
});

And the CSS:

.hppblock{
  width: 100%; /*I've also tried width: auto*/
  height: 100dvh;
  position: relative;
  overflow: hidden;
}
.hppblock h1{
  position: relative;
  display: block;
  text-align: center;
  color: #D0BFAD;
  font-size:7.5vw;
  margin: 0;
  top: 50%;
  transform: translateY(-50%);
}
.img-parallax {
  width: 100vmax;
  z-index: -1;
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%,0);
  pointer-events: none
}

I tried messing around with these settings, too, but things got wonky fast:

background-position: center;
background-repeat: no-repeat;
-webkit-background-size: 100%; 
-moz-background-size: 100%; 
-o-background-size: 100%; 
background-size: 100%; 
-webkit-background-size: cover; 
-moz-background-size: cover; 
-o-background-size: cover; 
background-size: cover;

And this also wonkified everything:

@media screen and (max-width: 800px) {
    .hppblock{
        object-fit: cover;
        display: flex;
        background-position: center;
        background-repeat: no-repeat;
        justify-content: center;
        align-items: center;
        text-align: center;
        min-height: 100vh;
}

Any help would be greatly appreciated, as I feel like I’m going around in circles.
As seen on a phone screen.

How to use dagre (or similar graph library) to construct only the edges given fixed node positions?

I need to draw a graph whose nodes’ positions and sizes are predetermined (from an external source) and cannot be changed. It is straightforward to draw the nodes using a library like d3. The difficulty arises in drawing the edges. I understand that libraries like dagre have functionality to figure out the layout of nodes and edges and make the graph look presentable. But for me, the challenge is in using dagre to work with fixed node positions and make it compute only the edge paths.

Here is a small working example of use dagre + d3 to draw a simple graph represented by an adjacency list.

// Simple adj list for my graph
const adjList = {
    "A": ["B", "C"],
    "B": ["D"],
    "C": [],
    "D": []
};

// Fixed positions.
// This will be from an external source which CANNOT be changed.
// I can't figure out to pass this to dagre's layout engine.
const fixedPositions = {
  A: { x: 300, y: 50 },
  B: { x: 150, y: 150 },
  C: { x: 450, y: 150 },
  D: { x: 100, y: 250 }
};

const g = new dagre.graphlib.Graph();
g.setGraph({});
g.setDefaultEdgeLabel(function() { return {}; });

// Adidng nodes and edges to  graph
Object.keys(adjList).forEach(node => {
    g.setNode(node, { label: node, width: 50, height: 30 });
});

Object.entries(adjList).forEach(([node, edges]) => {
    edges.forEach(target => {
        g.setEdge(node, target);
    });
});

// Run the layout algorithm (this part computes the layout considering everything and can't be forced to work with my fixed positions)
dagre.layout(g);

// Create the SVG
const svg = d3.select("#graph")
    .append("svg")
    .attr("width", "100%")
    .attr("height", "100%");

const inner = svg.append("g");

// zoom behavior
const zoom = d3.zoom().on("zoom", function(event) {
    inner.attr("transform", event.transform);
});
svg.call(zoom);

// Create nodes
const nodes = inner.selectAll(".node")
    .data(g.nodes())
    .enter()
    .append("g")
    .attr("class", "node")
    .attr("transform", function(v) {
        const node = g.node(v);
        return `translate(${node.x}, ${node.y})`;
    });

// draw rectangles for nodes
nodes.append("rect")
    .attr("width", function(v) { return g.node(v).width; })
    .attr("height", function(v) { return g.node(v).height; })
    .attr("x", function(v) { return -g.node(v).width / 2; })
    .attr("y", function(v) { return -g.node(v).height / 2; });

// print labels for nodes
nodes.append("text")
    .attr("text-anchor", "middle")
    .attr("dominant-baseline", "central")
    .text(function(v) { return v; });

// Create edges (this will use dagre's layout below)
const line = d3.line()
    .x(d => d.x)
    .y(d => d.y)
    .curve(d3.curveBasis);

const edges = inner.selectAll(".edge")
    .data(g.edges())
    .enter()
    .append("path")
    .attr("class", "edge")
    .attr("d", function(e) {
        const edge = g.edge(e);
        return line(edge.points);
    });


const graphBounds = inner.node().getBBox();
const width = svg.node().clientWidth;
const height = svg.node().clientHeight;
const scale = Math.min(width / graphBounds.width, height / graphBounds.height) * 0.9;
const translate = [
    (width - graphBounds.width * scale) / 2 - graphBounds.x * scale,
    (height - graphBounds.height * scale) / 2 - graphBounds.y * scale
];

svg.call(zoom.transform, d3.zoomIdentity
    .translate(translate[0], translate[1])
    .scale(scale));
#graph {
    width: 100%;
    height: 600px;
    background-color: #f9f9f9;
}
.node rect {
    stroke: #333;
    fill: #fff;
    stroke-width: 1px;
}
.node text {
    font: 12px sans-serif;
}
.edge {
    stroke: #333;
    stroke-width: 1px;
    fill: none;
}
<!DOCTYPE html>
<html>
<head>
    <title>Simple Graph</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <script src="https://unpkg.com/@dagrejs/[email protected]/dist/dagre.js"></script>
</head>
<body>
    <div id="graph"></div>
</body>
</html>

My only problem with this is that there doesn’t seem to be a way to pass my fixedPositions to force it to use them for placing nodes, and then do the layout for just the edges.

    const fixedPositions = {
      A: { x: 300, y: 50 },
      B: { x: 150, y: 150 },
      C: { x: 450, y: 150 },
      D: { x: 100, y: 250 }
    };

    // cannot do
    // dagre.layout(g, fixedPositions);

Is there a way to achieve this either with dagre or some other library? This question asks about this in a theoretical perspective but I’m asking in a more practical sense with the use of available open source libraries or otherwise.

The last resort is to just draw the edges manually using straight lines but then the graph does not look presentable. For example, this might be a result if I manually draw straight line edges for a different (not the only in my code) graph.

enter image description here

Should I use node.js, or PHP for a leaderboard? [closed]

I’m trying to make a leaderboard for a game. I am using JavaScript(and html) for the game itself. I feel like it would be easier to use JS for the server-side, but I use MySQL for databases. So should I use PHP instead? Can node.js connect to MySQL? And, because I am using object-oriented programming for the game itself, should I transmit the data with object-oriented programming?
Edit: I removed some stuff to make the question more focused.

Chrome/Safari blocking JavaScript (user originated) blob URL downloads (sandbox blocking download)

I’m running a simple web application that creates a JSON out of some data the user manipulates in a web UI. The page is served as a GitHub Pages hosted site. What I’m trying to do is to create a download link on that page which I pass through JavaScript’s Blob API’s to create a download URL that is programmatically clicked. The action, as shown below, is initiated by a user initiated click.

While doing this, I am intermittently seeing Chrome (Version 135.0.7049.115 (Official Build) (arm64)) and certain versions of Safari indicate ‘sandbox’ errors when clicking the button. I am not able to reproduce this on the Safari on my machine, but I do know that some folks had issues (ironically, some folks had older versions of Safari so it seems that newer versions are fine).

Error (Chrome):

Download is disallowed. The frame initiating or instantiating the download is sandboxed, but the flag ‘allow-downloads’ is not set. See https://www.chromestatus.com/feature/5706745674465280 for more details.*emphasized text*

As I understand this error, it is detecting that the download is being blocked because Chrome maybe does not think this is user initiated? Not quite sure what I could do to make this work since the event is very much user-triggered?

Application (note: @click directive is being used since I’m using Vue.js – Vue3 specifically):

<script>
    function downloadBtn() {
        const data = generateRandomData();
        const jsonStr = JSON.stringify(data, null, 2);
        const blob = new Blob([jsonStr], { type: 'application/json' });
        const url = URL.createObjectURL(blob);

        const a = document.createElement('a');
        a.href = url;
        a.download = 'data.json';

        document.body.appendChild(a);
        a.click();

        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    }
</script>
<template>
    <button @click="downloadBtn">PRESS ME</button>
</template>

Should I use setImmediate for caching responses in Redis after sending HTTP response?

I’m working on a Node.js + Express app where I use Redis to cache API responses. Here’s a simplified version of my route:

router.get("/get-acadamies", async (req, res) => {
  try {
    const redkey = "acadamy:all";
    const cached = await redisGet(redkey);

    if (cached !== null) {
      return res.json({
        success: true,
        message: "Result fetched from Redis cache",
        data: cached,
      });
    }

    const result = await academiesModel.find({}).limit(1000);

    // Cache result in background
    setImmediate(async () => {
      await redisSet(redkey, result, 3600);
    });

    return res.json({
      success: true,
      message: "Result data fetched from DB successfully",
      data: result,
    });
  } catch (error) {
    return res.json({
      success: false,
      message: "Something went wrong",
      data: [],
    });
  }
});

HTML/CSS Marquee with dragging NO JQUERY

Looking for a pure HTML/CSS solution to replace the deprecated tag.
Must not be in JQuery and have minimal JS (if any at all.)
One last condition: I should be able to move the marquee by dragging it (a scroll bar at the bottom is fine too)

Why are flex items expanding vertically in my wrapped flex container?

I have the following CSS setup using Flexbox:

.container {
  width: 350px;
  height: 500px;
  border: 2px solid black;
  display: flex;
  flex-wrap: wrap;
  align-content: stretch;
  align-items: stretch;
}

.container div {
  width: 80px;
  font-size: 2rem;
  text-align: center;
  background-color: lightblue;
}
  <body>
<div class="container">
  <div class="box" style="background-color: #ff5722">1</div>
  <div class="box" style="background-color: #ff9800">2</div>
  <div class="box" style="background-color: #ffc107">3</div>
  <div class="box" style="background-color: #ffeb3b">4</div>
  <div class="box" style="background-color: #cddc39">5</div>
  <div class="box" style="background-color: #8bc34a">6</div>
</div>
  </body>

When I add multiple items to the container, they wrap into multiple rows as expected. However, each item is stretching vertically, and I want to understand why that happens.

Is the following understanding correct?

When we have wrapping enabled via flex-wrap: wrap, then by default align-content: stretch will kick in. This causes the flex lines (rows) to grow taller to fill the container’s cross-axis (vertical) space. Then align-items: stretch makes the individual items within each line expand to fill the height of their line.

Any clarifications or visual explanations would be really helpful.

Reference

I’ve already read this helpful explanation on the difference between align-content and align-items:
What’s the difference between align-content and align-items?

useQuery with initialData and soft and hard loading states

I have a query that uses initialData, populated by the backend within the document on page load. This way there is no loading state when the page loads. However, I also need to be able to put this query into a hard loading state, where it has no data and goes back to isLoading state.

On page load, initialData is authoritative. However once the query is put back into the hard loading state, initialData should not be used any more, so resetQueries doesn’t work.

useUser should do:

  • On page load, use initialData. Don’t background refresh because the data is known to be valid.
  • Background refreshes should use stale-while-revalidate behavior, use the previous data while refreshing
  • Be able to trigger a hard loading state where stale data is not used,
    the previous data is totally invalid.
function useUser() {
  // Preloaded data from the backend so we can immediately display the user on
  // page load.
  const userJson = JSON.parse(document.getElementById('user')?.textContent || 'null');
  return useQuery({
    queryKey: ['profile'],
    queryFn: async () => {
      const response = await fetch('/api/accounts/me/');
      return await response.json();
    },
    // initial data is fresh, no need to refresh immediately.
    initialData,
    // do background refreshes that don't trigger a hard loading state
    staleTime: 1000 * 60,
  });
}

// This is used somewhere far away from useUser, so there's no way to check the
// loading status of resetUserMutation in the components that use useUser.
function resetUserMutation() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async () => {
      await fetch('...');
      // Here, put useUser into hard loading state, not a background refresh.
      // The cached data in useUser is now totally invalid and should never be
      // used again. initialData should never be used again.
      queryClient.hypotheticalHardReload({ queryKey: ['profile'] });
    },
  });
}

function SomeComponent() {
  // useUser is used all over the code. resetUserMutation is not used here.
  const userQ = useUser();
  if (userQ is in hard loading state) {
    // we can't display anything but a loading indicator in this state
    return <div className="loading"></div>;
  } else {
    // we can display stuff during regular background refreshes
    return <div>{userQ.data.username}</div>;
  }
}

What use to create a custom Shap for a Card

Sometimes I scrolldown on the reels in instagram and see this type of complex shape in designs on Figma, I can do this on figma, but I want to know how I can create this shape in WebApp, with React (Note.: I dont want use a specific lib of React, I need do natively, like HTML,CSS and JS basics)

(I’m not asking anyone to do it for me — I just need some direction to start, because I don’t know how people usually do it.)

I want too, a way to do this reponsible.

CARD SHAPE

I dont need a big description about, only some direction of what is the common to use (like Svg or css or image or clip-path or canvas), because is harder for me do with svg, and I need to make sure this is the only way

Sync custom colors in Mapbox

I am working on a feature in my next.js 15 application in which users have to draw the area on the map with different tools and when user finishes drawing it adds the area stay on the map and also it adds a row of some elements in area list on the left side of the page. In the row i have a custom color picker with my own color pallette. I want when user finish drawing it adds the color to the drew area too and when user changes the color of a particular area it will change the color of the area on the map too.

Here is my code for the parent component and here i have that color picker rendering:

"use client";

import type React from "react";

import { useState, useMemo, useRef, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
import {
  Check,
  MoreVertical,
  Pencil,
  Eye,
  Trash2,
  ArrowUpDown,
} from "lucide-react";
import CollectionAreaMap from "./collection-area-map";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import ColorPicker, { COLOR_PALETTE } from "./color-picker";

// Define the excluded color (grey with pattern)
const EXCLUDED_COLOR = { color: "#808080" };

interface CollectionArea {
  id: number;
  name: string;
  fee: string;
  enabled: boolean;
  color: string;
  featureId?: string; // Store the mapbox feature ID associated with this area
}

type SortDirection = "asc" | "desc" | null;
type SortField = "name" | "fee" | null;

export default function CollectionTerritoriesPage() {
  const [isEditMode, setIsEditMode] = useState(true);
  const [territoryName, setTerritoryName] = useState(
    "Matlock 30 miles Collection Territory"
  );
  const [territoryDescription, setTerritoryDescription] = useState("");
  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
  const [areaToDelete, setAreaToDelete] = useState<CollectionArea | null>(null);
  const [sortField, setSortField] = useState<SortField>(null);
  const [sortDirection, setSortDirection] = useState<SortDirection>(null);
  const [focusedAreaId, setFocusedAreaId] = useState<number | null>(null);

  // Initialize with an empty collection areas array
  const [collectionAreas, setCollectionAreas] = useState<CollectionArea[]>([]);
  const nextIdRef = useRef(1);

  // Add this ref to store the deleteFeature function from the map component
  const deleteFeatureRef = useRef<((featureId: string) => void) | null>(null);

  // Sort the collection areas based on the current sort field and direction
  const sortedCollectionAreas = useMemo(() => {
    if (!sortField || !sortDirection) {
      return [...collectionAreas];
    }

    return [...collectionAreas].sort((a, b) => {
      if (sortField === "name") {
        return sortDirection === "asc"
          ? a.name.localeCompare(b.name)
          : b.name.localeCompare(a.name);
      } else if (sortField === "fee") {
        const feeA = Number.parseInt(a.fee) || 0;
        const feeB = Number.parseInt(b.fee) || 0;
        return sortDirection === "asc" ? feeA - feeB : feeB - feeA;
      }
      return 0;
    });
  }, [collectionAreas, sortField, sortDirection]);

  // Update nextIdRef whenever areas change
  useEffect(() => {
    if (collectionAreas.length > 0) {
      const maxId = Math.max(...collectionAreas.map((area) => area.id));
      nextIdRef.current = maxId + 1;
    } else {
      nextIdRef.current = 1;
    }
  }, [collectionAreas]);

  const toggleEditMode = () => {
    setIsEditMode(!isEditMode);
  };

  const toggleAreaEnabled = (id: number) => {
    setCollectionAreas(
      collectionAreas.map((area) =>
        area.id === id ? { ...area, enabled: !area.enabled } : area
      )
    );
  };

  const handleSort = (field: SortField) => {
    if (sortField === field) {
      // Toggle direction if same field
      setSortDirection(sortDirection === "asc" ? "desc" : "asc");
    } else {
      // Set new field and default to ascending
      setSortField(field);
      setSortDirection("asc");
    }
  };

  const updateAreaName = (id: number, name: string) => {
    // Check if name is unique
    const isDuplicate = collectionAreas.some(
      (area) => area.id !== id && area.name === name
    );

    if (isDuplicate) {
      alert("Area name must be unique within the territory");
      return;
    }

    setCollectionAreas(
      collectionAreas.map((area) => (area.id === id ? { ...area, name } : area))
    );
  };

  const updateAreaFee = (id: number, fee: string) => {
    // Validate fee (3-digit integer)
    if (fee !== "" && !/^d{1,3}$/.test(fee)) {
      return;
    }

    setCollectionAreas(
      collectionAreas.map((area) => (area.id === id ? { ...area, fee } : area))
    );
  };

  const updateAreaColor = (id: number, color: string) => {
    setCollectionAreas(
      collectionAreas.map((area) =>
        area.id === id ? { ...area, color } : area
      )
    );
  };

  const deleteArea = (area: CollectionArea) => {
    setAreaToDelete(area);
    setDeleteDialogOpen(true);
  };

  // Updated to delete from map as well
  const confirmDeleteArea = () => {
    if (areaToDelete) {
      // Get the feature ID associated with this area
      const featureId = areaToDelete.featureId;

      // Remove the area from the state
      setCollectionAreas(
        collectionAreas.filter((area) => area.id !== areaToDelete.id)
      );

      // If there's a feature ID and we have a delete function, delete it from the map
      if (featureId && deleteFeatureRef.current) {
        deleteFeatureRef.current(featureId);
      }

      setDeleteDialogOpen(false);
      setAreaToDelete(null);
    }
  };

  // Handler for when an area is drawn on the map
  const handleAreaDrawn = (feature: any) => {
    const nextId = nextIdRef.current;
    // Get a color from the palette based on the ID
    const nextColorIndex = nextId % COLOR_PALETTE.length;

    // Create a new area object
    const newArea: CollectionArea = {
      id: nextId,
      name: `Area ${nextId}`,
      fee: "",
      enabled: true,
      color: COLOR_PALETTE[nextColorIndex].color,
      featureId: feature.id, // Store the feature ID for reference
    };

    // Log for debugging
    console.log(`Adding new area with ID ${nextId}, featureId: ${feature.id}`);

    // Update the state with the new area
    setCollectionAreas((prev) => [...prev, newArea]);

    // Automatically focus the newly created area
    setFocusedAreaId(nextId);

    return newArea;
  };

  const focusArea = (id: number) => {
    setFocusedAreaId(id === focusedAreaId ? null : id);
  };

  return (
    <div className="container mx-auto p-4 h-screen flex flex-col">
      <div className="grid grid-cols-1 lg:grid-cols-3 gap-4 flex-grow">
        {/* Collection Areas Panel */}
        <div className="bg-white rounded-lg shadow-sm border p-4 overflow-auto lg:col-span-1">
          <div className="flex justify-between items-center mb-4">
            <h2 className="text-lg font-semibold">Collection Areas</h2>
            {/* "Add Area" button has been removed as requested */}
          </div>

          {collectionAreas.length === 0 ? (
            <div className="text-center py-8 text-gray-500">
              {isEditMode ? (
                <p>
                  No areas added yet. Draw an area on the map to add your first
                  collection area.
                </p>
              ) : (
                <p>No collection areas defined.</p>
              )}
            </div>
          ) : (
            <>
              <div className="grid grid-cols-[auto_1fr_auto_auto] gap-2 items-center mb-2 px-2">
                <div className="font-medium text-sm text-gray-600"></div>
                <div
                  className="font-medium text-sm text-gray-600 flex items-center cursor-pointer"
                  onClick={() => handleSort("name")}
                >
                  Name
                  <ArrowUpDown
                    className={`ml-1 h-3 w-3 ${
                      sortField === "name" ? "opacity-100" : "opacity-50"
                    }`}
                  />
                </div>
                <div
                  className="font-medium text-sm text-gray-600 text-right flex items-center justify-end cursor-pointer"
                  onClick={() => handleSort("fee")}
                >
                  Fee
                  <ArrowUpDown
                    className={`ml-1 h-3 w-3 ${
                      sortField === "fee" ? "opacity-100" : "opacity-50"
                    }`}
                  />
                </div>
                <div className="font-medium text-sm text-gray-600 text-center">
                  Enable
                </div>
                <div></div>
              </div>

              <div className="space-y-2">
                {sortedCollectionAreas.map((area) => (
                  <div
                    key={area.id}
                    className={`grid grid-cols-[auto_1fr_auto_auto] gap-2 items-center border-b pb-2 ${
                      focusedAreaId === area.id ? "bg-gray-100" : ""
                    }`}
                  >
                    <div className="flex items-center">
                      {isEditMode ? (
                        <Popover>
                          <PopoverTrigger asChild>
                            <div
                              className="w-4 h-4 rounded-sm cursor-pointer"
                              style={{
                                backgroundColor: area.enabled
                                  ? area.color
                                  : EXCLUDED_COLOR.color,
                              }}
                            ></div>
                          </PopoverTrigger>
                          <PopoverContent className="p-0 w-[450px]">
                            <ColorPicker
                              color={area.color}
                              onChange={(color) =>
                                updateAreaColor(area.id, color)
                              }
                              disabled={!area.enabled}
                            />
                          </PopoverContent>
                        </Popover>
                      ) : (
                        <div
                          className="w-4 h-4 rounded-sm"
                          style={{
                            backgroundColor: area.enabled
                              ? area.color
                              : EXCLUDED_COLOR.color,
                          }}
                        ></div>
                      )}
                    </div>

                    <div>
                      {isEditMode ? (
                        <Input
                          value={area.name}
                          onChange={(e) =>
                            updateAreaName(area.id, e.target.value)
                          }
                          className="h-8 text-sm"
                          maxLength={20}
                          disabled={!area.enabled}
                        />
                      ) : (
                        <span>{area.name}</span>
                      )}
                    </div>

                    <div className="text-right">
                      {isEditMode ? (
                        area.enabled ? (
                          <Input
                            value={area.fee}
                            onChange={(e) =>
                              updateAreaFee(area.id, e.target.value)
                            }
                            className="h-8 text-sm w-20"
                            placeholder="0"
                          />
                        ) : null
                      ) : area.enabled ? (
                        `£${area.fee}`
                      ) : (
                        "Excluded"
                      )}
                    </div>

                    <div className="flex items-center gap-2">
                      <Switch
                        checked={area.enabled}
                        onCheckedChange={() => toggleAreaEnabled(area.id)}
                        className="data-[state=checked]:bg-blue-500"
                        disabled={!isEditMode}
                      />

                      <DropdownMenu>
                        <DropdownMenuTrigger asChild>
                          <Button
                            variant="ghost"
                            size="icon"
                            className="h-8 w-8"
                          >
                            <MoreVertical className="h-4 w-4" />
                          </Button>
                        </DropdownMenuTrigger>
                        <DropdownMenuContent align="end">
                          <DropdownMenuItem onClick={() => focusArea(area.id)}>
                            <Eye className="mr-2 h-4 w-4" />
                            Focus
                          </DropdownMenuItem>
                          {isEditMode && (
                            <DropdownMenuItem
                              onClick={() => deleteArea(area)}
                              className="text-red-600"
                            >
                              <Trash2 className="mr-2 h-4 w-4" />
                              Delete
                            </DropdownMenuItem>
                          )}
                        </DropdownMenuContent>
                      </DropdownMenu>
                    </div>
                  </div>
                ))}
              </div>
            </>
          )}

          {/* Collection Territory Info (only visible in edit mode) */}
          {isEditMode && (
            <div className="mt-6 border-t pt-4">
              <h2 className="text-lg font-semibold mb-4">
                Collection Territory Info
              </h2>
              <div className="space-y-4">
                <div className="space-y-2">
                  <Label htmlFor="territory-name">Name</Label>
                  <Input
                    id="territory-name"
                    value={territoryName}
                    onChange={(e) => setTerritoryName(e.target.value)}
                    maxLength={30}
                  />
                </div>
                <div className="space-y-2">
                  <Label htmlFor="territory-description">Description</Label>
                  <Input
                    id="territory-description"
                    value={territoryDescription}
                    onChange={(e) => setTerritoryDescription(e.target.value)}
                    maxLength={200}
                  />
                </div>
              </div>
            </div>
          )}
        </div>

        {/* Map Container */}
        <div className="lg:col-span-2 h-full" style={{ minHeight: "600px" }}>
          <CollectionAreaMap
            areas={collectionAreas}
            focusedAreaId={focusedAreaId}
            onAreaDrawn={handleAreaDrawn}
            onAreaDeleted={(deleteFeatureFunction) => {
              // Store the delete function from the map component
              deleteFeatureRef.current = deleteFeatureFunction;
            }}
          />
        </div>
      </div>

      {/* Floating Button */}
      <div className="fixed bottom-4 right-4">
        <Button
          onClick={toggleEditMode}
          className="bg-blue-500 hover:bg-blue-600 text-white"
        >
          {isEditMode ? (
            <>
              <Check className="mr-2 h-4 w-4" />
              Done editing
            </>
          ) : (
            <>
              <Pencil className="mr-2 h-4 w-4" />
              Edit
            </>
          )}
        </Button>
      </div>

      {/* Delete Confirmation Dialog */}
      <Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Delete Area</DialogTitle>
            <DialogDescription>
              Are you sure you want to delete {areaToDelete?.name}?
            </DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <Button
              variant="outline"
              onClick={() => setDeleteDialogOpen(false)}
            >
              Cancel
            </Button>
            <Button variant="destructive" onClick={confirmDeleteArea}>
              Delete
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}


I tried different approaches but couldn’t able to resolve this issue in syncing these colors. If anyone knows how to fix that just guide me and i will implement it myself. I can’t add the AreaMap component due to characters limit.

How to access contents of node in QuillJS?

I’m trying to add an id tag to each header (h1, h2, h3) for my Quill editor so I can link to specific sections of my page.

I have the following code which is working for adding the ID tag:

const Header = Quill.import("formats/header");

class EnhancedHeader extends Header {
  static blotName = "header";
  static create(value) {
    const node = super.create(value);
    const id = `header-123`;
    console.log(node.innerHTML);
    console.log(node.innerText);
    node.setAttribute("id", id);
    return node;
  }
}

Quill.register(EnhancedHeader);

const quill = new Quill('#editor', {
  modules: {
    toolbar: [
      [{ header: [1, 2, false] }],
      ['bold', 'italic', 'underline'],
      ['image', 'code-block'],
    ],
  },
  placeholder: 'Compose an epic...',
  theme: 'snow', // or 'bubble'
});

Playgound Example

How can I access the contents of the selected text in the create function? I need this so I can make a “slug” of the heading to use in the ID.

I’ve tried the following which return a blank string:

node.innerHTML
node.innerText