@simai-studio/view 1.2.0 → 1.4.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 +5 -5
- package/dist/package-receipt.json +124 -124
- package/dist/set.json +1 -1
- package/dist/smart/view/component.json +19 -10
- package/dist/smart/view/component.json.gz +0 -0
- package/dist/smart/view/js/view.js +2 -2
- package/dist/smart/view/js/view.js.gz +0 -0
- package/dist/smart/view/js/view.min.js +1 -1
- package/dist/smart/view/js/view.min.js.gz +0 -0
- package/dist/smart/view/smart.manifest.json +14 -6
- package/dist/smart/view/smart.manifest.json.gz +0 -0
- package/dist/smart/view/static/default/open-record.html +1 -1
- package/dist/smart/view/static/default/open-record.html.gz +0 -0
- package/dist/smart/view/static/default/reference.html +1 -1
- package/dist/smart/view/static/default/reference.html.gz +0 -0
- package/dist/smart/view-flow/component.json +10 -2
- package/dist/smart/view-flow/component.json.gz +0 -0
- package/dist/smart/view-flow/css/view-flow.css +367 -0
- package/dist/smart/view-flow/css/view-flow.css.gz +0 -0
- package/dist/smart/view-flow/css/view-flow.min.css +1 -0
- package/dist/smart/view-flow/css/view-flow.min.css.gz +0 -0
- package/dist/smart/view-flow/js/view-flow.js +1977 -72
- package/dist/smart/view-flow/js/view-flow.js.gz +0 -0
- package/dist/smart/view-flow/js/view-flow.min.js +320 -197
- package/dist/smart/view-flow/js/view-flow.min.js.gz +0 -0
- package/dist/smart/view-flow/smart.manifest.json +11 -3
- package/dist/smart/view-flow/smart.manifest.json.gz +0 -0
- package/dist/smart/view-flow/static/default/ask.html +1 -1
- package/dist/smart/view-flow/static/default/ask.html.gz +0 -0
- package/dist/smart/view-flow/static/default/reference.html +2 -2
- package/dist/smart/view-flow/static/default/reference.html.gz +0 -0
- package/dist/smart/view-properties/component.json +15 -1
- package/dist/smart/view-properties/component.json.gz +0 -0
- package/dist/smart/view-properties/css/view-properties.css +384 -4
- package/dist/smart/view-properties/css/view-properties.css.gz +0 -0
- package/dist/smart/view-properties/css/view-properties.min.css +2 -1
- package/dist/smart/view-properties/css/view-properties.min.css.gz +0 -0
- package/dist/smart/view-properties/js/view-properties.js +1974 -69
- package/dist/smart/view-properties/js/view-properties.js.gz +0 -0
- package/dist/smart/view-properties/js/view-properties.min.js +553 -430
- package/dist/smart/view-properties/js/view-properties.min.js.gz +0 -0
- package/dist/smart/view-properties/smart.manifest.json +42 -4
- package/dist/smart/view-properties/smart.manifest.json.gz +0 -0
- package/dist/smart/view-properties/template/default.css +13 -2
- package/dist/smart/view-properties/template/default.css.gz +0 -0
- package/dist/smart/view-table/component.json +8 -1
- package/dist/smart/view-table/component.json.gz +0 -0
- package/dist/smart/view-table/js/view-table.js +393 -33
- package/dist/smart/view-table/js/view-table.js.gz +0 -0
- package/dist/smart/view-table/js/view-table.min.js +152 -139
- package/dist/smart/view-table/js/view-table.min.js.gz +0 -0
- package/dist/smart/view-table/smart.manifest.json +9 -2
- package/dist/smart/view-table/smart.manifest.json.gz +0 -0
- package/package.json +1 -1
|
@@ -407,6 +407,173 @@ function composeTime(original, text) {
|
|
|
407
407
|
|
|
408
408
|
/***/ },
|
|
409
409
|
|
|
410
|
+
/***/ "ab3947ea3647"
|
|
411
|
+
(module, __webpack_exports__, __webpack_require__) {
|
|
412
|
+
|
|
413
|
+
__webpack_require__.a(module, async (__webpack_handle_async_dependencies__, __webpack_async_result__) => { try {
|
|
414
|
+
__webpack_require__.r(__webpack_exports__);
|
|
415
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
416
|
+
/* harmony export */ formatDecimal: () => (/* binding */ formatDecimal),
|
|
417
|
+
/* harmony export */ formatMoney: () => (/* binding */ formatMoney),
|
|
418
|
+
/* harmony export */ formatPercent: () => (/* binding */ formatPercent),
|
|
419
|
+
/* harmony export */ fractionDigits: () => (/* binding */ fractionDigits),
|
|
420
|
+
/* harmony export */ moneyParts: () => (/* binding */ moneyParts),
|
|
421
|
+
/* harmony export */ readDecimal: () => (/* binding */ readDecimal)
|
|
422
|
+
/* harmony export */ });
|
|
423
|
+
/* harmony import */ var _core_js_format_intl_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("15b4e93ff6ed");
|
|
424
|
+
var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([_core_js_format_intl_js__WEBPACK_IMPORTED_MODULE_0__]);
|
|
425
|
+
var __webpack_async_dependencies_result__ = (__webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__);
|
|
426
|
+
_core_js_format_intl_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_async_dependencies_result__[0];
|
|
427
|
+
// Decimal numbers as strings, never as floats (simai.property-view-port
|
|
428
|
+
// 1.18.0; Larena property types standard, notes of 2026-11-03: «Never a float
|
|
429
|
+
// on the wire»). What a person typed is read into a canonical decimal string,
|
|
430
|
+
// and a decimal, a money amount and a percent are shown in the page's language
|
|
431
|
+
// through Intl, which formats a string by its digits: «1500.10» never passes
|
|
432
|
+
// through a float on the way to «1 500,10 ₽». Pure, so the unit suite reads it
|
|
433
|
+
// without a browser; the View set draws the editors of money, percent and
|
|
434
|
+
// items with its own copy of the same rules.
|
|
435
|
+
|
|
436
|
+
const TYPED = /^([+-]?)(\d*)(?:[.,](\d*))?$/u; // Spaces a person or a formatter puts between groups of digits: the plain
|
|
437
|
+
// space, the no-break space and the narrow no-break space ru-RU writes, and
|
|
438
|
+
// the apostrophe some locales group with.
|
|
439
|
+
|
|
440
|
+
const GROUP_SPACES = /[\s ']/gu;
|
|
441
|
+
/**
|
|
442
|
+
* What a person typed, read as a decimal: `{ok: true, value}` with the
|
|
443
|
+
* canonical string (an optional minus, the integer digits without leading
|
|
444
|
+
* zeros, the fraction digits as typed; null for nothing typed), or
|
|
445
|
+
* `{ok: false}` for text that is not a number. A comma and a point both
|
|
446
|
+
* separate the fraction. A JSON integer is read as its digits; a float is not
|
|
447
|
+
* a decimal and is refused rather than guessed at.
|
|
448
|
+
*/
|
|
449
|
+
|
|
450
|
+
function readDecimal(text) {
|
|
451
|
+
if (text === null || typeof text === 'undefined') return {
|
|
452
|
+
ok: true,
|
|
453
|
+
value: null
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
if (typeof text === 'number') {
|
|
457
|
+
return Number.isSafeInteger(text) ? {
|
|
458
|
+
ok: true,
|
|
459
|
+
value: String(text)
|
|
460
|
+
} : {
|
|
461
|
+
ok: false
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const plain = String(text).replace(GROUP_SPACES, '');
|
|
466
|
+
if (plain === '') return {
|
|
467
|
+
ok: true,
|
|
468
|
+
value: null
|
|
469
|
+
};
|
|
470
|
+
const match = TYPED.exec(plain);
|
|
471
|
+
if (!match) return {
|
|
472
|
+
ok: false
|
|
473
|
+
};
|
|
474
|
+
const [, sign, whole, fraction] = match;
|
|
475
|
+
if (whole === '' && (fraction ?? '') === '') return {
|
|
476
|
+
ok: false
|
|
477
|
+
};
|
|
478
|
+
const integer = whole.replace(/^0+(?=\d)/u, '') || '0';
|
|
479
|
+
const digits = fraction ?? '';
|
|
480
|
+
const zero = /^0*$/u.test(integer + digits);
|
|
481
|
+
return {
|
|
482
|
+
ok: true,
|
|
483
|
+
value: `${sign === '-' && !zero ? '-' : ''}${integer}${digits ? `.${digits}` : ''}`
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
/** How many digits a canonical decimal string has after its point. */
|
|
487
|
+
|
|
488
|
+
function fractionDigits(value) {
|
|
489
|
+
const at = String(value ?? '').indexOf('.');
|
|
490
|
+
return at === -1 ? 0 : String(value).length - at - 1;
|
|
491
|
+
} // A float a host sent anyway is shown, not refused: a cell is not the place to
|
|
492
|
+
// argue with a stored value. Only what reads as a decimal is formatted.
|
|
493
|
+
|
|
494
|
+
function decimalOf(value) {
|
|
495
|
+
if (typeof value === 'number') return Number.isFinite(value) ? String(value) : null;
|
|
496
|
+
const read = readDecimal(value);
|
|
497
|
+
return read.ok ? read.value : null;
|
|
498
|
+
}
|
|
499
|
+
/** A decimal string in the locale's digits, with every digit it has: «1 500,5». */
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
function formatDecimal(value, locale) {
|
|
503
|
+
const decimal = decimalOf(value);
|
|
504
|
+
if (decimal === null) return '';
|
|
505
|
+
const digits = Math.min(20, fractionDigits(decimal));
|
|
506
|
+
return (0,_core_js_format_intl_js__WEBPACK_IMPORTED_MODULE_0__.intlFormatter)('NumberFormat', locale, {
|
|
507
|
+
minimumFractionDigits: digits,
|
|
508
|
+
maximumFractionDigits: digits
|
|
509
|
+
}).format(decimal);
|
|
510
|
+
}
|
|
511
|
+
/** A money value as {amount, currency}; a field that fixes the currency takes a bare amount. */
|
|
512
|
+
|
|
513
|
+
function moneyParts(value, fixed) {
|
|
514
|
+
const currency = typeof fixed === 'string' && fixed ? fixed : null;
|
|
515
|
+
|
|
516
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
517
|
+
return {
|
|
518
|
+
amount: value.amount ?? null,
|
|
519
|
+
currency: currency || (value.currency ?? null)
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (value === null || typeof value === 'undefined' || value === '') return {
|
|
524
|
+
amount: null,
|
|
525
|
+
currency
|
|
526
|
+
};
|
|
527
|
+
return {
|
|
528
|
+
amount: value,
|
|
529
|
+
currency
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* An amount with its currency, as a person reads it: «1 500,50 ₽», «$1,500.50».
|
|
534
|
+
* The digits after the point are at least the currency's minor units (ISO
|
|
535
|
+
* 4217) and every digit the amount has. Without a currency, the amount alone.
|
|
536
|
+
*/
|
|
537
|
+
|
|
538
|
+
function formatMoney(value, field = {}, locale = 'en') {
|
|
539
|
+
const {
|
|
540
|
+
amount,
|
|
541
|
+
currency
|
|
542
|
+
} = moneyParts(value, field.currency);
|
|
543
|
+
const decimal = decimalOf(amount);
|
|
544
|
+
if (decimal === null) return '';
|
|
545
|
+
if (!currency) return formatDecimal(decimal, locale);
|
|
546
|
+
const minor = (0,_core_js_format_intl_js__WEBPACK_IMPORTED_MODULE_0__.intlFormatter)('NumberFormat', locale, {
|
|
547
|
+
style: 'currency',
|
|
548
|
+
currency
|
|
549
|
+
}).resolvedOptions().maximumFractionDigits ?? 2;
|
|
550
|
+
const digits = Math.min(20, Math.max(minor, fractionDigits(decimal)));
|
|
551
|
+
return (0,_core_js_format_intl_js__WEBPACK_IMPORTED_MODULE_0__.intlFormatter)('NumberFormat', locale, {
|
|
552
|
+
style: 'currency',
|
|
553
|
+
currency,
|
|
554
|
+
currencyDisplay: 'narrowSymbol',
|
|
555
|
+
minimumFractionDigits: digits,
|
|
556
|
+
maximumFractionDigits: digits
|
|
557
|
+
}).format(decimal);
|
|
558
|
+
}
|
|
559
|
+
/** A percent, «12,5 %» or «12.5%»: the value is the percent ("20" is 20 %), never the share. */
|
|
560
|
+
|
|
561
|
+
function formatPercent(value, locale = 'en') {
|
|
562
|
+
const decimal = decimalOf(value);
|
|
563
|
+
if (decimal === null) return '';
|
|
564
|
+
const digits = Math.min(20, fractionDigits(decimal));
|
|
565
|
+
return (0,_core_js_format_intl_js__WEBPACK_IMPORTED_MODULE_0__.intlFormatter)('NumberFormat', locale, {
|
|
566
|
+
style: 'unit',
|
|
567
|
+
unit: 'percent',
|
|
568
|
+
minimumFractionDigits: digits,
|
|
569
|
+
maximumFractionDigits: digits
|
|
570
|
+
}).format(decimal);
|
|
571
|
+
}
|
|
572
|
+
__webpack_async_result__();
|
|
573
|
+
} catch(e) { __webpack_async_result__(e); } });
|
|
574
|
+
|
|
575
|
+
/***/ },
|
|
576
|
+
|
|
410
577
|
/***/ "01ae054f01b4"
|
|
411
578
|
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
412
579
|
|
|
@@ -473,8 +640,9 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
473
640
|
/* harmony import */ var _datetime_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__("9ce1a42b2a85");
|
|
474
641
|
/* harmony import */ var _mask_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__("dd6973f77245");
|
|
475
642
|
/* harmony import */ var _html_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__("01ae054f01b4");
|
|
476
|
-
var
|
|
477
|
-
([lit__WEBPACK_IMPORTED_MODULE_0__, lit_directives_unsafe_html_js__WEBPACK_IMPORTED_MODULE_1__, _core_js_page_language__WEBPACK_IMPORTED_MODULE_3__]
|
|
643
|
+
/* harmony import */ var _decimal_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__("ab3947ea3647");
|
|
644
|
+
var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([lit__WEBPACK_IMPORTED_MODULE_0__, lit_directives_unsafe_html_js__WEBPACK_IMPORTED_MODULE_1__, _core_js_page_language__WEBPACK_IMPORTED_MODULE_3__, _decimal_js__WEBPACK_IMPORTED_MODULE_8__]);
|
|
645
|
+
([lit__WEBPACK_IMPORTED_MODULE_0__, lit_directives_unsafe_html_js__WEBPACK_IMPORTED_MODULE_1__, _core_js_page_language__WEBPACK_IMPORTED_MODULE_3__, _decimal_js__WEBPACK_IMPORTED_MODULE_8__] = __webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__);
|
|
478
646
|
/* harmony import */ var lit__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(lit__WEBPACK_IMPORTED_MODULE_0__);
|
|
479
647
|
/* harmony import */ var lit_directives_unsafe_html_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(lit_directives_unsafe_html_js__WEBPACK_IMPORTED_MODULE_1__);
|
|
480
648
|
/* harmony import */ var _core_js_page_language__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__);
|
|
@@ -485,6 +653,7 @@ var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([lit_
|
|
|
485
653
|
|
|
486
654
|
|
|
487
655
|
|
|
656
|
+
|
|
488
657
|
// The Framework's field renderers, addressed by a property type and a template
|
|
489
658
|
// rather than by a control name.
|
|
490
659
|
//
|
|
@@ -844,7 +1013,13 @@ const FILTER_RENDERERS = {
|
|
|
844
1013
|
relation: renderEntityMultiSelectFilterControl,
|
|
845
1014
|
lookup: renderEntityMultiSelectFilterControl,
|
|
846
1015
|
file: renderFileFilterControl,
|
|
847
|
-
color: renderColorFilterControl
|
|
1016
|
+
color: renderColorFilterControl,
|
|
1017
|
+
// The types a component set draws (property-view port 1.18.0): a money
|
|
1018
|
+
// amount and a percent are filtered as numbers (the host compares the
|
|
1019
|
+
// amount, as a decimal), and an items field by its number of rows.
|
|
1020
|
+
money: renderNumberFilterControl,
|
|
1021
|
+
percent: renderNumberFilterControl,
|
|
1022
|
+
items: renderNumberFilterControl
|
|
848
1023
|
}; // What a field asks to be filtered by: its own declared control if it has one,
|
|
849
1024
|
// otherwise its property type, otherwise text.
|
|
850
1025
|
|
|
@@ -1098,7 +1273,10 @@ const CELL_RENDERERS = {
|
|
|
1098
1273
|
record: renderReferenceCell,
|
|
1099
1274
|
relation: renderReferenceCell,
|
|
1100
1275
|
lookup: renderLookupCell,
|
|
1101
|
-
html: renderHtmlCell
|
|
1276
|
+
html: renderHtmlCell,
|
|
1277
|
+
money: renderMoneyCell,
|
|
1278
|
+
percent: renderPercentCell,
|
|
1279
|
+
items: renderItemsCell
|
|
1102
1280
|
}; // A consumer found this on their stand: the words were written into the set, so
|
|
1103
1281
|
// an English admin saw «Да». The set must not decide what a value is called in
|
|
1104
1282
|
// anyone's language. They come from the field, which is where the rest of the
|
|
@@ -1158,6 +1336,41 @@ function renderChoiceCell(field, value) {
|
|
|
1158
1336
|
function renderHostFormattedCell(field, value, host) {
|
|
1159
1337
|
const formatted = host?.formatFieldValue?.(field, value);
|
|
1160
1338
|
return typeof formatted === 'string' && formatted !== '' ? formatted : null;
|
|
1339
|
+
} // The types a component set draws (property-view port 1.18.0) have their
|
|
1340
|
+
// cells here, so a table shows them wherever the set's editors live. A host's
|
|
1341
|
+
// formatFieldValue wins, as for a date; otherwise the page's language:
|
|
1342
|
+
//
|
|
1343
|
+
// money {amount, currency} or a bare amount under a fixed `currency`;
|
|
1344
|
+
// «1 500,50 ₽», every digit the amount has and at least the
|
|
1345
|
+
// currency's minor units, formatted from the digits, never a float
|
|
1346
|
+
// percent a decimal string of the percent, «12,5 %»
|
|
1347
|
+
// items a list of rows; how many, «3 строки»
|
|
1348
|
+
//
|
|
1349
|
+
// A value that is not one of these shapes falls through to the plain value.
|
|
1350
|
+
|
|
1351
|
+
|
|
1352
|
+
function renderMoneyCell(field, value, host) {
|
|
1353
|
+
const formatted = renderHostFormattedCell(field, value, host);
|
|
1354
|
+
if (formatted !== null) return formatted;
|
|
1355
|
+
const text = (0,_decimal_js__WEBPACK_IMPORTED_MODULE_8__.formatMoney)(scalarValue(value), field, (0,_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__.pageLocale)(host ?? null));
|
|
1356
|
+
return text || null;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
function renderPercentCell(field, value, host) {
|
|
1360
|
+
const formatted = renderHostFormattedCell(field, value, host);
|
|
1361
|
+
if (formatted !== null) return formatted;
|
|
1362
|
+
const text = (0,_decimal_js__WEBPACK_IMPORTED_MODULE_8__.formatPercent)(scalarValue(value), (0,_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__.pageLocale)(host ?? null));
|
|
1363
|
+
return text || null;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
function renderItemsCell(field, value, host) {
|
|
1367
|
+
const formatted = renderHostFormattedCell(field, value, host);
|
|
1368
|
+
if (formatted !== null) return formatted;
|
|
1369
|
+
if (!Array.isArray(value)) return null;
|
|
1370
|
+
return value.length ? (0,_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__.pageWord)(SET_TEXT, 'rowCount', host ?? null, {
|
|
1371
|
+
component: SET_NAME,
|
|
1372
|
+
count: value.length
|
|
1373
|
+
}) : '';
|
|
1161
1374
|
}
|
|
1162
1375
|
|
|
1163
1376
|
function renderNumericCell(field, value, host) {
|
|
@@ -1332,10 +1545,14 @@ function renderPhoneEdit(field, value, host) {
|
|
|
1332
1545
|
// Clearing a set password is not offered: an empty field means «keep».
|
|
1333
1546
|
//
|
|
1334
1547
|
// The typed text lives in the input and nowhere else: no `value` attribute on
|
|
1335
|
-
// sf-input, no data-*, no text node, no log.
|
|
1336
|
-
// switches the input's type and says
|
|
1337
|
-
//
|
|
1338
|
-
//
|
|
1548
|
+
// sf-input, no data-*, no text node, no log. «Показать пароль» is an icon button
|
|
1549
|
+
// that switches the input's type and says what it will do next by its name and
|
|
1550
|
+
// icon: «Показать пароль» (visibility) while hidden, «Скрыть пароль»
|
|
1551
|
+
// (visibility_off) while shown. A toggle whose name changes is not a pressed
|
|
1552
|
+
// toggle (rule C12, point 3): it carries no aria-pressed and no pressed look,
|
|
1553
|
+
// or a reader would hear «Скрыть пароль, нажата». It changes the DOM directly,
|
|
1554
|
+
// so a host's re-render (its attributes are static in the template) leaves the
|
|
1555
|
+
// person's choice as it is.
|
|
1339
1556
|
|
|
1340
1557
|
|
|
1341
1558
|
function isPasswordField(field) {
|
|
@@ -1351,13 +1568,13 @@ function passwordState(host, value) {
|
|
|
1351
1568
|
return setText(host, passwordIsSet(value) ? 'passwordSet' : 'passwordUnset');
|
|
1352
1569
|
}
|
|
1353
1570
|
|
|
1354
|
-
function togglePasswordShown(event) {
|
|
1571
|
+
function togglePasswordShown(event, host) {
|
|
1355
1572
|
const button = event.currentTarget;
|
|
1356
1573
|
const input = button?.closest?.('.sf-field-password')?.querySelector?.('sf-input');
|
|
1357
1574
|
if (!input) return;
|
|
1358
|
-
const shown =
|
|
1575
|
+
const shown = input.getAttribute('input-type') !== 'text';
|
|
1359
1576
|
input.setAttribute('input-type', shown ? 'text' : 'password');
|
|
1360
|
-
button.setAttribute('
|
|
1577
|
+
button.setAttribute('aria-label', setText(host, shown ? 'hidePassword' : 'showPassword'));
|
|
1361
1578
|
button.setAttribute('icon', shown ? 'visibility_off' : 'visibility');
|
|
1362
1579
|
}
|
|
1363
1580
|
|
|
@@ -1393,9 +1610,8 @@ function renderPasswordEdit(field, value, host) {
|
|
|
1393
1610
|
appearance="link"
|
|
1394
1611
|
scheme="on-surface"
|
|
1395
1612
|
icon="visibility"
|
|
1396
|
-
pressed="false"
|
|
1397
1613
|
aria-label=${setText(host, 'showPassword')}
|
|
1398
|
-
@click=${togglePasswordShown}
|
|
1614
|
+
@click=${event => togglePasswordShown(event, host)}
|
|
1399
1615
|
></sf-button-icon>
|
|
1400
1616
|
</span>
|
|
1401
1617
|
`;
|
|
@@ -1658,29 +1874,98 @@ function renderStepperEdit(field, value, host) {
|
|
|
1658
1874
|
@change=${write}
|
|
1659
1875
|
></sf-input-quantity>
|
|
1660
1876
|
`;
|
|
1877
|
+
} // A number is a decimal (property-view port 1.18.0: «never a float on the
|
|
1878
|
+
// wire»). What is typed is read with a comma or a point and spaces between
|
|
1879
|
+
// groups. A number field whose value arrives as a string, or that declares
|
|
1880
|
+
// `precision`, is a decimal-string field: it writes the canonical decimal
|
|
1881
|
+
// string («1500.50»), never a float, and refuses more digits after the point
|
|
1882
|
+
// than `precision` allows. Any other number field writes a number, as before;
|
|
1883
|
+
// an integer field is unchanged. Text that is not a number is reported as a
|
|
1884
|
+
// masked string's incomplete entry is: through setFieldError, which holds the
|
|
1885
|
+
// save, and under the field once the person leaves it.
|
|
1886
|
+
|
|
1887
|
+
|
|
1888
|
+
function isDecimalStringField(field, value) {
|
|
1889
|
+
return (field.propertyType || field.type) === 'number' && (typeof value === 'string' || Number.isInteger(field.precision));
|
|
1661
1890
|
}
|
|
1662
1891
|
|
|
1663
1892
|
function renderNumberEdit(field, value, host) {
|
|
1664
1893
|
if (isStepperField(field)) return renderStepperEdit(field, value, host);
|
|
1665
1894
|
const decimal = (field.propertyType || field.type) === 'number';
|
|
1666
1895
|
value = scalarValue(value);
|
|
1896
|
+
|
|
1897
|
+
if (!decimal) {
|
|
1898
|
+
return (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
1899
|
+
<sf-input
|
|
1900
|
+
size=${EDIT_SIZE}
|
|
1901
|
+
appearance="bordered"
|
|
1902
|
+
name=${fieldName(field)}
|
|
1903
|
+
value=${value ?? ''}
|
|
1904
|
+
inputmode="numeric"
|
|
1905
|
+
?required=${Boolean(field.required)}
|
|
1906
|
+
aria-label=${field.label || field.key || ''}
|
|
1907
|
+
@input=${event => {
|
|
1908
|
+
const next = event.target?.value ?? '';
|
|
1909
|
+
host.setFieldValue(field, next === '' ? null : Number(next));
|
|
1910
|
+
}}
|
|
1911
|
+
@change=${event => {
|
|
1912
|
+
const next = event.target?.value ?? '';
|
|
1913
|
+
host.setFieldValue(field, next === '' ? null : Number(next));
|
|
1914
|
+
}}
|
|
1915
|
+
></sf-input>
|
|
1916
|
+
`;
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
const asString = isDecimalStringField(field, value);
|
|
1920
|
+
|
|
1921
|
+
const write = (event, left) => {
|
|
1922
|
+
const input = event.currentTarget ?? event.target;
|
|
1923
|
+
const text = String(input?.value ?? '');
|
|
1924
|
+
const read = (0,_decimal_js__WEBPACK_IMPORTED_MODULE_8__.readDecimal)(text);
|
|
1925
|
+
let message = null;
|
|
1926
|
+
if (!read.ok) message = setText(host, 'notNumber');else if (read.value !== null && Number.isInteger(field.precision) && (0,_decimal_js__WEBPACK_IMPORTED_MODULE_8__.fractionDigits)(read.value) > field.precision) {
|
|
1927
|
+
message = (0,_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__.pageWord)(SET_TEXT, 'precisionMax', host ?? null, {
|
|
1928
|
+
component: SET_NAME,
|
|
1929
|
+
count: field.precision
|
|
1930
|
+
});
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
if (message) {
|
|
1934
|
+
if (left && input) {
|
|
1935
|
+
input.setAttribute('error-message', message);
|
|
1936
|
+
input.toggleAttribute('invalid', true);
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
if (typeof host.setFieldError === 'function') {
|
|
1940
|
+
host.setFieldError(field, message);
|
|
1941
|
+
host.setFieldValue(field, text);
|
|
1942
|
+
} else {
|
|
1943
|
+
host.setFieldValue(field, undefined);
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
return;
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
if (input?.hasAttribute?.('invalid')) {
|
|
1950
|
+
input.toggleAttribute('invalid', false);
|
|
1951
|
+
input.removeAttribute('error-message');
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
host.setFieldError?.(field, null);
|
|
1955
|
+
if (read.value === null) host.setFieldValue(field, null);else host.setFieldValue(field, asString ? read.value : Number(read.value));
|
|
1956
|
+
};
|
|
1957
|
+
|
|
1667
1958
|
return (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
1668
1959
|
<sf-input
|
|
1669
1960
|
size=${EDIT_SIZE}
|
|
1670
1961
|
appearance="bordered"
|
|
1671
1962
|
name=${fieldName(field)}
|
|
1672
1963
|
value=${value ?? ''}
|
|
1673
|
-
inputmode
|
|
1964
|
+
inputmode="decimal"
|
|
1674
1965
|
?required=${Boolean(field.required)}
|
|
1675
1966
|
aria-label=${field.label || field.key || ''}
|
|
1676
|
-
@input=${event =>
|
|
1677
|
-
|
|
1678
|
-
host.setFieldValue(field, next === '' ? null : Number(next));
|
|
1679
|
-
}}
|
|
1680
|
-
@change=${event => {
|
|
1681
|
-
const next = event.target?.value ?? '';
|
|
1682
|
-
host.setFieldValue(field, next === '' ? null : Number(next));
|
|
1683
|
-
}}
|
|
1967
|
+
@input=${event => write(event, false)}
|
|
1968
|
+
@change=${event => write(event, true)}
|
|
1684
1969
|
></sf-input>
|
|
1685
1970
|
`;
|
|
1686
1971
|
} // A date control has no clock. `sf-input-date` declares a format and a date,
|
|
@@ -2123,7 +2408,14 @@ const SET_TEXT = {
|
|
|
2123
2408
|
passwordUnset: 'Not set',
|
|
2124
2409
|
passwordKeep: 'Set; type a new one to replace it',
|
|
2125
2410
|
showPassword: 'Show password',
|
|
2126
|
-
|
|
2411
|
+
hidePassword: 'Hide password',
|
|
2412
|
+
maskIncomplete: 'Complete the field',
|
|
2413
|
+
notNumber: 'Enter a number',
|
|
2414
|
+
precisionMax: 'At most {n} digits after the point',
|
|
2415
|
+
rowCount: {
|
|
2416
|
+
one: '{n} row',
|
|
2417
|
+
other: '{n} rows'
|
|
2418
|
+
}
|
|
2127
2419
|
},
|
|
2128
2420
|
ru: {
|
|
2129
2421
|
yes: 'Да',
|
|
@@ -2151,7 +2443,16 @@ const SET_TEXT = {
|
|
|
2151
2443
|
passwordUnset: 'Не задан',
|
|
2152
2444
|
passwordKeep: 'Задан; введите новый, чтобы заменить',
|
|
2153
2445
|
showPassword: 'Показать пароль',
|
|
2154
|
-
|
|
2446
|
+
hidePassword: 'Скрыть пароль',
|
|
2447
|
+
maskIncomplete: 'Заполните поле полностью',
|
|
2448
|
+
notNumber: 'Введите число',
|
|
2449
|
+
precisionMax: 'Не больше {n} знаков после запятой',
|
|
2450
|
+
rowCount: {
|
|
2451
|
+
one: '{n} строка',
|
|
2452
|
+
few: '{n} строки',
|
|
2453
|
+
many: '{n} строк',
|
|
2454
|
+
other: '{n} строки'
|
|
2455
|
+
}
|
|
2155
2456
|
}
|
|
2156
2457
|
}; // The host's word wins, as it always did; the set's own fallback now follows
|
|
2157
2458
|
// the page's language instead of being Russian whatever the page is. `host` is
|
|
@@ -2668,10 +2969,13 @@ function renderChoiceView(field, value) {
|
|
|
2668
2969
|
const options = field.options || field.filter?.options || field.constraints?.options;
|
|
2669
2970
|
const option = Array.isArray(options) ? options.find(item => String(item?.value) === String(value)) : null;
|
|
2670
2971
|
if (!option) return lit__WEBPACK_IMPORTED_MODULE_0__.nothing;
|
|
2671
|
-
const badge = (0,_helpers_option_badge_js__WEBPACK_IMPORTED_MODULE_2__.optionBadgeProps)(option);
|
|
2972
|
+
const badge = (0,_helpers_option_badge_js__WEBPACK_IMPORTED_MODULE_2__.optionBadgeProps)(option); // A table cell is a dense place: the badge keeps the tight side room
|
|
2973
|
+
// (sf-badge tight), as it had before sf-badge 1.3.0 widened the default.
|
|
2974
|
+
|
|
2672
2975
|
return (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
2673
2976
|
<sf-badge
|
|
2674
2977
|
line=${VIEW_LINE}
|
|
2978
|
+
tight
|
|
2675
2979
|
appearance=${field.badgeAppearance ?? CELL_BADGE_APPEARANCE}
|
|
2676
2980
|
scheme=${badge.scheme}
|
|
2677
2981
|
style=${badge.rootStyle || lit__WEBPACK_IMPORTED_MODULE_0__.nothing}
|
|
@@ -2997,12 +3301,13 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
2997
3301
|
/* harmony export */ modesOf: () => (/* binding */ modesOf)
|
|
2998
3302
|
/* harmony export */ });
|
|
2999
3303
|
// The registry of universal properties (owner, 2026-10-15): every property
|
|
3000
|
-
// type the Framework renders,
|
|
3001
|
-
//
|
|
3002
|
-
//
|
|
3003
|
-
// from the renderers' source. Which
|
|
3004
|
-
// is
|
|
3005
|
-
//
|
|
3304
|
+
// type the Framework renders, or a component set renders beside it (`set`),
|
|
3305
|
+
// its variants and the parameters a field of it reads -- one list a host
|
|
3306
|
+
// (Larena's fields and settings), the property workbench and the documentation
|
|
3307
|
+
// read, instead of each learning the types from the renderers' source. Which
|
|
3308
|
+
// modes a type has is not written here: it is read from the renderer maps
|
|
3309
|
+
// themselves (modesOf), so the registry cannot claim a mode the set does not
|
|
3310
|
+
// draw. A contract keeps the two the same.
|
|
3006
3311
|
//
|
|
3007
3312
|
// `params` are the field keys the type's renderers read -- each one, and no
|
|
3008
3313
|
// other (the probe field-renderer-params calls them with a field that records
|
|
@@ -3257,6 +3562,58 @@ const PROPERTY_TYPES = Object.freeze([{
|
|
|
3257
3562
|
},
|
|
3258
3563
|
variants: [],
|
|
3259
3564
|
params: ['language', 'required', 'readonly']
|
|
3565
|
+
}, // The types a component set draws (property-view port 1.18.0; Larena
|
|
3566
|
+
// property types standard, notes of 2026-11-03): `set` names the set whose
|
|
3567
|
+
// components show and edit them -- the View set's sf-view-properties and
|
|
3568
|
+
// sf-view-flow -- and the Framework draws their table cells and filters
|
|
3569
|
+
// (money and percent as numbers, items by its number of rows). `params` are
|
|
3570
|
+
// the keys the type reads, in the set's renderers and in these. Every
|
|
3571
|
+
// number is a decimal string, never a float.
|
|
3572
|
+
{
|
|
3573
|
+
type: 'money',
|
|
3574
|
+
label: {
|
|
3575
|
+
ru: 'Деньги',
|
|
3576
|
+
en: 'Money'
|
|
3577
|
+
},
|
|
3578
|
+
set: 'view',
|
|
3579
|
+
variants: [],
|
|
3580
|
+
params: ['currency', 'min', 'max', 'required', 'readonly']
|
|
3581
|
+
}, {
|
|
3582
|
+
type: 'percent',
|
|
3583
|
+
label: {
|
|
3584
|
+
ru: 'Процент',
|
|
3585
|
+
en: 'Percent'
|
|
3586
|
+
},
|
|
3587
|
+
set: 'view',
|
|
3588
|
+
variants: [],
|
|
3589
|
+
params: ['min', 'max', 'required', 'readonly']
|
|
3590
|
+
}, {
|
|
3591
|
+
type: 'items',
|
|
3592
|
+
label: {
|
|
3593
|
+
ru: 'Строки',
|
|
3594
|
+
en: 'Rows'
|
|
3595
|
+
},
|
|
3596
|
+
set: 'view',
|
|
3597
|
+
variants: [],
|
|
3598
|
+
params: ['columns', 'minRows', 'maxRows', 'required', 'readonly']
|
|
3599
|
+
}, {
|
|
3600
|
+
type: 'compound',
|
|
3601
|
+
label: {
|
|
3602
|
+
ru: 'Составное',
|
|
3603
|
+
en: 'Compound'
|
|
3604
|
+
},
|
|
3605
|
+
set: 'view',
|
|
3606
|
+
variants: [],
|
|
3607
|
+
params: ['parts', 'required', 'readonly']
|
|
3608
|
+
}, {
|
|
3609
|
+
type: 'ru.requisites',
|
|
3610
|
+
label: {
|
|
3611
|
+
ru: 'Реквизиты',
|
|
3612
|
+
en: 'Requisites (Russia)'
|
|
3613
|
+
},
|
|
3614
|
+
set: 'view',
|
|
3615
|
+
variants: [],
|
|
3616
|
+
params: ['parts', 'required', 'readonly']
|
|
3260
3617
|
}]); // The modes a type has, read from the renderer maps: view and edit (a record),
|
|
3261
3618
|
// cell (a table) and filter -- `own` when the set has a filter control for the
|
|
3262
3619
|
// type, `text` when a filter falls back to a text search.
|
|
@@ -4517,6 +4874,226 @@ function fieldConditions(field, values) {
|
|
|
4517
4874
|
|
|
4518
4875
|
/***/ },
|
|
4519
4876
|
|
|
4877
|
+
/***/ "f44b979883f0"
|
|
4878
|
+
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
4879
|
+
|
|
4880
|
+
__webpack_require__.r(__webpack_exports__);
|
|
4881
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
4882
|
+
/* harmony export */ add: () => (/* binding */ add),
|
|
4883
|
+
/* harmony export */ compare: () => (/* binding */ compare),
|
|
4884
|
+
/* harmony export */ decimalString: () => (/* binding */ decimalString),
|
|
4885
|
+
/* harmony export */ editableDecimal: () => (/* binding */ editableDecimal),
|
|
4886
|
+
/* harmony export */ formatDecimal: () => (/* binding */ formatDecimal),
|
|
4887
|
+
/* harmony export */ fractionDigits: () => (/* binding */ fractionDigits),
|
|
4888
|
+
/* harmony export */ multiply: () => (/* binding */ multiply),
|
|
4889
|
+
/* harmony export */ readDecimal: () => (/* binding */ readDecimal),
|
|
4890
|
+
/* harmony export */ round: () => (/* binding */ round),
|
|
4891
|
+
/* harmony export */ share: () => (/* binding */ share),
|
|
4892
|
+
/* harmony export */ toDecimal: () => (/* binding */ toDecimal)
|
|
4893
|
+
/* harmony export */ });
|
|
4894
|
+
// Decimal numbers as strings, never as floats (Larena property types standard,
|
|
4895
|
+
// notes of 2026-11-03: «Arithmetic is on decimal digits, never on floats»;
|
|
4896
|
+
// «Never a float on the wire»). A money amount, a percent and every cell a row
|
|
4897
|
+
// computes are read, multiplied, summed and rounded here on BigInt units, so
|
|
4898
|
+
// 0.1 + 0.2 is 0.3 and 2 499,99 stays 2 499,99 however many rows add up to it.
|
|
4899
|
+
//
|
|
4900
|
+
// A decimal is {units, scale}: the value is units / 10^scale. The canonical
|
|
4901
|
+
// string has an optional minus, the integer digits without leading zeros and
|
|
4902
|
+
// the fraction digits as written ("1500.50", "0.5", "-3"); "-0" is "0".
|
|
4903
|
+
const TYPED = /^([+-]?)(\d*)(?:[.,](\d*))?$/u; // Spaces a person or a formatter puts between groups of digits: the plain
|
|
4904
|
+
// space, the no-break space and the narrow no-break space ru-RU writes.
|
|
4905
|
+
|
|
4906
|
+
const GROUP_SPACES = /[\s ']/gu;
|
|
4907
|
+
/**
|
|
4908
|
+
* What a person typed, read as a decimal: `{ok: true, value}` with the
|
|
4909
|
+
* canonical string (null for nothing typed), or `{ok: false}` for text that is
|
|
4910
|
+
* not a number. A comma and a point both separate the fraction; spaces between
|
|
4911
|
+
* groups go.
|
|
4912
|
+
*/
|
|
4913
|
+
|
|
4914
|
+
function readDecimal(text) {
|
|
4915
|
+
if (text === null || typeof text === 'undefined') return {
|
|
4916
|
+
ok: true,
|
|
4917
|
+
value: null
|
|
4918
|
+
};
|
|
4919
|
+
|
|
4920
|
+
if (typeof text === 'number') {
|
|
4921
|
+
// A JSON integer is taken as its digits (property types standard §5); a
|
|
4922
|
+
// float is not a decimal and is refused rather than guessed at.
|
|
4923
|
+
return Number.isSafeInteger(text) ? {
|
|
4924
|
+
ok: true,
|
|
4925
|
+
value: String(text)
|
|
4926
|
+
} : {
|
|
4927
|
+
ok: false
|
|
4928
|
+
};
|
|
4929
|
+
}
|
|
4930
|
+
|
|
4931
|
+
const plain = String(text).replace(GROUP_SPACES, '');
|
|
4932
|
+
if (plain === '') return {
|
|
4933
|
+
ok: true,
|
|
4934
|
+
value: null
|
|
4935
|
+
};
|
|
4936
|
+
const match = TYPED.exec(plain);
|
|
4937
|
+
if (!match) return {
|
|
4938
|
+
ok: false
|
|
4939
|
+
};
|
|
4940
|
+
const [, sign, whole, fraction] = match;
|
|
4941
|
+
if (whole === '' && (fraction ?? '') === '') return {
|
|
4942
|
+
ok: false
|
|
4943
|
+
};
|
|
4944
|
+
const integer = whole.replace(/^0+(?=\d)/u, '') || '0';
|
|
4945
|
+
const digits = fraction ?? '';
|
|
4946
|
+
const zero = /^0*$/u.test(integer + digits);
|
|
4947
|
+
const value = `${sign === '-' && !zero ? '-' : ''}${integer}${digits ? `.${digits}` : ''}`;
|
|
4948
|
+
return {
|
|
4949
|
+
ok: true,
|
|
4950
|
+
value
|
|
4951
|
+
};
|
|
4952
|
+
}
|
|
4953
|
+
/** The decimal of a canonical string or a JSON integer, or null. */
|
|
4954
|
+
|
|
4955
|
+
function toDecimal(value) {
|
|
4956
|
+
if (value && typeof value === 'object' && typeof value.units === 'bigint') return value;
|
|
4957
|
+
const read = readDecimal(value);
|
|
4958
|
+
if (!read.ok || read.value === null) return null;
|
|
4959
|
+
const negative = read.value.startsWith('-');
|
|
4960
|
+
const [whole, fraction = ''] = read.value.replace('-', '').split('.');
|
|
4961
|
+
const units = BigInt(whole + fraction) * (negative ? -1n : 1n);
|
|
4962
|
+
return {
|
|
4963
|
+
units,
|
|
4964
|
+
scale: fraction.length
|
|
4965
|
+
};
|
|
4966
|
+
}
|
|
4967
|
+
/** How many digits a decimal string has after its point. */
|
|
4968
|
+
|
|
4969
|
+
function fractionDigits(value) {
|
|
4970
|
+
const read = readDecimal(value);
|
|
4971
|
+
if (!read.ok || read.value === null) return 0;
|
|
4972
|
+
const at = read.value.indexOf('.');
|
|
4973
|
+
return at === -1 ? 0 : read.value.length - at - 1;
|
|
4974
|
+
}
|
|
4975
|
+
const POWERS = new Map();
|
|
4976
|
+
|
|
4977
|
+
const power = n => {
|
|
4978
|
+
if (!POWERS.has(n)) POWERS.set(n, 10n ** BigInt(n));
|
|
4979
|
+
return POWERS.get(n);
|
|
4980
|
+
};
|
|
4981
|
+
|
|
4982
|
+
function rescale(decimal, scale) {
|
|
4983
|
+
return decimal.scale === scale ? decimal : {
|
|
4984
|
+
units: decimal.units * power(scale - decimal.scale),
|
|
4985
|
+
scale
|
|
4986
|
+
};
|
|
4987
|
+
}
|
|
4988
|
+
|
|
4989
|
+
function multiply(a, b) {
|
|
4990
|
+
return {
|
|
4991
|
+
units: a.units * b.units,
|
|
4992
|
+
scale: a.scale + b.scale
|
|
4993
|
+
};
|
|
4994
|
+
}
|
|
4995
|
+
function add(a, b) {
|
|
4996
|
+
const scale = Math.max(a.scale, b.scale);
|
|
4997
|
+
return {
|
|
4998
|
+
units: rescale(a, scale).units + rescale(b, scale).units,
|
|
4999
|
+
scale
|
|
5000
|
+
};
|
|
5001
|
+
}
|
|
5002
|
+
/** A share: 20 percent is 0.2 (property types standard, rows: «A percent operand counts as its share»). */
|
|
5003
|
+
|
|
5004
|
+
function share(decimal) {
|
|
5005
|
+
return {
|
|
5006
|
+
units: decimal.units,
|
|
5007
|
+
scale: decimal.scale + 2
|
|
5008
|
+
};
|
|
5009
|
+
}
|
|
5010
|
+
/** Rounded half away from zero to `scale` digits after the point. */
|
|
5011
|
+
|
|
5012
|
+
function round(decimal, scale) {
|
|
5013
|
+
if (decimal.scale <= scale) return rescale(decimal, scale);
|
|
5014
|
+
const divisor = power(decimal.scale - scale);
|
|
5015
|
+
const negative = decimal.units < 0n;
|
|
5016
|
+
const magnitude = negative ? -decimal.units : decimal.units;
|
|
5017
|
+
let units = magnitude / divisor;
|
|
5018
|
+
if (magnitude % divisor * 2n >= divisor) units += 1n;
|
|
5019
|
+
return {
|
|
5020
|
+
units: negative ? -units : units,
|
|
5021
|
+
scale
|
|
5022
|
+
};
|
|
5023
|
+
}
|
|
5024
|
+
function compare(a, b) {
|
|
5025
|
+
const scale = Math.max(a.scale, b.scale);
|
|
5026
|
+
const left = rescale(a, scale).units;
|
|
5027
|
+
const right = rescale(b, scale).units;
|
|
5028
|
+
return left === right ? 0 : left < right ? -1 : 1;
|
|
5029
|
+
}
|
|
5030
|
+
/** The canonical string of a decimal, with exactly its scale's digits after the point. */
|
|
5031
|
+
|
|
5032
|
+
function decimalString(decimal) {
|
|
5033
|
+
const negative = decimal.units < 0n;
|
|
5034
|
+
const digits = (negative ? -decimal.units : decimal.units).toString().padStart(decimal.scale + 1, '0');
|
|
5035
|
+
const whole = decimal.scale ? digits.slice(0, -decimal.scale) : digits;
|
|
5036
|
+
const fraction = decimal.scale ? digits.slice(-decimal.scale) : '';
|
|
5037
|
+
const zero = decimal.units === 0n;
|
|
5038
|
+
return `${negative && !zero ? '-' : ''}${whole}${fraction ? `.${fraction}` : ''}`;
|
|
5039
|
+
} // The separators of a locale, read once from Intl and kept: a decimal is
|
|
5040
|
+
// formatted from its digits, so no amount passes through a float to be shown.
|
|
5041
|
+
|
|
5042
|
+
const SEPARATORS = new Map();
|
|
5043
|
+
|
|
5044
|
+
function separators(locale) {
|
|
5045
|
+
if (!SEPARATORS.has(locale)) {
|
|
5046
|
+
let group = ' ';
|
|
5047
|
+
let point = '.';
|
|
5048
|
+
|
|
5049
|
+
try {
|
|
5050
|
+
for (const part of new Intl.NumberFormat(locale).formatToParts(12345.6)) {
|
|
5051
|
+
if (part.type === 'group') group = part.value;
|
|
5052
|
+
if (part.type === 'decimal') point = part.value;
|
|
5053
|
+
}
|
|
5054
|
+
} catch {// An unknown locale keeps the defaults.
|
|
5055
|
+
}
|
|
5056
|
+
|
|
5057
|
+
SEPARATORS.set(locale, {
|
|
5058
|
+
group,
|
|
5059
|
+
point
|
|
5060
|
+
});
|
|
5061
|
+
}
|
|
5062
|
+
|
|
5063
|
+
return SEPARATORS.get(locale);
|
|
5064
|
+
}
|
|
5065
|
+
/**
|
|
5066
|
+
* A decimal string as a person reads it in `locale`: groups of three, the
|
|
5067
|
+
* locale's point, and at least `minFraction` digits after it («1 500,50»).
|
|
5068
|
+
* Groups start at four digits, as the standard reads money («1 500,50 ₽»).
|
|
5069
|
+
*/
|
|
5070
|
+
|
|
5071
|
+
|
|
5072
|
+
function formatDecimal(value, locale, {
|
|
5073
|
+
minFraction = 0
|
|
5074
|
+
} = {}) {
|
|
5075
|
+
const decimal = toDecimal(value);
|
|
5076
|
+
if (!decimal) return '';
|
|
5077
|
+
const text = decimalString(decimal.scale < minFraction ? rescale(decimal, minFraction) : decimal);
|
|
5078
|
+
const negative = text.startsWith('-');
|
|
5079
|
+
const [whole, fraction] = text.replace('-', '').split('.');
|
|
5080
|
+
const {
|
|
5081
|
+
group,
|
|
5082
|
+
point
|
|
5083
|
+
} = separators(locale);
|
|
5084
|
+
const grouped = whole.replace(/\B(?=(\d{3})+(?!\d))/gu, group);
|
|
5085
|
+
return `${negative ? '−' : ''}${grouped}${fraction ? `${point}${fraction}` : ''}`;
|
|
5086
|
+
}
|
|
5087
|
+
/** A decimal string as typed in an input: the locale's point, no groups («1500,5»). */
|
|
5088
|
+
|
|
5089
|
+
function editableDecimal(value, locale) {
|
|
5090
|
+
const read = readDecimal(value);
|
|
5091
|
+
if (!read.ok || read.value === null) return '';
|
|
5092
|
+
return read.value.replace('.', separators(locale).point);
|
|
5093
|
+
}
|
|
5094
|
+
|
|
5095
|
+
/***/ },
|
|
5096
|
+
|
|
4520
5097
|
/***/ "62c5c417c97d"
|
|
4521
5098
|
(module, __webpack_exports__, __webpack_require__) {
|
|
4522
5099
|
|
|
@@ -4527,10 +5104,12 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
4527
5104
|
/* harmony export */ });
|
|
4528
5105
|
/* harmony import */ var lit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("2a080a730149");
|
|
4529
5106
|
/* harmony import */ var _field_renderers_index_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("e5483002f3ea");
|
|
4530
|
-
var
|
|
4531
|
-
([lit__WEBPACK_IMPORTED_MODULE_0__, _field_renderers_index_js__WEBPACK_IMPORTED_MODULE_1__]
|
|
5107
|
+
/* harmony import */ var _set_fields_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__("db94c32f22be");
|
|
5108
|
+
var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([lit__WEBPACK_IMPORTED_MODULE_0__, _field_renderers_index_js__WEBPACK_IMPORTED_MODULE_1__, _set_fields_js__WEBPACK_IMPORTED_MODULE_2__]);
|
|
5109
|
+
([lit__WEBPACK_IMPORTED_MODULE_0__, _field_renderers_index_js__WEBPACK_IMPORTED_MODULE_1__, _set_fields_js__WEBPACK_IMPORTED_MODULE_2__] = __webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__);
|
|
4532
5110
|
/* harmony import */ var lit__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(lit__WEBPACK_IMPORTED_MODULE_0__);
|
|
4533
5111
|
|
|
5112
|
+
|
|
4534
5113
|
// One field's value, shown or edited, drawn by the SIMAI Framework field
|
|
4535
5114
|
// renderers. Moved out of the record component's template (sf-view-properties
|
|
4536
5115
|
// 1.10.0) so that every view of the set that draws a field draws it the same
|
|
@@ -4568,12 +5147,27 @@ function renderFieldValue(context, field, editing, {
|
|
|
4568
5147
|
setFieldOptionSearch: (target, text) => context.component.setFieldOptionSearch(target, text),
|
|
4569
5148
|
loadFieldOptions: (target, request) => context.component.loadFieldOptions(target, request)
|
|
4570
5149
|
} : {})
|
|
4571
|
-
}; //
|
|
5150
|
+
}; // Money, a percent, rows and the compound fields are the set's own
|
|
5151
|
+
// (js/set-fields.js, Larena addendum of 2026-11-03): drawn from Framework
|
|
5152
|
+
// controls, with the edit host above, before the Framework renderers are
|
|
5153
|
+
// asked for a type they do not draw.
|
|
5154
|
+
|
|
5155
|
+
const own = (0,_set_fields_js__WEBPACK_IMPORTED_MODULE_2__.renderSetField)(field, context.changed.has(key) && editing ? context.changed.get(key) : field.value, editing, editHost, {
|
|
5156
|
+
node: context.component,
|
|
5157
|
+
size: context.size,
|
|
5158
|
+
viewHost: context.viewHost,
|
|
5159
|
+
disabled
|
|
5160
|
+
});
|
|
5161
|
+
|
|
5162
|
+
if (own !== null) {
|
|
5163
|
+
return own === lit__WEBPACK_IMPORTED_MODULE_0__.nothing ? (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="sf-property-view-empty color-on-surface-variant">${context.emptyText}</span>` : own;
|
|
5164
|
+
} // A datetime and a time of day are the renderer set's: sf-input-date in
|
|
4572
5165
|
// mode datetime and time, one control each (SIMAI Framework duplication
|
|
4573
5166
|
// audit A-14). This view drew its own pair -- the day in a datepicker and
|
|
4574
5167
|
// the time in a masked field -- which cut the day short in a half-width
|
|
4575
5168
|
// field and held half-typed entries of its own.
|
|
4576
5169
|
|
|
5170
|
+
|
|
4577
5171
|
const content = editing ? (0,_field_renderers_index_js__WEBPACK_IMPORTED_MODULE_1__.renderEditField)(field, context.changed.has(key) ? context.changed.get(key) : field.value, editHost, // A field its condition turns off is drawn disabled where the set
|
|
4578
5172
|
// draws disabled apart from read-only (a choice as buttons).
|
|
4579
5173
|
{
|
|
@@ -4628,50 +5222,1352 @@ __webpack_async_result__();
|
|
|
4628
5222
|
|
|
4629
5223
|
/***/ },
|
|
4630
5224
|
|
|
4631
|
-
/***/ "
|
|
4632
|
-
(
|
|
5225
|
+
/***/ "d69ebe7e288e"
|
|
5226
|
+
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
4633
5227
|
|
|
4634
|
-
__webpack_require__.a(module, async (__webpack_handle_async_dependencies__, __webpack_async_result__) => { try {
|
|
4635
5228
|
__webpack_require__.r(__webpack_exports__);
|
|
4636
5229
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
4637
|
-
/* harmony export */
|
|
4638
|
-
/* harmony export */
|
|
5230
|
+
/* harmony export */ currencyOf: () => (/* binding */ currencyOf),
|
|
5231
|
+
/* harmony export */ formatMoney: () => (/* binding */ formatMoney),
|
|
5232
|
+
/* harmony export */ moneyParts: () => (/* binding */ moneyParts),
|
|
5233
|
+
/* harmony export */ percentText: () => (/* binding */ percentText)
|
|
4639
5234
|
/* harmony export */ });
|
|
4640
|
-
/* harmony import */ var
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
// A date, a date and time or a time of day in the page's language, through
|
|
4645
|
-
// the SIMAI Framework's format-date module, for a value the host did not
|
|
4646
|
-
// format (formatFieldValue). The host's own text still wins; without one, a
|
|
4647
|
-
// date read «2026-11-02» where a person expects «2 нояб. 2026 г.».
|
|
4648
|
-
//
|
|
4649
|
-
// The forms are read as a host stores them: a day «YYYY-MM-DD» is that day on
|
|
4650
|
-
// the reader's calendar (not UTC midnight, which is the day before west of
|
|
4651
|
-
// Greenwich); a date and time with a zone is that moment in the reader's zone
|
|
4652
|
-
// and one without a zone is the wall clock as written; a time «hh:mm[:ss]» is
|
|
4653
|
-
// a time of day. Anything else is left for the caller to print as it came.
|
|
4654
|
-
|
|
4655
|
-
const DAY = /^(\d{4})-(\d{2})-(\d{2})$/u;
|
|
4656
|
-
const TIME = /^(\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?$/u;
|
|
4657
|
-
const DATE_TIME = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?$/u;
|
|
4658
|
-
const DATE_TYPES = new Set(['date', 'datetime', 'time']);
|
|
5235
|
+
/* harmony import */ var _decimal_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("f44b979883f0");
|
|
5236
|
+
// Money and percent as a person reads them, from decimal strings, never a
|
|
5237
|
+
// float: the currency's sign and minor units come from Intl, the digits from
|
|
5238
|
+
// ./decimal.js. Pure, so the unit suite reads it without a browser.
|
|
4659
5239
|
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
if (day) return new Date(Number(day[1]), Number(day[2]) - 1, Number(day[3]));
|
|
4663
|
-
const time = TIME.exec(text);
|
|
4664
|
-
if (time) return type === 'time' ? new Date(2000, 0, 1, Number(time[1]), Number(time[2]), Number(time[3] ?? 0)) : null;
|
|
4665
|
-
const stamp = DATE_TIME.exec(text);
|
|
4666
|
-
if (!stamp) return null;
|
|
5240
|
+
const CURRENCY = new Map();
|
|
5241
|
+
/** {sign, before, minor}: the sign a locale writes, whether before the amount, and the ISO 4217 minor units. */
|
|
4667
5242
|
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
return Number.isNaN(moment.getTime()) ? null : moment;
|
|
4671
|
-
}
|
|
5243
|
+
function currencyOf(code, locale) {
|
|
5244
|
+
const id = `${code}|${locale}`;
|
|
4672
5245
|
|
|
4673
|
-
|
|
4674
|
-
|
|
5246
|
+
if (!CURRENCY.has(id)) {
|
|
5247
|
+
let sign = code;
|
|
5248
|
+
let before = false;
|
|
5249
|
+
let minor = 2;
|
|
5250
|
+
|
|
5251
|
+
try {
|
|
5252
|
+
const format = new Intl.NumberFormat(locale, {
|
|
5253
|
+
style: 'currency',
|
|
5254
|
+
currency: code,
|
|
5255
|
+
currencyDisplay: 'narrowSymbol'
|
|
5256
|
+
});
|
|
5257
|
+
const parts = format.formatToParts(1);
|
|
5258
|
+
const at = parts.findIndex(part => part.type === 'currency');
|
|
5259
|
+
sign = parts[at]?.value ?? code;
|
|
5260
|
+
before = at < parts.findIndex(part => part.type === 'integer');
|
|
5261
|
+
minor = format.resolvedOptions().maximumFractionDigits;
|
|
5262
|
+
} catch {// A code Intl does not know keeps its letters and two minor digits.
|
|
5263
|
+
}
|
|
5264
|
+
|
|
5265
|
+
CURRENCY.set(id, {
|
|
5266
|
+
sign,
|
|
5267
|
+
before,
|
|
5268
|
+
minor
|
|
5269
|
+
});
|
|
5270
|
+
}
|
|
5271
|
+
|
|
5272
|
+
return CURRENCY.get(id);
|
|
5273
|
+
}
|
|
5274
|
+
/** An amount with its currency, as a person reads it: «1 500,50 ₽», «$1,500.50». */
|
|
5275
|
+
|
|
5276
|
+
function formatMoney(amount, code, locale) {
|
|
5277
|
+
if (!code) return (0,_decimal_js__WEBPACK_IMPORTED_MODULE_0__.formatDecimal)(amount, locale);
|
|
5278
|
+
const {
|
|
5279
|
+
sign,
|
|
5280
|
+
before,
|
|
5281
|
+
minor
|
|
5282
|
+
} = currencyOf(code, locale);
|
|
5283
|
+
const number = (0,_decimal_js__WEBPACK_IMPORTED_MODULE_0__.formatDecimal)(amount, locale, {
|
|
5284
|
+
minFraction: minor
|
|
5285
|
+
});
|
|
5286
|
+
if (!number) return '';
|
|
5287
|
+
return before ? `${sign}${number}` : `${number} ${sign}`;
|
|
5288
|
+
}
|
|
5289
|
+
/** A money value as {amount, currency}; a field that fixes the currency takes a bare amount. */
|
|
5290
|
+
|
|
5291
|
+
function moneyParts(value, fixed) {
|
|
5292
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
5293
|
+
return {
|
|
5294
|
+
amount: value.amount ?? null,
|
|
5295
|
+
currency: fixed || (value.currency ?? null)
|
|
5296
|
+
};
|
|
5297
|
+
}
|
|
5298
|
+
|
|
5299
|
+
if (value === null || typeof value === 'undefined' || value === '') return {
|
|
5300
|
+
amount: null,
|
|
5301
|
+
currency: fixed || null
|
|
5302
|
+
};
|
|
5303
|
+
return {
|
|
5304
|
+
amount: value,
|
|
5305
|
+
currency: fixed || null
|
|
5306
|
+
};
|
|
5307
|
+
}
|
|
5308
|
+
function percentText(value, locale) {
|
|
5309
|
+
const number = (0,_decimal_js__WEBPACK_IMPORTED_MODULE_0__.formatDecimal)(value, locale);
|
|
5310
|
+
if (!number) return ''; // ru-RU writes «12,5 %», en «12.5%»: the space is the locale's, read from Intl.
|
|
5311
|
+
|
|
5312
|
+
let spaced = false;
|
|
5313
|
+
|
|
5314
|
+
try {
|
|
5315
|
+
spaced = new Intl.NumberFormat(locale, {
|
|
5316
|
+
style: 'percent'
|
|
5317
|
+
}).formatToParts(0.5).some(part => part.type === 'literal' && /\s/u.test(part.value));
|
|
5318
|
+
} catch {
|
|
5319
|
+
spaced = false;
|
|
5320
|
+
}
|
|
5321
|
+
|
|
5322
|
+
return `${number}${spaced ? ' ' : ''}%`;
|
|
5323
|
+
}
|
|
5324
|
+
|
|
5325
|
+
/***/ },
|
|
5326
|
+
|
|
5327
|
+
/***/ "06dfc7b3b9d9"
|
|
5328
|
+
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
5329
|
+
|
|
5330
|
+
__webpack_require__.r(__webpack_exports__);
|
|
5331
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
5332
|
+
/* harmony export */ REQUISITE_PARTS: () => (/* binding */ REQUISITE_PARTS),
|
|
5333
|
+
/* harmony export */ innValid: () => (/* binding */ innValid),
|
|
5334
|
+
/* harmony export */ ogrnValid: () => (/* binding */ ogrnValid),
|
|
5335
|
+
/* harmony export */ requisiteErrors: () => (/* binding */ requisiteErrors)
|
|
5336
|
+
/* harmony export */ });
|
|
5337
|
+
// The checks of Russian requisites (Larena property types standard, notes of
|
|
5338
|
+
// 2026-11-03, `ru.requisites@1`): each part on its own, and the parts that go
|
|
5339
|
+
// together, together. The server checks the same and is the authority; these
|
|
5340
|
+
// are here so a person sees a mistyped ИНН under the field instead of a refused
|
|
5341
|
+
// save after it.
|
|
5342
|
+
//
|
|
5343
|
+
// Every check answers the key of the words that explain the mistake, or null.
|
|
5344
|
+
const REQUISITE_PARTS = Object.freeze(['inn', 'kpp', 'ogrn', 'bik', 'account', 'corr_account']);
|
|
5345
|
+
|
|
5346
|
+
const digitsOf = text => [...text].map(Number);
|
|
5347
|
+
|
|
5348
|
+
const weighted = (digits, weights) => weights.reduce((sum, weight, index) => sum + weight * digits[index], 0);
|
|
5349
|
+
|
|
5350
|
+
function innValid(inn) {
|
|
5351
|
+
if (!/^\d{10}$|^\d{12}$/u.test(inn)) return false;
|
|
5352
|
+
const d = digitsOf(inn);
|
|
5353
|
+
if (inn.length === 10) return weighted(d, [2, 4, 10, 3, 5, 9, 4, 6, 8]) % 11 % 10 === d[9];
|
|
5354
|
+
const eleventh = weighted(d, [7, 2, 4, 10, 3, 5, 9, 4, 6, 8]) % 11 % 10;
|
|
5355
|
+
const twelfth = weighted(d, [3, 7, 2, 4, 10, 3, 5, 9, 4, 6, 8]) % 11 % 10;
|
|
5356
|
+
return eleventh === d[10] && twelfth === d[11];
|
|
5357
|
+
}
|
|
5358
|
+
function ogrnValid(ogrn) {
|
|
5359
|
+
if (!/^\d{13}$|^\d{15}$/u.test(ogrn)) return false;
|
|
5360
|
+
const body = BigInt(ogrn.slice(0, -1));
|
|
5361
|
+
const modulus = ogrn.length === 13 ? 11n : 13n;
|
|
5362
|
+
return Number(body % modulus % 10n) === Number(ogrn.at(-1));
|
|
5363
|
+
} // An account is checked with the БИК by the Bank of Russia key: the three
|
|
5364
|
+
// digits that come before it, then its twenty, weighted 7, 1, 3 in turn, add up
|
|
5365
|
+
// to a multiple of ten. A settlement account takes the БИК's last three digits;
|
|
5366
|
+
// a correspondent account takes «0» and the БИК's fifth and sixth.
|
|
5367
|
+
|
|
5368
|
+
function accountKeyValid(prefix, account) {
|
|
5369
|
+
const digits = digitsOf(prefix + account);
|
|
5370
|
+
const sum = digits.reduce((total, digit, index) => total + digit * [7, 1, 3][index % 3], 0);
|
|
5371
|
+
return sum % 10 === 0;
|
|
5372
|
+
}
|
|
5373
|
+
/** The mistakes of a requisites value, part by part: {inn: 'innWrong', …}. */
|
|
5374
|
+
|
|
5375
|
+
|
|
5376
|
+
function requisiteErrors(value, parts = REQUISITE_PARTS) {
|
|
5377
|
+
const errors = {};
|
|
5378
|
+
|
|
5379
|
+
const has = part => parts.includes(part);
|
|
5380
|
+
|
|
5381
|
+
const of = part => String(value?.[part] ?? '');
|
|
5382
|
+
|
|
5383
|
+
const inn = of('inn');
|
|
5384
|
+
const kpp = of('kpp');
|
|
5385
|
+
const ogrn = of('ogrn');
|
|
5386
|
+
const bik = of('bik');
|
|
5387
|
+
const account = of('account');
|
|
5388
|
+
const corr = of('corr_account');
|
|
5389
|
+
|
|
5390
|
+
if (has('inn')) {
|
|
5391
|
+
// The ИНН is required (the standard): a value without one is no value.
|
|
5392
|
+
if (!inn) errors.inn = 'innRequired';else if (!/^\d{10}$|^\d{12}$/u.test(inn)) errors.inn = 'innLength';else if (!innValid(inn)) errors.inn = 'innWrong';
|
|
5393
|
+
}
|
|
5394
|
+
|
|
5395
|
+
const person = inn.length === 12;
|
|
5396
|
+
const organization = inn.length === 10;
|
|
5397
|
+
|
|
5398
|
+
if (has('kpp') && kpp) {
|
|
5399
|
+
if (!/^\d{4}[\dA-Z]{2}\d{3}$/u.test(kpp)) errors.kpp = 'kppWrong';else if (person) errors.kpp = 'kppPerson';
|
|
5400
|
+
}
|
|
5401
|
+
|
|
5402
|
+
if (has('ogrn') && ogrn) {
|
|
5403
|
+
if (!/^\d{13}$|^\d{15}$/u.test(ogrn)) errors.ogrn = 'ogrnLength';else if (!ogrnValid(ogrn)) errors.ogrn = 'ogrnWrong';else if (organization && ogrn.length !== 13) errors.ogrn = 'ogrnOrganization';else if (person && ogrn.length !== 15) errors.ogrn = 'ogrnPerson';
|
|
5404
|
+
}
|
|
5405
|
+
|
|
5406
|
+
const bikRight = /^04\d{7}$/u.test(bik);
|
|
5407
|
+
if (has('bik') && bik && !bikRight) errors.bik = 'bikWrong';
|
|
5408
|
+
|
|
5409
|
+
if (has('account') && account) {
|
|
5410
|
+
if (!/^\d{20}$/u.test(account)) errors.account = 'accountLength';else if (bikRight && !accountKeyValid(bik.slice(-3), account)) errors.account = 'accountKey';
|
|
5411
|
+
}
|
|
5412
|
+
|
|
5413
|
+
if (has('corr_account') && corr) {
|
|
5414
|
+
if (!/^\d{20}$/u.test(corr)) errors.corr_account = 'accountLength';else if (!corr.startsWith('301')) errors.corr_account = 'corrStart';else if (bikRight && !accountKeyValid(`0${bik.slice(4, 6)}`, corr)) errors.corr_account = 'accountKey';
|
|
5415
|
+
}
|
|
5416
|
+
|
|
5417
|
+
return errors;
|
|
5418
|
+
}
|
|
5419
|
+
|
|
5420
|
+
/***/ },
|
|
5421
|
+
|
|
5422
|
+
/***/ "ebc72103d8b6"
|
|
5423
|
+
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
5424
|
+
|
|
5425
|
+
__webpack_require__.r(__webpack_exports__);
|
|
5426
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
5427
|
+
/* harmony export */ NUMERIC: () => (/* binding */ NUMERIC),
|
|
5428
|
+
/* harmony export */ cellDecimal: () => (/* binding */ cellDecimal),
|
|
5429
|
+
/* harmony export */ columnCurrency: () => (/* binding */ columnCurrency),
|
|
5430
|
+
/* harmony export */ columnOf: () => (/* binding */ columnOf),
|
|
5431
|
+
/* harmony export */ columnsOf: () => (/* binding */ columnsOf),
|
|
5432
|
+
/* harmony export */ computeRow: () => (/* binding */ computeRow),
|
|
5433
|
+
/* harmony export */ rowTotals: () => (/* binding */ rowTotals),
|
|
5434
|
+
/* harmony export */ rowsOf: () => (/* binding */ rowsOf)
|
|
5435
|
+
/* harmony export */ });
|
|
5436
|
+
/* harmony import */ var _decimal_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("f44b979883f0");
|
|
5437
|
+
/* harmony import */ var _money_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("d69ebe7e288e");
|
|
5438
|
+
|
|
5439
|
+
// Rows over columns (Larena property types standard, notes of 2026-11-03,
|
|
5440
|
+
// `items@1`): the columns as the set reads them, the computed cells of a row
|
|
5441
|
+
// and the totals under summed columns, on decimal strings. Pure, so the unit
|
|
5442
|
+
// suite reads it without a browser.
|
|
5443
|
+
|
|
5444
|
+
const NUMERIC = new Set(['integer', 'number', 'percent', 'money']); // A column as the set reads it: its settings beside its own keys, its type
|
|
5445
|
+
// under the name the renderers address (`propertyType`).
|
|
5446
|
+
|
|
5447
|
+
function columnOf(column) {
|
|
5448
|
+
const {
|
|
5449
|
+
settings,
|
|
5450
|
+
...own
|
|
5451
|
+
} = column ?? {};
|
|
5452
|
+
const type = own.propertyType || own.type;
|
|
5453
|
+
return { ...(settings && typeof settings === 'object' ? settings : {}),
|
|
5454
|
+
...own,
|
|
5455
|
+
propertyType: type,
|
|
5456
|
+
type
|
|
5457
|
+
};
|
|
5458
|
+
}
|
|
5459
|
+
function columnsOf(field) {
|
|
5460
|
+
return (Array.isArray(field.columns) ? field.columns : []).map(columnOf).filter(column => column.key);
|
|
5461
|
+
}
|
|
5462
|
+
function rowsOf(value) {
|
|
5463
|
+
return Array.isArray(value) ? value.map(row => row && typeof row === 'object' ? row : {}) : [];
|
|
5464
|
+
} // The decimal a numeric cell holds, or null.
|
|
5465
|
+
|
|
5466
|
+
function cellDecimal(column, cell) {
|
|
5467
|
+
if (cell === null || typeof cell === 'undefined' || cell === '') return null;
|
|
5468
|
+
if (column.propertyType === 'money') return (0,_decimal_js__WEBPACK_IMPORTED_MODULE_0__.toDecimal)((0,_money_js__WEBPACK_IMPORTED_MODULE_1__.moneyParts)(cell, column.currency).amount);
|
|
5469
|
+
return (0,_decimal_js__WEBPACK_IMPORTED_MODULE_0__.toDecimal)(cell);
|
|
5470
|
+
} // The currency a money column's cells are in: its own, else the one money
|
|
5471
|
+
// operand's when it is computed.
|
|
5472
|
+
|
|
5473
|
+
function columnCurrency(column, columns) {
|
|
5474
|
+
if (column.currency) return column.currency;
|
|
5475
|
+
|
|
5476
|
+
if (column.compute?.of) {
|
|
5477
|
+
const operand = columns.find(one => column.compute.of.includes(one.key) && one.propertyType === 'money');
|
|
5478
|
+
return operand?.currency ?? null;
|
|
5479
|
+
}
|
|
5480
|
+
|
|
5481
|
+
return null;
|
|
5482
|
+
}
|
|
5483
|
+
/**
|
|
5484
|
+
* A row with its computed cells worked out from the cells typed: a product of
|
|
5485
|
+
* two or three operands, a percent counting as its share, rounded half away
|
|
5486
|
+
* from zero to the currency's minor units or the column's `scale` (2 by
|
|
5487
|
+
* default). A computed cell whose operands are not all there is no cell. The
|
|
5488
|
+
* keys follow the columns, the computed ones last. The server works the same
|
|
5489
|
+
* out and is the authority.
|
|
5490
|
+
*/
|
|
5491
|
+
|
|
5492
|
+
function computeRow(row, columns, locale = 'en') {
|
|
5493
|
+
const typed = {};
|
|
5494
|
+
const computed = {};
|
|
5495
|
+
|
|
5496
|
+
for (const column of columns) {
|
|
5497
|
+
if (column.compute) continue;
|
|
5498
|
+
if (Object.hasOwn(row, column.key) && row[column.key] !== null && row[column.key] !== '') typed[column.key] = row[column.key];
|
|
5499
|
+
}
|
|
5500
|
+
|
|
5501
|
+
for (const column of columns) {
|
|
5502
|
+
if (!column.compute || column.compute.op !== 'multiply' || !Array.isArray(column.compute.of)) continue;
|
|
5503
|
+
let product = null;
|
|
5504
|
+
let currency = null;
|
|
5505
|
+
|
|
5506
|
+
for (const key of column.compute.of) {
|
|
5507
|
+
const operand = columns.find(one => one.key === key);
|
|
5508
|
+
const decimal = operand ? cellDecimal(operand, typed[key]) : null;
|
|
5509
|
+
|
|
5510
|
+
if (!decimal) {
|
|
5511
|
+
product = null;
|
|
5512
|
+
break;
|
|
5513
|
+
}
|
|
5514
|
+
|
|
5515
|
+
if (operand.propertyType === 'money') currency = (0,_money_js__WEBPACK_IMPORTED_MODULE_1__.moneyParts)(typed[key], operand.currency).currency;
|
|
5516
|
+
const factor = operand.propertyType === 'percent' ? (0,_decimal_js__WEBPACK_IMPORTED_MODULE_0__.share)(decimal) : decimal;
|
|
5517
|
+
product = product ? (0,_decimal_js__WEBPACK_IMPORTED_MODULE_0__.multiply)(product, factor) : factor;
|
|
5518
|
+
}
|
|
5519
|
+
|
|
5520
|
+
if (!product) continue;
|
|
5521
|
+
|
|
5522
|
+
if (column.propertyType === 'money') {
|
|
5523
|
+
const code = column.currency || currency;
|
|
5524
|
+
const scale = code ? (0,_money_js__WEBPACK_IMPORTED_MODULE_1__.currencyOf)(code, locale).minor : 2;
|
|
5525
|
+
computed[column.key] = {
|
|
5526
|
+
amount: (0,_decimal_js__WEBPACK_IMPORTED_MODULE_0__.decimalString)((0,_decimal_js__WEBPACK_IMPORTED_MODULE_0__.round)(product, scale)),
|
|
5527
|
+
currency: code
|
|
5528
|
+
};
|
|
5529
|
+
} else {
|
|
5530
|
+
const scale = column.propertyType === 'integer' ? 0 : Number.isInteger(column.scale) ? column.scale : 2;
|
|
5531
|
+
computed[column.key] = (0,_decimal_js__WEBPACK_IMPORTED_MODULE_0__.decimalString)((0,_decimal_js__WEBPACK_IMPORTED_MODULE_0__.round)(product, scale));
|
|
5532
|
+
}
|
|
5533
|
+
}
|
|
5534
|
+
|
|
5535
|
+
return { ...typed,
|
|
5536
|
+
...computed
|
|
5537
|
+
};
|
|
5538
|
+
}
|
|
5539
|
+
/** The sums of the columns marked `total: "sum"`, as decimals by key. */
|
|
5540
|
+
|
|
5541
|
+
function rowTotals(rows, columns) {
|
|
5542
|
+
const totals = {};
|
|
5543
|
+
|
|
5544
|
+
for (const column of columns) {
|
|
5545
|
+
if (column.total !== 'sum' || !NUMERIC.has(column.propertyType)) continue;
|
|
5546
|
+
let sum = {
|
|
5547
|
+
units: 0n,
|
|
5548
|
+
scale: 0
|
|
5549
|
+
};
|
|
5550
|
+
|
|
5551
|
+
for (const row of rows) {
|
|
5552
|
+
const decimal = cellDecimal(column, row[column.key]);
|
|
5553
|
+
if (decimal) sum = (0,_decimal_js__WEBPACK_IMPORTED_MODULE_0__.add)(sum, decimal);
|
|
5554
|
+
}
|
|
5555
|
+
|
|
5556
|
+
totals[column.key] = sum;
|
|
5557
|
+
}
|
|
5558
|
+
|
|
5559
|
+
return totals;
|
|
5560
|
+
}
|
|
5561
|
+
|
|
5562
|
+
/***/ },
|
|
5563
|
+
|
|
5564
|
+
/***/ "db94c32f22be"
|
|
5565
|
+
(module, __webpack_exports__, __webpack_require__) {
|
|
5566
|
+
|
|
5567
|
+
__webpack_require__.a(module, async (__webpack_handle_async_dependencies__, __webpack_async_result__) => { try {
|
|
5568
|
+
__webpack_require__.r(__webpack_exports__);
|
|
5569
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
5570
|
+
/* harmony export */ SET_FIELD_TYPES: () => (/* binding */ SET_FIELD_TYPES),
|
|
5571
|
+
/* harmony export */ drawsSetField: () => (/* binding */ drawsSetField),
|
|
5572
|
+
/* harmony export */ renderSetField: () => (/* binding */ renderSetField),
|
|
5573
|
+
/* harmony export */ setFieldText: () => (/* binding */ setFieldText)
|
|
5574
|
+
/* harmony export */ });
|
|
5575
|
+
/* harmony import */ var lit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("2a080a730149");
|
|
5576
|
+
/* harmony import */ var _set_fields_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("f59c8bcdc90a");
|
|
5577
|
+
/* harmony import */ var _field_renderers_index_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__("e5483002f3ea");
|
|
5578
|
+
/* harmony import */ var _core_js_page_language__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__("43090d1c8307");
|
|
5579
|
+
/* harmony import */ var _decimal_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__("f44b979883f0");
|
|
5580
|
+
/* harmony import */ var _money_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__("d69ebe7e288e");
|
|
5581
|
+
/* harmony import */ var _rows_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__("ebc72103d8b6");
|
|
5582
|
+
/* harmony import */ var _requisites_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__("06dfc7b3b9d9");
|
|
5583
|
+
var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([lit__WEBPACK_IMPORTED_MODULE_0__, _field_renderers_index_js__WEBPACK_IMPORTED_MODULE_2__, _core_js_page_language__WEBPACK_IMPORTED_MODULE_3__]);
|
|
5584
|
+
([lit__WEBPACK_IMPORTED_MODULE_0__, _field_renderers_index_js__WEBPACK_IMPORTED_MODULE_2__, _core_js_page_language__WEBPACK_IMPORTED_MODULE_3__] = __webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__);
|
|
5585
|
+
/* harmony import */ var lit__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(lit__WEBPACK_IMPORTED_MODULE_0__);
|
|
5586
|
+
/* harmony import */ var _core_js_page_language__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__);
|
|
5587
|
+
|
|
5588
|
+
|
|
5589
|
+
|
|
5590
|
+
|
|
5591
|
+
|
|
5592
|
+
|
|
5593
|
+
|
|
5594
|
+
// The property types the set draws itself, beside the SIMAI Framework field
|
|
5595
|
+
// renderers it draws every other type with (Larena addendum of 2026-11-03 to
|
|
5596
|
+
// the general components task, msg-bf1ad4f3ae00420bb752; Larena property types
|
|
5597
|
+
// standard, notes of 2026-11-03):
|
|
5598
|
+
//
|
|
5599
|
+
// money {amount, currency}: an amount and its ISO 4217 currency
|
|
5600
|
+
// percent a decimal with a % after it
|
|
5601
|
+
// items rows over the field's columns: a table editor, a card a row on a phone
|
|
5602
|
+
// compound a fixed set of parts with one value object, checked together
|
|
5603
|
+
// ru.requisites the compound of a Russian organisation's or person's requisites
|
|
5604
|
+
//
|
|
5605
|
+
// Each is drawn from Framework controls (sf-input with `text-end`, sf-dropdown,
|
|
5606
|
+
// sf-button-icon, sf-button) and, inside a row or a compound, by the Framework
|
|
5607
|
+
// renderers for the types they already draw. Every number is a decimal string
|
|
5608
|
+
// (./decimal.js): nothing here writes a float.
|
|
5609
|
+
//
|
|
5610
|
+
// A control writes what was typed. An entry that is not a value yet -- text
|
|
5611
|
+
// that is not a number, more digits after the point than the currency has, a
|
|
5612
|
+
// ИНН whose control digit is wrong -- is written as typed and reported through
|
|
5613
|
+
// setFieldError, which holds both saves while it stands (port 1.16.0, as a
|
|
5614
|
+
// masked string does), and the control says what is wrong under itself once
|
|
5615
|
+
// the person has left it.
|
|
5616
|
+
|
|
5617
|
+
const TEXT = {
|
|
5618
|
+
en: {
|
|
5619
|
+
currency: 'Currency',
|
|
5620
|
+
notNumber: 'Enter a number',
|
|
5621
|
+
fractionMax: 'At most {n} digits after the point',
|
|
5622
|
+
fractionNone: 'A whole amount, no digits after the point',
|
|
5623
|
+
chooseCurrency: 'Choose a currency',
|
|
5624
|
+
range: 'From {min} to {max}',
|
|
5625
|
+
atLeast: 'At least {min}',
|
|
5626
|
+
atMost: 'At most {max}',
|
|
5627
|
+
addRow: 'Add a row',
|
|
5628
|
+
removeRow: 'Remove the row',
|
|
5629
|
+
rowUp: 'Move the row up',
|
|
5630
|
+
rowDown: 'Move the row down',
|
|
5631
|
+
row: 'Row {n}',
|
|
5632
|
+
total: 'Total',
|
|
5633
|
+
cellsWrong: 'Some cells are not filled in correctly',
|
|
5634
|
+
rows: '{n} rows',
|
|
5635
|
+
row1: '1 row',
|
|
5636
|
+
inn: 'INN',
|
|
5637
|
+
kpp: 'KPP',
|
|
5638
|
+
ogrn: 'OGRN',
|
|
5639
|
+
bik: 'BIK',
|
|
5640
|
+
account: 'Account',
|
|
5641
|
+
corr_account: 'Correspondent account',
|
|
5642
|
+
innRequired: 'The INN is required',
|
|
5643
|
+
innLength: '10 digits for an organisation, 12 for a person',
|
|
5644
|
+
innWrong: 'The INN control digits do not match',
|
|
5645
|
+
kppWrong: 'Nine characters: four digits, two digits or letters, three digits',
|
|
5646
|
+
kppPerson: 'A person\'s INN has no KPP',
|
|
5647
|
+
ogrnLength: '13 digits (OGRN) or 15 (OGRNIP)',
|
|
5648
|
+
ogrnWrong: 'The control digit does not match',
|
|
5649
|
+
ogrnOrganization: 'An organisation has a 13-digit OGRN',
|
|
5650
|
+
ogrnPerson: 'A person has a 15-digit OGRNIP',
|
|
5651
|
+
bikWrong: 'Nine digits starting with 04',
|
|
5652
|
+
accountLength: '20 digits',
|
|
5653
|
+
accountKey: 'The account does not match the BIK',
|
|
5654
|
+
corrStart: 'A correspondent account starts with 301'
|
|
5655
|
+
},
|
|
5656
|
+
ru: {
|
|
5657
|
+
currency: 'Валюта',
|
|
5658
|
+
notNumber: 'Введите число',
|
|
5659
|
+
fractionMax: 'Не больше {n} знаков после запятой',
|
|
5660
|
+
fractionNone: 'Целая сумма, без знаков после запятой',
|
|
5661
|
+
chooseCurrency: 'Выберите валюту',
|
|
5662
|
+
range: 'От {min} до {max}',
|
|
5663
|
+
atLeast: 'Не меньше {min}',
|
|
5664
|
+
atMost: 'Не больше {max}',
|
|
5665
|
+
addRow: 'Добавить строку',
|
|
5666
|
+
removeRow: 'Удалить строку',
|
|
5667
|
+
rowUp: 'Поднять строку',
|
|
5668
|
+
rowDown: 'Опустить строку',
|
|
5669
|
+
row: 'Строка {n}',
|
|
5670
|
+
total: 'Итого',
|
|
5671
|
+
cellsWrong: 'Не все ячейки заполнены верно',
|
|
5672
|
+
rows: '{n} строк',
|
|
5673
|
+
row1: '1 строка',
|
|
5674
|
+
inn: 'ИНН',
|
|
5675
|
+
kpp: 'КПП',
|
|
5676
|
+
ogrn: 'ОГРН',
|
|
5677
|
+
bik: 'БИК',
|
|
5678
|
+
account: 'Расчётный счёт',
|
|
5679
|
+
corr_account: 'Корр. счёт',
|
|
5680
|
+
innRequired: 'ИНН обязателен',
|
|
5681
|
+
innLength: '10 цифр у организации, 12 у физлица',
|
|
5682
|
+
innWrong: 'Не сходятся контрольные цифры ИНН',
|
|
5683
|
+
kppWrong: 'Девять знаков: четыре цифры, две цифры или буквы, три цифры',
|
|
5684
|
+
kppPerson: 'У ИНН физлица нет КПП',
|
|
5685
|
+
ogrnLength: '13 цифр (ОГРН) или 15 (ОГРНИП)',
|
|
5686
|
+
ogrnWrong: 'Не сходится контрольная цифра',
|
|
5687
|
+
ogrnOrganization: 'У организации ОГРН из 13 цифр',
|
|
5688
|
+
ogrnPerson: 'У физлица ОГРНИП из 15 цифр',
|
|
5689
|
+
bikWrong: 'Девять цифр, начиная с 04',
|
|
5690
|
+
accountLength: '20 цифр',
|
|
5691
|
+
accountKey: 'Счёт не сходится с БИК',
|
|
5692
|
+
corrStart: 'Корр. счёт начинается с 301'
|
|
5693
|
+
}
|
|
5694
|
+
};
|
|
5695
|
+
const WORDS_OF = 'sf-view-properties';
|
|
5696
|
+
|
|
5697
|
+
function word(node, key, vars = {}) {
|
|
5698
|
+
let text = (0,_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__.pageText)(TEXT, key, node, WORDS_OF);
|
|
5699
|
+
|
|
5700
|
+
for (const [name, value] of Object.entries(vars)) text = text.replaceAll(`{${name}}`, String(value));
|
|
5701
|
+
|
|
5702
|
+
return text;
|
|
5703
|
+
} // ru counts: 1 строка, 2–4 строки, 5+ строк.
|
|
5704
|
+
|
|
5705
|
+
|
|
5706
|
+
function rowsWord(node, count) {
|
|
5707
|
+
const locale = (0,_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__.pageLocale)(node);
|
|
5708
|
+
if (count === 1) return word(node, 'row1');
|
|
5709
|
+
|
|
5710
|
+
if (locale.startsWith('ru')) {
|
|
5711
|
+
const tens = count % 100;
|
|
5712
|
+
const ones = count % 10;
|
|
5713
|
+
if (ones >= 2 && ones <= 4 && (tens < 12 || tens > 14)) return `${count} строки`;
|
|
5714
|
+
}
|
|
5715
|
+
|
|
5716
|
+
return word(node, 'rows', {
|
|
5717
|
+
n: count
|
|
5718
|
+
});
|
|
5719
|
+
}
|
|
5720
|
+
|
|
5721
|
+
const SET_FIELD_TYPES = Object.freeze(['money', 'percent', 'items', 'compound', 'ru.requisites']); // --- Currencies -------------------------------------------------------------
|
|
5722
|
+
// The currencies a money field offers when it does not fix one: the ones a
|
|
5723
|
+
// Russian-language business meets most, and the value's own currency first
|
|
5724
|
+
// when it is none of them. A field that fixes `currency` offers no choice.
|
|
5725
|
+
|
|
5726
|
+
const OFFERED_CURRENCIES = ['RUB', 'USD', 'EUR', 'CNY', 'KZT', 'BYN', 'AED', 'TRY', 'GBP', 'JPY']; // --- What a person is typing -------------------------------------------------
|
|
5727
|
+
// The text in a decimal input while it is being typed. The value written is
|
|
5728
|
+
// canonical («12.5»); drawn back as such it would move the caret under the
|
|
5729
|
+
// person typing «12,50». So the typed text is kept per control and shown while
|
|
5730
|
+
// it still reads as the value, and dropped when the control is left.
|
|
5731
|
+
|
|
5732
|
+
const DRAFTS = new WeakMap();
|
|
5733
|
+
|
|
5734
|
+
const draftsOf = node => {
|
|
5735
|
+
if (!DRAFTS.has(node)) DRAFTS.set(node, new Map());
|
|
5736
|
+
return DRAFTS.get(node);
|
|
5737
|
+
}; // The controls a person has left: a message stands under a control only then,
|
|
5738
|
+
// as a masked string's does.
|
|
5739
|
+
|
|
5740
|
+
|
|
5741
|
+
const LEFT = new WeakMap();
|
|
5742
|
+
|
|
5743
|
+
const leftOf = node => {
|
|
5744
|
+
if (!LEFT.has(node)) LEFT.set(node, new Set());
|
|
5745
|
+
return LEFT.get(node);
|
|
5746
|
+
};
|
|
5747
|
+
|
|
5748
|
+
const redraw = node => node?.requestComponentUpdate?.('changed');
|
|
5749
|
+
|
|
5750
|
+
function shownText(node, path, value, locale) {
|
|
5751
|
+
const draft = draftsOf(node).get(path);
|
|
5752
|
+
const read = (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.readDecimal)(value);
|
|
5753
|
+
if (!read.ok) return String(value ?? '');
|
|
5754
|
+
|
|
5755
|
+
if (typeof draft === 'string') {
|
|
5756
|
+
const typed = (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.readDecimal)(draft);
|
|
5757
|
+
if (typed.ok && typed.value === read.value) return draft;
|
|
5758
|
+
}
|
|
5759
|
+
|
|
5760
|
+
return (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.editableDecimal)(read.value, locale);
|
|
5761
|
+
} // The mistake of a typed decimal, or null: not a number, more digits after the
|
|
5762
|
+
// point than allowed, outside the bounds.
|
|
5763
|
+
|
|
5764
|
+
|
|
5765
|
+
function decimalError(node, raw, {
|
|
5766
|
+
maxFraction = null,
|
|
5767
|
+
min = null,
|
|
5768
|
+
max = null
|
|
5769
|
+
} = {}) {
|
|
5770
|
+
const read = (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.readDecimal)(raw);
|
|
5771
|
+
if (!read.ok) return word(node, 'notNumber');
|
|
5772
|
+
if (read.value === null) return null;
|
|
5773
|
+
|
|
5774
|
+
if (maxFraction !== null && (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.fractionDigits)(read.value) > maxFraction) {
|
|
5775
|
+
return maxFraction === 0 ? word(node, 'fractionNone') : word(node, 'fractionMax', {
|
|
5776
|
+
n: maxFraction
|
|
5777
|
+
});
|
|
5778
|
+
}
|
|
5779
|
+
|
|
5780
|
+
const value = (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.toDecimal)(read.value);
|
|
5781
|
+
const low = (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.toDecimal)(min);
|
|
5782
|
+
const high = (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.toDecimal)(max);
|
|
5783
|
+
const locale = (0,_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__.pageLocale)(node);
|
|
5784
|
+
|
|
5785
|
+
if (low && (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.compare)(value, low) < 0 || high && (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.compare)(value, high) > 0) {
|
|
5786
|
+
if (low && high) return word(node, 'range', {
|
|
5787
|
+
min: (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.formatDecimal)(min, locale),
|
|
5788
|
+
max: (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.formatDecimal)(max, locale)
|
|
5789
|
+
});
|
|
5790
|
+
return low ? word(node, 'atLeast', {
|
|
5791
|
+
min: (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.formatDecimal)(min, locale)
|
|
5792
|
+
}) : word(node, 'atMost', {
|
|
5793
|
+
max: (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.formatDecimal)(max, locale)
|
|
5794
|
+
});
|
|
5795
|
+
}
|
|
5796
|
+
|
|
5797
|
+
return null;
|
|
5798
|
+
}
|
|
5799
|
+
/**
|
|
5800
|
+
* One decimal input: sf-input with the decimal keyboard and `text-end` (a % or
|
|
5801
|
+
* a currency sign). `write(text, error)` gets what was typed -- canonical when
|
|
5802
|
+
* it reads as a number -- and the mistake, or null.
|
|
5803
|
+
*/
|
|
5804
|
+
|
|
5805
|
+
|
|
5806
|
+
function decimalInput(node, {
|
|
5807
|
+
path,
|
|
5808
|
+
value,
|
|
5809
|
+
size,
|
|
5810
|
+
label,
|
|
5811
|
+
required,
|
|
5812
|
+
textEnd,
|
|
5813
|
+
placeholder,
|
|
5814
|
+
check,
|
|
5815
|
+
write
|
|
5816
|
+
}) {
|
|
5817
|
+
const locale = (0,_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__.pageLocale)(node);
|
|
5818
|
+
const text = shownText(node, path, value, locale);
|
|
5819
|
+
const error = check(text);
|
|
5820
|
+
const shown = error && leftOf(node).has(path) ? error : '';
|
|
5821
|
+
|
|
5822
|
+
const typed = event => {
|
|
5823
|
+
const raw = event.target?.value ?? '';
|
|
5824
|
+
draftsOf(node).set(path, raw);
|
|
5825
|
+
const read = (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.readDecimal)(raw);
|
|
5826
|
+
write(read.ok ? read.value : raw, check(raw));
|
|
5827
|
+
};
|
|
5828
|
+
|
|
5829
|
+
return (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
5830
|
+
<sf-input
|
|
5831
|
+
class="sf-view-fields-decimal"
|
|
5832
|
+
size=${size}
|
|
5833
|
+
appearance="bordered"
|
|
5834
|
+
inputmode="decimal"
|
|
5835
|
+
value=${text}
|
|
5836
|
+
text-end=${textEnd || lit__WEBPACK_IMPORTED_MODULE_0__.nothing}
|
|
5837
|
+
placeholder=${placeholder || lit__WEBPACK_IMPORTED_MODULE_0__.nothing}
|
|
5838
|
+
?required=${Boolean(required)}
|
|
5839
|
+
?invalid=${Boolean(shown)}
|
|
5840
|
+
error-message=${shown || lit__WEBPACK_IMPORTED_MODULE_0__.nothing}
|
|
5841
|
+
aria-label=${label || ''}
|
|
5842
|
+
data-path=${path}
|
|
5843
|
+
@input=${typed}
|
|
5844
|
+
@change=${typed}
|
|
5845
|
+
@focusout=${() => {
|
|
5846
|
+
draftsOf(node).delete(path);
|
|
5847
|
+
leftOf(node).add(path);
|
|
5848
|
+
redraw(node);
|
|
5849
|
+
}}
|
|
5850
|
+
></sf-input>
|
|
5851
|
+
`;
|
|
5852
|
+
} // --- percent -------------------------------------------------------------------
|
|
5853
|
+
|
|
5854
|
+
|
|
5855
|
+
function percentBounds(field) {
|
|
5856
|
+
// 0 and 100 when the field sets none (property types standard §2).
|
|
5857
|
+
return {
|
|
5858
|
+
min: field.min ?? '0',
|
|
5859
|
+
max: field.max ?? '100'
|
|
5860
|
+
};
|
|
5861
|
+
}
|
|
5862
|
+
|
|
5863
|
+
function renderPercentEdit(field, value, host, ctx) {
|
|
5864
|
+
const {
|
|
5865
|
+
min,
|
|
5866
|
+
max
|
|
5867
|
+
} = percentBounds(field);
|
|
5868
|
+
return decimalInput(ctx.node, {
|
|
5869
|
+
path: ctx.path ?? String(field.key ?? ''),
|
|
5870
|
+
value,
|
|
5871
|
+
size: ctx.size,
|
|
5872
|
+
label: field.label || field.key,
|
|
5873
|
+
required: field.required,
|
|
5874
|
+
textEnd: '%',
|
|
5875
|
+
placeholder: field.placeholder,
|
|
5876
|
+
check: text => decimalError(ctx.node, text, {
|
|
5877
|
+
min,
|
|
5878
|
+
max
|
|
5879
|
+
}),
|
|
5880
|
+
write: (next, error) => {
|
|
5881
|
+
host.setFieldValue(field, next);
|
|
5882
|
+
host.setFieldError?.(field, error);
|
|
5883
|
+
}
|
|
5884
|
+
});
|
|
5885
|
+
}
|
|
5886
|
+
|
|
5887
|
+
function renderPercentView(field, value, ctx) {
|
|
5888
|
+
const text = (0,_money_js__WEBPACK_IMPORTED_MODULE_5__.percentText)(value, (0,_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__.pageLocale)(ctx.node));
|
|
5889
|
+
return text ? (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="sf-field-view">${text}</span>` : lit__WEBPACK_IMPORTED_MODULE_0__.nothing;
|
|
5890
|
+
} // --- money ---------------------------------------------------------------------
|
|
5891
|
+
|
|
5892
|
+
|
|
5893
|
+
function renderMoneyEdit(field, value, host, ctx) {
|
|
5894
|
+
const node = ctx.node;
|
|
5895
|
+
const locale = (0,_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__.pageLocale)(node);
|
|
5896
|
+
const path = ctx.path ?? String(field.key ?? '');
|
|
5897
|
+
const fixed = typeof field.currency === 'string' && field.currency ? field.currency : null;
|
|
5898
|
+
const parts = (0,_money_js__WEBPACK_IMPORTED_MODULE_5__.moneyParts)(value, fixed); // A currency chosen before an amount is typed waits here: a value is an
|
|
5899
|
+
// amount and its currency, never a currency alone.
|
|
5900
|
+
|
|
5901
|
+
const currencyPath = `${path}#currency`;
|
|
5902
|
+
const currency = parts.currency || draftsOf(node).get(currencyPath) || null;
|
|
5903
|
+
const minor = currency ? (0,_money_js__WEBPACK_IMPORTED_MODULE_5__.currencyOf)(currency, locale).minor : null;
|
|
5904
|
+
|
|
5905
|
+
const check = text => {
|
|
5906
|
+
const error = decimalError(node, text, {
|
|
5907
|
+
maxFraction: minor,
|
|
5908
|
+
min: field.min ?? null,
|
|
5909
|
+
max: field.max ?? null
|
|
5910
|
+
});
|
|
5911
|
+
if (error) return error;
|
|
5912
|
+
return (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.readDecimal)(text).value !== null && !currency ? word(node, 'chooseCurrency') : null;
|
|
5913
|
+
};
|
|
5914
|
+
|
|
5915
|
+
const write = (amount, error, code = currency) => {
|
|
5916
|
+
host.setFieldValue(field, amount === null ? null : {
|
|
5917
|
+
amount,
|
|
5918
|
+
currency: code
|
|
5919
|
+
});
|
|
5920
|
+
host.setFieldError?.(field, error);
|
|
5921
|
+
};
|
|
5922
|
+
|
|
5923
|
+
const amount = decimalInput(node, {
|
|
5924
|
+
path,
|
|
5925
|
+
value: parts.amount,
|
|
5926
|
+
size: ctx.size,
|
|
5927
|
+
label: field.label || field.key,
|
|
5928
|
+
required: field.required,
|
|
5929
|
+
textEnd: fixed ? (0,_money_js__WEBPACK_IMPORTED_MODULE_5__.currencyOf)(fixed, locale).sign : '',
|
|
5930
|
+
placeholder: field.placeholder,
|
|
5931
|
+
check,
|
|
5932
|
+
write
|
|
5933
|
+
});
|
|
5934
|
+
if (fixed) return amount;
|
|
5935
|
+
const offered = [...new Set([...(currency ? [currency] : []), ...OFFERED_CURRENCIES])];
|
|
5936
|
+
return (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
5937
|
+
<span class="sf-view-fields-money flex gap-1/3 min-w-0">
|
|
5938
|
+
${amount}
|
|
5939
|
+
<sf-dropdown
|
|
5940
|
+
class="sf-view-fields-currency"
|
|
5941
|
+
size=${ctx.size}
|
|
5942
|
+
appearance="outlined"
|
|
5943
|
+
mode="select"
|
|
5944
|
+
search="false"
|
|
5945
|
+
value=${currency ?? ''}
|
|
5946
|
+
placeholder=${word(node, 'currency')}
|
|
5947
|
+
aria-label=${word(node, 'currency')}
|
|
5948
|
+
.options=${offered.map(code => ({
|
|
5949
|
+
type: 'text',
|
|
5950
|
+
size: '1',
|
|
5951
|
+
value: code,
|
|
5952
|
+
text: `${code} ${(0,_money_js__WEBPACK_IMPORTED_MODULE_5__.currencyOf)(code, locale).sign === code ? '' : (0,_money_js__WEBPACK_IMPORTED_MODULE_5__.currencyOf)(code, locale).sign}`.trim(),
|
|
5953
|
+
selected: code === currency
|
|
5954
|
+
}))}
|
|
5955
|
+
@change=${event => {
|
|
5956
|
+
const code = event.detail?.value || null;
|
|
5957
|
+
draftsOf(node).set(currencyPath, code);
|
|
5958
|
+
const typed = parts.amount;
|
|
5959
|
+
|
|
5960
|
+
if (typed !== null && typed !== '') {
|
|
5961
|
+
const nextMinor = code ? (0,_money_js__WEBPACK_IMPORTED_MODULE_5__.currencyOf)(code, locale).minor : null;
|
|
5962
|
+
const error = decimalError(node, typed, {
|
|
5963
|
+
maxFraction: nextMinor,
|
|
5964
|
+
min: field.min ?? null,
|
|
5965
|
+
max: field.max ?? null
|
|
5966
|
+
}) || (code ? null : word(node, 'chooseCurrency'));
|
|
5967
|
+
write((0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.readDecimal)(typed).ok ? (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.readDecimal)(typed).value : typed, error, code);
|
|
5968
|
+
} else {
|
|
5969
|
+
redraw(node);
|
|
5970
|
+
}
|
|
5971
|
+
}}
|
|
5972
|
+
></sf-dropdown>
|
|
5973
|
+
</span>
|
|
5974
|
+
`;
|
|
5975
|
+
}
|
|
5976
|
+
|
|
5977
|
+
function moneyText(field, value, locale) {
|
|
5978
|
+
const fixed = typeof field.currency === 'string' && field.currency ? field.currency : null;
|
|
5979
|
+
const {
|
|
5980
|
+
amount,
|
|
5981
|
+
currency
|
|
5982
|
+
} = (0,_money_js__WEBPACK_IMPORTED_MODULE_5__.moneyParts)(value, fixed);
|
|
5983
|
+
return amount === null ? '' : (0,_money_js__WEBPACK_IMPORTED_MODULE_5__.formatMoney)(amount, currency, locale);
|
|
5984
|
+
}
|
|
5985
|
+
|
|
5986
|
+
function renderMoneyView(field, value, ctx) {
|
|
5987
|
+
const text = moneyText(field, value, (0,_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__.pageLocale)(ctx.node));
|
|
5988
|
+
return text ? (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="sf-field-view sf-view-fields-amount">${text}</span>` : lit__WEBPACK_IMPORTED_MODULE_0__.nothing;
|
|
5989
|
+
} // --- number inside a row -----------------------------------------------------------
|
|
5990
|
+
// A number cell of a row is a decimal string as a money amount is: the
|
|
5991
|
+
// Framework's number editor writes a float, which the rows refuse. An integer
|
|
5992
|
+
// cell is the Framework's own.
|
|
5993
|
+
|
|
5994
|
+
|
|
5995
|
+
function renderDecimalEdit(field, value, host, ctx) {
|
|
5996
|
+
return decimalInput(ctx.node, {
|
|
5997
|
+
path: ctx.path ?? String(field.key ?? ''),
|
|
5998
|
+
value,
|
|
5999
|
+
size: ctx.size,
|
|
6000
|
+
label: field.label || field.key,
|
|
6001
|
+
required: field.required,
|
|
6002
|
+
textEnd: field.unit || '',
|
|
6003
|
+
placeholder: field.placeholder,
|
|
6004
|
+
check: text => decimalError(ctx.node, text, {
|
|
6005
|
+
min: field.min ?? null,
|
|
6006
|
+
max: field.max ?? null
|
|
6007
|
+
}),
|
|
6008
|
+
write: (next, error) => {
|
|
6009
|
+
host.setFieldValue(field, next);
|
|
6010
|
+
host.setFieldError?.(field, error);
|
|
6011
|
+
}
|
|
6012
|
+
});
|
|
6013
|
+
}
|
|
6014
|
+
|
|
6015
|
+
function decimalText(field, value, locale) {
|
|
6016
|
+
const text = (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.formatDecimal)(value, locale);
|
|
6017
|
+
return text && field.unit ? `${text} ${field.unit}` : text;
|
|
6018
|
+
} // --- items: rows over columns ------------------------------------------------------
|
|
6019
|
+
|
|
6020
|
+
|
|
6021
|
+
function cellText(column, cell, columns, locale, viewHost) {
|
|
6022
|
+
if (cell === null || typeof cell === 'undefined' || cell === '') return null;
|
|
6023
|
+
|
|
6024
|
+
switch (column.propertyType) {
|
|
6025
|
+
case 'money':
|
|
6026
|
+
{
|
|
6027
|
+
const {
|
|
6028
|
+
amount,
|
|
6029
|
+
currency
|
|
6030
|
+
} = (0,_money_js__WEBPACK_IMPORTED_MODULE_5__.moneyParts)(cell, (0,_rows_js__WEBPACK_IMPORTED_MODULE_6__.columnCurrency)(column, columns));
|
|
6031
|
+
return amount === null ? null : (0,_money_js__WEBPACK_IMPORTED_MODULE_5__.formatMoney)(amount, currency, locale);
|
|
6032
|
+
}
|
|
6033
|
+
|
|
6034
|
+
case 'percent':
|
|
6035
|
+
return (0,_money_js__WEBPACK_IMPORTED_MODULE_5__.percentText)(cell, locale);
|
|
6036
|
+
|
|
6037
|
+
case 'number':
|
|
6038
|
+
return decimalText(column, cell, locale);
|
|
6039
|
+
|
|
6040
|
+
default:
|
|
6041
|
+
{
|
|
6042
|
+
const shown = (0,_field_renderers_index_js__WEBPACK_IMPORTED_MODULE_2__.renderViewField)({ ...column,
|
|
6043
|
+
value: cell
|
|
6044
|
+
}, cell, viewHost, {
|
|
6045
|
+
size: '1'
|
|
6046
|
+
});
|
|
6047
|
+
return shown === null || shown === lit__WEBPACK_IMPORTED_MODULE_0__.nothing ? null : shown;
|
|
6048
|
+
}
|
|
6049
|
+
}
|
|
6050
|
+
}
|
|
6051
|
+
|
|
6052
|
+
function totalText(column, total, columns, locale) {
|
|
6053
|
+
if (column.propertyType === 'money') {
|
|
6054
|
+
const currency = (0,_rows_js__WEBPACK_IMPORTED_MODULE_6__.columnCurrency)(column, columns);
|
|
6055
|
+
return (0,_money_js__WEBPACK_IMPORTED_MODULE_5__.formatMoney)((0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.decimalString)(total), currency, locale);
|
|
6056
|
+
}
|
|
6057
|
+
|
|
6058
|
+
if (column.propertyType === 'percent') return (0,_money_js__WEBPACK_IMPORTED_MODULE_5__.percentText)((0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.decimalString)(total), locale);
|
|
6059
|
+
return decimalText(column, (0,_decimal_js__WEBPACK_IMPORTED_MODULE_4__.decimalString)(total), locale);
|
|
6060
|
+
} // A column's share of the row: text widest, a yes-or-no narrowest.
|
|
6061
|
+
|
|
6062
|
+
|
|
6063
|
+
function track(column) {
|
|
6064
|
+
switch (column.propertyType) {
|
|
6065
|
+
case 'boolean':
|
|
6066
|
+
return 'minmax(4rem, auto)';
|
|
6067
|
+
// Wide enough for the control's own padding, a suffix (% or a currency
|
|
6068
|
+
// sign) and the digits a person types: a 5.5rem percent showed «2 %» for 20.
|
|
6069
|
+
|
|
6070
|
+
case 'integer':
|
|
6071
|
+
return 'minmax(5.5rem, 0.6fr)';
|
|
6072
|
+
|
|
6073
|
+
case 'percent':
|
|
6074
|
+
return 'minmax(6.5rem, 0.6fr)';
|
|
6075
|
+
|
|
6076
|
+
case 'number':
|
|
6077
|
+
return 'minmax(6.5rem, 0.7fr)';
|
|
6078
|
+
|
|
6079
|
+
case 'money':
|
|
6080
|
+
return column.currency ? 'minmax(9rem, 0.9fr)' : 'minmax(13rem, 1.2fr)';
|
|
6081
|
+
|
|
6082
|
+
case 'date':
|
|
6083
|
+
return 'minmax(9rem, 0.8fr)';
|
|
6084
|
+
|
|
6085
|
+
case 'choice':
|
|
6086
|
+
return 'minmax(8rem, 1fr)';
|
|
6087
|
+
|
|
6088
|
+
default:
|
|
6089
|
+
return 'minmax(10rem, 2fr)';
|
|
6090
|
+
}
|
|
6091
|
+
}
|
|
6092
|
+
|
|
6093
|
+
function itemsSummary(field, rows, columns, ctx) {
|
|
6094
|
+
const locale = (0,_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__.pageLocale)(ctx.node);
|
|
6095
|
+
const totals = (0,_rows_js__WEBPACK_IMPORTED_MODULE_6__.rowTotals)(rows, columns);
|
|
6096
|
+
const first = columns.find(column => Object.hasOwn(totals, column.key) && column.propertyType === 'money') ?? columns.find(column => Object.hasOwn(totals, column.key));
|
|
6097
|
+
const count = rowsWord(ctx.node, rows.length);
|
|
6098
|
+
return first ? `${count}, ${word(ctx.node, 'total').toLowerCase()} ${totalText(first, totals[first.key], columns, locale)}` : count;
|
|
6099
|
+
}
|
|
6100
|
+
|
|
6101
|
+
function renderItemsTable(field, rows, columns, ctx, {
|
|
6102
|
+
editing,
|
|
6103
|
+
host
|
|
6104
|
+
}) {
|
|
6105
|
+
const node = ctx.node;
|
|
6106
|
+
const locale = (0,_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__.pageLocale)(node);
|
|
6107
|
+
const key = String(field.key ?? '');
|
|
6108
|
+
const minRows = Number.isInteger(field.minRows) ? field.minRows : 0;
|
|
6109
|
+
const maxRows = Number.isInteger(field.maxRows) ? field.maxRows : 1000; // Fewer rows than the field asks for are drawn as empty rows to fill in,
|
|
6110
|
+
// and written only once something is typed in them.
|
|
6111
|
+
|
|
6112
|
+
const drawn = editing ? [...rows, ...Array.from({
|
|
6113
|
+
length: Math.max(0, minRows - rows.length)
|
|
6114
|
+
}, () => ({}))] : rows;
|
|
6115
|
+
const totals = (0,_rows_js__WEBPACK_IMPORTED_MODULE_6__.rowTotals)(rows, columns);
|
|
6116
|
+
const hasTotals = Object.keys(totals).length > 0;
|
|
6117
|
+
const template = `${columns.map(track).join(' ')}${editing ? ' auto' : ''}`; // Every cell's mistake, by row and column, reported as one for the field:
|
|
6118
|
+
// the field holds the save while any cell does.
|
|
6119
|
+
|
|
6120
|
+
const errors = cellErrorsOf(node, key);
|
|
6121
|
+
|
|
6122
|
+
const reportCells = () => {
|
|
6123
|
+
const any = [...errors.values()].some(Boolean);
|
|
6124
|
+
host.setFieldError?.(field, any ? word(node, 'cellsWrong') : null);
|
|
6125
|
+
};
|
|
6126
|
+
|
|
6127
|
+
const writeRows = next => {
|
|
6128
|
+
const computed = next.map(row => (0,_rows_js__WEBPACK_IMPORTED_MODULE_6__.computeRow)(row, columns, locale));
|
|
6129
|
+
host.setFieldValue(field, computed.length ? computed : null);
|
|
6130
|
+
};
|
|
6131
|
+
|
|
6132
|
+
const writeCell = (index, column, cell) => {
|
|
6133
|
+
const next = drawn.map(row => ({ ...row
|
|
6134
|
+
}));
|
|
6135
|
+
if (cell === null || typeof cell === 'undefined' || cell === '') delete next[index][column.key];else next[index][column.key] = cell;
|
|
6136
|
+
writeRows(next.slice(0, Math.max(rows.length, index + 1)));
|
|
6137
|
+
}; // Rows move: what was typed in a cell and is not a value yet goes with the
|
|
6138
|
+
// cell's place, so the drafts of this field are dropped.
|
|
6139
|
+
|
|
6140
|
+
|
|
6141
|
+
const reshape = next => {
|
|
6142
|
+
for (const path of [...draftsOf(node).keys()]) if (path.startsWith(`${key}[`)) draftsOf(node).delete(path);
|
|
6143
|
+
|
|
6144
|
+
for (const path of [...leftOf(node)]) if (path.startsWith(`${key}[`)) leftOf(node).delete(path);
|
|
6145
|
+
|
|
6146
|
+
errors.clear();
|
|
6147
|
+
reportCells();
|
|
6148
|
+
writeRows(next);
|
|
6149
|
+
};
|
|
6150
|
+
|
|
6151
|
+
const cell = (row, index, column) => {
|
|
6152
|
+
const path = `${key}[${index}].${column.key}`;
|
|
6153
|
+
const value = row[column.key];
|
|
6154
|
+
|
|
6155
|
+
if (!editing || column.compute || field.readonly === true || column.readonly === true) {
|
|
6156
|
+
const text = cellText(column, column.compute && editing ? (0,_rows_js__WEBPACK_IMPORTED_MODULE_6__.computeRow)(row, columns, locale)[column.key] : value, columns, locale, ctx.viewHost);
|
|
6157
|
+
return (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="sf-view-items-text ${column.compute ? 'color-on-surface-variant' : ''}" data-computed=${column.compute ? 'true' : lit__WEBPACK_IMPORTED_MODULE_0__.nothing}>${text ?? (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="color-on-surface-variant">—</span>`}</span>`;
|
|
6158
|
+
}
|
|
6159
|
+
|
|
6160
|
+
const cellField = { ...column,
|
|
6161
|
+
key: path,
|
|
6162
|
+
label: column.label || column.key,
|
|
6163
|
+
value
|
|
6164
|
+
};
|
|
6165
|
+
const cellHost = {
|
|
6166
|
+
setFieldValue: (_target, next) => writeCell(index, column, next),
|
|
6167
|
+
setFieldError: (_target, message) => {
|
|
6168
|
+
errors.set(path, message || null);
|
|
6169
|
+
reportCells();
|
|
6170
|
+
}
|
|
6171
|
+
};
|
|
6172
|
+
const cellCtx = { ...ctx,
|
|
6173
|
+
path
|
|
6174
|
+
};
|
|
6175
|
+
|
|
6176
|
+
switch (column.propertyType) {
|
|
6177
|
+
case 'money':
|
|
6178
|
+
return renderMoneyEdit({ ...cellField,
|
|
6179
|
+
currency: column.currency
|
|
6180
|
+
}, value, cellHost, cellCtx);
|
|
6181
|
+
|
|
6182
|
+
case 'percent':
|
|
6183
|
+
return renderPercentEdit(cellField, value, cellHost, cellCtx);
|
|
6184
|
+
|
|
6185
|
+
case 'number':
|
|
6186
|
+
return renderDecimalEdit(cellField, value, cellHost, cellCtx);
|
|
6187
|
+
|
|
6188
|
+
default:
|
|
6189
|
+
{
|
|
6190
|
+
const control = (0,_field_renderers_index_js__WEBPACK_IMPORTED_MODULE_2__.renderEditField)(cellField, value, cellHost, {
|
|
6191
|
+
size: ctx.size
|
|
6192
|
+
});
|
|
6193
|
+
return control ?? (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="sf-view-items-text">${cellText(column, value, columns, locale, ctx.viewHost) ?? '—'}</span>`;
|
|
6194
|
+
}
|
|
6195
|
+
}
|
|
6196
|
+
};
|
|
6197
|
+
|
|
6198
|
+
const rowActions = index => (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
6199
|
+
<span class="sf-view-items-actions flex items-cross-center gap-1/4" role="cell">
|
|
6200
|
+
<sf-button-icon size="1/2" appearance="link" scheme="secondary" icon="keyboard_arrow_up"
|
|
6201
|
+
data-action="row-up" ?disabled=${index === 0 || index >= rows.length}
|
|
6202
|
+
title=${word(node, 'rowUp')} aria-label=${word(node, 'rowUp')}
|
|
6203
|
+
@click=${() => {
|
|
6204
|
+
const next = rows.slice();
|
|
6205
|
+
[next[index - 1], next[index]] = [next[index], next[index - 1]];
|
|
6206
|
+
reshape(next);
|
|
6207
|
+
}}></sf-button-icon>
|
|
6208
|
+
<sf-button-icon size="1/2" appearance="link" scheme="secondary" icon="keyboard_arrow_down"
|
|
6209
|
+
data-action="row-down" ?disabled=${index >= rows.length - 1}
|
|
6210
|
+
title=${word(node, 'rowDown')} aria-label=${word(node, 'rowDown')}
|
|
6211
|
+
@click=${() => {
|
|
6212
|
+
const next = rows.slice();
|
|
6213
|
+
[next[index + 1], next[index]] = [next[index], next[index + 1]];
|
|
6214
|
+
reshape(next);
|
|
6215
|
+
}}></sf-button-icon>
|
|
6216
|
+
<sf-button-icon size="1/2" appearance="link" scheme="secondary" icon="delete"
|
|
6217
|
+
data-action="row-remove" ?disabled=${drawn.length <= minRows || index >= rows.length}
|
|
6218
|
+
title=${word(node, 'removeRow')} aria-label=${word(node, 'removeRow')}
|
|
6219
|
+
@click=${() => reshape(rows.filter((_row, at) => at !== index))}></sf-button-icon>
|
|
6220
|
+
</span>
|
|
6221
|
+
`;
|
|
6222
|
+
|
|
6223
|
+
return (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
6224
|
+
<span class="sf-view-items-box">
|
|
6225
|
+
<span class="sf-view-items ${editing ? 'sf-view-items--edit' : ''}" data-items=${key} role="table"
|
|
6226
|
+
aria-label=${field.label || key} style=${`--sf-view-items-columns: ${template}`}>
|
|
6227
|
+
<span class="sf-view-items-head" role="row">
|
|
6228
|
+
${columns.map(column => (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="sf-view-items-heading ${_rows_js__WEBPACK_IMPORTED_MODULE_6__.NUMERIC.has(column.propertyType) ? 'sf-view-items-numeric' : ''}" role="columnheader">${column.label || column.key}${editing && column.required && !column.compute ? (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="color-error" aria-hidden="true"> *</span>` : lit__WEBPACK_IMPORTED_MODULE_0__.nothing}</span>`)}
|
|
6229
|
+
${editing ? (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="sf-view-items-heading" role="columnheader"></span>` : lit__WEBPACK_IMPORTED_MODULE_0__.nothing}
|
|
6230
|
+
</span>
|
|
6231
|
+
${drawn.map((row, index) => (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
6232
|
+
<span class="sf-view-items-row" role="row" data-row=${index} aria-label=${word(node, 'row', {
|
|
6233
|
+
n: index + 1
|
|
6234
|
+
})}>
|
|
6235
|
+
${columns.map(column => (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
6236
|
+
<span class="sf-view-items-cell ${_rows_js__WEBPACK_IMPORTED_MODULE_6__.NUMERIC.has(column.propertyType) ? 'sf-view-items-numeric' : ''}" role="cell" data-column=${column.key}>
|
|
6237
|
+
<span class="sf-view-items-cell-label color-on-surface-variant" aria-hidden="true">${column.label || column.key}</span>
|
|
6238
|
+
${cell(row, index, column)}
|
|
6239
|
+
</span>`)}
|
|
6240
|
+
${editing ? rowActions(index) : lit__WEBPACK_IMPORTED_MODULE_0__.nothing}
|
|
6241
|
+
</span>`)}
|
|
6242
|
+
${hasTotals && rows.length ? (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
6243
|
+
<span class="sf-view-items-row sf-view-items-totals" role="row" data-totals="true">
|
|
6244
|
+
${columns.map((column, at) => (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
6245
|
+
<span class="sf-view-items-cell ${_rows_js__WEBPACK_IMPORTED_MODULE_6__.NUMERIC.has(column.propertyType) ? 'sf-view-items-numeric' : ''} ${Object.hasOwn(totals, column.key) ? '' : 'sf-view-items-cell--blank'}" role="cell" data-column=${column.key}>
|
|
6246
|
+
${Object.hasOwn(totals, column.key) ? (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="sf-view-items-cell-label color-on-surface-variant" aria-hidden="true">${word(node, 'total')}: ${column.label || column.key}</span><span class="sf-view-items-text sf-view-items-total">${totalText(column, totals[column.key], columns, locale)}</span>` : at === 0 ? (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="sf-view-items-text sf-view-items-total-word">${word(node, 'total')}</span>` : lit__WEBPACK_IMPORTED_MODULE_0__.nothing}
|
|
6247
|
+
</span>`)}
|
|
6248
|
+
${editing ? (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span role="cell"></span>` : lit__WEBPACK_IMPORTED_MODULE_0__.nothing}
|
|
6249
|
+
</span>` : lit__WEBPACK_IMPORTED_MODULE_0__.nothing}
|
|
6250
|
+
${editing ? (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
6251
|
+
<span class="sf-view-items-foot flex items-cross-center gap-1">
|
|
6252
|
+
<sf-button appearance="outline" size=${ctx.size} icon="add" text=${word(node, 'addRow')}
|
|
6253
|
+
data-action="row-add" ?disabled=${drawn.length >= maxRows}
|
|
6254
|
+
@click=${() => reshape([...drawn, {}])}></sf-button>
|
|
6255
|
+
</span>` : lit__WEBPACK_IMPORTED_MODULE_0__.nothing}
|
|
6256
|
+
</span>
|
|
6257
|
+
</span>
|
|
6258
|
+
`;
|
|
6259
|
+
}
|
|
6260
|
+
|
|
6261
|
+
const CELL_ERRORS = new WeakMap();
|
|
6262
|
+
|
|
6263
|
+
function cellErrorsOf(node, key) {
|
|
6264
|
+
if (!CELL_ERRORS.has(node)) CELL_ERRORS.set(node, new Map());
|
|
6265
|
+
const all = CELL_ERRORS.get(node);
|
|
6266
|
+
if (!all.has(key)) all.set(key, new Map());
|
|
6267
|
+
return all.get(key);
|
|
6268
|
+
}
|
|
6269
|
+
|
|
6270
|
+
function renderItemsEdit(field, value, host, ctx) {
|
|
6271
|
+
return renderItemsTable(field, (0,_rows_js__WEBPACK_IMPORTED_MODULE_6__.rowsOf)(value), (0,_rows_js__WEBPACK_IMPORTED_MODULE_6__.columnsOf)(field), ctx, {
|
|
6272
|
+
editing: true,
|
|
6273
|
+
host
|
|
6274
|
+
});
|
|
6275
|
+
}
|
|
6276
|
+
|
|
6277
|
+
function renderItemsView(field, value, ctx) {
|
|
6278
|
+
const rows = (0,_rows_js__WEBPACK_IMPORTED_MODULE_6__.rowsOf)(value);
|
|
6279
|
+
if (!rows.length) return lit__WEBPACK_IMPORTED_MODULE_0__.nothing;
|
|
6280
|
+
if (ctx.summary) return (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="sf-field-view">${itemsSummary(field, rows, (0,_rows_js__WEBPACK_IMPORTED_MODULE_6__.columnsOf)(field), ctx)}</span>`;
|
|
6281
|
+
return renderItemsTable(field, rows, (0,_rows_js__WEBPACK_IMPORTED_MODULE_6__.columnsOf)(field), ctx, {
|
|
6282
|
+
editing: false,
|
|
6283
|
+
host: null
|
|
6284
|
+
});
|
|
6285
|
+
} // --- compound: a fixed set of parts, one value --------------------------------------
|
|
6286
|
+
// The parts of a requisites field, in the standard's order, of those the field
|
|
6287
|
+
// names (`parts`; all six when it names none). Each is typed as digits, the КПП
|
|
6288
|
+
// as digits and capital letters.
|
|
6289
|
+
|
|
6290
|
+
|
|
6291
|
+
const REQUISITE_LENGTH = {
|
|
6292
|
+
inn: 12,
|
|
6293
|
+
kpp: 9,
|
|
6294
|
+
ogrn: 15,
|
|
6295
|
+
bik: 9,
|
|
6296
|
+
account: 20,
|
|
6297
|
+
corr_account: 20
|
|
6298
|
+
};
|
|
6299
|
+
|
|
6300
|
+
function requisiteParts(field) {
|
|
6301
|
+
const named = Array.isArray(field.parts) ? field.parts.map(String) : _requisites_js__WEBPACK_IMPORTED_MODULE_7__.REQUISITE_PARTS;
|
|
6302
|
+
return _requisites_js__WEBPACK_IMPORTED_MODULE_7__.REQUISITE_PARTS.filter(part => named.includes(part));
|
|
6303
|
+
}
|
|
6304
|
+
|
|
6305
|
+
function compoundParts(field) {
|
|
6306
|
+
return (Array.isArray(field.parts) ? field.parts : []).map(part => part && typeof part === 'object' ? { ...part,
|
|
6307
|
+
propertyType: part.propertyType || part.type
|
|
6308
|
+
} : null).filter(part => part?.key);
|
|
6309
|
+
}
|
|
6310
|
+
|
|
6311
|
+
function objectOf(value) {
|
|
6312
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
6313
|
+
} // The value written: the parts in their order, an empty part no key, and
|
|
6314
|
+
// nothing at all when every part is empty.
|
|
6315
|
+
|
|
6316
|
+
|
|
6317
|
+
function compoundValue(order, value) {
|
|
6318
|
+
const next = {};
|
|
6319
|
+
|
|
6320
|
+
for (const part of order) {
|
|
6321
|
+
const one = value[part];
|
|
6322
|
+
if (one !== null && typeof one !== 'undefined' && one !== '') next[part] = one;
|
|
6323
|
+
}
|
|
6324
|
+
|
|
6325
|
+
return Object.keys(next).length ? next : null;
|
|
6326
|
+
}
|
|
6327
|
+
|
|
6328
|
+
function renderRequisitesEdit(field, value, host, ctx) {
|
|
6329
|
+
const node = ctx.node;
|
|
6330
|
+
const key = String(field.key ?? '');
|
|
6331
|
+
const parts = requisiteParts(field);
|
|
6332
|
+
const held = objectOf(value);
|
|
6333
|
+
const errors = (0,_requisites_js__WEBPACK_IMPORTED_MODULE_7__.requisiteErrors)(held, parts);
|
|
6334
|
+
|
|
6335
|
+
const write = (part, text) => {
|
|
6336
|
+
const cleaned = part === 'kpp' ? text.replace(/\s/gu, '').toUpperCase() : text.replace(/\D/gu, '');
|
|
6337
|
+
const next = compoundValue(parts, { ...held,
|
|
6338
|
+
[part]: cleaned
|
|
6339
|
+
});
|
|
6340
|
+
const mistakes = (0,_requisites_js__WEBPACK_IMPORTED_MODULE_7__.requisiteErrors)(next ?? {}, parts); // Nothing typed in a field that is not required is no mistake.
|
|
6341
|
+
|
|
6342
|
+
const blank = next === null && field.required !== true;
|
|
6343
|
+
host.setFieldValue(field, next);
|
|
6344
|
+
host.setFieldError?.(field, !blank && Object.keys(mistakes).length ? word(node, mistakes[Object.keys(mistakes)[0]]) : null);
|
|
6345
|
+
};
|
|
6346
|
+
|
|
6347
|
+
return (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
6348
|
+
<span class="sf-view-fields-compound grid gap-1 w-full min-w-0" data-compound=${key}>
|
|
6349
|
+
${parts.map(part => {
|
|
6350
|
+
const path = `${key}.${part}`;
|
|
6351
|
+
const left = leftOf(node).has(path);
|
|
6352
|
+
const blank = Object.keys(held).length === 0 && field.required !== true;
|
|
6353
|
+
const message = left && !blank && errors[part] ? word(node, errors[part]) : '';
|
|
6354
|
+
return (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
6355
|
+
<span class="sf-view-fields-part flex flex-col gap-1/4 min-w-0 ${part === 'account' || part === 'corr_account' ? 'sf-view-fields-part--wide' : ''}" data-part=${part}>
|
|
6356
|
+
<span class="sf-view-fields-part-label color-on-surface-variant">${word(node, part)}${part === 'inn' && field.required ? (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="color-error" aria-hidden="true"> *</span>` : lit__WEBPACK_IMPORTED_MODULE_0__.nothing}</span>
|
|
6357
|
+
<sf-input
|
|
6358
|
+
size=${ctx.size}
|
|
6359
|
+
appearance="bordered"
|
|
6360
|
+
inputmode=${part === 'kpp' ? 'text' : 'numeric'}
|
|
6361
|
+
maxlength=${String(REQUISITE_LENGTH[part])}
|
|
6362
|
+
value=${String(held[part] ?? '')}
|
|
6363
|
+
?required=${part === 'inn' && field.required === true}
|
|
6364
|
+
?invalid=${Boolean(message)}
|
|
6365
|
+
error-message=${message || lit__WEBPACK_IMPORTED_MODULE_0__.nothing}
|
|
6366
|
+
aria-label=${`${field.label || key}: ${word(node, part)}`}
|
|
6367
|
+
@input=${event => write(part, String(event.target?.value ?? ''))}
|
|
6368
|
+
@change=${event => write(part, String(event.target?.value ?? ''))}
|
|
6369
|
+
@focusout=${() => {
|
|
6370
|
+
leftOf(node).add(path);
|
|
6371
|
+
redraw(node);
|
|
6372
|
+
}}
|
|
6373
|
+
></sf-input>
|
|
6374
|
+
</span>`;
|
|
6375
|
+
})}
|
|
6376
|
+
</span>
|
|
6377
|
+
`;
|
|
6378
|
+
}
|
|
6379
|
+
|
|
6380
|
+
function requisitesText(field, value, node) {
|
|
6381
|
+
const held = objectOf(value);
|
|
6382
|
+
return requisiteParts(field).filter(part => held[part]).map(part => `${word(node, part)} ${held[part]}`).join(', ');
|
|
6383
|
+
}
|
|
6384
|
+
|
|
6385
|
+
function renderRequisitesView(field, value, ctx) {
|
|
6386
|
+
const text = requisitesText(field, value, ctx.node);
|
|
6387
|
+
return text ? (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="sf-field-view">${text}</span>` : lit__WEBPACK_IMPORTED_MODULE_0__.nothing;
|
|
6388
|
+
}
|
|
6389
|
+
|
|
6390
|
+
function renderCompoundEdit(field, value, host, ctx) {
|
|
6391
|
+
const node = ctx.node;
|
|
6392
|
+
const key = String(field.key ?? '');
|
|
6393
|
+
const parts = compoundParts(field);
|
|
6394
|
+
const order = parts.map(part => part.key);
|
|
6395
|
+
const held = objectOf(value);
|
|
6396
|
+
const errors = cellErrorsOf(node, key);
|
|
6397
|
+
return (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
6398
|
+
<span class="sf-view-fields-compound grid gap-1 w-full min-w-0" data-compound=${key}>
|
|
6399
|
+
${parts.map(part => {
|
|
6400
|
+
const path = `${key}.${part.key}`;
|
|
6401
|
+
const partField = { ...part,
|
|
6402
|
+
key: path,
|
|
6403
|
+
label: part.label || part.key,
|
|
6404
|
+
readonly: field.readonly === true || part.readonly === true
|
|
6405
|
+
};
|
|
6406
|
+
const partHost = {
|
|
6407
|
+
setFieldValue: (_target, next) => host.setFieldValue(field, compoundValue(order, { ...held,
|
|
6408
|
+
[part.key]: next
|
|
6409
|
+
})),
|
|
6410
|
+
setFieldError: (_target, message) => {
|
|
6411
|
+
errors.set(path, message || null);
|
|
6412
|
+
const first = [...errors.values()].find(Boolean);
|
|
6413
|
+
host.setFieldError?.(field, first ?? null);
|
|
6414
|
+
}
|
|
6415
|
+
};
|
|
6416
|
+
const partCtx = { ...ctx,
|
|
6417
|
+
path
|
|
6418
|
+
};
|
|
6419
|
+
const control = SET_CONTROLS[part.propertyType]?.edit(partField, held[part.key], partHost, partCtx) ?? (0,_field_renderers_index_js__WEBPACK_IMPORTED_MODULE_2__.renderEditField)(partField, held[part.key], partHost, {
|
|
6420
|
+
size: ctx.size
|
|
6421
|
+
});
|
|
6422
|
+
return (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`
|
|
6423
|
+
<span class="sf-view-fields-part flex flex-col gap-1/4 min-w-0 ${part.width === 'full' ? 'sf-view-fields-part--wide' : ''}" data-part=${part.key}>
|
|
6424
|
+
<span class="sf-view-fields-part-label color-on-surface-variant">${partField.label}${part.required ? (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="color-error" aria-hidden="true"> *</span>` : lit__WEBPACK_IMPORTED_MODULE_0__.nothing}</span>
|
|
6425
|
+
${control ?? lit__WEBPACK_IMPORTED_MODULE_0__.nothing}
|
|
6426
|
+
</span>`;
|
|
6427
|
+
})}
|
|
6428
|
+
</span>
|
|
6429
|
+
`;
|
|
6430
|
+
}
|
|
6431
|
+
|
|
6432
|
+
function renderCompoundView(field, value, ctx) {
|
|
6433
|
+
const held = objectOf(value);
|
|
6434
|
+
const shown = compoundParts(field).filter(part => held[part.key] !== null && typeof held[part.key] !== 'undefined' && held[part.key] !== '');
|
|
6435
|
+
if (!shown.length) return lit__WEBPACK_IMPORTED_MODULE_0__.nothing;
|
|
6436
|
+
return (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="sf-field-view sf-view-fields-compound-view">${shown.map((part, at) => {
|
|
6437
|
+
const own = SET_CONTROLS[part.propertyType]?.view({ ...part
|
|
6438
|
+
}, held[part.key], ctx);
|
|
6439
|
+
const content = own ?? (0,_field_renderers_index_js__WEBPACK_IMPORTED_MODULE_2__.renderViewField)({ ...part
|
|
6440
|
+
}, held[part.key], ctx.viewHost, {
|
|
6441
|
+
size: ctx.size
|
|
6442
|
+
}) ?? String(held[part.key]);
|
|
6443
|
+
return (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`${at ? ', ' : ''}<span class="color-on-surface-variant">${part.label || part.key}</span> ${content}`;
|
|
6444
|
+
})}</span>`;
|
|
6445
|
+
} // --- the map ---------------------------------------------------------------------
|
|
6446
|
+
|
|
6447
|
+
|
|
6448
|
+
const SET_CONTROLS = {
|
|
6449
|
+
money: {
|
|
6450
|
+
edit: renderMoneyEdit,
|
|
6451
|
+
view: renderMoneyView
|
|
6452
|
+
},
|
|
6453
|
+
percent: {
|
|
6454
|
+
edit: renderPercentEdit,
|
|
6455
|
+
view: renderPercentView
|
|
6456
|
+
},
|
|
6457
|
+
items: {
|
|
6458
|
+
edit: renderItemsEdit,
|
|
6459
|
+
view: renderItemsView
|
|
6460
|
+
},
|
|
6461
|
+
compound: {
|
|
6462
|
+
edit: renderCompoundEdit,
|
|
6463
|
+
view: renderCompoundView
|
|
6464
|
+
},
|
|
6465
|
+
'ru.requisites': {
|
|
6466
|
+
edit: renderRequisitesEdit,
|
|
6467
|
+
view: renderRequisitesView
|
|
6468
|
+
}
|
|
6469
|
+
};
|
|
6470
|
+
/** Whether the set draws this property type itself. */
|
|
6471
|
+
|
|
6472
|
+
function drawsSetField(field) {
|
|
6473
|
+
return Object.hasOwn(SET_CONTROLS, String(field?.propertyType || field?.type || ''));
|
|
6474
|
+
}
|
|
6475
|
+
/**
|
|
6476
|
+
* A field of a type the set draws, edited or shown. `ctx` is {node, size,
|
|
6477
|
+
* viewHost, disabled}: the element whose page language the words follow, the
|
|
6478
|
+
* size on the ladder, the host's formatFieldValue, and whether a condition
|
|
6479
|
+
* turns the field off. A field the person may not change is shown, as the
|
|
6480
|
+
* Framework renderers show one.
|
|
6481
|
+
*/
|
|
6482
|
+
|
|
6483
|
+
function renderSetField(field, value, editing, host, ctx) {
|
|
6484
|
+
const control = SET_CONTROLS[String(field.propertyType || field.type)];
|
|
6485
|
+
if (!control) return null;
|
|
6486
|
+
const formatted = !editing ? ctx.viewHost?.formatFieldValue?.(field, value) : null;
|
|
6487
|
+
if (typeof formatted === 'string' && formatted !== '') return (0,lit__WEBPACK_IMPORTED_MODULE_0__.html)`<span class="sf-field-view">${formatted}</span>`;
|
|
6488
|
+
|
|
6489
|
+
if (editing && field.readonly !== true && ctx.disabled !== true && host?.setFieldValue) {
|
|
6490
|
+
return control.edit(field, value, host, ctx);
|
|
6491
|
+
}
|
|
6492
|
+
|
|
6493
|
+
return control.view(field, value, ctx);
|
|
6494
|
+
}
|
|
6495
|
+
/** A field's value as one line of text: «1 500,50 ₽», «2 строки, итого 2 499,99 ₽». */
|
|
6496
|
+
|
|
6497
|
+
function setFieldText(field, value, node) {
|
|
6498
|
+
const locale = (0,_core_js_page_language__WEBPACK_IMPORTED_MODULE_3__.pageLocale)(node);
|
|
6499
|
+
|
|
6500
|
+
switch (String(field?.propertyType || field?.type || '')) {
|
|
6501
|
+
case 'money':
|
|
6502
|
+
return moneyText(field, value, locale);
|
|
6503
|
+
|
|
6504
|
+
case 'percent':
|
|
6505
|
+
return (0,_money_js__WEBPACK_IMPORTED_MODULE_5__.percentText)(value, locale);
|
|
6506
|
+
|
|
6507
|
+
case 'items':
|
|
6508
|
+
{
|
|
6509
|
+
const rows = (0,_rows_js__WEBPACK_IMPORTED_MODULE_6__.rowsOf)(value);
|
|
6510
|
+
return rows.length ? itemsSummary(field, rows, (0,_rows_js__WEBPACK_IMPORTED_MODULE_6__.columnsOf)(field), {
|
|
6511
|
+
node
|
|
6512
|
+
}) : '';
|
|
6513
|
+
}
|
|
6514
|
+
|
|
6515
|
+
case 'ru.requisites':
|
|
6516
|
+
return requisitesText(field, value, node);
|
|
6517
|
+
|
|
6518
|
+
default:
|
|
6519
|
+
return null;
|
|
6520
|
+
}
|
|
6521
|
+
}
|
|
6522
|
+
__webpack_async_result__();
|
|
6523
|
+
} catch(e) { __webpack_async_result__(e); } });
|
|
6524
|
+
|
|
6525
|
+
/***/ },
|
|
6526
|
+
|
|
6527
|
+
/***/ "2f31b00ad99a"
|
|
6528
|
+
(module, __webpack_exports__, __webpack_require__) {
|
|
6529
|
+
|
|
6530
|
+
__webpack_require__.a(module, async (__webpack_handle_async_dependencies__, __webpack_async_result__) => { try {
|
|
6531
|
+
__webpack_require__.r(__webpack_exports__);
|
|
6532
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
6533
|
+
/* harmony export */ DATE_TYPES: () => (/* binding */ DATE_TYPES),
|
|
6534
|
+
/* harmony export */ formatDateValue: () => (/* binding */ formatDateValue)
|
|
6535
|
+
/* harmony export */ });
|
|
6536
|
+
/* harmony import */ var _core_js_format_date_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("e577837dd70a");
|
|
6537
|
+
var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([_core_js_format_date_js__WEBPACK_IMPORTED_MODULE_0__]);
|
|
6538
|
+
var __webpack_async_dependencies_result__ = (__webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__);
|
|
6539
|
+
_core_js_format_date_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_async_dependencies_result__[0];
|
|
6540
|
+
// A date, a date and time or a time of day in the page's language, through
|
|
6541
|
+
// the SIMAI Framework's format-date module, for a value the host did not
|
|
6542
|
+
// format (formatFieldValue). The host's own text still wins; without one, a
|
|
6543
|
+
// date read «2026-11-02» where a person expects «2 нояб. 2026 г.».
|
|
6544
|
+
//
|
|
6545
|
+
// The forms are read as a host stores them: a day «YYYY-MM-DD» is that day on
|
|
6546
|
+
// the reader's calendar (not UTC midnight, which is the day before west of
|
|
6547
|
+
// Greenwich); a date and time with a zone is that moment in the reader's zone
|
|
6548
|
+
// and one without a zone is the wall clock as written; a time «hh:mm[:ss]» is
|
|
6549
|
+
// a time of day. Anything else is left for the caller to print as it came.
|
|
6550
|
+
|
|
6551
|
+
const DAY = /^(\d{4})-(\d{2})-(\d{2})$/u;
|
|
6552
|
+
const TIME = /^(\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?$/u;
|
|
6553
|
+
const DATE_TIME = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?$/u;
|
|
6554
|
+
const DATE_TYPES = new Set(['date', 'datetime', 'time']);
|
|
6555
|
+
|
|
6556
|
+
function readMoment(type, text) {
|
|
6557
|
+
const day = DAY.exec(text);
|
|
6558
|
+
if (day) return new Date(Number(day[1]), Number(day[2]) - 1, Number(day[3]));
|
|
6559
|
+
const time = TIME.exec(text);
|
|
6560
|
+
if (time) return type === 'time' ? new Date(2000, 0, 1, Number(time[1]), Number(time[2]), Number(time[3] ?? 0)) : null;
|
|
6561
|
+
const stamp = DATE_TIME.exec(text);
|
|
6562
|
+
if (!stamp) return null;
|
|
6563
|
+
|
|
6564
|
+
if (stamp[7]) {
|
|
6565
|
+
const moment = new Date(text.replace(' ', 'T'));
|
|
6566
|
+
return Number.isNaN(moment.getTime()) ? null : moment;
|
|
6567
|
+
}
|
|
6568
|
+
|
|
6569
|
+
return new Date(Number(stamp[1]), Number(stamp[2]) - 1, Number(stamp[3]), Number(stamp[4]), Number(stamp[5]), Number(stamp[6] ?? 0));
|
|
6570
|
+
} // The text for a value of a date type, or '' when it is not one this reads.
|
|
4675
6571
|
|
|
4676
6572
|
|
|
4677
6573
|
function formatDateValue(type, value, node) {
|
|
@@ -4703,6 +6599,15 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
4703
6599
|
// extracted by mini-css-extract-plugin
|
|
4704
6600
|
|
|
4705
6601
|
|
|
6602
|
+
/***/ },
|
|
6603
|
+
|
|
6604
|
+
/***/ "f59c8bcdc90a"
|
|
6605
|
+
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
6606
|
+
|
|
6607
|
+
__webpack_require__.r(__webpack_exports__);
|
|
6608
|
+
// extracted by mini-css-extract-plugin
|
|
6609
|
+
|
|
6610
|
+
|
|
4706
6611
|
/***/ },
|
|
4707
6612
|
|
|
4708
6613
|
/***/ "5b7d7695b41b"
|
|
@@ -5665,7 +7570,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
5665
7570
|
/***/ "e4c87bda9c30"
|
|
5666
7571
|
(module) {
|
|
5667
7572
|
|
|
5668
|
-
module.exports = /*#__PURE__*/JSON.parse('{"id":"smart.view-flow","tag":"sf-view-flow","version":"1.
|
|
7573
|
+
module.exports = /*#__PURE__*/JSON.parse('{"id":"smart.view-flow","tag":"sf-view-flow","version":"1.1.0","kind":"leaf","settings":{"mode":{"type":"enum","values":["view","edit"],"default":"view"},"heading":{"type":"string","default":""},"heading-level":{"type":"enum","values":["2","3","4","5","6"],"default":"3"},"fields":{"type":"list","default":[],"of":{"key":"string","label":{"type":"string","optional":true},"propertyType":"string","value":{"type":"any","nullable":true,"optional":true},"required":{"type":"boolean","optional":true},"readonly":{"type":"boolean","optional":true},"width":{"type":"string","optional":true},"hint":{"type":"string","optional":true},"when":{"type":"object","optional":true}}},"groups":{"type":"list","default":[],"of":{"key":"string","label":{"type":"string","optional":true},"collapsible":{"type":"boolean","optional":true},"collapsed":{"type":"boolean","optional":true},"fields":"list"}},"state":{"type":"enum","values":["open","pending","accepted","declined","expired"],"default":"open"},"expires-at":{"type":"date","optional":true},"block-id":{"type":"id","default":""},"disabled":{"type":"boolean","default":false},"size":{"type":"enum","values":["1","1/2"],"default":"1/2"},"cancelable":{"type":"boolean","default":true},"submit-text":{"type":"string","default-from":"runtime.submit-text"},"cancel-text":{"type":"string","default-from":"runtime.cancel-text"},"empty-text":{"type":"string","default-from":"runtime.empty-text"},"required-text":{"type":"string","default-from":"runtime.required-text"},"invalid-text":{"type":"string","default-from":"runtime.invalid-text"}},"values":{},"events":{"answer":{"carries":{"id":{"type":"id"},"accepted":"boolean","values":{"type":"object","optional":true}},"cancelable":true},"expired":{"carries":{"id":{"type":"id"}},"own-verb":true},"open":{"carries":{"id":{"type":"id"},"field":"string","value":"object"}}},"actions":{"open":{},"answer":{"with":{"accepted":"boolean"},"emits":"answer"},"confirm":{},"cancel":{},"settle":{"with":{"result":"object"}}},"layer":{"kind":"surface","locks-page":false,"traps-focus":false},"host-states":{"data-state":"string"},"css-variables":{"--sf-view-flow-radius":{"type":"length","state":"default"},"--sf-view-flow-inset":{"type":"length","state":"default"},"--sf-view-flow-gap":{"type":"length","state":"default"},"--sf-view-flow-background-color":{"type":"color","state":"default"},"--sf-view-flow-border-color":{"type":"color","state":"default"}}}');
|
|
5669
7574
|
|
|
5670
7575
|
/***/ },
|
|
5671
7576
|
|