Skip to content

Commit

Permalink
feat: implemented basic tree index
Browse files Browse the repository at this point in the history
  • Loading branch information
reinvanoyen committed Nov 12, 2024
1 parent eef92db commit 3debcd7
Show file tree
Hide file tree
Showing 22 changed files with 974 additions and 100 deletions.
518 changes: 433 additions & 85 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@
"vue-template-compiler": "^2.7.14"
},
"dependencies": {
"@minoru/react-dnd-treeview": "^3.4.4",
"axios": "^1.3.2",
"clsx": "^2.1.0",
"react": "^18.2.0",
"react-dnd": "^14.0.4",
"react-dnd-html5-backend": "^14.0.2",
"react-dom": "^18.2.0",
"react": "^18.3.1",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18.3.1",
"react-redux": "^8.0.5",
"react-trix": "^0.10.0",
"redux": "^4.2.1",
Expand Down
2 changes: 1 addition & 1 deletion public/js/app.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/js/manifest.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion public/js/vendor.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions public/mix-manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"/js/app.js": "/js/app.js?id=421f94c4e76ff90ad8b56d037c27e657",
"/js/manifest.js": "/js/manifest.js?id=22bb55ce20c9c883a45b5c11d8eca703",
"/js/vendor.js": "/js/vendor.js?id=a21c2dcb4fdd557ce1ec6c040ad40be4",
"/js/app.js": "/js/app.js?id=82c6422f61e56e2fcf4f223d37ae20ba",
"/js/manifest.js": "/js/manifest.js?id=35aa9d461e033ff05abe890341153838",
"/js/vendor.js": "/js/vendor.js?id=b53083a0bb34e922d0d09ba0e52241c6",
"/css/app.css": "/css/app.css?id=d75c395a1c5c29d8b4c39d09727b550c"
}
2 changes: 2 additions & 0 deletions resources/js/actions/all.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Index from "./index";
import TreeIndex from "./tree-index";
import Edit from "./edit";
import Create from "./create";
import Delete from "./delete";
Expand All @@ -14,6 +15,7 @@ import BulkDelete from "./bulk-delete";
export default {
'bulk-delete': BulkDelete,
'index': Index,
'tree-index': TreeIndex,
'edit': Edit,
'create': Create,
'create-wizard': CreateWizard,
Expand Down
110 changes: 110 additions & 0 deletions resources/js/actions/tree-index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React, { useEffect, useState } from 'react';
import { useSelector } from "react-redux";
import api from "../api/api";
import str from "../util/str";
import i18n from "../util/i18n";
import Tree from "../core/ui/tree";
import path from "../state/path";

function TreeIndex(props) {

const [state, setState] = useState({
isLoading: true,
rows: []
});

const location = useSelector(state => state.location);

useEffect(() => {
load(location.current.params);
}, [location.current.params]);

useEffect(() => {
if (location.refresh) {
load(location.current.params);
}
}, [location.refresh]);

const load = async (params = {}) => {

// Execute the get request
const response = await api.execute.get(props.path, props.id,'load', params);

setState({
...state,
isLoading: false,
rows: response.data.data
});
}

const handleParentChange = async (id, parentId, children = []) => {
await api.execute.get(props.path, props.id,'updateParent', {id, parentId, children});
};

const handleOrderChange = async (children = []) => {
await api.execute.get(props.path, props.id,'updateOrder', {children});
};

const handleRowClick = (row) => {
path.goTo(props.path.module, props.action, {
id: row.id
});
}

const renderHeader = () => {
return (
<div className="index__header">
<div className="index__header-title">
{str.toUpperCaseFirst(props.plural)}
</div>
</div>
);
}

const renderPlaceholder = () => {
return (
<div className="index__placeholder">
{i18n.get('snippets.no_plural_found', {plural: props.plural})}
</div>
);
}

const renderRows = () => {

if (state.rows.length) {
return <Tree
data={state.rows}
onParentChange={handleParentChange}
onOrderChange={handleOrderChange}
onClick={handleRowClick}
/>;
}

return renderPlaceholder();
}

const render = () => {
return (
<div className={'index index--'+props.style+(state.isLoading ? ' index--loading' : '')}>
{renderHeader()}
{renderRows()}
</div>
);
}

return render();
}

TreeIndex.defaultProps = {
type: '',
components: [],
path: {},
id: 0,
data: {},
params: {},
action: '',
plural: '',
singular: ''
};

export default TreeIndex;
1 change: 0 additions & 1 deletion resources/js/components/many-to-many-field.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ class ManyToManyField extends React.Component {
}

open() {
console.log(this.props.filters);
this.setState({
isOpen: true
});
Expand Down
10 changes: 10 additions & 0 deletions resources/js/core/ui/drag-insert-placeholder/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import './index.scss';
import React from "react";

function DragInsertPlaceholder() {
return (
<div className="drag-insert-placeholder"></div>
);
}

export default DragInsertPlaceholder;
9 changes: 9 additions & 0 deletions resources/js/core/ui/drag-insert-placeholder/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@import 'resources/sass/settings';
@import 'resources/sass/tools';

@include block(drag-insert-placeholder)
{
height: 2px;
width: 100%;
background-color: $primary-color;
}
10 changes: 10 additions & 0 deletions resources/js/core/ui/drag-placeholder/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import './index.scss';
import React from "react";

function DragPlaceholder({text}) {
return (
<span className="drag-placeholder">{text}</span>
);
}

export default DragPlaceholder;
10 changes: 10 additions & 0 deletions resources/js/core/ui/drag-placeholder/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@import 'resources/sass/settings';
@import 'resources/sass/tools';

@include block(drag-placeholder)
{
background-color: $primary-color;
border-radius: $border-radius-1;
color: $color01;
padding: calc($rule / 6) calc($rule / 3);
}
93 changes: 93 additions & 0 deletions resources/js/core/ui/tree/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import './index.scss';

import React, { useState } from "react";
import { DndProvider } from "react-dnd";
import {Tree as TreeView, MultiBackend, getBackendOptions} from "@minoru/react-dnd-treeview";
import Icon from "../icon";
import DragInsertPlaceholder from "../drag-insert-placeholder";
import clsx from "clsx";
import DragPlaceholder from "../drag-placeholder";

function TreeRow({onClick, isDroppable, isDropTarget, isOpen, onToggle, text, depth}) {
return (
<div onClick={onClick} className={clsx('tree__item tree-row', {
'tree-row--drop-target': isDropTarget
})}>
<div className="tree-row__content" style={{ paddingInlineStart: depth * 25 }}>
{isDroppable && (
<div className="tree-row__toggle">
<Icon name={(isOpen ? 'expand_more' : 'chevron_right')} onClick={onToggle} />
</div>
)}
{text}
</div>
</div>
);
}

function Tree({data, onParentChange = null, onOrderChange = null, onClick = null}) {

const [treeData, setTreeData] = useState(data);

const handleDrop = (newTree, { dragSourceId, dropTargetId, dragSource, dropTarget }) => {
const children = newTree.filter((item) => item.parent === dropTargetId).map(child => child.id);

if (dragSource.parent !== dropTargetId) {
if (onParentChange) {
onParentChange(dragSourceId, dropTargetId, children);
}
} else {
if (onOrderChange) {
onOrderChange(children);
}
}
setTreeData(newTree);
};

const handleClick = (item) => {
if (onClick) {
onClick(item);
}
};

return (
<div className="tree">
<DndProvider backend={MultiBackend} options={getBackendOptions()}>
<TreeView
tree={treeData}
rootId={null}
sort={false}
listComponent={'div'}
listItemComponent={'div'}
dropTargetOffset={5}
insertDroppableFirst={false}
render={(node, { isDropTarget, depth, isOpen, onToggle }) => (
<TreeRow
onClick={() => handleClick(node)}
isDropTarget={isDropTarget}
isDroppable={node.droppable}
onToggle={onToggle}
depth={depth}
isOpen={isOpen}
text={node.text}
/>
)}
dragPreviewRender={(monitorProps) => (
<DragPlaceholder text={monitorProps.item.text} />
)}
canDrop={(tree, { dragSource, dropTargetId, dropTarget }) => {
if (dragSource?.parent === dropTargetId) {
return true;
}
}}
placeholderRender={() => (
<DragInsertPlaceholder />
)}
onDrop={handleDrop}
/>
</DndProvider>
</div>
);
}

export default Tree;
37 changes: 37 additions & 0 deletions resources/js/core/ui/tree/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@import 'resources/sass/settings';
@import 'resources/sass/tools';

@include block(tree)
{
@include element(item)
{
// for ref
}
}

@include block(tree-row)
{
cursor: pointer;
padding: calc($rule / 3) $rule;
transition: background-color .25s;

@include modifier(drop-target)
{
background-color: $fill-color-alt-1;
}

&:hover
{
background-color: $fill-color;
}

@include element(content)
{
display: flex;
}

@include element(toggle)
{
margin-right: calc($rule / 2);
}
}
3 changes: 1 addition & 2 deletions src/Action/Index.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace ReinVanOyen\Cmf\Action;

use ReinVanOyen\Cmf\Components\Component;
use ReinVanOyen\Cmf\Sorters\StaticSorter;

class Index extends CollectionAction
{
Expand Down Expand Up @@ -36,7 +35,7 @@ public function __construct(string $meta, array $components = [])
$searcher = $this->getMeta()::searcher();

if ($searcher) {
$this->searcher($this->getMeta()::searcher());
$this->searcher($searcher);
}

foreach ($this->getMeta()::filters() as $filter) {
Expand Down
Loading

0 comments on commit 3debcd7

Please sign in to comment.