@proteos/ui 0.55.0 → 0.56.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/index.cjs +41 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +41 -10
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/record-filter/chip-value.tsx +6 -5
- package/src/components/record-filter/filter-value-input.tsx +11 -9
- package/src/components/record-filter/operators.test.ts +67 -12
- package/src/components/record-filter/operators.ts +43 -2
- package/src/components/record-filter/types.ts +6 -1
package/dist/index.d.cts
CHANGED
|
@@ -2356,7 +2356,10 @@ type FilterField = {
|
|
|
2356
2356
|
/** Parent label for breadcrumb in chips, e.g. `'Owner'`. Omitted for top-level. */
|
|
2357
2357
|
parentLabel?: string;
|
|
2358
2358
|
type: FilterFieldType;
|
|
2359
|
-
/** Required when `type === 'enum'
|
|
2359
|
+
/** Required when `type === 'enum'`. For `type === 'array'` these are the
|
|
2360
|
+
* ITEM enum's values (an array of enum items), so the membership editors
|
|
2361
|
+
* and chips pick from the same list the record editor does. Ignored
|
|
2362
|
+
* otherwise. */
|
|
2360
2363
|
options?: ReadonlyArray<FilterFieldOption>;
|
|
2361
2364
|
/** Hint for `<Input type=…>`. Defaults derived from `type`. */
|
|
2362
2365
|
inputType?: 'text' | 'number' | 'date' | 'datetime' | 'email';
|
|
@@ -2407,7 +2410,7 @@ type FilterFieldRelation = {
|
|
|
2407
2410
|
/** Dot-path of the field on the related entity, e.g. `'name'` or `'address.city'`. */
|
|
2408
2411
|
relatedFieldPath: string;
|
|
2409
2412
|
};
|
|
2410
|
-
type ComparisonOperator = 'eq' | 'gt' | 'lt' | 'gte' | 'lte' | 'ne' | 'in' | 'not_in' | 'contains' | 'starts_with' | 'ends_with' | 'empty' | 'not_empty';
|
|
2413
|
+
type ComparisonOperator = 'eq' | 'gt' | 'lt' | 'gte' | 'lte' | 'ne' | 'in' | 'not_in' | 'contains' | 'not_contains' | 'contains_all' | 'starts_with' | 'ends_with' | 'empty' | 'not_empty';
|
|
2411
2414
|
type LogicalOperator = 'and' | 'or';
|
|
2412
2415
|
type FilterValue = string | string[] | boolean | null;
|
|
2413
2416
|
type FilterElement = {
|
package/dist/index.d.ts
CHANGED
|
@@ -2356,7 +2356,10 @@ type FilterField = {
|
|
|
2356
2356
|
/** Parent label for breadcrumb in chips, e.g. `'Owner'`. Omitted for top-level. */
|
|
2357
2357
|
parentLabel?: string;
|
|
2358
2358
|
type: FilterFieldType;
|
|
2359
|
-
/** Required when `type === 'enum'
|
|
2359
|
+
/** Required when `type === 'enum'`. For `type === 'array'` these are the
|
|
2360
|
+
* ITEM enum's values (an array of enum items), so the membership editors
|
|
2361
|
+
* and chips pick from the same list the record editor does. Ignored
|
|
2362
|
+
* otherwise. */
|
|
2360
2363
|
options?: ReadonlyArray<FilterFieldOption>;
|
|
2361
2364
|
/** Hint for `<Input type=…>`. Defaults derived from `type`. */
|
|
2362
2365
|
inputType?: 'text' | 'number' | 'date' | 'datetime' | 'email';
|
|
@@ -2407,7 +2410,7 @@ type FilterFieldRelation = {
|
|
|
2407
2410
|
/** Dot-path of the field on the related entity, e.g. `'name'` or `'address.city'`. */
|
|
2408
2411
|
relatedFieldPath: string;
|
|
2409
2412
|
};
|
|
2410
|
-
type ComparisonOperator = 'eq' | 'gt' | 'lt' | 'gte' | 'lte' | 'ne' | 'in' | 'not_in' | 'contains' | 'starts_with' | 'ends_with' | 'empty' | 'not_empty';
|
|
2413
|
+
type ComparisonOperator = 'eq' | 'gt' | 'lt' | 'gte' | 'lte' | 'ne' | 'in' | 'not_in' | 'contains' | 'not_contains' | 'contains_all' | 'starts_with' | 'ends_with' | 'empty' | 'not_empty';
|
|
2411
2414
|
type LogicalOperator = 'and' | 'or';
|
|
2412
2415
|
type FilterValue = string | string[] | boolean | null;
|
|
2413
2416
|
type FilterElement = {
|
package/dist/index.js
CHANGED
|
@@ -6217,7 +6217,8 @@ var OPERATORS = [
|
|
|
6217
6217
|
"relation",
|
|
6218
6218
|
"user",
|
|
6219
6219
|
"principal",
|
|
6220
|
-
"contact-address"
|
|
6220
|
+
"contact-address",
|
|
6221
|
+
"array"
|
|
6221
6222
|
]
|
|
6222
6223
|
},
|
|
6223
6224
|
{
|
|
@@ -6233,10 +6234,19 @@ var OPERATORS = [
|
|
|
6233
6234
|
"relation",
|
|
6234
6235
|
"user",
|
|
6235
6236
|
"principal",
|
|
6236
|
-
"contact-address"
|
|
6237
|
+
"contact-address",
|
|
6238
|
+
"array"
|
|
6237
6239
|
]
|
|
6238
6240
|
},
|
|
6239
6241
|
{ id: "contains", label: "contains", arity: 1, types: ["string", "array", "contact-address"] },
|
|
6242
|
+
{
|
|
6243
|
+
id: "not_contains",
|
|
6244
|
+
label: "does not contain",
|
|
6245
|
+
arity: 1,
|
|
6246
|
+
types: ["string", "array", "contact-address"]
|
|
6247
|
+
},
|
|
6248
|
+
// Array-only: "has every one of these". On a scalar the backend rejects it.
|
|
6249
|
+
{ id: "contains_all", label: "has all of", arity: "n", types: ["array"] },
|
|
6240
6250
|
{ id: "starts_with", label: "starts with", arity: 1, types: ["string", "contact-address"] },
|
|
6241
6251
|
{ id: "ends_with", label: "ends with", arity: 1, types: ["string", "contact-address"] },
|
|
6242
6252
|
{
|
|
@@ -6299,14 +6309,33 @@ var OP_LABEL_OVERRIDES = {
|
|
|
6299
6309
|
integer: { gt: "is greater than", lt: "is less than", eq: "equals", ne: "does not equal" },
|
|
6300
6310
|
currency: { gt: "is greater than", lt: "is less than", eq: "equals", ne: "does not equal" },
|
|
6301
6311
|
datetime: { eq: "is on", ne: "is not on", gt: "is after", lt: "is before" },
|
|
6302
|
-
|
|
6312
|
+
// Arrays filter by set membership, in the words every CRM uses for it.
|
|
6313
|
+
array: {
|
|
6314
|
+
contains: "has",
|
|
6315
|
+
not_contains: "does not have",
|
|
6316
|
+
in: "has any of",
|
|
6317
|
+
not_in: "has none of",
|
|
6318
|
+
contains_all: "has all of",
|
|
6319
|
+
empty: "is empty",
|
|
6320
|
+
not_empty: "is not empty"
|
|
6321
|
+
},
|
|
6303
6322
|
boolean: { eq: "is", ne: "is not" }
|
|
6304
6323
|
};
|
|
6324
|
+
var OP_ORDER_OVERRIDES = {
|
|
6325
|
+
array: ["contains", "not_contains", "in", "contains_all", "not_in", "empty", "not_empty"]
|
|
6326
|
+
};
|
|
6305
6327
|
function operatorsForType(type) {
|
|
6306
|
-
|
|
6328
|
+
const ops = OPERATORS.filter((op) => op.types.includes(type)).map((op) => {
|
|
6307
6329
|
const override = OP_LABEL_OVERRIDES[type]?.[op.id];
|
|
6308
6330
|
return override ? { ...op, label: override } : op;
|
|
6309
6331
|
});
|
|
6332
|
+
const order = OP_ORDER_OVERRIDES[type];
|
|
6333
|
+
if (!order) return ops;
|
|
6334
|
+
return [...ops].sort((a, b) => order.indexOf(a.id) - order.indexOf(b.id));
|
|
6335
|
+
}
|
|
6336
|
+
function isEnumLike(field) {
|
|
6337
|
+
if (field.type === "enum") return true;
|
|
6338
|
+
return field.type === "array" && (field.options?.length ?? 0) > 0;
|
|
6310
6339
|
}
|
|
6311
6340
|
function operatorById(id) {
|
|
6312
6341
|
return OPERATORS.find((o) => o.id === id);
|
|
@@ -6378,7 +6407,7 @@ function ChipItem({
|
|
|
6378
6407
|
const custom = effectiveRenderer({ field, operator, value });
|
|
6379
6408
|
if (custom !== void 0) return /* @__PURE__ */ jsx(Fragment, { children: custom });
|
|
6380
6409
|
}
|
|
6381
|
-
if (field
|
|
6410
|
+
if (isEnumLike(field)) {
|
|
6382
6411
|
const option = findEnumOption(field, value);
|
|
6383
6412
|
return /* @__PURE__ */ jsx(
|
|
6384
6413
|
Badge,
|
|
@@ -6406,7 +6435,7 @@ function formatValue(field, operator, value) {
|
|
|
6406
6435
|
if (arity === "n") {
|
|
6407
6436
|
const arr = Array.isArray(value) ? value : typeof value === "string" && value.length > 0 ? value.split(",") : [];
|
|
6408
6437
|
if (arr.length === 0) return "\u2014";
|
|
6409
|
-
if (field
|
|
6438
|
+
if (isEnumLike(field)) {
|
|
6410
6439
|
const labels = arr.map((v) => findEnumOption(field, v)?.label ?? v);
|
|
6411
6440
|
return labels.length <= 2 ? labels.join(", ") : `${labels[0]}, ${labels[1]} +${labels.length - 2}`;
|
|
6412
6441
|
}
|
|
@@ -6414,7 +6443,7 @@ function formatValue(field, operator, value) {
|
|
|
6414
6443
|
}
|
|
6415
6444
|
if (field.type === "boolean") return value === true || value === "true" ? "true" : "false";
|
|
6416
6445
|
if (value === "" || value == null) return "\u2014";
|
|
6417
|
-
if (field
|
|
6446
|
+
if (isEnumLike(field)) {
|
|
6418
6447
|
return findEnumOption(field, String(value))?.label ?? String(value);
|
|
6419
6448
|
}
|
|
6420
6449
|
if (field.type === "number" || field.type === "integer") {
|
|
@@ -6662,7 +6691,7 @@ function FilterValueInput({
|
|
|
6662
6691
|
}
|
|
6663
6692
|
const leaf = (() => {
|
|
6664
6693
|
if (arity === "n") {
|
|
6665
|
-
if (field
|
|
6694
|
+
if (isEnumLike(field)) {
|
|
6666
6695
|
return /* @__PURE__ */ jsx(
|
|
6667
6696
|
EnumMultiValue,
|
|
6668
6697
|
{
|
|
@@ -6689,7 +6718,7 @@ function FilterValueInput({
|
|
|
6689
6718
|
}
|
|
6690
6719
|
);
|
|
6691
6720
|
}
|
|
6692
|
-
if (field
|
|
6721
|
+
if (isEnumLike(field)) {
|
|
6693
6722
|
return /* @__PURE__ */ jsx(
|
|
6694
6723
|
EnumSingleValue,
|
|
6695
6724
|
{
|
|
@@ -6850,7 +6879,9 @@ function placeholderFor(field, operator) {
|
|
|
6850
6879
|
if (field.type === "number" || field.type === "integer" || field.type === "currency") return "0";
|
|
6851
6880
|
if (operator === "starts_with") return "starts with\u2026";
|
|
6852
6881
|
if (operator === "ends_with") return "ends with\u2026";
|
|
6853
|
-
if (operator === "contains") return "contains\u2026";
|
|
6882
|
+
if (operator === "contains") return field.type === "array" ? "has\u2026" : "contains\u2026";
|
|
6883
|
+
if (operator === "not_contains")
|
|
6884
|
+
return field.type === "array" ? "does not have\u2026" : "does not contain\u2026";
|
|
6854
6885
|
return "value";
|
|
6855
6886
|
}
|
|
6856
6887
|
function OperatorPicker({
|