Initial commit: igny8 project

This commit is contained in:
igny8
2025-11-09 10:27:02 +00:00
commit 60b8188111
27265 changed files with 4360521 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
import type { Action, Store } from 'redux'
import { createDragDropActions } from '../actions/dragDrop/index.js'
import type {
ActionCreator,
Backend,
DragDropActions,
DragDropManager,
DragDropMonitor,
HandlerRegistry,
} from '../interfaces.js'
import type { State } from '../reducers/index.js'
import type { DragDropMonitorImpl } from './DragDropMonitorImpl.js'
export class DragDropManagerImpl implements DragDropManager {
private store: Store<State>
private monitor: DragDropMonitor
private backend: Backend | undefined
private isSetUp = false
public constructor(store: Store<State>, monitor: DragDropMonitor) {
this.store = store
this.monitor = monitor
store.subscribe(this.handleRefCountChange)
}
public receiveBackend(backend: Backend): void {
this.backend = backend
}
public getMonitor(): DragDropMonitor {
return this.monitor
}
public getBackend(): Backend {
return this.backend as Backend
}
public getRegistry(): HandlerRegistry {
return (this.monitor as DragDropMonitorImpl).registry
}
public getActions(): DragDropActions {
/* eslint-disable-next-line @typescript-eslint/no-this-alias */
const manager = this
const { dispatch } = this.store
function bindActionCreator(actionCreator: ActionCreator<any>) {
return (...args: any[]) => {
const action = actionCreator.apply(manager, args as any)
if (typeof action !== 'undefined') {
dispatch(action)
}
}
}
const actions = createDragDropActions(this)
return Object.keys(actions).reduce(
(boundActions: DragDropActions, key: string) => {
const action: ActionCreator<any> = (actions as any)[
key
] as ActionCreator<any>
;(boundActions as any)[key] = bindActionCreator(action)
return boundActions
},
{} as DragDropActions,
)
}
public dispatch(action: Action<any>): void {
this.store.dispatch(action)
}
private handleRefCountChange = (): void => {
const shouldSetUp = this.store.getState().refCount > 0
if (this.backend) {
if (shouldSetUp && !this.isSetUp) {
this.backend.setup()
this.isSetUp = true
} else if (!shouldSetUp && this.isSetUp) {
this.backend.teardown()
this.isSetUp = false
}
}
}
}

View File

@@ -0,0 +1,216 @@
import { invariant } from '@react-dnd/invariant'
import type { Store } from 'redux'
import type {
DragDropMonitor,
HandlerRegistry,
Identifier,
Listener,
Unsubscribe,
XYCoord,
} from '../interfaces.js'
import type { State } from '../reducers/index.js'
import {
getDifferenceFromInitialOffset,
getSourceClientOffset,
} from '../utils/coords.js'
import { areDirty } from '../utils/dirtiness.js'
import { matchesType } from '../utils/matchesType.js'
export class DragDropMonitorImpl implements DragDropMonitor {
private store: Store<State>
public readonly registry: HandlerRegistry
public constructor(store: Store<State>, registry: HandlerRegistry) {
this.store = store
this.registry = registry
}
public subscribeToStateChange(
listener: Listener,
options: { handlerIds?: string[] } = {},
): Unsubscribe {
const { handlerIds } = options
invariant(typeof listener === 'function', 'listener must be a function.')
invariant(
typeof handlerIds === 'undefined' || Array.isArray(handlerIds),
'handlerIds, when specified, must be an array of strings.',
)
let prevStateId = this.store.getState().stateId
const handleChange = () => {
const state = this.store.getState()
const currentStateId = state.stateId
try {
const canSkipListener =
currentStateId === prevStateId ||
(currentStateId === prevStateId + 1 &&
!areDirty(state.dirtyHandlerIds, handlerIds))
if (!canSkipListener) {
listener()
}
} finally {
prevStateId = currentStateId
}
}
return this.store.subscribe(handleChange)
}
public subscribeToOffsetChange(listener: Listener): Unsubscribe {
invariant(typeof listener === 'function', 'listener must be a function.')
let previousState = this.store.getState().dragOffset
const handleChange = () => {
const nextState = this.store.getState().dragOffset
if (nextState === previousState) {
return
}
previousState = nextState
listener()
}
return this.store.subscribe(handleChange)
}
public canDragSource(sourceId: string | undefined): boolean {
if (!sourceId) {
return false
}
const source = this.registry.getSource(sourceId)
invariant(source, `Expected to find a valid source. sourceId=${sourceId}`)
if (this.isDragging()) {
return false
}
return source.canDrag(this, sourceId)
}
public canDropOnTarget(targetId: string | undefined): boolean {
// undefined on initial render
if (!targetId) {
return false
}
const target = this.registry.getTarget(targetId)
invariant(target, `Expected to find a valid target. targetId=${targetId}`)
if (!this.isDragging() || this.didDrop()) {
return false
}
const targetType = this.registry.getTargetType(targetId)
const draggedItemType = this.getItemType()
return (
matchesType(targetType, draggedItemType) && target.canDrop(this, targetId)
)
}
public isDragging(): boolean {
return Boolean(this.getItemType())
}
public isDraggingSource(sourceId: string | undefined): boolean {
// undefined on initial render
if (!sourceId) {
return false
}
const source = this.registry.getSource(sourceId, true)
invariant(source, `Expected to find a valid source. sourceId=${sourceId}`)
if (!this.isDragging() || !this.isSourcePublic()) {
return false
}
const sourceType = this.registry.getSourceType(sourceId)
const draggedItemType = this.getItemType()
if (sourceType !== draggedItemType) {
return false
}
return source.isDragging(this, sourceId)
}
public isOverTarget(
targetId: string | undefined,
options = { shallow: false },
): boolean {
// undefined on initial render
if (!targetId) {
return false
}
const { shallow } = options
if (!this.isDragging()) {
return false
}
const targetType = this.registry.getTargetType(targetId)
const draggedItemType = this.getItemType()
if (draggedItemType && !matchesType(targetType, draggedItemType)) {
return false
}
const targetIds = this.getTargetIds()
if (!targetIds.length) {
return false
}
const index = targetIds.indexOf(targetId)
if (shallow) {
return index === targetIds.length - 1
} else {
return index > -1
}
}
public getItemType(): Identifier {
return this.store.getState().dragOperation.itemType as Identifier
}
public getItem(): any {
return this.store.getState().dragOperation.item
}
public getSourceId(): string | null {
return this.store.getState().dragOperation.sourceId
}
public getTargetIds(): string[] {
return this.store.getState().dragOperation.targetIds
}
public getDropResult(): any {
return this.store.getState().dragOperation.dropResult
}
public didDrop(): boolean {
return this.store.getState().dragOperation.didDrop
}
public isSourcePublic(): boolean {
return Boolean(this.store.getState().dragOperation.isSourcePublic)
}
public getInitialClientOffset(): XYCoord | null {
return this.store.getState().dragOffset.initialClientOffset
}
public getInitialSourceClientOffset(): XYCoord | null {
return this.store.getState().dragOffset.initialSourceClientOffset
}
public getClientOffset(): XYCoord | null {
return this.store.getState().dragOffset.clientOffset
}
public getSourceClientOffset(): XYCoord | null {
return getSourceClientOffset(this.store.getState().dragOffset)
}
public getDifferenceFromInitialOffset(): XYCoord | null {
return getDifferenceFromInitialOffset(this.store.getState().dragOffset)
}
}

View File

@@ -0,0 +1,181 @@
import { asap } from '@react-dnd/asap'
import { invariant } from '@react-dnd/invariant'
import type { Store } from 'redux'
import {
addSource,
addTarget,
removeSource,
removeTarget,
} from '../actions/registry.js'
import {
validateSourceContract,
validateTargetContract,
validateType,
} from '../contracts.js'
import type {
DragSource,
DropTarget,
HandlerRegistry,
Identifier,
SourceType,
TargetType,
} from '../interfaces.js'
import { HandlerRole } from '../interfaces.js'
import type { State } from '../reducers/index.js'
import { getNextUniqueId } from '../utils/getNextUniqueId.js'
function getNextHandlerId(role: HandlerRole): string {
const id = getNextUniqueId().toString()
switch (role) {
case HandlerRole.SOURCE:
return `S${id}`
case HandlerRole.TARGET:
return `T${id}`
default:
throw new Error(`Unknown Handler Role: ${role}`)
}
}
function parseRoleFromHandlerId(handlerId: string) {
switch (handlerId[0]) {
case 'S':
return HandlerRole.SOURCE
case 'T':
return HandlerRole.TARGET
default:
throw new Error(`Cannot parse handler ID: ${handlerId}`)
}
}
function mapContainsValue<T>(map: Map<string, T>, searchValue: T) {
const entries = map.entries()
let isDone = false
do {
const {
done,
value: [, value],
} = entries.next()
if (value === searchValue) {
return true
}
isDone = !!done
} while (!isDone)
return false
}
export class HandlerRegistryImpl implements HandlerRegistry {
private types: Map<string, SourceType | TargetType> = new Map()
private dragSources: Map<string, DragSource> = new Map()
private dropTargets: Map<string, DropTarget> = new Map()
private pinnedSourceId: string | null = null
private pinnedSource: any = null
private store: Store<State>
public constructor(store: Store<State>) {
this.store = store
}
public addSource(type: SourceType, source: DragSource): string {
validateType(type)
validateSourceContract(source)
const sourceId = this.addHandler(HandlerRole.SOURCE, type, source)
this.store.dispatch(addSource(sourceId))
return sourceId
}
public addTarget(type: TargetType, target: DropTarget): string {
validateType(type, true)
validateTargetContract(target)
const targetId = this.addHandler(HandlerRole.TARGET, type, target)
this.store.dispatch(addTarget(targetId))
return targetId
}
public containsHandler(handler: DragSource | DropTarget): boolean {
return (
mapContainsValue(this.dragSources, handler) ||
mapContainsValue(this.dropTargets, handler)
)
}
public getSource(sourceId: string, includePinned = false): DragSource {
invariant(this.isSourceId(sourceId), 'Expected a valid source ID.')
const isPinned = includePinned && sourceId === this.pinnedSourceId
const source = isPinned ? this.pinnedSource : this.dragSources.get(sourceId)
return source
}
public getTarget(targetId: string): DropTarget {
invariant(this.isTargetId(targetId), 'Expected a valid target ID.')
return this.dropTargets.get(targetId) as DropTarget
}
public getSourceType(sourceId: string): Identifier {
invariant(this.isSourceId(sourceId), 'Expected a valid source ID.')
return this.types.get(sourceId) as Identifier
}
public getTargetType(targetId: string): Identifier | Identifier[] {
invariant(this.isTargetId(targetId), 'Expected a valid target ID.')
return this.types.get(targetId) as Identifier | Identifier[]
}
public isSourceId(handlerId: string): boolean {
const role = parseRoleFromHandlerId(handlerId)
return role === HandlerRole.SOURCE
}
public isTargetId(handlerId: string): boolean {
const role = parseRoleFromHandlerId(handlerId)
return role === HandlerRole.TARGET
}
public removeSource(sourceId: string): void {
invariant(this.getSource(sourceId), 'Expected an existing source.')
this.store.dispatch(removeSource(sourceId))
asap(() => {
this.dragSources.delete(sourceId)
this.types.delete(sourceId)
})
}
public removeTarget(targetId: string): void {
invariant(this.getTarget(targetId), 'Expected an existing target.')
this.store.dispatch(removeTarget(targetId))
this.dropTargets.delete(targetId)
this.types.delete(targetId)
}
public pinSource(sourceId: string): void {
const source = this.getSource(sourceId)
invariant(source, 'Expected an existing source.')
this.pinnedSourceId = sourceId
this.pinnedSource = source
}
public unpinSource(): void {
invariant(this.pinnedSource, 'No source is pinned at the time.')
this.pinnedSourceId = null
this.pinnedSource = null
}
private addHandler(
role: HandlerRole,
type: SourceType | TargetType,
handler: DragSource | DropTarget,
): string {
const id = getNextHandlerId(role)
this.types.set(id, type)
if (role === HandlerRole.SOURCE) {
this.dragSources.set(id, handler as DragSource)
} else if (role === HandlerRole.TARGET) {
this.dropTargets.set(id, handler as DropTarget)
}
return id
}
}