@tensor_1/react 2.30.0 → 2.31.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/README.md +80 -0
- package/dist/components/Attachment.d.ts +55 -0
- package/dist/components/Attachment.js +69 -0
- package/dist/components/EntityPicker.d.ts +56 -0
- package/dist/components/EntityPicker.js +168 -0
- package/dist/components/FileIcon.d.ts +9 -0
- package/dist/components/FileIcon.js +17 -0
- package/dist/components/FileUpload.d.ts +87 -0
- package/dist/components/FileUpload.js +139 -0
- package/dist/components/FilterEditor.d.ts +51 -0
- package/dist/components/FilterEditor.js +175 -0
- package/dist/components/Lightbox.d.ts +45 -0
- package/dist/components/Lightbox.js +102 -0
- package/dist/components/SearchSuggest.d.ts +6 -2
- package/dist/components/SearchSuggest.js +4 -0
- package/dist/file-model.d.ts +37 -0
- package/dist/file-model.js +50 -0
- package/dist/filter-model.d.ts +88 -0
- package/dist/filter-model.js +91 -0
- package/dist/icons/Icon.d.ts +1 -1
- package/dist/index.cjs +2213 -1407
- package/dist/index.d.cts +401 -3
- package/dist/index.d.ts +7 -0
- package/dist/index.js +35 -0
- package/package.json +1 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed column filters for `FilterEditor` (HAR-1365; TENSOR C6 `table-filter-editor.tsx`,
|
|
3
|
+
* MOTUS C-1 "+ filter"). The value is data the product serializes into its own query; the
|
|
4
|
+
* summary feeds a `Chip`. Pure, so a server can summarize a saved view too.
|
|
5
|
+
*/
|
|
6
|
+
interface FilterChoice {
|
|
7
|
+
value: string;
|
|
8
|
+
label: string;
|
|
9
|
+
}
|
|
10
|
+
type TextFilterOperator = 'contains' | 'not-contains' | 'is' | 'is-not' | 'starts-with';
|
|
11
|
+
type NumberFilterOperator = 'eq' | 'neq' | 'lt' | 'lte' | 'gt' | 'gte' | 'between';
|
|
12
|
+
type FilterValue = {
|
|
13
|
+
kind: 'enum';
|
|
14
|
+
values: string[];
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'text';
|
|
17
|
+
operator: TextFilterOperator;
|
|
18
|
+
text: string;
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'number';
|
|
21
|
+
operator: NumberFilterOperator;
|
|
22
|
+
value?: number;
|
|
23
|
+
to?: number;
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'date-range';
|
|
26
|
+
from?: string;
|
|
27
|
+
to?: string;
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'boolean';
|
|
30
|
+
value?: boolean;
|
|
31
|
+
} | {
|
|
32
|
+
kind: 'reference';
|
|
33
|
+
values: FilterChoice[];
|
|
34
|
+
};
|
|
35
|
+
type FilterValueKind = FilterValue['kind'];
|
|
36
|
+
interface FilterField {
|
|
37
|
+
id: string;
|
|
38
|
+
/** Column name, also the start of the chip summary. */
|
|
39
|
+
label: string;
|
|
40
|
+
kind: FilterValueKind;
|
|
41
|
+
/** `enum`: the choices. */
|
|
42
|
+
options?: readonly FilterChoice[];
|
|
43
|
+
/** `reference`: finds records to pick; rejected promises show the error state. */
|
|
44
|
+
onSearch?: (query: string) => Promise<readonly FilterChoice[]>;
|
|
45
|
+
/** `text` / `number`: the operators offered, in order. Default: all of the kind. */
|
|
46
|
+
operators?: readonly (TextFilterOperator | NumberFilterOperator)[];
|
|
47
|
+
/** `number`: a unit shown after the value, e.g. "days". */
|
|
48
|
+
unit?: string;
|
|
49
|
+
/** `boolean`: words for true and false, e.g. Escalated / Not escalated. Default from labels. */
|
|
50
|
+
trueLabel?: string;
|
|
51
|
+
falseLabel?: string;
|
|
52
|
+
}
|
|
53
|
+
interface FilterSummaryLabels {
|
|
54
|
+
operators: Record<TextFilterOperator | NumberFilterOperator, string>;
|
|
55
|
+
yes: string;
|
|
56
|
+
no: string;
|
|
57
|
+
/** `{from}`, `{to}`; also used for a number range. */
|
|
58
|
+
range: string;
|
|
59
|
+
/** Only a start date. `{from}`. */
|
|
60
|
+
from: string;
|
|
61
|
+
/** Only an end date. `{to}`. */
|
|
62
|
+
until: string;
|
|
63
|
+
/** More than `maxListed` values: `{first}`, `{count}`. */
|
|
64
|
+
more: string;
|
|
65
|
+
}
|
|
66
|
+
declare const DEFAULT_FILTER_SUMMARY_LABELS: FilterSummaryLabels;
|
|
67
|
+
declare const TEXT_FILTER_OPERATORS: readonly TextFilterOperator[];
|
|
68
|
+
declare const NUMBER_FILTER_OPERATORS: readonly NumberFilterOperator[];
|
|
69
|
+
/** The empty value for a field kind (what Clear resets to). */
|
|
70
|
+
declare function emptyFilterValue(kind: FilterValueKind): FilterValue;
|
|
71
|
+
/** True when the value filters nothing (so the chip goes away). */
|
|
72
|
+
declare function isFilterEmpty(value: FilterValue | undefined): boolean;
|
|
73
|
+
interface SummarizeFilterOptions {
|
|
74
|
+
labels?: Partial<FilterSummaryLabels>;
|
|
75
|
+
/** Formats a `YYYY-MM-DD` date (a product format such as DD.MM.YYYY). Default: as given. */
|
|
76
|
+
formatDate?: (date: string) => string;
|
|
77
|
+
/** Formats a number. Default: `String`. */
|
|
78
|
+
formatNumber?: (value: number) => string;
|
|
79
|
+
/** List this many values before "+N". Default 2. */
|
|
80
|
+
maxListed?: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* One line for the filter chip, e.g. "State: Open, Pending", "Priority ≥ 2", "Created:
|
|
84
|
+
* 01.10.2026 – 05.10.2026", "Escalated: Yes". Empty values give an empty string.
|
|
85
|
+
*/
|
|
86
|
+
declare function summarizeFilter(field: FilterField, value: FilterValue | undefined, options?: SummarizeFilterOptions): string;
|
|
87
|
+
|
|
88
|
+
export { DEFAULT_FILTER_SUMMARY_LABELS, type FilterChoice, type FilterField, type FilterSummaryLabels, type FilterValue, type FilterValueKind, NUMBER_FILTER_OPERATORS, type NumberFilterOperator, type SummarizeFilterOptions, TEXT_FILTER_OPERATORS, type TextFilterOperator, emptyFilterValue, isFilterEmpty, summarizeFilter };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
const DEFAULT_FILTER_SUMMARY_LABELS = {
|
|
2
|
+
operators: {
|
|
3
|
+
contains: "contains",
|
|
4
|
+
"not-contains": "does not contain",
|
|
5
|
+
is: "is",
|
|
6
|
+
"is-not": "is not",
|
|
7
|
+
"starts-with": "starts with",
|
|
8
|
+
eq: "=",
|
|
9
|
+
neq: "\u2260",
|
|
10
|
+
lt: "<",
|
|
11
|
+
lte: "\u2264",
|
|
12
|
+
gt: ">",
|
|
13
|
+
gte: "\u2265",
|
|
14
|
+
between: "between"
|
|
15
|
+
},
|
|
16
|
+
yes: "Yes",
|
|
17
|
+
no: "No",
|
|
18
|
+
range: "{from} \u2013 {to}",
|
|
19
|
+
from: "from {from}",
|
|
20
|
+
until: "until {to}",
|
|
21
|
+
more: "{first} +{count}"
|
|
22
|
+
};
|
|
23
|
+
const TEXT_FILTER_OPERATORS = ["contains", "not-contains", "is", "is-not", "starts-with"];
|
|
24
|
+
const NUMBER_FILTER_OPERATORS = ["eq", "neq", "lt", "lte", "gt", "gte", "between"];
|
|
25
|
+
function emptyFilterValue(kind) {
|
|
26
|
+
switch (kind) {
|
|
27
|
+
case "enum":
|
|
28
|
+
return { kind, values: [] };
|
|
29
|
+
case "text":
|
|
30
|
+
return { kind, operator: "contains", text: "" };
|
|
31
|
+
case "number":
|
|
32
|
+
return { kind, operator: "eq" };
|
|
33
|
+
case "date-range":
|
|
34
|
+
return { kind };
|
|
35
|
+
case "boolean":
|
|
36
|
+
return { kind };
|
|
37
|
+
case "reference":
|
|
38
|
+
return { kind, values: [] };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function isFilterEmpty(value) {
|
|
42
|
+
if (!value) return true;
|
|
43
|
+
switch (value.kind) {
|
|
44
|
+
case "enum":
|
|
45
|
+
case "reference":
|
|
46
|
+
return value.values.length === 0;
|
|
47
|
+
case "text":
|
|
48
|
+
return value.text.trim() === "";
|
|
49
|
+
case "number":
|
|
50
|
+
return value.value === void 0 || value.operator === "between" && value.to === void 0;
|
|
51
|
+
case "date-range":
|
|
52
|
+
return !value.from && !value.to;
|
|
53
|
+
case "boolean":
|
|
54
|
+
return value.value === void 0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const fill = (template, values) => template.replace(/\{(\w+)\}/g, (match, key) => key in values ? String(values[key]) : match);
|
|
58
|
+
function summarizeFilter(field, value, options = {}) {
|
|
59
|
+
if (!value || isFilterEmpty(value)) return "";
|
|
60
|
+
const labels = { ...DEFAULT_FILTER_SUMMARY_LABELS, ...options.labels, operators: { ...DEFAULT_FILTER_SUMMARY_LABELS.operators, ...options.labels?.operators } };
|
|
61
|
+
const date = options.formatDate ?? ((d) => d);
|
|
62
|
+
const num = (n) => `${options.formatNumber?.(n) ?? String(n)}${field.unit ? ` ${field.unit}` : ""}`;
|
|
63
|
+
const list = (items) => {
|
|
64
|
+
const max = Math.max(1, options.maxListed ?? 2);
|
|
65
|
+
return items.length > max ? fill(labels.more, { first: items.slice(0, max).join(", "), count: items.length - max }) : items.join(", ");
|
|
66
|
+
};
|
|
67
|
+
switch (value.kind) {
|
|
68
|
+
case "enum": {
|
|
69
|
+
const byValue = new Map((field.options ?? []).map((o) => [o.value, o.label]));
|
|
70
|
+
return `${field.label}: ${list(value.values.map((v) => byValue.get(v) ?? v))}`;
|
|
71
|
+
}
|
|
72
|
+
case "reference":
|
|
73
|
+
return `${field.label}: ${list(value.values.map((v) => v.label))}`;
|
|
74
|
+
case "text":
|
|
75
|
+
return `${field.label} ${labels.operators[value.operator]} \u201C${value.text.trim()}\u201D`;
|
|
76
|
+
case "number":
|
|
77
|
+
return value.operator === "between" ? `${field.label}: ${fill(labels.range, { from: num(value.value), to: num(value.to) })}` : `${field.label} ${labels.operators[value.operator]} ${num(value.value)}`;
|
|
78
|
+
case "date-range":
|
|
79
|
+
return `${field.label}: ${value.from && value.to ? fill(labels.range, { from: date(value.from), to: date(value.to) }) : value.from ? fill(labels.from, { from: date(value.from) }) : fill(labels.until, { to: date(value.to) })}`;
|
|
80
|
+
case "boolean":
|
|
81
|
+
return `${field.label}: ${value.value ? field.trueLabel ?? labels.yes : field.falseLabel ?? labels.no}`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export {
|
|
85
|
+
DEFAULT_FILTER_SUMMARY_LABELS,
|
|
86
|
+
NUMBER_FILTER_OPERATORS,
|
|
87
|
+
TEXT_FILTER_OPERATORS,
|
|
88
|
+
emptyFilterValue,
|
|
89
|
+
isFilterEmpty,
|
|
90
|
+
summarizeFilter
|
|
91
|
+
};
|
package/dist/icons/Icon.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { IconNode, UixIcon, IconProps } from './types.js';
|
|
|
5
5
|
/** Renders a glyph as an inline SVG; the props contract shared by every icon. */
|
|
6
6
|
declare function renderIcon(node: IconNode, { size, tone, label, strokeWidth, className, style, ...rest }: IconProps, ref?: unknown): react.ReactElement<{
|
|
7
7
|
string?: number | string | undefined | undefined;
|
|
8
|
+
r?: number | string | undefined | undefined;
|
|
8
9
|
type?: string | undefined | undefined;
|
|
9
10
|
id?: string | undefined | undefined;
|
|
10
11
|
name?: string | undefined | undefined;
|
|
@@ -252,7 +253,6 @@ declare function renderIcon(node: IconNode, { size, tone, label, strokeWidth, cl
|
|
|
252
253
|
fill: string;
|
|
253
254
|
clip?: number | string | undefined | undefined;
|
|
254
255
|
stroke: string;
|
|
255
|
-
r?: number | string | undefined | undefined;
|
|
256
256
|
filter?: string | undefined | undefined;
|
|
257
257
|
values?: string | undefined | undefined;
|
|
258
258
|
offset?: number | string | undefined | undefined;
|