pinch and zoom react native skia and react gesture handler

I am using react native skias library and I am trying to do matrix calculations to create a pannable and zoomable skia canvas (like figma or google maps). Here is my implementation so far

function MyCanvas({
  imageView,
  canvasWidth,
  canvasHeight,
}: {
  imageView: Element[];
  canvasWidth: number;
  canvasHeight: number;
}) {
  const translateX = useSharedValue(0);
  const translateY = useSharedValue(0);
  const scale = useSharedValue(1);
  const savedScale = useSharedValue(1);
  const focalX = useSharedValue(0);
  const focalY = useSharedValue(0);

  const matrix = useSharedValue(Matrix4());

  const context = useSharedValue({ x: 0, y: 0 });

  const panGesture = Gesture.Pan()
    .onStart(() => {
      context.value = { x: translateX.value, y: translateY.value };
    })
    .onUpdate((event) => {
      translateX.value = event.translationX + context.value.x;
      translateY.value = event.translationY + context.value.y;
    });

  const pinchGesture = Gesture.Pinch()
    .onUpdate((event) => {
      scale.value = event.scale * savedScale.value;
      focalX.value = event.focalX;
      focalY.value = event.focalY;
    })
    .onEnd(() => {
      savedScale.value = scale.value;
      console.log(matrix.value);
    });

  const computedMatrix = useDerivedValue(() => {
    return processTransform3d([
      // 1. Apply current global panning
      { translateX: translateX.value },
      { translateY: translateY.value },

      // 2. Translate to pivot point (focal point for zoom)
      { translateX: focalX.value },
      { translateY: focalY.value },

      // 3. Apply scaling
      { scale: scale.value },

      // 4. Translate back from pivot point
      { translateX: -focalX.value },
      { translateY: -focalY.value },
    ]);
  }, [scale, translateX, translateY, focalX, focalY]);

  useDerivedValue(() => {
    // console.log(computedMatrix.value);
    // This effect updates the Skia Matrix4 instance directly.
    matrix.value = computedMatrix.value;
  });

  const composedGesture = Gesture.Simultaneous(panGesture, pinchGesture);

  return (
    <GestureDetector gesture={composedGesture}>
      <View className="bg-gray-950 flex-1 overflow-visible">
        <Canvas style={{ flex: 1 }}>
          <Group matrix={computedMatrix}>{imageView}</Group>
        </Canvas>
      </View>
    </GestureDetector>
  );
}

This pans (translates) fine but whenever I try pinch to zoom in continues to zoom in on the origin (0,0) and not the focal point of the pinch gesture. Any suggestions?

How to create multiple SVGs in for loops [duplicate]

I am attempting to make a website that simulates lever frames from certain railways, and I was wondering how to create animated / unique images for each lever. I did some research and I am still not sure that an SVG is even the way to go here, but it seemed the best way in my very limited opinion. I want each lever to have its own unique number, but I didnt want to create 100+ levers of each type with numbers. When the lever is pulled I would like the lever to “pull” or “push” (I will simulate this by somehow shrinking and expanding the height of the lever)

I previously was simulating the levers with a button that just transformed down by its own height, so most of the infrastructure is there already, I just need help setting up the SVGs

This is my code so far (literally not much to it) and already I am getting probably the obvious error of leverimg.createElement is not a function

for (const lever of Object.keys(leverframe)) {
        const leverimg = document.createElement("svg")
        image = leverimg.createElement("img")
        text = leverimg.createElement("text")
        
        image.src = "path/to/img"
        text.innerHTML = lever
}

and here is an image for reference to the lever. The number goes on the white plate just under the handle

How can I create the SVG with js (again if I even should be using the SVG at all) so I can have the lever and the text overlapping and animated

Error when running next build in nextjs monorepo turborepo

I have a turborepo with 2 NextJS projects in it.

When I run next build I get this error:

[TypeError: Cannot read properties of null (reading 'useContext')]
Error occurred prerendering page "/404". Read more: https://nextjs.org/docs/messages/prerender-error
TypeError: Cannot read properties of null (reading 'useContext')
    at exports.useContext (/Users/user/dev/repos/repo-root/node_modules/styled-jsx/node_modules/react/cjs/react.production.js:488:33)

Package.json of app A

{
  "name": "docs",
  "version": "0.1.0",
  "type": "module",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack -p 3001",
    "build": "next build",
    "start": "next start",
    "lint": "next lint --max-warnings 0",
    "check-types": "tsc --noEmit"
  },
  "dependencies": {
    "next": "^15.2.1",
    "react": "^19.1.0",
    "react-dom": "^19.1.0"
  },
  "devDependencies": {
    "@types/node": "^22.13.10",
    "@types/react": "19.0.0",
    "@types/react-dom": "19.0.0",
    "eslint": "^9.22.0",
    "typescript": "5.8.2"
  }
}

Package.json of app B

{
  "name": "app",
  "version": "0.1.0",
  "type": "module",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack -p 3002",
    "build": "next build",
    "start": "next start",
    "lint": "next lint --max-warnings 0",
    "check-types": "tsc --noEmit"
  },
  "dependencies": {
    "next": "^15.2.1",
    "react": "19.0.0",
    "react-dom": "19.0.0"
  },
  "devDependencies": {
    "@types/node": "^22.13.10",
    "@types/react": "19.0.0",
    "@types/react-dom": "19.0.0",
    "eslint": "^9.22.0",
    "typescript": "5.8.2"
  }
}

I tried creating a custom 404 and/or error pages without any success.

Can an inappropriate callback be passed to waitFor?

I was going over Road to React (the book by Robin Wieruch), and I encountered a confusing piece of code. The code is on the Testing chapter, page 227:


describe('App', () => { 
    it('succeeds fetching data', 
        async () => { const promise = Promise.resolve({ data: { hits: stories,},
    });

    axios.get.mockImplementationOnce(() => promise);
    
    render(<App />); 
    
    expect(screen.queryByText(/Loading/)).toBeInTheDocument();
    
    await waitFor(async () => await promise);
 
    expect(screen.queryByText(/Loading/)).toBeNull();
    });
});

This is the useEffect code:


React.useEffect(() => { 
    handleFetchStories();
}, [url);

This is handleFetchStories and storiesReducer:


// handleFetchStories 

const handleFetchStories = React.useCallback(async () => {
    dispatchStories({ type: 'STORIES_FETCH_INIT' });
    const result = await axios.get(url);
    dispatchStories({ type: 'STORIES_FETCH_SUCCESS',
    payload: result.data.hits,
    });

    try { 
    const result = await axios.get(url);
    dispatchStories({ type: 'STORIES_FETCH_SUCCESS',
    payload: result.data.hits,});
    } 
    catch { 
    dispatchStories({ type: 'STORIES_FETCH_FAILURE' });
}
}, [url]);


// StoriesReducer
const storiesReducer = (state, action) => { 
switch (action.type) { 
case 'STORIES_FETCH_INIT':
return { ...state,
isLoading: true,
isError: false,
};
case 'STORIES_FETCH_SUCCESS':
return { ...state,
isLoading: false,
isError: false,
data: action.payload,
};
case 'STORIES_FETCH_FAILURE':
return { ...state,
isLoading: false,
isError: true,
};
case 'REMOVE_STORY':
return { ...state,
data: state.data.filter( (story) => action.payload.objectID !== story.objectID ),
};
default:
throw new Error();
}
};

My problem is with this line:

await waitFor (async () => await promise)

I understand waitFor's behaviour, but it seems like this code is redundant, given that the promise is already resolved. However, the code works. In addition, the callback has nothing to do with a DOM update, so I suspect that this is an inappropriate use of waitFor.

Can’t Change Font Awesome Icon On Click

In my password field I have an eye icon that is supposed to change to the slashed eye on click and show password elements. But when I click it, it does nothing. What needs to be changed in my code?? Excuse the ChatGPT word vomit. I have confirmed that nothing in my CSS seems to be overriding any changes

HTML:

<!-- ============================
       B) SIGN UP MODAL
       ============================ -->
  <div class="modal-overlay" id="signupModal" style="display: none;">
    <div class="modal-box">
      <h2>SIGN UP</h2>
      <form id="signupForm">
        <input
          type="text"
          id="signupFirstName"
          placeholder="First Name"
          required
        />
        <input
          type="text"
          id="signupLastName"
          placeholder="Last Name"
          required
        />
        <input
          type="text"
          id="signupUsername"
          placeholder="Username"
          required
        />

        <!-- Password Field (Signup) -->
        <div class="input-with-icon">
          <input
            type="password"
            id="signupPassword"
            placeholder="Password"
            required
          />
          <i
            class="fa-regular fa-eye toggle-password"
            data-target="signupPassword"
          ></i>
        </div>

        <!-- Confirm Password Field (Signup) -->
        <div class="input-with-icon">
          <input
            type="password"
            id="signupConfirmPassword"
            placeholder="Confirm Password"
            required
          />
          <i
            class="fa-regular fa-eye toggle-password"
            data-target="signupConfirmPassword"
          ></i>
        </div>

        <div id="signupError" class="error-box" style="display: none;">
          <!-- Error text will appear here -->
        </div>

        <button class="submit-btn" type="submit">SUBMIT</button>
      </form>
    </div>
    <!-- /.modal-box for signup -->
  </div>
  <!-- /#signupModal -->

JS:

document.addEventListener('DOMContentLoaded', () => {
  
  // Toggle password visibility
  document.querySelectorAll('.toggle-password').forEach(icon => {
    icon.addEventListener('click', () => {
        console.log('Eye icon clicked, toggling!');
      const targetId = icon.getAttribute('data-target');
      const input = document.getElementById(targetId);
      if (!input) return;

      const isHidden = input.type === 'password';
      input.type = isHidden ? 'text' : 'password';

      // Toggle icon class
      icon.classList.toggle('fa-eye');
      icon.classList.toggle('fa-eye-slash');
    });
  });

Wagtail – How can I use a custom StructBlock with programmatically assignable default values?

I have created a StreamField for some of the site’s settings. This StreamField contains a collection of a custom StructBlock, named FacetBlock. FacetBlock contains a BooleanBlock, a CharBlock, an IntegerBlock and a ChoiceBlock. Now I need to make the CharBlock translatable, which means the admin will be able to enter one text for each language available on the site.

The way I was thinking of doing this is by using another custom StructBlock, this time called TranslatableTextBlock. It would contain a ChoiceBlock, a CharBlock and some hidden input to store the complete value. Using javascript, the admin would select a language, enter the text for that language and then move on to the next language. I’m not sure yet how that would be saved the database, but I’m not even able to get that far.

Right now, I can have either the fields show up with no default value or I get a javascript error (TypeError: e is null) in vendor.js, which triggers the same error in comments.js. Here is the code I have so far. I have omitted the FacetBlock definition, because it works perfectly when reverting TranslatableTextBlock to a CharBlock.

Python code:

class TranslatableTextBlock(StructBlock):
    def __init__(self, default_text:str = None, default_language:str = None, local_blocks = None, search_index = True, **kwargs):
        local_blocks = [
            (
                "language",
                ChoiceBlock(
                    choices=LANGUAGES,
                    default=default_language if default_language else None,
                    help_text=_("Select the language for this text")
                )
            ),
            (
                "text",
                CharBlock(
                    default=default_text if default_text else None,
                    help_text=_("Enter your text in the selected language"),
                )
            )
        ]
        super().__init__(local_blocks, search_index, **kwargs)

    class Meta:
        form_classname = "struct-block translatable-text-block"
        form_template = 'blocks/translatable_text_block_admin.html'
        icon = 'doc-full'
        label = _('Translated text')
        template = 'blocks/translatable_text_block.html'


class TranslatableTextBlockAdapter(StructBlockAdapter):
    js_constructor = "website.blocks.TranslatableTextBlock"

    @cached_property
    def media(self):
        structblock_media = super().media
        return Media(
            js=structblock_media._js + ["website/js/translatable_text_block.js"],
            css=structblock_media._css
        )

register(TranslatableTextBlockAdapter(), TranslatableTextBlock)

The admin template (translatable_text_block_admin.html):

{% load wagtailadmin_tags  %}

<div class="{{ classname }}">
{% if help_text %}
    <span>
        <div class="help">
            {% icon name="help" classname="default" %}
            {{ help_text }}
        </div>
    </span>
{% endif %}
    {% for child in children %}
    {{ child }}
    <div class="w-field" data-field data-contentpath="{{ child.block.name }}">
        {% if child.block.label %}
        <label class="w-field__label" {% if child.id_for_label %}for="{{ child.id_for_label }}"{% endif %}>{{ child.block.label }}{% if child.block.required %}<span class="w-required-mark">*</span>{% endif %}</label>
        {% endif %}
        {{ child.render_form }}
    </div>
    {% endfor %}
</div>

Javascript code (translatable_text_block.js):

if (typeof TranslatableTextBlockDefinition === "undefined") {
    class TranslatableTextBlockDefinition extends window.wagtailStreamField.blocks.StructBlockDefinition {
        render(placeholder, prefix, initialState, initialError) {
            const block = super.render(placeholder, prefix, initialState, initialError);    // The error happens on this line.
            // Some custom modifications would happen here.
            return block;
        }
    }

    window.telepath.register("website.blocks.TranslatableTextBlock", TranslatableTextBlockDefinition);
}

The javascript error:

TypeError: e is null
vendor.js:2:191488
Uncaught TypeError: e is null
comments.js:1:47007

Is anyone familiar with nested StructBlock for Streamfields in admin settings? Or maybe I’m going at this the wrong way and there is an easier solution? I welcome frame challenges 🙂

Using D3 worked example, get page 404 error with my csv file

I have taken the worked example from D3 for multiple plots on one graph, and having downloaded the csv file, it worked:-

<!-- Code from d3-graph-gallery.com -->
<!DOCTYPE html>
<meta charset="utf-8">

<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>

<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>


<script>

// set the dimensions and margins of the graph
var margin = {top: 10, right: 30, bottom: 30, left: 60},
    width = 460 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
  .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform",
          "translate(" + margin.left + "," + margin.top + ")");

//Read the data
d3.csv("data/Org.csv", function(data) {

  // group the data: I want to draw one line per group
  var sumstat = d3.nest() // nest function allows to group the calculation per level of a factor
    .key(function(d) { return d.name;})
    .entries(data);

  // Add X axis --> it is a date format
  var x = d3.scaleLinear()
    .domain(d3.extent(data, function(d) { return d.year; }))
    .range([ 0, width ]);
  svg.append("g")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x).ticks(5));

  // Add Y axis
  var y = d3.scaleLinear()
    .domain([0, d3.max(data, function(d) { return +d.n; })])
    .range([ height, 0 ]);
  svg.append("g")
    .call(d3.axisLeft(y));

  // color palette
  var res = sumstat.map(function(d){ return d.key }) // list of group names
  var color = d3.scaleOrdinal()
    .domain(res)
    .range(['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628','#f781bf','#999999'])

  // Draw the line
  svg.selectAll(".line")
      .data(sumstat)
      .enter()
      .append("path")
        .attr("fill", "none")
        .attr("stroke", function(d){ return color(d.key) })
        .attr("stroke-width", 1.5)
        .attr("d", function(d){
          return d3.line()
            .x(function(d) { return x(d.year); })
            .y(function(d) { return y(+d.n); })
            (d.values)
        })

})

</script>

I changed the location of the csv file, to be local, which would allow my adaptations to be tested, and it worked.

I then modified the code, so it used my CSV file, which looks (with more entries) as follows:-

Time,TimeB,TimeC, Place, Data1, Data2, Data3
2025-03-10,2025-03-10,1, Place1,20.0625,19.76305,47996
2025-03-10,2025-03-10,2, Place2,20.1875,19.88156,49357
2025-03-10,2025-03-10,3, Place3,20.4375,19.98456,50589

I have changed the code to reflect that the column headings are different, and only tried to plot Data1, but I have tried changing the “year” heading to “Time” (which was a string) or TimeB (a date/Time value) or TimeC which is just a number.

However, for every attempt, I receive a Page 404 error.

I am running d3 v4, on an Apache server on a local machine; and I have used Firefox and Chromium on an Ubuntu machine, with identical results.

I cannot see why minor changes to a worked example (on my system) results in it breaking!

Any guidance would be most appreciated.

Google Tag Manager – Uncaught Error: [object Set] is not an iterable or ArrayLike

I am getting the following error when trying to use gtm.js:
Uncaught Error: [object Set] is not an iterable or ArrayLike

I have been using GTM for a long time and have never experienced any issues like this, but as of yesterday (June 5, 2025), I have been seeing thousands of errors being logged in all of our environments and it is preventing hundreds of customers from using the site. The error is being thrown when GTM is initialized as well as any time an event is pushed to the dataLayer. It seems to happen 19 out of 20 times on initialization, in which case pushing events will also cause the same error. On the 1 out of 20 times where it doesn’t happen on initialization, the events won’t throw the error either.

I noticed the Tag Assistant Companion Chrome extension was merged with the Tag Assistant extension the other day, and whether or not this is a coincidence, I found that if I use the Tag Assistant extension and debug GTM (both in our staging environment and in production), the error doesn’t occur. But, if I stop debugging (even if the extension is still installed/enabled), the next time I load the page it’ll stop working again.

Has anyone else been seeing this the past 2 days (or ever)? My guess is that Google made some kind of internal change that started causing this, but to my surprise I don’t see anyone else mentioning it.

Nuxt.js layout: false displays an empty page

I have a Nuxt site that has pages and layouts enabled. I have one layout called default.vue that contains:

    <div id="app">
        <AppSidebar />
        <main class="main-content">
            <NuxtPage />
        </main>
    </div>

I am using NuxtAuth for authentication. I would ideally like the authentication page to not contain a sidebar which results in this definePageMeta():

definePageMeta({
        layout: false,
        auth: { unauthenticatedOnly: true, navigateAuthenticatedTo: '/' },
})

When I set layout: to false, instead of becoming fullscreen and using the page as its own page, it instead displays pure black.

Screenshot of black webpage

How to run a widget after build without exposing JavaScript source code in browser DevTools?

I have a React widget that I want to load and run only after the build process. However, I don’t want the JavaScript source code (e.g., the logic inside bundle.js) to be viewable or readable in the browser’s Developer Tools.
I’ve tried
Removing the .map files.
Using javascript-obfuscator during the Webpack build process.
Any Suggestions .

Highcharter/Highmaps – how to use TiledWebMap?

I am trying to build a map with highcharter/highmaps which includes a topographical map via a tiled web map.

As a test, I would like to replicate the OpenStreetMap demo (JSFiddle) as provided by highcharts in their API documention. However, I am unsuccessful. All I get is a white page with the title, but no map.

I’ve tried two variants in R – using the highcharter functions and direct JS (within the highcharter functions).

Variant A)

library(highcharter)

highchart(type = "map") %>%
  hc_add_dependency("modules/map") %>%
  hc_add_dependency("modules/tiledwebmap") %>%
  hc_title(text = "Highcharts Maps basic TiledWebMap Series") %>%
  hc_mapNavigation(
    enabled       = TRUE,
    buttonOptions = list(alignTo = "spacingBox")
  ) %>%
  hc_legend(enabled = FALSE) %>%
  hc_chart(
    mapView = list(
      center     = c(10, 50),
      zoom       = 4
    )
  ) %>%
  hc_add_series(
    type     = "tiledwebmap",
    provider = list(type = "Esri", theme = "WorldTopoMap")
  )

Variant B)

library(highcharter)
library(htmlwidgets)
library(htmltools)

hc_lib       <- system.file("htmlwidgets/lib/highcharts", package = "highcharter")
map_js       <- paste(readLines(file.path(hc_lib, "modules/map.js"),       warn = FALSE), collapse = "n")
tiled_js     <- paste(readLines(file.path(hc_lib, "modules/tiledwebmap.js"), warn = FALSE), collapse = "n")

highchart() %>%
  hc_chart(
    type = "map",
    events = list(load = JS(
      "
      function () {
        this.mapView = new Highcharts.MapView(this, {
          center: [10, 50],
          zoom: 4
        });

        this.addSeries({
          type: 'tiledwebmap',
          provider: { type: 'Esri', theme: 'WorldTopoMap' }
        });
      }
      "
    ))
  ) %>%
  
  hc_title(text = "Highcharts Maps basic TiledWebMap Series") %>%
  hc_mapNavigation(
    enabled     = TRUE,
    buttonOptions = list(alignTo = "spacingBox")
  ) %>%
  hc_legend(enabled = FALSE) %>% 
  htmlwidgets::prependContent(tags$script(HTML(map_js_code))) %>%
  htmlwidgets::prependContent(tags$script(HTML(tile_js_code)))

I am using the most recent highcharter version 0.9.4.9000, installed from the highcharter GitHub repo, which uses HighchartsJS 12.2.0.

Has someone managed to get TiledWebMap to work with highcharter?

Thanks and best regards,
Martin

Blocks created in random places, without overlay on pure java script

Help, please! I’ve already seen how to make from 5 to 20 random blocks, but I can’t make them not touch each other. Please don’t explain in words how this should work, but show an example of code on java script!

let bufferZoneCnt = document.querySelector('.bufferZone-cnt');
let bufferZoneCntHeight = bufferZoneCnt.getBoundingClientRect().height;
let bufferZoneCntWidth = bufferZoneCnt.getBoundingClientRect().width;


function generateDiv() {
    var dfrag = document.createDocumentFragment();
    var count = generateRandom(5, 20);
    var i = 0;
    for (var i = 0; i < count; i++) {
        var div = document.createElement("div");
        dfrag.appendChild(div);
    }
    for (i = 0; i < dfrag.childNodes.length; i++) {
        div = dfrag.childNodes[i];
        alterDivStyle(div);
    }
    bufferZoneCnt.appendChild(dfrag);
}
function rndColor() {
    var r = ('0' + generateRandom(0, 255).toString(16)).substr(-2), // red
        g = ('0' + generateRandom(0, 255).toString(16)).substr(-2), // green
        b = ('0' + generateRandom(0, 255).toString(16)).substr(-2); // blue
    return '#' + r + g + b;
}

function generateRandom(min, max) {
    var number = Math.floor(Math.random() * (max - min)) + min;
    return number;
}
function alterDivStyle(div) {
    div.style.width = generateRandom(20, 100) + "px";
    div.style.height = generateRandom(20, 100) + "px";
    let divHeight = parseInt(div.style.height);
    let divWidth = parseInt(div.style.width);
    div.style.backgroundColor = rndColor();
    div.style.color = rndColor();
    div.style.position = "absolute";
    div.style.border = "solid";
    div.style.borderColor = rndColor();
    div.style.borderWidth = rndColor();
    div.style.borderRadius = generateRandom(0, 10) + "px";
    div.innerHTML = "<strong>div</strong>";
    div.style.top = generateRandom(0, bufferZoneCntHeight - divHeight) + "px";
    div.style.left = generateRandom(0, bufferZoneCntWidth - divWidth) + "px";

I tried to put all the coordinates into an array, and then run each section of the array through a loop, but something doesn’t work.

IS there an easy way to spread an object’s properties as event functions?

I have an interface called “DataSet”, which features an optional property, which is an object with various optional event functions on it:

export interface DataSet {
    colour: string;
    value: number;
    events?: {
        onAuxClick?(event: React.MouseEvent<SVGPathElement, MouseEvent>, data: DataSet): void;
        onBlur?(event: React.FocusEvent<SVGPathElement, Element>, data: DataSet): void;
        onClick?(event: React.MouseEvent<SVGPathElement, MouseEvent>, data: DataSet): void;
        onDoubleClick?(event: React.MouseEvent<SVGPathElement, MouseEvent>, data: DataSet): void;
        onFocus?(event: React.FocusEvent<SVGPathElement, Element>, data: DataSet): void;
        onKeyDown?(event: React.KeyboardEvent<SVGPathElement>, data: DataSet): void;
        onKeyPress?(event: React.KeyboardEvent<SVGPathElement>, data: DataSet): void;
        onKeyUp?(event: React.KeyboardEvent<SVGPathElement>, data: DataSet): void;
        onMouseDown?(event: React.MouseEvent<SVGPathElement, MouseEvent>, data: DataSet): void;
        onMouseEnter?(event: React.MouseEvent<SVGPathElement, MouseEvent>, data: DataSet): void;
        onMouseLeave?(event: React.MouseEvent<SVGPathElement, MouseEvent>, data: DataSet): void;
        onMouseMove?(event: React.MouseEvent<SVGPathElement, MouseEvent>, data: DataSet): void;
        onMouseOut?(event: React.MouseEvent<SVGPathElement, MouseEvent>, data: DataSet): void;
        onMouseUp?(event: React.MouseEvent<SVGPathElement, MouseEvent>, data: DataSet): void;
        onPointerCancel?(event: React.PointerEvent<SVGPathElement>, data: DataSet): void;
        onPointerDown?(event: React.PointerEvent<SVGPathElement>, data: DataSet): void;
        onPointerEnter?(event: React.PointerEvent<SVGPathElement>, data: DataSet): void;
        onPointerLeave?(event: React.PointerEvent<SVGPathElement>, data: DataSet): void;
        onPointerMove?(event: React.PointerEvent<SVGPathElement>, data: DataSet): void;
        onPointerOut?(event: React.PointerEvent<SVGPathElement>, data: DataSet): void;
        onPointerUp?(event: React.PointerEvent<SVGPathElement>, data: DataSet): void;
        onTouchCancel?(event: React.TouchEvent<SVGPathElement>, data: DataSet): void;
        onTouchEnd?(event: React.TouchEvent<SVGPathElement>, data: DataSet): void;
        onTouchMove?(event: React.TouchEvent<SVGPathElement>, data: DataSet): void;
        onTouchStart?(event: React.TouchEvent<SVGPathElement>, data: DataSet): void;
    }
}

In a component, I iterate over an array of these “DataSet” objects to create SVGPathElements and attach those events to the element (whilst passing the “DataSet” object into the function itself)

data.map((dataEntry, index) => {
            segments.push(<path 

                        onAuxClick={(event) => dataEntry.events?.onAuxClick ? dataEntry.events?.onAuxClick(event, dataEntry) : null}
                        onBlur={(event) => dataEntry.events?.onBlur ? dataEntry.events?.onBlur(event, dataEntry) : null}
                        onClick={(event) => dataEntry.events?.onClick ? dataEntry.events?.onClick(event, dataEntry) : null}
                        onDoubleClick={(event) => dataEntry.events?.onDoubleClick ? dataEntry.events?.onDoubleClick(event, dataEntry) : null}
                        onFocus={(event) => dataEntry.events?.onFocus ? dataEntry.events?.onFocus(event, dataEntry) : null}
                        onKeyDown={(event) => dataEntry.events?.onKeyDown ? dataEntry.events?.onKeyDown(event, dataEntry) : null}
                        onKeyPress={(event) => dataEntry.events?.onKeyPress ? dataEntry.events?.onKeyPress(event, dataEntry) : null}
                        onKeyUp={(event) => dataEntry.events?.onKeyUp ? dataEntry.events?.onKeyUp(event, dataEntry) : null}
                        onMouseDown={(event) => dataEntry.events?.onMouseDown ? dataEntry.events?.onMouseDown(event, dataEntry) : null}
                        onMouseEnter={(event) => dataEntry.events?.onMouseEnter ? dataEntry.events?.onMouseEnter(event, dataEntry) : null}
                        onMouseLeave={(event) => dataEntry.events?.onMouseLeave ? dataEntry.events?.onMouseLeave(event, dataEntry) : null}
                        onMouseMove={(event) => dataEntry.events?.onMouseMove ? dataEntry.events?.onMouseMove(event, dataEntry) : null}
                        onMouseOut={(event) => dataEntry.events?.onMouseOut ? dataEntry.events?.onMouseOut(event, dataEntry) : null}
                        onMouseUp={(event) => dataEntry.events?.onMouseUp ? dataEntry.events?.onMouseUp(event, dataEntry) : null}
                        onPointerCancel={(event) => dataEntry.events?.onPointerCancel ? dataEntry.events?.onPointerCancel(event, dataEntry) : null}
                        onPointerDown={(event) => dataEntry.events?.onPointerDown ? dataEntry.events?.onPointerDown(event, dataEntry) : null}
                        onPointerEnter={(event) => dataEntry.events?.onPointerEnter ? dataEntry.events?.onPointerEnter(event, dataEntry) : null}
                        onPointerLeave={(event) => dataEntry.events?.onPointerLeave ? dataEntry.events?.onPointerLeave(event, dataEntry) : null}
                        onPointerMove={(event) => dataEntry.events?.onPointerMove ? dataEntry.events?.onPointerMove(event, dataEntry) : null}
                        onPointerOut={(event) => dataEntry.events?.onPointerOut ? dataEntry.events?.onPointerOut(event, dataEntry) : null}
                        onPointerUp={(event) => dataEntry.events?.onPointerUp ? dataEntry.events?.onPointerUp(event, dataEntry) : null}
                        onTouchCancel={(event) => dataEntry.events?.onTouchCancel ? dataEntry.events?.onTouchCancel(event, dataEntry) : null}
                        onTouchEnd={(event) => dataEntry.events?.onTouchEnd ? dataEntry.events?.onTouchEnd(event, dataEntry) : null}
                        onTouchMove={(event) => dataEntry.events?.onTouchMove ? dataEntry.events?.onTouchMove(event, dataEntry) : null}
                        onTouchStart={(event) => dataEntry.events?.onTouchStart ? dataEntry.events?.onTouchStart(event, dataEntry) : null}
                />);      
    })

(I’ve removed irrelevant parts of the above code)

If I add a lot of other events to the interface, it’s going to be a bit time consuming to add them explicitly to the component.

Is there an elegant way to add all of those functions to the path element, so that I can only add to the interface without needing to edit the component too? Or has my approach of defining them on the DataSet made a rod for my own back? (Passing the DataSet object to the function probably complicates matters somewhat, too)

I did try {...dataEntry.events} but that didn’t work (I think the resulting code would just be the functions without binding them to the events on the element)

As a secondary question, is there any downside to adding the functions as I have (i.e. the path element will always have a click function, but it will do nothing if it wasn’t defined on the DataSet) or is React smart enough to strip them out when the function would just return null without any further processing?

Tailwind won’t show light mode and only shows dark mode styles

I made a toggle button to change to dark mode and light mode for the react project I’m working on. It does what it should, it correctly add and remove the “dark” class name from html tag and also local storage but the styles I see is just dark mode styles.
my dark mode button code:

export default function DarkModeSwitch() {
  const [isDarkMode, setIsDarkMode] = useState(false);

  useEffect(() => {
    // Check if theme is already saved in localStorage
    const savedTheme = localStorage.getItem('theme');

    // If no theme saved in localStorage, check system preference
    if (savedTheme === null) {
      const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
      setIsDarkMode(prefersDark);
      // Apply the correct class based on system preference
      if (prefersDark) {
        document.documentElement.classList.add('dark');
      } else {
        document.documentElement.classList.remove('dark');
      }
    } else {
      // Apply saved theme from localStorage
      setIsDarkMode(savedTheme === 'dark');
      if (savedTheme === 'dark') {
        document.documentElement.classList.add('dark');
      } else {
        document.documentElement.classList.remove('dark');
      }
    }
  }, []);

  // Toggle the theme and save it to localStorage
  const toggleDarkMode = () => {
    const newMode = !isDarkMode;
    setIsDarkMode(newMode);
    if (newMode) {
      document.documentElement.classList.add('dark');
      localStorage.setItem('theme', 'dark');
    } else {
      document.documentElement.classList.remove('dark');
      localStorage.setItem('theme', 'light');
    }
  };

  const spring = {
    type: 'spring',
    stiffness: 700,
    damping: 30,
  };

  return (
    <div
      onClick={toggleDarkMode}
      className={`flex-start flex h-[48px] w-[100px] rounded-[30px] bg-zinc-100 p-[5px] shadow-inner hover:cursor-pointer dark:bg-zinc-700 ${isDarkMode ? 'place-content-end' : ''}`}
    >
      <motion.div
        className="flex h-[40px] w-[40px] items-center justify-center rounded-full bg-black/90"
        layout
        transition={spring}
      >
        <motion.div whileTap={{ rotate: 360 }}>
          {isDarkMode ? (
            <RiSunFill className="h-6 w-6 text-yellow-300" />
          ) : (
            <RiMoonClearFill className="h-6 w-6 text-slate-200" />
          )}
        </motion.div>
      </motion.div>
    </div>
  );
}

and here is my tailwind config file:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  darkMode: "class",
  theme: {
    extend: {
      fontFamily: {
        iran: ["iran"],
      },
      colors: {
        background: {
          light: 'oklch(0.985 0.002 247.839)',
          dark: '#030712',
        },
        titles: {
          light: '#fafafa',
          dark: '#030712',
        },
        text: {
          light: '#fafafa',
          dark: '#030712',
          gray: '#374151',
        },
        icons: {
          light: '#44403c',
          dark: '#115e59',
        },
        nav: {
          light: '#99f6e4',
          dark: '#115e59',
        },
      },
      lineHeight: {
        relaxed: '1.25',
      },
    },
  },
  plugins: [
    require('tailwindcss-rtl'),
    require('daisyui'), 
  ],
};

It correctly changes the class name but when I use something like className="fixed w-full z-50 bg-white dark:bg-gray-800 backdrop-blur-sm border-b border-gray-100" it only shows the dark:bg-gray-800 and if I delete dark style it shows the light mode styles.

I tried ignoring system default from the JS but it didn’t work.

How to send a message from anonymous script executed with `browser.scripting.executeScript`?

I’m using browser.scripting.executeScript() and i need to send a message to webext background script/service worker script. Usually it means using browser.runtime.sendMessage(), but browser is reportedly undefined in anomymous script. I can pass browser as call script argument, but i believe it will be serialized and i’m not sure it survives the boundary passing.

Here is the code:

await browser.scripting.executeScript({
    target: details,
    world: "MAIN",
    injectImmediately: true,
    args: [tabId, frameId],
    func: (tabId, frameId) => {
      if (window.history.__pushState) {
        console.warn("Already injected");
        return;
      }
      window.history.__pushState = history.pushState;
      console.log("Injected pushState()");
      history.pushState = function(state, unused, url) {
        // eslint-disable-next-line no-console
        console.log("Intercepted pushState()", state, unused, url);
        window.history.__pushState(state, unused, url);

        console.log("debug", browser); // `browser` is undefined

        browser.runtime.sendMessage({
          type: "safari-onhistorystateupdated",
          state, unused, url
        });
      };
    }
  });

How can i access browser object in such anonymous script?