How do I add a custom post type with a custom taxonomy in WordPress?

I need to create a new post type called “Portfolio” and associate it with a taxonomy called “Project Type.” What is the correct way to do this using register_post_type() and register_taxonomy()?

I want to create a custom post type called “Portfolio” in WordPress, which will be used to showcase various projects. Each portfolio item should be categorized using a custom taxonomy named “Project Type.”

I am aware that WordPress provides functions like register_post_type() and register_taxonomy(), but I need a clear and detailed guide on how to properly register and associate them.

Why are my songs showing up as undefined?

Working in p5.js on a class assignment. We have to make a music player. I found a tutorial in the site, but despite copying the code near-exactly (only altering so as to implement multiple songs), I can’t seem to get the play button to work. What’s stopping it from reading my files?

If you want to test it for yourself, I got the music files from the incompetech library.

var track = 1;

let song1, song2, song3, song4, song5;

function setup() {
  createCanvas(500, 250);
  
  song1 = loadSound('Burn The World Waltz.mp3');
  song2 = loadSound('Le Grande Chase.mp3');
  song3 = loadSound('Mesmerizing Galaxy.mp3');
  song4 = loadSound('Guzheng City.mp3');
  song5 = loadSound('Holiday Weasel.mp3');
  
  //setup play
  let playButton;
  playButton = createButton('>||');
  playButton.size(100, 100);
  playButton.position(200,150);
  playButton.style('background:green');
  playButton.mousePressed(toggleSong);
  
  //setup shift_track_right
  let rightButton;
  rightButton = createButton('>>|');
  rightButton.size(100, 100);
  rightButton.position(300,150);
  rightButton.style('background:cyan');
  rightButton.mousePressed(right);
  
  //setup shift_track_left
  let leftButton;
  leftButton = createButton('|<<');
  leftButton.size(100, 100);
  leftButton.position(100,150);
  leftButton.style('background:cyan');
  leftButton.mousePressed(left);
}

function left()
{
  if (track <= 5)
    {
      track -= 1;
    }
}

function right()
{
  if (track >= 1)
    {
      track += 1;
    }
}

function toggleSong()
{
  if (track === 1)
    {
      if (song1.isPlaying())
      {
      song1.stop();
      }
      else {
      song1.play();
      }
    }
  else if (track === 2)
    {
      if (song2.isPlaying())
      {
      song2.stop();
      }
      else {
      song2.play();
      }
    }
  else if (track === 3)
    {
      if (song3.isPlaying())
      {
      song3.stop();
      }
      else {
      song3.play();
      }
    }
  else if (track === 4)
    {
      if (song4.isPlaying())
      {
      song4.stop();
      }
      else {
      song4.play();
      }
    }
  else if (track === 5)
    {
      if (song5.isPlaying())
      {
      song5.stop();
      }
      else {
      song5.play();
      }
    }
}

function draw() {
  background(220);
  if (track === 1)
    {
      title = 'Burn the World Waltz';
    }
  else if (track === 2)
    {
      title = 'Le Grand Chase';
    }
  else if (track === 3)
    {
      title = 'Mesmerizing Galaxy';
    }
  else if (track === 4)
    {
      title = 'Guzheng City';
    }
  else if (track === 5)
    {
      title = 'Holiday Weasel';
    }
  textSize(25)
  text(title, 100, 100)
}

rock, paper scissors JS

Currently working on my first JS project; rock paper scissors. I found my functions getComputerChoice() and getHumanChocie run without issues on their own however the functions that follow give no response. When i run the whole program at once after user inputs their choice nothing else happens, the console remains empty without any errors or warnings in the console.

I think my error is somewhere in located in the playRound function towards the end of the programp though i am uncertain what is causing the it to not give an output, nor to play through the rounds.

let computerScore = 0
let round = 0


function getComputerChoice() {

    let compChoice = Math.floor(Math.random() * 3) + 1;
    // choosese a random number between 1 and 3.

 
    if (compChoice === 1) {
     compChoice = "rock";
    } 
    if (compChoice === 2) {
     compChoice = "paper";
    }
    if (compChoice === 3) {
     compChoice === "scissors"
    };

    return compChoice;
    
 };

 function getHumanChoice() {
    //Takes player choice of Rock, paper and scissors.
    // will return players choice.
        let humanChoice = prompt("Pick: Rock, paper or scissors! ").toLowerCase();
    
        return humanChoice;
 }
        
function playRound(humanChoice, compChoice) {
    // increments the round winner's score and logs winner announcement.
    if (//conditions for computer to win
    humanChoice === "rock" && compChoice === "paper" ||
    humanChoice === "paper" && compChoice === "scissors" ||
    humanChoice === "scissors" && compChoice === "rock") {
    computerScore = computerScore + 1;
    return (`You lose ${compChoice} beats ${humanChoice}
            computers gains a point!`)
    } else if (// Conditions for player to win!
    humanChoice === "rock" && compChoice === "scissors" ||
    humanChoice == "paper" && compChoice === "rock" ||
    humanChoice === "scissors" && compChoice === "paper") {
    return (`You win. ${humanChoice} beats ${compChoice}.
        You gain a point!`)
    } else if (humanChoice === compChoice) { // condition if a tie
        return (`It's a TIE!`)
    } else { // Invalid selection
        return (`Invalid selection, please try again!`)
            
    };            

};

const humanSelection = getHumanChoice();
const computerSelection = getComputerChoice();

playRound(humanSelection, computerSelection)

function playGame(playRound, round) {  // 5 Rounds of Gameplay
   
    if (round === 0) {
        alert ("Round 1.....Show!")
        playRound();
        round = round +1; 
    } else if (round === 2) {
        alert ("Round 2.....Show!")
        playRound();
        round = round +1;
    } else if (round === 3) {
        alert ("Round 3.....Show!")
        playRound();
        round = round +1;
    } else if (round === 4) {
        alert ("Round 4.....Show!")
        playRound(); 
        round = round +1;
    } else if (round === 5) {
        alert ("Round 5.....Show!")
        playRound();
        round = round +1;
    } else if (round == 6) {
        winner();
    }
        
}
    
function winner (humanScore, computerScore) {
    if (humanScore > computerScore) {
        return (`YOU WIN! You have ${humanScore} points, 
            computer has ${computerScore} points`);
    } else if (humanScore < computerScore) {
        return (`Computer wins!, computer has ${computerScore} points
                you have ${humanScore} points`);
    } else {
        return (`You Tie! Both you and computer have ${humanScore} points!`);
    };
            
}
    
playRound();

GUID Error on column userId with javascript

So i have an issue while trying this endpoint with postman

router.get("/users/groups", authenticateToken, getUserGroups);

The function I’m trying is this one that gets the userId that is sent from the token:

export const getUserGroups = async (req, res) => {
  try {
    const userId = req.user.userId;
    console.log("ID DE L'USUARI: ", userId);
    const pool = await db();

    const result = await pool
      .request()
      .input("userId", sql.UniqueIdentifier, userId).query(`
        SELECT 
          g.GroupId,
          g.GroupName,
          g.imageUrl,
          ug.JoinedAt
        FROM UserGroups ug
        INNER JOIN Groups g ON ug.GroupId = g.GroupId
        WHERE ug.UserId = @userId
      `);

    res.status(200).json({
      success: true,
      data: result.recordset,
    });
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
};

I keep getting this error: RequestError: Validation failed for parameter ‘userId’. Invalid GUID. but the userId looks like this:

{
  userId: '5D7BF774-B868-47E9-8C55-3B7231D31F63',
  email: '[email protected]',
  iat: 1740071642,
  exp: 1740072842
}

three-globe custom html markers not updating their position when the globe is rotating on y axis

I’m using the three-globe library to make a 3D interactive globe with custom html markers, I used this example https://github.com/vasturiano/three-globe/blob/master/example/html-markers/index.html but when I add a rotation to the globe Globe.rotation.y += speedRotation; inside the animate() function the html markers are still visible even though they are on the back face of the globe but when I drag the globe they fix their position and z-index. How can I fix their position when the globe is rotating ?

Preview: https://codesandbox.io/p/sandbox/jjctmd?file=%2Findex.html%3A98%2C41

Uncaught TypeError: Cannot read properties of undefined (reading ‘_config’) while using Chakra UI

I am facing an issue while trying to use Chakra UI in my React application. The following error occurs when I attempt to run the app. I am using the latest version of React and Chakra UI.

The error happens when I attempt to apply a custom theme to Chakra UI using the ChakraProvider component.

Here is my current setup:

main.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import { ChakraProvider } from '@chakra-ui/react';
import { BrowserRouter } from 'react-router-dom';
import { App } from './App.jsx';

const theme = {
  config: {
    useSystemColorMode: false,
    initialColorMode: 'light',
    cssVarPrefix: 'chakra',
  },
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <ChakraProvider theme={theme}>
        <App />
      </ChakraProvider>
    </BrowserRouter>
  </React.StrictMode>
);

Any help to resolve this issue would be much appreciated!

Reactjs Map how to iterate

I am new to Reactjs, how can i iterate into the response below :

{
    "data": [       
        {
            "id": "1234",
            "name": "Adm",
            "date": "2020-01-14 00:00:00.000",
            "value": "4444.00"            
        }       
    ]
}

I try using map but gives me the error : Cannot read properties of undefined (reading ‘map’)

Or how can i change the json format to :

{
    "data": {       
        {
            "id": "1234",
            "name": "Adm",
            "date": "2020-01-14 00:00:00.000",
            "value": "4444.00"            
        }       
    }
}

Because the other examples are into this format .

How can I pass an XSLT variable to a JavaScript function in an XSLT template?

I am working with XSLT and need to pass an XSLT variable to a JavaScript function to dynamically update the content on the page. Specifically, I have a variable in my XSLT template that I want to use in a JavaScript function to modify an HTML element.

Here is my XSLT template:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:outline="http://wkhtmltopdf.org/outline"
                xmlns="http://www.w3.org/1999/xhtml">
    <xsl:output doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
                doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
                indent="yes" />    

    <xsl:template match="outline:outline">        
        <html>
            <head>
                <title>TOC</title>
                <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
            </head>
             <body>
                <!-- Note: I want to dynamically update the following h1 by JS -->
                <h1 id="toc">Table of Contents</h1>
                <ul id="top">
                    <xsl:apply-templates select="outline:item/outline:item"/>
                </ul>
                <script type="text/javascript">
                    const languageMap = {
                        'ENG': 'Table of Contents',
                        'JPN': '目次',
                        'CHS': '目录',
                        'CHT': '目錄'
                    };

                    <!-- Note: the following function gets the languageTitle from XSLT and replaces h1 with matched value -->
                    function updateTOCHeader(languageTitle) {                        
                        const tocHeader = document.getElementById('toc');
                        tocHeader.style.color = "green";
                       
                        if(languageMap[languageTitle]) {
                            tocHeader.innerText = languageMap[languageTitle];
                        }
                    }
                </script>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="outline:item">
        <xsl:choose>
            <!-- Note: the following if test checks if the ancestor item has a title starting with 'language_title:' -->
            <xsl:when test="ancestor::outline:item[starts-with(@title, 'language_title:')]">
                <xsl:variable name="language_title" select="normalize-space(substring-after(ancestor::outline:item[@title and starts-with(@title, 'language_title:')]/@title, 'language_title:'))"/>
                <script type="text/javascript">
                    <!-- Note: call the following function to update the TOC header based on language title -->
                    updateTOCHeader("<xsl:value-of select="$language_title"/>");
                </script>
            </xsl:when>
           
            <xsl:otherwise>
                <li>
                    <xsl:if test="@title != ''">
                        <div class="{if (outline:item) then 'category-item' else ''}">
                            <span class="toc-link">
                                <a>
                                    <xsl:if test="@link">
                                        <xsl:attribute name="href"><xsl:value-of select="@link"/></xsl:attribute>
                                    </xsl:if>
                                    <xsl:value-of select="@title"/>
                                </a>
                            </span>
                            <span class="page-no">
                                <xsl:value-of select="@page"/>
                            </span>
                        </div>
                    </xsl:if>
                    <ul>
                        <xsl:apply-templates select="outline:item"/>
                    </ul>
                </li>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

</xsl:stylesheet>

Here is sample XML:

<?xml version="1.0" encoding="UTF-8"?>
<outline xmlns="http://wkhtmltopdf.org/outline">
   <item title="" page="0"/>
   <!-- Note: I want to pass the substring of the item title (e.g. 'JPN') to JS -->
   <item title="language_title:JPN" page="1">
      <item title="Table of Contents" page="2"/>
   </item>
   <item title="document" page="2">
      <item title="introduction" page="1">
         <item title="Chapter1" page="2">
            <item title="Section1" page="3"/>            
         </item>
         <item title="Chapter2" page="4">
            <item title="Section2" page="5"/>            
         </item>
      </item>
   </item>
   <item title="" page="20"/>
</outline>

Shopify Error: Field ‘customerByIdentifier’ doesn’t exist on type ‘QueryRoot’

export const loader = async ({ request }: LoaderFunctionArgs) => {
  await authenticate.admin(request);

  const admin = await authenticate.admin(request);

  const email = "[email protected]";

  const response = await admin.admin.graphql(
    `#graphql
      query GetCustomers($query: String!) {
        customerByIdentifier(identifier: {emailAddress: $query}) {
          id
        }
      }
    `,
    {
      variables: { query: email }
    }
  );

  const data = await response.json();

  console.log(">>>>> data", data, "<<<<<<");

  return json({ apiKey: process.env.SHOPIFY_API_KEY || "" });
};

enter image description here

I have a big problem. Recently it became possible via Shopify’s admin API to make a customerByIdentifier request that looks like this

{
  customerByIdentifier(identifier) {
    # Customer fields
  }
}

As you can see from the above code, I am querying via graphQl and all I get on Frontend is ‘Error: Field “customerByIdentifier” doesn’t exist on type “QueryRoot”’

But when I test this query in the Shopify GraphQl App it gives me the user on this query correctly, here is the query I make through apk

{
  customerByIdentifier(identifier: {emailAddress: ‘[email protected]’}) {
    id
  }
}

Help me understand what I’m doing wrong ?

Problems running Jekyll and tasks via ESM

I’m encountering strange behavior running a Jekyll build. I kick off the build using npm start from the package.json

package.json

  "scripts": {
    "start": "node index.mjs",
    "test": "echo "Error: no test specified" && exit 1"
  },

That executes the necessary tasks to build the Jekyll site via the index.mjs file.

index.mjs:

import { exec } from "child_process";
import browserSync from "browser-sync";
import fs from "fs";
import postcss from "postcss";
import autoprefixer from "autoprefixer";
import { minify } from "terser";

const mainJsFile = "main.min.js";

function deleteSiteDirectory() {
  const siteDir = "./_site";
  const sassCacheDir = "./.sass-cache";

  // Delete _site directory
  fs.rm(siteDir, { recursive: true, force: true }, (err) => {
    if (err) {
      console.error(`Error deleting directory ${siteDir}: ${err}`);
    } else {
      console.log(`Directory ${siteDir} has been deleted`);
    }
  });

  // Delete .sass-cache directory
  fs.rm(sassCacheDir, { recursive: true, force: true }, (err) => {
    if (err) {
      console.error(`Error deleting directory ${sassCacheDir}: ${err}`);
    } else {
      console.log(`Directory ${sassCacheDir} has been deleted`);
    }
  });

  // Delete main.min.js file
  fs.rm(mainJsFile, { force: true }, (err) => {
    if (err) {
      console.error(`Error deleting file ${mainJsFile}: ${err}`);
    } else {
      console.log(`File ${mainJsFile} has been deleted`);
    }
  });
}

async function concatenateAndMinifyJsFiles() {
  const jsDir = "./js/utils/";

  try {
    // Delete main.min.js file
    await fs.promises.rm(mainJsFile, { force: true });
    console.log(`File ${mainJsFile} has been deleted`);

    // Concatenate and minify .js files
    const files = await fs.promises.readdir(jsDir, { withFileTypes: true });
    const jsFiles = files.filter((file) => file.isFile() && file.name.endsWith(".js"));
    if (jsFiles.length === 0) {
      console.log(`No .js files found in ${jsDir}`);
      return;
    }

    const concatenatedJs = jsFiles.map((file) => fs.readFileSync(`${jsDir}${file.name}`, "utf8")).join("n");
    const result = await minify(concatenatedJs);
    const minifiedJs = result.code;

    const outputDir = "./js/";
    const outputFile = `${outputDir}${mainJsFile}`;

    await fs.promises.writeFile(outputFile, minifiedJs);
    console.log(`File ${mainJsFile} has been created`);
  } catch (err) {
    console.error(`Error: ${err}`);
  }
}

function runJekyllCommand() {
  exec("jekyll serve --watch", (error, stdout, stderr) => {
    if (error) {
      console.error(`exec error: ${error}`);
      return;
    }
    console.log(`stdout: ${stdout}`);
    console.error(`stderr: ${stderr}`);

    // Start BrowserSync
    browserSync.init({
      server: {
        baseDir: "_site/",
      },
    });

    // Watch for changes in the source files and reload the browser
    browserSync.watch("./_site/*.*").on("change", browserSync.reload);
  });

  // Initialize Browsersync
  browserSync.init({
    server: {
      baseDir: "./_site/",
    },
  });

  // Use Browsersync reload function as a listener to Jekyll build events
  browserSync.watch("**/*.*").on("change", browserSync.reload);
}

function autoprefixCssFiles() {
  const cssDir = "./_site/css/";
  fs.readdir(cssDir, (err, files) => {
    if (err) {
      console.error(`Error reading directory ${cssDir}: ${err}`);
      return;
    }

    files.forEach((file) => {
      if (file.endsWith(".css")) {
        const filePath = `${cssDir}${file}`;
        fs.readFile(filePath, (err, css) => {
          if (err) {
            console.error(`Error reading file ${filePath}: ${err}`);
            return;
          }

          postcss([autoprefixer({ overrideBrowserslist: [">1%"] })])
            .process(css, { from: filePath, to: filePath })
            .then((result) => {
              fs.writeFile(filePath, result.css, (err) => {
                if (err) console.error(`Error writing file ${filePath}: ${err}`);
              });
            });
        });
      }
    });
  });
}

if (process.env.NODE_ENV !== "production") {
  deleteSiteDirectory();
  concatenateAndMinifyJsFiles();
  runJekyllCommand();
  autoprefixCssFiles();

  console.log("Running in development mode. Development tasks have been executed");
}

When the browser opens, I get a Cannot GET / message on the screen and nothing else. Here’s a screenshot. If I refresh the browser the site is there. I can’t determine why this is happening.

enter image description here

Show deleted whatsapp message

I’m storing whastapp deleted message with data-id using this code

(function () {
  let messageCache = new Map();
  function extractMessageContent(element) {
    let messageContainer = element.querySelector("[data-pre-plain-text]");
    let messageText = element.querySelector(".selectable-text");
    let mediaContainer = element.querySelector(".media-container");
    let timestampElement = element.querySelector("div[data-pre-plain-text]");
    let timestamp = "";
    let sender = "";
    let mediaUrl = "";
    let mediaName = "";
    if (timestampElement) {
      let preText = timestampElement.getAttribute("data-pre-plain-text");
      if (preText) {
        let matches = preText.match(/\[(.*?)\].*?([^:]+):/);
        if (matches) {
          timestamp = matches[1];
          sender = matches[2].trim();
        }
      }
    }
    if (mediaContainer) {
      let mediaElement = mediaContainer.querySelector("img, video");
      if (mediaElement) {
        mediaUrl = mediaElement.src;
        mediaName = mediaElement.alt || mediaElement.src.split("/").pop();
      }
    }
    return {
      id: element.getAttribute("data-id"),
      text: messageText ? messageText.innerText : "",
      sender: sender,
      timestamp: timestamp,
      hasMedia: !!mediaContainer,
      mediaUrl: mediaUrl,
      mediaName: mediaName,
    };
  }
  const messageObserver = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
      if (mutation.type === "childList") {
        mutation.addedNodes.forEach((node) => {
          if (node.nodeType === Node.ELEMENT_NODE) {
            let messageElement = node.querySelector("[data-id]");
            if (messageElement) {
              console.log("add", node);

              let message = extractMessageContent(messageElement);
              messageCache.set(message.id, message);
            }
          }
        });
        mutation.removedNodes.forEach((node) => {
          if (node.nodeType === Node.ELEMENT_NODE) {
            let messageElement = node.querySelector("[data-id]");
            if (messageElement) {
              let messageId = messageElement.getAttribute("data-id");
              let cachedMessage = messageCache.get(messageId);
              console.log("remove", node);

              if (cachedMessage) {
                // window.webkit.messageHandlers.messageObserver.postMessage({
                //   type: "messageDeleted",
                //   data: cachedMessage,
                // });
                messageCache.delete(messageId);
              }
            }
          }
        });
      }
    });
  });
  function observeChat() {
    const chatContainer = document.querySelector("#main div.copyable-area");
    if (chatContainer) {
      messageObserver.observe(chatContainer, {
        childList: true,
        subtree: true,
      });
    } else {
      setTimeout(observeChat, 1000);
    }
  }
  function initObserver() {
    if (document.querySelector("#app")) {
      observeChat();
    } else {
      setTimeout(initObserver, 1000);
    }
  }
  initObserver();
})();

But I’m getting new data-id on that deleted message.

How can I show that deleted message to that place?

Autodesk Forge Viewer: When the box-selection tool is activated but only a single click event is fired, the resulting selection is unpredictable

Given the following use case:

  • A user holds down the control key (enabling the box-selection tool)

  • They then do a single click (instead of a drag/box selection)

A large percentage of the time the selection seems to somewhat randomly select different elements or no elements.

This can also be recreated by first making a box selection then the control key + single clicking.

I imagine the generally expected behaviour would be for a new click to be treated as a new box selection of a single point. Effectively a single element selection.

Screen Recording Example


Why is this a problem? This probably wouldn’t be a major issue accept that many of our users assume that box-selection would be enabled by the shift key and cumulative selection/deselection would occur by the control key. However, these seem to be swapped. Therefore ending up with users accidently attempting to unselect an element using the control + click combination.

I have attempted to swap the key functionalities but ran into what seems to be a OS dependent bug.. This might solve the issue of confusion for our users but the buggy behaviour would stil exist.

I have also attempted some work arounds via creating custom tools and using global key and mouse events to try and disable the selection event in the event of control + single click. but have had no luck and this feels quite hacky.

I guess my question is. Is this a known issue to the development team and/or are there any known work arounds or plans to address this issue?

Issue in Properly Registering a Custom Transform in Vega

I am trying to implement a custom transform to use it in Vega.
For now, the transform is simple (it just doubles the data passed in input). These are the two files I am using:

This is the index.html file:

<!DOCTYPE html>
<html>
<head>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
    <script src="script.js" defer></script>
</head>
<body>
    <div id="vis"></div>
</body>
</html>

While this is the script.js file:

window.onload = () => {
    console.log("Script start");

    // =============================================
    // 1. REGISTER CUSTOM TRANSFORM (GLOBAL SCOPE)
    // =============================================

    function DoubleValueTransform(params) {
        vega.Transform.call(this, [], params); // Initialize superclass
        console.log("DoubleValueTransform Constructor");
    }

    DoubleValueTransform.prototype = Object.create(vega.Transform.prototype);
    DoubleValueTransform.prototype.constructor = DoubleValueTransform;

    DoubleValueTransform.Definition = {
        "type": "DoubleValueTransform",
        "metadata": {"modifies": true},
        "params": [
            { "name": "factor", "type": "number", "default": 2 }
        ]
    };

    // Implement transformation logic
    DoubleValueTransform.prototype.transform = function (_, pulse) {
        console.log("Inside the transform");
        const factor = _.factor || 2;
        pulse.visit(pulse.SOURCE, d => {
            d.value = d.value * factor;
            console.log("Value transformed:", d.value);
        });
        return pulse.reflow();
    };

    // Register the custom transform
    vega.transforms["DoubleValueTransform"] = DoubleValueTransform;

    if (!vega.schema) {
        vega.schema = {};
    }
    if (!vega.schema.transforms) {
        vega.schema.transforms = {};
    }

    vega.schema.transforms["DoubleValueTransform"] = DoubleValueTransform.Definition;

    console.log("Custom transform registered:", vega.transforms["DoubleValueTransform"]);

    // =============================================
    // 2. SPECIFICATION 
    // =============================================

    const spec = {
        "$schema": "https://vega.github.io/schema/vega/v5.json",
        "width": 400,
        "height": 200,
        "padding": 5,
        "signals": [
            {
                "name": "progress",
                "value": 0,
                "bind": {"input": "range", "min": 0, "max": 100},
                "on": [{"events": {"signal": "timer"}, "update": "(progress + 1) % 100"}]
            },
            {
                "name": "timer",
                "value": 0,
                "on": [{"events": {"type": "timer", "delay": 100}, "update": "timer + 1"}]
            }
        ],
        "data": [
            {
                "name": "table",
                "values": [
                    {"category": "A", "value": 30},
                    {"category": "B", "value": 80},
                    {"category": "C", "value": 45}
                ],
                "transform": [
                    {
                        "type": "DoubleValueTransform", // Custom transform
                        "factor": 2
                    },
                    {
                        "type": "filter",
                        "expr": "datum.value <= progress"
                    }
                ]
            }
        ],
        "scales": [
            {
                "name": "xscale",
                "type": "band",
                "domain": {"data": "table", "field": "category"},
                "range": "width",
                "padding": 0.05
            },
            {
                "name": "yscale",
                "domain": {"data": "table", "field": "value"},
                "nice": true,
                "range": "height"
            }
        ],
        "axes": [
            {"orient": "bottom", "scale": "xscale"},
            {"orient": "left", "scale": "yscale"}
        ],
        "marks": [
            {
                "type": "rect",
                "from": {"data": "table"},
                "encode": {
                    "enter": {
                        "x": {"scale": "xscale", "field": "category"},
                        "width": {"scale": "xscale", "band": 0.5},
                        "y": {"scale": "yscale", "field": "value"},
                        "y2": {"scale": "yscale", "value": 0},
                        "fill": {"value": "steelblue"}
                    }
                }
            }
        ]
    };

    // =============================================
    // 3. PARSE SPEC AND RENDER VIEW
    // =============================================

    const runtime = vega.parse(spec);

    const view = new vega.View(runtime)
        .logLevel(vega.Info)
        .renderer('canvas') 
        .initialize('#vis')   
        .hover()
        .run();

    window.view = view;
};

When I start a server, trying to visualize the results, nothing is visualized and, instead, I get these messages in the console:

Script start script.js:3
script.js:52 Custom transform registered: ƒ DoubleValueTransform(params) {
        vega.Transform.call(this, [], params); // Inizializza la superclasse
        console.log("DoubleValueTransform Constructor");
    }

[email protected]:1 Uncaught Error: Unrecognized transform type: "DoubleValueTransform"
    at u ([email protected]:1:560)
    at sU ([email protected]:1:476852)
    at [email protected]:1:487956
    at Array.forEach (<anonymous>)
    at TU ([email protected]:1:487936)
    at [email protected]:1:497845
    at Array.forEach (<anonymous>)
    at uL ([email protected]:1:497833)
    at lL ([email protected]:1:499276)
    at t.parse ([email protected]:1:506807)

It feels like the transform is registered, but I really don’t know how to bypass this error. Could this be a synchronicity issue? I am not very familiar with the low-level Vega grammar.

Horizontal scroll buttons

I have a dialog with a plusIconButton that allows me to create new column. I can create new columns till I reach this equality allBomDocumentIds.length === styleOptions.length. In that moment I disable the button.

I am defining this state and constant

const [currentView, setCurrentView] = useState(0);
const columnsPerView = 4;
const totalViews = Math.ceil(allBomDocumentIds.length / columnsPerView);

I need to show the following thing:

If the total nº of columns is >= 4, we always display the last 4 columns
example:

  • if we have 4 columns and we add a new one, we display columns 2, 3, 4, 5 and column 2 has the angleLeftIcon
  • if we have 5 columns and we add a new one, we display columns 3, 4, 5, 6 and column 3 has the angleLeftIcon
  • if we have 6 columns and we add a new one, we display columns 4, 5, 6, 7 and column 4 has the angleLeftIcon

Then, once I have created the columns and move back to the previous page I need to display every 4 columns.
example.

  • if I have a total of 10 columns, the first page goes from 1 to 4, the second from 5 to 8 and the third from 7 to 10

So that the first column OUT of view after clicking the button should become the first column IN view.
You create 1 column by 1 but you show 4 by 4.

I have these fn to handle previous/next page:

const handleNextView = (): void => {
  setCurrentView((prev) => Math.min(prev + 1, totalViews - 1));
};

const handlePrevView = (): void => {
  setCurrentView((prev) => Math.max(prev - 1, 0));
};

And this useEffect to show the last column every time we add one:

useEffect(() => {
    if (allBomDocumentIds.length > columnsPerView) {
        setCurrentView(totalViews - 1);
    }
}, [allBomDocumentIds.length, totalViews]);

In the return I have these two methods:

allBomDocumentIds
  .slice(currentView * columnsPerView,
   (currentView + 1) * columnsPerView)
  .map((bomDocumentId, index) => {
   
    const totalSelectedOptions = getTotalSelectedOptionsForColumn(bomDocumentId);     
    const areAllOptionsSelected = totalSelectedOptions === styleOptions.length;

How can I obtain the behaviour I am looking for?