@abgov/jsonforms-components 2.75.0 → 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 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
- return ajvErrors;
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,166 +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
- // Extracts the underlying input element from a GoA change/keypress detail, if available.
4932
- const getMaskInputTarget = detail => {
4933
- var _detail$event$target, _detail$event;
4934
- return (_detail$event$target = (_detail$event = detail.event) == null ? void 0 : _detail$event.target) != null ? _detail$event$target : undefined;
4935
- };
4936
- // Applies an in-place edit to the DOM: forces the (fixed-length) value and restores the caret after React commits.
4937
- const applyInPlaceEdit = (target, edit) => {
4938
- if (!target) {
4939
- return;
4940
- }
4941
- target.value = edit.display;
4942
- if (target.setSelectionRange) {
4943
- requestAnimationFrame(() => target.setSelectionRange == null ? void 0 : target.setSelectionRange(edit.caret, edit.caret));
4944
- }
4945
- };
4946
- /**
4947
- * Applies a mask template to a value, e.g. mask "(###) ###-####" turns "7801234567" into "(780) 123-4567".
4948
- * '#' consumes one content character from the input; every other character is a literal inserted into the output.
4949
- * The mask's literal characters are stripped from the input first so an already formatted value re-formats cleanly.
4950
- * Returns the original value unchanged when the mask is empty or no content characters remain.
4951
- * @param value - The raw input value
4952
- * @param mask - The mask template string
4953
- * @returns The masked value
4954
- */
4955
- const applyFormatPattern = (value, mask) => {
4956
- if (!mask || !value) {
4957
- return value;
4958
- }
4959
- const literals = new Set(mask.split('').filter(char => !isPlaceholder(char)));
4960
- const content = value.split('').filter(char => !literals.has(char));
4961
- if (content.length === 0) {
4962
- return value;
4963
- }
4964
- let contentIndex = 0;
4965
- let result = '';
4966
- for (const maskChar of mask) {
4967
- if (contentIndex >= content.length) {
4968
- break;
4969
- }
4970
- if (isPlaceholder(maskChar)) {
4971
- result += content[contentIndex];
4972
- contentIndex += 1;
4973
- } else {
4974
- result += maskChar;
4975
- }
4976
- }
4977
- // Append any remaining content characters that did not fit the mask.
4978
- if (contentIndex < content.length) {
4979
- result += content.slice(contentIndex).join('');
4980
- }
4981
- return result;
4982
- };
4983
-
4984
5075
  /**
4985
5076
  * Checks input controls data value to determine is required and has any data.
4986
5077
  * @param props - The JsonForm control props
@@ -7573,38 +7664,6 @@ function buildConditionalDeps(rootSchema) {
7573
7664
  return out;
7574
7665
  }
7575
7666
 
7576
- const sinTitle = 'Social insurance number';
7577
- const invalidSin = 'Social insurance number is invalid';
7578
- const DEFAULT_MAX_ITEMS = 50;
7579
- const REQUIRED_PROPERTY_ERROR = 'is a required property';
7580
- const ADDRESS_LOOKUP_LABELS = {
7581
- addressLine1: 'Address line 1',
7582
- addressLine2: 'Address line 2',
7583
- municipality: 'City',
7584
- postalCode: 'Postal code',
7585
- subdivisionCode: 'Province',
7586
- country: 'Country'
7587
- };
7588
- const VALIDATION_KEYWORDS = {
7589
- REQUIRED: 'required',
7590
- MIN_LENGTH: 'minLength',
7591
- MAX_LENGTH: 'maxLength',
7592
- FORMAT: 'format',
7593
- MINIMUM: 'minimum',
7594
- MAXIMUM: 'maximum',
7595
- TYPE: 'type',
7596
- ERROR_MESSAGE: 'errorMessage'
7597
- };
7598
- var StepStatus;
7599
- (function (StepStatus) {
7600
- StepStatus["NOT_STARTED"] = "NotStarted";
7601
- StepStatus["IN_PROGRESS"] = "InProgress";
7602
- StepStatus["COMPLETED"] = "Completed";
7603
- })(StepStatus || (StepStatus = {}));
7604
- const getAddressLookupFieldLabel = fieldName => {
7605
- return ADDRESS_LOOKUP_LABELS[fieldName] || fieldName;
7606
- };
7607
-
7608
7667
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
7609
7668
  function hasMeaningfulValue(val) {
7610
7669
  if (val === undefined || val === null) return false;
@@ -10007,13 +10066,6 @@ const resetInputValue = (detail, value) => {
10007
10066
  target.value = value;
10008
10067
  }
10009
10068
  };
10010
- // Resolve the mask format for a field: schema.format must match a DEFAULT_PATTERNS key.
10011
- const resolveFormatConfig = schema => {
10012
- if (schema.format && schema.format in DEFAULT_PATTERNS) {
10013
- return DEFAULT_PATTERNS[schema.format];
10014
- }
10015
- return schema.title === sinTitle ? DEFAULT_PATTERNS.sin : undefined;
10016
- };
10017
10069
  const GoAInputText = props => {
10018
10070
  return jsxs(JsonFormRegisterProvider, {
10019
10071
  defaultRegisters: undefined,
@@ -10171,6 +10223,11 @@ const InnerGoAInputText = props => {
10171
10223
  var _target$selectionStar;
10172
10224
  const target = getMaskInputTarget(detail);
10173
10225
  const caretIndex = (_target$selectionStar = target == null ? void 0 : target.selectionStart) != null ? _target$selectionStar : cleaned.length;
10226
+ const overflow = overflowMaskEdit(localValue, cleaned, caretIndex, mask);
10227
+ if (overflow) {
10228
+ applyInPlaceEdit(target, overflow);
10229
+ return;
10230
+ }
10174
10231
  const edit = computeMaskEdit(cleaned, caretIndex, mask);
10175
10232
  applyInPlaceEdit(target, edit);
10176
10233
  setLocalValue(edit.display);
@@ -10205,12 +10262,22 @@ const InnerGoAInputText = props => {
10205
10262
  });
10206
10263
  },
10207
10264
  onKeyPress: detail => {
10265
+ const keyPressDetail = detail;
10208
10266
  const blockDisallowed = shouldBlockKey(detail.key, allowedKeys);
10209
- // In-place has no length cap, so also block content keys once the template is full.
10267
+ // In-place has no maxLength, and GoA keyPress fires on keyup after the character is inserted.
10210
10268
  const blockOverflow = inPlace && !!mask && /^[A-Za-z0-9]$/.test(detail.key) && isMaskFilled(localValue, mask);
10211
10269
  if (blockDisallowed || blockOverflow) {
10212
- var _detail$event2;
10213
- (_detail$event2 = detail.event) == null || _detail$event2.preventDefault();
10270
+ var _keyPressDetail$event;
10271
+ (_keyPressDetail$event = keyPressDetail.event) == null || _keyPressDetail$event.preventDefault();
10272
+ }
10273
+ if (blockOverflow && mask) {
10274
+ var _ref, _target$value, _target$selectionStar2;
10275
+ const target = getMaskInputTarget(keyPressDetail);
10276
+ const rawValue = (_ref = (_target$value = target == null ? void 0 : target.value) != null ? _target$value : detail.value) != null ? _ref : localValue;
10277
+ const overflow = overflowMaskEdit(localValue, rawValue, (_target$selectionStar2 = target == null ? void 0 : target.selectionStart) != null ? _target$selectionStar2 : localValue.length, mask);
10278
+ if (overflow) {
10279
+ applyInPlaceEdit(target, overflow);
10280
+ }
10214
10281
  }
10215
10282
  }
10216
10283
  }, uischema == null || (_uischema$options10 = uischema.options) == null ? void 0 : _uischema$options10.componentProps))
@@ -13850,16 +13917,28 @@ const useMaskedInput = ({
13850
13917
  }
13851
13918
  const target = getMaskInputTarget(detail);
13852
13919
  const caretIndex = (_target$selectionStar = target == null ? void 0 : target.selectionStart) != null ? _target$selectionStar : rawValue.length;
13920
+ const overflow = overflowMaskEdit(value, rawValue, caretIndex, mask);
13921
+ if (overflow) {
13922
+ applyInPlaceEdit(target, overflow);
13923
+ return;
13924
+ }
13853
13925
  const edit = computeMaskEdit(rawValue, caretIndex, mask);
13854
13926
  applyInPlaceEdit(target, edit);
13855
13927
  setValue(edit.display);
13856
13928
  onCommit(edit.stored);
13857
13929
  };
13858
- // In-place mode has no native length cap, so block extra content characters at the source.
13930
+ // In-place has no maxLength (the template is already full length) and GoA keyPress is keyup, so restore on overflow.
13859
13931
  const handleKeyPress = detail => {
13860
- if (inPlace && /^[A-Za-z0-9]$/.test(detail.key) && isMaskFilled(value, mask)) {
13861
- var _detail$event;
13862
- (_detail$event = detail.event) == null || _detail$event.preventDefault();
13932
+ var _detail$event, _target$value, _target$selectionStar2;
13933
+ if (!inPlace || !/^[A-Za-z0-9]$/.test(detail.key) || !isMaskFilled(value, mask)) {
13934
+ return;
13935
+ }
13936
+ (_detail$event = detail.event) == null || _detail$event.preventDefault();
13937
+ const target = getMaskInputTarget(detail);
13938
+ const rawValue = (_target$value = target == null ? void 0 : target.value) != null ? _target$value : value;
13939
+ const overflow = overflowMaskEdit(value, rawValue, (_target$selectionStar2 = target == null ? void 0 : target.selectionStart) != null ? _target$selectionStar2 : value.length, mask);
13940
+ if (overflow) {
13941
+ applyInPlaceEdit(target, overflow);
13863
13942
  }
13864
13943
  };
13865
13944
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abgov/jsonforms-components",
3
- "version": "2.75.0",
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",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-09-08T14:57:25.870Z",
4
- "sourceCommit": "4bfd7a826d9a48271e8d7b7e77fbee5e560c102f",
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;
@@ -28,6 +42,7 @@ export declare const getMaskInputTarget: (detail: {
28
42
  event?: Event;
29
43
  }) => MaskInputTarget;
30
44
  export declare const applyInPlaceEdit: (target: MaskInputTarget, edit: MaskEdit) => void;
45
+ export declare const overflowMaskEdit: (previousDisplay: string, rawValue: string, caretIndex: number, mask: string) => MaskEdit | undefined;
31
46
  /**
32
47
  * Applies a mask template to a value, e.g. mask "(###) ###-####" turns "7801234567" into "(780) 123-4567".
33
48
  * '#' consumes one content character from the input; every other character is a literal inserted into the output.