@tylertech/forge 3.6.1 → 3.6.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.
@@ -147,3 +147,10 @@ export declare function task(duration?: number): Promise<void>;
147
147
  * Useful for delaying some code until the next animation frame is rendered by the browser.
148
148
  */
149
149
  export declare function frame(): Promise<void>;
150
+ /**
151
+ * Determines if an object is an instance of a specific type.
152
+ * @param obj The object to test.
153
+ * @param name The name of the type to test against.
154
+ * @returns `true` if the object is an instance of the type, otherwise `false`.
155
+ */
156
+ export declare function isInstanceOf<T>(obj: any, name: string): obj is T;
@@ -296,3 +296,12 @@ export function task(duration = 0) {
296
296
  export function frame() {
297
297
  return new Promise(resolve => requestAnimationFrame(() => resolve()));
298
298
  }
299
+ /**
300
+ * Determines if an object is an instance of a specific type.
301
+ * @param obj The object to test.
302
+ * @param name The name of the type to test against.
303
+ * @returns `true` if the object is an instance of the type, otherwise `false`.
304
+ */
305
+ export function isInstanceOf(obj, name) {
306
+ return Object.prototype.toString.call(obj) === `[object ${name}]`;
307
+ }
@@ -10,12 +10,11 @@ export interface IDialogAdapter extends IBaseAdapter<IDialogComponent> {
10
10
  readonly moveHandleElement: HTMLElement;
11
11
  readonly surfaceElement: HTMLElement;
12
12
  triggerElement: HTMLElement | null;
13
+ destroy(): void;
13
14
  show(): void;
14
15
  hide(): Promise<void>;
15
16
  addDialogFormSubmitListener(listener: EventListener): void;
16
17
  removeDialogFormSubmitListener(listener: EventListener): void;
17
- addDialogCancelListener(listener: EventListener): void;
18
- removeDialogCancelListener(listener: EventListener): void;
19
18
  addBackdropDismissListener(listener: EventListener): void;
20
19
  removeBackdropDismissListener(listener: EventListener): void;
21
20
  tryAutofocus(): void;
@@ -43,14 +42,13 @@ export declare class DialogAdapter extends BaseAdapter<IDialogComponent> impleme
43
42
  get moveHandleElement(): HTMLElement;
44
43
  get surfaceElement(): HTMLElement;
45
44
  constructor(component: IDialogComponent);
45
+ destroy(): void;
46
46
  show(): void;
47
47
  private _hideBackdrops;
48
48
  private _showBackdropMostRecent;
49
49
  hide(): Promise<void>;
50
50
  addDialogFormSubmitListener(listener: EventListener): void;
51
51
  removeDialogFormSubmitListener(listener: EventListener): void;
52
- addDialogCancelListener(listener: EventListener): void;
53
- removeDialogCancelListener(listener: EventListener): void;
54
52
  addBackdropDismissListener(listener: EventListener): void;
55
53
  removeBackdropDismissListener(listener: EventListener): void;
56
54
  tryAutofocus(): void;
@@ -66,4 +64,5 @@ export declare class DialogAdapter extends BaseAdapter<IDialogComponent> impleme
66
64
  setAccessibleLabel(label: string): void;
67
65
  setAccessibleDescription(description: string): void;
68
66
  private _createOrUpdateVisuallyHiddenElement;
67
+ private _forceClose;
69
68
  }
@@ -25,6 +25,9 @@ export class DialogAdapter extends BaseAdapter {
25
25
  window.customElements.upgrade(this._backdropElement);
26
26
  }
27
27
  }
28
+ destroy() {
29
+ this._forceClose();
30
+ }
28
31
  show() {
29
32
  /* c8 ignore next 3 */
30
33
  if (this._dialogElement.open) {
@@ -66,18 +69,13 @@ export class DialogAdapter extends BaseAdapter {
66
69
  .at(-1)?.[showBackdrop]();
67
70
  }
68
71
  async hide() {
69
- const close = () => {
70
- this._surfaceElement.classList.remove(BACKDROP_CONSTANTS.classes.EXITING);
71
- this._dialogElement.close();
72
- DialogComponent[dialogStack].delete(this._component);
73
- this._showBackdropMostRecent();
74
- };
75
72
  if (this._component.animationType === 'none') {
76
- return Promise.resolve(close());
73
+ this._forceClose();
74
+ return Promise.resolve();
77
75
  }
78
76
  this._backdropElement.fadeOut();
79
77
  await playKeyframeAnimation(this._surfaceElement, BACKDROP_CONSTANTS.classes.EXITING);
80
- close();
78
+ this._forceClose();
81
79
  }
82
80
  addDialogFormSubmitListener(listener) {
83
81
  this._dialogElement.addEventListener('submit', listener);
@@ -85,12 +83,6 @@ export class DialogAdapter extends BaseAdapter {
85
83
  removeDialogFormSubmitListener(listener) {
86
84
  this._dialogElement.removeEventListener('submit', listener);
87
85
  }
88
- addDialogCancelListener(listener) {
89
- this._dialogElement.addEventListener('cancel', listener);
90
- }
91
- removeDialogCancelListener(listener) {
92
- this._dialogElement.removeEventListener('cancel', listener);
93
- }
94
86
  addBackdropDismissListener(listener) {
95
87
  this._backdropElement.addEventListener('click', listener);
96
88
  }
@@ -191,4 +183,10 @@ export class DialogAdapter extends BaseAdapter {
191
183
  element.textContent = content;
192
184
  return element;
193
185
  }
186
+ _forceClose() {
187
+ this._surfaceElement.classList.remove(BACKDROP_CONSTANTS.classes.EXITING);
188
+ this._dialogElement.close();
189
+ DialogComponent[dialogStack].delete(this._component);
190
+ this._showBackdropMostRecent();
191
+ }
194
192
  }
@@ -58,6 +58,8 @@ export declare class DialogCore implements IDialogCore {
58
58
  showBackdrop(): void;
59
59
  private _show;
60
60
  private _hide;
61
+ private _release;
62
+ private _tryResetFullscreenValue;
61
63
  private _applyOpen;
62
64
  private _onEscapeDismiss;
63
65
  private _onBackdropDismiss;
@@ -49,9 +49,9 @@ export class DialogCore {
49
49
  if (this._moveController) {
50
50
  this._destroyMoveController();
51
51
  }
52
- if (this._open) {
53
- this._hide();
54
- }
52
+ this._release();
53
+ this._tryResetFullscreenValue();
54
+ this._adapter.destroy();
55
55
  }
56
56
  dispatchBeforeCloseEvent() {
57
57
  const evt = new CustomEvent(DIALOG_CONSTANTS.events.BEFORE_CLOSE, {
@@ -72,10 +72,7 @@ export class DialogCore {
72
72
  this._adapter.show();
73
73
  this._adapter.addDialogFormSubmitListener(this._dialogFormSubmitListener);
74
74
  DismissibleStack.instance.add(this._adapter.hostElement);
75
- if (this._mode === 'modal') {
76
- this._adapter.addDialogCancelListener(this._escapeDismissListener);
77
- }
78
- else if (this._mode === 'inline-modal') {
75
+ if (this._mode === 'modal' || this._mode === 'inline-modal') {
79
76
  this._adapter.addDocumentListener('keydown', this._escapeDismissListener);
80
77
  }
81
78
  if (!this._persistent) {
@@ -91,21 +88,26 @@ export class DialogCore {
91
88
  this._adapter.dispatchHostEvent(new CustomEvent(DIALOG_CONSTANTS.events.OPEN, { bubbles: true, composed: true }));
92
89
  }
93
90
  async _hide() {
91
+ this._release();
92
+ await this._adapter.hide();
93
+ // Reset the fullscreen value to the original value if it was changed internally by our media query listener
94
+ this._tryResetFullscreenValue();
95
+ if (this._moveController) {
96
+ this._destroyMoveController();
97
+ }
98
+ this._adapter.dispatchHostEvent(new CustomEvent(DIALOG_CONSTANTS.events.CLOSE, { bubbles: true, composed: true }));
99
+ }
100
+ _release() {
94
101
  this._adapter.removeDialogFormSubmitListener(this._dialogFormSubmitListener);
95
- this._adapter.removeDialogCancelListener(this._escapeDismissListener);
96
102
  this._adapter.removeDocumentListener('keydown', this._escapeDismissListener);
97
103
  this._adapter.removeBackdropDismissListener(this._backdropDismissListener);
98
104
  DismissibleStack.instance.remove(this._adapter.hostElement);
99
- await this._adapter.hide();
100
- // Reset the fullscreen value to the original value if it was changed internally by our media query listener
105
+ }
106
+ _tryResetFullscreenValue() {
101
107
  if (typeof this._originalFullscreenValue === 'boolean') {
102
108
  this.fullscreen = this._originalFullscreenValue;
103
109
  }
104
110
  this._originalFullscreenValue = undefined;
105
- if (this._moveController) {
106
- this._destroyMoveController();
107
- }
108
- this._adapter.dispatchHostEvent(new CustomEvent(DIALOG_CONSTANTS.events.CLOSE, { bubbles: true, composed: true }));
109
111
  }
110
112
  async _applyOpen() {
111
113
  if (this._open) {
@@ -119,15 +121,10 @@ export class DialogCore {
119
121
  this._adapter.toggleHostAttribute(DIALOG_CONSTANTS.attributes.OPEN, this._open);
120
122
  }
121
123
  _onEscapeDismiss(evt) {
122
- if (evt.type === 'keydown') {
123
- const key = evt.key;
124
- if (key !== 'Escape' || !DismissibleStack.instance.isMostRecent(this._adapter.hostElement)) {
125
- return;
126
- }
127
- }
128
- else if (evt.type === 'cancel') {
129
- evt.preventDefault();
124
+ if (evt.key !== 'Escape' || !DismissibleStack.instance.isMostRecent(this._adapter.hostElement)) {
125
+ return;
130
126
  }
127
+ evt.preventDefault();
131
128
  if (!this._persistent) {
132
129
  this._tryClose();
133
130
  }
@@ -13,7 +13,7 @@ import { SplitViewPanelCore } from './split-view-panel-core';
13
13
  import { SplitViewPanelAdapter } from './split-view-panel-adapter';
14
14
  import { IconComponent, IconRegistry } from '../../icon';
15
15
  const template = '<template><div class=\"forge-split-view-panel\" id=\"root\" part=\"root\"><div class=\"forge-split-view-panel__handle\" id=\"handle\" part=\"handle\" role=\"separator\" aria-controls=\"content\" aria-grabbed=\"false\" tabindex=\"0\"><forge-icon class=\"forge-split-view-panel__icon\" id=\"icon\" part=\"icon\"></forge-icon><forge-state-layer target=\"handle\" id=\"state-layer\" exportparts=\"surface:state-layer\"></forge-state-layer><forge-focus-indicator inward target=\"handle\" part=\"focus-indicator\"></forge-focus-indicator></div><div class=\"forge-split-view-panel__content\" id=\"content\" part=\"content\" role=\"group\"><slot></slot></div></div></template>';
16
- const styles = '.forge-split-view-panel{display:flex;width:100%;height:100%;overflow:hidden}.forge-split-view-panel__handle{color:var(--forge-theme-text-medium,rgba(0,0,0,.6));background-color:var(--forge-theme-outline,#e0e0e0);position:relative;display:flex;flex-shrink:0;justify-content:center;align-items:center;outline:0}.forge-split-view-panel__content{flex:1;overflow:hidden}.forge-split-view-panel--closed{display:none}.forge-split-view-panel--disabled #handle{pointer-events:none}.forge-split-view-panel--disabled .forge-split-view-panel__icon{display:none}.forge-split-view-panel[orientation=horizontal]{min-width:var(--forge-split-view-handle-width,8px);width:calc(var(--forge-split-view-panel-size,unset) + var(--forge-split-view-handle-width,8px));flex-direction:row}.forge-split-view-panel[orientation=horizontal] .forge-split-view-panel__handle{width:var(--forge-split-view-handle-width,8px);cursor:var(--forge-split-view-panel-cursor)}.forge-split-view-panel[orientation=horizontal].forge-split-view-panel--closing[resizable=end]{position:absolute;top:0;left:0;animation-name:uku04wx;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1))}@keyframes uku04wx{from{transform:none}to{transform:translateX(-100%)}}.forge-split-view-panel[orientation=horizontal].forge-split-view-panel--closing[resizable=start]{position:absolute;top:0;right:0;animation-name:uku04xl;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1))}@keyframes uku04xl{from{transform:none}to{transform:translateX(100%)}}.forge-split-view-panel[orientation=horizontal].forge-split-view-panel--opening[resizable=end]{position:absolute;top:0;left:0;animation-name:uku04yg;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1));animation-direction:reverse}@keyframes uku04yg{from{transform:none}to{transform:translateX(-100%)}}.forge-split-view-panel[orientation=horizontal].forge-split-view-panel--opening[resizable=start]{position:absolute;top:0;right:0;animation-name:uku04yp;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1));animation-direction:reverse}@keyframes uku04yp{from{transform:none}to{transform:translateX(100%)}}.forge-split-view-panel[orientation=vertical]{min-height:var(--forge-split-view-handle-width,8px);height:calc(var(--forge-split-view-panel-size,unset) + var(--forge-split-view-handle-width,8px));flex-direction:column}.forge-split-view-panel[orientation=vertical] .forge-split-view-panel__handle{height:var(--forge-split-view-handle-width,8px);cursor:var(--forge-split-view-panel-cursor)}.forge-split-view-panel[orientation=vertical].forge-split-view-panel--closing[resizable=end]{position:absolute;top:0;left:0;animation-name:uku04zn;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1))}@keyframes uku04zn{from{transform:none}to{transform:translateY(-100%)}}.forge-split-view-panel[orientation=vertical].forge-split-view-panel--closing[resizable=start]{position:absolute;bottom:0;left:0;animation-name:uku0502;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1))}@keyframes uku0502{from{transform:none}to{transform:translateY(100%)}}.forge-split-view-panel[orientation=vertical].forge-split-view-panel--opening[resizable=end]{position:absolute;top:0;left:0;animation-name:uku050h;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1));animation-direction:reverse}@keyframes uku050h{from{transform:none}to{transform:translateY(-100%)}}.forge-split-view-panel[orientation=vertical].forge-split-view-panel--opening[resizable=start]{position:absolute;bottom:0;left:0;animation-name:uku050u;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1));animation-direction:reverse}@keyframes uku050u{from{transform:none}to{transform:translateY(100%)}}:host{z-index:var(--forge-split-view-animating-layer)!important;display:block;position:relative;height:100%;width:100%;flex:0}:host([hidden]){display:none}:host(:not([resizable=start],[resizable=end])){flex:1}:host(:not([resizable=start],[resizable=end])) .forge-split-view-panel{width:100%;height:100%;min-width:0;min-height:0}:host(:not([resizable=start],[resizable=end])) .forge-split-view-panel__handle{display:none}forge-focus-indicator{--forge-focus-indicator-active-width:2px}';
16
+ const styles = '.forge-split-view-panel{display:flex;width:100%;height:100%;overflow:hidden}.forge-split-view-panel__handle{color:var(--forge-theme-text-medium,rgba(0,0,0,.6));background-color:var(--forge-theme-outline,#e0e0e0);position:relative;display:flex;flex-shrink:0;justify-content:center;align-items:center;outline:0}.forge-split-view-panel__content{flex:1;overflow:hidden}.forge-split-view-panel--closed{display:none}.forge-split-view-panel--disabled #handle{pointer-events:none}.forge-split-view-panel--disabled .forge-split-view-panel__icon{display:none}.forge-split-view-panel[orientation=horizontal]{min-width:var(--forge-split-view-handle-width,8px);width:calc(var(--forge-split-view-panel-size,unset) + var(--forge-split-view-handle-width,8px));flex-direction:row}.forge-split-view-panel[orientation=horizontal] .forge-split-view-panel__handle{width:var(--forge-split-view-handle-width,8px);cursor:var(--forge-split-view-panel-cursor)}.forge-split-view-panel[orientation=horizontal].forge-split-view-panel--closing[resizable=end]{position:absolute;top:0;left:0;animation-name:u9e6ka3;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1))}@keyframes u9e6ka3{from{transform:none}to{transform:translateX(-100%)}}.forge-split-view-panel[orientation=horizontal].forge-split-view-panel--closing[resizable=start]{position:absolute;top:0;right:0;animation-name:u9e6kah;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1))}@keyframes u9e6kah{from{transform:none}to{transform:translateX(100%)}}.forge-split-view-panel[orientation=horizontal].forge-split-view-panel--opening[resizable=end]{position:absolute;top:0;left:0;animation-name:u9e6kau;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1));animation-direction:reverse}@keyframes u9e6kau{from{transform:none}to{transform:translateX(-100%)}}.forge-split-view-panel[orientation=horizontal].forge-split-view-panel--opening[resizable=start]{position:absolute;top:0;right:0;animation-name:u9e6kbu;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1));animation-direction:reverse}@keyframes u9e6kbu{from{transform:none}to{transform:translateX(100%)}}.forge-split-view-panel[orientation=vertical]{min-height:var(--forge-split-view-handle-width,8px);height:calc(var(--forge-split-view-panel-size,unset) + var(--forge-split-view-handle-width,8px));flex-direction:column}.forge-split-view-panel[orientation=vertical] .forge-split-view-panel__handle{height:var(--forge-split-view-handle-width,8px);cursor:var(--forge-split-view-panel-cursor)}.forge-split-view-panel[orientation=vertical].forge-split-view-panel--closing[resizable=end]{position:absolute;top:0;left:0;animation-name:u9e6kcn;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1))}@keyframes u9e6kcn{from{transform:none}to{transform:translateY(-100%)}}.forge-split-view-panel[orientation=vertical].forge-split-view-panel--closing[resizable=start]{position:absolute;bottom:0;left:0;animation-name:u9e6kcs;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1))}@keyframes u9e6kcs{from{transform:none}to{transform:translateY(100%)}}.forge-split-view-panel[orientation=vertical].forge-split-view-panel--opening[resizable=end]{position:absolute;top:0;left:0;animation-name:u9e6kdd;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1));animation-direction:reverse}@keyframes u9e6kdd{from{transform:none}to{transform:translateY(-100%)}}.forge-split-view-panel[orientation=vertical].forge-split-view-panel--opening[resizable=start]{position:absolute;bottom:0;left:0;animation-name:u9e6kdt;animation-duration:var(--forge-animation-duration-medium2, 300ms);animation-timing-function:var(--forge-animation-easing-standard,cubic-bezier(0.2,0,0,1));animation-direction:reverse}@keyframes u9e6kdt{from{transform:none}to{transform:translateY(100%)}}:host{z-index:var(--forge-split-view-animating-layer)!important;display:block;position:relative;height:100%;width:100%;flex:0}:host([hidden]){display:none}:host(:not([resizable=start],[resizable=end])){flex:1}:host(:not([resizable=start],[resizable=end])) .forge-split-view-panel{width:100%;height:100%;min-width:0;min-height:0}:host(:not([resizable=start],[resizable=end])) .forge-split-view-panel__handle{display:none}forge-focus-indicator{--forge-focus-indicator-active-width:2px}';
17
17
  import { StateLayerComponent } from '../../state-layer';
18
18
  import { FocusIndicatorComponent } from '../../focus-indicator';
19
19
  /**
@@ -27,7 +27,11 @@ export declare class TextFieldCore extends BaseFieldCore<ITextFieldAdapter> impl
27
27
  private _handleSlotChange;
28
28
  private _onInputAttributeChange;
29
29
  private _onClearButtonClick;
30
+ /** Responds to the `input` event from the <input> element. */
31
+ private _onInputChange;
32
+ /** Called from the `value` property setter on the <input> element. */
30
33
  private _onValueChange;
34
+ private _syncValueChange;
31
35
  private _toggleClearButtonVisibility;
32
36
  get showClear(): boolean;
33
37
  set showClear(value: boolean);
@@ -3,6 +3,7 @@
3
3
  * Copyright Tyler Technologies, Inc.
4
4
  * License: Apache-2.0
5
5
  */
6
+ import { isInstanceOf } from '../core/utils/utils';
6
7
  import { FIELD_CONSTANTS } from '../field';
7
8
  import { BaseFieldCore } from '../field/base/base-field-core';
8
9
  import { TEXT_FIELD_CONSTANTS } from './text-field-constants';
@@ -14,7 +15,7 @@ export class TextFieldCore extends BaseFieldCore {
14
15
  this._slotChangeListener = this._onSlotChange.bind(this);
15
16
  this._inputAttributeListener = this._onInputAttributeChange.bind(this);
16
17
  this._valueChangeListener = this._onValueChange.bind(this);
17
- this._inputListener = this._onValueChange.bind(this);
18
+ this._inputListener = this._onInputChange.bind(this);
18
19
  this._clearButtonClickListener = (evt) => this._onClearButtonClick(evt);
19
20
  }
20
21
  initialize() {
@@ -71,13 +72,22 @@ export class TextFieldCore extends BaseFieldCore {
71
72
  this._adapter.clearInput();
72
73
  }
73
74
  }
74
- _onValueChange(evt) {
75
- let force;
76
- // Handle the special case where a number input allows invalid characters
77
- if (evt.target?.type === 'number' && (evt.data != null || evt.target.validity.badInput)) {
78
- force = true;
75
+ /** Responds to the `input` event from the <input> element. */
76
+ _onInputChange(evt) {
77
+ let floatLabel;
78
+ // Handle the special case where a number input allows invalid characters to be entered.
79
+ // In this case, we need to force the label to float.
80
+ if (isInstanceOf(evt, InputEvent.name) && evt.target.type === 'number' && (evt.data != null || evt.target.validity.badInput)) {
81
+ floatLabel = true;
79
82
  }
80
- this._tryFloatLabel(force);
83
+ this._syncValueChange({ floatLabel });
84
+ }
85
+ /** Called from the `value` property setter on the <input> element. */
86
+ _onValueChange() {
87
+ this._syncValueChange();
88
+ }
89
+ _syncValueChange({ floatLabel = undefined } = {}) {
90
+ this._tryFloatLabel(floatLabel);
81
91
  this._toggleClearButtonVisibility();
82
92
  }
83
93
  _toggleClearButtonVisibility() {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tylertech/forge",
3
3
  "description": "Tyler Forge™ Web Components library",
4
- "version": "3.6.1",
4
+ "version": "3.6.2",
5
5
  "author": "Tyler Technologies, Inc.",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {