Skip to content

Commit 964d7be

Browse files
Merge pull request #5 from CodeWithDennis/feature/add-additional-options
Added new options, multiple feature, styling
2 parents 5559387 + b9ec5b1 commit 964d7be

File tree

7 files changed

+220
-27
lines changed

7 files changed

+220
-27
lines changed

README.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ composer require codewithdennis/filament-select-tree
1818
## Features
1919
- ✅ Compatible with dark mode
2020
- ✅ Featuring search functionality
21-
- ❌ Multi-select (Coming soon)
22-
- ❌ Relationships
21+
- ✅ Comma seperated multi-select
22+
- ❌ Relationships (Planned)
2323

2424
## Usage
2525

@@ -32,15 +32,34 @@ SelectTree::make('category_id')
3232
->tree(Category::class, 'category_id', 'name', function ($query) {
3333
return $query;
3434
})
35-
35+
3636
// The label 'Category' is assigned to the field.
3737
->label(__('Category'))
38+
39+
// Set a custom placeholder for when no items are selected
40+
->placeholder(__('Your custom placeholder here'))
3841

3942
// Ensures that only leaf nodes can be selected while preventing the selection of groups.
4043
->disabledBranchNode()
4144

4245
// Show the count of children alongside the group's name.
4346
->withCount()
47+
48+
// To disable tags and display a text message instead (e.g., "X items have been selected")
49+
// Tags is always disabled on single select
50+
->disableTags()
51+
52+
// To keep the dropdown open at all times
53+
->alwaysOpen()
54+
55+
// By default, all nodes are independent. Set this to false if you want to display groups when all subnodes are selected.
56+
->showGroupsWhenAllSelected(false)
57+
58+
// By default, the clearable icon is enabled, but you can hide it with:
59+
->clearable(false)
60+
61+
// Enable the option to save multiple values as a string (comma-separated)
62+
->multiple()
4463

4564
// Activates the search functionality for the SelectTree.
4665
->searchable()

resources/css/custom.css

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
outline: 2px solid transparent;
88
outline-offset: 2px;
99
background: transparent;
10+
padding: 8px;
1011
}
1112

1213
.treeselect {
@@ -107,4 +108,90 @@ html.dark .treeselect-list {
107108

108109
html.dark .treeselect-list.treeselect-list--single-select html.dark.treeselect-list__item--single-selected, html.dark .treeselect-list__item--focused, html.dark .treeselect-list__item:hover, html.dark .treeselect-list.treeselect-list--single-select .treeselect-list__item--single-selected, html.dark .treeselect-list__item--focused, html.dark .treeselect-list__item:hover {
109110
background-color: hsla(0, 0%, 100%, .05) !important;
111+
}
112+
113+
html.dark .treeselect-list__item--checked, .treeselect-list__item--checked {
114+
background: transparent;
115+
}
116+
117+
.treeselect-input__tags-element {
118+
--tw-bg-opacity: 1;
119+
--tw-text-opacity: 1;
120+
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
121+
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
122+
--tw-ring-inset: inset;
123+
--tw-ring-color: rgba(var(--primary-600), 0.1);
124+
align-items: center;
125+
background-color: rgba(var(--primary-50), var(--tw-bg-opacity));
126+
border-radius: 0.375rem;
127+
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
128+
color: rgba(var(--primary-600), var(--tw-text-opacity));
129+
display: inline-flex;
130+
font-size: .75rem;
131+
font-weight: 500;
132+
gap: 0.25rem;
133+
line-height: 1rem;
134+
padding: 0.25rem 0.5rem;
135+
word-break: break-all;
136+
}
137+
138+
html.dark .treeselect-input__tags-element {
139+
--tw-text-opacity: 1;
140+
--tw-ring-color: rgba(var(--primary-400), 0.3);
141+
background-color: rgba(var(--primary-400), .1);
142+
color: rgba(var(--primary-400), var(--tw-text-opacity));
143+
}
144+
145+
.treeselect-list__item-checkbox-container {
146+
border-radius: 0.25rem;
147+
height: 16px;
148+
min-width: 16px;
149+
width: 16px;
150+
}
151+
152+
.treeselect-list__item--checked .treeselect-list__item-checkbox-container, .treeselect-list__item--partial-checked .treeselect-list__item-checkbox-container {
153+
background-color: rgba(var(--primary-600), var(--tw-text-opacity));
154+
}
155+
156+
.treeselect-list__item-checkbox {
157+
transition-duration: 75ms;
158+
background-color: transparent !important;
159+
border: none;
160+
}
161+
162+
.treeselect-list__item-checkbox-container {
163+
background-color: #f8f5f5;
164+
border: none;
165+
}
166+
167+
html.dark .treeselect-list__item-checkbox-container {
168+
border: rgb(255 255 255/var(--tw-text-opacity));
169+
}
170+
171+
html.dark .treeselect-list__item-checkbox-container {
172+
background-color: hsla(0, 0%, 100%, .05);
173+
}
174+
175+
.treeselect-list__item-checkbox-icon {
176+
height: 80%;
177+
left: 0.1rem;
178+
top: 0.1rem;
179+
width: 80%;
180+
}
181+
182+
.treeselect-input__tags-element:hover {
183+
background-color: rgba(0, 0, 0, 0.1);
184+
}
185+
186+
.treeselect-input__tags-element:hover {
187+
background-color: rgba(var(--primary-600), var(--tw-text-opacity));
188+
color: white;
189+
}
190+
191+
.treeselect-input__tags-element:hover .treeselect-input__tags-cross svg {
192+
stroke: rgb(255 255 255/var(--tw-text-opacity));
193+
}
194+
195+
.treeselect-input__tags {
196+
margin-left: 3px;
110197
}

resources/dist/custom.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/dist/tree.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/js/index.js

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,33 @@
11
import Treeselect from 'treeselectjs'
22

33
export default function tree({
4-
state,
5-
name,
6-
options,
7-
searchable,
8-
showCount,
9-
placeholder,
10-
disabledBranchNode,
11-
disabled = false,
12-
isSingleSelect = true,
13-
showTags = false,
14-
clearable = false,
15-
}) {
4+
state,
5+
name,
6+
options,
7+
searchable,
8+
showCount,
9+
placeholder,
10+
disabledBranchNode,
11+
disabled = false,
12+
isSingleSelect = true,
13+
showTags = true,
14+
clearable = true,
15+
isIndependentNodes = true,
16+
alwaysOpen = false
17+
}) {
1618
return {
1719
state,
1820
tree: null,
1921
init() {
22+
const values = this.isSingleSelect
23+
? (this.state !== null ? this.state : '')
24+
: (this.state !== null ? this.state.split(',').map(Number) : '');
25+
2026
this.tree = new Treeselect({
2127
id: `tree-${name}-id`,
2228
ariaLabel: `tree-${name}-label`,
2329
parentHtmlContainer: this.$refs.tree,
24-
value: this.state,
30+
value: values,
2531
options,
2632
searchable,
2733
showCount,
@@ -30,11 +36,17 @@ export default function tree({
3036
disabled,
3137
isSingleSelect,
3238
showTags,
33-
clearable
39+
clearable,
40+
isIndependentNodes,
41+
alwaysOpen,
3442
});
3543

3644
this.tree.srcElement.addEventListener('input', (e) => {
37-
this.state = e.detail;
45+
if (Array.isArray(e.detail)) {
46+
this.state = e.detail.join(",");
47+
} else {
48+
this.state = e.detail;
49+
}
3850
});
3951
}
4052
}

resources/views/select-tree.blade.php

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
<x-dynamic-component :component="$getFieldWrapperView()" :field="$field">
22
<div
3-
wire:ignore
4-
x-data
5-
x-load-css="[
3+
wire:ignore
4+
x-data
5+
x-load-css="[
66
@js(\Filament\Support\Facades\FilamentAsset::getStyleHref('tree', package: 'codewithdennis/filament-select-tree')),
77
@js(\Filament\Support\Facades\FilamentAsset::getStyleHref('custom', package: 'codewithdennis/filament-select-tree'))
88
]"
99
>
1010
<div
11-
x-ignore
12-
ax-load="visible"
13-
ax-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('tree', package: 'codewithdennis/filament-select-tree') }}"
14-
x-data="tree({
11+
x-ignore
12+
ax-load="visible"
13+
ax-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('tree', package: 'codewithdennis/filament-select-tree') }}"
14+
x-data="tree({
1515
name: '{{ $getName() }}',
1616
state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$getStatePath()}')") }},
1717
options: {{ json_encode($getTree()) }},
@@ -20,6 +20,11 @@
2020
placeholder: '{{ $getPlaceholder() }}',
2121
disabledBranchNode: '{{ $getDisabledBranchNode() }}',
2222
disabled: '{{ $isDisabled() }}',
23+
isSingleSelect: '{{ !$getMultiple() }}',
24+
isIndependentNodes: '{{ $getIndependent() }}',
25+
showTags: '{{ $getMultiple() && $getShowTags() }}',
26+
alwaysOpen: '{{ $getAlwaysOpen() }}',
27+
clearable: '{{ $getClearable() }}',
2328
})"
2429
>
2530
<div x-ref="tree"></div>

src/SelectTree.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ class SelectTree extends Field
1919

2020
protected bool $withCount = false;
2121

22+
protected bool $multiple = false;
23+
24+
protected bool $alwaysOpen = false;
25+
26+
protected bool $showTags = true;
27+
28+
protected bool $independent = true;
29+
30+
protected bool $clearable = true;
31+
2232
protected bool $disabledBranchNode = false;
2333

2434
protected string $treeModel;
@@ -36,6 +46,41 @@ public function withCount(bool $withCount = true): static
3646
return $this;
3747
}
3848

49+
public function clearable(bool $clearable = true): static
50+
{
51+
$this->clearable = $clearable;
52+
53+
return $this;
54+
}
55+
56+
public function independent(bool $independent = true): static
57+
{
58+
$this->independent = $independent;
59+
60+
return $this;
61+
}
62+
63+
public function showTags(bool $showTags = true): static
64+
{
65+
$this->showTags = $showTags;
66+
67+
return $this;
68+
}
69+
70+
public function alwaysOpen(bool $alwaysOpen = true): static
71+
{
72+
$this->alwaysOpen = $alwaysOpen;
73+
74+
return $this;
75+
}
76+
77+
public function multiple(bool $multiple = true): static
78+
{
79+
$this->multiple = $multiple;
80+
81+
return $this;
82+
}
83+
3984
public function disabledBranchNode(bool $disabledBranchNode = true): static
4085
{
4186
$this->disabledBranchNode = $disabledBranchNode;
@@ -48,11 +93,36 @@ public function getTree(): Collection
4893
return $this->evaluate($this->buildTree());
4994
}
5095

96+
public function getIndependent(): bool
97+
{
98+
return $this->evaluate($this->independent);
99+
}
100+
51101
public function getWithCount(): bool
52102
{
53103
return $this->evaluate($this->withCount);
54104
}
55105

106+
public function getMultiple(): bool
107+
{
108+
return $this->evaluate($this->multiple);
109+
}
110+
111+
public function getClearable(): bool
112+
{
113+
return $this->evaluate($this->clearable);
114+
}
115+
116+
public function getAlwaysOpen(): bool
117+
{
118+
return $this->evaluate($this->alwaysOpen);
119+
}
120+
121+
public function getShowTags(): bool
122+
{
123+
return $this->evaluate($this->showTags);
124+
}
125+
56126
public function getDisabledBranchNode(): bool
57127
{
58128
return $this->evaluate($this->disabledBranchNode);

0 commit comments

Comments
 (0)