Skip to content

Commit

Permalink
#757 Make sidebar items drag & droppable
Browse files Browse the repository at this point in the history
  • Loading branch information
Polleps committed Mar 7, 2024
1 parent 5e3004c commit 954ec25
Show file tree
Hide file tree
Showing 24 changed files with 850 additions and 228 deletions.
1 change: 1 addition & 0 deletions browser/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ This changelog covers all three packages, as they are (for now) updated as a who
- [#841](https://github.com/atomicdata-dev/atomic-server/issues/841) Add better inputs for `Timestamp` and `Date` datatypes.
- [#842](https://github.com/atomicdata-dev/atomic-server/issues/842) Add media picker for properties with classtype file.
- [#850](https://github.com/atomicdata-dev/atomic-server/issues/850) Add drag & drop sorting to ResourceArray inputs.
- [#757](https://github.com/atomicdata-dev/atomic-server/issues/757) Add drag & drop sorting to sidebar.

## v0.37.0

Expand Down
6 changes: 3 additions & 3 deletions browser/data-browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
"@bugsnag/js": "^7.16.5",
"@bugsnag/plugin-react": "^7.16.5",
"@dagrejs/dagre": "^1.0.2",
"@dnd-kit/core": "^6.0.5",
"@dnd-kit/sortable": "^7.0.1",
"@dnd-kit/utilities": "^3.2.0",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@emoji-mart/react": "^1.1.1",
"@emotion/is-prop-valid": "^1.2.1",
"@radix-ui/react-popover": "^1.0.6",
Expand Down
129 changes: 64 additions & 65 deletions browser/data-browser/src/components/AtomicLink.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ReactNode } from 'react';
import { ReactNode, forwardRef } from 'react';
import { styled } from 'styled-components';
import { constructOpenURL, pathToURL } from '../helpers/navigation';
import { FaExternalLinkAlt } from 'react-icons/fa';
Expand Down Expand Up @@ -26,80 +26,79 @@ export interface AtomicLinkProps
* Renders a link. Either a subject or a href is required. You can wrap this
* around other components and pass the `clean` prop to skip styling.
*/
export const AtomicLink = ({
children,
clean,
subject,
path,
href,
untabbable,
className,
...props
}: AtomicLinkProps): JSX.Element => {
const navigate = useNavigateWithTransition();

if (!subject && !href && !path) {
return (
<ErrorLook>
No `subject`, `path` or `href` passed to this AtomicLink.
</ErrorLook>
);
}
export const AtomicLink = forwardRef<HTMLAnchorElement, AtomicLinkProps>(
(
{ children, clean, subject, path, href, untabbable, className, ...props },
ref,
): JSX.Element => {
const navigate = useNavigateWithTransition();

if (!subject && !href && !path) {
return (
<ErrorLook>
No `subject`, `path` or `href` passed to this AtomicLink.
</ErrorLook>
);
}

let isOnCurrentPage: boolean;
let isOnCurrentPage: boolean;

try {
isOnCurrentPage = subject
? window.location.toString() === constructOpenURL(subject)
: false;
} catch (e) {
return <span>{subject}</span>;
}

const handleClick = (e: React.MouseEvent<HTMLElement>) => {
if (href) {
// When there is a regular URL, let the browser handle it
return;
try {
isOnCurrentPage = subject
? window.location.toString() === constructOpenURL(subject)
: false;
} catch (e) {
return <span>{subject}</span>;
}

e.preventDefault();
const handleClick = (e: React.MouseEvent<HTMLElement>) => {
if (href) {
// When there is a regular URL, let the browser handle it
return;
}

if (path) {
navigate(path);
e.preventDefault();

return;
}
if (path) {
navigate(path);

if (subject) {
if (isOnCurrentPage) {
return;
}

navigate(constructOpenURL(subject));
}
};

const hrefConstructed = href || subject || pathToURL(path!);

return (
<LinkView
clean={clean}
className={className}
about={subject}
onClick={handleClick}
href={hrefConstructed}
disabled={isOnCurrentPage}
tabIndex={isOnCurrentPage || untabbable ? -1 : 0}
// Tauri always opens `_blank` in new tab, and ignores preventDefault() for some reason.
// https://github.com/tauri-apps/tauri/issues/1657
target={isRunningInTauri() && !href ? '' : '_blank'}
{...props}
>
{children}
{href && !clean && <FaExternalLinkAlt />}
</LinkView>
);
};
if (subject) {
if (isOnCurrentPage) {
return;
}

navigate(constructOpenURL(subject));
}
};

const hrefConstructed = href || subject || pathToURL(path!);

return (
<LinkView
clean={clean}
className={className}
about={subject}
onClick={handleClick}
href={hrefConstructed}
disabled={isOnCurrentPage}
tabIndex={isOnCurrentPage || untabbable ? -1 : 0}
// Tauri always opens `_blank` in new tab, and ignores preventDefault() for some reason.
// https://github.com/tauri-apps/tauri/issues/1657
target={isRunningInTauri() && !href ? '' : '_blank'}
{...props}
ref={ref}
>
{children}
{href && !clean && <FaExternalLinkAlt />}
</LinkView>
);
},
);

AtomicLink.displayName = 'AtomicLink';

type LinkViewProps = {
disabled?: boolean;
Expand Down
9 changes: 5 additions & 4 deletions browser/data-browser/src/components/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { styled } from 'styled-components';
import { transitionName } from '../helpers/transitionName';
import { getTransitionStyle } from '../helpers/transitionName';

type CardProps = {
/** Adds a colorful border */
Expand All @@ -9,7 +9,10 @@ type CardProps = {
};

/** A Card with a border. */
export const Card = styled.div<CardProps>`
export const Card = styled.div.attrs<CardProps>(p => ({
// When we render a lot of cards it is more performant to use styles instead of classes when each card has a unique style
style: getTransitionStyle('resource-page', p.about),
}))`
background-color: ${props => props.theme.colors.bg};
border: solid 1px
Expand All @@ -24,8 +27,6 @@ export const Card = styled.div<CardProps>`
border-radius: ${props => props.theme.radius};
max-height: ${props => (props.small ? '10rem' : 'none')};
overflow: ${props => (props.small ? 'hidden' : 'visible')};
${p => transitionName('resource-page', p.about)};
`;

export interface CardRowProps {
Expand Down
2 changes: 1 addition & 1 deletion browser/data-browser/src/components/SideBar/AppMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function AppMenu({ onItemClick }: AppMenuProps): JSX.Element {
<SideBarMenuItem
icon={<FaCog />}
label='Settings'
helper='Edit the theme (t)'
helper='Change client settings (t)'
path={paths.themeSettings}
onClick={onItemClick}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useDndMonitor, useDroppable } from '@dnd-kit/core';
import { styled } from 'styled-components';
import { useState } from 'react';
import { transition } from '../../../helpers/transition';
import { SideBarDropData } from '../useSidebarDnd';

interface DropEdgeProps {
parentHierarchy: string[];
position: number;
}

export function DropEdge({
parentHierarchy,
position,
}: DropEdgeProps): React.JSX.Element {
if (parentHierarchy.length === 0) {
throw new Error('renderedHierargy should not be empty');
}

const [activeDraggedSubject, setDraggingSubject] = useState<string>();

const parent = parentHierarchy.at(-1)!;

useDndMonitor({
onDragStart: event => setDraggingSubject(event.active.id as string),
onDragEnd: () => setDraggingSubject(undefined),
});

const data: SideBarDropData = {
parent,
position,
};

const { setNodeRef, isOver } = useDroppable({
id: `${parent}-${position}`,
data,
});

const shouldRender =
!!activeDraggedSubject && !parentHierarchy.includes(activeDraggedSubject);

return (
<DropEdgeElement ref={setNodeRef} active={isOver} visible={shouldRender} />
);
}

const DropEdgeElement = styled.div<{ visible: boolean; active: boolean }>`
display: ${p => (p.visible ? 'block' : 'none')};
position: absolute;
left: 0;
height: 3px;
border-radius: 1.5px;
transform: scaleX(${p => (p.active ? 1 : 0.9)});
background: ${p => p.theme.colors.main};
opacity: ${p => (p.active ? 1 : 0)};
z-index: 2;
width: calc(var(--width) - 2rem);
${transition('opacity', 'transform')}
`;
Loading

0 comments on commit 954ec25

Please sign in to comment.