Strip template artifacts and fix lint errors
Remove npm lockfile, unused create-next-app SVGs, project-specific next.config workarounds, and the unused @remixicon/react and date-fns deps. Replace three set-state-in-effect patterns with useSyncExternalStore in use-mobile, DialogPortal, and Carousel; the carousel change also fixes a leaked reInit listener. yarn lint and yarn build are clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K8PmydbW6A1S2Tw3SFc3Cg
This commit is contained in:
@@ -58,14 +58,32 @@ function Carousel({
|
||||
},
|
||||
plugins
|
||||
)
|
||||
const [canScrollPrev, setCanScrollPrev] = React.useState(false)
|
||||
const [canScrollNext, setCanScrollNext] = React.useState(false)
|
||||
// Embla is an external store: subscribe to its events rather than mirroring
|
||||
// its scroll state into React state from an effect.
|
||||
const subscribe = React.useCallback(
|
||||
(onStoreChange: () => void) => {
|
||||
if (!api) return () => {}
|
||||
api.on("reInit", onStoreChange)
|
||||
api.on("select", onStoreChange)
|
||||
|
||||
const onSelect = React.useCallback((api: CarouselApi) => {
|
||||
if (!api) return
|
||||
setCanScrollPrev(api.canScrollPrev())
|
||||
setCanScrollNext(api.canScrollNext())
|
||||
}, [])
|
||||
return () => {
|
||||
api.off("reInit", onStoreChange)
|
||||
api.off("select", onStoreChange)
|
||||
}
|
||||
},
|
||||
[api]
|
||||
)
|
||||
|
||||
const canScrollPrev = React.useSyncExternalStore(
|
||||
subscribe,
|
||||
() => api?.canScrollPrev() ?? false,
|
||||
() => false
|
||||
)
|
||||
const canScrollNext = React.useSyncExternalStore(
|
||||
subscribe,
|
||||
() => api?.canScrollNext() ?? false,
|
||||
() => false
|
||||
)
|
||||
|
||||
const scrollPrev = React.useCallback(() => {
|
||||
api?.scrollPrev()
|
||||
@@ -93,17 +111,6 @@ function Carousel({
|
||||
setApi(api)
|
||||
}, [api, setApi])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!api) return
|
||||
onSelect(api)
|
||||
api.on("reInit", onSelect)
|
||||
api.on("select", onSelect)
|
||||
|
||||
return () => {
|
||||
api?.off("select", onSelect)
|
||||
}
|
||||
}, [api, onSelect])
|
||||
|
||||
return (
|
||||
<CarouselContext.Provider
|
||||
value={{
|
||||
|
||||
@@ -19,15 +19,21 @@ function DialogTrigger({
|
||||
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
||||
}
|
||||
|
||||
const subscribeToNothing = () => () => {}
|
||||
|
||||
function DialogPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
||||
// Radix's Portal calls createPortal(children, document.body) as soon as it
|
||||
// renders. During static prerendering there's no `document`, so gate the
|
||||
// portal behind an effect that only runs in the browser. Closed dialogs
|
||||
// render nothing on the server anyway, so there's no visual/hydration change.
|
||||
const [mounted, setMounted] = React.useState(false)
|
||||
React.useEffect(() => setMounted(true), [])
|
||||
// portal on the client snapshot, which is only read in the browser. Closed
|
||||
// dialogs render nothing on the server anyway, so there's no visual/
|
||||
// hydration change.
|
||||
const mounted = React.useSyncExternalStore(
|
||||
subscribeToNothing,
|
||||
() => true,
|
||||
() => false
|
||||
)
|
||||
|
||||
if (!mounted) return null
|
||||
|
||||
|
||||
Reference in New Issue
Block a user