modal-g
Version:
gantd modal
327 lines (312 loc) • 12.8 kB
text/typescript
import React from 'react'
import { ModalsState, PrivateModalStateProps, ActionTypes, Action } from './interface'
export const getModalState = (state: ModalsState, id: string): PrivateModalStateProps => state.modals[id] || state.initialModalState
const clamp = (min: number, max: number, value: number) => Math.max(min, Math.min(max, value))
const getAxis = (windowMeter: number, targetMeter: number, num?: number | string) => {
if (typeof num == 'number') return num
return (windowMeter - targetMeter) / 2
}
const convertPercentage = (target: string | number, windowSize: number, inital: number) => {
if (typeof target == 'number') return target
let reg = new RegExp(/^\d+%$/)
if (reg.test(target)) return Math.floor(windowSize * (Number(target.replace("%", "")) / 100))
return inital
}
const mapObject = <R, K extends keyof R>(obj: R, fn: (v: R[K]) => R[K]): R => Object.assign({}, ...Object.keys(obj).map(key => ({ [key]: fn(obj[key]) })))
const getNextZIndex = (state: ModalsState, id: string) => {
const { modals, maxZIndex } = state
if (Object.keys(modals).length === 1) return maxZIndex
let modalState = getModalState(state, id)
return modalState.zIndex === maxZIndex ? maxZIndex : maxZIndex + 1
}
const clampDrag = (windowWidth: number, windowHeight: number, x: number, y: number, width: number, height: number): { x: number, y: number } => {
const maxX = windowWidth - width
const maxY = windowHeight - height
const clampedX = clamp(0, maxX, x)
const clampedY = clamp(0, maxY, y)
return { x: clampedX, y: clampedY }
}
const clampResize = (minWidth: number, minHeight: number, windowWidth: number, windowHeight: number, x: number, y: number, width: number, height: number): { width: number, height: number } => {
const maxWidth = windowWidth - x
const maxHeight = windowHeight - y
const clampedWidth = clamp(minWidth, maxWidth, width)
const clampedHeight = clamp(minHeight, maxHeight, height)
return { width: clampedWidth, height: clampedHeight }
}
/**
* 获取当前页面以展示的所有modal的最大zIndex
* @param originMaxZindex 原有的maxZIndex
* @returns
*/
function getPageMaxZIndex(originMaxZindex: number) {
const allModals = document.querySelectorAll('.ant-modal-wrap');
let maxZindex = originMaxZindex;
allModals.forEach((modal: HTMLDivElement) => {
let zIndex = Number(modal?.style?.zIndex || 0);
let display = modal?.style?.display;
if (display != 'none' && zIndex && zIndex > maxZindex) {
maxZindex = zIndex;
}
});
return maxZindex + 1;
}
const resizableReducer: React.Reducer<ModalsState, Action> = (state, action) => {
const { minWidth, minHeight, initialModalState, windowSize } = state
const needIncrease = Object.keys(state.modals).length != 1
switch (action.type) {
case ActionTypes.mount:
let combineState = { ...initialModalState, ...action.itemState }
let inital = {
width: combineState.width,
height: combineState.height,
x: combineState.x,
y: combineState.y,
}
combineState.width = convertPercentage(combineState.width, windowSize.width, <number>initialModalState.width)
combineState.height = convertPercentage(combineState.height, windowSize.height, <number>initialModalState.height)
const x = getAxis(windowSize.width, combineState.width, combineState.x)
const y = getAxis(windowSize.height, combineState.height, combineState.y)
return {
...state,
maxZIndex: state.maxZIndex + 1,
modals: {
...state.modals,
[action.id]: {
inital,
...combineState,
x, y,
zIndex: state.maxZIndex + 1,
},
},
}
case ActionTypes.unmount:
const modalsClone = { ...state.modals }
delete modalsClone[action.id]
return {
...state,
modals: modalsClone,
}
case ActionTypes.focus:
const modalState = state.modals[action.id]
const maxZIndex = needIncrease ? state.maxZIndex + 1 : state.maxZIndex
return {
...state,
maxZIndex,
modals: {
...state.modals,
[action.id]: {
...modalState,
zIndex: maxZIndex,
},
},
}
case ActionTypes.show: {
const modalState = { ...state.modals[action.id] }
const needKeep = modalState.keepStateOnClose
const { inital, maximize } = modalState
const target = needKeep ? modalState : inital
if (!needKeep) {
typeof inital.width == 'string' && (modalState.width = convertPercentage(inital.width, windowSize.width, <number>initialModalState.width))
typeof inital.height == 'string' && (modalState.height = convertPercentage(inital.height, windowSize.height, <number>initialModalState.height))
}
const maxZIndex = needIncrease ? state.maxZIndex + 1 : state.maxZIndex
const centerX = getAxis(windowSize.width, <number>modalState.width, target.x)
const centerY = getAxis(windowSize.height, <number>modalState.height, target.y)
let isMaximized = modalState.isMaximized
let position = clampDrag(
windowSize.width,
windowSize.height,
centerX,
centerY,
<number>modalState.width,
<number>modalState.height,
)
let size = clampResize(
minWidth,
minHeight,
windowSize.width,
windowSize.height,
position.x,
position.y,
<number>modalState.width,
<number>modalState.height,
)
if (!needKeep && maximize) {
position = { x: 0, y: 0 }
size = { width: windowSize.width, height: windowSize.height }
isMaximized = maximize
}
return {
...state,
maxZIndex,
modals: {
...state.modals,
[action.id]: {
...modalState,
...position,
...size,
isMaximized,
zIndex: maxZIndex,
visible: true,
},
},
}
}
case ActionTypes.hide: {
const modalState = state.modals[action.id]
let resetState = {
...modalState,
width: convertPercentage(modalState.inital.width, windowSize.width, <number>initialModalState.width),
height: convertPercentage(modalState.inital.height, windowSize.height, <number>initialModalState.height),
isMaximized: false,
visible: false,
}
let newState = modalState.keepStateOnClose ? modalState : resetState
return {
...state,
modals: {
...state.modals,
[action.id]: newState,
},
}
}
case ActionTypes.max: {
const modalState = state.modals[action.id]
const history = {
x: modalState.x,
y: modalState.y,
width: modalState.width,
height: modalState.height,
}
return {
...state,
modals: {
...state.modals,
[action.id]: {
...modalState,
x: 0,
y: 0,
height: window.innerHeight,
width: window.innerWidth,
history,
isMaximized: true,
}
}
}
}
case ActionTypes.reset: {
const modalState = state.modals[action.id]
const { inital, history } = modalState
let target = history || inital
target.width = convertPercentage(target.width, windowSize.width, <number>initialModalState.width)
target.height = convertPercentage(target.height, windowSize.height, <number>initialModalState.height)
let x = target.x != undefined ? target.x : getAxis(windowSize.width, target.width)
let y = target.y != undefined ? target.y : getAxis(windowSize.height, target.height)
const position = clampDrag(
windowSize.width,
windowSize.height,
x,
y,
target.width,
target.height,
)
const size = clampResize(
minWidth,
minHeight,
windowSize.width,
windowSize.height,
position.x,
position.y,
target.width,
target.height,
)
return {
...state,
modals: {
...state.modals,
[action.id]: {
...modalState,
...position,
...size,
history: null,
isMaximized: false,
},
},
}
}
case ActionTypes.resize:
return {
...state,
maxZIndex: getNextZIndex(state, action.id),
modals: {
...state.modals,
[action.id]: {
...state.modals[action.id],
height:action.height,
width:action.width,
x:action.x,
y:action.y,
zIndex: getNextZIndex(state, action.id),
},
},
}
case ActionTypes.drag:
return {
...state,
maxZIndex: getNextZIndex(state, action.id),
modals: {
...state.modals,
[action.id]: {
...state.modals[action.id],
...clampDrag(
windowSize.width,
windowSize.height,
action.x,
action.y,
<number>state.modals[action.id].width,
<number>state.modals[action.id].height,
),
zIndex: getNextZIndex(state, action.id),
},
},
}
case ActionTypes.windowResize:
return {
...state,
windowSize: action.size,
modals: mapObject(state.modals, (modalState) => {
if (!modalState.visible) {
return modalState
}
const position = modalState.isMaximized ? { x: 0, y: 0 } : clampDrag(
action.size.width,
action.size.height,
modalState.x,
modalState.y,
<number>modalState.width,
<number>modalState.height,
)
const size = modalState.isMaximized ?
{ width: action.size.width, height: action.size.height }
: clampResize(
minWidth,
minHeight,
action.size.width,
action.size.height,
position.x,
position.y,
<number>modalState.width,
<number>modalState.height,
)
return {
...modalState,
...position,
...size,
}
}),
}
default:
throw new Error()
}
}
export default resizableReducer