@sustaina/shared-ui 1.24.0 → 1.26.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.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +136 -80
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +136 -80
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,6 @@ import { twMerge } from 'tailwind-merge';
|
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
import * as React4 from 'react';
|
|
6
6
|
import React4__default, { forwardRef, useRef, useMemo, useCallback, isValidElement, useState, useEffect, useLayoutEffect, createElement } from 'react';
|
|
7
|
-
import { format, isValid, parseISO, isAfter, compareAsc, parse } from 'date-fns';
|
|
8
7
|
import { CircleX, CircleHelp, Undo, Redo, Bold, Italic, Underline, Strikethrough, Code, Pilcrow, Heading1, Heading2, Heading3, List as List$1, ListOrdered, Quote, CodeSquare, Link, Link2Off, Image as Image$1, AlignLeft, AlignCenter, AlignRight, XIcon, ChevronRight, CheckIcon, Triangle, CalendarIcon, X, Search, ChevronUp, ChevronDown, Minimize2, Maximize2, Plus, ChevronLeft, CircleUserRound, PanelLeftIcon, Bug, GripVertical, Info, CircleMinus, Minus } from 'lucide-react';
|
|
9
8
|
import { createPortal } from 'react-dom';
|
|
10
9
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
@@ -12,6 +11,7 @@ import { useForm, FormProvider, Controller, useFormContext, useFormState, useFie
|
|
|
12
11
|
import { Slot } from '@radix-ui/react-slot';
|
|
13
12
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
14
13
|
import { cva } from 'class-variance-authority';
|
|
14
|
+
import { format, isValid, parseISO, isAfter, compareAsc, parse } from 'date-fns';
|
|
15
15
|
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
16
16
|
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
|
17
17
|
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
|
|
@@ -3421,6 +3421,66 @@ function getBuilder(fieldType) {
|
|
|
3421
3421
|
return new JSONBuilder();
|
|
3422
3422
|
}
|
|
3423
3423
|
}
|
|
3424
|
+
var FILTER_FIELD_MAP = {
|
|
3425
|
+
timezone: "timezoneId",
|
|
3426
|
+
decimalSeparator: "decimalSeparatorId",
|
|
3427
|
+
country: "countryId",
|
|
3428
|
+
currency: "currencyId"
|
|
3429
|
+
};
|
|
3430
|
+
function transformFilterKeys(obj, fieldMap = FILTER_FIELD_MAP) {
|
|
3431
|
+
if (Array.isArray(obj)) {
|
|
3432
|
+
return obj.map((item) => transformFilterKeys(item, fieldMap));
|
|
3433
|
+
}
|
|
3434
|
+
if (obj && typeof obj === "object") {
|
|
3435
|
+
const newObj = {};
|
|
3436
|
+
for (const key in obj) {
|
|
3437
|
+
const mappedKey = fieldMap[key] ?? key;
|
|
3438
|
+
newObj[mappedKey] = transformFilterKeys(obj[key], fieldMap);
|
|
3439
|
+
}
|
|
3440
|
+
return newObj;
|
|
3441
|
+
}
|
|
3442
|
+
return obj;
|
|
3443
|
+
}
|
|
3444
|
+
var sanitizeInput = (val) => {
|
|
3445
|
+
if (!val) return val;
|
|
3446
|
+
if (typeof val !== "string") return "__INVALID_TYPE__";
|
|
3447
|
+
if (val.includes("\n") || val.includes("\r") || /[\u2028\u2029]/u.test(val))
|
|
3448
|
+
return "__INVALID_NEWLINE__";
|
|
3449
|
+
if (/\\\\/.test(val)) return "__INVALID_ESCAPE__";
|
|
3450
|
+
if (/\\(n|t|r|b|f|u[0-9a-fA-F]{4})/.test(val)) return "__INVALID_ESCAPE__";
|
|
3451
|
+
if (/\p{Cf}/u.test(val)) return "__INVALID_UNICODE_WHITESPACE__";
|
|
3452
|
+
if (/[\u00A0\u1680\u180E\u202F\u205F\u3000]/u.test(val)) return "__INVALID_UNICODE_WHITESPACE__";
|
|
3453
|
+
const trimmed = val.trim();
|
|
3454
|
+
if (/^\{.*\}$/s.test(trimmed) || /^\[.*\]$/s.test(trimmed)) return "__INVALID_JSON_LITERAL__";
|
|
3455
|
+
if (/\\\{/.test(val) || /\\\}/.test(val)) return "__INVALID_JSON_ESCAPE__";
|
|
3456
|
+
if (/[%_*~^]/.test(trimmed)) return "__INVALID_WILDCARD__";
|
|
3457
|
+
if (/[%><={}\\[\]"']/u.test(trimmed)) return "__INVALID_CHAR__";
|
|
3458
|
+
if (/\p{Cc}/u.test(val)) return "__INVALID_CONTROL_CHAR__";
|
|
3459
|
+
return trimmed.replace(/\s+/g, " ");
|
|
3460
|
+
};
|
|
3461
|
+
var numericTypes = ["number", "integer", "decimal"];
|
|
3462
|
+
var dateTypes = ["date", "datemonth"];
|
|
3463
|
+
var validateByFieldType = (value, fieldType) => {
|
|
3464
|
+
if (!value) return { valid: true };
|
|
3465
|
+
if (numericTypes.includes(fieldType)) {
|
|
3466
|
+
if (!/^\d+(\.\d+)?$/.test(value)) {
|
|
3467
|
+
return { valid: false, message: "Please enter a valid number." };
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3470
|
+
if (fieldType === "boolean") {
|
|
3471
|
+
if (!["true", "false"].includes(value.toLowerCase())) {
|
|
3472
|
+
return { valid: false, message: "Please enter a boolean value (true/false)." };
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
if (dateTypes.includes(fieldType)) {
|
|
3476
|
+
const normalized = fieldType === "datemonth" ? `${value}-01` : value;
|
|
3477
|
+
const parsed = parseISO(normalized);
|
|
3478
|
+
if (!isValid(parsed)) {
|
|
3479
|
+
return { valid: false, message: "Invalid date format." };
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
return { valid: true };
|
|
3483
|
+
};
|
|
3424
3484
|
var AdvanceSearch = ({
|
|
3425
3485
|
fields,
|
|
3426
3486
|
portalId,
|
|
@@ -3428,7 +3488,8 @@ var AdvanceSearch = ({
|
|
|
3428
3488
|
limitRows = 4,
|
|
3429
3489
|
onSearch,
|
|
3430
3490
|
onClear,
|
|
3431
|
-
shortDateFormat
|
|
3491
|
+
shortDateFormat,
|
|
3492
|
+
filterFieldMap = FILTER_FIELD_MAP
|
|
3432
3493
|
}) => {
|
|
3433
3494
|
const fieldsData = useMemo(() => {
|
|
3434
3495
|
if (fields.length === 0) throw new Error("fields cannot be an empty array");
|
|
@@ -3480,72 +3541,75 @@ var AdvanceSearch = ({
|
|
|
3480
3541
|
const onSubmit = useCallback(() => {
|
|
3481
3542
|
const operatorValidation = {};
|
|
3482
3543
|
rows.forEach((r) => {
|
|
3483
|
-
const
|
|
3484
|
-
if (!
|
|
3544
|
+
const ops = operatorsForField(r.fieldName);
|
|
3545
|
+
if (!ops.length || !ops.includes(r.operator))
|
|
3485
3546
|
operatorValidation[r.id] = "Please select an operator.";
|
|
3486
|
-
}
|
|
3487
3547
|
});
|
|
3488
3548
|
setOperatorErrors(operatorValidation);
|
|
3489
|
-
if (Object.keys(operatorValidation).length > 0)
|
|
3490
|
-
return;
|
|
3491
|
-
}
|
|
3549
|
+
if (Object.keys(operatorValidation).length > 0) return;
|
|
3492
3550
|
const currentValues = getValues();
|
|
3493
|
-
let
|
|
3494
|
-
const
|
|
3495
|
-
const
|
|
3496
|
-
const
|
|
3551
|
+
let hasError = false;
|
|
3552
|
+
const processedRows = rows.map((r) => {
|
|
3553
|
+
const startField = `value_${r.id}`;
|
|
3554
|
+
const endField = `value2_${r.id}`;
|
|
3555
|
+
let v1 = currentValues[startField] ?? "";
|
|
3556
|
+
let v2 = currentValues[endField] ?? "";
|
|
3557
|
+
const s1 = sanitizeInput(v1);
|
|
3558
|
+
if (s1?.startsWith("__INVALID")) {
|
|
3559
|
+
hasError = true;
|
|
3560
|
+
setError(startField, { type: "validate", message: "Invalid input." });
|
|
3561
|
+
return null;
|
|
3562
|
+
}
|
|
3563
|
+
v1 = s1 || "";
|
|
3564
|
+
const valid1 = validateByFieldType(v1, r.fieldType);
|
|
3565
|
+
if (!valid1.valid) {
|
|
3566
|
+
hasError = true;
|
|
3567
|
+
setError(startField, { type: "validate", message: valid1.message });
|
|
3568
|
+
return null;
|
|
3569
|
+
}
|
|
3497
3570
|
if (r.operator === "between") {
|
|
3498
|
-
const
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3571
|
+
const s2 = sanitizeInput(v2);
|
|
3572
|
+
if (s2?.startsWith("__INVALID")) {
|
|
3573
|
+
hasError = true;
|
|
3574
|
+
setError(endField, { type: "validate", message: "Invalid input." });
|
|
3575
|
+
return null;
|
|
3576
|
+
}
|
|
3577
|
+
v2 = s2 || "";
|
|
3578
|
+
const valid2 = validateByFieldType(v2, r.fieldType);
|
|
3579
|
+
if (!valid2.valid) {
|
|
3580
|
+
hasError = true;
|
|
3581
|
+
setError(endField, { type: "validate", message: valid2.message });
|
|
3582
|
+
return null;
|
|
3583
|
+
}
|
|
3584
|
+
if (v1 && v2 && ["date", "datemonth"].includes(r.fieldType)) {
|
|
3585
|
+
const d1 = parseRangeValue(v1, r.fieldType);
|
|
3586
|
+
const d2 = parseRangeValue(v2, r.fieldType);
|
|
3587
|
+
if (d1 && d2 && isAfter(d1, d2)) {
|
|
3588
|
+
hasError = true;
|
|
3589
|
+
setError(startField, { type: "validate", message: "Start value must be before end value." });
|
|
3590
|
+
setError(endField, { type: "validate", message: "End value must be after start value." });
|
|
3591
|
+
return null;
|
|
3515
3592
|
}
|
|
3516
3593
|
}
|
|
3517
|
-
return {
|
|
3518
|
-
...r,
|
|
3519
|
-
value: startValue ?? "",
|
|
3520
|
-
value2: endValue ?? ""
|
|
3521
|
-
};
|
|
3594
|
+
return { ...r, value: v1, value2: v2 };
|
|
3522
3595
|
}
|
|
3523
|
-
return {
|
|
3524
|
-
...r,
|
|
3525
|
-
value: startValue ?? ""
|
|
3526
|
-
};
|
|
3596
|
+
return { ...r, value: v1 };
|
|
3527
3597
|
});
|
|
3528
|
-
if (
|
|
3529
|
-
|
|
3530
|
-
}
|
|
3598
|
+
if (hasError) return;
|
|
3599
|
+
const cleanedRows = processedRows.filter(Boolean);
|
|
3531
3600
|
const param = {
|
|
3532
|
-
AND:
|
|
3533
|
-
const builder = getBuilder(r.fieldType);
|
|
3534
|
-
return builder.build(r);
|
|
3535
|
-
}).filter(Boolean)
|
|
3601
|
+
AND: cleanedRows.map((r) => getBuilder(r.fieldType).build(r)).filter(Boolean)
|
|
3536
3602
|
};
|
|
3537
|
-
if (onSearch)
|
|
3538
|
-
onSearch(param);
|
|
3539
|
-
}
|
|
3603
|
+
if (onSearch) onSearch(transformFilterKeys(param, filterFieldMap));
|
|
3540
3604
|
}, [
|
|
3541
|
-
|
|
3542
|
-
getValues,
|
|
3543
|
-
onSearch,
|
|
3605
|
+
rows,
|
|
3544
3606
|
operatorsForField,
|
|
3607
|
+
getValues,
|
|
3545
3608
|
parseRangeValue,
|
|
3546
|
-
rows,
|
|
3547
3609
|
setError,
|
|
3548
|
-
setOperatorErrors
|
|
3610
|
+
setOperatorErrors,
|
|
3611
|
+
filterFieldMap,
|
|
3612
|
+
onSearch
|
|
3549
3613
|
]);
|
|
3550
3614
|
return /* @__PURE__ */ jsx(
|
|
3551
3615
|
ExpandCollapse_default,
|
|
@@ -3583,9 +3647,7 @@ var AdvanceSearch = ({
|
|
|
3583
3647
|
unregister(`value_${row.id}`);
|
|
3584
3648
|
unregister(`value2_${row.id}`);
|
|
3585
3649
|
},
|
|
3586
|
-
onClearValue: (which) =>
|
|
3587
|
-
clearValue(row.id, which);
|
|
3588
|
-
},
|
|
3650
|
+
onClearValue: (which) => clearValue(row.id, which),
|
|
3589
3651
|
disableAdd: limitRows !== void 0 && rows.length >= limitRows
|
|
3590
3652
|
},
|
|
3591
3653
|
row.id
|
|
@@ -3610,7 +3672,7 @@ var AdvanceSearch = ({
|
|
|
3610
3672
|
Button,
|
|
3611
3673
|
{
|
|
3612
3674
|
type: "submit",
|
|
3613
|
-
className: "w-full bg-
|
|
3675
|
+
className: "w-full bg-sus-green-2 text-white hover:bg-[#2f7c21] md:w-auto md:min-w-[120px]",
|
|
3614
3676
|
"data-testid": "advsearch-btn-search",
|
|
3615
3677
|
children: "Search"
|
|
3616
3678
|
}
|
|
@@ -6203,7 +6265,8 @@ var FormulaEditor = ({
|
|
|
6203
6265
|
onChange,
|
|
6204
6266
|
onSelectSuggestion,
|
|
6205
6267
|
field,
|
|
6206
|
-
fieldState
|
|
6268
|
+
fieldState,
|
|
6269
|
+
mode = "edit"
|
|
6207
6270
|
}) => {
|
|
6208
6271
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
6209
6272
|
const lastEmittedValueRef = useRef(null);
|
|
@@ -6220,27 +6283,22 @@ var FormulaEditor = ({
|
|
|
6220
6283
|
const prefixMap = useMemo(() => buildPrefixMap(normalizedConfigs), [normalizedConfigs]);
|
|
6221
6284
|
const configLookup = useMemo(() => {
|
|
6222
6285
|
const lookup = /* @__PURE__ */ new Map();
|
|
6223
|
-
normalizedConfigs.forEach((config) =>
|
|
6224
|
-
lookup.set(config.prefix, config);
|
|
6225
|
-
});
|
|
6286
|
+
normalizedConfigs.forEach((config) => lookup.set(config.prefix, config));
|
|
6226
6287
|
return lookup;
|
|
6227
6288
|
}, [normalizedConfigs]);
|
|
6228
6289
|
const allowedOperators = useMemo(() => operators.map((operator) => operator.value), [operators]);
|
|
6229
6290
|
const displayError = errorMessage ?? fieldState?.error?.message;
|
|
6230
6291
|
const hasError = Boolean(displayError);
|
|
6231
|
-
const
|
|
6292
|
+
const isEditorReadOnly = mode === "display";
|
|
6293
|
+
const isEditorDisabled = disabled || loading || isEditorReadOnly;
|
|
6232
6294
|
const convertValueToContent = useCallback(
|
|
6233
6295
|
(input) => {
|
|
6234
6296
|
if (!input) return "";
|
|
6235
6297
|
const trimmed = input.trim();
|
|
6236
6298
|
if (!trimmed) return "";
|
|
6237
6299
|
const parsedJSON = tryParseJSON(trimmed);
|
|
6238
|
-
if (parsedJSON && parsedJSON.type === "doc")
|
|
6239
|
-
|
|
6240
|
-
}
|
|
6241
|
-
if (looksLikeHTML(trimmed)) {
|
|
6242
|
-
return input;
|
|
6243
|
-
}
|
|
6300
|
+
if (parsedJSON && parsedJSON.type === "doc") return parsedJSON;
|
|
6301
|
+
if (looksLikeHTML(trimmed)) return input;
|
|
6244
6302
|
return buildDocFromRaw(input, prefixMap, configLookup);
|
|
6245
6303
|
},
|
|
6246
6304
|
[configLookup, prefixMap]
|
|
@@ -6271,7 +6329,7 @@ var FormulaEditor = ({
|
|
|
6271
6329
|
hasError ? "border border-destructive" : "border focus-visible:border-ring",
|
|
6272
6330
|
"w-full rounded-lg bg-white px-4 py-3",
|
|
6273
6331
|
"overflow-y-auto whitespace-pre-wrap wrap-break-word focus:outline-none",
|
|
6274
|
-
|
|
6332
|
+
isEditorDisabled && "pointer-events-none",
|
|
6275
6333
|
editorClassName
|
|
6276
6334
|
),
|
|
6277
6335
|
...loading ? { "aria-busy": "true" } : {}
|
|
@@ -6280,8 +6338,8 @@ var FormulaEditor = ({
|
|
|
6280
6338
|
});
|
|
6281
6339
|
useEffect(() => {
|
|
6282
6340
|
if (!editor) return;
|
|
6283
|
-
editor.setEditable(!
|
|
6284
|
-
}, [editor,
|
|
6341
|
+
editor.setEditable(!isEditorDisabled);
|
|
6342
|
+
}, [editor, isEditorDisabled]);
|
|
6285
6343
|
useEffect(() => {
|
|
6286
6344
|
if (!editor || resolvedContent === void 0) return;
|
|
6287
6345
|
if (ignorePropValueRef.current && typeof value === "string" && value === lastEmittedValueRef.current) {
|
|
@@ -6315,9 +6373,7 @@ var FormulaEditor = ({
|
|
|
6315
6373
|
className: "relative",
|
|
6316
6374
|
"aria-busy": loading,
|
|
6317
6375
|
onFocus: () => {
|
|
6318
|
-
if (editor && !editor.isFocused)
|
|
6319
|
-
editor.chain().focus().run();
|
|
6320
|
-
}
|
|
6376
|
+
if (editor && !editor.isFocused) editor.chain().focus().run();
|
|
6321
6377
|
},
|
|
6322
6378
|
children: [
|
|
6323
6379
|
/* @__PURE__ */ jsx(EditorContent, { editor }),
|
|
@@ -6329,14 +6385,14 @@ var FormulaEditor = ({
|
|
|
6329
6385
|
spinnerClassName: "size-6 text-sus-blue-3"
|
|
6330
6386
|
}
|
|
6331
6387
|
),
|
|
6332
|
-
/* @__PURE__ */ jsx(
|
|
6388
|
+
!isEditorReadOnly && /* @__PURE__ */ jsx(
|
|
6333
6389
|
Button,
|
|
6334
6390
|
{
|
|
6335
6391
|
type: "button",
|
|
6336
6392
|
variant: "ghost",
|
|
6337
6393
|
size: "icon",
|
|
6338
6394
|
className: "absolute bottom-2 right-4 h-6 w-6 rounded-full bg-white shadow",
|
|
6339
|
-
disabled:
|
|
6395
|
+
disabled: isEditorDisabled,
|
|
6340
6396
|
onClick: () => setIsExpanded((prev) => !prev),
|
|
6341
6397
|
children: isExpanded ? /* @__PURE__ */ jsx(Minimize2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Maximize2, { className: "h-4 w-4" })
|
|
6342
6398
|
}
|
|
@@ -6345,13 +6401,13 @@ var FormulaEditor = ({
|
|
|
6345
6401
|
}
|
|
6346
6402
|
),
|
|
6347
6403
|
hasError && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", role: "alert", children: displayError }),
|
|
6348
|
-
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap justify-end gap-2 py-2", children: operators.map((operator) => /* @__PURE__ */ jsx(
|
|
6404
|
+
mode === "edit" && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap justify-end gap-2 py-2", children: operators.map((operator) => /* @__PURE__ */ jsx(
|
|
6349
6405
|
Button,
|
|
6350
6406
|
{
|
|
6351
6407
|
type: "button",
|
|
6352
6408
|
onClick: () => insertOperator(operator.value),
|
|
6353
6409
|
className: "min-w-10 rounded-sm px-3 bg-sus-blue-3",
|
|
6354
|
-
disabled:
|
|
6410
|
+
disabled: isEditorDisabled,
|
|
6355
6411
|
children: operator.label
|
|
6356
6412
|
},
|
|
6357
6413
|
operator.value
|
|
@@ -6547,8 +6603,8 @@ var GridSettingsModal = ({
|
|
|
6547
6603
|
}
|
|
6548
6604
|
}, [isOpen, currentColumns, form]);
|
|
6549
6605
|
const addColumn = async () => {
|
|
6550
|
-
const
|
|
6551
|
-
if (
|
|
6606
|
+
const isValid6 = await trigger("columns");
|
|
6607
|
+
if (isValid6) {
|
|
6552
6608
|
append({ id: "" });
|
|
6553
6609
|
requestAnimationFrame(() => {
|
|
6554
6610
|
const container = scrollRef.current;
|