mirror of
https://github.com/PostHog/posthog.git
synced 2024-11-21 21:49:51 +01:00
feat: Better floating of editing widget (#18780)
This commit is contained in:
parent
e35c8e574d
commit
9033d0822f
@ -56,7 +56,7 @@
|
||||
}
|
||||
|
||||
&--selected {
|
||||
--border-color: var(--primary-3000);
|
||||
--border-color: var(--border-bold);
|
||||
}
|
||||
|
||||
&--auto-hide-metadata {
|
||||
|
@ -75,7 +75,17 @@ function NodeWrapper<T extends CustomNotebookNodeAttributes>(props: NodeWrapperP
|
||||
// nodeId can start null, but should then immediately be generated
|
||||
const nodeLogic = useMountedLogic(notebookNodeLogic(logicProps))
|
||||
const { resizeable, expanded, actions, nodeId } = useValues(nodeLogic)
|
||||
const { setExpanded, deleteNode, toggleEditing, insertOrSelectNextLine } = useActions(nodeLogic)
|
||||
const { setRef, setExpanded, deleteNode, toggleEditing, insertOrSelectNextLine } = useActions(nodeLogic)
|
||||
|
||||
const { ref: inViewRef, inView } = useInView({ triggerOnce: true })
|
||||
|
||||
const setRefs = useCallback(
|
||||
(node) => {
|
||||
setRef(node)
|
||||
inViewRef(node)
|
||||
},
|
||||
[inViewRef]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
// TRICKY: child nodes mount the parent logic so we need to control the mounting / unmounting directly in this component
|
||||
@ -92,7 +102,6 @@ function NodeWrapper<T extends CustomNotebookNodeAttributes>(props: NodeWrapperP
|
||||
mountedNotebookLogic,
|
||||
})
|
||||
|
||||
const [ref, inView] = useInView({ triggerOnce: true })
|
||||
const contentRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
// If resizeable is true then the node attr "height" is required
|
||||
@ -136,7 +145,7 @@ function NodeWrapper<T extends CustomNotebookNodeAttributes>(props: NodeWrapperP
|
||||
<BindLogic logic={notebookNodeLogic} props={logicProps}>
|
||||
<NodeViewWrapper as="div">
|
||||
<div
|
||||
ref={ref}
|
||||
ref={setRefs}
|
||||
className={clsx(nodeType, 'NotebookNode', {
|
||||
'NotebookNode--auto-hide-metadata': autoHideMetadata,
|
||||
'NotebookNode--editable': getPos && isEditable,
|
||||
|
@ -63,6 +63,7 @@ export const notebookNodeLogic = kea<notebookNodeLogicType>([
|
||||
initializeNode: true,
|
||||
setMessageListeners: (listeners: NotebookNodeMessagesListeners) => ({ listeners }),
|
||||
setTitlePlaceholder: (titlePlaceholder: string) => ({ titlePlaceholder }),
|
||||
setRef: (ref: HTMLElement | null) => ({ ref }),
|
||||
}),
|
||||
|
||||
connect((props: NotebookNodeLogicProps) => ({
|
||||
@ -71,6 +72,13 @@ export const notebookNodeLogic = kea<notebookNodeLogicType>([
|
||||
})),
|
||||
|
||||
reducers(({ props }) => ({
|
||||
ref: [
|
||||
null as HTMLElement | null,
|
||||
{
|
||||
setRef: (_, { ref }) => ref,
|
||||
unregisterNodeLogic: () => null,
|
||||
},
|
||||
],
|
||||
expanded: [
|
||||
props.startExpanded ?? true,
|
||||
{
|
||||
@ -246,7 +254,9 @@ export const notebookNodeLogic = kea<notebookNodeLogicType>([
|
||||
props.updateAttributes(attributes)
|
||||
},
|
||||
toggleEditing: ({ visible }) => {
|
||||
const shouldShowThis = typeof visible === 'boolean' ? visible : !values.notebookLogic.values.editingNodeId
|
||||
const shouldShowThis =
|
||||
typeof visible === 'boolean' ? visible : values.notebookLogic.values.editingNodeId !== values.nodeId
|
||||
|
||||
props.notebookLogic.actions.setEditingNodeId(shouldShowThis ? values.nodeId : null)
|
||||
},
|
||||
initializeNode: () => {
|
||||
|
@ -147,14 +147,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&--editable {
|
||||
.NotebookEditor .ProseMirror {
|
||||
// Add some padding to help clicking below the last element
|
||||
padding-bottom: 10rem;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.NotebookColumn {
|
||||
position: relative;
|
||||
width: 0;
|
||||
@ -191,6 +183,11 @@
|
||||
.NotebookColumn__content {
|
||||
width: var(--notebook-column-left-width);
|
||||
transform: translateX(-100%);
|
||||
|
||||
> .LemonWidget .LemonWidget__content {
|
||||
max-height: var(--notebook-sidebar-height);
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,12 +215,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
&--editable {
|
||||
.NotebookEditor .ProseMirror {
|
||||
// Add some padding to help clicking below the last element
|
||||
padding-bottom: 10rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.NotebookColumn--left.NotebookColumn--showing {
|
||||
& + .NotebookEditor {
|
||||
.ProseMirror {
|
||||
// Add a lot of padding to allow the entire column to always be on screen
|
||||
padding-bottom: 100vh;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.NotebookHistory {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: var(--notebook-sidebar-height);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.NotebookInlineMenu {
|
||||
@ -236,13 +248,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.NotebookColumnLeft__widget {
|
||||
> .LemonWidget__content {
|
||||
max-height: calc(100vh - 220px);
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.LemonTable__content > table > thead {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
|
@ -4,8 +4,8 @@ import clsx from 'clsx'
|
||||
import { notebookLogic } from './notebookLogic'
|
||||
import { notebookNodeLogicType } from '../Nodes/notebookNodeLogicType'
|
||||
import { LemonButton } from '@posthog/lemon-ui'
|
||||
import { IconEyeVisible } from 'lib/lemon-ui/icons'
|
||||
import { NotebookHistory } from './NotebookHistory'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export const NotebookColumnLeft = (): JSX.Element | null => {
|
||||
const { editingNodeLogic, isShowingLeftColumn, showHistory } = useValues(notebookLogic)
|
||||
@ -16,7 +16,7 @@ export const NotebookColumnLeft = (): JSX.Element | null => {
|
||||
'NotebookColumn--showing': isShowingLeftColumn,
|
||||
})}
|
||||
>
|
||||
<div className="NotebookColumn__padding" />
|
||||
{editingNodeLogic ? <NotebookNodeSettingsOffset logic={editingNodeLogic} /> : null}
|
||||
<div className="NotebookColumn__content">
|
||||
{isShowingLeftColumn ? (
|
||||
editingNodeLogic ? (
|
||||
@ -30,6 +30,41 @@ export const NotebookColumnLeft = (): JSX.Element | null => {
|
||||
)
|
||||
}
|
||||
|
||||
export const NotebookNodeSettingsOffset = ({ logic }: { logic: BuiltLogic<notebookNodeLogicType> }): JSX.Element => {
|
||||
const { ref } = useValues(logic)
|
||||
const offsetRef = useRef<HTMLDivElement>(null)
|
||||
const [height, setHeight] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
// Interval to check the relative positions of the node and the offset div
|
||||
// updating the height so that it always is inline
|
||||
const updateHeight = (): void => {
|
||||
if (ref && offsetRef.current) {
|
||||
const newHeight = ref.getBoundingClientRect().top - offsetRef.current.getBoundingClientRect().top
|
||||
|
||||
if (height !== newHeight) {
|
||||
setHeight(newHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const interval = setInterval(updateHeight, 100)
|
||||
updateHeight()
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [ref, offsetRef.current, height])
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={offsetRef}
|
||||
// eslint-disable-next-line react/forbid-dom-props
|
||||
style={{
|
||||
height,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export const NotebookNodeSettingsWidget = ({ logic }: { logic: BuiltLogic<notebookNodeLogicType> }): JSX.Element => {
|
||||
const { setEditingNodeId } = useActions(notebookLogic)
|
||||
const { Settings, nodeAttributes, title } = useValues(logic)
|
||||
@ -41,16 +76,21 @@ export const NotebookNodeSettingsWidget = ({ logic }: { logic: BuiltLogic<notebo
|
||||
className="NotebookColumn__widget"
|
||||
actions={
|
||||
<>
|
||||
<LemonButton icon={<IconEyeVisible />} size="small" status="primary" onClick={() => selectNode()} />
|
||||
<LemonButton size="small" status="primary" onClick={() => setEditingNodeId(null)}>
|
||||
Done
|
||||
</LemonButton>
|
||||
</>
|
||||
}
|
||||
>
|
||||
{Settings ? (
|
||||
<Settings key={nodeAttributes.nodeId} attributes={nodeAttributes} updateAttributes={updateAttributes} />
|
||||
) : null}
|
||||
<div onClick={() => selectNode()}>
|
||||
{Settings ? (
|
||||
<Settings
|
||||
key={nodeAttributes.nodeId}
|
||||
attributes={nodeAttributes}
|
||||
updateAttributes={updateAttributes}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</LemonWidget>
|
||||
)
|
||||
}
|
||||
|
@ -151,7 +151,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-draggable": "^4.2.0",
|
||||
"react-grid-layout": "^1.3.0",
|
||||
"react-intersection-observer": "^9.4.3",
|
||||
"react-intersection-observer": "^9.5.3",
|
||||
"react-markdown": "^5.0.3",
|
||||
"react-modal": "^3.15.1",
|
||||
"react-resizable": "^3.0.5",
|
||||
|
@ -261,8 +261,8 @@ dependencies:
|
||||
specifier: ^1.3.0
|
||||
version: 1.3.4(react-dom@18.2.0)(react@18.2.0)
|
||||
react-intersection-observer:
|
||||
specifier: ^9.4.3
|
||||
version: 9.4.3(react@18.2.0)
|
||||
specifier: ^9.5.3
|
||||
version: 9.5.3(react@18.2.0)
|
||||
react-markdown:
|
||||
specifier: ^5.0.3
|
||||
version: 5.0.3(@types/react@17.0.52)(react@18.2.0)
|
||||
@ -16838,8 +16838,8 @@ packages:
|
||||
react: 18.2.0
|
||||
dev: true
|
||||
|
||||
/react-intersection-observer@9.4.3(react@18.2.0):
|
||||
resolution: {integrity: sha512-WNRqMQvKpupr6MzecAQI0Pj0+JQong307knLP4g/nBex7kYfIaZsPpXaIhKHR+oV8z+goUbH9e10j6lGRnTzlQ==}
|
||||
/react-intersection-observer@9.5.3(react@18.2.0):
|
||||
resolution: {integrity: sha512-NJzagSdUPS5rPhaLsHXYeJbsvdpbJwL6yCHtMk91hc0ufQ2BnXis+0QQ9NBh6n9n+Q3OyjR6OQLShYbaNBkThQ==}
|
||||
peerDependencies:
|
||||
react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
|
Loading…
Reference in New Issue
Block a user