Skip to content

Commit 695e595

Browse files
authored
Merge pull request #423 from unigraph-dev/alex/editor
Improve editor UX
2 parents 1912845 + 4dbea60 commit 695e595

File tree

4 files changed

+151
-88
lines changed

4 files changed

+151
-88
lines changed

packages/default-packages/unigraph.notes/executables/dailyNotes.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ React.useEffect(() => {
4444
}, []);
4545

4646
return note && note?.type?.['unigraph.id'] === '$/schema/journal' ? (
47-
<AutoDynamicViewDetailed object={note} />
47+
<div style={{ margin: '0px -16px', display: 'flex' }}>
48+
<AutoDynamicViewDetailed object={note} />
49+
</div>
4850
) : note ? (
4951
''
5052
) : (

packages/unigraph-dev-explorer/src/components/ObjectView/DragandDrop.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export function DragandDrop({
7878
<Comp>
7979
{children.map((child: any, index: number) => (
8080
<ChildrenComp key={`${child.key || index}`}>
81-
<div key={`${child.key || index}_dropper`}>
81+
<div key={`${child.key || index}_dropper`} style={{ position: 'relative' }}>
8282
{child}
8383
<BelowDropAcceptor
8484
onDrop={(props: any) => {

packages/unigraph-dev-explorer/src/examples/notes/NoteBlock.tsx

Lines changed: 146 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable react/require-default-props */
22
/* eslint-disable @typescript-eslint/no-empty-function */
33
/* eslint-disable no-param-reassign */
4-
import { Typography, colors } from '@mui/material';
4+
import { Typography, colors, IconButton } from '@mui/material';
55
import { styled } from '@mui/styles';
66
import React from 'react';
77
import _ from 'lodash';
@@ -74,6 +74,14 @@ const BlockChild = ({ elindex, shortcuts, displayAs, isCollapsed, setCollapsed,
7474
/>
7575
);
7676

77+
const dropIndicatorStyles = {
78+
position: 'absolute',
79+
height: '6px',
80+
marginTop: '-3px',
81+
marginBottom: '1px',
82+
zIndex: 999,
83+
};
84+
7785
const BlockChildren = ({
7886
isChildren,
7987
subentities,
@@ -98,13 +106,7 @@ const BlockChildren = ({
98106
dndContext={tabContext.viewId}
99107
listId={data?.uid}
100108
arrayId={data?._value?.children?.uid}
101-
style={{
102-
position: 'absolute',
103-
height: '6px',
104-
marginTop: '-3px',
105-
marginBottom: '1px',
106-
zIndex: 999,
107-
}}
109+
style={dropIndicatorStyles}
108110
transformOnDrop={(props: any) => {
109111
return {
110112
...props,
@@ -175,51 +177,40 @@ const Outline = styled('div')({
175177
display: 'flex',
176178
alignItems: 'baseline',
177179
position: 'relative',
180+
transition: 'transform 0.1s ease-in',
178181
});
179182

180-
const ControlsContainer = styled('div')({
183+
const controlStyles = {
181184
flex: '0 0 1rem',
182-
display: 'flex',
183185
width: '1rem',
184186
height: '1rem',
187+
display: 'flex',
185188
justifyContent: 'center',
186189
alignItems: 'center',
187-
transform: 'translateY(-0.2rem)', // fine tune vertical alignment
188-
position: 'relative',
189-
});
190+
};
190191

191192
const Bullet = styled('div')({
192-
flex: '0 0 1rem',
193-
display: 'flex',
194-
width: '1rem',
195-
height: '1rem',
196-
borderRadius: '50%',
197-
justifyContent: 'center',
198-
alignItems: 'center',
199-
transition: 'background 0.1s ease-in',
200-
'& > svg': {
201-
fill: colors.common.black,
193+
...controlStyles,
194+
position: 'relative',
195+
top: '0.125rem',
196+
'& > svg > circle': {
197+
transition: 'fill 0.1s ease-in',
202198
},
203199
});
204200

205201
const Toggle = styled('button')({
206-
position: 'absolute',
207-
top: 0,
208-
left: '-1rem',
209-
width: '1rem',
210-
height: '1rem',
211-
display: 'flex',
212-
justifyContent: 'center',
213-
alignItems: 'center',
202+
...controlStyles,
203+
position: 'relative',
204+
top: '0.125rem',
214205
cursor: 'pointer',
215206
background: 'none',
216207
border: 'none',
217208
borderRadius: '50%',
218209
outline: 'none',
219210
margin: 0,
220211
padding: '0.1rem',
221-
fontSize: '1.25rem',
222-
transition: 'transform 0.1s ease-in',
212+
fontSize: '1rem',
213+
transition: 'transform 0.1s ease-in, background 0.1s ease-in',
223214
'&:hover': {
224215
backgroundColor: colors.grey[200],
225216
},
@@ -231,7 +222,7 @@ const ChildrenLeftBorder = styled('div')({
231222
width: '1px',
232223
backgroundColor: colors.grey[300],
233224
position: 'absolute',
234-
left: 'calc(-0.8rem - 0.5px)',
225+
left: 'calc(0.2rem - 0.5px)',
235226
});
236227

237228
const ChildrenContainer = styled('div')({
@@ -276,29 +267,31 @@ export function OutlineComponent({
276267
const toggleChildren = React.useCallback(() => setCollapsed && setCollapsed(!collapsed), [collapsed, setCollapsed]);
277268

278269
return (
279-
<Outline onPointerMove={onPointerMove} onPointerLeave={onPointerLeave}>
280-
<ControlsContainer>
281-
<Toggle
282-
style={{
283-
visibility: showCollapse && hover ? 'visible' : 'hidden',
284-
transform: `rotate(${collapsed ? '0deg' : '90deg'})`,
285-
}}
286-
onClick={toggleChildren}
287-
>
288-
<ChevronRight fontSize="inherit" />
289-
</Toggle>
290-
{displayAs === 'outliner' && (
291-
<Bullet
292-
style={{
293-
backgroundColor: collapsed ? colors.grey[200] : 'transparent',
294-
}}
295-
>
296-
<svg width="0.375rem" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
297-
<circle cx="8" cy="8" r="8" />
298-
</svg>
299-
</Bullet>
300-
)}
301-
</ControlsContainer>
270+
<Outline
271+
onPointerMove={onPointerMove}
272+
onPointerLeave={onPointerLeave}
273+
style={{
274+
transform: displayAs === 'outliner' ? 'translateX(-1rem)' : 'translateX(-1.3rem)',
275+
width: displayAs === 'outliner' ? 'calc(100% + 1rem)' : 'calc(100% + 1.3rem)',
276+
}}
277+
>
278+
<Toggle
279+
style={{
280+
visibility: showCollapse && hover ? 'visible' : 'hidden',
281+
transform: `rotate(${collapsed ? '0deg' : '90deg'})`,
282+
}}
283+
onClick={toggleChildren}
284+
>
285+
<ChevronRight fontSize="inherit" />
286+
</Toggle>
287+
{displayAs === 'outliner' && (
288+
<Bullet>
289+
<svg width="100%" height="100%" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
290+
<circle cx="12" cy="12" r="10" style={{ fill: collapsed ? colors.grey[200] : 'transparent' }} />
291+
<circle cx="12" cy="12" r="4" style={{ fill: 'black' }} />
292+
</svg>
293+
</Bullet>
294+
)}
302295
{displayAs === 'outliner' && (
303296
<ChildrenLeftBorder
304297
style={{
@@ -381,24 +374,85 @@ export function ParentsAndReferences({ data }: any) {
381374
}
382375

383376
function NoteViewPageWrapper({ children, isRoot }: any) {
384-
return !isRoot ? children : <div style={{ height: '100%', width: '100%', padding: '16px' }}>{children}</div>;
377+
return !isRoot ? (
378+
children
379+
) : (
380+
<div style={{ height: '100%', width: '100%', padding: '1rem 2rem', overflow: 'auto' }}>{children}</div>
381+
);
385382
}
386383

387-
function NoteViewTextWrapper({ children, semanticChildren, isRoot, onContextMenu, callbacks, isEditing }: any) {
388-
// console.log(callbacks.BacklinkComponent);
384+
/** Renders both page title and content of each note block. */
385+
function NoteViewTextWrapper({
386+
children,
387+
semanticChildren,
388+
isRoot,
389+
isEditing,
390+
onContextMenu,
391+
callbacks,
392+
}: {
393+
children: React.ReactNode;
394+
semanticChildren: React.ReactNode;
395+
isRoot: boolean;
396+
isEditing: boolean;
397+
onContextMenu: React.MouseEventHandler;
398+
callbacks: any;
399+
}) {
389400
return (
390401
<div
391402
style={{ display: 'flex', alignItems: 'center' }}
392403
onContextMenu={isRoot || isEditing ? undefined : onContextMenu}
393404
>
394405
{children}
395406
{semanticChildren}
396-
{isRoot ? <MoreVert onClick={onContextMenu} style={{ marginLeft: '8px' }} /> : []}
397-
{callbacks.BacklinkComponent ? callbacks.BacklinkComponent : []}
407+
{isRoot && (
408+
<IconButton onClick={onContextMenu} style={{ marginLeft: '8px' }} aria-label="more" size="small">
409+
<MoreVert fontSize="small" />
410+
</IconButton>
411+
)}
412+
{callbacks.BacklinkComponent}
398413
</div>
399414
);
400415
}
401416

417+
const DateContainer = styled('div')({
418+
marginTop: '4px',
419+
marginBottom: '12px',
420+
display: 'flex',
421+
alignItems: 'center',
422+
color: colors.grey[500],
423+
fontSize: '0.9rem',
424+
});
425+
426+
const ChildrenIndicator = React.memo(function ChildrenIndicator({
427+
count,
428+
onClick,
429+
}: {
430+
count: number;
431+
onClick?: React.MouseEventHandler;
432+
}) {
433+
return (
434+
<Typography
435+
style={{
436+
position: 'absolute',
437+
top: 0,
438+
left: '100%',
439+
height: '100%',
440+
display: 'flex',
441+
alignItems: 'center',
442+
padding: '0.125rem',
443+
color: colors.grey[500],
444+
wordBreak: 'keep-all',
445+
fontSize: '0.8rem',
446+
cursor: 'pointer',
447+
}}
448+
title={`Show ${count} children`}
449+
onClick={onClick}
450+
>
451+
({count})
452+
</Typography>
453+
);
454+
});
455+
402456
export function DetailedOutlinerBlock({
403457
data,
404458
isChildren,
@@ -580,6 +634,22 @@ export function DetailedOutlinerBlock({
580634
[rOutlineContentEl],
581635
);
582636

637+
const onContextMenu = React.useCallback(
638+
(event: React.MouseEvent) => {
639+
onUnigraphContextMenu(event, data, undefined, { ...callbacks, componentId });
640+
},
641+
[callbacks, componentId, data],
642+
);
643+
644+
const onClickChildrenIndicator = React.useCallback(
645+
(ev: React.MouseEvent) => {
646+
ev.preventDefault();
647+
ev.stopPropagation();
648+
setCollapsed(false);
649+
},
650+
[setCollapsed],
651+
);
652+
583653
return (
584654
<NoteViewPageWrapper isRoot={!isChildren}>
585655
<div
@@ -591,9 +661,7 @@ export function DetailedOutlinerBlock({
591661
<NoteViewTextWrapper
592662
isRoot={!isChildren}
593663
isEditing={isEditing}
594-
onContextMenu={(event: any) =>
595-
onUnigraphContextMenu(event, data, undefined, { ...callbacks, componentId })
596-
}
664+
onContextMenu={onContextMenu}
597665
callbacks={callbacks}
598666
semanticChildren={otherChildren
599667
.filter((el: any) => el?.type)
@@ -658,30 +726,22 @@ export function DetailedOutlinerBlock({
658726
editorContext,
659727
)}
660728
{NoteEditorInner}
661-
<Typography
662-
style={{
663-
display: isEditing || !isCollapsed ? 'none' : '',
664-
marginLeft: '6px',
665-
color: 'gray',
666-
cursor: 'pointer',
667-
}}
668-
onClick={(ev) => {
669-
ev.preventDefault();
670-
ev.stopPropagation();
671-
setCollapsed(false);
672-
}}
673-
>{`(${subentities.length})`}</Typography>
729+
{!isEditing && isCollapsed && (
730+
<ChildrenIndicator count={subentities.length} onClick={onClickChildrenIndicator} />
731+
)}
674732
</div>
675733
</NoteViewTextWrapper>
676-
{!isChildren && !callbacks.isEmbed ? (
677-
<div style={{ marginTop: '4px', marginBottom: '12px', display: 'flex', color: 'gray' }}>
678-
<Icon path={mdiClockOutline} size={0.8} style={{ marginRight: '4px' }} />
679-
{`${new Date(data._updatedAt || 0).toLocaleString()} (${Sugar.Date.relative(
734+
{!isChildren && !callbacks.isEmbed && (
735+
<DateContainer>
736+
<Icon
737+
path={mdiClockOutline}
738+
size={0.75}
739+
style={{ marginRight: '0.25rem', transform: 'translateY(-0.08rem)' }}
740+
/>
741+
<span>{`${Sugar.Date(data._updatedAt || 0).long()} (${Sugar.Date.relative(
680742
new Date(data._updatedAt || 0),
681-
)})`}
682-
</div>
683-
) : (
684-
[]
743+
)})`}</span>
744+
</DateContainer>
685745
)}
686746
{!isCollapsed && (
687747
<BlockChildren

packages/unigraph-dev-explorer/src/examples/semantic/Markdown.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ export const Markdown: DynamicViewRenderer = ({ data, callbacks, isHeading }) =>
175175
children,
176176
style: {
177177
display: '',
178+
maxWidth: '100%',
178179
// eslint-disable-next-line no-nested-ternary
179180
height: node?.properties?.alt?.startsWith?.('inline:') ? '1.5em' : '',
180181
},

0 commit comments

Comments
 (0)