@solidpb/ui-kit 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/DateInput/DateInput.jsx +3 -0
- package/dist/components/DropdownMenu/DropdownMenu.d.ts +2 -2
- package/dist/components/FilterBar/AddFilter.d.ts +0 -1
- package/dist/components/FilterBar/AddFilter.jsx +0 -1
- package/dist/components/FilterBar/EditAdvancedFilter.d.ts +9 -0
- package/dist/components/FilterBar/EditAdvancedFilter.jsx +16 -0
- package/dist/components/FilterBar/EditFilters.d.ts +0 -1
- package/dist/components/FilterBar/EditFilters.jsx +0 -1
- package/dist/components/FilterBar/FilterBar.d.ts +18 -9
- package/dist/components/FilterBar/FilterBar.jsx +126 -45
- package/dist/components/FilterBar/FilterChip.d.ts +7 -3
- package/dist/components/FilterBar/FilterChip.jsx +77 -5
- package/dist/components/FilterBar/FilterEdit.jsx +22 -54
- package/dist/components/Form.d.ts +2 -1
- package/dist/components/Form.jsx +4 -17
- package/dist/components/RelationPicker.jsx +1 -8
- package/dist/components/{Table/Table.jsx → Table.jsx} +2 -2
- package/package.json +1 -1
- package/dist/components/Table/index.d.ts +0 -1
- package/dist/components/Table/index.js +0 -1
- /package/dist/components/{Table/Table.d.ts → Table.d.ts} +0 -0
|
@@ -11,7 +11,7 @@ type DropdownMenuRootProps<T extends ValidComponent = "div"> = PolymorphicProps<
|
|
|
11
11
|
class?: string;
|
|
12
12
|
};
|
|
13
13
|
type DropDownMenuItemProps<T extends ValidComponent = "div"> = PolymorphicProps<T, DropdownMenuItemProps<T>> & {
|
|
14
|
-
onSelect
|
|
14
|
+
onSelect?: () => void;
|
|
15
15
|
};
|
|
16
16
|
interface DropdownMenuComponents {
|
|
17
17
|
MenuItem: ParentComponent<DropDownMenuItemProps>;
|
|
@@ -22,6 +22,6 @@ export declare const DropdownMenu: ParentComponent<DropdownMenuRootProps> & Drop
|
|
|
22
22
|
export declare const DropdownMenuTrigger: ParentComponent<ButtonProps>;
|
|
23
23
|
export declare const DropdownMenuContent: ParentComponent<DropdownMenuProps>;
|
|
24
24
|
export declare const DropdownMenuItem: ParentComponent<{
|
|
25
|
-
onSelect
|
|
25
|
+
onSelect?: () => void;
|
|
26
26
|
}>;
|
|
27
27
|
export default DropdownMenu;
|
|
@@ -3,7 +3,6 @@ interface AddFilterProps<T> {
|
|
|
3
3
|
availableFields: FilterField<T>[];
|
|
4
4
|
onAddFilters: (filters: Filter<T>[]) => void;
|
|
5
5
|
size?: "xs" | "sm" | "md" | "lg" | "xl";
|
|
6
|
-
setOpen: (v: boolean) => void;
|
|
7
6
|
}
|
|
8
7
|
export declare const AddFilter: <T>(props: AddFilterProps<T>) => import("solid-js").JSX.Element;
|
|
9
8
|
export default AddFilter;
|
|
@@ -3,7 +3,6 @@ import FiltersEdit from "./FiltersEdit";
|
|
|
3
3
|
export const AddFilter = (props) => {
|
|
4
4
|
const handleSaveFilters = (filters) => {
|
|
5
5
|
props.onAddFilters(filters);
|
|
6
|
-
props.setOpen(false);
|
|
7
6
|
};
|
|
8
7
|
return (<FiltersEdit addConditionTrigger={<>
|
|
9
8
|
<Plus class="h-[1em] w-[1em]"/> <span>Condition</span>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Component } from "solid-js";
|
|
2
|
+
import { AdvancedFilter } from "./FilterBar";
|
|
3
|
+
interface EditAdvancedFilterProps {
|
|
4
|
+
filter?: AdvancedFilter;
|
|
5
|
+
onSave: (filter: AdvancedFilter) => void;
|
|
6
|
+
size?: "xs" | "sm" | "md" | "lg" | "xl";
|
|
7
|
+
}
|
|
8
|
+
export declare const EditAdvancedFilter: Component<EditAdvancedFilterProps>;
|
|
9
|
+
export default EditAdvancedFilter;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createSignal } from "solid-js";
|
|
2
|
+
import { Input } from "../Input";
|
|
3
|
+
import { Button } from "../Button";
|
|
4
|
+
export const EditAdvancedFilter = (props) => {
|
|
5
|
+
const [filter, setFilter] = createSignal(props.filter ?? { filter: "", label: "Filter" });
|
|
6
|
+
return (<div class="flex flex-col gap-2 sm:min-w-150">
|
|
7
|
+
<Input label="Filter Name" value={filter().label} onChange={(v) => setFilter({ ...filter(), label: v })}/>
|
|
8
|
+
<Input label="Expression" inputProps={{ placeholder: "Expression", class: "w-full" }} value={filter().filter} onChange={(v) => setFilter({ ...filter(), filter: v })} size={props.size}/>
|
|
9
|
+
<div class="flex justify-end">
|
|
10
|
+
<Button appearance="success" onClick={() => props.onSave(filter())}>
|
|
11
|
+
Save
|
|
12
|
+
</Button>
|
|
13
|
+
</div>
|
|
14
|
+
</div>);
|
|
15
|
+
};
|
|
16
|
+
export default EditAdvancedFilter;
|
|
@@ -3,7 +3,6 @@ interface EditFiltersProps<T> {
|
|
|
3
3
|
availableFields: FilterField<T>[];
|
|
4
4
|
onSaveFilters: (filters: Filter<T>[]) => void;
|
|
5
5
|
size?: "xs" | "sm" | "md" | "lg" | "xl";
|
|
6
|
-
setOpen: (v: boolean) => void;
|
|
7
6
|
currentFilters: Filter<T>[];
|
|
8
7
|
}
|
|
9
8
|
export declare const EditFilters: <T>(props: EditFiltersProps<T>) => import("solid-js").JSX.Element;
|
|
@@ -3,7 +3,6 @@ import FiltersEdit from "./FiltersEdit";
|
|
|
3
3
|
export const EditFilters = (props) => {
|
|
4
4
|
const handleSaveFilters = (filters) => {
|
|
5
5
|
props.onSaveFilters(filters);
|
|
6
|
-
props.setOpen(false);
|
|
7
6
|
};
|
|
8
7
|
return (<FiltersEdit addConditionTrigger={<>
|
|
9
8
|
<Plus class="h-[1em] w-[1em]"/> <span>Condition</span>
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import { JSXElement } from "solid-js";
|
|
2
2
|
export type FieldType = "text" | "number" | "date" | "select" | "bool";
|
|
3
|
-
export type FilterOperator = "
|
|
3
|
+
export type FilterOperator = "=" | "!=" | ">" | ">=" | "<" | "<=" | "~" | "!~" | "?=" | "?!=" | "?>" | "?>=" | "?<" | "?<=" | "?~" | "?!~";
|
|
4
4
|
export declare const filterDefaults: Record<FieldType, FilterOperator>;
|
|
5
5
|
export declare const filterLabels: Record<FieldType, Partial<Record<FilterOperator, string>>>;
|
|
6
6
|
export type FilterSelectValue = {
|
|
7
7
|
label: string;
|
|
8
8
|
value: string;
|
|
9
9
|
};
|
|
10
|
-
export type
|
|
11
|
-
startDate: Date | null;
|
|
12
|
-
endDate: Date | null;
|
|
13
|
-
};
|
|
14
|
-
export type FilterValue = string | number | boolean | FilterSelectValue | FilterDateValue | null;
|
|
10
|
+
export type FilterValue = string | number | boolean | FilterSelectValue | Date | null;
|
|
15
11
|
export interface Filter<T> {
|
|
16
12
|
field: FilterField<T>;
|
|
17
13
|
operator: FilterOperator;
|
|
@@ -20,6 +16,10 @@ export interface Filter<T> {
|
|
|
20
16
|
export interface FilterGroup<T> {
|
|
21
17
|
filters: Filter<T>[];
|
|
22
18
|
}
|
|
19
|
+
export interface AdvancedFilter {
|
|
20
|
+
label: string;
|
|
21
|
+
filter: string;
|
|
22
|
+
}
|
|
23
23
|
export interface FilterField<T> {
|
|
24
24
|
name: keyof T;
|
|
25
25
|
label: string;
|
|
@@ -33,22 +33,31 @@ export interface SortOption<T> {
|
|
|
33
33
|
field: keyof T;
|
|
34
34
|
direction: "asc" | "desc";
|
|
35
35
|
}
|
|
36
|
+
interface SavedFilterPreset<T> {
|
|
37
|
+
filter: AdvancedFilter;
|
|
38
|
+
sortBy?: SortOption<T>[];
|
|
39
|
+
onApply: () => void;
|
|
40
|
+
}
|
|
36
41
|
interface FilterBarProps<T> {
|
|
37
|
-
items?: (Filter<T> | FilterGroup<T>)[];
|
|
38
|
-
setItems?: (items: (Filter<T> | FilterGroup<T>)[]) => void;
|
|
42
|
+
items?: (Filter<T> | FilterGroup<T> | AdvancedFilter)[];
|
|
43
|
+
setItems?: (items: (Filter<T> | FilterGroup<T> | AdvancedFilter)[]) => void;
|
|
39
44
|
availableFields?: FilterField<T>[];
|
|
40
45
|
leftAction?: JSXElement;
|
|
41
46
|
sortBy?: SortOption<T>;
|
|
42
47
|
setSortBy?: (sort?: SortOption<T>) => void;
|
|
43
48
|
onAddFilterGroup: (filters: Filter<T>[]) => void;
|
|
44
49
|
onUpdateFilterGroup: (ind: number, filters: Filter<T>[]) => void;
|
|
45
|
-
|
|
50
|
+
onAddAdvancedFilter: (filter: AdvancedFilter) => void;
|
|
51
|
+
onUpdateAdvancedFilter: (ind: number, filter: AdvancedFilter) => void;
|
|
52
|
+
onFilterRemove: (ind: number, filter: Filter<T> | FilterGroup<T> | AdvancedFilter) => void;
|
|
46
53
|
onGroupDrag: (sourceInd: number, targetInd: number, sourceFilterGroupInd?: number) => void;
|
|
47
54
|
value: string;
|
|
48
55
|
onChangeValue: (val: string) => void;
|
|
49
56
|
placeholder?: string;
|
|
50
57
|
size?: "xs" | "sm" | "md" | "lg" | "xl";
|
|
51
58
|
class?: string;
|
|
59
|
+
savedFilters?: SavedFilterPreset<T>[];
|
|
60
|
+
onSavePreset?: (name: string, items: (Filter<T> | FilterGroup<T> | AdvancedFilter)[]) => void;
|
|
52
61
|
}
|
|
53
62
|
export declare const FilterBar: <T>(props: FilterBarProps<T>) => import("solid-js").JSX.Element;
|
|
54
63
|
export {};
|
|
@@ -1,62 +1,59 @@
|
|
|
1
|
-
import { createEffect, createMemo, createSignal, For, onCleanup, Show } from "solid-js";
|
|
1
|
+
import { createEffect, createMemo, createSignal, For, Match, onCleanup, Show, Switch, } from "solid-js";
|
|
2
2
|
import { dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
|
|
3
3
|
import { TextField } from "@kobalte/core/text-field";
|
|
4
4
|
import { tv } from "tailwind-variants";
|
|
5
5
|
import Search from "lucide-solid/icons/search";
|
|
6
6
|
import ListFilter from "lucide-solid/icons/list-filter";
|
|
7
|
+
import Plus from "lucide-solid/icons/plus";
|
|
8
|
+
import Sparkles from "lucide-solid/icons/sparkles";
|
|
7
9
|
import ArrowDown from "lucide-solid/icons/arrow-down-narrow-wide";
|
|
10
|
+
import Save from "lucide-solid/icons/save";
|
|
8
11
|
import invariant from "tiny-invariant";
|
|
9
|
-
import { FilterChip, FilterGroupChip } from "./FilterChip";
|
|
12
|
+
import { AdvancedFilterChip, FilterChip, FilterGroupChip } from "./FilterChip";
|
|
10
13
|
import AddFilter from "./AddFilter";
|
|
11
14
|
import AddSortingDropdown from "./AddSortingDropdown";
|
|
12
15
|
import EditFilters from "./EditFilters";
|
|
13
16
|
import { DropdownMenu } from "../DropdownMenu";
|
|
14
17
|
import { Modal } from "../Modal";
|
|
18
|
+
import EditAdvancedFilter from "./EditAdvancedFilter";
|
|
15
19
|
import { Button } from "../Button";
|
|
20
|
+
import { Input } from "../Input";
|
|
16
21
|
export const filterDefaults = {
|
|
17
|
-
text: "
|
|
18
|
-
bool: "
|
|
19
|
-
number: "
|
|
20
|
-
select: "
|
|
21
|
-
date: "
|
|
22
|
+
text: "~",
|
|
23
|
+
bool: "=",
|
|
24
|
+
number: "=",
|
|
25
|
+
select: "=",
|
|
26
|
+
date: ">",
|
|
22
27
|
};
|
|
23
28
|
export const filterLabels = {
|
|
24
29
|
text: {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
is_set: "Is set",
|
|
32
|
-
is_not_set: "Is not set",
|
|
30
|
+
"~": "Contains",
|
|
31
|
+
// "~*": "Fuzzy contains",
|
|
32
|
+
// "~": "Contains (strict)",
|
|
33
|
+
"!~": "Doesn't contain",
|
|
34
|
+
"=": "Is",
|
|
35
|
+
"!=": "Is not",
|
|
33
36
|
},
|
|
34
37
|
bool: {
|
|
35
|
-
|
|
38
|
+
"=": "Is",
|
|
36
39
|
},
|
|
37
40
|
number: {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
is_not: "Is not",
|
|
41
|
+
"=": "Is",
|
|
42
|
+
">": "Is greater than",
|
|
43
|
+
"<": "Is less than",
|
|
44
|
+
"!=": "Is not",
|
|
43
45
|
},
|
|
44
46
|
select: {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
is_set: "Is set",
|
|
50
|
-
is_not_set: "Is not set",
|
|
47
|
+
"=": "Is",
|
|
48
|
+
"!=": "Is not",
|
|
49
|
+
"~": "Contains",
|
|
50
|
+
"!~": "Doesn't contain",
|
|
51
51
|
},
|
|
52
52
|
date: {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
is_not: "Is not",
|
|
58
|
-
is_set: "Is set",
|
|
59
|
-
is_not_set: "Is not set",
|
|
53
|
+
">": "Is after",
|
|
54
|
+
"<": "Is before",
|
|
55
|
+
"=": "Is",
|
|
56
|
+
"!=": "Is not",
|
|
60
57
|
},
|
|
61
58
|
};
|
|
62
59
|
const filterBarBase = tv({
|
|
@@ -96,6 +93,9 @@ export const FilterBar = (props) => {
|
|
|
96
93
|
let ref;
|
|
97
94
|
const [dragging, setDragging] = createSignal("idle");
|
|
98
95
|
const [filterDropdownOpen, setFilterDropdownOpen] = createSignal(false);
|
|
96
|
+
const [saveFilterPresetOpen, setSaveFilterPresetOpen] = createSignal(false);
|
|
97
|
+
const [filterPresetName, setFilterPresetName] = createSignal("");
|
|
98
|
+
const [advancedFilterDropdownOpen, setAdvancedFilterDropdownOpen] = createSignal(false);
|
|
99
99
|
const [showFieldDropdown, setShowFieldDropdown] = createSignal(false);
|
|
100
100
|
const textFields = createMemo(() => props.availableFields?.filter((f) => f.type === "text"));
|
|
101
101
|
const handleTextValueChange = (val) => {
|
|
@@ -106,14 +106,18 @@ export const FilterBar = (props) => {
|
|
|
106
106
|
const createTextFilter = (field) => {
|
|
107
107
|
const newFilter = {
|
|
108
108
|
field,
|
|
109
|
-
operator: "
|
|
109
|
+
operator: "~",
|
|
110
110
|
value: props.value,
|
|
111
111
|
};
|
|
112
112
|
props.onAddFilterGroup([newFilter]);
|
|
113
113
|
handleTextValueChange("");
|
|
114
114
|
};
|
|
115
|
-
const
|
|
116
|
-
|
|
115
|
+
const getFilterType = (item) => {
|
|
116
|
+
if ("filter" in item)
|
|
117
|
+
return "advanced";
|
|
118
|
+
if ("filters" in item)
|
|
119
|
+
return "group";
|
|
120
|
+
return "filter";
|
|
117
121
|
};
|
|
118
122
|
createEffect(() => {
|
|
119
123
|
const element = ref;
|
|
@@ -178,12 +182,71 @@ export const FilterBar = (props) => {
|
|
|
178
182
|
<Search class="w-[1em] h-[1em] opacity-90"/>
|
|
179
183
|
<TextField.Input placeholder={props.placeholder || "Search"} class="grow focus:outline-none" onFocusIn={() => setShowFieldDropdown(!!props.value)} onFocusOut={() => setShowFieldDropdown(false)}/>
|
|
180
184
|
</TextField>
|
|
181
|
-
<
|
|
182
|
-
<
|
|
185
|
+
<DropdownMenu>
|
|
186
|
+
<DropdownMenu.Trigger modifier="square" class="join-item">
|
|
183
187
|
<ListFilter class="w-[1em] h-[1em]"/>
|
|
184
|
-
</
|
|
188
|
+
</DropdownMenu.Trigger>
|
|
189
|
+
<DropdownMenu.Content>
|
|
190
|
+
<Show when={props.savedFilters && props.savedFilters.length > 0}>
|
|
191
|
+
<For each={props.savedFilters}>
|
|
192
|
+
{(filter) => (<DropdownMenu.MenuItem onSelect={filter.onApply}>
|
|
193
|
+
<a>
|
|
194
|
+
<span>{filter.filter.label}</span>
|
|
195
|
+
</a>
|
|
196
|
+
</DropdownMenu.MenuItem>)}
|
|
197
|
+
</For>
|
|
198
|
+
<div class="bg-base-300 w-full h-px my-1"/>
|
|
199
|
+
</Show>
|
|
200
|
+
<DropdownMenu.MenuItem onSelect={() => setFilterDropdownOpen(true)}>
|
|
201
|
+
<a>
|
|
202
|
+
<Plus class="h-[1em] w-[1em]"/>
|
|
203
|
+
<span>Add filters</span>
|
|
204
|
+
</a>
|
|
205
|
+
</DropdownMenu.MenuItem>
|
|
206
|
+
<DropdownMenu.MenuItem onSelect={() => setAdvancedFilterDropdownOpen(true)}>
|
|
207
|
+
<a>
|
|
208
|
+
<Sparkles class="h-[1em] w-[1em]"/>
|
|
209
|
+
<span>Add Advanced filter</span>
|
|
210
|
+
</a>
|
|
211
|
+
</DropdownMenu.MenuItem>
|
|
212
|
+
<div class="bg-base-300 w-full h-px my-1"/>
|
|
213
|
+
<DropdownMenu.MenuItem onSelect={() => setSaveFilterPresetOpen(true)}>
|
|
214
|
+
<a>
|
|
215
|
+
<Save class="h-[1em] w-[1em]"/>
|
|
216
|
+
<span>Save current filters</span>
|
|
217
|
+
</a>
|
|
218
|
+
</DropdownMenu.MenuItem>
|
|
219
|
+
</DropdownMenu.Content>
|
|
220
|
+
</DropdownMenu>
|
|
221
|
+
<Modal title="Save Filter Preset" open={saveFilterPresetOpen()} onOpenChange={setSaveFilterPresetOpen}>
|
|
222
|
+
<Modal.Modal>
|
|
223
|
+
<div class="flex flex-col gap-3">
|
|
224
|
+
<Input value={filterPresetName()} onChange={setFilterPresetName} label="Name" inputProps={{ placeholder: "Name" }}/>
|
|
225
|
+
<Button appearance="success" onClick={() => {
|
|
226
|
+
if (filterPresetName()) {
|
|
227
|
+
props.onSavePreset?.(filterPresetName(), props.items ?? []);
|
|
228
|
+
setSaveFilterPresetOpen(false);
|
|
229
|
+
}
|
|
230
|
+
}}>
|
|
231
|
+
Save
|
|
232
|
+
</Button>
|
|
233
|
+
</div>
|
|
234
|
+
</Modal.Modal>
|
|
235
|
+
</Modal>
|
|
236
|
+
<Modal title="Add filters" open={filterDropdownOpen()} onOpenChange={setFilterDropdownOpen}>
|
|
185
237
|
<Modal.Modal class="bg-base-200">
|
|
186
|
-
<AddFilter size={props.size} availableFields={props.availableFields ?? []} onAddFilters={
|
|
238
|
+
<AddFilter size={props.size} availableFields={props.availableFields ?? []} onAddFilters={(filters) => {
|
|
239
|
+
props.onAddFilterGroup(filters);
|
|
240
|
+
setFilterDropdownOpen(false);
|
|
241
|
+
}}/>
|
|
242
|
+
</Modal.Modal>
|
|
243
|
+
</Modal>
|
|
244
|
+
<Modal title="Add Advanced Filter" open={advancedFilterDropdownOpen()} onOpenChange={setAdvancedFilterDropdownOpen}>
|
|
245
|
+
<Modal.Modal>
|
|
246
|
+
<EditAdvancedFilter size={props.size} onSave={(filter) => {
|
|
247
|
+
props.onAddAdvancedFilter(filter);
|
|
248
|
+
setAdvancedFilterDropdownOpen(false);
|
|
249
|
+
}}/>
|
|
187
250
|
</Modal.Modal>
|
|
188
251
|
</Modal>
|
|
189
252
|
<Show when={props.setSortBy}>
|
|
@@ -220,11 +283,29 @@ export const FilterBar = (props) => {
|
|
|
220
283
|
{(item, i) => {
|
|
221
284
|
const [itemOpen, setItemOpen] = createSignal(false);
|
|
222
285
|
return (<Modal title="Edit filters" open={itemOpen()} onOpenChange={setItemOpen}>
|
|
223
|
-
<
|
|
224
|
-
<
|
|
225
|
-
|
|
286
|
+
<Switch>
|
|
287
|
+
<Match when={getFilterType(item) === "advanced"}>
|
|
288
|
+
<Modal.Trigger as={AdvancedFilterChip} index={i()} size={props.size} filter={item} onDelete={() => props.onFilterRemove(i(), item)} setOpen={setItemOpen}/>
|
|
289
|
+
</Match>
|
|
290
|
+
<Match when={getFilterType(item) === "filter"}>
|
|
291
|
+
<Modal.Trigger as={FilterChip} onDelete={() => props.onFilterRemove(i(), item)} filter={item} size={props.size} onGroupDrag={(sourceInd, sourceFilterGroupInd) => props.onGroupDrag(sourceInd, i(), sourceFilterGroupInd)} index={i()} setOpen={setItemOpen}/>
|
|
292
|
+
</Match>
|
|
293
|
+
<Match when={getFilterType(item) === "group"}>
|
|
294
|
+
<Modal.Trigger as={FilterGroupChip} filterGroup={item} size={props.size} onGroupDrag={(sourceInd, sourceFilterGroupInd) => props.onGroupDrag(sourceInd, i(), sourceFilterGroupInd)} index={i()} setOpen={setItemOpen}/>
|
|
295
|
+
</Match>
|
|
296
|
+
</Switch>
|
|
226
297
|
<Modal.Modal class="bg-base-200">
|
|
227
|
-
<
|
|
298
|
+
<Show when={getFilterType(item) === "group" || getFilterType(item) === "filter"} fallback={<EditAdvancedFilter size={props.size} filter={item} onSave={(updatedFilter) => {
|
|
299
|
+
props.onUpdateAdvancedFilter(i(), updatedFilter);
|
|
300
|
+
setItemOpen(false);
|
|
301
|
+
}}/>}>
|
|
302
|
+
<EditFilters size={props.size} availableFields={props.availableFields ?? []} onSaveFilters={(filters) => {
|
|
303
|
+
props.onUpdateFilterGroup(i(), filters);
|
|
304
|
+
setItemOpen(false);
|
|
305
|
+
}} currentFilters={getFilterType(item) === "group"
|
|
306
|
+
? item.filters
|
|
307
|
+
: [item]}/>
|
|
308
|
+
</Show>
|
|
228
309
|
</Modal.Modal>
|
|
229
310
|
</Modal>);
|
|
230
311
|
}}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Setter } from "solid-js";
|
|
2
|
-
import { Filter, FilterGroup } from "./FilterBar";
|
|
1
|
+
import { Component, Setter } from "solid-js";
|
|
2
|
+
import { AdvancedFilter, Filter, FilterGroup } from "./FilterBar";
|
|
3
3
|
interface FilterChipOrGroupProps {
|
|
4
|
-
onGroupDrag
|
|
4
|
+
onGroupDrag?: (sourceInd: number, sourceFilterGroupInd?: number) => void;
|
|
5
5
|
onDelete?: () => void;
|
|
6
6
|
size?: "xs" | "sm" | "md" | "lg" | "xl";
|
|
7
7
|
class?: string;
|
|
@@ -20,4 +20,8 @@ interface FilterGroupProps<T> extends FilterChipOrGroupProps {
|
|
|
20
20
|
onClick?: (filterGroup: FilterGroup<T>) => void;
|
|
21
21
|
}
|
|
22
22
|
export declare const FilterGroupChip: <T>(props: FilterGroupProps<T>) => import("solid-js").JSX.Element;
|
|
23
|
+
interface AdvancedFilterChipProps extends FilterChipOrGroupProps {
|
|
24
|
+
filter: AdvancedFilter;
|
|
25
|
+
}
|
|
26
|
+
export declare const AdvancedFilterChip: Component<AdvancedFilterChipProps>;
|
|
23
27
|
export default FilterChip;
|
|
@@ -2,6 +2,7 @@ import { createEffect, createMemo, createSignal, For, onCleanup } from "solid-js
|
|
|
2
2
|
import { dropTargetForElements, draggable } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
|
|
3
3
|
import invariant from "tiny-invariant";
|
|
4
4
|
import CloseIcon from "lucide-solid/icons/x";
|
|
5
|
+
import Sparkles from "lucide-solid/icons/sparkles";
|
|
5
6
|
import { tv } from "tailwind-variants";
|
|
6
7
|
import { filterLabels } from "./FilterBar";
|
|
7
8
|
import { Button } from "../Button";
|
|
@@ -39,9 +40,7 @@ export const FilterChip = (props) => {
|
|
|
39
40
|
return val.label;
|
|
40
41
|
}
|
|
41
42
|
else {
|
|
42
|
-
let valText = val
|
|
43
|
-
if (val.endDate)
|
|
44
|
-
valText += ` - ${val.endDate.toLocaleDateString("en-GB")}`;
|
|
43
|
+
let valText = val ? String(val.toLocaleDateString("en-GB")) : "";
|
|
45
44
|
return valText;
|
|
46
45
|
}
|
|
47
46
|
}
|
|
@@ -74,7 +73,7 @@ export const FilterChip = (props) => {
|
|
|
74
73
|
if (source.data.index === props.index)
|
|
75
74
|
return;
|
|
76
75
|
// add the target ind and group source ind if it exists
|
|
77
|
-
props.onGroupDrag(source.data.index, source.data.groupIndex);
|
|
76
|
+
props.onGroupDrag?.(source.data.index, source.data.groupIndex);
|
|
78
77
|
},
|
|
79
78
|
});
|
|
80
79
|
onCleanup(dispose);
|
|
@@ -167,7 +166,7 @@ export const FilterGroupChip = (props) => {
|
|
|
167
166
|
if (location.current.dropTargets[0].element !== ref)
|
|
168
167
|
return;
|
|
169
168
|
// add the target ind and group source ind if it exists
|
|
170
|
-
props.onGroupDrag(source.data.index, source.data.groupIndex);
|
|
169
|
+
props.onGroupDrag?.(source.data.index, source.data.groupIndex);
|
|
171
170
|
},
|
|
172
171
|
});
|
|
173
172
|
onCleanup(dispose);
|
|
@@ -217,4 +216,77 @@ export const FilterGroupChip = (props) => {
|
|
|
217
216
|
</Button>
|
|
218
217
|
</div>);
|
|
219
218
|
};
|
|
219
|
+
export const AdvancedFilterChip = (props) => {
|
|
220
|
+
let ref;
|
|
221
|
+
const [dragging, setDragging] = createSignal("idle");
|
|
222
|
+
createEffect(() => {
|
|
223
|
+
const element = ref;
|
|
224
|
+
invariant(element);
|
|
225
|
+
const dispose = dropTargetForElements({
|
|
226
|
+
element,
|
|
227
|
+
canDrop() {
|
|
228
|
+
return false;
|
|
229
|
+
},
|
|
230
|
+
onDragEnter() {
|
|
231
|
+
setDragging("dragging-over");
|
|
232
|
+
},
|
|
233
|
+
onDrag() {
|
|
234
|
+
if (dragging() !== "dragging-over") {
|
|
235
|
+
setDragging("dragging-over");
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
onDragLeave() {
|
|
239
|
+
setDragging("idle");
|
|
240
|
+
},
|
|
241
|
+
onDrop({ source }) {
|
|
242
|
+
setDragging("idle");
|
|
243
|
+
if (source.data.index === props.index)
|
|
244
|
+
return;
|
|
245
|
+
// add the target ind and group source ind if it exists
|
|
246
|
+
props.onGroupDrag?.(source.data.index, source.data.groupIndex);
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
onCleanup(dispose);
|
|
250
|
+
});
|
|
251
|
+
createEffect(() => {
|
|
252
|
+
const element = ref;
|
|
253
|
+
invariant(element);
|
|
254
|
+
const dispose = draggable({
|
|
255
|
+
element,
|
|
256
|
+
getInitialData() {
|
|
257
|
+
return {
|
|
258
|
+
isAdvancedFilterChip: true,
|
|
259
|
+
index: props.index,
|
|
260
|
+
isInGroup: false,
|
|
261
|
+
groupIndex: -1,
|
|
262
|
+
};
|
|
263
|
+
},
|
|
264
|
+
onDragStart() {
|
|
265
|
+
setDragging("dragging");
|
|
266
|
+
},
|
|
267
|
+
onDrop() {
|
|
268
|
+
setDragging("idle");
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
onCleanup(() => dispose());
|
|
272
|
+
});
|
|
273
|
+
const bgStyle = createMemo(() => {
|
|
274
|
+
if (dragging() === "dragging-over") {
|
|
275
|
+
return { "background-color": "color-mix(in srgb, var(--color-primary) 10%, transparent)" };
|
|
276
|
+
}
|
|
277
|
+
return {};
|
|
278
|
+
});
|
|
279
|
+
return (<div class={filterChip({ size: props.size, class: props.class })} ref={ref} style={{ opacity: dragging() === "dragging" ? 0.2 : 1, ...bgStyle() }} onClick={() => props.setOpen?.((prev) => !prev)}>
|
|
280
|
+
<div class="tooltip tooltip-bottom tooltip-neutral flex items-center">
|
|
281
|
+
<Sparkles class="w-[0.9em] h-[0.9em] mr-1"/>
|
|
282
|
+
<span>{String(props.filter.label)}</span>
|
|
283
|
+
<div class="inline tooltip-content">
|
|
284
|
+
<span class="font-bold">{String(props.filter.filter)}</span>
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
{props.onDelete && (<Button onClick={props.onDelete} size="xs" variant="ghost" class="p-0.5 m-0 h-min">
|
|
288
|
+
<CloseIcon class="w-[1em] h-[1em]" stroke-width={4}/>
|
|
289
|
+
</Button>)}
|
|
290
|
+
</div>);
|
|
291
|
+
};
|
|
220
292
|
export default FilterChip;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createMemo, createSignal, Match, Show, Switch } from "solid-js";
|
|
2
|
-
import { createStore } from "solid-js/store";
|
|
3
2
|
import X from "lucide-solid/icons/x";
|
|
4
3
|
import { filterDefaults, filterLabels, } from "./FilterBar";
|
|
5
4
|
import { Select } from "../Select";
|
|
@@ -17,9 +16,11 @@ export const FilterEdit = (props) => {
|
|
|
17
16
|
const [selectedTextValue, setSelectedTextValue] = createSignal(props.filter.field?.type === "text" ? props.filter.value : "");
|
|
18
17
|
const [selectedNumberValue, setSelectedNumberValue] = createSignal(props.filter.field?.type === "number" ? props.filter.value : 0);
|
|
19
18
|
const [selectedSelectValue, setSelectedSelectValue] = createSignal(props.filter.field?.type === "select" ? props.filter.value : null);
|
|
20
|
-
const [
|
|
19
|
+
const [selectedDateValue, setSelectedDateValue] = createSignal(props.filter.field?.type === "date"
|
|
21
20
|
? props.filter.value
|
|
22
|
-
|
|
21
|
+
? new Date(props.filter.value)
|
|
22
|
+
: null
|
|
23
|
+
: null);
|
|
23
24
|
const availableOperators = createMemo(() => props.filter.field
|
|
24
25
|
? Object.entries(filterLabels[props.filter.field.type]).map(([value, label]) => ({
|
|
25
26
|
label,
|
|
@@ -28,26 +29,17 @@ export const FilterEdit = (props) => {
|
|
|
28
29
|
: []);
|
|
29
30
|
const handleOperatorChange = (operator) => {
|
|
30
31
|
props.setOperator(operator);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return;
|
|
41
|
-
case "text":
|
|
42
|
-
props.setValue("");
|
|
43
|
-
default:
|
|
44
|
-
props.setValue(undefined);
|
|
45
|
-
}
|
|
46
|
-
props.setCanConfirm(false);
|
|
47
|
-
// hmm, for ux I don't want to clear too much when switching operator. will see how this feels.
|
|
48
|
-
// need to do this for canConfirm behaviour atm
|
|
49
|
-
setSelectedDateValues("endDate", null);
|
|
32
|
+
switch (props.filter.field?.type) {
|
|
33
|
+
case "number":
|
|
34
|
+
props.setValue(0);
|
|
35
|
+
props.setCanConfirm(true);
|
|
36
|
+
return;
|
|
37
|
+
case "text":
|
|
38
|
+
props.setValue("");
|
|
39
|
+
default:
|
|
40
|
+
props.setValue(undefined);
|
|
50
41
|
}
|
|
42
|
+
props.setCanConfirm(false);
|
|
51
43
|
};
|
|
52
44
|
const handleFieldChange = (f) => {
|
|
53
45
|
props.setField(f);
|
|
@@ -55,13 +47,8 @@ export const FilterEdit = (props) => {
|
|
|
55
47
|
handleOperatorChange(newOperator);
|
|
56
48
|
};
|
|
57
49
|
const canConfirm = (val) => {
|
|
58
|
-
if (!props.filter.field || !props.filter.operator || val === undefined || val === "")
|
|
59
|
-
return false;
|
|
60
|
-
if (props.filter.field?.type === "date" &&
|
|
61
|
-
props.filter.operator === "between" &&
|
|
62
|
-
(selectedDateValues.endDate === null || selectedDateValues.startDate === null)) {
|
|
50
|
+
if (!props.filter.field || !props.filter.operator || val === undefined || val === "" || val === null)
|
|
63
51
|
return false;
|
|
64
|
-
}
|
|
65
52
|
return true;
|
|
66
53
|
};
|
|
67
54
|
const handleValueChange = (val) => {
|
|
@@ -104,8 +91,7 @@ export const FilterEdit = (props) => {
|
|
|
104
91
|
{ label: "Is False", value: false },
|
|
105
92
|
]} size={props.size} class="min-w-50"/>
|
|
106
93
|
</Match>
|
|
107
|
-
<Match when={props.filter.field?.type === "text"
|
|
108
|
-
!["is_set", "is_not_set"].includes(props.filter.operator ?? "")}>
|
|
94
|
+
<Match when={props.filter.field?.type === "text"}>
|
|
109
95
|
<Input label="Value" value={selectedTextValue()} onChange={(val) => {
|
|
110
96
|
setSelectedTextValue(val);
|
|
111
97
|
handleValueChange(val);
|
|
@@ -117,8 +103,7 @@ export const FilterEdit = (props) => {
|
|
|
117
103
|
handleValueChange(val);
|
|
118
104
|
}} inputProps={{ class: "w-full" }} size={props.size} class="min-w-50"/>
|
|
119
105
|
</Match>
|
|
120
|
-
<Match when={props.filter.field?.type === "select"
|
|
121
|
-
!["is_set", "is_not_set"].includes(props.filter.operator ?? "")}>
|
|
106
|
+
<Match when={props.filter.field?.type === "select"}>
|
|
122
107
|
<Show when={["in", "not_in"].includes(props.filter.operator ?? "")} fallback={<Select value={selectedSelectValue()} label="Value" labelKey="label" valueKey="value" onChange={(val) => {
|
|
123
108
|
setSelectedSelectValue(val);
|
|
124
109
|
handleValueChange(val);
|
|
@@ -129,28 +114,11 @@ export const FilterEdit = (props) => {
|
|
|
129
114
|
}} size={props.size} class="min-w-50"/>
|
|
130
115
|
</Show>
|
|
131
116
|
</Match>
|
|
132
|
-
<Match when={props.filter.field?.type === "date"
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (val && !(props.filter.operator === "between" && selectedDateValues.endDate !== null)) {
|
|
138
|
-
newVal = { ...selectedDateValues };
|
|
139
|
-
}
|
|
140
|
-
handleValueChange(newVal);
|
|
141
|
-
}} max={selectedDateValues.endDate ? selectedDateValues.endDate.toISOString().slice(0, 10) : undefined} label={props.filter.operator === "between" ? "Start" : ""} size={props.size} class="min-w-50"/>
|
|
142
|
-
<Show when={props.filter.operator === "between"}>
|
|
143
|
-
<DateInput value={selectedDateValues.endDate} label="End" min={selectedDateValues.startDate
|
|
144
|
-
? selectedDateValues.startDate.toISOString().slice(0, 10)
|
|
145
|
-
: undefined} onChange={(val) => {
|
|
146
|
-
setSelectedDateValues("endDate", val);
|
|
147
|
-
let newVal = undefined;
|
|
148
|
-
if (val && selectedDateValues.startDate !== null) {
|
|
149
|
-
newVal = { ...selectedDateValues };
|
|
150
|
-
}
|
|
151
|
-
handleValueChange(newVal);
|
|
152
|
-
}} size={props.size} class="min-w-50"/>
|
|
153
|
-
</Show>
|
|
117
|
+
<Match when={props.filter.field?.type === "date"}>
|
|
118
|
+
<DateInput value={selectedDateValue()} onChange={(val) => {
|
|
119
|
+
setSelectedDateValue(val);
|
|
120
|
+
handleValueChange(val);
|
|
121
|
+
}} label="Date" size={props.size} class="min-w-50"/>
|
|
154
122
|
</Match>
|
|
155
123
|
</Switch>
|
|
156
124
|
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JSXElement } from "solid-js";
|
|
1
|
+
import { JSXElement, Setter } from "solid-js";
|
|
2
2
|
import { type SwitchProps } from "./Switch";
|
|
3
3
|
import { type SelectProps } from "./Select";
|
|
4
4
|
import { type InputRootProps } from "./Input";
|
|
@@ -11,6 +11,7 @@ import { type FileInputProps } from "./FileInput";
|
|
|
11
11
|
import { RelationPickerProps } from "./RelationPicker";
|
|
12
12
|
export interface FormProps<T> {
|
|
13
13
|
data: Partial<T>;
|
|
14
|
+
setData: Setter<Partial<T>>;
|
|
14
15
|
title?: string;
|
|
15
16
|
onSave?: (values: Partial<T>) => Promise<void>;
|
|
16
17
|
onCancel?: () => void;
|
package/dist/components/Form.jsx
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { splitProps } from "solid-js";
|
|
2
|
-
import { createStore } from "solid-js/store";
|
|
3
2
|
import { tv } from "tailwind-variants";
|
|
4
3
|
import { InternalFormContext, useInternalFormContext } from "./formContext";
|
|
5
4
|
import { Switch } from "./Switch";
|
|
@@ -18,17 +17,16 @@ const formClass = tv({
|
|
|
18
17
|
});
|
|
19
18
|
export function createForm() {
|
|
20
19
|
const Form = (props) => {
|
|
21
|
-
const [values, setValues] = createStore({ ...props.data });
|
|
22
20
|
const setValue = (key, value) => {
|
|
23
|
-
|
|
21
|
+
props.setData((prev) => ({ ...prev, [key]: value }));
|
|
24
22
|
};
|
|
25
23
|
const getValue = (key) => {
|
|
26
|
-
return
|
|
24
|
+
return props.data[key];
|
|
27
25
|
};
|
|
28
26
|
const contextValue = { setValue, getValue };
|
|
29
27
|
const handleSubmit = (e) => {
|
|
30
28
|
e.preventDefault();
|
|
31
|
-
props.onSave?.(
|
|
29
|
+
props.onSave?.(props.data);
|
|
32
30
|
};
|
|
33
31
|
return (<InternalFormContext.Provider value={contextValue}>
|
|
34
32
|
<form onSubmit={handleSubmit} class={formClass({ class: props.class })}>
|
|
@@ -94,18 +92,7 @@ export function createForm() {
|
|
|
94
92
|
return (<Slider {...props} value={form.getValue(props.field)} onChange={(v) => form.setValue(props.field, v)}/>);
|
|
95
93
|
};
|
|
96
94
|
const RelationField = (props) => {
|
|
97
|
-
|
|
98
|
-
const [local, others] = splitProps(props, ["onChange"]);
|
|
99
|
-
const handleChange = (val) => {
|
|
100
|
-
if (props.multi) {
|
|
101
|
-
form.setValue(props.field, (Array.isArray(val) ? val.map((v) => v.id) : []));
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
form.setValue(props.field, (val?.id || null));
|
|
105
|
-
}
|
|
106
|
-
local.onChange?.(val);
|
|
107
|
-
};
|
|
108
|
-
return <RelationPicker {...others} onChange={handleChange}/>;
|
|
95
|
+
return <RelationPicker {...props}/>;
|
|
109
96
|
};
|
|
110
97
|
Form.TextField = TextField;
|
|
111
98
|
Form.NumberField = NumberField;
|
|
@@ -121,14 +121,7 @@ export const RelationPicker = (props) => {
|
|
|
121
121
|
{!props.multi && values() && (props.href || props.onLinkClick) && (<a class="btn btn-ghost btn-primary btn-xs btn-square" href={props.href} onClick={() => props.onLinkClick?.(props.value)}>
|
|
122
122
|
<Link class="w-[1em] h-[1em]"/>
|
|
123
123
|
</a>)}
|
|
124
|
-
<Combobox.Input onBlur={(e) => {
|
|
125
|
-
if (!props.value) {
|
|
126
|
-
e.currentTarget.value = "";
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
e.currentTarget.value = String(state.selectedOptions()[0][props.labelKey]);
|
|
130
|
-
}
|
|
131
|
-
}} ref={inputRef} onInput={(e) => props.onTextInputChange?.(e.currentTarget.value)} onKeyDown={handleKeyDown}/>
|
|
124
|
+
<Combobox.Input onBlur={(e) => (e.currentTarget.value = "")} ref={inputRef} onInput={(e) => props.onTextInputChange?.(e.currentTarget.value)} onKeyDown={handleKeyDown}/>
|
|
132
125
|
</>}>
|
|
133
126
|
<div class="flex flex-wrap gap-1 w-full">
|
|
134
127
|
<For each={state.selectedOptions()}>
|
|
@@ -7,8 +7,8 @@ import { createSolidTable, flexRender, getCoreRowModel } from "@tanstack/solid-t
|
|
|
7
7
|
import { tv } from "tailwind-variants";
|
|
8
8
|
import Loader from "lucide-solid/icons/loader";
|
|
9
9
|
import GripVertical from "lucide-solid/icons/grip-vertical";
|
|
10
|
-
import { triggerFlash } from "
|
|
11
|
-
import { iconSize } from "
|
|
10
|
+
import { triggerFlash } from "../methods/triggerFlash";
|
|
11
|
+
import { iconSize } from "../constants";
|
|
12
12
|
const tableClass = tv({
|
|
13
13
|
base: "table table-pin-rows",
|
|
14
14
|
variants: {
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./Table";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./Table";
|
|
File without changes
|