How does React render promises within returned JSX?

I have this code:

import { useState } from "react"

export default function TestRoute() {
  const [response, setResponse] = useState<Promise<string>>()

  const sendRequest = async (): Promise<string> => {
    const response = await fetch('http://localhost:9090/test/test_delayed')
    const message = await response.json()
    return message.message
  }

  const handleButtonClick = async () => {
    const result = sendRequest()
    setResponse(result)

  }
  return (
    <div>
      <button onClick={handleButtonClick}>Delayed response</button>
      <p>{response}</p>
    </div>
  )
}

The test/test_delayed endpoint sleeps 3 seconds, then returns. This is my FastAPI code:

@router.get("/test_delayed")
async def test_delayed():
    await asyncio.sleep(3)
    return {
        "message": "Delayed response",
    }

What surprises me is that the above TSX code works. As in, after I click the button, after a 3s delay the response text appears. I would have expected React to return an error (cannot render object of type Promise or something). How is React doing this? I am using React 19.

Not fetch data from some one of my JSON URL

I have try fetch json data from url getting data from some url & not getting data from some url.

getting data from this url : https://raw.githubusercontent.com/rsk1987/test-project/refs/heads/main/test2.json

not getting data from this url : https://raw.githubusercontent.com/rsk1987/test-project/refs/heads/main/test.json

let http = new XMLHttpRequest();

http.open('get', 'https://raw.githubusercontent.com/rsk1987/test-project/refs/heads/main/test.json', true);

http.send();
http.onload = function(){

    if(this.readyState == 4 && this.status == 200){

        let products = JSON.parse(this.responseText);

        let output = "";

        for(let item of products){
            output += `

    <a onclick="jwplayer().load({file: '${item.adfree_url}'})">
        <div class="product">
        <img src="${item.src}" alt="${item.title}">
        <p class="title" style="display:none">${item.title}</p>
        <p class="match_name">${item.match_name}</p>
        </div>
    </a>
            `;
        }
        document.querySelector(".json-data").innerHTML = output;
    }
} 
 .product{
    text-align: center;
    display: inline-grid;
    padding: 5px;
    box-shadow: -0px 5px 5px 8px rgba(1, 1, 1, 0.5);
    grid-gap: 10px;
    margin: 8px auto;
    width: 180px;
    max-width: 180px;
    height: : 280px;
    max-height:280px;
    padding: 5px;
}
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
  
    <div class="json-data"></div>

I want to show on page category, title, & image object.

How and when to use arrow functions efficiently in JavaScript

I’ve mostly been writing traditional (regular) functions up until now, but I still don’t fully understand how and when to use arrow functions. Could someone kindly explain the best practices for using arrow functions? Here’s a simple implementation of a minimum-finding algorithm written using a traditional function. I’d appreciate it if someone could show me how to rewrite it using an arrow function. Please be respectful when responding – I’m here to learn.

function findMinimum(values) {
  let minimum = values[0]

  for (let counter = 0; counter < values.length; counter++) {
    if (values[counter] < minimum) {
      minimum = values[counter]
    }
  }
  console.log(`The minimum value is ${minimum}`);
}

let numbers = [77, 45, 98, 77, 12, 33, 99, 76]

findMinimum(numbers)

Console is showing node rather than expected output with array and for loop

I am relatively new to coding and working through a bootcamp right now. I will summarize the task at hand and then post the code below and my console result. If anyone can help me see what I’m doing wrong that would be greatly appreciated!

Task: loop through the array and use the age of the people in the array to determine if they’re old enough to see Mad Max.

Context: earlier in my work, I was getting at least some output, not getting different results for different ages in the array (getting all “not old enough”), but there was at least something being displayed in the console. Now all I get is this:

node "/Users/me/Dev/vs intro/10-Loops-and-Arrays-Practice/loopsArrays.js"

Here is my code:

var peopleWhoWantToSeeMadMaxFuryRoad = [
    {
      name: "Mike",
      age: 12,
      gender: "male"
    },{
      name: "Madeline",
      age: 80,
      gender: "female"
    },{
      name: "Cheryl",
      age: 22,
      gender: "female"
    },{
      name: "Sam",
      age: 30,
      gender: "male"
    },{
      name: "Suzy",
      age: 4,
      gender: "female"
    }
  ];

for (var i = 0; i < peopleWhoWantToSeeMadMaxFuryRoad.length; i++){
    if (peopleWhoWantToSeeMadMaxFuryRoad.age >= 18){
        console.log("is old enough to see Mad Max")
    } else if (peopleWhoWantToSeeMadMaxFuryRoad.age <= 17){
        console.log("is not old enough to see Mad Max")
    }
};

Thank you again to anyone who can help!

How to implement text along a bezier curve in a react js canvas webapp?

I am making a custom canvas editor using the open-source polotno sdk with react js, whcih also uses the konva framework. It is aimed at making heraldry blazons.

I am trying to implement a feature so that a user can create text along a customizable bezier curve with handles, but i have found no resources online that address my specific problem.

I have successfully implemented a button that adds a modifiable bezier curve with two handles, but what i am looking for is a bezier curve that can have however many handles the user wants, and be able to have text on it. I am including the images of what i am looking to implement and how i want it to function.

This image is an example of the result I want the user to be able to create

This image is an example of how I image it would function in the canvas

I am doing this in separate files: Sections.js contains the sidepannels (text, images, upload, etc…) as well as their sub sections. Editor.js imports sections.js, and App.js is what imports Editor.js and runs.

Ideally, I would like to define the functions for the bezier text in a separate file, and include it inside the Sections.js file where the text panel is. But any functional way of implementing it is good enough.

My sections.js code is very long, so here is the relevant code of the text panel, where i want the buttons and options for the curved text to be:

const TextePanel = ({ store }) => {
  const [text, setText] = useState('');
  const [fontSize, setFontSize] = useState(24);
  const [fontFamily, setFontFamily] = useState('Arial');

  const addNormalText = () => {
    const page = store.activePage;
    page.addElement({
      type: 'text',
      text,
      fontSize,
      fontFamily,
      x: 100,
      y: 100,
    });
  };

  const addBezierCurve = () => {
    const page = store.activePage;
    page.addElement({
      type: 'bezier-curve',
      x1: 100,
      y1: 100,
      cx: 200,
      cy: 50,
      x2: 300,
      y2: 100,
      stroke: 'red',
      strokeWidth: 3,
    });
  };


  return (
    <div style={{ padding: 10 }}>
      <div>
        <label>Text:</label>
        <input
          type="text"
          value={text}
          onChange={(e) => setText(e.target.value)}
          style={{ width: '100%' }}
        />
      </div>

      <div style={{ marginTop: 10 }}>
        <label>Font:</label>
        <select
          value={fontFamily}
          onChange={(e) => setFontFamily(e.target.value)}
        >
          <option value="Arial">Arial</option>
          <option value="Georgia">Georgia</option>
          <option value="Courier New">Courier New</option>
          <option value="Comic Sans MS">Comic Sans</option>
        </select>
      </div>

      <div style={{ marginTop: 10 }}>
        <label>Font Size:</label>
        <input
          type="number"
          value={fontSize}
          onChange={(e) => setFontSize(parseInt(e.target.value))}
        />
      </div>
      <div style={{ marginTop: 10 }}>
        <button onClick={addNormalText}>Add Normal Text</button>
      </div>
      <div style={{ marginTop: 10 }}>
        <button onClick={addBezierCurve}>Add Bézier Curve</button>
      </div>
    </div>
  );
};

const TexteSection = {  
  name: 'Texte',
  Tab: (props) => (
    <SectionTab name="Texte" {...props}>
      <img
        src="/tabicons/fountain-pen.svg"
        alt="Text"
        style={{ width: 30, height: 30 }}
      />
    </SectionTab>
  ),
  Panel: TextePanel,
};

Here is my full editor.js file:

// src/Editor.js
import React, { useEffect } from "react";
import {
  PolotnoContainer,
  SidePanelWrap,
  WorkspaceWrap,
} from "polotno";
import { Toolbar } from "polotno/toolbar/toolbar";
import { PagesTimeline } from "polotno/pages-timeline";
import { ZoomButtons } from "polotno/toolbar/zoom-buttons";
import { SidePanel } from "polotno/side-panel";
import { Workspace } from "polotno/canvas/workspace";

import "./elements/BezierCurveElement";

import { createStore } from "polotno/model/store";
import { sections } from "./Sections";

import "@blueprintjs/core/lib/css/blueprint.css";

const store = createStore({
  key: "YOUR_API_KEY",
  showCredit: true,
});

store.addPage();

const addWatermark = () => {
  const page = store.activePage;
  const imageUrl = "/watermark.png"; // Make sure it's in /public

  const image = new window.Image();
  image.src = imageUrl;

  image.onload = () => {
    const imageWidth = image.width;
    const imageHeight = image.height;

    const targetWidth = 300;
    const scaleFactor = targetWidth / imageWidth;
    const scaledHeight = imageHeight * scaleFactor;

    // Use the actual page height after ensuring it's available
    const pageHeight = page.height || 1080; // Fallback in case undefined
    const x = 15;
    const y = 850; // Bottom left

    page.addElement({
      type: "image",
      src: imageUrl,
      x,
      y,
      width: targetWidth,
      height: scaledHeight,
      alwaysOnTop: true,
      showInExport: true,
      locked: true,
      selectable: false,
      draggable: false,
      resizable: false,
      removable: false,
      name: "WATERMARK_IMAGE",
    });
  };
};

const Editor = () => {
  useEffect(() => {
    addWatermark();
  }, []);

  return (
    <PolotnoContainer style={{ width: "100vw", height: "100vh" }}>
      <SidePanelWrap>
        <SidePanel store={store} sections={sections} defaultSection="Gabarits" />
      </SidePanelWrap>
      <WorkspaceWrap>
        <Toolbar store={store} downloadButtonEnabled />
        <Workspace store={store} />
        <ZoomButtons store={store} />
        <PagesTimeline store={store} />
      </WorkspaceWrap>
    </PolotnoContainer>
  );
};

export default Editor;

And here is my App.js file:

// src/App.js
import React from "react";
import Editor from "./Editor";

function App() {
  return <Editor />;
}

export default App;

If you need any further files, information, or more contents from my Sections.js file, I can include it.

How can make the player and bots choose cards to pass to the player clockwise to them?

I’m trying to do an assignment where I have to create the card game Game of Hearts.

Here is an example game (scroll down for rules): https://cardgames.io/hearts/

Right now, I am trying to implement the part where the player selects 3 cards to pass to the player next to them, and the bots will also automatically do the same thing after pressing the ‘pass’ button. However, I can’t seem to get the pass button to appear. I also believe I have to put each card in a container to make them clickable, but I’m not 100% how to go about implementing that as well. I am only supposed to be updating the view, not the model or controller.

Here is the model:

import {HU} from "./hearts_utils.js";

export class HeartsModel extends EventTarget {
    #playerNames;
    #state;
    #scorelog;
    #hands;
    #current_trick;
    #collected_tricks;
    #passing;

constructor() {
    super();

    this.#playerNames = {
        north: null,
        east: null,
        south: null,
        west: null
    };

    this.#state = 'uninitialized';

    this.#scorelog = [];

    this.#hands = {
        north: null,
        east: null,
        south: null,
        west: null
    };

    this.#collected_tricks = {
        north: [],
        east: [],
        south: [],
        west: []
    };
}

// Private methods
#dealCards () {
    let deck = [];
    ['spades', 'hearts', 'diamonds', 'clubs'].forEach(suit => {
        [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14].forEach(rank => {
            deck.push(new Card(suit, rank));
        })
    });
    deck = deck.map((card) => [card, Math.random()]).sort((a,b) => a[1]-b[1]).map((cpair) => cpair[0]);
    return {
        north: new Hand(deck.slice(0,13)),
        east: new Hand(deck.slice(13,26)),
        south: new Hand(deck.slice(26,39)),
        west: new Hand(deck.slice(39,52))
    };
}

// These methods are only available to controller to update model.
// They should never be called from view objects.

initialize (north_name, east_name, south_name, west_name) {
    if (this.#state != 'uninitialized') return;

    this.#playerNames.north = north_name;
    this.#playerNames.east = east_name;
    this.#playerNames.south = south_name;
    this.#playerNames.west = west_name;

    this.#scorelog = [];
    this.setupGame('right');
};

passCards (cards_to_pass) {
    if (this.#state != 'passing') return;

    HU.positions.forEach(p => this.#hands[p].remove(cards_to_pass[p]));
    HU.positions.forEach(p => {
        let pass_to = HU.passing_maps[this.#passing][p];
        this.#hands[pass_to].add(cards_to_pass[p]);
    });

    this.#state = 'playing';

    let lead = HU.positions.find(p => this.#hands[p].contains(Card.TWO_OF_CLUBS));
    
    this.#current_trick = new Trick(lead);

    this.dispatchEvent(new Event('stateupdate'));
    this.dispatchEvent(new Event('trickstart'));
};

playCardIntoTrick (position, card) {
    if (this.#state != 'playing') return;

    this.#hands[position].remove([card]);
    this.#current_trick.playCard(card);
    this.dispatchEvent(new CustomEvent('trickplay', {detail: {
        position: position,
        card: card
    }}));

    if (this.#current_trick.isComplete()) {
        this.dispatchEvent(new Event('trickend'));
    } 
};

collectTrick (position) {
    if (this.#state != 'playing') return;

    this.#collected_tricks[position].push(this.#current_trick);
    this.dispatchEvent(new CustomEvent('trickcollected', {detail: {
        position: position,
        trick: this.#current_trick}
    }));
    this.#current_trick = null;
};

setupTrick (position) {
    if (this.#state != 'playing') return;

    this.#current_trick = new Trick(position);
    this.dispatchEvent(new Event('trickstart'));
};

updateScoreLog (scorelog_entry, moonshooter) {
    this.#scorelog.push(scorelog_entry);
    this.dispatchEvent(new CustomEvent('scoreupdate', {detail: {
        entry: scorelog_entry,
        moonshooter: moonshooter
    }}));
};

setupGame (passing) {
    if ((this.#state == 'complete') || (this.#state == 'passing')) return;

    this.#passing = passing;
    this.#hands = this.#dealCards();
    this.#current_trick = null;
    this.#collected_tricks = {
        north: [],
        east: [],
        south: [],
        west: []
    };
    this.#state = 'passing';

    this.dispatchEvent(new Event('stateupdate'));
};

matchOver () {
    this.#state = 'complete';
    this.dispatchEvent(new Event('stateupdate'));
};

// These methods are available to view objects to query model information 

getState () {
    return this.#state;
}

getPlayerName (position) {
    return this.#playerNames[position];
};

getCurrentGamePoints (position) {
    return this.#collected_tricks[position].reduce((sum, trick) => sum + trick.getPoints(), 0);
};

getScoreLog () {
    return this.#scorelog;
};

getScore (position) {
    return this.#scorelog.reduce((score, entry) => score + entry[position], 0);
}

getPassing() {
    return this.#passing;
}

getCurrentTrick () {
    return this.#current_trick;
}

getTricksLeft () {
    return 13 - HU.positions.reduce((sum, p) => sum += this.#collected_tricks[p].length, 0);
}

getHand (position) {
    return this.#hands[position];
}

getCollectedTricks(position) {
    return this.#collected_tricks[position];
}
}

export class Card {
    #suit
    #rank

static TWO_OF_CLUBS = new Card('clubs', 2);
static QUEEN_OF_SPADES = new Card('spades', 12);

constructor (suit, rank) {
    this.#suit = suit;
    this.#rank = rank;
}

toString() {
        return `${this.getRankName()} of ${this.getSuit()}`;
}

getSuit() {
    return this.#suit;
}

getRank() {
    return this.#rank;
}

getRankName() {
    let honors_map = {};
    honors_map[11] = 'jack';
    honors_map[12] = 'queen';
    honors_map[13] = 'king';
    honors_map[14] = 'ace';

    return this.#rank < 11 ? this.#rank.toString() : honors_map[this.#rank];
}

equals(other) {
    return (other.getRank() == this.#rank) && (other.getSuit() == this.#suit);
}
}


export class Trick {
    #lead;
    #next_to_play;
    #played_by_position;

static #next_to_play_map = {
    north: 'east',
    east: 'south',
    south: 'west',
    west: 'north'
};

constructor(lead) {
    this.#lead = lead;
    this.#next_to_play = lead;
    this.#played_by_position = {
        north: null,
        east: null,
        south: null,
        west: null
    }
}

getLead() {
    return this.#lead;
}

getLeadSuit() {
    return this.getCard(this.getLead()).getSuit();
}

nextToPlay () {
    return this.#next_to_play;
}

playCard (card) {
    this.#played_by_position[this.#next_to_play] = card;
    this.#next_to_play = this.isComplete() ? null : Trick.#next_to_play_map[this.#next_to_play];
}

getCard(position) {
    return this.#played_by_position[position];
}

isComplete() {
    return !HU.positions.find(p => this.#played_by_position[p] == null);
};

getPoints() {
    return HU.positions.map(p => this.#played_by_position[p])
                       .filter(c => c != null)
                       .reduce((points, c) => points + 
                                (c.equals(Card.QUEEN_OF_SPADES) ? 13 : 
                                 ((c.getSuit() == 'hearts') ? 1 : 0)), 0);
}

toString() {
    return `next to play: ${this.#next_to_play}
north: ${this.#played_by_position.north}
east : ${this.#played_by_position.east}
south: ${this.#played_by_position.south}
west : ${this.#played_by_position.west}
`
    }
}

export class Hand extends EventTarget {
    #cards;

constructor (cards) {
    super();
    this.#cards = cards;
}

contains (card) {
    return this.#cards.some(c => c.equals(card));
}

hasSuit(suit) {
    return this.#cards.some(c => c.getSuit() == suit);
}

hasOnlyHearts() {
    return !this.#cards.some(c => c.getSuit() != 'hearts');
}

add (cards) {
    if (cards.length == 0) return;

    this.#cards.push(...cards);
    this.dispatchEvent(new CustomEvent('update', {detail:
        {type: 'add', cards: [...cards]}}));
}

remove (cards) {
    if (cards.length == 0) return;
    
    cards.forEach((c_to_remove) => {
        this.#cards = this.#cards.filter((c) => !c.equals(c_to_remove));
    });
    this.dispatchEvent(new CustomEvent('update', {detail:
        {type: 'remove', cards: [...cards]}}));
}

getCards () {
    return [...this.#cards];
}

toString() {
    return `Hearts  : ${this.#cards.filter(c=> c.getSuit() == 'hearts').sort((a,b) => a.getRank() - b.getRank()).map(c => c.getRankName()).join()}
Spades  : ${this.#cards.filter(c=> c.getSuit() == 'spades').sort((a,b) => a.getRank() - b.getRank()).map(c => c.getRankName()).join()}
Diamonds: ${this.#cards.filter(c=> c.getSuit() == 'diamonds').sort((a,b) => a.getRank() - b.getRank()).map(c => c.getRankName()).join()}
Clubs   : ${this.#cards.filter(c=> c.getSuit() == 'clubs').sort((a,b) => a.getRank() - b.getRank()).map(c => c.getRankName()).join()}
`;
    }
}

Here is the controller:

import { HU } from "./hearts_utils.js";
import { Card } from "./hearts_model.js";

export class HeartsController {
#model;
#cards_to_pass;
#hearts_broken;

constructor(model) {
    this.#model = model;
    this.#model.addEventListener('trickend', () => this.#handleTrickEnd());
}

#doAsync() {
    return new Promise((resolve) => setTimeout(resolve, 0));
}

startGame(north_name, east_name, south_name, west_name) {
    this.#cards_to_pass = {
        north: [],
        east: [],
        south: [],
        west: []
    };
    this.#hearts_broken = false;
    this.#doAsync().then(() => {
        this.#model.initialize(north_name, east_name, south_name, west_name);
    });
}

passCards(position, cards) {
    if (this.#model.getState() != 'passing') {
        alert('Controller error: attempt to pass cards when not in passing state');
        return;
    }

    if (this.#model.getPassing() == 'none') {
        alert('Controller error: attempt to pass cards when passing is none');
        return;
    }

    if (cards.length != 3) {
        alert('Controller error: attempt to pass more/less than three cards');
        return;
    }

    let hand = this.#model.getHand(position);
    if (cards.some(c => !hand.contains(c))) {
        alert('Controller error: attempt to pass a card not in the hand of position');
        return;
    }

    if (this.#cards_to_pass[position].length != 0) {
        alert('Controller error: attempt to pass cards twice');
        return;
    }

    this.#cards_to_pass[position] = [...cards];

    if (!HU.positions.find(p => this.#cards_to_pass[p].length == 0)) {
        this.#doAsync().then(() => {
            this.#model.passCards(this.#cards_to_pass);
            this.#cards_to_pass = {
                north: [],
                east: [],
                south: [],
                west: []
            }
        });
    }
}

isPlayable(position, card) {
    let cur_trick = this.#model.getCurrentTrick();
    let hand = this.#model.getHand(position);

    if (cur_trick.getLead() == position) {
        // If lead of first trick in game, then only 2 of clubs is playable.
        if (this.#model.getTricksLeft() == 13) {
            return card.equals(Card.TWO_OF_CLUBS);
        }

        // Can only lead hearts if hearts are broken or hand only has hearts.
        if (card.getSuit() == 'hearts') {
            if (!this.#hearts_broken) {
                return hand.hasOnlyHearts();
            }
        }
        return true;
    } else {
        let lead_card = cur_trick.getCard(cur_trick.getLead());
        if (!lead_card) {
            return false;
        }
        if (!hand.hasSuit(lead_card.getSuit())) {
            return true;
        }
        return card.getSuit() == lead_card.getSuit();
    }
}

playCard(position, card) {
    if (this.#model.getState() != 'playing') {
        alert('Controller error: playCard called when not in playing state.');
        return;
    }

    if (this.#model.getCurrentTrick().nextToPlay() != position) {
        alert('Controller error: attempt to play card out of position');
        return;
    }

    if (!this.#model.getHand(position).contains(card)) {
        alert('Controller error: attmept to play card not in hand');
        return;
    }

    if (!this.isPlayable(position, card)) {
        alert('Controller error: attmept to play unplayable card');
        return;
    }

    this.#doAsync().then(() => {
        this.#model.playCardIntoTrick(position, card);
        this.#hearts_broken ||= (card.getSuit() == 'hearts');
    });
}

#handleTrickEnd() {
    // Figure out who won.
    let cur_trick = this.#model.getCurrentTrick();
    let winner = cur_trick.getLead();
    let winning_card = cur_trick.getCard(winner);

    HU.positions.forEach(position => {
        if (winner != position) {
            let card = cur_trick.getCard(position);
            if ((card.getSuit() == winning_card.getSuit()) &&
                (card.getRank() > winning_card.getRank())) {
                winning_card = card;
                winner = position;
            }
        }
    });
    
    this.#doAsync().then(() => this.#model.collectTrick(winner))
        .then(() => {
            if (this.#model.getTricksLeft() > 0) {
                this.#model.setupTrick(winner);
                return false;
            } else {
                // Game's over.
                // Create scorelog entry (detect shooting the moon)
                // Update scorelog
                // Detect possible match end.
                // Figure out next passing mode and set up next game.

                //                let scorelog_entry = HU.positions.reduce((entry, pos) => entry[pos] = this.#model.getCurrentGamePoints(pos), {});
                let scorelog_entry = {
                    north: this.#model.getCurrentGamePoints('north'),
                    east: this.#model.getCurrentGamePoints('east'),
                    south: this.#model.getCurrentGamePoints('south'),
                    west: this.#model.getCurrentGamePoints('west'),
                };

                let moonshooter = HU.positions.find(p => scorelog_entry[p] == 26);
                if (moonshooter) {
                    HU.positions.forEach(p => {
                        scorelog_entry[p] = (scorelog_entry[p] + 26) % 52;
                    });
                } else {
                    moonshooter = null;
                }

                this.#model.updateScoreLog(scorelog_entry, moonshooter);
                return true;
            }
        })
        .then((game_over) => {
            if (game_over) {
                if (HU.positions.find(p => this.#model.getScore(p) >= 100)) {
                    this.#model.matchOver();
                } else {
                    let next_passing = HU.next_passing_map[this.#model.getPassing()];
                    this.#hearts_broken = false;
                    this.#cards_to_pass = {
                        north: [],
                        east: [],
                        south: [],
                        west: []
                    };
                    this.#model.setupGame(next_passing);

                    if (next_passing == 'none') {
                        this.#doAsync().then(() => this.#model.passCards(this.#cards_to_pass));
                    }
                }
            }
        });
}

}

And here is the view (only file that can be altered)

import {HeartsRobotKmp} from "./hearts_robot_kmp.js";
import {Card, Hand, Trick} from "./hearts_model.js";
import {HU} from "./hearts_utils.js";

export class HeartsView {

#model
#controller

constructor(model, controller) {
    this.#model = model;
    this.#controller = controller;
    this.dealPressed = false;
}

render(render_div) {
    this.render_div = render_div;
    render_div.innerHTML = '';

this.#model.addEventListener("stateupdate", () => this.update());
this.#model.addEventListener("trickplay", () => this.updateTrick());
this.#model.addEventListener("scoreupdate", () => this.updateScores());

if (this.#model.getState() === 'uninitialized') {
    this.renderNameSetup();
}  else {
    this.renderGameTable();
    this.update();
}
}

renderNameSetup() {
    this.render_div.innerHTML = `
        <div class="name-setup">
            <h2>Welcome to Hearts!</h2>
            <label>Your Name: <input type="text" id="player-name" placeholder="You (South)"></label>
            <label>West Player: <input type="text" id="west-name" ></label>
            <label>North Player: <input type="text" id="north-name"></label>
            <label>East Player: <input type="text" id="east-name"></label>
            <button id="confirm-names">Start Game</button>
        </div>
    `;

    document.getElementById('confirm-names').addEventListener('click', () => {
        const south = document.getElementById('player-name').value || "You";
        const west = document.getElementById('west-name').value;
        const north = document.getElementById('north-name').value;
        const east = document.getElementById('east-name').value;

        this.#model.getPlayerName('north');
        this.#model.getPlayerName('east');
        this.#model.getPlayerName('south');
        this.#model.getPlayerName('west');

        this.#controller.startGame(north, east, south, west);
    });
}

renderGameTable() {
    const names = {
        north: this.#model.getPlayerName('north'),
        east: this.#model.getPlayerName('east'),
        south: this.#model.getPlayerName('south'),
        west: this.#model.getPlayerName('west')
    }
    this.render_div.innerHTML = `
        <div id="game-table" style="display: grid; grid-template-areas:
            'north north north'
            'west center east'
            'south south south';
            gap: 20px; justify-items: center; align-items: center;">
            
            <div id="north" style="grid-area: north;">${names.north}
            <div id="north-hand" class="hand"></div>
            </div>
            
            <div id="west" style="grid-area: west;">${names.west}
                <div id="west-hand" class="hand"></div>
            </div>
            <div id="center" style="grid-area: center;">
                <div id="trick-area">Trick Goes Here</div>
                <button id="deal-button">Deal</button>
                <button id="pass-button" style="display:none;">Pass</button>
                <div id="score-table">Scores Go Here</div>
            </div>
            <div id="east" style="grid-area: east;">${names.east}
                <div id="east-hand" class="hand"></div>
            </div>
            <div id="south" style="grid-area: south;">${names.south}
                <div id="south-hand" class="hand"></div>
            </div>
        </div>
    `;

    document.getElementById("deal-button").addEventListener("click", () => {
        this.dealPressed = true;
        this.renderHands();
                  
    });
    


}


update() {
    const state = this.#model.getState();
    if (!document.getElementById("game-table")) {
        this.renderGameTable();
    }
    if (state === 'passing') {
        if (this.dealPressed) {
            this.showPassCardsUI();
        }
    } else if (state === 'playing') {
        this.showPlayableCards();
        this.updateTrick();
    } else if (state === 'complete') {
        this.showWinner();
    }
    this.updateScores();
}

renderHands() {
    const positions = ["north", "east", "south", "west"];

    positions.forEach(position => {
        const hand = this.#model.getHand(position); // A Hand object
        if (!hand) return;

        const cards = hand.getCards(); // Array of Card objects
        const container = document.getElementById(`${position}-hand`);
        container.innerHTML = '';

        cards.forEach(card => {
            const cardDiv = document.createElement("div");
            cardDiv.className = "card";
            cardDiv.textContent = `${card.getRankName()} of ${card.getSuit()}`;
            container.appendChild(cardDiv);
        });
    });
}

showPassCardsUI() {
    const southHandDiv = document.getElementById('south-hand');
    const passButton = document.getElementById('pass-button');
    passButton.style.display = 'inline-block';

    const selectedCards = new Set();

    // Make each card clickable
    [...southHandDiv.children].forEach(cardDiv => {
        cardDiv.addEventListener('click', () => {
            const cardText = cardDiv.textContent;
            cardDiv.classList.toggle('selected');

            if (selectedCards.has(cardText)) {
                selectedCards.delete(cardText);
            } else if (selectedCards.size < 3) {
                selectedCards.add(cardText);
            }

            passButton.disabled = selectedCards.size !== 3;
        });
    });

    passButton.disabled = true;

    passButton.addEventListener('click', () => {
        // Convert text labels back to actual Card objects from model
        const southHand = this.#model.getHand('south').getCards();
        const selectedCardObjs = [...selectedCards].map(text =>
            southHand.find(c => `${c.getRankName()} of ${c.getSuit()}` === text)
        );

        // Player passes cards
        this.#controller.passCard("south", selectedCardObjs);

        // Other players pass random cards
        for (const pos of ["north", "east", "west"]) {
            const hand = this.#model.getHand(pos).getCards();
            const randomCards = this.getRandomCards(hand, 3);
            this.#controller.passCard(pos, randomCards);
        }

        passButton.style.display = 'none'; // Hide after passing
    });
}

getRandomCards(cards, count) {
    const shuffled = [...cards].sort(() => 0.5 - Math.random());
    return shuffled.slice(0, count);
}


showPlayableCards() { /* To be implemented */ }
updateTrick() { /* To be implemented */ }
updateScores() { /* To be implemented */ }
showWinner() { /* To be implemented */ }

renderMatchOver() {
    // todo
}

}

any help would be appreciated, Thank you so much!

How to redirect to GitHub login page using Clerk?

I’m using Clerk for authentication in my web application. I successfully created that social connection but I want to redirect users to the GitHub login page (using Clerk’s GitHub OAuth provider) when they click a “Login with GitHub” button — without showing Clerk’s default sign-in page first.

How can I achieve this?
Is there a specific method or URL I should use to directly trigger GitHub OAuth via Clerk?
mention that i don’t want to use clerks Components.

Any code snippet or reference would be greatly appreciated.
Thanks!

Prismic Slice machine is not working with the raw official starter

I had a problem opening the slice machine panel in my app so I tried to install a brand new repo with the official Prismic nextJS starter. Same problem. When I open localhost:9999, I got this :

hook.js:608 Error: react-modal: No elements were found for selector #__next.
at l (…

re-fetch a fresh copy SyntaxError: Unexpected token ‘L’, “Loading{“p”… is not valid JSON
at JSON.parse ()

Basically it’s saying that pageProps is empty

Here is the package.json

{
  "name": "nextjs-starter-prismic-multi-page",
  "version": "0.0.0",
  "private": true,
  "license": "Apache-2.0",
  "author": "Prismic <[email protected]> (https://prismic.io)",
  "scripts": {
    "dev": "concurrently "npm:next:dev" "npm:slicemachine" --names "next,slicemachine" --prefix-colors blue,magenta",
    "next:dev": "next",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "slicemachine": "start-slicemachine",
    "format": "prettier --write ."
  },
  "dependencies": {
    "@prismicio/client": "^7.17.2",
    "@prismicio/next": "^2.0.1",
    "@prismicio/react": "^3.2.1",
    "clsx": "^2.1.1",
    "next": "^15.1.6",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
    "@slicemachine/adapter-next": "^0.3.75",
    "@tailwindcss/aspect-ratio": "^0.4.2",
    "@tailwindcss/postcss": "^4.0.1",
    "@types/node": "^22.12.0",
    "@types/react": "^19.0.8",
    "@types/react-dom": "^19.0.3",
    "concurrently": "^9.1.2",
    "eslint": "^9.19.0",
    "eslint-config-next": "^15.1.6",
    "postcss": "^8.5.1",
    "prettier": "^3.4.2",
    "prettier-plugin-tailwindcss": "^0.6.11",
    "slice-machine-ui": "^2.15.2",
    "tailwindcss": "^4.0.1",
    "typescript": "^5.7.3"
  }
}

I was on node 23.
I tried to downgrade on v18.20.2, but it didn’t solved anything

Could it be a config problem in my laptop? 4 hours that I try everything and still at the same point.

How to make Shopify Admin API GraphQL call using offline session in Remix Flow Action handler?

I’m building a Shopify app using the Remix template (@shopify/shopify-app-remix). I have a Flow Action trigger that needs to make a GraphQL call to the Shopify Admin API from the backend action handler to fetch the shop’s email address (shop { email }).

The Flow action is triggered correctly, and I can authenticate the request using shopify.authenticate.flow(request). I can also successfully retrieve the shop’s offline session (which includes the accessToken) from my Prisma database using the sessionStorage provided by the library.

But I’m struggling to find the right way to instantiate an authenticated GraphQL client using this offline session to make the Admin API call.

Context:

Framework: Remix (using Shopify Remix App template)

Library: @shopify/shopify-app-remix (mention version if known, e.g., v3.x) / @shopify/shopify-api (mention version if known, e.g., v11.x)

Goal: Inside a Flow Action handler (a Remix action function), fetch the shop’s email using an offline token.

shopify.server.js Config:

// app/shopify.server.js (relevant parts)
import "@shopify/shopify-app-remix/adapters/node";
import {
  ApiVersion,
  AppDistribution,
  shopifyApp,
} from "@shopify/shopify-app-remix/server";
import { PrismaSessionStorage } from "@shopify/shopify-app-session-storage-prisma";
import prisma from "./db.server";

const shopify = shopifyApp({
  apiKey: process.env.SHOPIFY_API_KEY,
  apiSecretKey: process.env.SHOPIFY_API_SECRET || "",
  apiVersion: ApiVersion.January25, // Or your version
  scopes: process.env.SCOPES?.split(","), // Includes 'read_shop'
  appUrl: process.env.SHOPIFY_APP_URL || "",
  authPathPrefix: "/auth",
  sessionStorage: new PrismaSessionStorage(prisma),
  distribution: AppDistribution.AppStore,
  future: {
    unstable_newEmbeddedAuthStrategy: true,
    removeRest: false, // Currently set to false for testing, but also failed with true
  },
  // ...
});

export default shopify;
export const authenticate = shopify.authenticate;
export const sessionStorage = shopify.sessionStorage;
// ...

Prisma Session Model:

// prisma/schema.prisma
model Session {
  id            String    @id
  shop          String
  state         String
  isOnline      Boolean   @default(false)
  scope         String?
  expires       DateTime?
  accessToken   String
  userId        BigInt?
  firstName     String?
  lastName      String?
  email         String?
  accountOwner  Boolean?
  locale        String?
  collaborator  Boolean?
  emailVerified Boolean?
}

Problem:

Inside my Flow action handler (app/routes/api.flow.action.send-email.jsx), after successfully getting the shopDomain and fetching the offlineSession using sessionStorage.findSessionsByShop(shopDomain), I cannot create a working GraphQL client.

Attempts Made & Errors:

// Inside the action function, after getting shopDomain and offlineSession

// Method 1: Standard path via shopify.api.clients
try {
  const client = new shopify.api.clients.Graphql({ session: offlineSession });
  // FAILED with: TypeError: Cannot read properties of undefined (reading 'clients')
} catch (e) { console.error("Method 1 Failed", e); }

// Method 2: Using shopify.admin (doesn't seem correct for offline)
try {
  const adminContext = await shopify.admin({ session: offlineSession });
  // FAILED with: TypeError: shopify.admin is not a function
} catch (e) { console.error("Method 2 Failed", e); }

// Method 3: Direct import of GraphqlClient (only session)
try {
  // import { GraphqlClient } from "@shopify/shopify-api";
  const client = new GraphqlClient({ session: offlineSession });
  // FAILED with: TypeError: Cannot read properties of undefined (reading 'isCustomStoreApp')
} catch (e) { console.error("Method 3 Failed", e); }

// Method 4: Direct import + specific config from main shopify object
try {
  // import { GraphqlClient } from "@shopify/shopify-api";
  // import shopify, { apiVersion } from "../shopify.server";
  const client = new GraphqlClient({
       config: shopify.config, // Also tried passing specific props like apiVersion, hostName etc.
       session: offlineSession
      });
   // FAILED with: TypeError: Cannot read properties of undefined (reading 'isCustomStoreApp' or 'hostName')
} catch (e) { console.error("Method 4 Failed", e); }

// Method 5: Using admin client from authenticate.flow context
// const flowContext = await shopify.authenticate.flow(request);
// if (flowContext && flowContext.admin && typeof flowContext.admin.graphql === 'function') {
//    const response = await flowContext.admin.graphql(SHOP_EMAIL_QUERY);
//    // FAILED with: 401 Unauthorized (suggests this client isn't using the correct offline token)
// }

Question:

What is the correct way to instantiate or obtain an authenticated Shopify Admin API GraphQL client within a Remix backend route (like a Flow action handler), using an offline session token retrieved from sessionStorage, especially considering the potential impact of the removeRest: true future flag (even if currently disabled)?

I can successfully make the shop { email } query using the GraphiQL tool provided by shopify app dev, which confirms the offline session and read_shop scope are valid. I just can’t get the client working in the action handler code.

Any help or examples would be greatly appreciated!

Bootstrap DatePicker disappearing

My CSS code is like below.

<link href="http://127.0.0.1:8000/assets/libs/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="http://127.0.0.1:8000/assets/libs/jquery-ui/jquery-ui.min.css" rel="stylesheet">
<link rel="stylesheet" href="http://127.0.0.1:8000/assets/css/animate.min.css">
<link rel="stylesheet" href="http://127.0.0.1:8000/assets/libs/owl-carousel/owl.carousel.min.css">
<link rel="stylesheet" href="http://127.0.0.1:8000/assets/libs/owl-carousel/owl.theme.default.min.css">
<link rel="stylesheet" href="http://127.0.0.1:8000/assets/libs/venobox/venobox.min.css">
<link href="http://127.0.0.1:8000/assets/css/icons.min.css" rel="stylesheet">
<link href="http://127.0.0.1:8000/assets/css/style.css" rel="stylesheet">
<link href="http://127.0.0.1:8000/assets/css/extra-style.css" rel="stylesheet">
<link rel="stylesheet" href="http://127.0.0.1:8000/assets/css/responsive.css">
<link rel="stylesheet" href="http://127.0.0.1:8000/assets/sweetalert2/sweetalert2.css">
<link rel="stylesheet" href="http://127.0.0.1:8000/assets/css/toastr.min.css">
<link rel="stylesheet" href="http://127.0.0.1:8000/assets/css/dropify.css">
<link href="http://127.0.0.1:8000/assets/libs/dropzone/dropzone.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="http://keith-wood.name/css/jquery.signature.css">
<link href="http://127.0.0.1:8000/assets/css/select2.min.css" rel="stylesheet" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/build/css/intlTelInput.css">        
<link href="http://127.0.0.1:8000/assets/libs/datatables.net-bs4/css/dataTables.bootstrap4.min.css" rel="stylesheet">
<link href="http://127.0.0.1:8000/assets/libs/datatables.net-buttons-bs4/css/buttons.bootstrap4.min.css" rel="stylesheet">
<link href="http://127.0.0.1:8000/assets/libs/datatables.net-select-bs4/css/select.bootstrap4.min.css" rel="stylesheet">
<link href="http://127.0.0.1:8000/assets/libs/datatables.net-responsive-bs4/css/responsive.bootstrap4.min.css" rel="stylesheet">

My JavaScript Code is like below.

<script src="http://127.0.0.1:8000/assets/libs/jquery/jquery-3.6.0.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/jquery-easing/jquery.easing.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/jquery-ui/jquery-ui.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/owl-carousel/owl.carousel.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/venobox/venobox.min.js"></script>
<script src="http://127.0.0.1:8000/assets/js/iconify.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/metismenu/metisMenu.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/simplebar/simplebar.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/dropzone/dropzone.js"></script>
<script src="http://127.0.0.1:8000/assets/js/pages/form-file-upload.init.js"></script>
<script src="http://127.0.0.1:8000/assets/sweetalert2/sweetalert2.all.js"></script>
<script src="http://127.0.0.1:8000/assets/js/toastr.min.js"></script>
<script src="http://127.0.0.1:8000/assets/js/custom/common.js"></script>
<script src="http://127.0.0.1:8000/assets/js/moment.js"></script>
<script src="http://127.0.0.1:8000/assets/js/dropify.js"></script>
<script src="http://127.0.0.1:8000/assets/js/select2.min.js"></script>
<script src="http://127.0.0.1:8000/assets/js/custom/information-view.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/signature_pad/1.5.3/signature_pad.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/datatables.net/js/jquery.dataTables.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/datatables.net-bs4/js/dataTables.bootstrap4.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/datatables.net-buttons/js/dataTables.buttons.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/datatables.net-buttons-bs4/js/buttons.bootstrap4.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/datatables.net-buttons/js/buttons.html5.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/datatables.net-buttons/js/buttons.print.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/datatables.net-buttons/js/buttons.colVis.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/datatables.net-keytable/js/dataTables.keyTable.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/datatables.net-select/js/dataTables.select.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/datatables.net-responsive/js/dataTables.responsive.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/datatables.net-responsive-bs4/js/responsive.bootstrap4.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/datatable/jszip/jszip.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/datatable/pdfmake/pdfmake.min.js"></script>
<script src="http://127.0.0.1:8000/assets/libs/datatable/pdfmake/vfs_fonts.js"></script>
<script src="http://127.0.0.1:8000/assets/js/custom/gas_safeties.js"></script>

I am using Bootstrap Datepicker in my Web Page. My DatePicker code is like below.

$(".datepicker").datepicker({
        dateFormat: "dd-mm-yy", 
        changeMonth: true, 
        changeYear: true, 
        yearRange: "1950:2050", 
        showButtonPanel: true,
    });

My Datepicker is look like below.

enter image description here

My issue is when I click on drop down arrow of Month then list of Month appears but the list disappears at once. Month list is not stay.

ESLint import sorting rule not enforcing React import at the top of the file

Project Setup:
I have a new React project set up with TypeScript and Vite. I’m using ESLint for code linting with a custom configuration that enforces proper import ordering. The project is set up with the following dependencies:

  • React with TypeScript.
  • Vite as the build tool.

I have configured ESLint to ensure that imports are ordered properly, with the react import appearing at the top of the file.

eslint.config.js

import js from '@eslint/js';
import importPlugin from 'eslint-plugin-import';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import globals from 'globals';
import tseslint from 'typescript-eslint';

export default tseslint.config(
  {
    ignores: ['dist'],
  },

  js.configs.recommended,
  ...tseslint.configs.recommended,

  {
    files: ['**/*.{ts,tsx,js,jsx}'],
    languageOptions: {
      ecmaVersion: 2020,
      sourceType: 'module',
      globals: {
        ...globals.browser,
        React: 'readonly', // Make React available globally
      },
    },
    plugins: {
      'react-hooks': reactHooks,
      'react-refresh': reactRefresh,
      import: importPlugin,
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],

      // formatting
      semi: ['error', 'always'],
      indent: ['error', 2],
      'no-mixed-spaces-and-tabs': 'error',

      // import sorting - enhanced rules
      'import/first': 'error', // Ensures all imports are at the top of the file
      'import/no-duplicates': 'error', // Prevents duplicate imports
      'import/newline-after-import': 'error', // Enforces a newline after import statements
      'import/order': [
        'error',
        {
          groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
          pathGroups: [
            {
              pattern: 'react',
              group: 'external',
              position: 'before',
            },
            {
              "pattern": "assets/**",
              "group": "internal",
              "position": "before"
            },
          ],
          pathGroupsExcludedImportTypes: ['react'],
          'newlines-between': 'always',
          alphabetize: {
            order: 'asc',
            caseInsensitive: true,
          },
        },
      ],
    },
  }
);

App.tsx

import reactLogo from './assets/react.svg';

import { useState } from 'react';

import viteLogo from '/vite.svg';
import './App.css';

function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>
        <a href="https://vite.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
   
    </>
  );
}

export default App;

Problem:

I expect ESLint to enforce that the react import appears at the top of the file (based on the ‘import/order’ rule), but it does not show an error when react is below other imports like viteLogo. However, when I move the react import below viteLogo, I do see the error saying react import should occur before import of ./assets/react.svg eslint(import/order)

I have the following import sorting rules in place:

  • ‘import/first’: ‘error’ (ensures imports appear at the top).

  • ‘import/order’: […] (sorting groups with a specific position for react).

What could be the issue with my configuration that prevents the react import from being enforced at the top of the file?

app.use(express.json()) is not working in express version [email protected] , but works fine in [email protected]

import express from "express";
const app = express();
import dotenv from "dotenv";
import path from "path";
let _dirname = path.resolve();
import cors from "cors"
import { router } from "./routes.js";
import connectDB from "./src/helper/dbConnection.js";
dotenv.config();
const PORT = process.env.PORT;

app.use(cors)
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(_dirname, "public")));
router(app);

connectDB();

app.listen(PORT, () => {
  console.log("Server listening on PORT:", PORT);
});

**
I am getting error like
TypeError: Cannot destructure property ‘name’ of ‘req.body’ as it is undefined.

I tried a fix for it
instead of writing it like

  const { name } = req.body;

I replaced it with this

  const { name } = req.body ||{};

it resolves the issue but i need the solution from the app.use(express.json()) itself

What is the replacement for app.use(express.json()) in the latest version ? Do they have any ?

how to make a floating copy text that slowly disspear for my website

so my current website https://asmophacy.github.io/website/
does this ()
then it go and copy it to clipboard this is my current js code for my email

function copyText() {
    /* Copy text into clipboard */
    navigator.clipboard.writeText("[email protected]").then(() => {
        // Optional: Add feedback here if you want
        console.log("Email copied via copyText function!");
        // alert("Email copied!"); // Simple feedback
    }).catch(err => {
        console.error("Failed to copy email via copyText function:", err);
    });
}

document.addEventListener('DOMContentLoaded', () => {
// email copy code
    const emailLink = document.querySelector('a.social-button.proton');

    if (emailLink) {


        emailLink.addEventListener('click', (event) => {
            event.preventDefault(); // Prevent navigating to the link's href
            copyText(); // Call your function here

            // Optional: Add the visual feedback logic here if needed,
            // using iconSpan and originalIconHTML like before,
            // since copyText() itself doesn't handle it.
            const iconSpan = emailLink.querySelector('span.icon');
            if (iconSpan) {
                const originalIconHTML = iconSpan.innerHTML;
                iconSpan.textContent = 'Copied';
                 setTimeout(() => {
                    iconSpan.innerHTML = originalIconHTML;
                 }, 2000);
            }
        if (svg) {
            const originalIconHTML = iconSpan.innerHTML;
            iconSpan.textContent = 'Copied';
             setTimeout(() => {
                iconSpan.innerHTML = originalIconHTML;
             }, 2000);
        }
            
            
            
    });

    } else {
        console.warn('Could not find the email link element (a.social-button.proton).');

i want it to instead be like steam where it float and slowly dissapear how do i do that? like it keep the original details there and just have a floating copied text goes up and dissapear
thanks!
(my first post)

In JavaScript how can I get the text insertion point where dragged text was dropped into a texteara?

When text is selected and then dragged into a textarea the browser moves the caret around in the textarea as you drag, showing where the dragged text will be dropped, and when the text is dropped it goes into place at that spot. This is all good default behavior, but now I want to modify the dropped text, but only if it’s dropped into an empty line. In this case I want the dropped text to be surrounded with extra newlines so it ends up on its own paragraph. If the text is dropped into the middle of an existing paragraph I don’t want to change the dropped text.

My problem is how to know where the text is dropped. During either the ‘drop’ event or the ‘dragover’ event I can’t figure out what property to access or what function to call to get the caret position. My workaround at this point is to require the user to first click in the target textarea at the position where the dragged text should be dropped, then I call event.preventDefault() during dragover so that the caret isn’t visible at all during dragover so it’s clear the insertion point can’t be changed, then during ‘drop’ I check event.target.selectionStart to know where the text was dropped.

This basically works, but it’s unnatural for the user to have to click in the desired spot ahead of time. A standard textarea lets the insertion point change during dragover, and I want my special textarea to work that way, too.

Why is my React component not re-rendering after updating state?

I’m building a React app, and I have a component that fetches data and sets it in the state. However, when the state updates, the component doesn’t seem to re-render. I expected the updated data to be shown automatically after the state change, but it’s not happening.

I tried using useState to manage the data and calling the state setter function inside an API fetch. I expected that updating the state would cause the component to re-render with the new data, but the component remains the same. I also tried forcing an update manually, but it didn’t seem to help.