@kopexa/filter 0.0.2

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.
Files changed (51) hide show
  1. package/LICENSE +201 -0
  2. package/dist/chunk-3WNAREG6.mjs +29 -0
  3. package/dist/chunk-63P4ITVP.mjs +263 -0
  4. package/dist/chunk-7KY2PDNL.mjs +136 -0
  5. package/dist/chunk-7QP7FRID.mjs +47 -0
  6. package/dist/chunk-EF4VI36D.mjs +36 -0
  7. package/dist/chunk-I3Z2T4N2.mjs +14 -0
  8. package/dist/chunk-PTJ7HZPA.mjs +164 -0
  9. package/dist/chunk-TBHYZZSX.mjs +139 -0
  10. package/dist/chunk-URDCG5NI.mjs +111 -0
  11. package/dist/filter-active.d.mts +13 -0
  12. package/dist/filter-active.d.ts +13 -0
  13. package/dist/filter-active.js +538 -0
  14. package/dist/filter-active.mjs +12 -0
  15. package/dist/filter-context.d.mts +28 -0
  16. package/dist/filter-context.d.ts +28 -0
  17. package/dist/filter-context.js +39 -0
  18. package/dist/filter-context.mjs +10 -0
  19. package/dist/filter-i18n.d.mts +20 -0
  20. package/dist/filter-i18n.d.ts +20 -0
  21. package/dist/filter-i18n.js +52 -0
  22. package/dist/filter-i18n.mjs +7 -0
  23. package/dist/filter-menu.d.mts +65 -0
  24. package/dist/filter-menu.d.ts +65 -0
  25. package/dist/filter-menu.js +169 -0
  26. package/dist/filter-menu.mjs +15 -0
  27. package/dist/filter-trigger.d.mts +14 -0
  28. package/dist/filter-trigger.d.ts +14 -0
  29. package/dist/filter-trigger.js +170 -0
  30. package/dist/filter-trigger.mjs +10 -0
  31. package/dist/filter-types.d.mts +60 -0
  32. package/dist/filter-types.d.ts +60 -0
  33. package/dist/filter-types.js +72 -0
  34. package/dist/filter-types.mjs +11 -0
  35. package/dist/filter-value-editor.d.mts +11 -0
  36. package/dist/filter-value-editor.d.ts +11 -0
  37. package/dist/filter-value-editor.js +414 -0
  38. package/dist/filter-value-editor.mjs +11 -0
  39. package/dist/filter.d.mts +24 -0
  40. package/dist/filter.d.ts +24 -0
  41. package/dist/filter.js +242 -0
  42. package/dist/filter.mjs +11 -0
  43. package/dist/index.d.mts +13 -0
  44. package/dist/index.d.ts +13 -0
  45. package/dist/index.js +909 -0
  46. package/dist/index.mjs +39 -0
  47. package/dist/messages.d.mts +104 -0
  48. package/dist/messages.d.ts +104 -0
  49. package/dist/messages.js +134 -0
  50. package/dist/messages.mjs +7 -0
  51. package/package.json +69 -0
@@ -0,0 +1,36 @@
1
+ "use client";
2
+ import {
3
+ messages
4
+ } from "./chunk-URDCG5NI.mjs";
5
+ import {
6
+ useFilterContext
7
+ } from "./chunk-I3Z2T4N2.mjs";
8
+
9
+ // src/filter-trigger.tsx
10
+ import { DropdownMenu } from "@kopexa/dropdown-menu";
11
+ import { useSafeIntl } from "@kopexa/i18n";
12
+ import { PlusIcon } from "@kopexa/icons";
13
+ import { cn } from "@kopexa/shared-utils";
14
+ import { jsx, jsxs } from "react/jsx-runtime";
15
+ function FilterTrigger(props) {
16
+ const { className, children, icon, ...rest } = props;
17
+ const { styles } = useFilterContext();
18
+ const t = useSafeIntl();
19
+ return /* @__PURE__ */ jsxs(
20
+ DropdownMenu.Trigger,
21
+ {
22
+ "data-slot": "filter-trigger",
23
+ className: cn(styles.trigger(), className),
24
+ ...rest,
25
+ children: [
26
+ /* @__PURE__ */ jsx("span", { className: styles.triggerIcon(), children: icon != null ? icon : /* @__PURE__ */ jsx(PlusIcon, { className: "size-4" }) }),
27
+ children != null ? children : t.formatMessage(messages.add_filter)
28
+ ]
29
+ }
30
+ );
31
+ }
32
+ FilterTrigger.displayName = "FilterTrigger";
33
+
34
+ export {
35
+ FilterTrigger
36
+ };
@@ -0,0 +1,14 @@
1
+ "use client";
2
+
3
+ // src/filter-context.tsx
4
+ import { createContext } from "@kopexa/react-utils";
5
+ var [FilterProvider, useFilterContext] = createContext({
6
+ name: "FilterContext",
7
+ strict: true,
8
+ errorMessage: "useFilterContext must be used within a Filter component. Make sure to wrap your FilterMenu, FilterTrigger, etc. inside a <Filter> component."
9
+ });
10
+
11
+ export {
12
+ FilterProvider,
13
+ useFilterContext
14
+ };
@@ -0,0 +1,164 @@
1
+ "use client";
2
+ import {
3
+ DEFAULT_I18N
4
+ } from "./chunk-3WNAREG6.mjs";
5
+ import {
6
+ FilterProvider
7
+ } from "./chunk-I3Z2T4N2.mjs";
8
+ import {
9
+ getDefaultOperator
10
+ } from "./chunk-7QP7FRID.mjs";
11
+
12
+ // src/filter.tsx
13
+ import { DropdownMenu } from "@kopexa/dropdown-menu";
14
+ import { cn } from "@kopexa/shared-utils";
15
+ import { filter as filterTheme } from "@kopexa/theme";
16
+ import * as React from "react";
17
+ import { jsx } from "react/jsx-runtime";
18
+ function Filter(props) {
19
+ const {
20
+ value = [],
21
+ onChange,
22
+ allowMultiple = true,
23
+ size = "md",
24
+ variant = "outline",
25
+ i18n,
26
+ className,
27
+ children,
28
+ ...rest
29
+ } = props;
30
+ const mergedI18n = React.useMemo(
31
+ () => ({
32
+ ...DEFAULT_I18N,
33
+ ...i18n,
34
+ operators: {
35
+ ...DEFAULT_I18N.operators,
36
+ ...i18n == null ? void 0 : i18n.operators
37
+ }
38
+ }),
39
+ [i18n]
40
+ );
41
+ const styles = React.useMemo(
42
+ () => filterTheme({ size, variant }),
43
+ [size, variant]
44
+ );
45
+ const [fields, setFields] = React.useState(
46
+ /* @__PURE__ */ new Map()
47
+ );
48
+ const [menuOpen, setMenuOpen] = React.useState(false);
49
+ const [editingFilterId, setEditingFilterId] = React.useState(
50
+ null
51
+ );
52
+ const registerField = React.useCallback((field) => {
53
+ setFields((prev) => {
54
+ const next = new Map(prev);
55
+ next.set(field.id, field);
56
+ return next;
57
+ });
58
+ }, []);
59
+ const unregisterField = React.useCallback((fieldId) => {
60
+ setFields((prev) => {
61
+ const next = new Map(prev);
62
+ next.delete(fieldId);
63
+ return next;
64
+ });
65
+ }, []);
66
+ const updateFilter = React.useCallback(
67
+ (filterId, updates) => {
68
+ const newFilters = value.map((filter) => {
69
+ if (filter.id === filterId) {
70
+ const updated = { ...filter, ...updates };
71
+ if (updates.operator === "is_empty" || updates.operator === "is_not_empty") {
72
+ updated.value = void 0;
73
+ }
74
+ return updated;
75
+ }
76
+ return filter;
77
+ });
78
+ onChange == null ? void 0 : onChange(newFilters);
79
+ },
80
+ [value, onChange]
81
+ );
82
+ const removeFilter = React.useCallback(
83
+ (filterId) => {
84
+ onChange == null ? void 0 : onChange(value.filter((f) => f.id !== filterId));
85
+ },
86
+ [value, onChange]
87
+ );
88
+ const clearFilters = React.useCallback(() => {
89
+ onChange == null ? void 0 : onChange([]);
90
+ }, [onChange]);
91
+ const addFilter = React.useCallback(
92
+ (fieldKey) => {
93
+ const field = fields.get(fieldKey);
94
+ if (!field) return;
95
+ const defaultOperator = getDefaultOperator(field.type);
96
+ const newFilter = {
97
+ id: `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
98
+ fieldId: fieldKey,
99
+ operator: defaultOperator,
100
+ value: void 0
101
+ };
102
+ onChange == null ? void 0 : onChange([...value, newFilter]);
103
+ },
104
+ [fields, value, onChange]
105
+ );
106
+ const contextValue = React.useMemo(
107
+ () => ({
108
+ // State
109
+ value,
110
+ fields,
111
+ allowMultiple,
112
+ // Style
113
+ styles,
114
+ size: size != null ? size : "md",
115
+ variant: variant != null ? variant : "outline",
116
+ i18n: mergedI18n,
117
+ // Actions
118
+ addFilter,
119
+ updateFilter,
120
+ removeFilter,
121
+ clearFilters,
122
+ // Field registration
123
+ registerField,
124
+ unregisterField,
125
+ // Menu state
126
+ menuOpen,
127
+ setMenuOpen,
128
+ // Editor state
129
+ editingFilterId,
130
+ setEditingFilterId
131
+ }),
132
+ [
133
+ value,
134
+ fields,
135
+ allowMultiple,
136
+ styles,
137
+ size,
138
+ variant,
139
+ mergedI18n,
140
+ addFilter,
141
+ updateFilter,
142
+ removeFilter,
143
+ clearFilters,
144
+ registerField,
145
+ unregisterField,
146
+ menuOpen,
147
+ editingFilterId
148
+ ]
149
+ );
150
+ return /* @__PURE__ */ jsx(FilterProvider, { value: contextValue, children: /* @__PURE__ */ jsx(DropdownMenu.Root, { open: menuOpen, onOpenChange: setMenuOpen, children: /* @__PURE__ */ jsx(
151
+ "div",
152
+ {
153
+ "data-slot": "filter",
154
+ className: cn(styles.root(), className),
155
+ ...rest,
156
+ children
157
+ }
158
+ ) }) });
159
+ }
160
+ Filter.displayName = "Filter";
161
+
162
+ export {
163
+ Filter
164
+ };
@@ -0,0 +1,139 @@
1
+ "use client";
2
+ import {
3
+ useFilterContext
4
+ } from "./chunk-I3Z2T4N2.mjs";
5
+
6
+ // src/filter-menu.tsx
7
+ import { DropdownMenu } from "@kopexa/dropdown-menu";
8
+ import { cn } from "@kopexa/shared-utils";
9
+ import {
10
+ createContext,
11
+ useContext,
12
+ useLayoutEffect
13
+ } from "react";
14
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
15
+ function FilterMenu(props) {
16
+ var _a;
17
+ const { children, className } = props;
18
+ const { fields, styles, addFilter, allowMultiple, value } = useFilterContext();
19
+ const fieldGroups = /* @__PURE__ */ new Map();
20
+ for (const [id, field] of fields) {
21
+ const group = field.group;
22
+ if (!fieldGroups.has(group)) {
23
+ fieldGroups.set(group, /* @__PURE__ */ new Map());
24
+ }
25
+ (_a = fieldGroups.get(group)) == null ? void 0 : _a.set(id, field);
26
+ }
27
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
28
+ /* @__PURE__ */ jsx("div", { style: { display: "none" }, "aria-hidden": "true", children }),
29
+ /* @__PURE__ */ jsx(
30
+ DropdownMenu.Content,
31
+ {
32
+ "data-slot": "filter-menu",
33
+ className: cn("min-w-[220px]", className),
34
+ align: "start",
35
+ children: fields.size === 0 ? /* @__PURE__ */ jsx("div", { className: "px-2 py-3 text-sm text-muted-foreground text-center", children: "Loading..." }) : Array.from(fieldGroups.entries()).map(([groupLabel, groupFields]) => /* @__PURE__ */ jsxs(DropdownMenu.Group, { children: [
36
+ groupLabel && /* @__PURE__ */ jsx(DropdownMenu.Label, { children: groupLabel }),
37
+ Array.from(groupFields.values()).map((field) => {
38
+ const hasFilter = value.some((f) => f.fieldId === field.id);
39
+ const isDisabled = field.disabled || !allowMultiple && hasFilter;
40
+ return /* @__PURE__ */ jsxs(
41
+ DropdownMenu.Item,
42
+ {
43
+ "data-slot": "filter-item",
44
+ disabled: isDisabled,
45
+ onSelect: () => addFilter(field.id),
46
+ children: [
47
+ field.icon && /* @__PURE__ */ jsx("span", { className: styles.menuItemIcon(), children: field.icon }),
48
+ /* @__PURE__ */ jsx("span", { children: field.label })
49
+ ]
50
+ },
51
+ field.id
52
+ );
53
+ })
54
+ ] }, groupLabel != null ? groupLabel : "ungrouped"))
55
+ }
56
+ )
57
+ ] });
58
+ }
59
+ FilterMenu.displayName = "FilterMenu";
60
+ function FilterGroup(props) {
61
+ const { label, children } = props;
62
+ return /* @__PURE__ */ jsx(FilterGroupProvider, { label, children });
63
+ }
64
+ FilterGroup.displayName = "FilterGroup";
65
+ var FilterGroupContext = createContext(void 0);
66
+ function FilterGroupProvider({
67
+ label,
68
+ children
69
+ }) {
70
+ return /* @__PURE__ */ jsx(FilterGroupContext.Provider, { value: label, children });
71
+ }
72
+ function useFilterGroup() {
73
+ return useContext(FilterGroupContext);
74
+ }
75
+ function FilterItem(props) {
76
+ const {
77
+ id,
78
+ label,
79
+ icon,
80
+ type,
81
+ options,
82
+ operators,
83
+ placeholder,
84
+ min,
85
+ max,
86
+ step,
87
+ searchable,
88
+ disabled
89
+ } = props;
90
+ const { registerField, unregisterField } = useFilterContext();
91
+ const group = useFilterGroup();
92
+ useLayoutEffect(() => {
93
+ registerField({
94
+ id,
95
+ label,
96
+ icon,
97
+ type,
98
+ options,
99
+ operators,
100
+ placeholder,
101
+ min,
102
+ max,
103
+ step,
104
+ searchable,
105
+ group,
106
+ disabled
107
+ });
108
+ return () => unregisterField(id);
109
+ }, [
110
+ id,
111
+ label,
112
+ icon,
113
+ type,
114
+ options,
115
+ operators,
116
+ placeholder,
117
+ min,
118
+ max,
119
+ step,
120
+ searchable,
121
+ group,
122
+ disabled,
123
+ registerField,
124
+ unregisterField
125
+ ]);
126
+ return null;
127
+ }
128
+ FilterItem.displayName = "FilterItem";
129
+ function FilterMenuSeparator() {
130
+ return /* @__PURE__ */ jsx(DropdownMenu.Separator, { "data-slot": "filter-menu-separator" });
131
+ }
132
+ FilterMenuSeparator.displayName = "FilterMenuSeparator";
133
+
134
+ export {
135
+ FilterMenu,
136
+ FilterGroup,
137
+ FilterItem,
138
+ FilterMenuSeparator
139
+ };
@@ -0,0 +1,111 @@
1
+ "use client";
2
+
3
+ // src/messages.ts
4
+ import { defineMessages } from "@kopexa/i18n";
5
+ var messages = defineMessages({
6
+ add_filter: {
7
+ id: "filter.add_filter",
8
+ defaultMessage: "Add Filter",
9
+ description: "Button text for adding a new filter"
10
+ },
11
+ clear_all: {
12
+ id: "filter.clear_all",
13
+ defaultMessage: "Clear all",
14
+ description: "Button text for clearing all filters"
15
+ },
16
+ no_fields: {
17
+ id: "filter.no_fields",
18
+ defaultMessage: "No filter options available",
19
+ description: "Message when no filter fields are configured"
20
+ },
21
+ apply: {
22
+ id: "filter.apply",
23
+ defaultMessage: "Apply",
24
+ description: "Button text for applying a filter value"
25
+ },
26
+ cancel: {
27
+ id: "filter.cancel",
28
+ defaultMessage: "Cancel",
29
+ description: "Button text for canceling filter editing"
30
+ },
31
+ select_value: {
32
+ id: "filter.select_value",
33
+ defaultMessage: "Select value...",
34
+ description: "Placeholder for value selection"
35
+ },
36
+ enter_value: {
37
+ id: "filter.enter_value",
38
+ defaultMessage: "Enter value...",
39
+ description: "Placeholder for value input"
40
+ },
41
+ // Operators
42
+ op_equals: {
43
+ id: "filter.operator.equals",
44
+ defaultMessage: "is",
45
+ description: "Operator: equals"
46
+ },
47
+ op_not_equals: {
48
+ id: "filter.operator.not_equals",
49
+ defaultMessage: "is not",
50
+ description: "Operator: not equals"
51
+ },
52
+ op_contains: {
53
+ id: "filter.operator.contains",
54
+ defaultMessage: "contains",
55
+ description: "Operator: contains"
56
+ },
57
+ op_not_contains: {
58
+ id: "filter.operator.not_contains",
59
+ defaultMessage: "does not contain",
60
+ description: "Operator: does not contain"
61
+ },
62
+ op_starts_with: {
63
+ id: "filter.operator.starts_with",
64
+ defaultMessage: "starts with",
65
+ description: "Operator: starts with"
66
+ },
67
+ op_ends_with: {
68
+ id: "filter.operator.ends_with",
69
+ defaultMessage: "ends with",
70
+ description: "Operator: ends with"
71
+ },
72
+ op_gt: {
73
+ id: "filter.operator.gt",
74
+ defaultMessage: "greater than",
75
+ description: "Operator: greater than"
76
+ },
77
+ op_lt: {
78
+ id: "filter.operator.lt",
79
+ defaultMessage: "less than",
80
+ description: "Operator: less than"
81
+ },
82
+ op_gte: {
83
+ id: "filter.operator.gte",
84
+ defaultMessage: "greater or equal",
85
+ description: "Operator: greater than or equal"
86
+ },
87
+ op_lte: {
88
+ id: "filter.operator.lte",
89
+ defaultMessage: "less or equal",
90
+ description: "Operator: less than or equal"
91
+ },
92
+ op_between: {
93
+ id: "filter.operator.between",
94
+ defaultMessage: "between",
95
+ description: "Operator: between"
96
+ },
97
+ op_is_empty: {
98
+ id: "filter.operator.is_empty",
99
+ defaultMessage: "is empty",
100
+ description: "Operator: is empty"
101
+ },
102
+ op_is_not_empty: {
103
+ id: "filter.operator.is_not_empty",
104
+ defaultMessage: "is not empty",
105
+ description: "Operator: is not empty"
106
+ }
107
+ });
108
+
109
+ export {
110
+ messages
111
+ };
@@ -0,0 +1,13 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ComponentProps } from 'react';
3
+
4
+ interface FilterActiveProps extends ComponentProps<"div"> {
5
+ /** Show clear all button */
6
+ showClearAll?: boolean;
7
+ }
8
+ declare function FilterActive(props: FilterActiveProps): react_jsx_runtime.JSX.Element | null;
9
+ declare namespace FilterActive {
10
+ var displayName: string;
11
+ }
12
+
13
+ export { FilterActive, type FilterActiveProps };
@@ -0,0 +1,13 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ComponentProps } from 'react';
3
+
4
+ interface FilterActiveProps extends ComponentProps<"div"> {
5
+ /** Show clear all button */
6
+ showClearAll?: boolean;
7
+ }
8
+ declare function FilterActive(props: FilterActiveProps): react_jsx_runtime.JSX.Element | null;
9
+ declare namespace FilterActive {
10
+ var displayName: string;
11
+ }
12
+
13
+ export { FilterActive, type FilterActiveProps };