Create a timeline in a table shape

I’m trying to create a pretty specific component that should look like this:

enter image description here

I give it an array with id, date and it should display the event in a timeline manner but in a table manner also since each event must be placed inside the month, with different spacing based on the date. On top of that there should be a radio button under the table align to its event (onClick the event should make the radio button active).

So far I succeeded in doing the timeline and the event but with equal and not in a table.

I’m not looking for someone to code the entire thing but more for an idea of how to achieve this tricky component, some directions.

Thank you.

Node.js API Call Error: Cannot read properties of undefined (reading ‘chat’)

I’m working on an excel taskpane addin that calls the Openai API, but I’m getting the “Error: Cannot read properties of undefined (reading ‘chat’)” error msg.

The error seems to suggest that the chat property is being called on an undefined object within thesendToGPT() function. Click image below to see my code.

Code

Note that I set the API key in the environment variables and I verified that I’m on SDK v4.28

Appending to MediaSource after seeking to a nonbuffered position

This is the closest copy of my question: MediaSource appendBuffer at different positions

Essentially, I have a video file stored in S3 that I am able to successfully fetch byteranges of and load into a MediaSource buffer when I fetch sequentially (starting from 0 onwards). However, if I simply fetch a different range that does NOT start from 0 and load it into the buffer, I get no errors but the video does not play (blank video element).

Below, I have a reproducible example:

var videoElement = document.querySelector('video');
var mediaSource = new MediaSource();
var sourceBuffer;

// URL of the video file
var videoUrl = 'some-url';

// Event listener for when the MediaSource is opened
mediaSource.addEventListener('sourceopen', function() {
    sourceBuffer = mediaSource.addSourceBuffer('some-value');
    sourceBuffer.mode = "segments"
    // Fetch the video file with the appropriate byte range
    fetchVideo(videoUrl);
});

// Function to fetch the video data with the specified byte range
function fetchVideo(url) {
    var headers = {
        Range: 'bytes=1353653023-1494852067'
    };

    fetch(url, { headers: headers })
        .then(function(response) {
            return response.arrayBuffer();
        })
        .then(function(buffer) {
            // Append the fetched video data to the source buffer. When the line below is uncommented, playback still does not work.
            // sourceBuffer.timestampOffset += 83.0
            sourceBuffer.appendBuffer(buffer);
        })
        .catch(function(error) {
            console.error('Error fetching video:', error);
        });
}

// Set the MediaSource object as the source of the video element
videoElement.src = URL.createObjectURL(mediaSource);

My question is how can I fetch this byte range that does not start from 0? I also checked to see if this byte range was valid using ffmpeg:

some-user:~$ ffprobe -i video.webm -show_frames 
> -show_entries frame=pkt_pos 
> -read_intervals 01:23%+#1 
> -of default=noprint_wrappers=1:nokey=1 
> -hide_banner -loglevel panic
1353653023
some-user:~$ ffprobe -i video.webm -show_frames 
> -show_entries frame=pkt_pos 
> -read_intervals 01:30%+#1 
> -of default=noprint_wrappers=1:nokey=1 
> -hide_banner -loglevel panic
1494852067

Is there a Javascript library that allows to customize chart’s container so it has 3D effect

I need a 2 dimensional chart to appear as 3 dimensional like in the example below. Data will be displayed only using 2 axises (x and y) so there is basically no need to use 3D charts and adding z axis, but I need the chart to appear in 3D. Does anyone know if there are libraries that allow to do this kind of customizations?

enter image description here

I thought of using ploty.js but thought maybe there are other libraries like chart.js that allow to do this visualisation without adding 3rd axis.

How can i use a nestjs service function in a typescript file?

I have a service with a specific function and i have a controller with a route that executes the service function but now i need to execute that function in a cron job, i am using node-cron library. The thing is if i have call that service function in another typescrip file?

This is my typescript file where i have a cron job and i try to execute the service function.

cronJob.ts

@Injectable() export class SslCronJob { // @Inject(SslService) // private sslService: SslService;

constructor (
    @Inject(SslService)
    private readonly sslService: SslService
) {}

async upload() {
    await this.sslService.upload()
    cron.schedule('* * * * *', async () => {
        console.log('running a task every minute');
        try {
            await this.sslService.upload()
        } catch (err) {
            console.log("error: ", err)
        }
    });

}
}

and i try to execute this from main.ts

async function bootstrap() {
    ...
    ...
    const sslService: SslService = new SslService()
    let sslCron = new SslCronJob(sslService);
    sslCron.upload()
}

enter image description here

but i have this error when executes it, can access to model property.

Slider Navbar for a react site

I want to create a horizontal navbar and when I click on the links, I want a slider to appear on them, which moves from one link to another depending on the link clicked. However, I’m struggling a bit with this. I managed to create the slider, but I’m not very skilled at making it move based on the clicked links. I’m not very experienced with transitions. Thank you!

I’ve created the navbar exactly as I wanted, and next to it, I’ve created a slider from a div and applied some CSS to it. Now, the issue is that I want it to move to the link I press.

Detect any form field change to update a date updated field

I’m using Bootstrap Studio 5.3 to create a page with a form that has 22 fields in it.

One field is a “Date Updated” field that I’d like to have updated with the current date and time, any time something changed in one of the other fields (text inputs, select drop downs, check box group, textareas).

Currently, that Date Updated field is set to the date and time whenever the page loads.

How to detect any field change (except the Date Updated field), and then have the Date Updated field changed with the current date and time?

The field:
enter image description here

It’s attributes:
enter image description here

The JS:

//Account for timezones and formate date as mm/dd/yyyy HH:MM AM/PM
function getFormattedDateTime(date) {
  const options = {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    hour12: false,
  };

  const formatter = new Intl.DateTimeFormat(undefined, options);
  const formattedDateTime = formatter.format(date);

  return formattedDateTime.replace(
    /(d+)/(d+)/(d+), (d+):(d+)/,
    '$3-$1-$2T$4:$5'
  );
}

const date = new Date(Date.now());
const formattedDateTime = getFormattedDateTime(date);

document.querySelector('#date_started').value= formattedDateTime;
document.querySelector('#date_updated').value= formattedDateTime;

Found something kinda similar here Detect change in form

However, that solution only applies to a “Submit” event.

Not sure how to convert that to when any field changes instead, and then how to pass that event…or somehow trigger the existing JS above to run again but only update the #date_updated field.

Is it possible to link the footnote-marker back to the footnote-call for Paged Media?

I want to link the footnote mark with the footnote and vice verca at the bottom of my page.

I am using the CSS Paged Media Module with vivliostyle-cli to create a PDF based on HTML + CSS. I’m creating footnotes like this:

        page {
            counter-reset: footnote 0;
        }
         
        @page {
            @footnote {
                float: bottom;
            }
        }
         
        span.footnote { 
            float: footnote; 
        }
         
        .footnote::footnote-call {
            content: counter(footnote, decimal) " ";
            vertical-align: super;
            font-size: 0.8em;
        }
         
        .footnote::footnote-marker {
            content: counter(footnote, decimal);
            font-size: 14pt;
            display: inline-block;
            width: 2em;
            text-align: right;
        }
<html>
    <head>
      <link rel="stylesheet" type="text/css" href="styles.css" />
    </head>
    
    <body>
         <p>Example Text.<a href="#test"><span class="footnote"><span id="test">Footnote Text.</span></span></p>
    </body>
</html>

Result:

PDF with linked footnote

The w3 draft doesn’t seem to support linked footnotes (yet), but this way I managed to link from the footnote-call in the text to the footnote-marker. But how can I link back?

Drag start not selecting correct element

Code is in this Codepen. Looking at this code will make you understand this problem much better, I think at least.

The dragstart event is not referencing all elements that are in the array draggables. I am wondering if it is a problem with the forEach loop, but I’m not sure. Dragging from the sidebar into the containers works just as intended, but when trying to drag the item from container to container, the dragstart event doesn’t even pick up the existence of the item, only the dragover event notices anything.

I’ve tried pushing each new clone to the array, but even that doesn’t work. I’ve spent like 3 hours on this one problem, and I just cannot figure it out as this is my first javascript project (I’m learning js without a tutorial).

let draggables = Array.from(document.querySelectorAll(".left-item"));
const droppables = document.querySelectorAll(".web-prev>div");

var draggedItem;

draggables.forEach((item) => {
  item.addEventListener("dragstart", () => {
    if(item.parentElement.classList.contains("left-side")) {
      draggedItem = item.cloneNode(true);
    } else {
      draggedItem = item;
    }
    draggedItem.classList.add("is-dragging");
    draggables.push(draggedItem);
    console.log(draggables);
  });

  item.addEventListener("dragend", () => {
    draggedItem.classList.remove("is-dragging");
  });
});

droppables.forEach((zone, i) => {
  zone.addEventListener("dragover", (e) => {
    e.preventDefault();
    const bottomItem = insertAboveItem(zone, e.clientY);

    if (!bottomItem) {
      zone.appendChild(draggedItem);
    } else {
      zone.insertBefore(draggedItem, bottomItem);
    }
  });
});

const insertAboveItem = (zone, mouseY) => {
  const nonDragging = zone.querySelectorAll(".left-item:not(.is-dragging)");

  let closestItem = null;
  let closestOffset = Number.NEGATIVE_INFINITY;

  nonDragging.forEach((item) => {
    const { top } = item.getBoundingClientRect();

    const offset = mouseY - top;

    if (offset < 0 && offset > closestOffset) {
      closestOffset = offset;
      closestItem = item;
    }
  });

  return closestItem;
};

javascript sub folder function is not working [closed]

Why this script is not finding the subfolders?
It give the error: “element does not exist”

In this part of a javascript the following subfolder function is not working on windows PhotoShop.
It seems to go through all the if statements and finds no subfolders with files). When the subfolder is found, a photoshop action is run for that entire folder of files.

this is part of the script that is having the issue:

     while (fileList.length) {

        for (var a = 0; a < 1; a++) {

            app.open(fileList.pop());

            // Conditionally play the actions corresponding to the folder name
            if (//folder1$/i.test(app.activeDocument.path.fsName)) {
                app.doAction('action1', 'photo adjust');

            } else if (//folder2$/i.test(app.activeDocument.path.fsName)) {
                app.doAction('action2', 'photo adjust');

            } else if (//folder3$/i.test(app.activeDocument.path.fsName)) {
                app.doAction('action3', 'photo adjust');

            } else {
                alert('The idea is not to see this message...')
                app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
            } 

            // To avoid an infinite loop!
            app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);

            // Increment the file processing counter
            fileCounter++
        }
    }

how to call a website aspx project in one folder from an MVC project from a cshtml, controller method or javascript within mvc project

I have an aspx website project folder. I converted it to a web application. However, the web app dll is empty because aspx files seem to run at runtime, therefore the web app dll is empty. That’s one reason why.
So, I am trying to use the website project to where the aspx files live. This website project folder has its own web.config. The website project was setup around 2013, and it has lots of folders.
I believe the best way to access these website project aspx files from the MVC project is to use javascript, controller methods or cshtml?
I think the issue is that these two projects have there own start URL’s. The MVC project has this URL http://localhost:51712/ and the website project has a URL of “ http://localhost:58150 “.
I get messages that seem to say, the website folder aspx file cannot be found.
I tried these in my controller method :

 public EmptyResult ShowMBStest()
   {
   // Process.Start("http://MBSAnalysis/MbsTest.aspx");
   //          Process.Start("MBSAnalysis/MbsTest.aspx");
            // ~/MBSAnalysis/MbsTest.aspx
            // interesting  Response.Redirect("http://localhost:58150/MBSAnalysis/MbsTest.aspx", true);
            Response.Redirect("~/MBSAnalysis/MbsTest.aspx", true);
            // http://localhost:51712/MBSAnalysis/MbsTest.aspx
            return new EmptyResult();
        }

I tried some things in my cshtml and javascript. But I thought that I would try to ask you folks for the best way to go about this.

Thanks for any suggestions or advice.

Blur event doesn’t occur well when I install million.js

My project is made with CRA(Create React App).
I just watched a React lecture, and I installed million.js.
https://million.dev/

But now, there’s a serious problem with blur event handling.
blur event never occurs. So, I tried to uninstall million.js, then blur event works…

Then my Question

  1. I did not use useRef hook in this project. If I use ‘useRef’, will I be able to overcome problem with blur event?
  2. Why blur event doesn’t work in million.js envirionment?
  3. Except for this problems, what should I do to improve my codes?

The code below is part of my project with problem above.
my github JPoint-project, https://github.com/Koh-Du-Beom/JPoint-project.

You can see my code in components/UserInfo/Userinfo.tsx.
thank you for reading my article.

/*eslint-disable*/
import classes from '../../styles/FormStyles.module.css';
import ImageControler from '../ImageControler/ImageControler';
import { useState } from 'react';
import { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { RootState, AppDispatch } from '../../stores/redux/store';
import { updateUserInfo } from '../../stores/redux/userSlice';
import Divider from '../Divider/Divider';
import { isValidAccountNumber, isValidEmail, isValidName, isValidPhoneNumber, isValidStudentNumber, } from '../../utils/regularExpression/isValidUserInfo';

//million-ignore
const UserInfo : React.FC = () => {
    const [name, setName] = useState<string>('');
    const [grade, setGrade] = useState<string>('');
    const [major, setMajor] = useState<string>(''); //전공
    const [studentNumber, setStudentNumber] = useState<string>('');
    const [phoneNumber, setPhoneNumber] = useState<string>('');
    const [email, setEmail] = useState<string>('');
    
    const [bankAccount, setBankAccount] = useState<string>('');//계좌번호
    const [bankName, setBankName] = useState<string>(''); // 은행명
    const [bankBookImg, setBankBookImg] = useState<string>('');
    const [idCardImg, setIdCardImg] = useState<string>('');
    const [signImg, setSignImg] = useState<string>('');
    
    const [lastBlurTime, setLastBlurTime] = useState<number>(0);
    const [isValueChanged, setIsValueChanged] = useState<boolean>(false);
    const [errorMsg, setErrorMsg] = useState<{
        name?: string;
        grade?: string;
        major?: string;
        email?: string;
        phoneNumber?: string;
        studentNumber?: string;
        bankAccount?: string;
        bankName?: string;
    }>({});

    const dispatch = useDispatch<AppDispatch>();
    const userInfo = useSelector((state: RootState) => state.userInfo);

    const handleSaveButtonClick = () => {
        if(!isValueChanged){
            return;
        }
        setIsValueChanged(false);
        setLastBlurTime(0);
        
        dispatch(updateUserInfo({
            name: name,
      grade: grade,
      major: major,
      studentNumber: studentNumber,
      phoneNumber: phoneNumber,
      email: email,
      bankAccount: bankAccount,
      bankName: bankName,
      bankBookImg: bankBookImg,
      idCardImg: idCardImg,
      signImg: signImg,
        }));
        
  };

    const handleBlur = () => {
        setLastBlurTime(Date.now());
    }

    useEffect(() => {
        if (lastBlurTime === 0 || !isValueChanged) return;
        
        const timer = setTimeout(()=>{
            handleSaveButtonClick(); // 둘 다 비동기 함수지만 아래 코드가 먼저 실행될 수 있음. 그런 동작 막기위함
            setIsValueChanged(false);
        }, 1000); // 라우터(페이지 이동 시에도 실행되도록 하기)

        return () => clearTimeout(timer);
    }, [lastBlurTime, isValueChanged, handleSaveButtonClick])

    useEffect(() => {
    if (userInfo) {
      setName(userInfo.name);
      setGrade(userInfo.grade);
      setMajor(userInfo.major);
      setStudentNumber(userInfo.studentNumber);
      setPhoneNumber(userInfo.phoneNumber);
      setEmail(userInfo.email);
      setBankAccount(userInfo.bankAccount);
      setBankName(userInfo.bankName);
      setBankBookImg(userInfo.bankBookImg || '');
      setIdCardImg(userInfo.idCardImg || '');
      setSignImg(userInfo.signImg || '');
    }       
        
  }, [userInfo]);

    const handleName = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newName = event.target.value;
        setName(newName);
        setIsValueChanged(true);
    };

    const handleNameBlur = () => {
        
        if (!isValidName(name)){
            setErrorMsg((prev) => ({...prev, name : `${name? '올바른 이름이 아닙니다' : ''}`}));
            setName('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, name : undefined}));
            setLastBlurTime(Date.now());
        }
    };

    const handleGrade = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newGrade = event.target.value;
        setGrade(newGrade);
        setIsValueChanged(true);
    };

    const handleGradeBlur = () => {
        if (!isValidStudentNumber(grade)){
            setErrorMsg((prev) => ({...prev, grade : `${grade? '올바른 학년이 아닙니다' : ''}`}));
            setGrade('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, grade : undefined}));
            setLastBlurTime(Date.now());
        }
    };

    const handleMajor = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newMajor = event.target.value;
        setMajor(newMajor);
        setIsValueChanged(true);
    };

    const handleMajorBlur = () => {
        if(!isValidName(major)){
            setErrorMsg((prev) => ({...prev, major : `${major? '올바른 학과가 아닙니다' : ''}`}));
            setMajor('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, major : undefined}));
            setLastBlurTime(Date.now());
        }
    };

    const handleStudentNumber = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newStudentNumber = event.target.value;
        setStudentNumber(newStudentNumber);
        setIsValueChanged(true);
    };

    const handleStudentNumberBlur = () => {
        if (!isValidStudentNumber(studentNumber)){
            setErrorMsg((prev) => ({...prev, studentNumber : `${studentNumber? '올바른 학번이 아닙니다' : ''}`}));
            setStudentNumber('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, studentNumber: undefined}));
            setLastBlurTime(Date.now());
        }
    };

    const handlePhoneNumber = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newPhoneNumber = event.target.value;
        setPhoneNumber(newPhoneNumber);
        setIsValueChanged(true);
    };

    const handlePhoneNumberBlur = () => {
        if (!isValidPhoneNumber(phoneNumber)){
            setErrorMsg((prev) => ({...prev, phoneNumber : `${phoneNumber? '올바른 연락처가 아닙니다' : ''}`}));
            setPhoneNumber('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, phoneNumber : undefined}));
            setLastBlurTime(Date.now());
        }
    };

    const handleEmail = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newEmail = event.target.value;
        setEmail(newEmail);
        setIsValueChanged(true);
    };

    const handleEmailBlur = () => {
        if (!isValidEmail(email)){
            setErrorMsg((prev) => ({...prev, email : `${email? '올바른 이메일이 아닙니다' : ''}`}));
            setEmail('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, email : undefined}));
            setLastBlurTime(Date.now());
        }
    };


    const handleBankAccount = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newBankAccount = event.target.value;
        setBankAccount(newBankAccount);
        setIsValueChanged(true);
    };

    const handleBankAccountBlur = () => {
        if (!isValidAccountNumber(bankAccount)){
            setErrorMsg((prev) => ({...prev, bankAccount : `${bankAccount? '올바른 계좌번호가 아닙니다' : ''}`}));
            setBankAccount('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, bankAccount : undefined}));
            setLastBlurTime(Date.now());
        }
    };

    const handleBankName = (event : React.ChangeEvent<HTMLInputElement>) => {
        const newBankName = event.target.value;
        setBankName(newBankName);
        setIsValueChanged(true);
    };

    const handleBankNameBlur = () => {
        if(!isValidName(bankName)){
            setErrorMsg((prev) => ({...prev, bankName : `${bankName? '올바른 은행명이 아닙니다' : ''}`}));
            setBankName('');
            setIsValueChanged(false);
        }else{
            setErrorMsg((prev) => ({...prev, bankName : undefined}));
            setLastBlurTime(Date.now());
        }
    };

    const handlebankBookImg = (file : File | null) => {
        if(file){
            convertToBase64(file, setBankBookImg);
            setIsValueChanged(true);
        }else{
            setBankBookImg('');
        }
        handleBlur();
    };

    const handleIdCardImg= (file : File | null) => {
        if(file){
            convertToBase64(file, setIdCardImg);
            setIsValueChanged(true);
        }else{
            setIdCardImg('');
        }
        handleBlur();
    };

    const handleSignImg = (file : File | null) => {
        if(file){
            convertToBase64(file, setSignImg);
            setIsValueChanged(true);
        }else{
            setSignImg('');
        }
        handleBlur();
    };

    const convertToBase64 = (file : File, callback: (base64String : string) => void) => {
        const reader = new FileReader();
        reader.onload = () => {
            callback(reader.result as string);
        };
        reader.readAsDataURL(file); 
    };//fileReader 알아보기
    

    return (
        <form className={classes.container}>
            <div className={`${classes.wrapper} ${classes.end_double}`}>
                <div>
                    <div className={classes.big_title}>내 정보</div>
                </div>
            </div>
            <Divider/>      
            <div className={classes.big_title}>기본 정보</div>

            <div className={`${classes.wrapper} ${classes.double}`}>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>이름</div>
                        <input 
                            className={classes.input}
                            type='text'
                            onChange={handleName}
                            onBlur={handleNameBlur}
                            value={name}
                        />
                        {errorMsg.name && <div className={classes.errorMsg}>{errorMsg.name}</div>}
                    </div>
                </div>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>학과</div>
                        <input
                            className={classes.input} 
                            type='text'
                            onChange={handleMajor}  
                            onBlur={handleMajorBlur}
                            value={major}
                        />
                        {errorMsg.major && <div className={classes.errorMsg}>{errorMsg.major}</div>}
                    </div>
                </div>
            </div>

            <div className={`${classes.wrapper} ${classes.double}`}>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>학년</div>
                        <input 
                            className={classes.input}
                            type='text'
                            onChange={handleGrade}
                            onBlur={handleGradeBlur}
                            value={!grade ? '' : grade.toString()}
                        />
                        {errorMsg.grade && <div className={classes.errorMsg}>{errorMsg.grade}</div>}
                    </div>
                </div>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>학번</div>
                        <input
                            className={classes.input} 
                            type='text'
                            onChange={handleStudentNumber}  
                            onBlur={handleStudentNumberBlur}
                            value={studentNumber}
                        />
                        {errorMsg.studentNumber && <div className={classes.errorMsg}>{errorMsg.studentNumber}</div>}
                    </div>
                </div>
            </div>

            <div className={`${classes.wrapper} ${classes.double}`}>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>전화번호</div>
                        <input 
                            className={classes.input}
                            type='text'
                            onChange={handlePhoneNumber}
                            onBlur={handlePhoneNumberBlur}
                            value={phoneNumber}
                        />
                        {errorMsg.phoneNumber && <div className={classes.errorMsg}>{errorMsg.phoneNumber}</div>}
                    </div>
                </div>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>이메일</div>
                        <input
                            className={classes.input} 
                            type='text'
                            onChange={handleEmail}  
                            onBlur={handleEmailBlur}
                            value={email}
                        />
                        {errorMsg.email && <div className={classes.errorMsg}>{errorMsg.email}</div>}
                    </div>
                </div>
            </div>
            
            <Divider/>  
            
            <div className={classes.big_title}>통장 및 신분증사본</div>
            <div className={classes.wrapper}>
                <div>
                    <div className={classes.small_title}>통장사본</div>
                    <ImageControler onImageChange={handlebankBookImg} data={bankBookImg}/>
                </div>
            </div>
            <div className={`${classes.wrapper} ${classes.double}`}>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>계좌번호</div>
                        <input
                            className={classes.input} 
                            type='text'
                            onChange={handleBankAccount}    
                            onBlur={handleBankAccountBlur}
                            value={bankAccount}
                        />
                        {errorMsg.bankAccount && <div className={classes.errorMsg}>{errorMsg.bankAccount}</div>}
                    </div>
                </div>
                <div className={classes.wrapper}>
                    <div>
                        <div className={classes.small_title}>은행명</div>
                        <input 
                            className={classes.input}
                            type='text'
                            onChange={handleBankName}
                            onBlur={handleBankNameBlur}
                            value={bankName}
                        />
                        {errorMsg.bankName && <div className={classes.errorMsg}>{errorMsg.bankName}</div>}
                    </div>
                </div>
            </div>


            <div className={classes.wrapper}>
                <div>
                    <div className={classes.small_title}>신분증사본</div>
                    <ImageControler onImageChange={handleIdCardImg} data={idCardImg}/>
                </div>
            </div>

            <div className={classes.wrapper}>
                <div>
                    <div className={classes.small_title}>서명 사진</div>
                    <ImageControler onImageChange={handleSignImg} data={signImg}/>
                </div>
            </div>
        </form>
    )
}

export default UserInfo;

My project is made with CRA(Create React App).
I just watched a React lecture, and I installed million.js.
But now, there’s a serious problem with blur event handling.
blur event never occurs. So, I tried to uninstall million.js, then blur event works…

Then my Question

  1. I did not use useRef hook in this project. If I use ‘useRef’, will I be able to overcome problem with blur event?
  2. Why blur event doesn’t work in million.js envirionment?

The code below is part of my project with problem above.
my github JPoint-project, https://github.com/Koh-Du-Beom/JPoint-project.

You can see my code in components/UserInfo/Userinfo.tsx.
thank you for reading my article.

How to stop Click Event when selecting the text?

I have this Todo app in React. In the app when I click the <h2> item. It takes it to the edit mode. But When I try to select the text from the <h2>, it still takes it into the edit mode. I don’t want that to happen. How can I stop that from happening? I think window.getSelection() is not working because the click is registered first and then the selection happens. So when the click is registered, it has no selected text.

Here is the necessary code block for the problem:

import { useState } from 'react';
import './Todo.css';
export default function Todo() {
    const [list, setList] = useState([]);

    function onC() {
        let ele = document.getElementById('text');
        if (ele.value.trim() !== '') {
            const temp = {
                text: ele.value.trim(),
                edit: false
            }
            setList(l => [...l, temp]);
        }
        ele.value = "";
    }

    function editTodo(index) {
        if(window.getSelection().toString()!=='')
        setList(list.map((x, i) => { if (i !== index) { return x } else { return { ...x, edit: true } } }))
    }

    function deleteTodo(index) {
        setList(list.filter((_, i) => i !== index))
    }

    function editingTodo(val, index) {
        setList(list.map((x, i) => { if (i !== index) { return x } else { return { text: val, edit: true } } }))

    }

    function saveTodo(index) {
        let ele = document.getElementById('editTodo');
        if (ele.value.trim() !== '') {
            const temp = {
                text: ele.value.trim(),
                edit: false
            }
            setList(
                list.map((x, i) => {
                    if (i !== index) {
                        return x;
                    } else {
                        return temp;
                    }
                })
            );
        }
        ele.value = "";
    }

    function moveUp(index){
        let c = [...list];
        if(index>0){
            let a = c[index-1];
            let b = c[index];
            c[index-1]=b;
            c[index]=a;
        }
        setList(c)
    }

    function moveDown(index){
        let c = [...list] 
        if(index<c.length-1){
        let a = c[index+1];
        let b = c[index];
        c[index+1]=b;
        c[index]=a;
    }
    setList(c)
    }

    return (
        <div className="todo">
            <ul className='list-data-area'>
                {list.map((t, index) => {
                    if (!t.edit) {
                        return <li key={index}>
                            <h2 onClick={() => editTodo(index)}>{t.text}</h2>
                            <button onClick={(e) => { deleteTodo(index); e.stopPropagation() }}>Delete</button>
                            <button onClick={()=>moveUp(index)}>⬆️</button>
                            <button onClick={()=>moveDown(index)}>⬇️</button>
                        </li>
                    }
                    else {
                        return <li key={index}>
                            <input type='text' className='editTodo' id='editTodo' value={t.text} onChange={(e) => { editingTodo(e.target.value, index) }}></input>
                            <button onClick={(e) => { saveTodo(index); e.stopPropagation() }}>Save</button>
                            <button onClick={()=>moveUp(index)}>⬆️</button>
                            <button onClick={()=>moveDown(index)}>⬇️</button>
                        </li>
                    }
                })}
            </ul>
            <AddTodo onC={onC}></AddTodo>
        </div>);
}


function AddTodo({ onC }) {
    return (
        <div className='add-todo'>
            <input id='text' type='text' name='text'></input>
            <button onClick={onC}>Add</button>
        </div>
    );
}

I tried window.getSelection() as you can see but it’s not working

Transfer data from Javascript to Pyscript

Im trying to build a website where you have a canvas, 28×28 sized and you draw a number. This is all handled through classic HTML, css and Javascript. Hovewever, once the image is drawn, I want to store that as an array (pixel drawn is a 0, and pixel not drawn is valued 255), and pass it onto a python algorithm that I have, that uses the k-nearest neighbors algorithm to try and determine which number it is.

All the code is already done, the only issue im having is trying to get that javascript array into my pyscript.

Im not proffiecient in javascript, so im having trouble writing my own code with it, but I think all the code is written, except that communication between js and Pyscript

CODE IN HTML

<html>
    <head>
        <title>My OCR project</title>
        <link rel="stylesheet" href = "https://pyscript.net/alpha/pyscript.css"/>
        <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
        <link rel="stylesheet" href="style.css">
        <script src="main.js"></script>
    </head>
    <body>
        <h1 align="center">Optical Character Recognition</h1>
        <p align = "center">Este proyecto está hecho por Luca Siegel usando una mezcla de Python, Pyscript, HTML, JavaScript y CSS. 
            El objetivo es dibujar un numero de 28x28 pixeles en el canvas y que un algoritmo de machine learning llamado knn 
            (k-nearest-neighbors) sea capaz de distinguirlo comparándolo con la database de MNIST. Actualmente hay una probabilidad de acierto
            de alrededor del 80%.

        </p>

        <canvas id="gridCanvas" width="420px" height="420px"></canvas>
        <button id="clearButton">Limpiar</button>
        <button id="processButton">Procesar</button>
        <py-script >
            import time

            DATA_DIR = r"C:/Users/lucas/Downloads/OCR/"
            TEST_DIR = r"C:/Users/lucas/Downloads/OCR/test/"
            TEST_DATA_FILENAME = DATA_DIR + "t10k-images.idx3-ubyte"
            TEST_LABELS_FILENAME = DATA_DIR + "t10k-labels.idx1-ubyte"
            TRAIN_DATA_FILENAME = DATA_DIR + "train-images.idx3-ubyte"
            TRAIN_LABELS_FILENAME = DATA_DIR + "train-labels.idx1-ubyte"
            start_time = time.time()
            DEBUG = True
            if DEBUG:
                from PIL import Image
                import numpy as np
                
                def read_image(path):
                    return np.asarray(Image.open(path).convert("L"))
                def write_image(image,path):
                    img = Image.fromarray(np.array(image),"L")
                    img.save(path)
            def bytes_to_int(byte_data):
                return int.from_bytes(byte_data,"big")


            def read_labels(filename,n_max_labels = None):
                labels = [] #variable que guarda todas las imagenes
                with open(filename, "rb") as f: #abrir el fichero filename como f, y leerlo en binario ("rb")
                    _  =f.read(4) #numero inutil (representa algo que no necesitamos)
                    #los siguientes 12 bytes representan el numero de imagenes, el numero de filas y de columnas
                    n_labels = bytes_to_int(f.read(4))
                    if n_max_labels:
                        n_labels = n_max_labels
                    for label_idx in range(n_labels):
                        label = f.read(1)
                        labels.append(label)
                        
                return labels

            count = 0

            def read_images(filename,n_max_images = None):
                global count
                images = [] #variable que guarda todas las imagenes
                with open(filename, "rb") as f: #abrir el fichero filename como f, y leerlo en binario ("rb")
                    _  =f.read(4) #numero inutil (representa algo que no necesitamos)
                    #los siguientes 12 bytes representan el numero de imagenes, el numero de filas y de columnas
                    n_images = bytes_to_int(f.read(4))
                    if n_max_images:
                        n_images = n_max_images
                    n_rows = bytes_to_int(f.read(4))
                    n_columns = bytes_to_int(f.read(4))
                    for image_idx in range(n_images):
                        image = []#variable que guarda la imagen actual
                        for row_idx in range(n_rows):
                            row = []#variable que guarda la columna actual
                            for column_idx in range(n_columns):
                                count += 1
                                pixel = f.read(1) #leemos el pixel actual de 8 bits y lo apendizamos a la row
                                row.append(pixel)
                            image.append(row)#metemos la row en la image
                        images.append(image)#metemos la image en el conjunto de images
                return images

            def aplanar_lista(l):
                return [pixel for sublist in l for pixel in sublist]


            def pasar_lista_unidimensional(X):
                return [aplanar_lista(sample) for sample in X]

            def dist(x,y):
                return sum((bytes_to_int(x_i) - bytes_to_int(y_i)) **2 for x_i,y_i in zip(x,y))**0.5 #distancia euclides
                
                
            def distancia_entre_samples(X_train,test_sample):
                return [dist(train_sample,test_sample) for train_sample in X_train] #por todas las imagenes, calculamos su distancia arriba

            def most_frequent_element(list):
                return max(list, key= list.count)


            def knn(X_train,y_train,X_test,y_test, k = 3):
                y_pred = [] #la prediccion que tenemos a los x_test
                for test_sample_idx,test_sample in enumerate(X_test):
                    training_distances = distancia_entre_samples(X_train,test_sample) #queremos conseguir las distancias a todos los puntos
                    sorted_distance_indices = [
                        pair[0]
                        for pair in sorted(enumerate(training_distances), key = lambda x: x[1]) ]#escogemos la menor distancia
                    candidates = [bytes_to_int(y_train[idx]) for idx in sorted_distance_indices[:k]] # k mejores candidatos
                    top_candidate = most_frequent_element(candidates)
                    y_pred.append(top_candidate) #apuntamos a predicción
                return y_pred
            def main():
                
                #"X" es igual al dataset y "y" es el label asignado
                X_train = read_images(TRAIN_DATA_FILENAME,1000)
                X_test = read_images(TEST_DATA_FILENAME,500)
                y_train = read_labels(TRAIN_LABELS_FILENAME,1000)
                y_test = read_labels(TEST_LABELS_FILENAME,500)
                if DEBUG:
                    for idx,test_sample in enumerate(X_test):
                        write_image(test_sample,f"{TEST_DIR}{idx}.png")
                #inputear nuestra propia imagen
                    X_test= [read_image(f"{TEST_DIR}mitest.png")]
                    print(X_test)
                
                
                X_train = pasar_lista_unidimensional(X_train) #queremos pasar la matriz de valores a una matriz unidimensional
                X_test = pasar_lista_unidimensional(X_test)
                print(X_test)
                
                y_pred = knn(X_train,y_train,X_test,y_test,5)
                accuracy = sum([bytes_to_int(y_pred_i) == y_test_i 
                                for y_pred_i,y_test_i 
                                in zip(y_test,y_pred)
                                ])/len(y_test)
                end_time = time.time()
                print("The number you have just written is: " ,y_pred, "Time that it took to guess:", round(end_time-start_time,5), " Seconds")
                print("Number of iterations: ", count)
                
                
                
            if __name__ == "__main__":
                main()
        </py-script>
    </body>
</html>

CODE IN JAVASCRIPT

document.addEventListener('DOMContentLoaded', function() {
    const canvas = document.getElementById('gridCanvas');
    const ctx = canvas.getContext('2d');

    const gridSize = 28;
    const cellSize = canvas.width / gridSize;
    let isDrawing = false;

    // Función para dibujar la cuadrícula
    function drawGrid() {
        ctx.beginPath();
        for (let i = 1; i < gridSize; i++) {
            const pos = i * cellSize;
            // Líneas verticales
            ctx.moveTo(pos, 0);
            ctx.lineTo(pos, canvas.height);
            // Líneas horizontales
            ctx.moveTo(0, pos);
            ctx.lineTo(canvas.width, pos);
        }
        ctx.strokeStyle = '#ccc';
        ctx.stroke();
    }

    function clearGrid() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        drawGrid();
    }

    function getGridValues() {
        const gridValues = [];

        for (let y = 0; y < gridSize; y++) {
            const row = [];
            for (let x = 0; x < gridSize; x++) {
                const imageData = ctx.getImageData(x * cellSize, y * cellSize, 1, 1).data;
                const isLightColor = imageData[0] + imageData[1] + imageData[2] > 255 * 3 / 2; // Más de la mitad del valor máximo (blanco)
                row.push(dec2bin(isLightColor ? 255 : 0));
            }
            gridValues.push(row);
        }
        return gridValues;
    }

    function downloadImage() {
        const imageDataURL = canvas.toDataURL('image/png', 1.0);
        const image = new Image();
        image.src = imageDataURL;

        image.onload = function() {
            const resizedCanvas = document.createElement('canvas');
            const resizedCtx = resizedCanvas.getContext('2d');

            // Establece el nuevo tamaño
            resizedCanvas.width = gridSize;
            resizedCanvas.height = gridSize;

            // Copia la parte del dibujo sin los bordes de la cuadrícula
            resizedCtx.drawImage(image, 0, 0, gridSize * cellSize, gridSize * cellSize, 0, 0, gridSize, gridSize);

            // Descarga la imagen redimensionada
            const resizedDataURL = resizedCanvas.toDataURL('image/png');
            const a = document.createElement('a');
            a.href = resizedDataURL;
            a.download = 'grid.png';
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
        };
        sendGridValues(getGridValues());
    }
    function dec2bin(dec) {
        return (dec >>> 0).toString(2);
      }


    // Función para manejar el evento de dibujo
    function handleDraw(event) {
        if (!isDrawing) return;

        const x = Math.floor(event.offsetX / cellSize);
        const y = Math.floor(event.offsetY / cellSize);

        // Dibuja en la celda correspondiente
        ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
    }

    // Dibuja la cuadrícula inicial
    drawGrid();

    // Agrega eventos para dibujar en el lienzo
    canvas.addEventListener('mousedown', () => {
        isDrawing = true;
    });

    canvas.addEventListener('mousemove', handleDraw);

    canvas.addEventListener('mouseup', () => {
        isDrawing = false;
         // Muestra los valores de la cuadrícula después de terminar de dibujar
    });

    canvas.addEventListener('mouseleave', () => {
        isDrawing = false;
    });

    document.getElementById('clearButton').addEventListener('click', clearGrid);
    document.getElementById('processButton').addEventListener('click', downloadImage);
});

How to smoothly apply currency mask in MUI DataGrid?

I’m trying to apply the monetary mask inside editable cells, not with a final value, but after every digit.

I almost have success utilizando Intl.NumberFormat, but they don’t get smoothly.

I am trying:

  const formatToDecimal = (value: string = "0") => {
    return parseFloat(value.replace(/[^0-9,]*/g, "").replace(",", ".")).toFixed(
      2
    );
  };

  const formatCurrency = (value) => {
    console.log(value);
    if (!value) return "R$ 0,00";
    let teste = formatToDecimal(value.replace(/[^0-9.,]/g, ""));

    return new Intl.NumberFormat("pt-BR", {
      style: "currency",
      currency: "BRL",
    }).format(teste);
  };

  const columns: GridColDef[] = [
    { field: "name", headerName: "Name", width: 180, editable: true },
    {
      field: "currency",
      headerName: "Currency",
      // type: "number",
      width: 180,
      editable: true,
      valueFormatter: (params: GridValueFormatterParams<number>) => {
        if (params.value == null) {
          return "R$ 0,00";
        }
        return params.value.toLocaleString("pt-BR", {
          style: "currency",
          currency: "BRL",
        });
      },
      valueParser: (newValue) => {
        return formatCurrency(newValue);
      },
    },
  ];

It works, but the user has to use the arrows or mouse to create the correct formatted number.

I use the valueParser to modify entered values.

Readind the docs: “You can use the valueParser property in the column definition to modify the value entered by the user…” https://mui.com/x/react-data-grid/editing/#value-parser-and-value-setter

My test: https://codesandbox.io/p/sandbox/elegant-napier-nwt2ss?file=%2Fsrc%2FDemo.tsx%3A112%2C29