0
0
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:
Ben White 2023-11-22 12:30:20 +01:00 committed by GitHub
parent e35c8e574d
commit 9033d0822f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 97 additions and 33 deletions

View File

@ -56,7 +56,7 @@
}
&--selected {
--border-color: var(--primary-3000);
--border-color: var(--border-bold);
}
&--auto-hide-metadata {

View File

@ -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,

View File

@ -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: () => {

View File

@ -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;

View File

@ -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>
)
}

View File

@ -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",

View File

@ -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: