-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into admin-ui-list-config-cleanup
- Loading branch information
Showing
22 changed files
with
341 additions
and
296 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
'@keystonejs/list-plugins': patch | ||
--- | ||
|
||
Tweaked hooks and utility function. | ||
* Renamed `composeResolveInput` utility function to `composeHook` to indicate right use by name, this can also be used in other hook type and not just `resolveInput` hook. | ||
* Switch to use of `operation` param to hook for detecting if this is `create` or `update` operation instead of existingItem being `undefined`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@keystonejs/app-admin-ui': patch | ||
--- | ||
|
||
Converted ResizeHandler and ScrollQuery components to custom hooks. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
'@keystonejs/keystone': patch | ||
'@keystonejs/utils': patch | ||
--- | ||
|
||
Converted some stray promise chains to async/await. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
--- | ||
'@keystonejs/cypress-project-access-control': patch | ||
'@keystonejs/cypress-project-basic': patch | ||
'@keystonejs/cypress-project-client-validation': patch | ||
'@keystonejs/cypress-project-login': patch | ||
'@keystonejs/keystone': major | ||
'@keystonejs/session': major | ||
--- | ||
|
||
The `cookieSecret` option no longer defaults to a static value. It is now required in production mode. In development mode, if undefined, a random new value is generated each time the server is started. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@keystonejs/app-admin-ui': major | ||
--- | ||
|
||
Removed the method `AdminUIApp.getAdminMeta()` in favour of the more complete `AdminUIApp.getAdminUIMeta(keystone)`. |
179 changes: 96 additions & 83 deletions
179
packages/app-admin-ui/client/components/Nav/ResizeHandler.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,115 +1,128 @@ | ||
import { Component } from 'react'; | ||
import { useState, useRef, useEffect } from 'react'; | ||
import raf from 'raf-schd'; | ||
|
||
import { withKeyboardConsumer } from '../KeyboardShortcuts'; | ||
import { useKeyboardManager } from '../KeyboardShortcuts'; | ||
|
||
const LS_KEY = 'KEYSTONE_NAVIGATION_STATE'; | ||
const DEFAULT_STATE = { isCollapsed: false, width: 280 }; | ||
const MIN_WIDTH = 140; | ||
const MAX_WIDTH = 800; | ||
|
||
export const KEYBOARD_SHORTCUT = '['; | ||
|
||
function getCache() { | ||
if (typeof localStorage !== 'undefined') { | ||
const getCache = () => { | ||
if (localStorage !== undefined) { | ||
const stored = localStorage.getItem(LS_KEY); | ||
return stored ? JSON.parse(stored) : DEFAULT_STATE; | ||
|
||
if (stored) { | ||
return JSON.parse(stored); | ||
} | ||
} | ||
|
||
return DEFAULT_STATE; | ||
} | ||
function setCache(state) { | ||
if (typeof localStorage !== 'undefined') { | ||
}; | ||
|
||
const setCache = state => { | ||
if (localStorage !== undefined) { | ||
localStorage.setItem(LS_KEY, JSON.stringify(state)); | ||
} | ||
} | ||
}; | ||
|
||
class ResizeHandler extends Component { | ||
state = getCache(); | ||
export const useResizeHandler = () => { | ||
// TODO: should we be calling this in the function body? | ||
const { width: cachedWidth, isCollapsed: cachedIsCollapsed } = getCache(); | ||
|
||
componentDidMount() { | ||
this.props.keyManager.subscribe(KEYBOARD_SHORTCUT, this.toggleCollapse); | ||
} | ||
componentWillUnmount() { | ||
this.props.keyManager.unsubscribe(KEYBOARD_SHORTCUT); | ||
} | ||
// These should trigger renders | ||
const [width, setWidth] = useState(cachedWidth); | ||
const [isCollapsed, setIsCollapsed] = useState(cachedIsCollapsed); | ||
const [isMouseDown, setIsMouseDown] = useState(false); | ||
const [isDragging, setIsDragging] = useState(false); | ||
|
||
storeState = s => { | ||
// only keep the `isCollapsed` and `width` properties in locals storage | ||
const isCollapsed = s.isCollapsed !== undefined ? s.isCollapsed : this.state.isCollapsed; | ||
const width = s.width !== undefined ? s.width : this.state.width; | ||
// Internal state tracking | ||
const initialX = useRef(); | ||
const initialWidth = useRef(); | ||
|
||
setCache({ isCollapsed, width }); | ||
const { addBinding, removeBinding } = useKeyboardManager(); | ||
|
||
this.setState(s); | ||
}; | ||
useEffect(() => { | ||
addBinding(KEYBOARD_SHORTCUT, toggleCollapse); | ||
return () => { | ||
removeBinding(KEYBOARD_SHORTCUT); | ||
}; | ||
}, []); | ||
|
||
useEffect(() => { | ||
const handleResize = raf(event => { | ||
// on occasion a mouse move event occurs before the event listeners have a chance to detach | ||
if (!isMouseDown) return; | ||
|
||
// initialize dragging | ||
if (!isDragging) { | ||
setIsDragging(true); | ||
initialWidth.current = width; | ||
return; | ||
} | ||
|
||
// allow the product nav to be 75% of the available page width | ||
const adjustedMax = MAX_WIDTH - initialWidth.current; | ||
const adjustedMin = MIN_WIDTH - initialWidth.current; | ||
|
||
const newDelta = Math.max(Math.min(event.pageX - initialX.current, adjustedMax), adjustedMin); | ||
const newWidth = initialWidth.current + newDelta; | ||
|
||
setWidth(newWidth); | ||
}); | ||
|
||
const handleResizeEnd = () => { | ||
// reset non-width states | ||
setIsDragging(false); | ||
setIsMouseDown(false); | ||
}; | ||
|
||
window.addEventListener('mousemove', handleResize, { passive: true }); | ||
window.addEventListener('mouseup', handleResizeEnd, { passive: true }); | ||
|
||
return () => { | ||
window.removeEventListener('mousemove', handleResize, { passive: true }); | ||
window.removeEventListener('mouseup', handleResizeEnd, { passive: true }); | ||
}; | ||
}, [isMouseDown, isDragging]); | ||
|
||
// Only keep the `isCollapsed` and `width` properties in locals storage | ||
useEffect(() => { | ||
setCache({ isCollapsed, width }); | ||
}, [isCollapsed, width]); | ||
|
||
handleResizeStart = (event: MouseEvent) => { | ||
const handleResizeStart = event => { | ||
// bail if not "left click" | ||
if (event.button && event.button > 0) return; | ||
|
||
// initialize resize gesture | ||
this.setState({ initialX: event.pageX, mouseIsDown: true }); | ||
|
||
// attach handlers (handleResizeStart is a bound to onMouseDown) | ||
window.addEventListener('mousemove', this.handleResize); | ||
window.addEventListener('mouseup', this.handleResizeEnd); | ||
initialX.current = event.pageX; | ||
setIsMouseDown(true); | ||
}; | ||
|
||
initializeDrag = () => { | ||
let initialWidth = this.state.width; | ||
|
||
this.setState({ initialWidth, isDragging: true }); | ||
const toggleCollapse = () => { | ||
setIsCollapsed(prevCollapsed => !prevCollapsed); | ||
}; | ||
|
||
handleResize = raf((event: MouseEvent) => { | ||
const { initialX, initialWidth, isDragging, mouseIsDown } = this.state; | ||
|
||
// on occasion a mouse move event occurs before the event listeners | ||
// have a chance to detach | ||
if (!mouseIsDown) return; | ||
|
||
// initialize dragging | ||
if (!isDragging) { | ||
this.initializeDrag(event); | ||
return; | ||
} | ||
|
||
// allow the product nav to be 75% of the available page width | ||
const adjustedMax = MAX_WIDTH - initialWidth; | ||
const adjustedMin = MIN_WIDTH - initialWidth; | ||
|
||
const delta = Math.max(Math.min(event.pageX - initialX, adjustedMax), adjustedMin); | ||
const width = initialWidth + delta; | ||
|
||
this.setState({ delta, width }); | ||
}); | ||
handleResizeEnd = () => { | ||
// reset non-width states | ||
this.setState({ delta: 0, isDragging: false, mouseIsDown: false }); | ||
|
||
// store the width | ||
this.storeState({ width: this.state.width }); | ||
|
||
// cleanup | ||
window.removeEventListener('mousemove', this.handleResize); | ||
window.removeEventListener('mouseup', this.handleResizeEnd); | ||
}; | ||
toggleCollapse = () => { | ||
const isCollapsed = !this.state.isCollapsed; | ||
this.storeState({ isCollapsed }); | ||
const resizeProps = { | ||
title: 'Drag to Resize', | ||
onMouseDown: handleResizeStart, | ||
}; | ||
|
||
render() { | ||
const resizeProps = { | ||
title: 'Drag to Resize', | ||
onMouseDown: this.handleResizeStart, | ||
}; | ||
const clickProps = { | ||
onClick: this.toggleCollapse, | ||
}; | ||
const snapshot = this.state; | ||
const clickProps = { | ||
onClick: toggleCollapse, | ||
}; | ||
|
||
return this.props.children(resizeProps, clickProps, snapshot); | ||
} | ||
} | ||
const snapshot = { | ||
width, | ||
isCollapsed, | ||
isMouseDown, | ||
isDragging, | ||
initialX: initialX.current, | ||
initialWidth: initialWidth.current, | ||
}; | ||
|
||
export default withKeyboardConsumer(ResizeHandler); | ||
return { resizeProps, clickProps, snapshot }; | ||
}; |
Oops, something went wrong.