@l3mpire/ui 2.12.0 → 2.14.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/USAGE.md +38 -1
- package/dist/index.d.mts +45 -9
- package/dist/index.d.ts +45 -9
- package/dist/index.js +2769 -2095
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2254 -1573
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
package/USAGE.md
CHANGED
|
@@ -941,7 +941,10 @@ import {
|
|
|
941
941
|
| `InteractiveFilterChip` | FilterChip with per-segment popovers (property, operator, value, kebab) |
|
|
942
942
|
| `PropertySelector` | 2-level popover: categories → properties with search |
|
|
943
943
|
| `AdvancedChip` | Split button "Advanced filters (N)" + close (outlined neutral) |
|
|
944
|
-
| `AdvancedPopover` | Popover with Where/And/Or rows, property/operator selects
|
|
944
|
+
| `AdvancedPopover` | Popover with Where/And/Or rows + nested groups, property/operator/value selects |
|
|
945
|
+
| `AdvancedRow` | Single condition row (property + operator + type-aware value popover + actions) |
|
|
946
|
+
| `AdvancedGroup` | Nested group container with shared connector and inline property selector |
|
|
947
|
+
| `FilterNodeActions` | Kebab menu shared by rows and groups (Duplicate / Turn into group↔filter / Delete) |
|
|
945
948
|
| `SaveViewButton` | Split button (Save view + dropdown chevron) |
|
|
946
949
|
| `SummaryChip` | Minimal mode: "Filters (N)" with interactive popover |
|
|
947
950
|
|
|
@@ -954,8 +957,35 @@ import {
|
|
|
954
957
|
|
|
955
958
|
**And/Or toggle:** each row in the advanced popover has an independent And/Or toggle button. The choice is **persisted on each `FilterCondition` as `logicOperator: "and" | "or"`** (defaults to `"and"` when undefined), so it survives remounts and can be serialized.
|
|
956
959
|
|
|
960
|
+
**Nested groups:** the advanced popover supports arbitrarily-nested `FilterGroup` nodes via `FilterNode = FilterCondition | FilterGroup`. Each row's kebab menu (`FilterNodeActions`) lets users **Duplicate**, **Turn into group** / **Turn into filter**, or **Delete**. Groups share a single connector (`Where`/`And`/`Or`) for all their children and render an inline `PropertySelector` to add new conditions. Tree mutations go through the immutable `updateNodeInTree` / `removeNodeFromTree` / `insertAfterInTree` / `replaceNodeInTree` helpers in `utils`.
|
|
961
|
+
|
|
962
|
+
**Type-aware value inputs:** every `AdvancedRow` value slot renders the same `<ValueInput>` used by `InteractiveFilterChip` — text → text input, number → number / range, date → calendar, enum/tags → single/multi select with `dynamicOptions` support, etc. The trigger button shows the formatted current value (via `formatFilterValue`) and a `+N` badge for multi-selections (via `getBadgeCount`).
|
|
963
|
+
|
|
957
964
|
**Advanced filter shortcut:** the PropertySelector footer shows an "Advanced filter · N rule(s)" item. Clicking it closes the property selector and opens the advanced popover directly in its initial empty state — where the user picks the first property inline via a "Where | [Select property ▾]" draft row. When no advanced filters exist yet, clicking this shortcut makes the AdvancedChip appear with the popover already open; closing without adding a filter removes the chip automatically.
|
|
958
965
|
|
|
966
|
+
**Dynamic options ("Me", "Unassigned", …):** enum/tags/relation properties accept a `dynamicOptions` array. Each entry is `{ value, label, description?, icon? }` and is rendered at the top of the SingleSelect / MultiSelect dropdown with a divider separating it from the regular options. The `value` is a sentinel string stored on `FilterCondition.value` — the DS only renders, the consuming app resolves it at query time (e.g. `"__me__"` → `currentUser.id`). This keeps session/business logic out of the DS while still getting a consistent visual treatment.
|
|
967
|
+
|
|
968
|
+
```tsx
|
|
969
|
+
{
|
|
970
|
+
id: "contact_owner",
|
|
971
|
+
label: "Contact owner",
|
|
972
|
+
type: "enum",
|
|
973
|
+
icon: faUserOutline,
|
|
974
|
+
group: "contact",
|
|
975
|
+
groupLabel: "Contact",
|
|
976
|
+
options: ["Quentin", "Simon", "Philippe"],
|
|
977
|
+
dynamicOptions: [
|
|
978
|
+
{
|
|
979
|
+
value: "__me__",
|
|
980
|
+
label: "Me",
|
|
981
|
+
description: "This value is dynamically applied to the current user",
|
|
982
|
+
icon: faBoltOutline,
|
|
983
|
+
},
|
|
984
|
+
{ value: "__deactivated__", label: "All deactivated and removed owners", icon: faBoltOutline },
|
|
985
|
+
],
|
|
986
|
+
}
|
|
987
|
+
```
|
|
988
|
+
|
|
959
989
|
| Utility | Purpose |
|
|
960
990
|
|---|---|
|
|
961
991
|
| `OPERATORS_BY_TYPE` | Operators for each DataType |
|
|
@@ -963,6 +993,13 @@ import {
|
|
|
963
993
|
| `createFilterWithDefaults(propertyId, type)` | Creates FilterCondition with default operator |
|
|
964
994
|
| `isNoValueOperator(op)` | True for is empty / is not empty / is true / is false |
|
|
965
995
|
| `getValueInputType(type, op)` | Returns the input component name for a type+operator combo |
|
|
996
|
+
| `formatFilterValue(value)` | Stringifies a `FilterValue` for display (handles Date, ranges, arrays, booleans) |
|
|
997
|
+
| `getBadgeCount(value)` | Returns `+N` count for multi-value selections, `undefined` otherwise |
|
|
998
|
+
| `createEmptyGroup()` / `isFilterGroup(node)` | Tree helpers for nested groups |
|
|
999
|
+
| `wrapInGroup(condition)` / `unwrapGroup(group)` | Convert between condition and single-child group |
|
|
1000
|
+
| `duplicateNode(node)` | Deep clone a node tree with fresh UUIDs |
|
|
1001
|
+
| `updateNodeInTree` / `removeNodeFromTree` / `insertAfterInTree` / `replaceNodeInTree` | Immutable tree mutations by id |
|
|
1002
|
+
| `countConditions(nodes)` | Count all leaf conditions in a tree |
|
|
966
1003
|
|
|
967
1004
|
---
|
|
968
1005
|
|
package/dist/index.d.mts
CHANGED
|
@@ -676,12 +676,28 @@ declare const FilterChip: React.ForwardRefExoticComponent<FilterChipProps & Reac
|
|
|
676
676
|
type DataType = "text" | "number" | "date" | "enum" | "tags" | "boolean" | "relation";
|
|
677
677
|
type TextOperator = "contains" | "does not contain" | "is" | "is not" | "starts with" | "ends with" | "is empty" | "is not empty";
|
|
678
678
|
type NumberOperator = "=" | "≠" | ">" | "<" | "≥" | "≤" | "is between" | "is empty" | "is not empty";
|
|
679
|
-
type DateOperator = "is" | "is before" | "is after" | "is on or before" | "is on or after" | "is between" | "is
|
|
679
|
+
type DateOperator = "is" | "is before" | "is after" | "is on or before" | "is on or after" | "is between" | "is empty" | "is not empty";
|
|
680
680
|
type EnumOperator = "is" | "is not" | "is any of" | "is none of" | "is empty" | "is not empty";
|
|
681
681
|
type TagsOperator = "contains" | "does not contain" | "contains any of" | "contains all of" | "is empty" | "is not empty";
|
|
682
682
|
type BooleanOperator = "is true" | "is false";
|
|
683
683
|
type RelationOperator = "is" | "is not" | "is any of" | "is none of" | "is empty" | "is not empty";
|
|
684
684
|
type OperatorType = TextOperator | NumberOperator | DateOperator | EnumOperator | TagsOperator | BooleanOperator | RelationOperator;
|
|
685
|
+
/**
|
|
686
|
+
* A "smart" / dynamic option shown at the top of a relation or enum selector
|
|
687
|
+
* (e.g. "Me", "My team", "Unassigned"). The value is a sentinel string that
|
|
688
|
+
* the consuming app resolves at query time (e.g. `__me__` → `currentUser.id`).
|
|
689
|
+
*
|
|
690
|
+
* The design system only handles the rendering (icon + label + description +
|
|
691
|
+
* divider above the regular options). Resolving the sentinels is an app
|
|
692
|
+
* concern.
|
|
693
|
+
*/
|
|
694
|
+
interface DynamicOption {
|
|
695
|
+
/** Sentinel value stored on FilterCondition.value (e.g. "__me__"). */
|
|
696
|
+
value: string;
|
|
697
|
+
label: string;
|
|
698
|
+
description?: string;
|
|
699
|
+
icon?: _l3mpire_icons.IconDefinition;
|
|
700
|
+
}
|
|
685
701
|
interface PropertyDefinition {
|
|
686
702
|
id: string;
|
|
687
703
|
label: string;
|
|
@@ -690,6 +706,12 @@ interface PropertyDefinition {
|
|
|
690
706
|
group: string;
|
|
691
707
|
groupLabel: string;
|
|
692
708
|
options?: string[];
|
|
709
|
+
/**
|
|
710
|
+
* Dynamic/smart options rendered at the top of the value selector with a
|
|
711
|
+
* divider. Only used for enum, tags, and relation types. The app resolves
|
|
712
|
+
* the sentinel values at query time.
|
|
713
|
+
*/
|
|
714
|
+
dynamicOptions?: DynamicOption[];
|
|
693
715
|
relationConfig?: {
|
|
694
716
|
entity: string;
|
|
695
717
|
displayProperty: string;
|
|
@@ -701,15 +723,27 @@ type FilterValue = string | number | boolean | Date | [number, number] | [Date,
|
|
|
701
723
|
type LogicOperator = "and" | "or";
|
|
702
724
|
interface FilterCondition {
|
|
703
725
|
id: string;
|
|
726
|
+
/** Optional discriminator. Defaults to "condition" when absent. */
|
|
727
|
+
type?: "condition";
|
|
704
728
|
propertyId: string;
|
|
705
729
|
operator: OperatorType | null;
|
|
706
730
|
value: FilterValue;
|
|
707
|
-
/** Logic connector to the previous
|
|
731
|
+
/** Logic connector to the previous sibling. Defaults to "and". */
|
|
732
|
+
logicOperator?: LogicOperator;
|
|
733
|
+
}
|
|
734
|
+
interface FilterGroup {
|
|
735
|
+
id: string;
|
|
736
|
+
type: "group";
|
|
737
|
+
/** Logic connector to the previous sibling. Defaults to "and". */
|
|
708
738
|
logicOperator?: LogicOperator;
|
|
739
|
+
children: FilterNode[];
|
|
709
740
|
}
|
|
741
|
+
/** A filter node is either a single condition or a group of nested nodes. */
|
|
742
|
+
type FilterNode = FilterCondition | FilterGroup;
|
|
710
743
|
interface FilterState {
|
|
711
744
|
basicFilters: FilterCondition[];
|
|
712
|
-
|
|
745
|
+
/** Supports nested groups. Each node is either a condition or a group. */
|
|
746
|
+
advancedFilters: FilterNode[];
|
|
713
747
|
sort: {
|
|
714
748
|
field: string;
|
|
715
749
|
direction: "asc" | "desc";
|
|
@@ -814,6 +848,8 @@ interface ValueInputProps {
|
|
|
814
848
|
onChange: (value: FilterValue) => void;
|
|
815
849
|
onSubmit?: () => void;
|
|
816
850
|
options?: string[];
|
|
851
|
+
/** Dynamic/smart entries rendered at the top with a divider. */
|
|
852
|
+
dynamicOptions?: DynamicOption[];
|
|
817
853
|
className?: string;
|
|
818
854
|
}
|
|
819
855
|
declare const ValueInput: React.FC<ValueInputProps>;
|
|
@@ -914,13 +950,15 @@ interface AdvancedRowProps {
|
|
|
914
950
|
onUpdate: (condition: FilterCondition) => void;
|
|
915
951
|
onPropertyChange: (property: PropertyDefinition) => void;
|
|
916
952
|
onDelete: () => void;
|
|
953
|
+
onDuplicate?: () => void;
|
|
954
|
+
onTurnIntoGroup?: () => void;
|
|
917
955
|
}
|
|
918
956
|
declare const AdvancedRow: React.FC<AdvancedRowProps>;
|
|
919
957
|
|
|
920
958
|
interface AdvancedPopoverProps {
|
|
921
|
-
filters:
|
|
959
|
+
filters: FilterNode[];
|
|
922
960
|
properties: PropertyDefinition[];
|
|
923
|
-
onFiltersChange: (filters:
|
|
961
|
+
onFiltersChange: (filters: FilterNode[]) => void;
|
|
924
962
|
open?: boolean;
|
|
925
963
|
onOpenChange?: (open: boolean) => void;
|
|
926
964
|
children?: React.ReactNode;
|
|
@@ -929,14 +967,12 @@ declare const AdvancedPopover: React.FC<AdvancedPopoverProps>;
|
|
|
929
967
|
|
|
930
968
|
interface SummaryChipProps {
|
|
931
969
|
count: number;
|
|
932
|
-
filters:
|
|
970
|
+
filters: FilterNode[];
|
|
933
971
|
properties: PropertyDefinition[];
|
|
934
|
-
onFiltersChange: (filters:
|
|
972
|
+
onFiltersChange: (filters: FilterNode[]) => void;
|
|
935
973
|
onClearAll: () => void;
|
|
936
|
-
/** Custom trigger element. When omitted, renders the default "Filters (N)" button. */
|
|
937
974
|
children?: React.ReactNode;
|
|
938
975
|
className?: string;
|
|
939
|
-
/** Controlled open state. */
|
|
940
976
|
open?: boolean;
|
|
941
977
|
onOpenChange?: (open: boolean) => void;
|
|
942
978
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -676,12 +676,28 @@ declare const FilterChip: React.ForwardRefExoticComponent<FilterChipProps & Reac
|
|
|
676
676
|
type DataType = "text" | "number" | "date" | "enum" | "tags" | "boolean" | "relation";
|
|
677
677
|
type TextOperator = "contains" | "does not contain" | "is" | "is not" | "starts with" | "ends with" | "is empty" | "is not empty";
|
|
678
678
|
type NumberOperator = "=" | "≠" | ">" | "<" | "≥" | "≤" | "is between" | "is empty" | "is not empty";
|
|
679
|
-
type DateOperator = "is" | "is before" | "is after" | "is on or before" | "is on or after" | "is between" | "is
|
|
679
|
+
type DateOperator = "is" | "is before" | "is after" | "is on or before" | "is on or after" | "is between" | "is empty" | "is not empty";
|
|
680
680
|
type EnumOperator = "is" | "is not" | "is any of" | "is none of" | "is empty" | "is not empty";
|
|
681
681
|
type TagsOperator = "contains" | "does not contain" | "contains any of" | "contains all of" | "is empty" | "is not empty";
|
|
682
682
|
type BooleanOperator = "is true" | "is false";
|
|
683
683
|
type RelationOperator = "is" | "is not" | "is any of" | "is none of" | "is empty" | "is not empty";
|
|
684
684
|
type OperatorType = TextOperator | NumberOperator | DateOperator | EnumOperator | TagsOperator | BooleanOperator | RelationOperator;
|
|
685
|
+
/**
|
|
686
|
+
* A "smart" / dynamic option shown at the top of a relation or enum selector
|
|
687
|
+
* (e.g. "Me", "My team", "Unassigned"). The value is a sentinel string that
|
|
688
|
+
* the consuming app resolves at query time (e.g. `__me__` → `currentUser.id`).
|
|
689
|
+
*
|
|
690
|
+
* The design system only handles the rendering (icon + label + description +
|
|
691
|
+
* divider above the regular options). Resolving the sentinels is an app
|
|
692
|
+
* concern.
|
|
693
|
+
*/
|
|
694
|
+
interface DynamicOption {
|
|
695
|
+
/** Sentinel value stored on FilterCondition.value (e.g. "__me__"). */
|
|
696
|
+
value: string;
|
|
697
|
+
label: string;
|
|
698
|
+
description?: string;
|
|
699
|
+
icon?: _l3mpire_icons.IconDefinition;
|
|
700
|
+
}
|
|
685
701
|
interface PropertyDefinition {
|
|
686
702
|
id: string;
|
|
687
703
|
label: string;
|
|
@@ -690,6 +706,12 @@ interface PropertyDefinition {
|
|
|
690
706
|
group: string;
|
|
691
707
|
groupLabel: string;
|
|
692
708
|
options?: string[];
|
|
709
|
+
/**
|
|
710
|
+
* Dynamic/smart options rendered at the top of the value selector with a
|
|
711
|
+
* divider. Only used for enum, tags, and relation types. The app resolves
|
|
712
|
+
* the sentinel values at query time.
|
|
713
|
+
*/
|
|
714
|
+
dynamicOptions?: DynamicOption[];
|
|
693
715
|
relationConfig?: {
|
|
694
716
|
entity: string;
|
|
695
717
|
displayProperty: string;
|
|
@@ -701,15 +723,27 @@ type FilterValue = string | number | boolean | Date | [number, number] | [Date,
|
|
|
701
723
|
type LogicOperator = "and" | "or";
|
|
702
724
|
interface FilterCondition {
|
|
703
725
|
id: string;
|
|
726
|
+
/** Optional discriminator. Defaults to "condition" when absent. */
|
|
727
|
+
type?: "condition";
|
|
704
728
|
propertyId: string;
|
|
705
729
|
operator: OperatorType | null;
|
|
706
730
|
value: FilterValue;
|
|
707
|
-
/** Logic connector to the previous
|
|
731
|
+
/** Logic connector to the previous sibling. Defaults to "and". */
|
|
732
|
+
logicOperator?: LogicOperator;
|
|
733
|
+
}
|
|
734
|
+
interface FilterGroup {
|
|
735
|
+
id: string;
|
|
736
|
+
type: "group";
|
|
737
|
+
/** Logic connector to the previous sibling. Defaults to "and". */
|
|
708
738
|
logicOperator?: LogicOperator;
|
|
739
|
+
children: FilterNode[];
|
|
709
740
|
}
|
|
741
|
+
/** A filter node is either a single condition or a group of nested nodes. */
|
|
742
|
+
type FilterNode = FilterCondition | FilterGroup;
|
|
710
743
|
interface FilterState {
|
|
711
744
|
basicFilters: FilterCondition[];
|
|
712
|
-
|
|
745
|
+
/** Supports nested groups. Each node is either a condition or a group. */
|
|
746
|
+
advancedFilters: FilterNode[];
|
|
713
747
|
sort: {
|
|
714
748
|
field: string;
|
|
715
749
|
direction: "asc" | "desc";
|
|
@@ -814,6 +848,8 @@ interface ValueInputProps {
|
|
|
814
848
|
onChange: (value: FilterValue) => void;
|
|
815
849
|
onSubmit?: () => void;
|
|
816
850
|
options?: string[];
|
|
851
|
+
/** Dynamic/smart entries rendered at the top with a divider. */
|
|
852
|
+
dynamicOptions?: DynamicOption[];
|
|
817
853
|
className?: string;
|
|
818
854
|
}
|
|
819
855
|
declare const ValueInput: React.FC<ValueInputProps>;
|
|
@@ -914,13 +950,15 @@ interface AdvancedRowProps {
|
|
|
914
950
|
onUpdate: (condition: FilterCondition) => void;
|
|
915
951
|
onPropertyChange: (property: PropertyDefinition) => void;
|
|
916
952
|
onDelete: () => void;
|
|
953
|
+
onDuplicate?: () => void;
|
|
954
|
+
onTurnIntoGroup?: () => void;
|
|
917
955
|
}
|
|
918
956
|
declare const AdvancedRow: React.FC<AdvancedRowProps>;
|
|
919
957
|
|
|
920
958
|
interface AdvancedPopoverProps {
|
|
921
|
-
filters:
|
|
959
|
+
filters: FilterNode[];
|
|
922
960
|
properties: PropertyDefinition[];
|
|
923
|
-
onFiltersChange: (filters:
|
|
961
|
+
onFiltersChange: (filters: FilterNode[]) => void;
|
|
924
962
|
open?: boolean;
|
|
925
963
|
onOpenChange?: (open: boolean) => void;
|
|
926
964
|
children?: React.ReactNode;
|
|
@@ -929,14 +967,12 @@ declare const AdvancedPopover: React.FC<AdvancedPopoverProps>;
|
|
|
929
967
|
|
|
930
968
|
interface SummaryChipProps {
|
|
931
969
|
count: number;
|
|
932
|
-
filters:
|
|
970
|
+
filters: FilterNode[];
|
|
933
971
|
properties: PropertyDefinition[];
|
|
934
|
-
onFiltersChange: (filters:
|
|
972
|
+
onFiltersChange: (filters: FilterNode[]) => void;
|
|
935
973
|
onClearAll: () => void;
|
|
936
|
-
/** Custom trigger element. When omitted, renders the default "Filters (N)" button. */
|
|
937
974
|
children?: React.ReactNode;
|
|
938
975
|
className?: string;
|
|
939
|
-
/** Controlled open state. */
|
|
940
976
|
open?: boolean;
|
|
941
977
|
onOpenChange?: (open: boolean) => void;
|
|
942
978
|
}
|