AG Grid React: nested (child) grid blinks on every 5s update even with immutableData + deltaRowDataMode. How do I stop the flash?

I have a parent AG Grid that renders a nested child grid inside a custom “detail” row. I poll an API every 5 seconds and update both the parent rows and the child rows. The child grid visibly blinks (flashes) on each poll, which looks like it is being torn down and recreated.

I’m already using:

Stable row IDs (getRowId) for both grids based on Tick + Time

immutableData and deltaRowDataMode on both grids

React.memo on the child grid with a custom prop comparer

An update helper that diffs incoming rows and only moves/updates what changed

Still getting a flash on the second grid whenever new data arrives.

Only changed rows should update. The nested grid should not tear down or blink when polling updates arrive.

What happens

On every poll, the child grid area flashes for a frame. It looks like React is re-rendering the child grid container or AG Grid is remounting the rows despite stable IDs.

How the UI is structured

I do not use AG Grid’s built-in Master/Detail.

I emulate a detail row by injecting an extra row with __kind: “detail” below the clicked parent row.

That detail row cellRenderer returns a component (another ).

React-pdf-highloghter first time load issue

import { useCallback, useEffect, useRef, useState } from "react"
import { XIcon } from "lucide-react"

import { Button } from "../ui/button"
import {
  RightDrawer,
  RightDrawerTitle,
  RightDrawerHeader,
  RightDrawerContent,
  RightDrawerClose,
  RightDrawerDescription,
} from "@/components/right-drawer"

import {
  PdfLoader,
  PdfHighlighter,
  Highlight,
  Popup,
  AreaHighlight,
  type IHighlight,
  Tip,
  ScaledPosition,
  NewHighlight,
  Content,
} from "react-pdf-highlighter"

import { Spinner } from "../ui/spinner"

export interface SourceWithCitations {
  title: string
  document_url: string
  citations: any[]
}

interface DocumentWithCitationsSidebarProps {
  isOpen: boolean
  onClose: () => void
  sources: SourceWithCitations | null
}

const HighlightPopup = ({ comment }: { comment?: { text?: string; emoji?: string } }) =>
  comment && comment.text ? (
    <div className="Highlight__popup">
      {comment.emoji} {comment.text}
    </div>
  ) : null

const resetHash = () => {
  document.location.hash = ""
}

const getNextId = () => String(Math.random()).slice(2)

const parseIdFromHash = () =>
  document.location.hash.slice("#highlight-".length)

export function DocumentWithCitationsSidebar({
  isOpen,
  onClose,
  sources,
}: DocumentWithCitationsSidebarProps) {

  const [highlights, setHighlights] = useState<Array<IHighlight>>(sources?.citations ?? [])

  // store scrollTo ref from PdfHighlighter
  const scrollViewerTo = useRef<(highlight: IHighlight) => void>(() => {})

  // Reset highlights whenever sources change
  useEffect(() => {
    if (sources?.citations) {
      const mapped = sources.citations.map((c, i) => ({
        id: c.id ?? String(i + 1),
        content: c.content ?? { text: "" },
        comment: c.comment ?? {},
        position: c.position,
      }))
      setHighlights(mapped)
    } else {
      setHighlights([])
    }
  }, [sources])

  // After highlights are set, force re-render/scroll
  useEffect(() => {
    if (highlights.length > 0 && scrollViewerTo.current) {
      // wait for PdfHighlighter to fully render pages
      setTimeout(() => {
        scrollViewerTo.current(highlights[0])
        // trigger a resize to ensure highlights paint correctly
        window.dispatchEvent(new Event("resize"))
      }, 50)
    }
  }, [highlights])

  const handleOpenChange = (open: boolean) => {
    if (!open) {
      onClose()
    }
  }

  const getHighlightById = useCallback(
    (id: string) => highlights.find((h) => h.id === id),
    [highlights],
  )

  const scrollToHighlightFromHash = useCallback(() => {
    const highlight = getHighlightById(parseIdFromHash())
    if (highlight) {
      scrollViewerTo.current(highlight)
    }
  }, [getHighlightById])

  useEffect(() => {
    window.addEventListener("hashchange", scrollToHighlightFromHash, false)
    return () => {
      window.removeEventListener("hashchange", scrollToHighlightFromHash, false)
    }
  }, [scrollToHighlightFromHash])

  const addHighlight = (highlight: NewHighlight) => {
    console.log("Saving highlight", highlight)
    setHighlights((prev) => [
      { ...highlight, id: getNextId() } as IHighlight,
      ...prev,
    ])
  }

  const updateHighlight = (
    highlightId: string,
    position: Partial<ScaledPosition>,
    content: Partial<Content>,
  ) => {
    console.log("Updating highlight", highlightId, position, content)
    setHighlights((prev) =>
      prev.map((h) =>
        h.id === highlightId
          ? {
              ...h,
              position: { ...h.position, ...position },
              content: { ...h.content, ...content },
            }
          : h,
      ),
    )
  }

  return (
    <RightDrawer open={isOpen} onOpenChange={handleOpenChange}>
      <RightDrawerContent className="w-[700px] max-w-[90vw]">
        <RightDrawerHeader className="flex items-center justify-between p-6 pb-4 border-b border-slate-100">
          <div>
            <RightDrawerTitle className="text-lg font-semibold text-slate-900">
              {sources?.title}
            </RightDrawerTitle>
            <RightDrawerDescription className="text-sm text-slate-500">
              {sources?.document_url}
            </RightDrawerDescription>
          </div>
          <RightDrawerClose asChild>
            <Button
              variant="ghost"
              size="sm"
              className="text-slate-400 hover:text-slate-600 hover:bg-slate-50"
            >
              <XIcon className="w-4 h-4" />
            </Button>
          </RightDrawerClose>
        </RightDrawerHeader>

        {sources?.document_url && (
          <PdfLoader url={sources.document_url} beforeLoad={<Spinner />}>
            {(pdfDocument) => (
              <PdfHighlighter
                pdfDocument={pdfDocument}
                enableAreaSelection={(event) => event.altKey}
                onScrollChange={resetHash}
                scrollRef={(scrollTo) => {
                  scrollViewerTo.current = scrollTo
                }}
                onSelectionFinished={(
                  position,
                  content,
                  hideTipAndSelection,
                  transformSelection,
                ) => (
                  <Tip
                    onOpen={transformSelection}
                    onConfirm={(comment) => {
                      addHighlight({ content, position, comment })
                      hideTipAndSelection()
                    }}
                  />
                )}
                highlightTransform={(
                  highlight,
                  index,
                  setTip,
                  hideTip,
                  viewportToScaled,
                  screenshot,
                  isScrolledTo,
                ) => {
                  const isTextHighlight = !highlight.content?.image

                  const component = isTextHighlight ? (
                    <Highlight
                      isScrolledTo={isScrolledTo}
                      position={highlight.position}
                      comment={highlight.comment}
                    />
                  ) : (
                    <AreaHighlight
                      isScrolledTo={isScrolledTo}
                      highlight={highlight}
                      onChange={(boundingRect) => {
                        updateHighlight(
                          highlight.id,
                          { boundingRect: viewportToScaled(boundingRect) },
                          { image: screenshot(boundingRect) },
                        )
                      }}
                    />
                  )

                  return (
                    <Popup
                      key={index}
                      popupContent={
                        <HighlightPopup comment={highlight.comment} />
                      }
                      onMouseOver={(popupContent) =>
                        setTip(highlight, () => popupContent)
                      }
                      onMouseOut={hideTip}
                    >
                      {component}
                    </Popup>
                  )
                }}
                highlights={highlights}
              />
            )}
          </PdfLoader>
        )}
      </RightDrawerContent>
    </RightDrawer>
  )
}

This code works correctly to load the documents but the highlights are not coming in the start. When i click on the document anywhere only then I am able to see them why?

I guess somehow the highlights are not able to be present in the start but somehow they comes in one-click. That is what I am not able to understand actually.

Slider inside of Horizontal FlatList: dragging slider also scrolls FlatList

I have a React Native screen that renders a horizontal FlatList.
Each item in the list contains a Slider (from @react-native-community/slider).

  • On iOS, when I try to drag the slider thumb horizontally, the FlatList scroll gesture also gets triggered.
  • On Android, it works fine: the slider moves without scrolling the list.
  <FlatList
          data={pages}
          ref={flatListRef}
          horizontal
          pagingEnabled
          bounces={false}
          showsHorizontalScrollIndicator={false}
          keyExtractor={(_, index) => index.toString()}
          onMomentumScrollEnd={handleScroll}
          renderItem={({ item }) => (
            <View style={[styles.pageContainer, { width: containerWidth }]}>
              {item.map((device: any) => (
                 <TouchableOpacity style={[styles.groupHeader, { width: containerWidth }]} key={device.id}>
              <Textc size={mvs(24)} medium>{groupName}</Textc>
              <Image source={bulbOn} style={[styles.iconImage, { width: s(100) }]} />
              <View style={{ height: mvs(42), justifyContent: 'center', width: '60%' }}>
                <Slider
                  animateTransitions
                  maximumTrackTintColor={Colors.gray400}
                  minimumTrackTintColor={Colors.primary}
                  thumbTintColor={Colors.primary}
                  // value={value}
                  minimumValue={0}
                  maximumValue={100}
                  step={1}
                  onSlidingComplete={([num]) => {
                     setValue(num)
                    onAction("LED_BRIGHTNESS", num)
                  }}
                />
              </View>
            </TouchableOpacity>
              ))}
            </View>
          )}
        />

I’ve tried:

  • Wrapping Slider in TouchableOpacity or PanGestureHandler → didn’t help.

  • onStartShouldSetResponderCapture={() => true} on parent View on Slider → but it stops the slider and flatlist still scrolls

  • Temporary fix: disable FlatList scroll while dragging the slider using onSlidingStart and onSlidingComplete

Is there a proper way to let the slider thumb consume horizontal gestures, without disabling FlatList scrolling manually?

How to correctly close a listening port in oak?

I’m trying to close the port manually (in somewhere else) but not sure the way to do it.

A smallest example:

import { Application, Router } from 'jsr:@oak/oak'
const app = new Application()
const router = new Router()

app.use(router.routes())
app.use(router.allowedMethods())

router.get('/', (ctx) => {
  ctx.response.body = JSON.stringify({ message: 'Hello, Oak!' })
})

const ac = new AbortController()
const promise = app.listen({ port: 8000, signal: ac.signal })

async function close() {
  ac.abort()
  await promise
  // never reached
  console.log('Server closed')
}

await close()

Vue V-for to loop through Objects in Array to display with intput type text box and select option in html page

The below code is a working code. The “additional” columns displays correctly, just like a simple input box. But I would like to edit this code to “select” option for “sex” and “city”. Would be great if someone gives suggestion. Thank you.

    function student(){
       this['name']='',
       this['age']='',
       this['course']='',
       this['additional']=[]
    }

    function additional(){
      this['sex']='',
      this['phone']='',
      this['city']=''
    }

    var student = new student();

    var data=new Vue({
    el="#app",
    data(){
       return{
         student:student,
         sexOptions:['Male','Female'],
         cityOptions:['city1','city2','city3']
       }
    },
    mounted(){
    },
    methods: {
    createadditional: function(){
    var val = this.student['course'];
    var len = this.student['additional'].length;
    var diff = val - len;
    
    if(diff < 0){
      this.student['additional'].splice(diff);
      }else if (diff > 0){
      while (len < val){
      var newadditional = new additional();
      this.student['additional'].push(newadditional);
      len++;
      }
     }  
    },      
   },
   watch:{
   },
   computed: {
    additional_display: {
    get: function(){
    return this.student['additional'].slice(0,this.student['course']);
    },
    },
    }
 });


 <form action="saveSchoolData"  id = "schoolForm" method="post"> 
<table>
<tr> <th> Name: </th> <td> <input type="text" v-model = "school['name']"> </td> </tr>
<tr> <th> Age: </th> <td> <input type="text" v-model = "school['Age']"> </td> </tr>
<tr> <th> Course: </th> <td> <input type="text" v-model = "school['Course']"> </td>       
</tr>
</table>

<table  v-if = "school.course> 0" >
<tr>
<td>Nbr</td>
<td>Sex</td>
<td>Phone</td>
<td>City</td>
</tr>

<tr v-for='additional in additional_display' >
<td>{{additional_display.indexOf(school)+1}}</td>
<td v-for="key in Object.keys(additional)">
{{key}}
<input v-model="additional[key]"> 
</td>
</tr>

</table>

 <input type="button" value="Save" id="saveButton" onclick="saveSchool()" />

 </form>

I would like to have the code like this below, but not getting correct – the values are not getting passed through action when i click “Save”.

<tr v-for='additional in additional_display' >
<td>{{additional_display.indexOf(school)+1}}</td>
<td><select v-model="additional['sex']">
    <option v-for="option in sexOptions" v-bind:value="option">
    {{ option }}
    </option>
    </select>
</td>
<td><input type="text" v-model="additional['phone']>
<td><select v-model="additional['city']">
    <option v-for="option in cityOptions" v-bind:value="option">
    {{ option }}
    </option>
    </select>
</td>
</tr>

Local script does not load when chrome DevTools is open

Working locally, and when Chrome DevTools is open, I get the following errors:

GET http://localhost:3000/main-bundle.js net::ERR_ABORTED 404 (Not Found)

Refused to execute script from ‘http://localhost:3000/main-bundle.js’ because its MIME type (‘text/html’) is not executable, and strict MIME type checking is enabled.

Using Webpack for this project.
When I close the DevTools and refresh, it does not occur.
Does not happen with FireFox DevTools.

How to specify dataset schema if you don’t know the right values beforehand?

The tutorial page here shows to specify schema like this:

const series = chart.addLineSeries({
    schema: {
        index: { auto: true },
        x: { pattern: 'progressive' },
        y: { storage: Float32Array },
        pointSize: {}
    }
})

That seems simple, but in my application I don’t know beforehand what data properties there will be coming. Like “index”, “x” or “y”. There is always “time”, but the other properties can be like “Fp1”, “Fp2”, “F3”, “F4”, “Fz”, … I really don’t like the idea of hardcoding these.

It seems I can also just omit the schema and things work still correctly, but then there are warnings in console from LightningChart.

The documentation doesn’t seem to list any recommended way to approach this. Does anyone have any other ideas or experiences?

In the Autodesk Viewer SDK, how do I get the Vectors for a selected element from a locally loaded PDF?

I have been developing a POC application to load a PDF into the Autodesk GUIViewer3D, and using a custom extension, draw PolygonPaths and PolylinePaths over the PDF. My extension is simple, it adds a toolbar and manages the active tools, so that only one Edit2d tool is active at a time. The toolbar buttons I have added essentially toggle or switch between them.

I have the measurement extension loaded and can set the calibration, this carries over to the Edit2d Extension and allows me to have the correct values when getting one of the drawn shapes, and doing getArea() with the measureTransform etc.

Todo: add the code to get calibrated area of 2d shape via measure transform

I want to add a new toolbar button to my extension that enables the following functionality:

When the viewer selection event is fired, giving me the dbid of a my selected line in the PDF, I retrieve the raw coords / vector data of that line and draw a Polyline shape programmatically using those coords.


Note that this is a rough idea of the setup. In reality I have the viewer bound in a class called ApsViewerManager and have an instance of that assigned to the window object. The ApsViewerManager supports loading from a PDF that is downloaded from another api, or loading from the APS OSS API. I will try strip things down to the bare minimum of what is needed for the “local” PDF loading.

My setup for loading a pdf into the viewer:


const url = "some-url" // in reality this is pointing to a get endpoint on an external api that returns the pdf content
const page = 1 // you can only load 1 pdf page at a time

const localManager = this; // this is an instance of the APSViewerManager

const options = {
    accessToken: "",
    useCredentials: false
    env: 'Local'
}

await Autodesk.Viewing.Initializer(options, async function () {

    const htmlDiv = document.querySelector(localManager.containerSelector);
    localManager.viewer = new Autodesk.Viewing.GuiViewer3D(htmlDiv);

    attachViewerEvents.call(localManager); // I have my events handling setup in a separate module called as if part of the APSViewerManager

    const startedCode = localManager.viewer.start(null, options);

    if (startedCode > 0) {
        localManager.report_error('Failed to create Viewer', {startCode: startedCode});
        return;
    }

    // setup the edit2d extension from 'Autodesk.Edit2d'
    localManager.edit2d = await localManager.viewer.loadExtension('Autodesk.Edit2D', {enableArcs: true});

    // setup the inhouse extension to add edit2d controls as toolbar buttons + inhouse functionality consuming edit2d
    localManager.rlb2dTakeOff = await localManager.viewer.loadExtension(
        'RLB2DTakeOffExtension', 
        {
            manager: localManager,
            filename: localManager.shapesFilename,
        }
    );

    // setup for loading the pdf extension from 'Autodesk.PDF'
    await localManager.viewer.loadExtension('Autodesk.PDF')
        
    // URL parameter page will override value passed to loadModel
    localManager.viewer.loadModel(url, {page: page});
    
});


The challenge is, While I have the dbId from the selection event, I am unable to query the property database using this Id.

const dbid = 2900 //the dbid for a wall I selected on the pdf - obtained from viewer selection event
const db = apsViewerManager.viewer.model.getPropertyDb();

db.getProperties2(
  dbid, 
  function(x){
    console.log("Promise resolved")
    console.dir(x)
  }, 
  function(x){
    console.log("Promise rejected")
    console.dir(x)
  }
)

The promise rejects, and I am left with the following object:

{
    "err": undefined,
    "instanceTree": null,
    "maxTreeDepth": 0
}

I figured maybe my db was fragmented – I am not entirely sure what this means, but I do see properties in the db object full of “fragments”.

const frags = apsViewerManager.viewer.model.getFragmentList();
console.dir(frags);

result:
Console FragmentList output

I wanted to try the following script to retrieve the props from there:

I am not sure the exact source of this script combination but I see it was derived from this APS blog: https://aps.autodesk.com/blog/working-2d-and-3d-scenes-and-geometry-forge-viewer

viewer = apsViewerManager.viewer;

viewer.addEventListener(
  Autodesk.Viewing.SELECTION_CHANGED_EVENT, 
  function () {
    const tree = viewer.model.getInstanceTree();
    const frags = viewer.model.getFragmentList();
    
    function listFragmentProperties(fragId) {
      console.log('Fragment ID:', fragId);
      // Get IDs of all objects linked to this fragment
      const objectIds = frags.getDbIds(fragId);
      console.log('Linked object IDs:', objectIds);
      // Get the fragment's world matrix
      let matrix = new THREE.Matrix4();
      frags.getWorldMatrix(fragId, matrix);
      console.log('World matrix:', JSON.stringify(matrix));
      // Get the fragment's world bounds
      let bbox = new THREE.Box3();
      frags.getWorldBounds(fragId, bbox);
      console.log('World bounds:', JSON.stringify(bbox));
    }
    
    if (tree) { // Could be null if the tree hasn't been loaded yet
      const selectedIds = viewer.getSelection();
      for (const dbId of selectedIds) {
        const fragIds = [];
        tree.enumNodeFragments(
          dbId,
          listFragmentProperties,
          false
        );
      }
    }
    else {
      console.log("No tree!");
    }
  }
);

Unfortunately, the tree is always null, so my browser spits out “No tree!”


What approaches / hidden documentation secrets can I look at to progress further?

I had seen a thread somewhere stating that PDFs are always converted to Vectors, hence my ambition of getting some coords of the selected wall (line) in the PDF.

Why does a text string operate identically to event object in Dicord.JS snippet

The following two code snippets appear to function the same. I am aware that there is likely something occurring under the hood for the string snippet to call the event object, but I am unfamiliar with Javascript and I would like to have a better understanding of why it is operating this way.

client.on('messageCreate', (message: any) => {
    console.log(`message sent: ${message.content}`)
    message.reply(`${message.content}`)
})
client.on(Events.MessageCreate, (message: any) => {
    console.log(`message sent: ${message.content}`)
    message.reply(`${message.content}`)
})

Uncaught ReferenceError: Cannot access ‘StripeWrapper’ before initialization

I keep getting this error:

Uncaught ReferenceError: Cannot access 'StripeWrapper' before initialization

This is my App.js:

  import logo from './logo.svg';
  import { Routes, Route, createBrowserRouter, RouterProvider, Navigate, Outlet } from 'react-router-dom';
  import { Elements } from '@stripe/react-stripe-js';
  import { loadStripe } from '@stripe/stripe-js';
  import './App.css';
  import 'bootstrap/dist/css/bootstrap.css';
  import Header from "../src/Components/Header";
  import Home from "../src/Components/Home";
  import Search from "../src/Components/Search";
  import Login from "../src/Components/Login";
  import Sell from "../src/Components/Sell";
  import Signup from "../src/Components/Signup";
  import ErrorPage from "../src/Components/ErrorPage";
  import StripeComplete from "../src/Components/StripeComplete";
  import StripeError from "../src/Components/StripeError";
  import BuyImageCheckout from "../src/Components/BuyImageCheckout";
  import Selling from "../src/Components/Selling";
  import {} from "./APIRequests/Api";


  const stripePromise = loadStripe('xxxxx');

  const router = createBrowserRouter([
  {
    path: '/',
      element: <LayoutComponent />,
      children: [
        {
          index: true,
          element: <Home />,
        },
        {
          path: '/search',
          element: <Search />,
        },
        { path: '/sell',
          element: (
            <PrivateRoute>
              <Sell />
            </PrivateRoute>
          ),
        },
        {
          path: '/login',
          element: <Login />,
        },
        {
          path: '/signup',
          element: <Signup />,
        },
        {
          path: '/stripecomplete',
          element: <StripeComplete />,
        },
        {
          path: '/stripeerror',
          element: <StripeError />,
        },
        {
          path: '/selling',
          element: <Selling />,
        },
        {
          path: '/sell/buyimagecheckout',
          element: (
            <PrivateRoute> <-- This is where the error occurs
              <StripeWrapper>
                <BuyImageCheckout />
              </StripeWrapper>
            </PrivateRoute>
          )
        },{
          path: '*',
          element: <ErrorPage />,
        },
      ],
    },

  ]);
  function PrivateRoute({ children }) {
    return localStorage.getItem("userGuid") != null ? children : <Navigate to="/login" />;
  }
  const StripeWrapper = ({ children }) => {
    return (
      <Elements stripe={stripePromise}>
        {children}
      </Elements>
    );
  };
  function LayoutComponent() {
    return (
      <div>
        <Header></Header>
        <main>
          <Outlet /> {/* Nested routes render here */}
        </main>
      </div>
    );
  }

  function App() {
    return <RouterProvider router={router} />;
  }

  export default App;

The BuyImageCheckout is the page that loads the Card element from stripe, if you want me to post the code for BuyImageCheckout let me know its got a load of html that builds the Image Checkout Page.

Full Error:

ERROR
Cannot access 'StripeWrapper' before initialization
ReferenceError: Cannot access 'StripeWrapper' before initialization
at ./src/App.js (http://localhost:3000/static/js/bundle.js:59619:93)
at options.factory (http://localhost:3000/static/js/bundle.js:65759:30)
at __webpack_require__ (http://localhost:3000/static/js/bundle.js:65114:32)
at fn (http://localhost:3000/static/js/bundle.js:65375:21)
at hotRequire (http://localhost:3000/static/js/bundle.js:65742:47)
at ./src/index.js (http://localhost:3000/static/js/bundle.js:64706:62)
at options.factory (http://localhost:3000/static/js/bundle.js:65759:30)
at __webpack_require__ (http://localhost:3000/static/js/bundle.js:65114:32)
at http://localhost:3000/static/js/bundle.js:66363:37
at http://localhost:3000/static/js/bundle.js:66365:12

Multiple lightbox galleries

I know next to nothing about JavaScript, so I’m not sure what I’m even doing, but I’m trying to make a page with multiple image galleries that opens up a lightbox when you click one of the pics.

I got it to work with only one gallery, but when I added the second one, the images open the lightbox for the first gallery instead of the current one.

Here’s the code for what I was able to do so far (I got the code for the lightbox at W3Schools and modified it a bit):

const lightboxes = document.querySelectorAll(".lightbox");

function openLightbox() {
  lightboxes.forEach(lightbox => {
    document.querySelector('.lightbox').style.display = "block";
    document.querySelector('body').style.overflowY = "hidden";
  });
}

function closeLightbox() {
  lightboxes.forEach(lightbox => {
    const lightClose = document.querySelector('.close');
    const lightContent = document.querySelector('.lightbox-content');

    if (lightClose.contains(event.target) || !lightContent.contains(event.target)) {
      lightbox.style.display = "none";
      document.querySelector('body').style.overflowY = "visible";
    }
  }); 
}

var slideIndex = 1;
showSlides(slideIndex);

function plusSlides(n) {
  showSlides(slideIndex += n);
}

function currentSlide(n) {
  showSlides(slideIndex = n);
}

function showSlides(n) {
  lightboxes.forEach(lightbox => {
    var i;
    var slides = document.getElementsByClassName("mySlides");
    if (n > slides.length) {slideIndex = 1;}
    if (n < 1) {slideIndex = slides.length;}
    for (i = 0; i < slides.length; i++) {
      slides[i].style.display = "none";
    }

    slides[slideIndex-1].style.display = "block";
  });
}
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.gallery {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-gap: 1vw;
  max-width: 100vw; /* Recommended */
  margin: 1vw auto;
  padding: 1vw;
}

.gallery figure {
  display: flex;
  justify-items: center;
  height: 180px;
  margin: 0;
  
  img {
    width: 100%;
    object-fit: cover;
    object-position: center;
  }
}

img:hover {
  cursor: pointer;
}

.lightbox {
  display: none;
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background-color: #ffffffee;
  align-content: center;
  justify-items: center;
}

.lightbox-content {
  position: relative;
  width: 80vh;
  justify-items: center;
  align-content: center;
  z-index: 100;
  grid-gap: 5vh;
  margin-bottom: 10vh;
  
  figure {
    display: flex;
    flex-flow: column;
    margin: 0;
  }
  
  figure img {
    max-height: 60vh;
    object-fit: contain;
  }
}

.imgcap {
  width: min-content;
}

figcaption {
  height: 40px;
}

.mySlides {
  display: none;
  grid-gap: 1vh;
}

.prev, .next, .close {
  position: absolute;
  color: black;
  font-weight: bold;
  font-size: 40px;
  font: 700 40px;
  cursor: pointer;
  align-content: center;
  text-shadow: 2px 2px 4px #00000064;
  opacity: 0.6;
  transition: 0.3s ease;
}

.close {
  position: relative;
  float: right;
  margin-right: -30px;
  margin-top: 10px;
  z-index: 1000;
}

.next {
  right: 0;
  padding-left: 50%;
}

.prev {
  padding-right: 50%;
}

.next, .prev {
  top: 5%;
  height: 80%;
  margin: 0 -8vh;
}

.prev:before {
  content: '3008';
}

.close:before {
  content: '2A2F';
}

.next:before {
  content: '3009';
}

/* Number text (1/3 etc) */
.numbertext {
  color: black;
  font: 700 1em;
  text-align: center;
}

:where(.prev, .next, .close):hover {
  opacity: 1;
}

@media screen and (max-width: 650px) {
  .lightbox-content {
    width: 80vw;
    max-width: none;
    figure img {
      max-width: none;
      height: 60vw;
    }
  }
  
  .next, .prev {
    margin: 0 -8vh;
  }
}
<!-- gallery 1 -->
    <section class="gallery">
      <figure>
        <img src="https://placehold.co/500x300/000/cyan?text=1" onclick="openLightbox();currentSlide(1)"/>
      </figure>
      
      <figure>
        <img src="https://placehold.co/500x300/000/cyan?text=2" onclick="openLightbox();currentSlide(2)"/>
      </figure>
      
      <figure>
        <img src="https://placehold.co/500x300/000/cyan?text=3" onclick="openLightbox();currentSlide(3)"/>
      </figure>
      
      <figure>
        <img src="https://placehold.co/500x300/000/cyan?text=4" onclick="openLightbox();currentSlide(4)"/>
      </figure>
      
      <figure>
        <img src="https://placehold.co/500x300/000/cyan?text=5" onclick="openLightbox();currentSlide(5)"/>
      </figure>
      
      <figure>
        <img src="https://placehold.co/500x300/000/cyan?text=6" onclick="openLightbox();currentSlide(6)"/>
      </figure>
      
      <figure>
        <img src="https://placehold.co/500x300/000/cyan?text=7" onclick="openLightbox();currentSlide(7)"/>
      </figure>
      
      <figure>
        <img src="https://placehold.co/500x300/000/cyan?text=8" onclick="openLightbox();currentSlide(8)"/>
      </figure>
    </section>

    <!-- lightbox for gallery 1 -->
    <div class="lightbox" onclick="closeLightbox()">
      <div class="lightbox-content">
        <div class="imgcap">
          <span class="close" onclick="closeLightbox()"></span>
          <figure class="mySlides">
            <p class="numbertext">1 / 8</p>
            <img src="https://placehold.co/500x300/000/cyan?text=1"/>
            <figcaption>first cyan</figcaption>
          </figure>

          <figure class="mySlides">
            <div class="numbertext">2 / 8</div>
            <img src="https://placehold.co/500x300/000/cyan?text=2"/>
            <figcaption>second cyan</figcaption>
          </figure>

          <figure class="mySlides">
            <div class="numbertext">3 / 8</div>
            <img src="https://placehold.co/500x300/000/cyan?text=3">
            <figcaption>third cyan</figcaption>
          </figure>

          <figure class="mySlides">
            <div class="numbertext">4 / 8</div>
            <img src="https://placehold.co/500x300/000/cyan?text=4">
            <figcaption>fourth cyan</figcaption>
          </figure>
          
          <figure class="mySlides">
            <div class="numbertext">5 / 8</div>
            <img src="https://placehold.co/500x300/000/cyan?text=5">
            <figcaption>fifth cyan</figcaption>
          </figure>
          
          <figure class="mySlides">
            <div class="numbertext">6 / 8</div>
            <img src="https://placehold.co/500x300/000/cyan?text=6">
            <figcaption>sixth cyan</figcaption>
          </figure>
          
          <figure class="mySlides">
            <div class="numbertext">7 / 8</div>
            <img src="https://placehold.co/500x300/000/cyan?text=7">
            <figcaption>seventh cyan</figcaption>
          </figure>
          
          <figure class="mySlides">
            <div class="numbertext">8 / 8</div>
            <img src="https://placehold.co/500x300/000/cyan?text=8">
            <figcaption>eighth cyan</figcaption>
          </figure>
        </div>        

        <a class="prev" onclick="plusSlides(-1)"><span/></a>
        <a class="next" onclick="plusSlides(1)"><span/></a>
      </div>
    </div>
    
    <!-- gallery 2 -->
    
    <section class="gallery">
      <figure>
        <img src="https://placehold.co/500x300/000/chartreuse?text=1" onclick="openLightbox();currentSlide(1)"/>
      </figure>
      
      <figure>
        <img src="https://placehold.co/500x300/000/chartreuse?text=2" onclick="openLightbox();currentSlide(2)"/>
      </figure>
      
      <figure>
        <img src="https://placehold.co/500x300/000/chartreuse?text=3" onclick="openLightbox();currentSlide(3)"/>
      </figure>
      
      <figure>
        <img src="https://placehold.co/500x300/000/chartreuse?text=4" onclick="openLightbox();currentSlide(4)"/>
      </figure>
      
      <figure>
        <img src="https://placehold.co/500x300/000/chartreuse?text=5" onclick="openLightbox();currentSlide(5)"/>
      </figure>
      
      <figure>
        <img src="https://placehold.co/500x300/000/chartreuse?text=6" onclick="openLightbox();currentSlide(6)"/>
      </figure>
      
      <figure>
        <img src="https://placehold.co/500x300/000/chartreuse?text=7" onclick="openLightbox();currentSlide(7)"/>
      </figure>
      
      <figure>
        <img src="https://placehold.co/500x300/000/chartreuse?text=8" onclick="openLightbox();currentSlide(8)"/>
      </figure>
    </section>

    <!-- lightbox  for gallery 2 -->
    <div class="lightbox" onclick="closeLightbox()">
      
      <div class="lightbox-content">
        <div class="imgcap">
          <span class="close" onclick="closeLightbox()"></span>
          <figure class="mySlides">
            <p class="numbertext">1 / 8</p>
            <img src="https://placehold.co/500x300/000/chartreuse?text=1"/>
            <figcaption>first green</figcaption>
          </figure>

          <figure class="mySlides">
            <div class="numbertext">2 / 8</div>
            <img src="https://placehold.co/500x300/000/chartreuse?text=2"/>
            <figcaption>second green</figcaption>
          </figure>

          <figure class="mySlides">
            <div class="numbertext">3 / 8</div>
            <img src="https://placehold.co/500x300/000/chartreuse?text=3">
            <figcaption>third green</figcaption>
          </figure>

          <figure class="mySlides">
            <div class="numbertext">4 / 8</div>
            <img src="https://placehold.co/500x300/000/chartreuse?text=4">
            <figcaption>fourth green</figcaption>
          </figure>
          
          <figure class="mySlides">
            <div class="numbertext">5 / 8</div>
            <img src="https://placehold.co/500x300/000/chartreuse?text=5">
            <figcaption>fifth green</figcaption>
          </figure>
          
          <figure class="mySlides">
            <div class="numbertext">6 / 8</div>
            <img src="https://placehold.co/500x300/000/chartreuse?text=6">
            <figcaption>sixth green</figcaption>
          </figure>
          
          <figure class="mySlides">
            <div class="numbertext">7 / 8</div>
            <img src="https://placehold.co/500x300/000/chartreuse?text=7">
            <figcaption>seventh green</figcaption>
          </figure>
          
          <figure class="mySlides">
            <div class="numbertext">8 / 8</div>
            <img src="https://placehold.co/500x300/000/chartreuse?text=8">
            <figcaption>eighth green</figcaption>
          </figure>
        </div>

        <a class="prev" onclick="plusSlides(-1)"><span/></a>
        <a class="next" onclick="plusSlides(1)"><span/></a>
      </div>
    </div>

I don’t know what I’m doing wrong :^(

Fill primevue select label by value on load

I have a primevue Select that accepts a key value pair for the label/value:

       <FloatLabel variant="on">
          <Select
              id="edit-array"
              v-model="formData.arrayChoice"
              :options="optionsArray"
              optionLabel="label"
              optionValue="value"
              dropdown
              fluid
          />
          <label for="edit-array">Select Option</label>
        </FloatLabel>

// form data that is set on page load from the DB in another spot in the code
const formData = ref({
  arrayChoice: '',
  etc
})
const optionsArray = computed(() => {
  return options.map((op) => ({
        value: op.value,
        label: op.label
      })
  )
})

This properly displays the options when I click the dropdown and it correctly sets the value when the associated label is chosen, however the previously set option isn’t displayed when the form first loads.

formData.arrayChoice is analogous to optionsArray‘s value so I was expecting the label that’s selected to match the one associated with the value but it never does, it’s just a blank dropdown until something is selected. What am I missing?

I did also confirm that formData.arrayChoice is correctly set on page load (as the dropdown value)

How to check if NextJs Link is exactly active with hash?

I’m using NextJs with shadcn and would like to highlight the active link. My index page contains some divs with anchor tags so there are several routes

  • /
  • /#section-1
  • /#section-2

I created a link component that should check if there is an exact match

"use client";

import clsx from "clsx";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useEffect, useState } from "react";

interface Props {
    href: string;
    title: string;
}

export default function NavigationLink({ href, title }: Props) {
    const pathname = usePathname();
    const [hash, setHash] = useState("");

    useEffect(() => {
        const updateHash = () => setHash(window.location.hash);
        updateHash();
        window.addEventListener("hashchange", updateHash);
        return () => window.removeEventListener("hashchange", updateHash);
    }, []);

    const [basePath, anchor] = href.split("#");
    const isSamePath = pathname === basePath;
    const isSameAnchor = anchor ? hash === `#${anchor}` : hash === "";
    const isActive = isSamePath && isSameAnchor;

    return (
        <Link
            href={href}
            aria-current={isActive ? "page" : undefined}
            className={clsx(
                "text-sm transition-colors duration-300",
                isActive
                    ? "leading-none font-medium"
                    : "not-hover:text-muted-foreground",
            )}
        >
            {title}
        </Link>
    );
}

Unfortunately this is not 100% correct yet. When visiting the app on /#section-1 the highlighting works. But when starting on / and then navigating to /#section-1 the index route remains highlighted and the section route won’t be highlighted. So I guess there is no trigger for a rerender when the hash changes.

Do you have any ideas how to fix this behaviour?

If there is an already builtin NextJs solution for this please let me know!

Is there a way to automatically reorder functions/variables by call order in VSCode (TypeScript)?

I’m working in a large private scope (~1300 lines) that contains many variables and helper functions defined with let.

When I move code around, I sometimes run into errors like:

Cannot access 'x' before initialization

because a function or variable is referenced before it’s declared.

Is there a refactoring command, VSCode feature, or extension that can automatically reorder function/variable declarations according to call order, so these initialization issues are avoided?