Skip to content

Commit

Permalink
wip on contents
Browse files Browse the repository at this point in the history
  • Loading branch information
pnicolli committed Mar 5, 2024
1 parent 905c0fe commit 2929479
Show file tree
Hide file tree
Showing 49 changed files with 262 additions and 246 deletions.
9 changes: 9 additions & 0 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React, { forwardRef, ForwardedRef } from 'react';
import { Button as RACButton, ButtonProps } from 'react-aria-components';

export const Button = forwardRef(function _Button(
props: ButtonProps,
ref: ForwardedRef<HTMLButtonElement>,
) {
return <RACButton ref={ref} {...props} />;
});
5 changes: 3 additions & 2 deletions src/components/Contents/AddContentPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@ import React from 'react';
import { Link, ChevronrightIcon, Popover } from '@plone/components';

interface Props {
path: string;
addableTypes: {
'@id': string;
id: string;
title: string;
}[];
}

export const AddContentPopover = ({ addableTypes }: Props) => {
export const AddContentPopover = ({ path, addableTypes }: Props) => {
// const page = addableTypes.find((type) => type.id === 'Document');

return (
<Popover className="react-aria-Popover add-content-popover">
<ul className="add-content-list">
{addableTypes.map((type) => (
<li key={type.id} className="add-content-list-item">
<Link href={type['@id']}>
<Link href={`${path}/add?type=${encodeURIComponent(type.id)}`}>
{type.title}
<ChevronrightIcon />
</Link>
Expand Down
88 changes: 54 additions & 34 deletions src/components/Contents/Contents.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
import React from 'react';
import React, { ComponentProps, useState } from 'react';
import './styles/basic/main.css';
import './styles/quanta/main.css';
import type { ActionsResponse } from '@plone/types';
import { ComponentProps, ReactNode, useState } from 'react';
import {
DialogTrigger,
Tooltip,
TooltipTrigger,
useDragAndDrop,
} from 'react-aria-components';
import { useDragAndDrop } from 'react-aria-components';
import cx from 'classnames';
import {
AddIcon,
// AddIcon,
Breadcrumbs,
Button,
Container,
QuantaTextField,
} from '@plone/components';
import { Table } from '../Table/Table';
import { ContentsCell } from './ContentsCell';
import { AddContentPopover } from './AddContentPopover';
// import { AddContentPopover } from './AddContentPopover';
import { indexes, defaultIndexes } from '../../helpers/indexes';
import type { ArrayElement, Brain } from '../../helpers/types';

interface ContentsProps {
pathname: string;
breadcrumbs: ComponentProps<typeof Breadcrumbs>['items'];
objectActions: ActionsResponse['object'];
loading: boolean;
title: string;
items: Brain[];
orderContent: (baseUrl: string, id: string, delta: number) => Promise<void>;
addableTypes: ComponentProps<typeof AddContentPopover>['addableTypes'];
cut: (value?: string) => Promise<void>;
copy: (value?: string) => Promise<void>;
deleteItem: (value?: string) => Promise<void>;
orderItem: (id: string, delta: number) => Promise<void>;
moveToTop: (index: number) => Promise<void>;
moveToBottom: (index: number) => Promise<void>;
// addableTypes: ComponentProps<typeof AddContentPopover>['addableTypes'];
}

/**
Expand All @@ -41,16 +39,18 @@ interface ContentsProps {
*/
export function Contents({
pathname,
breadcrumbs = [],
objectActions,
loading,
title,
items,
orderContent,
addableTypes,
cut,
copy,
deleteItem,
orderItem,
moveToTop,
moveToBottom, // addableTypes,
}: ContentsProps) {
const [selected, setSelected] = useState<string[]>([]);
// const path = getBaseUrl(pathname);
const path = pathname;

const folderContentsActions = objectActions.find(
(action) => action.id === 'folderContents',
Expand Down Expand Up @@ -79,12 +79,21 @@ export function Contents({
},
] as const;

const rows = items.map((item) =>
const rows = items.map((item, itemIndex) =>
columns.reduce<ArrayElement<ComponentProps<typeof Table>['rows']>>(
(cells, column) => ({
...cells,
[column.id]: (
<ContentsCell key={column.id} item={item} column={column.id} />
<ContentsCell
key={column.id}
item={item}
column={column.id}
onMoveToBottom={() => moveToBottom(itemIndex)}
onMoveToTop={() => moveToTop(itemIndex)}
onCut={() => cut(item['@id'])}
onCopy={() => copy(item['@id'])}
onDelete={() => deleteItem(item['@id'])}
/>
),
}),
{ id: item['@id'] },
Expand All @@ -98,22 +107,28 @@ export function Contents({
})),
onReorder(e) {
if (e.keys.size !== 1) {
// TODO mostrare toast o rendere non ordinabile quando più di un elemento è selezionato
console.error('Only one item can be moved at a time');
return;
}
const item = items.find((item) => item['@id'] === [...e.keys][0]);
const target = [...e.keys][0];
if (target === e.target.key) return;

const item = items.find((item) => item['@id'] === target);
if (!item) return;

const initialPosition = rows.findIndex((row) => row.id === item['@id']);
if (initialPosition === -1) return;

let finalPosition = rows.findIndex((row) => row.id === e.target.key);
if (e.target.dropPosition === 'after') finalPosition += 1;
const finalPosition = rows.findIndex((row) => row.id === e.target.key);

orderContent(
path,
item.id.replace(/^.*\//, ''),
finalPosition - initialPosition,
);
let delta = finalPosition - initialPosition;
if (delta > 0 && e.target.dropPosition === 'before') delta -= 1;
if (delta < 0 && e.target.dropPosition === 'after') delta += 1;

if (delta !== 0) {
orderItem(item.id, delta);
}
},
});

Expand All @@ -132,28 +147,33 @@ export function Contents({
<article id="content">
<section className="topbar">
<div className="title-block">
<Breadcrumbs includeRoot={true} items={[]} />
<h1>{title}</h1>
<Breadcrumbs
includeRoot={true}
root="/contents"
items={[...breadcrumbs].slice(0, -1)}
/>
<h1>{[...breadcrumbs].slice(-1)[0]?.title}</h1>
</div>
<QuantaTextField
name="sortable_title"
placeholder="Search site"
className="search-input"
/>
<TooltipTrigger>
{/* <TooltipTrigger>
<DialogTrigger>
<Button className="react-aria-Button add">
<AddIcon />
</Button>
<AddContentPopover addableTypes={addableTypes} />
<AddContentPopover path={path} addableTypes={addableTypes} />
</DialogTrigger>
<Tooltip className="react-aria-Tooltip tooltip" placement="bottom">
Add content
</Tooltip>
</TooltipTrigger>
</TooltipTrigger> */}
</section>
<section className="contents-table">
<Table
aria-label="Contents"
columns={[...columns]}
rows={rows}
selectionMode="multiple"
Expand Down
80 changes: 64 additions & 16 deletions src/components/Contents/ContentsCell.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
import React from 'react';
import React, { ComponentProps, useRef, useState } from 'react';
import { DialogTrigger } from 'react-aria-components';
import { Brain } from '../../helpers/types';
import { Button, Link, MoreoptionsIcon, PageIcon } from '@plone/components';
import { Link, MoreoptionsIcon, PageIcon } from '@plone/components';
import { indexes } from '../../helpers/indexes';
import { Button } from '../Button';
import { ItemActionsPopover } from './ItemActionsPopover';

interface Props {
item: Brain;
column: keyof typeof indexes | 'title' | '_actions';
onMoveToTop: ComponentProps<typeof ItemActionsPopover>['onMoveToTop'];
onMoveToBottom: ComponentProps<typeof ItemActionsPopover>['onMoveToBottom'];
onCut: ComponentProps<typeof ItemActionsPopover>['onCut'];
onCopy: ComponentProps<typeof ItemActionsPopover>['onCopy'];
onDelete: ComponentProps<typeof ItemActionsPopover>['onDelete'];
}

export function ContentsCell({ item, column }: Props) {
export function ContentsCell({
item,
column,
onMoveToTop,
onMoveToBottom,
onCut,
onCopy,
onDelete,
}: Props) {
const [isMoreOptionsOpen, setIsMoreOptionsOpen] = useState(false);
const triggerRef = useRef<HTMLButtonElement>(null);

if (column === 'title') {
return (
<Link
Expand All @@ -31,27 +48,55 @@ export function ContentsCell({ item, column }: Props) {
);
} else if (column === '_actions') {
return (
<DialogTrigger>
<Button aria-label="More options">
<>
<Button
className="react-aria-Button item-actions-trigger"
aria-label="More options"
onPress={() => setIsMoreOptionsOpen(true)}
ref={triggerRef}
>
<MoreoptionsIcon />
</Button>
<ItemActionsPopover
triggerRef={triggerRef}
isOpen={isMoreOptionsOpen}
onOpenChange={setIsMoreOptionsOpen}
editLink={`${item['@id']}/edit`}
viewLink={item['@id']}
onMoveToBottom={async () => {}}
onMoveToTop={async () => {}}
onCopy={async () => {}}
onCut={async () => {}}
onDelete={async () => {}}
onMoveToBottom={async () => {
const res = await onMoveToBottom();
setIsMoreOptionsOpen(false);
return res;
}}
onMoveToTop={async () => {
const res = await onMoveToTop();
setIsMoreOptionsOpen(false);
return res;
}}
onCopy={async () => {
const res = await onCopy();
setIsMoreOptionsOpen(false);
return res;
}}
onCut={async () => {
const res = await onCut();
setIsMoreOptionsOpen(false);
return res;
}}
onDelete={async () => {
const res = await onDelete();
setIsMoreOptionsOpen(false);
return res;
}}
/>
</DialogTrigger>
</>
);
} else {
if (indexes[column].type === 'boolean') {
return item[column] ? 'Yes' : 'No';
return <>{item[column] ? 'Yes' : 'No'}</>;
} else if (indexes[column].type === 'string') {
if (column !== 'review_state') {
return item[column];
return <>{item[column]}</>;
} else {
return (
<div>
Expand All @@ -65,13 +110,16 @@ export function ContentsCell({ item, column }: Props) {
} else if (indexes[column].type === 'date') {
if (item[column] && item[column] !== 'None') {
// @ts-ignore TODO fix this, maybe a more strict type for the indexes?
return new Date(item[column]).toLocaleDateString();
return <>{new Date(item[column]).toLocaleDateString()}</>;
} else {
return 'None';
return <>None</>;
}
} else if (indexes[column].type === 'array') {
const value = item[column];
return Array.isArray(value) ? value.join(', ') : value;
return <>{Array.isArray(value) ? value.join(', ') : value}</>;
} else {
// TODO do we get here? needed for type checking?
return null;
}
}
}
9 changes: 7 additions & 2 deletions src/components/Contents/ItemActionsPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Link,
Button,
Popover,
type PopoverProps,
EditIcon,
EyeIcon,
RowbeforeIcon,
Expand All @@ -12,7 +13,7 @@ import {
BinIcon,
} from '@plone/components';

interface Props {
interface Props extends Omit<PopoverProps, 'children'> {
editLink: string;
viewLink: string;
onMoveToTop: () => Promise<void>;
Expand All @@ -30,9 +31,13 @@ export function ItemActionsPopover({
onCut,
onCopy,
onDelete,
...popoverProps
}: Props) {
return (
<Popover className="react-aria-Popover item-actions-popover">
<Popover
{...popoverProps}
className="react-aria-Popover item-actions-popover"
>
<ul className="item-actions-list">
<li className="item-actions-list-item edit">
<Link href={editLink}>
Expand Down
6 changes: 0 additions & 6 deletions src/components/Contents/styles/basic/BlockToolbar.css
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
@import './Checkbox.css';
@import './Button.css';
@import './ToggleButton.css';
@import './Menu.css';
@import './theme.css';

.blocks-toolbar {
display: flex;
flex-wrap: wrap;
Expand Down
2 changes: 0 additions & 2 deletions src/components/Contents/styles/basic/Breadcrumbs.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@import './theme.css';

.react-aria-Breadcrumbs {
display: flex;
align-items: center;
Expand Down
2 changes: 0 additions & 2 deletions src/components/Contents/styles/basic/Button.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@import './theme.css';

@layer components {
.react-aria-Button {
padding: 8px 8px;
Expand Down
3 changes: 0 additions & 3 deletions src/components/Contents/styles/basic/Calendar.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
@import './Button.css';
@import './theme.css';

.react-aria-Calendar {
width: fit-content;
max-width: 100%;
Expand Down
2 changes: 0 additions & 2 deletions src/components/Contents/styles/basic/Checkbox.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@import './theme.css';

.react-aria-Checkbox {
--selected-color: var(--highlight-background);
--selected-color-pressed: var(--highlight-background-pressed);
Expand Down
Loading

0 comments on commit 2929479

Please sign in to comment.