@abgov/jsonforms-components 2.75.1 → 2.75.2
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/index.esm.js +252 -229
- package/package.json +1 -1
- package/renderer-catalog.json +2 -2
- package/src/lib/util/patternForm.d.ts +14 -0
package/index.esm.js
CHANGED
|
@@ -3902,6 +3902,38 @@ $$x({ target: 'Iterator', proto: true, real: true, forced: IS_PURE$2 }, {
|
|
|
3902
3902
|
map: map
|
|
3903
3903
|
});
|
|
3904
3904
|
|
|
3905
|
+
const sinTitle = 'Social insurance number';
|
|
3906
|
+
const invalidSin = 'Social insurance number is invalid';
|
|
3907
|
+
const DEFAULT_MAX_ITEMS = 50;
|
|
3908
|
+
const REQUIRED_PROPERTY_ERROR = 'is a required property';
|
|
3909
|
+
const ADDRESS_LOOKUP_LABELS = {
|
|
3910
|
+
addressLine1: 'Address line 1',
|
|
3911
|
+
addressLine2: 'Address line 2',
|
|
3912
|
+
municipality: 'City',
|
|
3913
|
+
postalCode: 'Postal code',
|
|
3914
|
+
subdivisionCode: 'Province',
|
|
3915
|
+
country: 'Country'
|
|
3916
|
+
};
|
|
3917
|
+
const VALIDATION_KEYWORDS = {
|
|
3918
|
+
REQUIRED: 'required',
|
|
3919
|
+
MIN_LENGTH: 'minLength',
|
|
3920
|
+
MAX_LENGTH: 'maxLength',
|
|
3921
|
+
FORMAT: 'format',
|
|
3922
|
+
MINIMUM: 'minimum',
|
|
3923
|
+
MAXIMUM: 'maximum',
|
|
3924
|
+
TYPE: 'type',
|
|
3925
|
+
ERROR_MESSAGE: 'errorMessage'
|
|
3926
|
+
};
|
|
3927
|
+
var StepStatus;
|
|
3928
|
+
(function (StepStatus) {
|
|
3929
|
+
StepStatus["NOT_STARTED"] = "NotStarted";
|
|
3930
|
+
StepStatus["IN_PROGRESS"] = "InProgress";
|
|
3931
|
+
StepStatus["COMPLETED"] = "Completed";
|
|
3932
|
+
})(StepStatus || (StepStatus = {}));
|
|
3933
|
+
const getAddressLookupFieldLabel = fieldName => {
|
|
3934
|
+
return ADDRESS_LOOKUP_LABELS[fieldName] || fieldName;
|
|
3935
|
+
};
|
|
3936
|
+
|
|
3905
3937
|
var classof$5 = classofRaw$2;
|
|
3906
3938
|
|
|
3907
3939
|
// `IsArray` abstract operation
|
|
@@ -4558,6 +4590,222 @@ const isRequiredBySchema = (rootSchema, rootData, path, options = {}) => {
|
|
|
4558
4590
|
return reqSet.has(dotPath);
|
|
4559
4591
|
};
|
|
4560
4592
|
|
|
4593
|
+
// Default formats keyed by type; the mask can be overridden per field via UI schema options.
|
|
4594
|
+
const DEFAULT_PATTERNS = {
|
|
4595
|
+
phone: {
|
|
4596
|
+
mask: '(###) ###-####',
|
|
4597
|
+
pattern: /^\(?\d{3}\)?[- ]?\d{3}[- ]?\d{4}$/,
|
|
4598
|
+
error: 'Must be a valid 10-digit phone number in format (000) 000-0000',
|
|
4599
|
+
allowedKeys: /[0-9]/
|
|
4600
|
+
},
|
|
4601
|
+
sin: {
|
|
4602
|
+
mask: '### ### ###',
|
|
4603
|
+
pattern: /^\d{3} \d{3} \d{3}$/,
|
|
4604
|
+
error: 'Must be a valid social insurance number in format 000 000 000',
|
|
4605
|
+
allowedKeys: /[0-9]/
|
|
4606
|
+
},
|
|
4607
|
+
postalCode: {
|
|
4608
|
+
mask: '### ###',
|
|
4609
|
+
pattern: /^[A-Za-z]\d[A-Za-z] \d[A-Za-z]\d$/,
|
|
4610
|
+
error: 'Must be in A0A 0A0 capital letters and numbers format',
|
|
4611
|
+
allowedKeys: /[A-Za-z0-9]/
|
|
4612
|
+
},
|
|
4613
|
+
driverId: {
|
|
4614
|
+
// Alberta driver's licence: six digits, a dash, then three digits.
|
|
4615
|
+
mask: '######-###',
|
|
4616
|
+
pattern: /^\d{6}-\d{3}$/,
|
|
4617
|
+
error: "Must be a valid driver's licence number in format 000000-000",
|
|
4618
|
+
allowedKeys: /[0-9]/
|
|
4619
|
+
},
|
|
4620
|
+
mvid: {
|
|
4621
|
+
// Motor vehicle ID: four digits, a dash, then five digits.
|
|
4622
|
+
mask: '####-#####',
|
|
4623
|
+
pattern: /^\d{4}-\d{5}$/,
|
|
4624
|
+
error: 'Must be a valid motor vehicle ID in format 0000-00000',
|
|
4625
|
+
allowedKeys: /[0-9]/
|
|
4626
|
+
}
|
|
4627
|
+
};
|
|
4628
|
+
// Resolve the mask format for a field: schema.format must match a DEFAULT_PATTERNS key.
|
|
4629
|
+
const resolveFormatConfig = schema => {
|
|
4630
|
+
if (!schema) {
|
|
4631
|
+
return undefined;
|
|
4632
|
+
}
|
|
4633
|
+
if (schema.format && schema.format in DEFAULT_PATTERNS) {
|
|
4634
|
+
return DEFAULT_PATTERNS[schema.format];
|
|
4635
|
+
}
|
|
4636
|
+
return schema.title === sinTitle ? DEFAULT_PATTERNS.sin : undefined;
|
|
4637
|
+
};
|
|
4638
|
+
const getSchemaErrorMessage = errorMessage => {
|
|
4639
|
+
if (typeof errorMessage === 'string' && errorMessage.trim() !== '') {
|
|
4640
|
+
return errorMessage;
|
|
4641
|
+
}
|
|
4642
|
+
if (errorMessage && typeof errorMessage === 'object') {
|
|
4643
|
+
const configured = errorMessage.pattern || errorMessage.format;
|
|
4644
|
+
if (typeof configured === 'string' && configured.trim() !== '') {
|
|
4645
|
+
return configured;
|
|
4646
|
+
}
|
|
4647
|
+
}
|
|
4648
|
+
return undefined;
|
|
4649
|
+
};
|
|
4650
|
+
// Prefer a schema or format-config error; callers fall back to AJV when this is empty.
|
|
4651
|
+
const getConfiguredFormatError = schema => {
|
|
4652
|
+
var _resolveFormatConfig;
|
|
4653
|
+
return getSchemaErrorMessage(schema == null ? void 0 : schema.errorMessage) || ((_resolveFormatConfig = resolveFormatConfig(schema)) == null ? void 0 : _resolveFormatConfig.error);
|
|
4654
|
+
};
|
|
4655
|
+
// Mask placeholder characters; each consumes one input character. Any other mask character is treated as a literal.
|
|
4656
|
+
const MASK_PLACEHOLDERS = new Set(['#', '*', '_']);
|
|
4657
|
+
const isPlaceholder = char => MASK_PLACEHOLDERS.has(char);
|
|
4658
|
+
// Navigation/editing keys that keystroke filtering must always let through.
|
|
4659
|
+
const MASK_CONTROL_KEYS = new Set(['Backspace', 'Delete', 'Tab', 'Enter', 'Escape', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End']);
|
|
4660
|
+
// A single-character key should be blocked when the format restricts input and the key isn't allowed.
|
|
4661
|
+
const shouldBlockKey = (key, allowedKeys) => !!allowedKeys && key.length === 1 && !MASK_CONTROL_KEYS.has(key) && !allowedKeys.test(key);
|
|
4662
|
+
// Removes characters the format doesn't allow (e.g. letters from a digit-only field), for paste/programmatic input.
|
|
4663
|
+
const filterAllowedKeys = (value, allowedKeys) => allowedKeys ? [...(value != null ? value : '')].filter(char => allowedKeys.test(char)).join('') : value != null ? value : '';
|
|
4664
|
+
// A mask slot holds one content character (letter or digit); everything else in the value is a separator.
|
|
4665
|
+
const NON_CONTENT = /[^a-zA-Z0-9]/g;
|
|
4666
|
+
const IS_CONTENT = /[a-zA-Z0-9]/;
|
|
4667
|
+
// Number of fill slots in a mask (its placeholder characters).
|
|
4668
|
+
const maskDigitCount = mask => [...mask].filter(isPlaceholder).length;
|
|
4669
|
+
// Formats a value into a placeholder mask (e.g. '(###) ###-####'), filling only the content entered so far.
|
|
4670
|
+
// Placeholder characters consume one content character each; every other mask character is a literal.
|
|
4671
|
+
const formatWithPattern = (value, mask) => {
|
|
4672
|
+
const maxContent = maskDigitCount(mask);
|
|
4673
|
+
const content = (value != null ? value : '').replace(NON_CONTENT, '').slice(0, maxContent);
|
|
4674
|
+
let index = 0;
|
|
4675
|
+
let formatted = '';
|
|
4676
|
+
for (const maskChar of mask) {
|
|
4677
|
+
if (index >= content.length) {
|
|
4678
|
+
break;
|
|
4679
|
+
}
|
|
4680
|
+
formatted += isPlaceholder(maskChar) ? content[index++] : maskChar;
|
|
4681
|
+
}
|
|
4682
|
+
return formatted;
|
|
4683
|
+
};
|
|
4684
|
+
// Fills the mask with the value's content, leaving the placeholder character for positions not yet entered.
|
|
4685
|
+
const toMaskTemplate = (value, mask) => {
|
|
4686
|
+
const maxContent = maskDigitCount(mask);
|
|
4687
|
+
const content = (value != null ? value : '').replace(NON_CONTENT, '').slice(0, maxContent);
|
|
4688
|
+
let index = 0;
|
|
4689
|
+
let display = '';
|
|
4690
|
+
for (const maskChar of mask) {
|
|
4691
|
+
display += isPlaceholder(maskChar) ? index < content.length ? content[index++] : maskChar : maskChar;
|
|
4692
|
+
}
|
|
4693
|
+
return display;
|
|
4694
|
+
};
|
|
4695
|
+
// Caret position just after the Nth content character within a rendered mask, so in-place edits keep the cursor in place.
|
|
4696
|
+
const caretAfterDigits = (display, digitCount) => {
|
|
4697
|
+
if (digitCount <= 0) {
|
|
4698
|
+
return 0;
|
|
4699
|
+
}
|
|
4700
|
+
let seen = 0;
|
|
4701
|
+
for (let i = 0; i < display.length; i++) {
|
|
4702
|
+
if (IS_CONTENT.test(display[i])) {
|
|
4703
|
+
seen += 1;
|
|
4704
|
+
if (seen === digitCount) {
|
|
4705
|
+
return i + 1;
|
|
4706
|
+
}
|
|
4707
|
+
}
|
|
4708
|
+
}
|
|
4709
|
+
return display.length;
|
|
4710
|
+
};
|
|
4711
|
+
// Computes an in-place masked edit from the raw input and caret index: what to show, what to store, where to put the caret.
|
|
4712
|
+
const computeMaskEdit = (rawValue, caretIndex, mask) => {
|
|
4713
|
+
const digitsBeforeCaret = rawValue.slice(0, caretIndex).replace(NON_CONTENT, '').length;
|
|
4714
|
+
const display = toMaskTemplate(rawValue, mask);
|
|
4715
|
+
const stored = formatWithPattern(rawValue, mask);
|
|
4716
|
+
const caret = caretAfterDigits(display, Math.min(digitsBeforeCaret, maskDigitCount(mask)));
|
|
4717
|
+
return {
|
|
4718
|
+
display,
|
|
4719
|
+
stored,
|
|
4720
|
+
caret
|
|
4721
|
+
};
|
|
4722
|
+
};
|
|
4723
|
+
// True once the value has as many content characters as the mask has fill slots.
|
|
4724
|
+
const isMaskFilled = (value, mask) => value.replace(NON_CONTENT, '').length >= maskDigitCount(mask);
|
|
4725
|
+
// A human placeholder for a mask, e.g. '(###) ###-####' -> '(000) 000-0000'.
|
|
4726
|
+
const maskPlaceholder = mask => [...mask].map(char => isPlaceholder(char) ? '0' : char).join('');
|
|
4727
|
+
// GoA retargets composed events to the host, so prefer the inner native input from the composed path.
|
|
4728
|
+
const getMaskInputTarget = detail => {
|
|
4729
|
+
var _target$shadowRoot$qu, _target$shadowRoot;
|
|
4730
|
+
const event = detail.event;
|
|
4731
|
+
if (!event) {
|
|
4732
|
+
return undefined;
|
|
4733
|
+
}
|
|
4734
|
+
const path = typeof event.composedPath === 'function' ? event.composedPath() : [];
|
|
4735
|
+
const fromPath = path.find(node => node instanceof HTMLInputElement);
|
|
4736
|
+
if (fromPath) {
|
|
4737
|
+
return fromPath;
|
|
4738
|
+
}
|
|
4739
|
+
const target = event.target;
|
|
4740
|
+
if (target instanceof HTMLInputElement) {
|
|
4741
|
+
return target;
|
|
4742
|
+
}
|
|
4743
|
+
return (_target$shadowRoot$qu = target == null || (_target$shadowRoot = target.shadowRoot) == null ? void 0 : _target$shadowRoot.querySelector('input')) != null ? _target$shadowRoot$qu : undefined;
|
|
4744
|
+
};
|
|
4745
|
+
const applyMaskValue = (target, edit) => {
|
|
4746
|
+
target.value = edit.display;
|
|
4747
|
+
target.setSelectionRange == null || target.setSelectionRange(edit.caret, edit.caret);
|
|
4748
|
+
};
|
|
4749
|
+
// Applies an in-place edit to the inner input and re-applies after the web component paints.
|
|
4750
|
+
const applyInPlaceEdit = (target, edit) => {
|
|
4751
|
+
if (!target) {
|
|
4752
|
+
return;
|
|
4753
|
+
}
|
|
4754
|
+
applyMaskValue(target, edit);
|
|
4755
|
+
requestAnimationFrame(() => applyMaskValue(target, edit));
|
|
4756
|
+
};
|
|
4757
|
+
// When the template is already filled, extra content characters must be rejected rather than shifting the value.
|
|
4758
|
+
const overflowMaskEdit = (previousDisplay, rawValue, caretIndex, mask) => {
|
|
4759
|
+
if (!isMaskFilled(previousDisplay, mask)) {
|
|
4760
|
+
return undefined;
|
|
4761
|
+
}
|
|
4762
|
+
if ((rawValue != null ? rawValue : '').replace(NON_CONTENT, '').length <= maskDigitCount(mask)) {
|
|
4763
|
+
return undefined;
|
|
4764
|
+
}
|
|
4765
|
+
return {
|
|
4766
|
+
display: previousDisplay,
|
|
4767
|
+
stored: formatWithPattern(previousDisplay, mask),
|
|
4768
|
+
caret: Math.min(Math.max(caretIndex, 0), previousDisplay.length)
|
|
4769
|
+
};
|
|
4770
|
+
};
|
|
4771
|
+
/**
|
|
4772
|
+
* Applies a mask template to a value, e.g. mask "(###) ###-####" turns "7801234567" into "(780) 123-4567".
|
|
4773
|
+
* '#' consumes one content character from the input; every other character is a literal inserted into the output.
|
|
4774
|
+
* The mask's literal characters are stripped from the input first so an already formatted value re-formats cleanly.
|
|
4775
|
+
* Returns the original value unchanged when the mask is empty or no content characters remain.
|
|
4776
|
+
* @param value - The raw input value
|
|
4777
|
+
* @param mask - The mask template string
|
|
4778
|
+
* @returns The masked value
|
|
4779
|
+
*/
|
|
4780
|
+
const applyFormatPattern = (value, mask) => {
|
|
4781
|
+
if (!mask || !value) {
|
|
4782
|
+
return value;
|
|
4783
|
+
}
|
|
4784
|
+
const literals = new Set(mask.split('').filter(char => !isPlaceholder(char)));
|
|
4785
|
+
const content = value.split('').filter(char => !literals.has(char));
|
|
4786
|
+
if (content.length === 0) {
|
|
4787
|
+
return value;
|
|
4788
|
+
}
|
|
4789
|
+
let contentIndex = 0;
|
|
4790
|
+
let result = '';
|
|
4791
|
+
for (const maskChar of mask) {
|
|
4792
|
+
if (contentIndex >= content.length) {
|
|
4793
|
+
break;
|
|
4794
|
+
}
|
|
4795
|
+
if (isPlaceholder(maskChar)) {
|
|
4796
|
+
result += content[contentIndex];
|
|
4797
|
+
contentIndex += 1;
|
|
4798
|
+
} else {
|
|
4799
|
+
result += maskChar;
|
|
4800
|
+
}
|
|
4801
|
+
}
|
|
4802
|
+
// Append any remaining content characters that did not fit the mask.
|
|
4803
|
+
if (contentIndex < content.length) {
|
|
4804
|
+
result += content.slice(contentIndex).join('');
|
|
4805
|
+
}
|
|
4806
|
+
return result;
|
|
4807
|
+
};
|
|
4808
|
+
|
|
4561
4809
|
/**
|
|
4562
4810
|
* Sets the first word to be capitalized so that it is sentence cased.
|
|
4563
4811
|
* @param words
|
|
@@ -4755,7 +5003,10 @@ const checkFieldValidity = (props, rootData) => {
|
|
|
4755
5003
|
if (isRegisterBackedEnum) {
|
|
4756
5004
|
return '';
|
|
4757
5005
|
}
|
|
4758
|
-
|
|
5006
|
+
if (!ajvErrors || ajvErrors === invalidSin) {
|
|
5007
|
+
return ajvErrors;
|
|
5008
|
+
}
|
|
5009
|
+
return getConfiguredFormatError(schema) || ajvErrors;
|
|
4759
5010
|
};
|
|
4760
5011
|
/**
|
|
4761
5012
|
* Check if the date is a valid date/time
|
|
@@ -4821,195 +5072,6 @@ const UTCToFullLocalTime = (fullTime, useUTC = false) => {
|
|
|
4821
5072
|
}).replace('p.m.', 'PM').replace('a.m.', 'AM');
|
|
4822
5073
|
};
|
|
4823
5074
|
|
|
4824
|
-
// Default formats keyed by type; the mask can be overridden per field via UI schema options.
|
|
4825
|
-
const DEFAULT_PATTERNS = {
|
|
4826
|
-
phone: {
|
|
4827
|
-
mask: '(###) ###-####',
|
|
4828
|
-
pattern: /^\(?\d{3}\)?[- ]?\d{3}[- ]?\d{4}$/,
|
|
4829
|
-
error: 'Must be a valid 10-digit phone number in format (000) 000-0000',
|
|
4830
|
-
allowedKeys: /[0-9]/
|
|
4831
|
-
},
|
|
4832
|
-
sin: {
|
|
4833
|
-
mask: '### ### ###',
|
|
4834
|
-
pattern: /^\d{3} \d{3} \d{3}$/,
|
|
4835
|
-
error: 'Must be three groups of three digits.',
|
|
4836
|
-
allowedKeys: /[0-9]/
|
|
4837
|
-
},
|
|
4838
|
-
postalCode: {
|
|
4839
|
-
mask: '### ###',
|
|
4840
|
-
pattern: /^[A-Za-z]\d[A-Za-z] \d[A-Za-z]\d$/,
|
|
4841
|
-
error: 'Must be in A0A 0A0 capital letters and numbers format',
|
|
4842
|
-
allowedKeys: /[A-Za-z0-9]/
|
|
4843
|
-
},
|
|
4844
|
-
driverId: {
|
|
4845
|
-
// Alberta driver's licence: six digits, a dash, then three digits.
|
|
4846
|
-
mask: '######-###',
|
|
4847
|
-
pattern: /^\d{6}-\d{3}$/,
|
|
4848
|
-
error: "Must be a valid driver's licence number in format 000000-000",
|
|
4849
|
-
allowedKeys: /[0-9]/
|
|
4850
|
-
},
|
|
4851
|
-
mvid: {
|
|
4852
|
-
// Motor vehicle ID: four digits, a dash, then five digits.
|
|
4853
|
-
mask: '####-#####',
|
|
4854
|
-
pattern: /^\d{4}-\d{5}$/,
|
|
4855
|
-
error: 'Must be a valid motor vehicle ID in format 0000-00000',
|
|
4856
|
-
allowedKeys: /[0-9]/
|
|
4857
|
-
}
|
|
4858
|
-
};
|
|
4859
|
-
// Mask placeholder characters; each consumes one input character. Any other mask character is treated as a literal.
|
|
4860
|
-
const MASK_PLACEHOLDERS = new Set(['#', '*', '_']);
|
|
4861
|
-
const isPlaceholder = char => MASK_PLACEHOLDERS.has(char);
|
|
4862
|
-
// Navigation/editing keys that keystroke filtering must always let through.
|
|
4863
|
-
const MASK_CONTROL_KEYS = new Set(['Backspace', 'Delete', 'Tab', 'Enter', 'Escape', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End']);
|
|
4864
|
-
// A single-character key should be blocked when the format restricts input and the key isn't allowed.
|
|
4865
|
-
const shouldBlockKey = (key, allowedKeys) => !!allowedKeys && key.length === 1 && !MASK_CONTROL_KEYS.has(key) && !allowedKeys.test(key);
|
|
4866
|
-
// Removes characters the format doesn't allow (e.g. letters from a digit-only field), for paste/programmatic input.
|
|
4867
|
-
const filterAllowedKeys = (value, allowedKeys) => allowedKeys ? [...(value != null ? value : '')].filter(char => allowedKeys.test(char)).join('') : value != null ? value : '';
|
|
4868
|
-
// A mask slot holds one content character (letter or digit); everything else in the value is a separator.
|
|
4869
|
-
const NON_CONTENT = /[^a-zA-Z0-9]/g;
|
|
4870
|
-
const IS_CONTENT = /[a-zA-Z0-9]/;
|
|
4871
|
-
// Number of fill slots in a mask (its placeholder characters).
|
|
4872
|
-
const maskDigitCount = mask => [...mask].filter(isPlaceholder).length;
|
|
4873
|
-
// Formats a value into a placeholder mask (e.g. '(###) ###-####'), filling only the content entered so far.
|
|
4874
|
-
// Placeholder characters consume one content character each; every other mask character is a literal.
|
|
4875
|
-
const formatWithPattern = (value, mask) => {
|
|
4876
|
-
const maxContent = maskDigitCount(mask);
|
|
4877
|
-
const content = (value != null ? value : '').replace(NON_CONTENT, '').slice(0, maxContent);
|
|
4878
|
-
let index = 0;
|
|
4879
|
-
let formatted = '';
|
|
4880
|
-
for (const maskChar of mask) {
|
|
4881
|
-
if (index >= content.length) {
|
|
4882
|
-
break;
|
|
4883
|
-
}
|
|
4884
|
-
formatted += isPlaceholder(maskChar) ? content[index++] : maskChar;
|
|
4885
|
-
}
|
|
4886
|
-
return formatted;
|
|
4887
|
-
};
|
|
4888
|
-
// Fills the mask with the value's content, leaving the placeholder character for positions not yet entered.
|
|
4889
|
-
const toMaskTemplate = (value, mask) => {
|
|
4890
|
-
const maxContent = maskDigitCount(mask);
|
|
4891
|
-
const content = (value != null ? value : '').replace(NON_CONTENT, '').slice(0, maxContent);
|
|
4892
|
-
let index = 0;
|
|
4893
|
-
let display = '';
|
|
4894
|
-
for (const maskChar of mask) {
|
|
4895
|
-
display += isPlaceholder(maskChar) ? index < content.length ? content[index++] : maskChar : maskChar;
|
|
4896
|
-
}
|
|
4897
|
-
return display;
|
|
4898
|
-
};
|
|
4899
|
-
// Caret position just after the Nth content character within a rendered mask, so in-place edits keep the cursor in place.
|
|
4900
|
-
const caretAfterDigits = (display, digitCount) => {
|
|
4901
|
-
if (digitCount <= 0) {
|
|
4902
|
-
return 0;
|
|
4903
|
-
}
|
|
4904
|
-
let seen = 0;
|
|
4905
|
-
for (let i = 0; i < display.length; i++) {
|
|
4906
|
-
if (IS_CONTENT.test(display[i])) {
|
|
4907
|
-
seen += 1;
|
|
4908
|
-
if (seen === digitCount) {
|
|
4909
|
-
return i + 1;
|
|
4910
|
-
}
|
|
4911
|
-
}
|
|
4912
|
-
}
|
|
4913
|
-
return display.length;
|
|
4914
|
-
};
|
|
4915
|
-
// Computes an in-place masked edit from the raw input and caret index: what to show, what to store, where to put the caret.
|
|
4916
|
-
const computeMaskEdit = (rawValue, caretIndex, mask) => {
|
|
4917
|
-
const digitsBeforeCaret = rawValue.slice(0, caretIndex).replace(NON_CONTENT, '').length;
|
|
4918
|
-
const display = toMaskTemplate(rawValue, mask);
|
|
4919
|
-
const stored = formatWithPattern(rawValue, mask);
|
|
4920
|
-
const caret = caretAfterDigits(display, Math.min(digitsBeforeCaret, maskDigitCount(mask)));
|
|
4921
|
-
return {
|
|
4922
|
-
display,
|
|
4923
|
-
stored,
|
|
4924
|
-
caret
|
|
4925
|
-
};
|
|
4926
|
-
};
|
|
4927
|
-
// True once the value has as many content characters as the mask has fill slots.
|
|
4928
|
-
const isMaskFilled = (value, mask) => value.replace(NON_CONTENT, '').length >= maskDigitCount(mask);
|
|
4929
|
-
// A human placeholder for a mask, e.g. '(###) ###-####' -> '(000) 000-0000'.
|
|
4930
|
-
const maskPlaceholder = mask => [...mask].map(char => isPlaceholder(char) ? '0' : char).join('');
|
|
4931
|
-
// GoA retargets composed events to the host, so prefer the inner native input from the composed path.
|
|
4932
|
-
const getMaskInputTarget = detail => {
|
|
4933
|
-
var _target$shadowRoot$qu, _target$shadowRoot;
|
|
4934
|
-
const event = detail.event;
|
|
4935
|
-
if (!event) {
|
|
4936
|
-
return undefined;
|
|
4937
|
-
}
|
|
4938
|
-
const path = typeof event.composedPath === 'function' ? event.composedPath() : [];
|
|
4939
|
-
const fromPath = path.find(node => node instanceof HTMLInputElement);
|
|
4940
|
-
if (fromPath) {
|
|
4941
|
-
return fromPath;
|
|
4942
|
-
}
|
|
4943
|
-
const target = event.target;
|
|
4944
|
-
if (target instanceof HTMLInputElement) {
|
|
4945
|
-
return target;
|
|
4946
|
-
}
|
|
4947
|
-
return (_target$shadowRoot$qu = target == null || (_target$shadowRoot = target.shadowRoot) == null ? void 0 : _target$shadowRoot.querySelector('input')) != null ? _target$shadowRoot$qu : undefined;
|
|
4948
|
-
};
|
|
4949
|
-
const applyMaskValue = (target, edit) => {
|
|
4950
|
-
target.value = edit.display;
|
|
4951
|
-
target.setSelectionRange == null || target.setSelectionRange(edit.caret, edit.caret);
|
|
4952
|
-
};
|
|
4953
|
-
// Applies an in-place edit to the inner input and re-applies after the web component paints.
|
|
4954
|
-
const applyInPlaceEdit = (target, edit) => {
|
|
4955
|
-
if (!target) {
|
|
4956
|
-
return;
|
|
4957
|
-
}
|
|
4958
|
-
applyMaskValue(target, edit);
|
|
4959
|
-
requestAnimationFrame(() => applyMaskValue(target, edit));
|
|
4960
|
-
};
|
|
4961
|
-
// When the template is already filled, extra content characters must be rejected rather than shifting the value.
|
|
4962
|
-
const overflowMaskEdit = (previousDisplay, rawValue, caretIndex, mask) => {
|
|
4963
|
-
if (!isMaskFilled(previousDisplay, mask)) {
|
|
4964
|
-
return undefined;
|
|
4965
|
-
}
|
|
4966
|
-
if ((rawValue != null ? rawValue : '').replace(NON_CONTENT, '').length <= maskDigitCount(mask)) {
|
|
4967
|
-
return undefined;
|
|
4968
|
-
}
|
|
4969
|
-
return {
|
|
4970
|
-
display: previousDisplay,
|
|
4971
|
-
stored: formatWithPattern(previousDisplay, mask),
|
|
4972
|
-
caret: Math.min(Math.max(caretIndex, 0), previousDisplay.length)
|
|
4973
|
-
};
|
|
4974
|
-
};
|
|
4975
|
-
/**
|
|
4976
|
-
* Applies a mask template to a value, e.g. mask "(###) ###-####" turns "7801234567" into "(780) 123-4567".
|
|
4977
|
-
* '#' consumes one content character from the input; every other character is a literal inserted into the output.
|
|
4978
|
-
* The mask's literal characters are stripped from the input first so an already formatted value re-formats cleanly.
|
|
4979
|
-
* Returns the original value unchanged when the mask is empty or no content characters remain.
|
|
4980
|
-
* @param value - The raw input value
|
|
4981
|
-
* @param mask - The mask template string
|
|
4982
|
-
* @returns The masked value
|
|
4983
|
-
*/
|
|
4984
|
-
const applyFormatPattern = (value, mask) => {
|
|
4985
|
-
if (!mask || !value) {
|
|
4986
|
-
return value;
|
|
4987
|
-
}
|
|
4988
|
-
const literals = new Set(mask.split('').filter(char => !isPlaceholder(char)));
|
|
4989
|
-
const content = value.split('').filter(char => !literals.has(char));
|
|
4990
|
-
if (content.length === 0) {
|
|
4991
|
-
return value;
|
|
4992
|
-
}
|
|
4993
|
-
let contentIndex = 0;
|
|
4994
|
-
let result = '';
|
|
4995
|
-
for (const maskChar of mask) {
|
|
4996
|
-
if (contentIndex >= content.length) {
|
|
4997
|
-
break;
|
|
4998
|
-
}
|
|
4999
|
-
if (isPlaceholder(maskChar)) {
|
|
5000
|
-
result += content[contentIndex];
|
|
5001
|
-
contentIndex += 1;
|
|
5002
|
-
} else {
|
|
5003
|
-
result += maskChar;
|
|
5004
|
-
}
|
|
5005
|
-
}
|
|
5006
|
-
// Append any remaining content characters that did not fit the mask.
|
|
5007
|
-
if (contentIndex < content.length) {
|
|
5008
|
-
result += content.slice(contentIndex).join('');
|
|
5009
|
-
}
|
|
5010
|
-
return result;
|
|
5011
|
-
};
|
|
5012
|
-
|
|
5013
5075
|
/**
|
|
5014
5076
|
* Checks input controls data value to determine is required and has any data.
|
|
5015
5077
|
* @param props - The JsonForm control props
|
|
@@ -7602,38 +7664,6 @@ function buildConditionalDeps(rootSchema) {
|
|
|
7602
7664
|
return out;
|
|
7603
7665
|
}
|
|
7604
7666
|
|
|
7605
|
-
const sinTitle = 'Social insurance number';
|
|
7606
|
-
const invalidSin = 'Social insurance number is invalid';
|
|
7607
|
-
const DEFAULT_MAX_ITEMS = 50;
|
|
7608
|
-
const REQUIRED_PROPERTY_ERROR = 'is a required property';
|
|
7609
|
-
const ADDRESS_LOOKUP_LABELS = {
|
|
7610
|
-
addressLine1: 'Address line 1',
|
|
7611
|
-
addressLine2: 'Address line 2',
|
|
7612
|
-
municipality: 'City',
|
|
7613
|
-
postalCode: 'Postal code',
|
|
7614
|
-
subdivisionCode: 'Province',
|
|
7615
|
-
country: 'Country'
|
|
7616
|
-
};
|
|
7617
|
-
const VALIDATION_KEYWORDS = {
|
|
7618
|
-
REQUIRED: 'required',
|
|
7619
|
-
MIN_LENGTH: 'minLength',
|
|
7620
|
-
MAX_LENGTH: 'maxLength',
|
|
7621
|
-
FORMAT: 'format',
|
|
7622
|
-
MINIMUM: 'minimum',
|
|
7623
|
-
MAXIMUM: 'maximum',
|
|
7624
|
-
TYPE: 'type',
|
|
7625
|
-
ERROR_MESSAGE: 'errorMessage'
|
|
7626
|
-
};
|
|
7627
|
-
var StepStatus;
|
|
7628
|
-
(function (StepStatus) {
|
|
7629
|
-
StepStatus["NOT_STARTED"] = "NotStarted";
|
|
7630
|
-
StepStatus["IN_PROGRESS"] = "InProgress";
|
|
7631
|
-
StepStatus["COMPLETED"] = "Completed";
|
|
7632
|
-
})(StepStatus || (StepStatus = {}));
|
|
7633
|
-
const getAddressLookupFieldLabel = fieldName => {
|
|
7634
|
-
return ADDRESS_LOOKUP_LABELS[fieldName] || fieldName;
|
|
7635
|
-
};
|
|
7636
|
-
|
|
7637
7667
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7638
7668
|
function hasMeaningfulValue(val) {
|
|
7639
7669
|
if (val === undefined || val === null) return false;
|
|
@@ -10036,13 +10066,6 @@ const resetInputValue = (detail, value) => {
|
|
|
10036
10066
|
target.value = value;
|
|
10037
10067
|
}
|
|
10038
10068
|
};
|
|
10039
|
-
// Resolve the mask format for a field: schema.format must match a DEFAULT_PATTERNS key.
|
|
10040
|
-
const resolveFormatConfig = schema => {
|
|
10041
|
-
if (schema.format && schema.format in DEFAULT_PATTERNS) {
|
|
10042
|
-
return DEFAULT_PATTERNS[schema.format];
|
|
10043
|
-
}
|
|
10044
|
-
return schema.title === sinTitle ? DEFAULT_PATTERNS.sin : undefined;
|
|
10045
|
-
};
|
|
10046
10069
|
const GoAInputText = props => {
|
|
10047
10070
|
return jsxs(JsonFormRegisterProvider, {
|
|
10048
10071
|
defaultRegisters: undefined,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abgov/jsonforms-components",
|
|
3
|
-
"version": "2.75.
|
|
3
|
+
"version": "2.75.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Government of Alberta - React renderers for JSON Forms based on the design system.",
|
|
6
6
|
"repository": "https://github.com/GovAlta/adsp-monorepo",
|
package/renderer-catalog.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-09-
|
|
4
|
-
"sourceCommit": "
|
|
3
|
+
"generatedAt": "2026-09-09T19:26:19.202Z",
|
|
4
|
+
"sourceCommit": "7fb9064678498d4c0d4a0cc3926968d141a9f242",
|
|
5
5
|
"sourcePath": "libs/jsonforms-components/src/index.ts",
|
|
6
6
|
"rendererCount": 33,
|
|
7
7
|
"renderers": [
|
|
@@ -5,6 +5,20 @@ export interface MaskPattern {
|
|
|
5
5
|
allowedKeys?: RegExp;
|
|
6
6
|
}
|
|
7
7
|
export declare const DEFAULT_PATTERNS: Record<string, MaskPattern>;
|
|
8
|
+
export declare const resolveFormatConfig: (schema?: {
|
|
9
|
+
format?: string;
|
|
10
|
+
title?: string;
|
|
11
|
+
}) => MaskPattern | undefined;
|
|
12
|
+
type SchemaErrorMessage = string | {
|
|
13
|
+
pattern?: string;
|
|
14
|
+
format?: string;
|
|
15
|
+
};
|
|
16
|
+
type FormatSchema = {
|
|
17
|
+
format?: string;
|
|
18
|
+
title?: string;
|
|
19
|
+
errorMessage?: SchemaErrorMessage;
|
|
20
|
+
};
|
|
21
|
+
export declare const getConfiguredFormatError: (schema?: FormatSchema) => string | undefined;
|
|
8
22
|
export declare const MASK_PLACEHOLDERS: Set<string>;
|
|
9
23
|
export declare const MASK_CONTROL_KEYS: Set<string>;
|
|
10
24
|
export declare const shouldBlockKey: (key: string, allowedKeys?: RegExp) => boolean;
|