preact/hooks#useLayoutEffect JavaScript Examples

The following examples show how to use preact/hooks#useLayoutEffect. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: CodePanel.js    From v8-deopt-viewer with MIT License 5 votes vote down vote up
/**
 * @typedef CodePanelProps
 * @property {import("../").FileV8DeoptInfoWithSources} fileDeoptInfo
 * @property {number} fileId
 * @property {import('./CodeSettings').CodeSettingsState} settings
 * @param {CodePanelProps} props
 */
export function CodePanel({ fileDeoptInfo, fileId, settings }) {
	if (fileDeoptInfo.srcError) {
		return <CodeError srcError={fileDeoptInfo.srcError} />;
	} else if (!fileDeoptInfo.src) {
		return <CodeError srcError="No sources for the file were found." />;
	}

	const lang = determineLanguage(fileDeoptInfo.srcPath);

	const state = useAppState();
	const selectedLine = state.selectedPosition?.line;

	/**
	 * @typedef {Map<string, import('../utils/deoptMarkers').Marker>} MarkerMap
	 * @type {[MarkerMap, import('preact/hooks').StateUpdater<MarkerMap>]}
	 */
	const [markers, setMarkers] = useState(null);

	/** @type {import('preact').RefObject<HTMLElement>} */
	const codeRef = useRef(null);
	useLayoutEffect(() => {
		// Saved the new markers so we can select them when CodePanelContext changes
		const markers = addDeoptMarkers(codeRef.current, fileId, fileDeoptInfo);
		setMarkers(new Map(markers.map((marker) => [marker.id, marker])));
	}, [fileId, fileDeoptInfo]);

	useEffect(() => {
		if (state.prevSelectedEntry) {
			markers
				.get(getMarkerId(state.prevSelectedEntry))
				?.classList.remove(active);
		}

		/** @type {ScrollIntoViewOptions} */
		const scrollIntoViewOpts = { block: "center", behavior: "smooth" };
		if (state.selectedEntry) {
			const target = markers.get(getMarkerId(state.selectedEntry));
			target.classList.add(active);
			// TODO: Why doesn't the smooth behavior always work? It seems that only
			// the first or last call to scrollIntoView with behavior smooth works?
			target.scrollIntoView(scrollIntoViewOpts);
		} else if (state.selectedPosition) {
			const lineSelector = `.line-numbers-rows > span:nth-child(${state.selectedPosition.line})`;
			document.querySelector(lineSelector)?.scrollIntoView(scrollIntoViewOpts);
		}

		// TODO: Figure out how to scroll line number into view when
		// selectedPosition is set but selectedMarkerId is not
	}, [state]);

	return (
		<div
			class={[
				codePanel,
				(settings.showLowSevs && showLowSevsClass) || null,
			].join(" ")}
		>
			<PrismCode
				src={fileDeoptInfo.src}
				lang={lang}
				class={(!settings.hideLineNums && "line-numbers") || null}
				ref={codeRef}
			>
				<LineNumbers selectedLine={selectedLine} contents={fileDeoptInfo.src} />
			</PrismCode>
		</div>
	);
}
Example #2
Source File: graph.js    From rctf with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
function Graph ({ graphData, classes }) {
  const svgRef = useRef(null)
  const [width, setWidth] = useState(window.innerWidth)
  const updateWidth = useCallback(() => {
    if (svgRef.current === null) return
    setWidth(svgRef.current.getBoundingClientRect().width)
  }, [])

  const [tooltipData, setTooltipData] = useState({
    x: 0,
    y: 0,
    content: ''
  })

  useLayoutEffect(() => {
    updateWidth()
  }, [updateWidth])
  useEffect(() => {
    function handleResize () {
      updateWidth()
    }
    window.addEventListener('resize', handleResize)
    return () => window.removeEventListener('resize', handleResize)
  }, [updateWidth])

  const { polylines, labels } = useMemo(() => {
    if (!graphData || graphData.length === 0) {
      return {
        polylines: [],
        labels: []
      }
    }
    const minX = config.startTime
    const maxX = Math.min(Date.now(), config.endTime)
    let maxY = 0
    graphData.graph.forEach((user) => {
      user.points.forEach((point) => {
        if (point.score > maxY) {
          maxY = point.score
        }
      })
    })
    const labels = getXLabels({ minX, maxX, width })
    const polylines = graphData.graph.map((user) => pointsToPolyline({
      points: user.points,
      id: user.id,
      name: user.name,
      currentScore: user.points[0].score,
      maxX,
      minX,
      maxY,
      width
    }))
    return { polylines, labels }
  }, [graphData, width])

  const handleTooltipIn = useCallback((content) => () => {
    setTooltipData(d => ({
      ...d,
      content
    }))
  }, [])

  const handleTooltipMove = useCallback((evt) => {
    setTooltipData(d => ({
      ...d,
      x: evt.clientX,
      y: evt.clientY
    }))
  }, [])

  const handleTooltipOut = useCallback(() => {
    setTooltipData(d => ({
      ...d,
      content: ''
    }))
  }, [])

  if (graphData === null) {
    return null
  }

  return (
    <div class={`frame ${classes.root}`}>
      <div class='frame__body'>
        <svg ref={svgRef} viewBox={`${-stroke - axis} ${-stroke} ${width + stroke * 2 + axis} ${height + stroke * 2 + axis + axisGap}`}>
          <Fragment>
            {polylines.map(({ points, color, name, currentScore }, i) => (
              <GraphLine
                key={i}
                stroke={color}
                points={points}
                name={name}
                currentScore={currentScore}
                onMouseMove={handleTooltipMove}
                onMouseOut={handleTooltipOut}
                onTooltipIn={handleTooltipIn}
              />
            ))}
          </Fragment>
          <Fragment>
            {labels.map((label, i) => (
              <text x={label.x} y={height + axis + axisGap} key={i} fill='#fff'>{label.label}</text>
            ))}
          </Fragment>
          <line
            x1={-axisGap}
            y1={height + axisGap}
            x2={width}
            y2={height + axisGap}
            stroke='var(--cirrus-bg)'
            stroke-linecap='round'
            stroke-width={stroke}
          />
          <line
            x1={-axisGap}
            y1='0'
            x2={-axisGap}
            y2={height + axisGap}
            stroke='var(--cirrus-bg)'
            stroke-linecap='round'
            stroke-width={stroke}
          />
        </svg>
      </div>
      {tooltipData.content && (
        <div
          class={classes.tooltip}
          style={{
            transform: `translate(${tooltipData.x}px, ${tooltipData.y}px)`
          }}
        >
          {tooltipData.content}
        </div>
      )}
    </div>
  )
}