Skip to content

Commit 832fc9a

Browse files
committed
feat: ltr renderers (#369)
1 parent 8dc435a commit 832fc9a

File tree

7 files changed

+96
-4
lines changed

7 files changed

+96
-4
lines changed

next-release-notes.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
### Features
2+
- RTL mode can be used on default renderers, see [docs for details](https://rct.lukasbach.com//docs/guides/accessibility#right-to-left-mode-rtl) (#369)
3+
14
### Bug Fixes and Improvements
25
- Fixes an issue where not providing a submit button ref in a custom rename input renderer would prevent dismissing the input on blur (#368)
36
- Fixes an issue where dropping an item on its children was possible through keyboard-based dragging (#363)

packages/core/src/renderers/createDefaultRenderers.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ const cx = (...classNames: Array<string | undefined | false>) =>
55
classNames.filter(cn => !!cn).join(' ');
66

77
export const createDefaultRenderers = (
8-
renderDepthOffset: number
8+
renderDepthOffset: number,
9+
rtl?: boolean
910
): AllTreeRenderProps => ({
1011
renderItemTitle: ({ title, context, info }) => {
1112
if (!info.isSearching || !context.isSearchMatching) {
@@ -31,6 +32,7 @@ export const createDefaultRenderers = (
3132
<div
3233
className={cx(
3334
item.isFolder && 'rct-tree-item-arrow-isFolder',
35+
context.isExpanded && 'rct-tree-item-arrow-expanded',
3436
'rct-tree-item-arrow'
3537
)}
3638
{...context.arrowProps}
@@ -102,7 +104,7 @@ export const createDefaultRenderers = (
102104
>
103105
<div
104106
{...(context.itemContainerWithoutChildrenProps as any)}
105-
style={{ paddingLeft: `${(depth + 1) * renderDepthOffset}px` }}
107+
style={{ '--depthOffset': `${(depth + 1) * renderDepthOffset}px` }}
106108
className={cx(
107109
'rct-tree-item-title-container',
108110
item.isFolder && 'rct-tree-item-title-container-isFolder',
@@ -164,7 +166,8 @@ export const createDefaultRenderers = (
164166
'rct-tree-root',
165167
info.isFocused && 'rct-tree-root-focus',
166168
info.isRenaming && 'rct-tree-root-renaming',
167-
info.areItemsSelected && 'rct-tree-root-itemsselected'
169+
info.areItemsSelected && 'rct-tree-root-itemsselected',
170+
rtl && 'rct-rtl'
168171
)}
169172
>
170173
<div

packages/core/src/stories/BasicExamples.stories.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { StaticTreeDataProvider } from '../uncontrolledEnvironment/StaticTreeDat
88
import { UncontrolledTreeEnvironment } from '../uncontrolledEnvironment/UncontrolledTreeEnvironment';
99
import { buildTestTree } from '../../test/helpers';
1010
import { TreeItemIndex } from '../types';
11+
import { createDefaultRenderers } from '../renderers';
1112

1213
export default {
1314
title: 'Core/Basic Examples',
@@ -638,3 +639,33 @@ export const AnimatedExpandingAndCollapsing = () => {
638639
</UncontrolledTreeEnvironment>
639640
);
640641
};
642+
643+
export const RightToLeftRenderers = () => (
644+
<UncontrolledTreeEnvironment<string>
645+
canDragAndDrop
646+
canDropOnFolder
647+
canReorderItems
648+
dataProvider={
649+
new StaticTreeDataProvider(longTree.items, (item, data) => ({
650+
...item,
651+
data,
652+
}))
653+
}
654+
getItemTitle={item => item.data}
655+
viewState={{
656+
'tree-1': {
657+
expandedItems: [
658+
'Fruit',
659+
'Meals',
660+
'America',
661+
'Europe',
662+
'Asia',
663+
'Desserts',
664+
],
665+
},
666+
}}
667+
{...createDefaultRenderers(10, true)}
668+
>
669+
<Tree treeId="tree-1" rootItem="root" treeLabel="Tree Example" />
670+
</UncontrolledTreeEnvironment>
671+
);

packages/core/src/style-modern.css

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@
167167
outline: none;
168168
}
169169

170-
171170
.rct-tree-input-icon {
172171
content: url(data:image/svg+xml,%3Csvg%20stroke%3D%22currentColor%22%20fill%3D%22currentColor%22%20stroke-width%3D%220%22%20viewBox%3D%220%200%2016%2016%22%20height%3D%221em%22%20width%3D%221em%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M11.742%2010.344a6.5%206.5%200%201%200-1.397%201.398h-.001c.03.04.062.078.098.115l3.85%203.85a1%201%200%200%200%201.415-1.414l-3.85-3.85a1.007%201.007%200%200%200-.115-.1zM12%206.5a5.5%205.5%200%201%201-11%200%205.5%205.5%200%200%201%2011%200z%22%3E%3C%2Fpath%3E%3C%2Fsvg%3E);
173172
position: fixed;
@@ -178,3 +177,25 @@
178177
.rct-dark .rct-tree-input-icon {
179178
content: url(data:image/svg+xml,%3Csvg%20stroke%3D%22%23ffffff%22%20fill%3D%22%23ffffff%22%20stroke-width%3D%220%22%20viewBox%3D%220%200%2016%2016%22%20height%3D%221em%22%20width%3D%221em%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M11.742%2010.344a6.5%206.5%200%201%200-1.397%201.398h-.001c.03.04.062.078.098.115l3.85%203.85a1%201%200%200%200%201.415-1.414l-3.85-3.85a1.007%201.007%200%200%200-.115-.1zM12%206.5a5.5%205.5%200%201%201-11%200%205.5%205.5%200%200%201%2011%200z%22%3E%3C%2Fpath%3E%3C%2Fsvg%3E);
180179
}
180+
181+
.rct-rtl .rct-tree-item-title-container {
182+
flex-direction: row-reverse;
183+
}
184+
.rct-rtl .rct-tree-item-button {
185+
text-align: right;
186+
justify-content: flex-end;
187+
}
188+
.rct-rtl .rct-tree-item-title-container {
189+
padding-right: var(--depthOffset, 0px);
190+
padding-left: 0;
191+
}
192+
.rct-rtl .rct-tree-item-title-container-selected .rct-tree-item-button::before {
193+
left: unset;
194+
right: calc(-0.5 * var(--rct-bar-width));
195+
}
196+
.rct-rtl .rct-tree-item-arrow {
197+
margin-left: 4px;
198+
}
199+
.rct-rtl .rct-tree-item-arrow:not(.rct-tree-item-arrow-expanded) {
200+
transform: rotate(180deg);
201+
}

packages/core/src/style.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
align-items: center;
5050
border-top: 1px solid transparent;
5151
border-bottom: 1px solid transparent;
52+
padding-left: var(--depthOffset, 0px);
5253
}
5354

5455
.rct-tree-child-list {

packages/docs/docs/faq.mdx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,4 +242,18 @@ Reference: [#290](https://github.com/lukasbach/react-complex-tree/issues/290)
242242

243243
</details>
244244

245+
246+
<details><summary>I want to enable RTL (right-to-left) mode for my tree</summary>
247+
248+
The library doesn't make any assumptions about how you render your tree, and you can fairly easily implement
249+
RTL mode in custom renderers yourself. However, if you are using the built-in default renderers, you can also
250+
enable RTL mode by providing the renderers with an `rtl` flag. Add `{...createDefaultRenderers(10, true)}`
251+
to either the `Tree` or `TreeEnvironment` component to enable RTL mode.
252+
253+
See [here](/docs/guides/accessibility#right-to-left-mode-rtl) for more details.
254+
255+
Reference: [#369](https://github.com/lukasbach/react-complex-tree/issues/369)
256+
257+
</details>
258+
245259
<!-- stopped at #147, next is #140 -->

packages/docs/docs/guides/accessibility.mdx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,25 @@ All features can be accessed via keybindings, including
2424
[drag and drop](/docs/guides/keyboard#keyboard-bound-drag-and-drop-sequences), searching and renaming.
2525
[Find out more in the documentation on keyboard bindings.](/docs/guides/keyboard)
2626

27+
## Right-To-Left Mode (RTL)
28+
29+
The library doesn't make any assumptions about how you render your tree, and you can fairly easily implement
30+
RTL mode in custom renderers yourself. However, if you are using the built-in default renderers, you can also
31+
enable RTL mode by providing the renderers with an `rtl` flag. Add `{...createDefaultRenderers(10, true)}`
32+
to either the `Tree` or `TreeEnvironment` component to enable RTL mode.
33+
34+
```typescript jsx
35+
import { createDefaultRenderers } from 'react-complex-tree';
36+
37+
<UncontrolledTreeEnvironment<string>
38+
{...createDefaultRenderers(10, true)}
39+
>
40+
<Tree treeId="tree-1" rootItem="root" treeLabel="Tree Example" />
41+
</UncontrolledTreeEnvironment>
42+
```
43+
44+
<StoryEmbed storyName="core-basic-examples--right-to-left-renderers" iframeProps={{ width: 600 }} />
45+
2746
## Live descriptors
2847

2948
A visually hidden live section is rendered at the top of the tree that explains the state of the tree

0 commit comments

Comments
 (0)