snapshot of https://github.com/shadcn-ui/ui.git (main, 2026-09-15; blobs > 131072B omitted)
This commit is contained in:
97
packages/react/src/message-scroller/stores.ts
Normal file
97
packages/react/src/message-scroller/stores.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { EMPTY_MESSAGE_SCROLLER_VISIBILITY_STATE } from "./types"
|
||||
import type {
|
||||
MessageScrollerScrollable,
|
||||
MessageScrollerStore,
|
||||
MessageScrollerVisibilityState,
|
||||
MessageScrollerVisibilityStore,
|
||||
} from "./types"
|
||||
|
||||
// Generic useSyncExternalStore backing: a stable snapshot (referentially equal
|
||||
// while isEqual holds, so subscribers only re-render on real transitions) plus
|
||||
// optional reference-counting via the subscribe-time lifecycle callbacks.
|
||||
function createExternalStore<T>(
|
||||
initialSnapshot: T,
|
||||
isEqual: (a: T, b: T) => boolean
|
||||
) {
|
||||
let snapshot = initialSnapshot
|
||||
const listeners = new Set<() => void>()
|
||||
|
||||
return {
|
||||
getSnapshot: () => snapshot,
|
||||
hasListeners: () => listeners.size > 0,
|
||||
setSnapshot: (nextSnapshot: T) => {
|
||||
if (isEqual(snapshot, nextSnapshot)) {
|
||||
return
|
||||
}
|
||||
|
||||
snapshot = nextSnapshot
|
||||
listeners.forEach((listener) => listener())
|
||||
},
|
||||
subscribe: (
|
||||
listener: () => void,
|
||||
onFirstSubscribe?: () => void,
|
||||
onLastUnsubscribe?: () => void
|
||||
) => {
|
||||
const wasEmpty = listeners.size === 0
|
||||
|
||||
listeners.add(listener)
|
||||
|
||||
if (wasEmpty) {
|
||||
onFirstSubscribe?.()
|
||||
}
|
||||
|
||||
return () => {
|
||||
listeners.delete(listener)
|
||||
|
||||
if (listeners.size === 0) {
|
||||
onLastUnsubscribe?.()
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function createMessageScrollerStore<T>(
|
||||
initialSnapshot: T,
|
||||
isEqual: (a: T, b: T) => boolean
|
||||
): MessageScrollerStore<T> {
|
||||
return createExternalStore(initialSnapshot, isEqual)
|
||||
}
|
||||
|
||||
function createMessageScrollerVisibilityStore(): MessageScrollerVisibilityStore {
|
||||
return createExternalStore(
|
||||
EMPTY_MESSAGE_SCROLLER_VISIBILITY_STATE,
|
||||
areVisibilityStatesEqual
|
||||
)
|
||||
}
|
||||
|
||||
function areScrollStatesEqual(
|
||||
current: MessageScrollerScrollable,
|
||||
next: MessageScrollerScrollable
|
||||
) {
|
||||
return current.start === next.start && current.end === next.end
|
||||
}
|
||||
|
||||
function areVisibilityStatesEqual(
|
||||
current: MessageScrollerVisibilityState,
|
||||
next: MessageScrollerVisibilityState
|
||||
) {
|
||||
if (current.currentAnchorId !== next.currentAnchorId) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (current.visibleMessageIds.length !== next.visibleMessageIds.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
return current.visibleMessageIds.every(
|
||||
(messageId, index) => messageId === next.visibleMessageIds[index]
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
areScrollStatesEqual,
|
||||
areVisibilityStatesEqual,
|
||||
createMessageScrollerStore,
|
||||
createMessageScrollerVisibilityStore,
|
||||
}
|
||||
Reference in New Issue
Block a user