@smallpearl/ngx-helper 0.29.23

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.
Files changed (117) hide show
  1. package/README.md +230 -0
  2. package/core/index.d.ts +2 -0
  3. package/core/src/ngx-helper.d.ts +7 -0
  4. package/core/src/version.d.ts +1 -0
  5. package/entity-field/index.d.ts +2 -0
  6. package/entity-field/src/entity-field-spec.d.ts +69 -0
  7. package/entity-field/src/provider.d.ts +27 -0
  8. package/fesm2022/smallpearl-ngx-helper-core.mjs +23 -0
  9. package/fesm2022/smallpearl-ngx-helper-core.mjs.map +1 -0
  10. package/fesm2022/smallpearl-ngx-helper-entity-field.mjs +112 -0
  11. package/fesm2022/smallpearl-ngx-helper-entity-field.mjs.map +1 -0
  12. package/fesm2022/smallpearl-ngx-helper-forms.mjs +112 -0
  13. package/fesm2022/smallpearl-ngx-helper-forms.mjs.map +1 -0
  14. package/fesm2022/smallpearl-ngx-helper-hover-dropdown.mjs +108 -0
  15. package/fesm2022/smallpearl-ngx-helper-hover-dropdown.mjs.map +1 -0
  16. package/fesm2022/smallpearl-ngx-helper-locale.mjs +296 -0
  17. package/fesm2022/smallpearl-ngx-helper-locale.mjs.map +1 -0
  18. package/fesm2022/smallpearl-ngx-helper-mat-busy-wheel.mjs +504 -0
  19. package/fesm2022/smallpearl-ngx-helper-mat-busy-wheel.mjs.map +1 -0
  20. package/fesm2022/smallpearl-ngx-helper-mat-context-menu.mjs +184 -0
  21. package/fesm2022/smallpearl-ngx-helper-mat-context-menu.mjs.map +1 -0
  22. package/fesm2022/smallpearl-ngx-helper-mat-entity-crud.mjs +1486 -0
  23. package/fesm2022/smallpearl-ngx-helper-mat-entity-crud.mjs.map +1 -0
  24. package/fesm2022/smallpearl-ngx-helper-mat-entity-list.mjs +800 -0
  25. package/fesm2022/smallpearl-ngx-helper-mat-entity-list.mjs.map +1 -0
  26. package/fesm2022/smallpearl-ngx-helper-mat-file-input.mjs +328 -0
  27. package/fesm2022/smallpearl-ngx-helper-mat-file-input.mjs.map +1 -0
  28. package/fesm2022/smallpearl-ngx-helper-mat-form-error.mjs +468 -0
  29. package/fesm2022/smallpearl-ngx-helper-mat-form-error.mjs.map +1 -0
  30. package/fesm2022/smallpearl-ngx-helper-mat-select-entity.mjs +854 -0
  31. package/fesm2022/smallpearl-ngx-helper-mat-select-entity.mjs.map +1 -0
  32. package/fesm2022/smallpearl-ngx-helper-mat-side-menu-layout.mjs +930 -0
  33. package/fesm2022/smallpearl-ngx-helper-mat-side-menu-layout.mjs.map +1 -0
  34. package/fesm2022/smallpearl-ngx-helper-mat-tel-input.mjs +926 -0
  35. package/fesm2022/smallpearl-ngx-helper-mat-tel-input.mjs.map +1 -0
  36. package/fesm2022/smallpearl-ngx-helper-sideload.mjs +111 -0
  37. package/fesm2022/smallpearl-ngx-helper-sideload.mjs.map +1 -0
  38. package/fesm2022/smallpearl-ngx-helper-stationary-with-line-items.mjs +384 -0
  39. package/fesm2022/smallpearl-ngx-helper-stationary-with-line-items.mjs.map +1 -0
  40. package/fesm2022/smallpearl-ngx-helper.mjs +13 -0
  41. package/fesm2022/smallpearl-ngx-helper.mjs.map +1 -0
  42. package/forms/index.d.ts +1 -0
  43. package/forms/src/validation-error-handler.d.ts +52 -0
  44. package/hover-dropdown/index.d.ts +1 -0
  45. package/hover-dropdown/src/hover-dropdown.directive.d.ts +41 -0
  46. package/index.d.ts +5 -0
  47. package/locale/index.d.ts +5 -0
  48. package/locale/src/currency.pipe.d.ts +14 -0
  49. package/locale/src/date.pipe.d.ts +14 -0
  50. package/locale/src/format-currency.d.ts +1 -0
  51. package/locale/src/format-date.d.ts +2 -0
  52. package/locale/src/is-empty.d.ts +1 -0
  53. package/locale/src/providers.d.ts +20 -0
  54. package/mat-busy-wheel/index.d.ts +4 -0
  55. package/mat-busy-wheel/src/busy-wheel-op.d.ts +65 -0
  56. package/mat-busy-wheel/src/busy-wheel.component.d.ts +12 -0
  57. package/mat-busy-wheel/src/busy-wheel.service.d.ts +42 -0
  58. package/mat-busy-wheel/src/host-busy-wheel.directive.d.ts +35 -0
  59. package/mat-context-menu/index.d.ts +1 -0
  60. package/mat-context-menu/src/mat-context-menu.component.d.ts +54 -0
  61. package/mat-entity-crud/index.d.ts +5 -0
  62. package/mat-entity-crud/src/default-config.d.ts +9 -0
  63. package/mat-entity-crud/src/form-view-host.component.d.ts +34 -0
  64. package/mat-entity-crud/src/mat-entity-crud-form-base.d.ts +95 -0
  65. package/mat-entity-crud/src/mat-entity-crud-internal-types.d.ts +66 -0
  66. package/mat-entity-crud/src/mat-entity-crud-types.d.ts +141 -0
  67. package/mat-entity-crud/src/mat-entity-crud.component.d.ts +267 -0
  68. package/mat-entity-crud/src/preview-host.component.d.ts +19 -0
  69. package/mat-entity-crud/src/preview-pane.component.d.ts +27 -0
  70. package/mat-entity-crud/src/providers.d.ts +3 -0
  71. package/mat-entity-list/index.d.ts +3 -0
  72. package/mat-entity-list/src/config.d.ts +6 -0
  73. package/mat-entity-list/src/mat-entity-list-types.d.ts +53 -0
  74. package/mat-entity-list/src/mat-entity-list.component.d.ts +209 -0
  75. package/mat-entity-list/src/providers.d.ts +3 -0
  76. package/mat-file-input/README.md +63 -0
  77. package/mat-file-input/index.d.ts +1 -0
  78. package/mat-file-input/src/mat-file-input.component.d.ts +58 -0
  79. package/mat-form-error/README.md +306 -0
  80. package/mat-form-error/index.d.ts +6 -0
  81. package/mat-form-error/src/locales/en.d.ts +4 -0
  82. package/mat-form-error/src/locales/hu.d.ts +4 -0
  83. package/mat-form-error/src/locales/index.d.ts +3 -0
  84. package/mat-form-error/src/locales/pt-br.d.ts +4 -0
  85. package/mat-form-error/src/ngx-error-list.component.d.ts +9 -0
  86. package/mat-form-error/src/ngx-mat-error-control.d.ts +17 -0
  87. package/mat-form-error/src/ngx-mat-error-def.directive.d.ts +30 -0
  88. package/mat-form-error/src/ngx-mat-errors-for-date-range-picker.directive.d.ts +8 -0
  89. package/mat-form-error/src/ngx-mat-errors.component.d.ts +23 -0
  90. package/mat-form-error/src/types.d.ts +68 -0
  91. package/mat-form-error/src/utils/coerce-to-observable.d.ts +3 -0
  92. package/mat-form-error/src/utils/distinct-until-error-changed.d.ts +2 -0
  93. package/mat-form-error/src/utils/find-error-for-control.d.ts +9 -0
  94. package/mat-form-error/src/utils/get-abstract-controls.d.ts +3 -0
  95. package/mat-form-error/src/utils/get-control-with-error.d.ts +3 -0
  96. package/mat-select-entity/index.d.ts +2 -0
  97. package/mat-select-entity/src/mat-select-entity.component.d.ts +207 -0
  98. package/mat-select-entity/src/providers.d.ts +9 -0
  99. package/mat-side-menu-layout/index.d.ts +6 -0
  100. package/mat-side-menu-layout/src/layout.service.d.ts +23 -0
  101. package/mat-side-menu-layout/src/mat-menu-layout.component.d.ts +39 -0
  102. package/mat-side-menu-layout/src/mat-menu-layout.module.d.ts +18 -0
  103. package/mat-side-menu-layout/src/mat-menu-list-item.component.d.ts +36 -0
  104. package/mat-side-menu-layout/src/mat-menu-pane.component.d.ts +66 -0
  105. package/mat-side-menu-layout/src/nav-item.d.ts +10 -0
  106. package/mat-tel-input/README.md +18 -0
  107. package/mat-tel-input/index.d.ts +2 -0
  108. package/mat-tel-input/src/country-codes.d.ts +5 -0
  109. package/mat-tel-input/src/mat-telephone.component.d.ts +129 -0
  110. package/mat-tel-input/src/providers.d.ts +38 -0
  111. package/ngx-helper.d.ts +2 -0
  112. package/package.json +114 -0
  113. package/public-api.d.ts +1 -0
  114. package/sideload/index.d.ts +1 -0
  115. package/sideload/src/sideload.d.ts +17 -0
  116. package/stationary-with-line-items/index.d.ts +1 -0
  117. package/stationary-with-line-items/src/stationary-with-line-items.component.d.ts +74 -0
package/README.md ADDED
@@ -0,0 +1,230 @@
1
+ # ngx-helper
2
+
3
+ A library of UI components that covers requirements not met by standard
4
+ components in `@angular/material` library.
5
+
6
+ # Components
7
+
8
+ ## qq-mat-file-input
9
+ A File Input Control that shows a thumbnail of the selected file. This can be
10
+ used wherever an <input matInput type='file'> is required.
11
+
12
+ ## qq-mat-tel-input
13
+ A lightweight telephone numner input control that allows country selection by
14
+ typing the country name or country code.
15
+
16
+ Dependencies:
17
+ * google-libphonenumber - ^3.2.34
18
+ * ngx-mat-select-search - ^7.0.5
19
+
20
+
21
+ ## qq-mat-menu-list-item
22
+ A primitive component for the `qq-mat-menu-pane` component that allows building
23
+ a side menu based UI. This component supports hierarchical relationship with
24
+ itself, in which case the the parent menu item componet will display a chevron
25
+ to indicate child menu items.
26
+
27
+ Menu item can specified as the `item` attribute value of type `NavItem`.
28
+
29
+ ### Inputs
30
+
31
+ | Name | Description |
32
+ |------|-------------|
33
+ | item | `NavItem` object representing the menu item. |
34
+
35
+ ### Color customization
36
+ Since application's UI (where this component will be typically used) would
37
+ require color customization, this component uses the following CSS variables
38
+ for its color.
39
+
40
+
41
+ | Variable | Description |
42
+ |----------|-------------|
43
+ | --qq-menu-item-bg-color | Menu item background color |
44
+ | --qq-menu-item-fg-color | Menu item foreground color |
45
+ | --qq-highlighted-menu-item-bg-color | Highlighted menu item background color |
46
+ | --qq-highlighted-menu-item-fg-color | Highlighted menu item foreground color |
47
+
48
+ ## qq-mat-menu-pane
49
+ A parent container component that functions as the menu pane for the
50
+ `mat-sidenav` based user interface. The sidenav user interface (or layout),
51
+ consisting of a sidenav and a top toolbar, is wrapped as a component in
52
+ `sp-mat-menu-layout` described below.
53
+
54
+ This component takes an array of `NavItem` objects and renders it as the menu
55
+ in a container `div`. Typically this `div` will be hosted in a `mat-sidenav` as
56
+ part of an applications primary UI.
57
+
58
+ ### Inputs
59
+
60
+ | Name | Description |
61
+ |------|-------------|
62
+ | brandingImage | Branding logo (32x32) that will be displayed on the top of the menu pane. |
63
+ | brandingText | Branding text that is displayed next to the logo on the top of the menu pane. |
64
+ | menuItems | Menu items which is an array of `NavItem` objects. |
65
+ | menuPaneFooterContent | `TemplateRef<>` content of which will be used as the footer at the bottom of the pane. |
66
+ | title | Title text displayed above the the menu items. |
67
+ | backButtonHref | If specified, the `href` for the back button displayed on the top. Will not be displayed if not set. |
68
+ | matSideNav | `MatSideNav` instance that will host the menu pane. |
69
+
70
+ ### Example
71
+
72
+ See `mat-side-menu-layout` implementation for how this component is used.
73
+
74
+ ## mat-side-menu-layout
75
+ A component that helps implement a side menu user interface with a top toolbar.
76
+ The layout is responsive and hides the side menu when run on small screens.
77
+ Toolbar displays the app title and on the right can accommodate buttons or any
78
+ other content (via content projection).
79
+
80
+ To use this component, initialize it with the branding logo & text, application
81
+ title and menu items (`NavItem[]`). This is the sample code from the example
82
+ in this repo: -
83
+ ```
84
+ <sp-mat-menu-layout
85
+ brandingImage="assets/angular.png"
86
+ brandingText="SMALLPEARL"
87
+ appTitle="QQBOOKS"
88
+ [menuItems]="menuItems"
89
+ [menuPaneFooterContent]="versionInfoFooter"
90
+ [toolbarTitleContent]="toolbarTitle"
91
+ ></sp-mat-menu-layout>
92
+ <ng-template #versionInfoFooter>
93
+ <div style="text-align: center; font-size: 0.8em;">
94
+ <select name="language" id="language">
95
+ <option value="en">English</option>
96
+ <option value="zh">中文</option>
97
+ </select>
98
+ <br />
99
+ <small>2.3.102</small>
100
+ </div>
101
+ </ng-template>
102
+ <ng-template #toolbarTitle>
103
+ <button mat-button [matMenuTriggerFor]="otherCommunitiesMenu">
104
+ <h4 class="community-name">Signature Park</h4>
105
+ <mat-icon iconPositionEnd>arrow_drop_down</mat-icon>
106
+ </button>
107
+ <mat-menu #otherCommunitiesMenu="matMenu">
108
+ <button mat-menu-item>Cavenagh Garden</button>
109
+ <button mat-menu-item>Cashew Heights</button>
110
+ </mat-menu>
111
+ </ng-template>
112
+ <ng-template #toolbarEndContent>
113
+ <button mat-icon-button (click)="onNotificationsToggle()">
114
+ <mat-icon>notifications</mat-icon>
115
+ </button>
116
+ <button
117
+ mat-icon-button
118
+ class="user-button"
119
+ [matMenuTriggerFor]="userProfileMenu"
120
+ >
121
+ <img src="assets/avatar.jpg" alt="" />
122
+ </button>
123
+ <mat-menu #userProfileMenu="matMenu">
124
+ <button mat-menu-item (click)="onUpdateProfile()">
125
+ <mat-icon>face</mat-icon>
126
+ <span>Profile</span>
127
+ </button>
128
+ <button mat-menu-item (click)="onChangePassword()">
129
+ <mat-icon>password</mat-icon>
130
+ <span>Change password</span>
131
+ </button>
132
+ <button mat-menu-item (click)="onSignOut()">
133
+ <mat-icon>logout</mat-icon>
134
+ <span>Sign out</span>
135
+ </button>
136
+ </mat-menu>
137
+ </ng-template>
138
+ ```
139
+
140
+ ### Inputs
141
+ | Name | Type | Description |
142
+ |------|------|-------|
143
+ | showBackButton | boolean | A boolean property that controls if a Back button should be displayed at the top of the side menu. This is useful for secondary menus (note that this is NOT a submenu) from where a navigation path to the parent menu ought to be provided. |
144
+ | defaultBackButtonHref | string | The href for the back button is typically automatically determined. If there's no back history, the back button (if specified) will use this href. |
145
+ | brandingImage | string | Branding logo (32x32) that will be displayed on the top of the menu pane. |
146
+ | brandingText | string | Branding text that is displayed next to the logo on the top of the menu pane. |
147
+ | menuItems | NavItem[] | Menu items which is an array of `NavItem` objects. |
148
+ | appTitle | string | Application title. |
149
+ | menuPaneFooterContent | `TemplateRef<any>` | NgTemplate for the footer displayed below side menu. This will be passed to the `qq-mat-menu-pane`. |
150
+ | toolbarEndContent | `TemplateRef<any>` | NgTemplate for the content displayed at the end of the top toolbar. |
151
+ | infoPaneContent | `TemplateRef<any>` | NgTemplate for the content displayed in the information pane at the end of the page. |
152
+ | toolbarTitleContent | `TemplateRef<any>` | NgTemplate for the toolbar title. If specified, `appTitle` will be ignored. |
153
+ | infoPaneMinWidth | number | Minimum width of the info pane at the end of the page (right on LTR text). |
154
+ | infoPaneMaxWidth | number | Maximum width of the info pane at the end of the page (right on LTR text). |
155
+ | contentContainerClass | string | If specified, the CSS class that is applied to the content container div. This allows global styling of the container class (possibly padding, margin, color, etc) that will be applied to all the compoents corresponding to each menu item's link. |
156
+ | menuTitle | string | A title for the menu pane. Typically used in secondary side-menu layout pages. Defaults to an empty string. |
157
+
158
+ ### CSS
159
+
160
+ | Variable | Description |
161
+ |----------|-------------|
162
+ | --qq-sidenav-bg-color | Sidenav menu pane background color |
163
+ | --qq-sidenav-fg-color | Sidenav menu pane foreground color |
164
+ | --qq-toolbar-bg-color | Toolbar background color |
165
+ | --qq-toolbar-fg-color | Toolbar foreground color |
166
+ | --qq-sidenav-border-color | Sidemenu border color |
167
+ | --qq-toolbar-border-color | Toolbar border color |
168
+ | --qq-menu-item-fg-color | Menu item foreground color |
169
+ | --qq-menu-item-bg-color | Menu item background color |
170
+ | --qq-highlighted-menu-item-fg-color | Highlighted menu item foreground color |
171
+ | --qq-highlighted-menu-item-bg-color | Highlighted menu item background color |
172
+
173
+ ### Info pane
174
+
175
+ The component supports an information pane on the end of the pag. But it's
176
+ hidden by default and has to be explicitly shown. One way to do that would be
177
+ to query the `infoPane` member of `SPMatMenuLayoutComponent`
178
+ which is an instance of the `MatSideNav` component. You can toggle its
179
+ visibility by calling its `toggle()` method. An approach could be to show a
180
+ button at the end of the toolbar and then call `infoPane.toggle()` when the
181
+ button is selected.
182
+
183
+ ```
184
+ export class AppHomeComponent implements OnInit {
185
+ @ViewChild(SPMatMenuLayoutComponent)
186
+ sideMenuLayout: SPMatMenuLayoutComponent;
187
+
188
+ ngOnInit(): void {}
189
+
190
+ ...
191
+ onNotificationsToggle() {
192
+ if (this.sideMenuLayout) {
193
+ this.sideMenuLayout.infoPane.toggle();
194
+ }
195
+ }
196
+ ...
197
+ }
198
+ ```
199
+
200
+ ## NavItem
201
+
202
+ This is the interface that is used to define the side menu items and pass it
203
+ as the value for the `menuItems` property. It is defined as:-
204
+
205
+ ```
206
+ export interface NavItem {
207
+ text: string;
208
+ icon: string;
209
+ iconType?: 'mat' | 'bi' | 'fa';
210
+ disabled?: boolean;
211
+ route?: string;
212
+ children?: NavItem[];
213
+ backButton?: boolean;
214
+ backHref?: string;
215
+ }
216
+ ```
217
+ Members are described in the following table:
218
+
219
+ | Member | Description |
220
+ |-------|----|
221
+ | text | The menu item text |
222
+ | icon | Icon displayed along with the text |
223
+ | iconType | One of {'mat'|'bi'|'fa'}. If not specified, defaults to 'mat' |
224
+ | disabled | Indicates that the menu item is disabled |
225
+ | route | The relative URL to navigate when the item is selected |
226
+ | children | If this item is a container for child menu items, set this to an array of `NavItem` objects |
227
+ | backButton | Used internally |
228
+ | backHref | Used internally |
229
+
230
+
@@ -0,0 +1,2 @@
1
+ export * from './src/version';
2
+ export * from './src/ngx-helper';
@@ -0,0 +1,7 @@
1
+ /** Version of the ngx-helper library */
2
+ import { InjectionToken } from '@angular/core';
3
+ export interface SPNgxHelperConfig {
4
+ i18nTranslate: (label: string, context?: any) => string;
5
+ }
6
+ export declare const SP_NGX_HELPER_CONFIG: InjectionToken<SPNgxHelperConfig>;
7
+ export declare function getNgxHelperConfig(): SPNgxHelperConfig;
@@ -0,0 +1 @@
1
+ export declare const NGX_HELPER_VERSION = "0.1.0";
@@ -0,0 +1,2 @@
1
+ export * from './src/entity-field-spec';
2
+ export * from './src/provider';
@@ -0,0 +1,69 @@
1
+ import { SPNgxHelperConfig } from '@smallpearl/ngx-helper/core';
2
+ import { SPIntlDateFormat } from '@smallpearl/ngx-helper/locale';
3
+ import { SPEntityFieldConfig } from './provider';
4
+ /**
5
+ * This structure defines the data formatting details for a field of the
6
+ * entity. All entity fields need not necessarily be actual entity object's
7
+ * properties. Fields can also be computed fields, in which case the valueFn
8
+ * should be initialized with a valid function to provide the field's value.
9
+ */
10
+ export type SPEntityFieldSpec<TEntity extends {
11
+ [P in IdKey]: PropertyKey;
12
+ }, IdKey extends string = 'id'> = {
13
+ name: string;
14
+ label?: string;
15
+ valueOptions?: {
16
+ dateTimeFormat?: SPIntlDateFormat;
17
+ isCurrency?: boolean;
18
+ currency?: string;
19
+ class?: string;
20
+ alignment?: 'start' | 'center' | 'end';
21
+ routerLink?: ((e: TEntity) => string[]) | [string];
22
+ };
23
+ valueFn?: (item: TEntity) => string | number | Date | boolean;
24
+ };
25
+ /**
26
+ * A class that represents a SPEntityFieldSpec<>. This is typically used
27
+ * by the library to evaluate a SPEntityFieldSpec<> object.
28
+ */
29
+ export declare class SPEntityField<TEntity extends {
30
+ [P in IdKey]: PropertyKey;
31
+ }, IdKey extends string = 'id'> {
32
+ helperConfig: SPNgxHelperConfig;
33
+ fieldConfig?: SPEntityFieldConfig | undefined;
34
+ _fieldSpec: SPEntityFieldSpec<TEntity, IdKey>;
35
+ constructor(spec: SPEntityFieldSpec<TEntity, IdKey> | string, helperConfig: SPNgxHelperConfig, fieldConfig?: SPEntityFieldConfig | undefined);
36
+ get spec(): SPEntityFieldSpec<TEntity, IdKey>;
37
+ /**
38
+ * Returns the effective fieldValueOptions by merging the global field
39
+ * options (if one has been spefified) with the local field value options.
40
+ * @returns SPEntityFieldSpec<any>['valueOptions']
41
+ */
42
+ get options(): {
43
+ dateTimeFormat?: SPIntlDateFormat;
44
+ isCurrency?: boolean;
45
+ currency?: string;
46
+ class?: string;
47
+ alignment?: "start" | "center" | "end";
48
+ routerLink?: [string] | ((e: TEntity) => string[]) | undefined;
49
+ };
50
+ /**
51
+ * @returns the label for the field.
52
+ */
53
+ label(): string;
54
+ /**
55
+ * Given an entity, returns the value of the field matching the
56
+ * SPEntityFieldSpec<> in fieldSpec.
57
+ * @param entity TEntity instance which will be evaluated for
58
+ * SPEntityFieldSpec<>.
59
+ * @returns
60
+ */
61
+ value(entity: TEntity): any;
62
+ /**
63
+ * If specified, will be added to the CSS classes of the field's wrapper
64
+ * element.
65
+ */
66
+ get class(): string;
67
+ hasRouterLink(entity: TEntity): boolean;
68
+ getRouterLink(entity: TEntity): string[];
69
+ }
@@ -0,0 +1,27 @@
1
+ import { InjectionToken } from '@angular/core';
2
+ import { SPEntityFieldSpec } from './entity-field-spec';
3
+ export type FIELD_VALUE_FN = (entity: any, fieldName: string) => string | number | Date | boolean;
4
+ /**
5
+ * Global config for SPEntityField component.
6
+ */
7
+ export interface SPEntityFieldConfig {
8
+ /**
9
+ * These are global field value functions.
10
+ *
11
+ * If a value function for a field is not explicitly specified, this map is
12
+ * looked up with the field name. If an entry exists in this table, it will
13
+ * be used to render the field's value.
14
+ *
15
+ * This is useful for formatting certain fields which tend to have the
16
+ * same name across the app. For instance fields such as 'amount', 'total'
17
+ * or 'balance'. Or 'date', 'timestamp', etc.
18
+ */
19
+ fieldValueFns?: Map<string, FIELD_VALUE_FN>;
20
+ /**
21
+ * Similar to above, but allows setting the options for certain fields
22
+ * globally. As in the case of `fieldValueFns`, the per field specification,
23
+ * if one exists, takes precedence over the global setting.
24
+ */
25
+ fieldValueOptions?: Map<string, SPEntityFieldSpec<any>['valueOptions']>;
26
+ }
27
+ export declare const SP_ENTITY_FIELD_CONFIG: InjectionToken<SPEntityFieldConfig>;
@@ -0,0 +1,23 @@
1
+ import { InjectionToken, inject } from '@angular/core';
2
+
3
+ const NGX_HELPER_VERSION = '0.1.0';
4
+
5
+ /** Version of the ngx-helper library */
6
+ const SP_NGX_HELPER_CONFIG = new InjectionToken('SPNgxHelperConfig');
7
+ function getNgxHelperConfig() {
8
+ const defaultNgxHelperConfig = {
9
+ i18nTranslate: (label, context) => label
10
+ };
11
+ const helperConfig = inject(SP_NGX_HELPER_CONFIG, { optional: true });
12
+ return {
13
+ ...defaultNgxHelperConfig,
14
+ ...(helperConfig ?? {}),
15
+ };
16
+ }
17
+
18
+ /**
19
+ * Generated bundle index. Do not edit.
20
+ */
21
+
22
+ export { NGX_HELPER_VERSION, SP_NGX_HELPER_CONFIG, getNgxHelperConfig };
23
+ //# sourceMappingURL=smallpearl-ngx-helper-core.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smallpearl-ngx-helper-core.mjs","sources":["../../../../projects/smallpearl/ngx-helper/core/src/version.ts","../../../../projects/smallpearl/ngx-helper/core/src/ngx-helper.ts","../../../../projects/smallpearl/ngx-helper/core/smallpearl-ngx-helper-core.ts"],"sourcesContent":["export const NGX_HELPER_VERSION = '0.1.0';\n","/** Version of the ngx-helper library */\nimport { inject, InjectionToken } from '@angular/core';\n\nexport interface SPNgxHelperConfig {\n i18nTranslate: (label: string, context?: any) => string;\n}\n\nexport const SP_NGX_HELPER_CONFIG = new InjectionToken<SPNgxHelperConfig>(\n 'SPNgxHelperConfig'\n);\n\nexport function getNgxHelperConfig(): SPNgxHelperConfig {\n const defaultNgxHelperConfig: SPNgxHelperConfig = {\n i18nTranslate: (label: string, context?: any) => label\n }\n const helperConfig = inject(SP_NGX_HELPER_CONFIG, {optional: true});\n return {\n ...defaultNgxHelperConfig,\n ...(helperConfig ?? {}),\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;AAAO,MAAM,kBAAkB,GAAG;;ACAlC;MAOa,oBAAoB,GAAG,IAAI,cAAc,CACpD,mBAAmB;SAGL,kBAAkB,GAAA;AAChC,IAAA,MAAM,sBAAsB,GAAsB;QAChD,aAAa,EAAE,CAAC,KAAa,EAAE,OAAa,KAAK;KAClD;AACD,IAAA,MAAM,YAAY,GAAG,MAAM,CAAC,oBAAoB,EAAE,EAAC,QAAQ,EAAE,IAAI,EAAC,CAAC;IACnE,OAAO;AACL,QAAA,GAAG,sBAAsB;AACzB,QAAA,IAAI,YAAY,IAAI,EAAE,CAAC;KACxB;AACH;;ACpBA;;AAEG;;;;"}
@@ -0,0 +1,112 @@
1
+ import { spFormatDate, spFormatCurrency } from '@smallpearl/ngx-helper/locale';
2
+ import { InjectionToken } from '@angular/core';
3
+
4
+ /**
5
+ * A class that represents a SPEntityFieldSpec<>. This is typically used
6
+ * by the library to evaluate a SPEntityFieldSpec<> object.
7
+ */
8
+ class SPEntityField {
9
+ helperConfig;
10
+ fieldConfig;
11
+ _fieldSpec;
12
+ constructor(spec, helperConfig, fieldConfig) {
13
+ this.helperConfig = helperConfig;
14
+ this.fieldConfig = fieldConfig;
15
+ if (typeof spec === 'string') {
16
+ this._fieldSpec = {
17
+ name: spec,
18
+ };
19
+ }
20
+ else {
21
+ this._fieldSpec = spec;
22
+ }
23
+ }
24
+ get spec() {
25
+ return this._fieldSpec;
26
+ }
27
+ /**
28
+ * Returns the effective fieldValueOptions by merging the global field
29
+ * options (if one has been spefified) with the local field value options.
30
+ * @returns SPEntityFieldSpec<any>['valueOptions']
31
+ */
32
+ get options() {
33
+ let globalFieldValueOptions = {};
34
+ if (this.fieldConfig && this.fieldConfig?.fieldValueOptions && this.fieldConfig.fieldValueOptions.has(this._fieldSpec.name)) {
35
+ globalFieldValueOptions = this.fieldConfig.fieldValueOptions.get(this._fieldSpec.name);
36
+ }
37
+ return {
38
+ ...globalFieldValueOptions,
39
+ ...(this._fieldSpec?.valueOptions ?? {})
40
+ };
41
+ }
42
+ /**
43
+ * @returns the label for the field.
44
+ */
45
+ label() {
46
+ return this.helperConfig.i18nTranslate(this._fieldSpec.label ?? this._fieldSpec.name);
47
+ }
48
+ /**
49
+ * Given an entity, returns the value of the field matching the
50
+ * SPEntityFieldSpec<> in fieldSpec.
51
+ * @param entity TEntity instance which will be evaluated for
52
+ * SPEntityFieldSpec<>.
53
+ * @returns
54
+ */
55
+ value(entity) {
56
+ let val = undefined;
57
+ if (!this._fieldSpec.valueFn) {
58
+ if (this.fieldConfig &&
59
+ this.fieldConfig?.fieldValueFns &&
60
+ this.fieldConfig.fieldValueFns.has(this._fieldSpec.name)) {
61
+ val = this.fieldConfig.fieldValueFns.get(this._fieldSpec.name)(entity, this._fieldSpec.name);
62
+ }
63
+ else {
64
+ val = entity[this._fieldSpec.name];
65
+ }
66
+ }
67
+ else {
68
+ val = this._fieldSpec.valueFn(entity);
69
+ }
70
+ const valueOptions = this.options;
71
+ if (val instanceof Date) {
72
+ val = spFormatDate(val);
73
+ }
74
+ else if (typeof val === 'number' &&
75
+ valueOptions?.isCurrency) {
76
+ val = spFormatCurrency(val, this._fieldSpec?.valueOptions?.currency);
77
+ }
78
+ else if (typeof val === 'boolean') {
79
+ val = val ? '✔' : '✖';
80
+ }
81
+ return val;
82
+ }
83
+ /**
84
+ * If specified, will be added to the CSS classes of the field's wrapper
85
+ * element.
86
+ */
87
+ get class() {
88
+ return this._fieldSpec?.valueOptions?.class ?? '';
89
+ }
90
+ hasRouterLink(entity) {
91
+ return !!this._fieldSpec?.valueOptions?.routerLink;
92
+ }
93
+ getRouterLink(entity) {
94
+ const rl = this._fieldSpec?.valueOptions?.routerLink;
95
+ if (rl) {
96
+ if (typeof rl == 'function') {
97
+ return rl(entity);
98
+ }
99
+ return rl;
100
+ }
101
+ return [];
102
+ }
103
+ }
104
+
105
+ const SP_ENTITY_FIELD_CONFIG = new InjectionToken('SPEntityFieldConfig');
106
+
107
+ /**
108
+ * Generated bundle index. Do not edit.
109
+ */
110
+
111
+ export { SPEntityField, SP_ENTITY_FIELD_CONFIG };
112
+ //# sourceMappingURL=smallpearl-ngx-helper-entity-field.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smallpearl-ngx-helper-entity-field.mjs","sources":["../../../../projects/smallpearl/ngx-helper/entity-field/src/entity-field-spec.ts","../../../../projects/smallpearl/ngx-helper/entity-field/src/provider.ts","../../../../projects/smallpearl/ngx-helper/entity-field/smallpearl-ngx-helper-entity-field.ts"],"sourcesContent":["import { SPNgxHelperConfig } from '@smallpearl/ngx-helper/core';\nimport {\n spFormatCurrency,\n spFormatDate,\n SPIntlDateFormat,\n} from '@smallpearl/ngx-helper/locale';\nimport { SPEntityFieldConfig } from './provider';\n\n/**\n * This structure defines the data formatting details for a field of the\n * entity. All entity fields need not necessarily be actual entity object's\n * properties. Fields can also be computed fields, in which case the valueFn\n * should be initialized with a valid function to provide the field's value.\n */\nexport type SPEntityFieldSpec<TEntity extends { [P in IdKey]: PropertyKey }, IdKey extends string = 'id'> = {\n // Column name. If valueFn is not specified, this will be used as the\n // key name to retrieve the value for the column from TEntity.\n name: string;\n // If omitted, 'name' will be used as field label.\n label?: string;\n // Column value specific formatting options. Currently, only used for\n // Date types.\n valueOptions?: {\n // Specify the same format string argument that is passed to DatePipe.\n dateTimeFormat?: SPIntlDateFormat;\n // If boolean, number field will be formatted using spFormatCurrency()\n // using the current currency or 'currency' value below.\n isCurrency?: boolean;\n // Currency code, if different from default locale.\n currency?: string;\n // CSS class name; if provided will be applied to field value's wrapper\n // element. This will be <td> & <th>.\n class?: string;\n // Alignment options. Field's value will be aligned based on this.\n alignment?: 'start'|'center'|'end';\n // A fixed string or a function that returns an array of strings\n // to be used as the routerlink for the column value.\n routerLink?: ((e: TEntity) => string[])|[string];\n };\n // If the column value cannot be derived by simple TEntity[name] lookup,\n // use this function to return a custom computed or formatted value.\n valueFn?: (item: TEntity) => string | number | Date | boolean;\n};\n\n/**\n * A class that represents a SPEntityFieldSpec<>. This is typically used\n * by the library to evaluate a SPEntityFieldSpec<> object.\n */\nexport class SPEntityField<TEntity extends { [P in IdKey]: PropertyKey }, IdKey extends string = 'id'> {\n public _fieldSpec!: SPEntityFieldSpec<TEntity, IdKey>;\n\n constructor(\n spec: SPEntityFieldSpec<TEntity, IdKey> | string,\n public helperConfig: SPNgxHelperConfig,\n public fieldConfig?: SPEntityFieldConfig\n ) {\n if (typeof spec === 'string') {\n this._fieldSpec = {\n name: spec,\n };\n } else {\n this._fieldSpec = spec;\n }\n }\n\n get spec() {\n return this._fieldSpec;\n }\n\n /**\n * Returns the effective fieldValueOptions by merging the global field\n * options (if one has been spefified) with the local field value options.\n * @returns SPEntityFieldSpec<any>['valueOptions']\n */\n get options() {\n let globalFieldValueOptions: SPEntityFieldSpec<any>['valueOptions'] = {};\n if (this.fieldConfig && this.fieldConfig?.fieldValueOptions && this.fieldConfig.fieldValueOptions.has(this._fieldSpec.name)) {\n globalFieldValueOptions = this.fieldConfig.fieldValueOptions.get(this._fieldSpec.name);\n }\n return {\n ...globalFieldValueOptions,\n ...(this._fieldSpec?.valueOptions ?? {})\n };\n }\n /**\n * @returns the label for the field.\n */\n label() {\n return this.helperConfig.i18nTranslate(\n this._fieldSpec.label ?? this._fieldSpec.name\n );\n }\n\n /**\n * Given an entity, returns the value of the field matching the\n * SPEntityFieldSpec<> in fieldSpec.\n * @param entity TEntity instance which will be evaluated for\n * SPEntityFieldSpec<>.\n * @returns\n */\n value(entity: TEntity) {\n let val = undefined;\n if (!this._fieldSpec.valueFn) {\n if (\n this.fieldConfig &&\n this.fieldConfig?.fieldValueFns &&\n this.fieldConfig.fieldValueFns.has(this._fieldSpec.name)\n ) {\n val = this.fieldConfig.fieldValueFns.get(this._fieldSpec.name)!(entity, this._fieldSpec.name);\n } else {\n val = (entity as any)[this._fieldSpec.name];\n }\n } else {\n val = this._fieldSpec.valueFn(entity);\n }\n const valueOptions = this.options;\n if (val instanceof Date) {\n val = spFormatDate(val);\n } else if (\n typeof val === 'number' &&\n valueOptions?.isCurrency\n ) {\n val = spFormatCurrency(val, this._fieldSpec?.valueOptions?.currency);\n } else if (typeof val === 'boolean') {\n val = val ? '✔' : '✖';\n }\n return val;\n }\n\n /**\n * If specified, will be added to the CSS classes of the field's wrapper\n * element.\n */\n get class() {\n return this._fieldSpec?.valueOptions?.class ?? '';\n }\n\n hasRouterLink(entity: TEntity) {\n return !!this._fieldSpec?.valueOptions?.routerLink;\n }\n\n getRouterLink(entity: TEntity) {\n const rl = this._fieldSpec?.valueOptions?.routerLink;\n if (rl) {\n if (typeof rl == 'function') {\n return rl(entity);\n }\n return rl\n }\n return [];\n }\n}\n","import { InjectionToken } from '@angular/core';\nimport { SPEntityFieldSpec } from './entity-field-spec';\n\n\nexport type FIELD_VALUE_FN = (entity: any, fieldName: string) => string|number|Date|boolean;\n\n/**\n * Global config for SPEntityField component.\n */\nexport interface SPEntityFieldConfig {\n /**\n * These are global field value functions.\n *\n * If a value function for a field is not explicitly specified, this map is\n * looked up with the field name. If an entry exists in this table, it will\n * be used to render the field's value.\n *\n * This is useful for formatting certain fields which tend to have the\n * same name across the app. For instance fields such as 'amount', 'total'\n * or 'balance'. Or 'date', 'timestamp', etc.\n */\n fieldValueFns?: Map<string, FIELD_VALUE_FN>;\n /**\n * Similar to above, but allows setting the options for certain fields\n * globally. As in the case of `fieldValueFns`, the per field specification,\n * if one exists, takes precedence over the global setting.\n */\n fieldValueOptions?: Map<string, SPEntityFieldSpec<any>['valueOptions']>;\n}\n\nexport const SP_ENTITY_FIELD_CONFIG = new InjectionToken<SPEntityFieldConfig>(\n 'SPEntityFieldConfig'\n);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ import { of, throwError, catchError } from 'rxjs';
2
+
3
+ /**
4
+ * Handle's form validation errors sent from the server by setting the returned
5
+ * error code in the respective control. Errors not associated with any control
6
+ * in the form are attached to the form itself.
7
+ *
8
+ * These errors can then be rendered using error-tailor. All that needs to be
9
+ * done is to import errorTailorImports in the respective page's module and set
10
+ * the 'errorTailor' directive on the <form..> element.
11
+ *
12
+ * @param form FormGroup instance
13
+ * @param error error object
14
+ */
15
+ function handleValidationErrors(form, error, cdr) {
16
+ if (error.status == 400) {
17
+ /**
18
+ * A helper function that converts the server error codes to an error
19
+ * object that can be set on the form control. If the error code is a
20
+ * string with embedded spaces, it is treated as an actual error message
21
+ * and is set as the 'serverMessage' error code. Otherwise, the error code
22
+ * is returned as the object { errorCode: true }.
23
+ * @param errorCode
24
+ * @returns
25
+ */
26
+ const serverErrorsToErrorObject = (errorCode) => {
27
+ if (!Array.isArray(errorCode)) {
28
+ errorCode = [errorCode];
29
+ }
30
+ const errorsObj = {};
31
+ errorCode.forEach((code) => {
32
+ if (code.includes(' ')) {
33
+ // errorCode is an actual error message that we forgot to represent
34
+ // as a camelcase string. So represent the error as the error code
35
+ // 'serverMessage', which will be treated differently by our ErrorTailor
36
+ // error's config provider. (see ionic-error-tailor.module.ts)
37
+ errorsObj['serverMessage'] = { message: code };
38
+ }
39
+ else {
40
+ // Real error code
41
+ errorsObj[code] = true;
42
+ }
43
+ });
44
+ return errorsObj;
45
+ };
46
+ // server form validation errors
47
+ let pendingDetechChanges = false;
48
+ for (const controlName in error.error) {
49
+ const errorsObj = serverErrorsToErrorObject(error.error[controlName]);
50
+ if (form.contains(controlName)) {
51
+ const control = form.controls[controlName];
52
+ control.setErrors(errorsObj);
53
+ pendingDetechChanges = true;
54
+ }
55
+ else {
56
+ form.setErrors(errorsObj);
57
+ pendingDetechChanges = true;
58
+ }
59
+ }
60
+ if (pendingDetechChanges && cdr) {
61
+ cdr.detectChanges();
62
+ }
63
+ return of(null);
64
+ }
65
+ return throwError(() => error);
66
+ }
67
+ /**
68
+ * Returns an rxjs operator that would track an http request's error
69
+ * state and if the return code is 400, would enumerate the error codes
70
+ * set in the response body and set the error state of corresponding
71
+ * form controls.
72
+ *
73
+ * Use it like below:
74
+ *
75
+ * this.http.get<User>('https://google.com/..').pipe(
76
+ * setServerErrorsAsFormErrors(form),
77
+ * tap(user => {
78
+ * if (user) { // if user = null, it means there was a server validation
79
+ * // error.
80
+ * this.user = user;
81
+ * }
82
+ * })
83
+ * ).subscribe();
84
+ *
85
+ * Note that setServerErrorsAsFormErrors() is the last operator in
86
+ * the operators list. This is important because if a server validation
87
+ * error is encountered, setServerErrorsAsFormErrors(), would place
88
+ * the error on the respective form control and in the end would
89
+ * return Observable<null>. Any subsequent opeators that are added
90
+ * to the list after this should check for this null value in its
91
+ * handler.
92
+ *
93
+ * @param form The FormGroup instance where the controls corresponding
94
+ * to the errors are looked up and its control.error is set to the
95
+ * returned error code.
96
+ * @param cdr ChangeDetectorRef instance. If provided, it would be
97
+ * used to detect changes after the error codes are set on the form
98
+ * controls.
99
+ * @returns An rxjs op that can be added to the pipe() arg list.
100
+ */
101
+ function setServerErrorsAsFormErrors(form, cdr) {
102
+ return function (source) {
103
+ return source.pipe(catchError((error) => handleValidationErrors(form, error, cdr)));
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Generated bundle index. Do not edit.
109
+ */
110
+
111
+ export { handleValidationErrors, setServerErrorsAsFormErrors };
112
+ //# sourceMappingURL=smallpearl-ngx-helper-forms.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smallpearl-ngx-helper-forms.mjs","sources":["../../../../projects/smallpearl/ngx-helper/forms/src/validation-error-handler.ts","../../../../projects/smallpearl/ngx-helper/forms/smallpearl-ngx-helper-forms.ts"],"sourcesContent":["import { HttpErrorResponse } from '@angular/common/http';\nimport { ChangeDetectorRef } from '@angular/core';\nimport { UntypedFormGroup } from '@angular/forms';\nimport { catchError, Observable, of, throwError } from 'rxjs';\n\n/**\n * Handle's form validation errors sent from the server by setting the returned\n * error code in the respective control. Errors not associated with any control\n * in the form are attached to the form itself.\n *\n * These errors can then be rendered using error-tailor. All that needs to be\n * done is to import errorTailorImports in the respective page's module and set\n * the 'errorTailor' directive on the <form..> element.\n *\n * @param form FormGroup instance\n * @param error error object\n */\nexport function handleValidationErrors(\n form: UntypedFormGroup,\n error: HttpErrorResponse,\n cdr?: ChangeDetectorRef\n): Observable<any> {\n if (error.status == 400) {\n /**\n * A helper function that converts the server error codes to an error\n * object that can be set on the form control. If the error code is a\n * string with embedded spaces, it is treated as an actual error message\n * and is set as the 'serverMessage' error code. Otherwise, the error code\n * is returned as the object { errorCode: true }.\n * @param errorCode\n * @returns\n */\n const serverErrorsToErrorObject = (\n errorCode: string | string[]\n ): unknown => {\n if (!Array.isArray(errorCode)) {\n errorCode = [errorCode];\n }\n const errorsObj = {};\n errorCode.forEach((code) => {\n if (code.includes(' ')) {\n // errorCode is an actual error message that we forgot to represent\n // as a camelcase string. So represent the error as the error code\n // 'serverMessage', which will be treated differently by our ErrorTailor\n // error's config provider. (see ionic-error-tailor.module.ts)\n (errorsObj as any)['serverMessage'] = { message: code };\n } else {\n // Real error code\n (errorsObj as any)[code] = true;\n }\n });\n return errorsObj;\n };\n\n // server form validation errors\n let pendingDetechChanges = false;\n for (const controlName in error.error) {\n const errorsObj = serverErrorsToErrorObject(error.error[controlName]);\n if (form.contains(controlName)) {\n const control = form.controls[controlName];\n control.setErrors(errorsObj as any);\n pendingDetechChanges = true;\n } else {\n form.setErrors(errorsObj as any);\n pendingDetechChanges = true;\n }\n }\n if (pendingDetechChanges && cdr) {\n cdr.detectChanges();\n }\n return of(null);\n }\n return throwError(() => error);\n}\n\n/**\n * Returns an rxjs operator that would track an http request's error\n * state and if the return code is 400, would enumerate the error codes\n * set in the response body and set the error state of corresponding\n * form controls.\n *\n * Use it like below:\n *\n * this.http.get<User>('https://google.com/..').pipe(\n * setServerErrorsAsFormErrors(form),\n * tap(user => {\n * if (user) { // if user = null, it means there was a server validation\n * // error.\n * this.user = user;\n * }\n * })\n * ).subscribe();\n *\n * Note that setServerErrorsAsFormErrors() is the last operator in\n * the operators list. This is important because if a server validation\n * error is encountered, setServerErrorsAsFormErrors(), would place\n * the error on the respective form control and in the end would\n * return Observable<null>. Any subsequent opeators that are added\n * to the list after this should check for this null value in its\n * handler.\n *\n * @param form The FormGroup instance where the controls corresponding\n * to the errors are looked up and its control.error is set to the\n * returned error code.\n * @param cdr ChangeDetectorRef instance. If provided, it would be\n * used to detect changes after the error codes are set on the form\n * controls.\n * @returns An rxjs op that can be added to the pipe() arg list.\n */\nexport function setServerErrorsAsFormErrors(\n form: UntypedFormGroup,\n cdr?: ChangeDetectorRef\n) {\n return function <T>(source: Observable<T>): Observable<T> {\n return source.pipe(\n catchError((error) => handleValidationErrors(form, error, cdr))\n );\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;AAKA;;;;;;;;;;;AAWG;SACa,sBAAsB,CACpC,IAAsB,EACtB,KAAwB,EACxB,GAAuB,EAAA;AAEvB,IAAA,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG,EAAE;AACvB;;;;;;;;AAQG;AACH,QAAA,MAAM,yBAAyB,GAAG,CAChC,SAA4B,KACjB;YACX,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;AAC7B,gBAAA,SAAS,GAAG,CAAC,SAAS,CAAC;;YAEzB,MAAM,SAAS,GAAG,EAAE;AACpB,YAAA,SAAS,CAAC,OAAO,CAAC,CAAC,IAAI,KAAI;AACzB,gBAAA,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;;;;;oBAKrB,SAAiB,CAAC,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE;;qBAClD;;AAEJ,oBAAA,SAAiB,CAAC,IAAI,CAAC,GAAG,IAAI;;AAEnC,aAAC,CAAC;AACF,YAAA,OAAO,SAAS;AAClB,SAAC;;QAGD,IAAI,oBAAoB,GAAG,KAAK;AAChC,QAAA,KAAK,MAAM,WAAW,IAAI,KAAK,CAAC,KAAK,EAAE;YACrC,MAAM,SAAS,GAAG,yBAAyB,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AACrE,YAAA,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE;gBAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;AAC1C,gBAAA,OAAO,CAAC,SAAS,CAAC,SAAgB,CAAC;gBACnC,oBAAoB,GAAG,IAAI;;iBACtB;AACL,gBAAA,IAAI,CAAC,SAAS,CAAC,SAAgB,CAAC;gBAChC,oBAAoB,GAAG,IAAI;;;AAG/B,QAAA,IAAI,oBAAoB,IAAI,GAAG,EAAE;YAC/B,GAAG,CAAC,aAAa,EAAE;;AAErB,QAAA,OAAO,EAAE,CAAC,IAAI,CAAC;;AAEjB,IAAA,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC;AAChC;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCG;AACa,SAAA,2BAA2B,CACzC,IAAsB,EACtB,GAAuB,EAAA;AAEvB,IAAA,OAAO,UAAa,MAAqB,EAAA;QACvC,OAAO,MAAM,CAAC,IAAI,CAChB,UAAU,CAAC,CAAC,KAAK,KAAK,sBAAsB,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,CAChE;AACH,KAAC;AACH;;ACtHA;;AAEG;;;;"}