Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v2 restructure using TreeModel to hold state #424

Open
wants to merge 18 commits into
base: v2.0-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 160 additions & 68 deletions README.md

Large diffs are not rendered by default.

26 changes: 24 additions & 2 deletions examples/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ <h2>Custom Icons Example</h2>
<h2>Disabled Example</h2>
<div id="disabled-example"></div>

<h2>Native Checkboxes Example</h2>
<div id="native-checkboxes-example"></div>

<h2>No Cascading Example</h2>
<p>
By default, the check state of children determine the check state of a parent. Similarly, checking or unchecking
Expand All @@ -73,8 +76,8 @@ <h2>Clickable Labels Example</h2>
property, the tree will instead call the provided function and will restrict toggling to the checkbox icon.
</p>
<p>
When the <code>onClick</code> function is defined, passing the <code>expandOnClick</code> property will also
expand the clicked node automatically.
When the <code>onClick</code> function is defined, passing the <code>expandOnClick</code> property will
cause the clicked node to expand/contract when clicked.
</p>
<div id="clickable-labels-example"></div>

Expand All @@ -100,6 +103,25 @@ <h2>Filter Example</h2>
<p>Filtering tree nodes is possible.</p>
<div id="filter-example"></div>

<h2>Radio Button Group Example</h2>
<p>
By adding <code>isRadioGroup: true</code> to a node in the tree initial
configuration, that node's children will work as a Radio Group.
</p>
<div id="radio-button-example"></div>

<h2>Custom Label Example</h2>
<p>
A custom label component can be passed to replace the default label
component. All labels can be replaced with the same component or parent
and leaf nodes can have different replacement label components.
</p>
<p>
See <code>LabelComponent</code>, <code>LeafLabelComponent</code>, and
<code>ParentLabelComponent</code> properties of <code>CheckboxTree</code>.
</p>
<div id="custom-label-example"></div>

<footer class="site-footer">
<span class="site-footer-owner">
<a href="https://github.com/jakezatecky/react-checkbox-tree">React Checkbox Tree</a> is maintained by <a href="https://github.com/jakezatecky">jakezatecky</a>.
Expand Down
22 changes: 14 additions & 8 deletions examples/src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,29 @@ import React from 'react';
import { createRoot } from 'react-dom/client';

import BasicExample from './js/BasicExample';
import CustomIconsExample from './js/CustomIconsExample';
import ClickableLabelsExample from './js/ClickableLabelsExample';
import CustomIconsExample from './js/CustomIconsExample';
import CustomLabelExample from './js/CustomLabelExample';
import DisabledExample from './js/DisabledExample';
import ExpandAllExample from './js/ExpandAllExample';
import FilterExample from './js/FilterExample';
import HiddenCheckboxesExample from './js/HiddenCheckboxesExample';
import NoCascadeExample from './js/NoCascadeExample';
import LargeDataExample from './js/LargeDataExample';
import NativeCheckboxExample from './js/NativeCheckboxExample';
import NoCascadeExample from './js/NoCascadeExample';
import PessimisticToggleExample from './js/PessimisticToggleExample';
import FilterExample from './js/FilterExample';
import RadioButtonExample from './js/RadioButtonExample';

createRoot(document.getElementById('basic-example')).render(<BasicExample />);
createRoot(document.getElementById('clickable-labels-example')).render(<ClickableLabelsExample />);
createRoot(document.getElementById('custom-icons-example')).render(<CustomIconsExample />);
createRoot(document.getElementById('custom-label-example')).render(<CustomLabelExample />);
createRoot(document.getElementById('disabled-example')).render(<DisabledExample />);
createRoot(document.getElementById('no-cascade-example')).render(<NoCascadeExample />);
createRoot(document.getElementById('pessimistic-toggle-example')).render(<PessimisticToggleExample />);
createRoot(document.getElementById('clickable-labels-example')).render(<ClickableLabelsExample />);
createRoot(document.getElementById('hidden-checkboxes-example')).render(<HiddenCheckboxesExample />);
createRoot(document.getElementById('expand-all-example')).render(<ExpandAllExample />);
createRoot(document.getElementById('large-data-example')).render(<LargeDataExample />);
createRoot(document.getElementById('filter-example')).render(<FilterExample />);
createRoot(document.getElementById('hidden-checkboxes-example')).render(<HiddenCheckboxesExample />);
createRoot(document.getElementById('large-data-example')).render(<LargeDataExample />);
createRoot(document.getElementById('native-checkboxes-example')).render(<NativeCheckboxExample />);
createRoot(document.getElementById('no-cascade-example')).render(<NoCascadeExample />);
createRoot(document.getElementById('pessimistic-toggle-example')).render(<PessimisticToggleExample />);
createRoot(document.getElementById('radio-button-example')).render(<RadioButtonExample />);
37 changes: 19 additions & 18 deletions examples/src/js/BasicExample.jsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
import React, { useState } from 'react';
import CheckboxTree from 'react-checkbox-tree';
import CheckboxTree, { TreeModel } from 'react-checkbox-tree';

import { fileSystem as nodes } from './common';
import { fileSystem as nodes } from './data';

const initialTree = new TreeModel(nodes);

function BasicExample() {
const [checked, setChecked] = useState([
'/app/Http/Controllers/WelcomeController.js',
'/app/Http/routes.js',
'/public/assets/style.css',
'/public/index.html',
'/.gitignore',
]);
const [expanded, setExpanded] = useState(['/app']);

const onCheck = (value) => {
setChecked(value);
const [tree, setTree] = useState(initialTree);

const onChange = (newTree) => {
setTree(newTree);
};

const onCheck = (changedNode, newTree) => {
console.log(`changed node = ${changedNode.label}`);
console.log(newTree.getChecked());
};

const onExpand = (value) => {
setExpanded(value);
const onExpand = (changedNode, newTree) => {
console.log(`changed node = ${changedNode.label} => expanded = ${changedNode.expanded}`);
console.log(newTree.getExpanded());
};

return (
<CheckboxTree
checked={checked}
expanded={expanded}
nodes={nodes}
id="my-awesome-id"
tree={tree}
onChange={onChange}
onCheck={onCheck}
onExpand={onExpand}
/>
Expand Down
38 changes: 19 additions & 19 deletions examples/src/js/ClickableLabelsExample.jsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
import React, { useState } from 'react';
import CheckboxTree from 'react-checkbox-tree';
import CheckboxTree, { TreeModel } from 'react-checkbox-tree';

import { fileSystem as nodes } from './common';
import { fileSystem as nodes } from './data';

const initialTree = new TreeModel(nodes);

function ClickExample() {
const [checked, setChecked] = useState([
'/app/Http/Controllers/WelcomeController.js',
'/app/Http/routes.js',
'/public/assets/style.css',
'/public/index.html',
'/.gitignore',
]);
const [expanded, setExpanded] = useState(['/app']);
const [tree, setTree] = useState(initialTree);
const [clicked, setClicked] = useState({});

const onCheck = (value) => {
setChecked(value);
const onChange = (newTree) => {
setTree(newTree);
};

const onCheck = (changedNode, newTree) => {
console.log(`changed node = ${changedNode.label}`);
console.log(newTree.getChecked());
};

const onExpand = (value) => {
setExpanded(value);
const onExpand = (changedNode, newTree) => {
console.log(`changed node = ${changedNode.label} => expanded = ${changedNode.expanded}`);
console.log(newTree.getExpanded());
};

const onClick = (value) => {
setClicked(value);
const onClick = (node) => {
setClicked(node);
};

const notClickedText = '(none)';
Expand All @@ -32,10 +33,9 @@ function ClickExample() {
return (
<div className="clickable-labels">
<CheckboxTree
checked={checked}
expandOnClick
expanded={expanded}
nodes={nodes}
tree={tree}
onChange={onChange}
onCheck={onCheck}
onClick={onClick}
onExpand={onExpand}
Expand Down
30 changes: 18 additions & 12 deletions examples/src/js/CustomIconsExample.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import CheckboxTree from 'react-checkbox-tree';
import CheckboxTree, { TreeModel } from 'react-checkbox-tree';

const nodes = [
{
Expand Down Expand Up @@ -41,27 +41,33 @@ const nodes = [
},
];

function CustomIconsExamples() {
const [checked, setChecked] = useState([]);
const [expanded, setExpanded] = useState(['Documents']);
const initialTree = new TreeModel(nodes);

const onCheck = (value) => {
setChecked(value);
function CustomIconsExample() {
const [tree, setTree] = useState(initialTree);

const onChange = (newTree) => {
setTree(newTree);
};

const onCheck = (changedNode, newTree) => {
console.log(`changed node = ${changedNode.label}`);
console.log(newTree.getChecked());
};

const onExpand = (value) => {
setExpanded(value);
const onExpand = (changedNode, newTree) => {
console.log(`changed node = ${changedNode.label} => expanded = ${changedNode.expanded}`);
console.log(newTree.getExpanded());
};

return (
<CheckboxTree
checked={checked}
expanded={expanded}
nodes={nodes}
tree={tree}
onChange={onChange}
onCheck={onCheck}
onExpand={onExpand}
/>
);
}

export default CustomIconsExamples;
export default CustomIconsExample;
118 changes: 118 additions & 0 deletions examples/src/js/CustomLabelExample.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import CheckboxTree, { TreeModel } from 'react-checkbox-tree';

import NodeModel from '../../../src/js/models/NodeModel';

import { mapLayerTreeSmall as nodes } from './data';

const initialTree = new TreeModel(nodes);

const propTypes = {
node: PropTypes.instanceOf(NodeModel).isRequired,
onChange: PropTypes.func,
};

const defaultProps = {
onChange: null,
};

function CustomLabel({ node, onChange }) {
const onChangeHandler = (e) => {
const nodeKey = e.target.name;
const { value } = e.target;
onChange(nodeKey, value);
};

return (
<div
style={{
border: '1px solid lightgrey',
borderRadius: '8px',
margin: '2px',
padding: '3px 8px',
minWidth: '180px',
backgroundColor: 'lightblue',
}}
>
<div>
{node.label}
</div>
<div style={{ display: 'flex' }}>
<label htmlFor="volume">opacity:</label>
<span>
<input
id={node.value}
max="100"
min="0"
name={node.value}
style={{
display: 'block',
margin: '0.4rem',
opacity: 1,
width: '100px',
height: '2px',
}}
type="range"
onChange={onChangeHandler}
/>
</span>
</div>
</div>
);
}
CustomLabel.propTypes = propTypes;
CustomLabel.defaultProps = defaultProps;

function CustomLabelExample() {
const [tree, setTree] = useState(initialTree);
const [nodeKey, setNodeKey] = useState('');
const [value, setValue] = useState('');

const onChange = (newTree) => {
setTree(newTree);
};

const onCheck = (changedNode, newTree) => {
console.log(`changed node = ${changedNode.label}`);
console.log(newTree.getChecked());
};

const onExpand = (changedNode, newTree) => {
console.log(`changed node = ${changedNode.label} => expanded = ${changedNode.expanded}`);
console.log(newTree.getExpanded());
};

// this handler gets passed by CheckboxTree through TreeNode
// along with the CustomLabel replacing the DefaultLabel
const onLeafLabelChangeHandler = (key, newValue) => {
setNodeKey(key);
setValue(newValue);
};

const defaultText = '(none)';
const displayText = nodeKey || defaultText;

return (
<div className="clickable-labels">
<CheckboxTree
LeafLabelComponent={CustomLabel}
tree={tree}
onChange={onChange}
onCheck={onCheck}
onExpand={onExpand}
onLeafLabelChange={onLeafLabelChangeHandler}
/>
<div className="clickable-labels-info">
<strong>layer</strong>
{`: ${displayText}`}
<br />
Slider Value
{`: ${value}`}

</div>
</div>
);
}

export default CustomLabelExample;
Loading