@nexterajs/ui 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +279 -34
- package/fesm2022/nexterajs-ui-charts.mjs +1096 -0
- package/fesm2022/nexterajs-ui-charts.mjs.map +1 -0
- package/fesm2022/nexterajs-ui-core.mjs +652 -12
- package/fesm2022/nexterajs-ui-core.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-display.mjs +1142 -0
- package/fesm2022/nexterajs-ui-display.mjs.map +1 -0
- package/fesm2022/nexterajs-ui-dropdowns.mjs +321 -33
- package/fesm2022/nexterajs-ui-dropdowns.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-file-uploader.mjs +572 -10
- package/fesm2022/nexterajs-ui-file-uploader.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-forms.mjs +2027 -136
- package/fesm2022/nexterajs-ui-forms.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-grid.mjs +1222 -188
- package/fesm2022/nexterajs-ui-grid.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-intl.mjs +103 -11
- package/fesm2022/nexterajs-ui-intl.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-navigation.mjs +1054 -0
- package/fesm2022/nexterajs-ui-navigation.mjs.map +1 -0
- package/fesm2022/nexterajs-ui-notify.mjs +46 -44
- package/fesm2022/nexterajs-ui-notify.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-overlay.mjs +1671 -0
- package/fesm2022/nexterajs-ui-overlay.mjs.map +1 -0
- package/fesm2022/nexterajs-ui-pivot.mjs +51 -26
- package/fesm2022/nexterajs-ui-pivot.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-shell.mjs +542 -0
- package/fesm2022/nexterajs-ui-shell.mjs.map +1 -0
- package/fesm2022/nexterajs-ui-tree-view.mjs +5 -2
- package/fesm2022/nexterajs-ui-tree-view.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-validation.mjs +42 -11
- package/fesm2022/nexterajs-ui-validation.mjs.map +1 -1
- package/fesm2022/nexterajs-ui.mjs +5 -0
- package/fesm2022/nexterajs-ui.mjs.map +1 -1
- package/package.json +33 -2
- package/styles/_dropdown.scss +5 -5
- package/styles/_editor.scss +52 -17
- package/styles/_mixins.scss +1 -1
- package/styles/_scrollbar.scss +5 -5
- package/styles/_status.scss +21 -0
- package/styles/_toast.scss +14 -14
- package/styles/_tokens.scss +420 -3
- package/styles/_widget.scss +14 -3
- package/styles/index.scss +6 -4
- package/types/nexterajs-ui-charts.d.ts +446 -0
- package/types/nexterajs-ui-core.d.ts +387 -22
- package/types/nexterajs-ui-display.d.ts +720 -0
- package/types/nexterajs-ui-dropdowns.d.ts +191 -48
- package/types/nexterajs-ui-file-uploader.d.ts +236 -7
- package/types/nexterajs-ui-forms.d.ts +675 -30
- package/types/nexterajs-ui-grid.d.ts +448 -93
- package/types/nexterajs-ui-intl.d.ts +39 -4
- package/types/nexterajs-ui-navigation.d.ts +531 -0
- package/types/nexterajs-ui-notify.d.ts +4 -2
- package/types/nexterajs-ui-overlay.d.ts +703 -0
- package/types/nexterajs-ui-pivot.d.ts +56 -48
- package/types/nexterajs-ui-shell.d.ts +294 -0
- package/types/nexterajs-ui-tree-view.d.ts +6 -3
- package/types/nexterajs-ui-validation.d.ts +20 -11
- package/types/nexterajs-ui.d.ts +5 -0
|
@@ -1,5 +1,559 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, ElementRef, NgZone, model, input, booleanAttribute,
|
|
2
|
+
import { InjectionToken, inject, signal, computed, DestroyRef, Injectable, makeEnvironmentProviders, ElementRef, NgZone, model, input, booleanAttribute, output, Directive, TemplateRef } from '@angular/core';
|
|
3
|
+
import { NXE_ID_SEPARATORS, NXE_EN_US_SEPARATORS, NXE_NUMBER_SEPARATORS } from '@nexterajs/ui/intl';
|
|
4
|
+
import { Settings } from 'luxon';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Every user-visible string the library renders, in English.
|
|
8
|
+
*
|
|
9
|
+
* Flat keys, namespaced by component (`grid.pager.info`). `{0}`, `{1}`... are positional
|
|
10
|
+
* arguments filled by `NxeMessageService.t(key, ...args)`. English is the default bundle and
|
|
11
|
+
* the fallback for any key a custom bundle leaves out, so the library renders exactly what it
|
|
12
|
+
* always did until an application opts into another locale.
|
|
13
|
+
*
|
|
14
|
+
* The key set is the type: `NxeMessageKey` is derived from it and the Indonesian bundle is
|
|
15
|
+
* checked against it, so a key added here without a translation fails to compile.
|
|
16
|
+
*/
|
|
17
|
+
const NXE_MESSAGES_EN = {
|
|
18
|
+
// Shared by several components.
|
|
19
|
+
'common.search': 'Search',
|
|
20
|
+
'common.clear': 'Clear',
|
|
21
|
+
'common.close': 'Close',
|
|
22
|
+
'common.ok': 'OK',
|
|
23
|
+
'common.cancel': 'Cancel',
|
|
24
|
+
'common.confirm': 'Confirm',
|
|
25
|
+
'common.yes': 'Yes',
|
|
26
|
+
'common.no': 'No',
|
|
27
|
+
'common.all': '(All)',
|
|
28
|
+
'common.selectAll': 'Select All',
|
|
29
|
+
'common.blanks': '(Blanks)',
|
|
30
|
+
// nxe-data-grid.
|
|
31
|
+
'grid.noData': 'No data',
|
|
32
|
+
'grid.groupPanel': 'Drag a column header here to group by that column',
|
|
33
|
+
'grid.edit': 'Edit',
|
|
34
|
+
'grid.save': 'Save',
|
|
35
|
+
'grid.cancel': 'Cancel',
|
|
36
|
+
'grid.expand': 'Expand',
|
|
37
|
+
'grid.collapse': 'Collapse',
|
|
38
|
+
'grid.expandedRow': 'Expanded row',
|
|
39
|
+
'grid.collapsedRow': 'Collapsed row',
|
|
40
|
+
'grid.editableCell': 'Editable',
|
|
41
|
+
'grid.filterRow.operation': 'Filter operation',
|
|
42
|
+
'grid.filterRow.reset': 'Reset',
|
|
43
|
+
'grid.filterRow.true': 'true',
|
|
44
|
+
'grid.filterRow.false': 'false',
|
|
45
|
+
'grid.op.contains': 'Contains',
|
|
46
|
+
'grid.op.notContains': 'Does not contain',
|
|
47
|
+
'grid.op.startsWith': 'Starts with',
|
|
48
|
+
'grid.op.endsWith': 'Ends with',
|
|
49
|
+
'grid.op.equals': 'Equals',
|
|
50
|
+
'grid.op.notEquals': 'Does not equal',
|
|
51
|
+
'grid.op.lessThan': 'Less than',
|
|
52
|
+
'grid.op.greaterThan': 'Greater than',
|
|
53
|
+
'grid.op.lessThanOrEqual': 'Less than or equal to',
|
|
54
|
+
'grid.op.greaterThanOrEqual': 'Greater than or equal to',
|
|
55
|
+
'grid.op.between': 'Between',
|
|
56
|
+
// Pager (inside nxe-data-grid).
|
|
57
|
+
'grid.pager.info': 'Page {0} of {1} ({2} items)',
|
|
58
|
+
'grid.pager.navigation': 'Page navigation',
|
|
59
|
+
'grid.pager.pageSize': 'Page size',
|
|
60
|
+
'grid.pager.itemsPerPage': 'Items per page: {0}',
|
|
61
|
+
'grid.pager.previous': 'Previous page',
|
|
62
|
+
'grid.pager.next': 'Next page',
|
|
63
|
+
'grid.pager.page': 'Page {0}',
|
|
64
|
+
'grid.pager.first': 'First page',
|
|
65
|
+
'grid.pager.last': 'Last page',
|
|
66
|
+
'grid.pager.total': '{0} items',
|
|
67
|
+
// Server-side data (NxeRemoteDataSource) in nxe-data-grid.
|
|
68
|
+
'grid.remote.loading': 'Loading',
|
|
69
|
+
'grid.remote.error': 'The data could not be loaded.',
|
|
70
|
+
'grid.remote.retry': 'Retry',
|
|
71
|
+
// nxe-file-uploader.
|
|
72
|
+
'uploader.selectFile': 'Select File',
|
|
73
|
+
'uploader.dropHere': 'or Drop here',
|
|
74
|
+
'uploader.cancel': 'Cancel',
|
|
75
|
+
'uploader.sizeB': '{0} b',
|
|
76
|
+
'uploader.sizeKb': '{0} Kb',
|
|
77
|
+
'uploader.sizeMb': '{0} Mb',
|
|
78
|
+
// nxe-validator rules without a message of their own.
|
|
79
|
+
'validation.rule.required': 'Required',
|
|
80
|
+
'validation.rule.email': 'Email is invalid',
|
|
81
|
+
'validation.rule.pattern': 'Value does not match pattern',
|
|
82
|
+
'validation.rule.range': 'Value is out of range',
|
|
83
|
+
'validation.rule.compare': 'Values do not match',
|
|
84
|
+
'validation.rule.custom': 'Value is invalid',
|
|
85
|
+
// nxe-form-field, for Angular's built-in validators.
|
|
86
|
+
'validation.field.required': 'This field is required.',
|
|
87
|
+
'validation.field.email': 'Enter a valid email address.',
|
|
88
|
+
'validation.field.minlength': 'Enter at least {0} characters.',
|
|
89
|
+
'validation.field.maxlength': 'Enter no more than {0} characters.',
|
|
90
|
+
'validation.field.min': 'Must be at least {0}.',
|
|
91
|
+
'validation.field.max': 'Must be at most {0}.',
|
|
92
|
+
'validation.field.pattern': 'Enter a value in the expected format.',
|
|
93
|
+
'validation.field.invalid': 'This value is not valid.',
|
|
94
|
+
// nxe-date-box calendar. Month and weekday names come from Luxon for the active locale.
|
|
95
|
+
'date.now': 'Now',
|
|
96
|
+
'date.done': 'Done',
|
|
97
|
+
'date.hour': 'Hour',
|
|
98
|
+
'date.minute': 'Minute',
|
|
99
|
+
// nxe-date-range-box.
|
|
100
|
+
'dateRange.start': 'Start date',
|
|
101
|
+
'dateRange.end': 'End date',
|
|
102
|
+
'dateRange.startPlaceholder': 'Start date',
|
|
103
|
+
'dateRange.endPlaceholder': 'End date',
|
|
104
|
+
'dateRange.dialog': 'Choose a date range',
|
|
105
|
+
'dateRange.previousMonth': 'Previous month',
|
|
106
|
+
'dateRange.nextMonth': 'Next month',
|
|
107
|
+
'dateRange.presets': 'Quick ranges',
|
|
108
|
+
'dateRange.pickStart': 'Select the start date',
|
|
109
|
+
'dateRange.pickEnd': 'Select the end date',
|
|
110
|
+
'dateRange.preset.today': 'Today',
|
|
111
|
+
'dateRange.preset.yesterday': 'Yesterday',
|
|
112
|
+
'dateRange.preset.last7Days': 'Last 7 days',
|
|
113
|
+
'dateRange.preset.last30Days': 'Last 30 days',
|
|
114
|
+
'dateRange.preset.thisMonth': 'This month',
|
|
115
|
+
'dateRange.preset.lastMonth': 'Last month',
|
|
116
|
+
'dateRange.preset.thisYear': 'This year',
|
|
117
|
+
// Drop-downs (nxe-select-box, nxe-tag-box).
|
|
118
|
+
'dropdown.noData': 'No data to display',
|
|
119
|
+
'dropdown.selectAll': 'Select All',
|
|
120
|
+
// nxe-file-input, and its errors in nxe-form-field.
|
|
121
|
+
'uploader.selectFiles': 'Select files',
|
|
122
|
+
'uploader.dropFiles': 'or drop files here',
|
|
123
|
+
'uploader.listLabel': 'Attached files',
|
|
124
|
+
'uploader.acceptHint': 'Accepted: {0}',
|
|
125
|
+
'uploader.maxSizeHint': 'Up to {0} each',
|
|
126
|
+
'uploader.maxFilesHint': 'Up to {0} files',
|
|
127
|
+
'uploader.statusPending': 'Waiting to upload',
|
|
128
|
+
'uploader.statusUploading': 'Uploading {0}%',
|
|
129
|
+
'uploader.statusUploaded': 'Uploaded',
|
|
130
|
+
'uploader.statusFailed': 'Upload failed',
|
|
131
|
+
'uploader.uploadingFile': 'Uploading {0}',
|
|
132
|
+
'uploader.uploadFailed': 'The upload failed.',
|
|
133
|
+
'uploader.cancelled': 'Upload cancelled.',
|
|
134
|
+
'uploader.cancelFile': 'Cancel upload of {0}',
|
|
135
|
+
'uploader.retryFile': 'Retry upload of {0}',
|
|
136
|
+
'uploader.removeFile': 'Remove {0}',
|
|
137
|
+
'uploader.retry': 'Retry',
|
|
138
|
+
'uploader.remove': 'Remove',
|
|
139
|
+
'uploader.rejectType': '{0} is not an accepted file type.',
|
|
140
|
+
'uploader.rejectSize': '{0} is larger than {1}.',
|
|
141
|
+
'uploader.rejectCount': '{0} is over the limit of {1} files.',
|
|
142
|
+
'uploader.announceAdded': '{0} added',
|
|
143
|
+
'uploader.announceRemoved': '{0} removed',
|
|
144
|
+
'validation.field.fileType': 'Only these file types are accepted: {0}.',
|
|
145
|
+
'validation.field.fileSize': 'Each file must be {0} or smaller.',
|
|
146
|
+
'validation.field.maxFiles': 'Attach at most {0} files.',
|
|
147
|
+
'validation.field.uploading': 'Wait for the uploads to finish.',
|
|
148
|
+
'validation.field.uploadFailed': 'Retry or remove the files that failed to upload.',
|
|
149
|
+
// nxe-timeline.
|
|
150
|
+
'timeline.pending': 'Pending',
|
|
151
|
+
// nxe-stepper.
|
|
152
|
+
'stepper.listLabel': 'Steps',
|
|
153
|
+
'stepper.stepOf': 'Step {0} of {1}: {2}',
|
|
154
|
+
'stepper.completed': 'Completed',
|
|
155
|
+
'stepper.error': 'Has errors',
|
|
156
|
+
'stepper.optional': 'Optional',
|
|
157
|
+
'stepper.blocked': 'Step {0} is not complete. Fix the highlighted fields to continue.',
|
|
158
|
+
// nxe-notify toasts.
|
|
159
|
+
'notify.close': 'Close notification',
|
|
160
|
+
// nxe-shell, nxe-sidenav, nxe-topbar.
|
|
161
|
+
'shell.skipToContent': 'Skip to content',
|
|
162
|
+
'shell.mainNavigation': 'Main navigation',
|
|
163
|
+
'shell.toggleNavigation': 'Toggle navigation',
|
|
164
|
+
'shell.closeNavigation': 'Close navigation',
|
|
165
|
+
'shell.collapseSidebar': 'Collapse sidebar',
|
|
166
|
+
'shell.expandSidebar': 'Expand sidebar',
|
|
167
|
+
// nxe-pivot-grid.
|
|
168
|
+
'pivot.grandTotal': 'Grand Total',
|
|
169
|
+
'pivot.total': '{0} Total',
|
|
170
|
+
'pivot.noData': 'No data',
|
|
171
|
+
'pivot.showFieldChooser': 'Show Field Chooser',
|
|
172
|
+
'pivot.chooser.title': 'Field Chooser',
|
|
173
|
+
'pivot.chooser.allFields': 'All Fields',
|
|
174
|
+
'pivot.chooser.rowFields': 'Row Fields',
|
|
175
|
+
'pivot.chooser.columnFields': 'Column Fields',
|
|
176
|
+
'pivot.chooser.filterFields': 'Filter Fields',
|
|
177
|
+
'pivot.chooser.dataFields': 'Data Fields',
|
|
178
|
+
'pivot.chooser.checkState': 'Check state',
|
|
179
|
+
'pivot.panel.dropFilter': 'Drop Filter Fields Here',
|
|
180
|
+
'pivot.panel.dropData': 'Drop Data Fields Here',
|
|
181
|
+
'pivot.panel.dropColumn': 'Drop Column Fields Here',
|
|
182
|
+
'pivot.panel.dropRow': 'Drop Row Fields Here',
|
|
183
|
+
// nxe-text-box password reveal and nxe-text-area counter.
|
|
184
|
+
'forms.password.show': 'Show password',
|
|
185
|
+
'forms.password.hide': 'Hide password',
|
|
186
|
+
'forms.textArea.charactersLeft': '{0} characters left',
|
|
187
|
+
'forms.textArea.charactersOver': '{0} characters over the limit',
|
|
188
|
+
'forms.numberBox.increase': 'Increase',
|
|
189
|
+
'forms.numberBox.decrease': 'Decrease',
|
|
190
|
+
// nxe-description-list: read in place of the empty-value glyph.
|
|
191
|
+
'display.noValue': 'No value',
|
|
192
|
+
// @nexterajs/ui/charts: nxe-line-chart, nxe-area-chart, nxe-donut-chart.
|
|
193
|
+
'charts.other': 'Other',
|
|
194
|
+
'charts.total': 'Total',
|
|
195
|
+
'charts.empty': 'No data to chart',
|
|
196
|
+
'charts.legend': 'Legend',
|
|
197
|
+
'charts.viewAsTable': 'View as table',
|
|
198
|
+
'charts.viewAsChart': 'View as chart',
|
|
199
|
+
'charts.navigate': 'Chart data. Use the arrow keys to move between points, Escape to leave.',
|
|
200
|
+
'charts.navigateSegments': 'Chart segments. Use the arrow keys to move between segments, Escape to leave.',
|
|
201
|
+
'charts.summary.line': 'Line chart, {0} series, {1} points from {2} to {3}.',
|
|
202
|
+
'charts.summary.area': 'Area chart, {0} series, {1} points from {2} to {3}.',
|
|
203
|
+
'charts.summary.stackedArea': 'Stacked area chart, {0} series, {1} points from {2} to {3}.',
|
|
204
|
+
'charts.summary.series': '{0}: from {1} to {2}, lowest {3}, highest {4}.',
|
|
205
|
+
'charts.summary.donut': 'Donut chart, {0} segments, total {1}.',
|
|
206
|
+
'charts.summary.segment': '{0}: {1}, {2}.',
|
|
207
|
+
'charts.table.category': 'Category',
|
|
208
|
+
'charts.table.segment': 'Segment',
|
|
209
|
+
'charts.table.value': 'Value',
|
|
210
|
+
'charts.table.share': 'Share',
|
|
211
|
+
// nxe-avatar status dot, and the "+N" chip of nxe-avatar-group.
|
|
212
|
+
'avatar.status.online': 'Online',
|
|
213
|
+
'avatar.status.away': 'Away',
|
|
214
|
+
'avatar.status.busy': 'Busy',
|
|
215
|
+
'avatar.status.offline': 'Offline',
|
|
216
|
+
'avatar.more': '{0} more',
|
|
217
|
+
// nxe-breadcrumb.
|
|
218
|
+
'breadcrumb.label': 'Breadcrumb',
|
|
219
|
+
'breadcrumb.more': 'Show hidden levels',
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
/** Indonesian bundle (Bahasa Indonesia), checked against the English key set. */
|
|
223
|
+
const NXE_MESSAGES_ID = {
|
|
224
|
+
'common.search': 'Cari',
|
|
225
|
+
'common.clear': 'Hapus',
|
|
226
|
+
'common.close': 'Tutup',
|
|
227
|
+
'common.ok': 'OK',
|
|
228
|
+
'common.cancel': 'Batal',
|
|
229
|
+
'common.confirm': 'Konfirmasi',
|
|
230
|
+
'common.yes': 'Ya',
|
|
231
|
+
'common.no': 'Tidak',
|
|
232
|
+
'common.all': '(Semua)',
|
|
233
|
+
'common.selectAll': 'Pilih Semua',
|
|
234
|
+
'common.blanks': '(Kosong)',
|
|
235
|
+
'grid.noData': 'Tidak ada data',
|
|
236
|
+
'grid.groupPanel': 'Seret header kolom ke sini untuk mengelompokkan berdasarkan kolom tersebut',
|
|
237
|
+
'grid.edit': 'Ubah',
|
|
238
|
+
'grid.save': 'Simpan',
|
|
239
|
+
'grid.cancel': 'Batal',
|
|
240
|
+
'grid.expand': 'Perluas',
|
|
241
|
+
'grid.collapse': 'Ciutkan',
|
|
242
|
+
'grid.expandedRow': 'Baris diperluas',
|
|
243
|
+
'grid.collapsedRow': 'Baris diciutkan',
|
|
244
|
+
'grid.editableCell': 'Dapat diubah',
|
|
245
|
+
'grid.filterRow.operation': 'Operasi filter',
|
|
246
|
+
'grid.filterRow.reset': 'Atur ulang',
|
|
247
|
+
'grid.filterRow.true': 'Ya',
|
|
248
|
+
'grid.filterRow.false': 'Tidak',
|
|
249
|
+
'grid.op.contains': 'Mengandung',
|
|
250
|
+
'grid.op.notContains': 'Tidak mengandung',
|
|
251
|
+
'grid.op.startsWith': 'Diawali dengan',
|
|
252
|
+
'grid.op.endsWith': 'Diakhiri dengan',
|
|
253
|
+
'grid.op.equals': 'Sama dengan',
|
|
254
|
+
'grid.op.notEquals': 'Tidak sama dengan',
|
|
255
|
+
'grid.op.lessThan': 'Kurang dari',
|
|
256
|
+
'grid.op.greaterThan': 'Lebih dari',
|
|
257
|
+
'grid.op.lessThanOrEqual': 'Kurang dari atau sama dengan',
|
|
258
|
+
'grid.op.greaterThanOrEqual': 'Lebih dari atau sama dengan',
|
|
259
|
+
'grid.op.between': 'Di antara',
|
|
260
|
+
'grid.pager.info': 'Halaman {0} dari {1} ({2} item)',
|
|
261
|
+
'grid.pager.navigation': 'Navigasi halaman',
|
|
262
|
+
'grid.pager.pageSize': 'Ukuran halaman',
|
|
263
|
+
'grid.pager.itemsPerPage': 'Item per halaman: {0}',
|
|
264
|
+
'grid.pager.previous': 'Halaman sebelumnya',
|
|
265
|
+
'grid.pager.next': 'Halaman berikutnya',
|
|
266
|
+
'grid.pager.page': 'Halaman {0}',
|
|
267
|
+
'grid.pager.first': 'Halaman pertama',
|
|
268
|
+
'grid.pager.last': 'Halaman terakhir',
|
|
269
|
+
'grid.pager.total': '{0} item',
|
|
270
|
+
'grid.remote.loading': 'Memuat',
|
|
271
|
+
'grid.remote.error': 'Data tidak dapat dimuat.',
|
|
272
|
+
'grid.remote.retry': 'Coba lagi',
|
|
273
|
+
'uploader.selectFile': 'Pilih File',
|
|
274
|
+
'uploader.dropHere': 'atau seret ke sini',
|
|
275
|
+
'uploader.cancel': 'Batal',
|
|
276
|
+
'uploader.sizeB': '{0} B',
|
|
277
|
+
'uploader.sizeKb': '{0} KB',
|
|
278
|
+
'uploader.sizeMb': '{0} MB',
|
|
279
|
+
'validation.rule.required': 'Wajib diisi',
|
|
280
|
+
'validation.rule.email': 'Email tidak valid',
|
|
281
|
+
'validation.rule.pattern': 'Nilai tidak sesuai pola',
|
|
282
|
+
'validation.rule.range': 'Nilai di luar rentang',
|
|
283
|
+
'validation.rule.compare': 'Nilai tidak sama',
|
|
284
|
+
'validation.rule.custom': 'Nilai tidak valid',
|
|
285
|
+
'validation.field.required': 'Kolom ini wajib diisi.',
|
|
286
|
+
'validation.field.email': 'Masukkan alamat email yang valid.',
|
|
287
|
+
'validation.field.minlength': 'Masukkan minimal {0} karakter.',
|
|
288
|
+
'validation.field.maxlength': 'Masukkan maksimal {0} karakter.',
|
|
289
|
+
'validation.field.min': 'Minimal {0}.',
|
|
290
|
+
'validation.field.max': 'Maksimal {0}.',
|
|
291
|
+
'validation.field.pattern': 'Masukkan nilai dengan format yang sesuai.',
|
|
292
|
+
'validation.field.invalid': 'Nilai ini tidak valid.',
|
|
293
|
+
'date.now': 'Sekarang',
|
|
294
|
+
'date.done': 'Selesai',
|
|
295
|
+
'date.hour': 'Jam',
|
|
296
|
+
'date.minute': 'Menit',
|
|
297
|
+
'dateRange.start': 'Tanggal mulai',
|
|
298
|
+
'dateRange.end': 'Tanggal akhir',
|
|
299
|
+
'dateRange.startPlaceholder': 'Tanggal mulai',
|
|
300
|
+
'dateRange.endPlaceholder': 'Tanggal akhir',
|
|
301
|
+
'dateRange.dialog': 'Pilih rentang tanggal',
|
|
302
|
+
'dateRange.previousMonth': 'Bulan sebelumnya',
|
|
303
|
+
'dateRange.nextMonth': 'Bulan berikutnya',
|
|
304
|
+
'dateRange.presets': 'Rentang cepat',
|
|
305
|
+
'dateRange.pickStart': 'Pilih tanggal mulai',
|
|
306
|
+
'dateRange.pickEnd': 'Pilih tanggal akhir',
|
|
307
|
+
'dateRange.preset.today': 'Hari ini',
|
|
308
|
+
'dateRange.preset.yesterday': 'Kemarin',
|
|
309
|
+
'dateRange.preset.last7Days': '7 hari terakhir',
|
|
310
|
+
'dateRange.preset.last30Days': '30 hari terakhir',
|
|
311
|
+
'dateRange.preset.thisMonth': 'Bulan ini',
|
|
312
|
+
'dateRange.preset.lastMonth': 'Bulan lalu',
|
|
313
|
+
'dateRange.preset.thisYear': 'Tahun ini',
|
|
314
|
+
'dropdown.noData': 'Tidak ada data',
|
|
315
|
+
'dropdown.selectAll': 'Pilih Semua',
|
|
316
|
+
// nxe-file-input, and its errors in nxe-form-field.
|
|
317
|
+
'uploader.selectFiles': 'Pilih file',
|
|
318
|
+
'uploader.dropFiles': 'atau seret file ke sini',
|
|
319
|
+
'uploader.listLabel': 'File terlampir',
|
|
320
|
+
'uploader.acceptHint': 'Diterima: {0}',
|
|
321
|
+
'uploader.maxSizeHint': 'Maksimal {0} per file',
|
|
322
|
+
'uploader.maxFilesHint': 'Maksimal {0} file',
|
|
323
|
+
'uploader.statusPending': 'Menunggu diunggah',
|
|
324
|
+
'uploader.statusUploading': 'Mengunggah {0}%',
|
|
325
|
+
'uploader.statusUploaded': 'Terunggah',
|
|
326
|
+
'uploader.statusFailed': 'Gagal diunggah',
|
|
327
|
+
'uploader.uploadingFile': 'Mengunggah {0}',
|
|
328
|
+
'uploader.uploadFailed': 'Pengunggahan gagal.',
|
|
329
|
+
'uploader.cancelled': 'Pengunggahan dibatalkan.',
|
|
330
|
+
'uploader.cancelFile': 'Batalkan pengunggahan {0}',
|
|
331
|
+
'uploader.retryFile': 'Coba lagi mengunggah {0}',
|
|
332
|
+
'uploader.removeFile': 'Hapus {0}',
|
|
333
|
+
'uploader.retry': 'Coba lagi',
|
|
334
|
+
'uploader.remove': 'Hapus',
|
|
335
|
+
'uploader.rejectType': '{0} bukan jenis file yang diterima.',
|
|
336
|
+
'uploader.rejectSize': '{0} lebih besar dari {1}.',
|
|
337
|
+
'uploader.rejectCount': '{0} melebihi batas {1} file.',
|
|
338
|
+
'uploader.announceAdded': '{0} ditambahkan',
|
|
339
|
+
'uploader.announceRemoved': '{0} dihapus',
|
|
340
|
+
'validation.field.fileType': 'Hanya jenis file berikut yang diterima: {0}.',
|
|
341
|
+
'validation.field.fileSize': 'Setiap file maksimal {0}.',
|
|
342
|
+
'validation.field.maxFiles': 'Lampirkan paling banyak {0} file.',
|
|
343
|
+
'validation.field.uploading': 'Tunggu hingga pengunggahan selesai.',
|
|
344
|
+
'validation.field.uploadFailed': 'Coba lagi atau hapus file yang gagal diunggah.',
|
|
345
|
+
// nxe-timeline.
|
|
346
|
+
'timeline.pending': 'Menunggu',
|
|
347
|
+
// nxe-stepper.
|
|
348
|
+
'stepper.listLabel': 'Langkah',
|
|
349
|
+
'stepper.stepOf': 'Langkah {0} dari {1}: {2}',
|
|
350
|
+
'stepper.completed': 'Selesai',
|
|
351
|
+
'stepper.error': 'Ada kesalahan',
|
|
352
|
+
'stepper.optional': 'Opsional',
|
|
353
|
+
'stepper.blocked': 'Langkah {0} belum lengkap. Perbaiki kolom yang ditandai untuk melanjutkan.',
|
|
354
|
+
'notify.close': 'Tutup notifikasi',
|
|
355
|
+
'shell.skipToContent': 'Lewati ke konten',
|
|
356
|
+
'shell.mainNavigation': 'Navigasi utama',
|
|
357
|
+
'shell.toggleNavigation': 'Buka atau tutup navigasi',
|
|
358
|
+
'shell.closeNavigation': 'Tutup navigasi',
|
|
359
|
+
'shell.collapseSidebar': 'Ciutkan bilah samping',
|
|
360
|
+
'shell.expandSidebar': 'Perluas bilah samping',
|
|
361
|
+
'pivot.grandTotal': 'Total Keseluruhan',
|
|
362
|
+
'pivot.total': 'Total {0}',
|
|
363
|
+
'pivot.noData': 'Tidak ada data',
|
|
364
|
+
'pivot.showFieldChooser': 'Tampilkan Pemilih Field',
|
|
365
|
+
'pivot.chooser.title': 'Pemilih Field',
|
|
366
|
+
'pivot.chooser.allFields': 'Semua Field',
|
|
367
|
+
'pivot.chooser.rowFields': 'Field Baris',
|
|
368
|
+
'pivot.chooser.columnFields': 'Field Kolom',
|
|
369
|
+
'pivot.chooser.filterFields': 'Field Filter',
|
|
370
|
+
'pivot.chooser.dataFields': 'Field Data',
|
|
371
|
+
'pivot.chooser.checkState': 'Status centang',
|
|
372
|
+
'pivot.panel.dropFilter': 'Letakkan Field Filter di Sini',
|
|
373
|
+
'pivot.panel.dropData': 'Letakkan Field Data di Sini',
|
|
374
|
+
'pivot.panel.dropColumn': 'Letakkan Field Kolom di Sini',
|
|
375
|
+
'pivot.panel.dropRow': 'Letakkan Field Baris di Sini',
|
|
376
|
+
'forms.password.show': 'Tampilkan kata sandi',
|
|
377
|
+
'forms.password.hide': 'Sembunyikan kata sandi',
|
|
378
|
+
'forms.textArea.charactersLeft': 'Sisa {0} karakter',
|
|
379
|
+
'forms.textArea.charactersOver': 'Kelebihan {0} karakter dari batas',
|
|
380
|
+
'forms.numberBox.increase': 'Tambah',
|
|
381
|
+
'forms.numberBox.decrease': 'Kurangi',
|
|
382
|
+
'display.noValue': 'Tidak ada nilai',
|
|
383
|
+
'charts.other': 'Lainnya',
|
|
384
|
+
'charts.total': 'Total',
|
|
385
|
+
'charts.empty': 'Tidak ada data untuk ditampilkan',
|
|
386
|
+
'charts.legend': 'Legenda',
|
|
387
|
+
'charts.viewAsTable': 'Lihat sebagai tabel',
|
|
388
|
+
'charts.viewAsChart': 'Lihat sebagai grafik',
|
|
389
|
+
'charts.navigate': 'Data grafik. Gunakan tombol panah untuk berpindah antar titik, Escape untuk keluar.',
|
|
390
|
+
'charts.navigateSegments': 'Segmen grafik. Gunakan tombol panah untuk berpindah antar segmen, Escape untuk keluar.',
|
|
391
|
+
'charts.summary.line': 'Grafik garis, {0} seri, {1} titik dari {2} sampai {3}.',
|
|
392
|
+
'charts.summary.area': 'Grafik area, {0} seri, {1} titik dari {2} sampai {3}.',
|
|
393
|
+
'charts.summary.stackedArea': 'Grafik area bertumpuk, {0} seri, {1} titik dari {2} sampai {3}.',
|
|
394
|
+
'charts.summary.series': '{0}: dari {1} ke {2}, terendah {3}, tertinggi {4}.',
|
|
395
|
+
'charts.summary.donut': 'Grafik donat, {0} segmen, total {1}.',
|
|
396
|
+
'charts.summary.segment': '{0}: {1}, {2}.',
|
|
397
|
+
'charts.table.category': 'Kategori',
|
|
398
|
+
'charts.table.segment': 'Segmen',
|
|
399
|
+
'charts.table.value': 'Nilai',
|
|
400
|
+
'charts.table.share': 'Porsi',
|
|
401
|
+
'avatar.status.online': 'Aktif',
|
|
402
|
+
'avatar.status.away': 'Tidak di tempat',
|
|
403
|
+
'avatar.status.busy': 'Sibuk',
|
|
404
|
+
'avatar.status.offline': 'Luring',
|
|
405
|
+
'avatar.more': '{0} lainnya',
|
|
406
|
+
'breadcrumb.label': 'Jalur navigasi',
|
|
407
|
+
'breadcrumb.more': 'Tampilkan level tersembunyi',
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* The library's initial message configuration, provided by {@link provideNxeMessages}. Absent,
|
|
412
|
+
* the service starts in English with nothing overridden.
|
|
413
|
+
*/
|
|
414
|
+
const NXE_MESSAGES = new InjectionToken('NXE_MESSAGES', {
|
|
415
|
+
providedIn: 'root',
|
|
416
|
+
factory: () => ({}),
|
|
417
|
+
});
|
|
418
|
+
const BUILT_IN = { en: NXE_MESSAGES_EN, id: NXE_MESSAGES_ID };
|
|
419
|
+
const BUILT_IN_SEPARATORS = {
|
|
420
|
+
en: NXE_EN_US_SEPARATORS,
|
|
421
|
+
id: NXE_ID_SEPARATORS,
|
|
422
|
+
};
|
|
423
|
+
/** `'id-ID'` and `'id_ID'` to `'id'`. */
|
|
424
|
+
function languageOf(locale) {
|
|
425
|
+
return locale.split(/[-_]/)[0].toLowerCase();
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Runtime-switchable messages for every `nxe-*` component.
|
|
429
|
+
*
|
|
430
|
+
* State is signals, so a component that reads {@link t} (or {@link messages}) inside a template or
|
|
431
|
+
* a `computed` re-renders when the language changes: no reload, no recreation. That is also what
|
|
432
|
+
* makes it safe under zoneless OnPush, because the template's reactive consumer tracks the read.
|
|
433
|
+
*
|
|
434
|
+
* ```ts
|
|
435
|
+
* const messages = inject(NxeMessageService);
|
|
436
|
+
* messages.use('id');
|
|
437
|
+
* messages.t('grid.pager.info', 1, 5, 42); // 'Halaman 1 dari 5 (42 item)'
|
|
438
|
+
* ```
|
|
439
|
+
*/
|
|
440
|
+
class NxeMessageService {
|
|
441
|
+
config = inject(NXE_MESSAGES);
|
|
442
|
+
localeState = signal(this.config.locale ?? 'en', /* @ts-ignore */
|
|
443
|
+
...(ngDevMode ? [{ debugName: "localeState" }] : /* istanbul ignore next */ []));
|
|
444
|
+
customBundle = signal(null, /* @ts-ignore */
|
|
445
|
+
...(ngDevMode ? [{ debugName: "customBundle" }] : /* istanbul ignore next */ []));
|
|
446
|
+
overrideState = signal(this.config.overrides ?? {}, /* @ts-ignore */
|
|
447
|
+
...(ngDevMode ? [{ debugName: "overrideState" }] : /* istanbul ignore next */ []));
|
|
448
|
+
/** The active locale as the application stated it (`'id'`, `'en-US'`...). */
|
|
449
|
+
locale = this.localeState.asReadonly();
|
|
450
|
+
/** The active language (`'id'` for `'id-ID'`); what picks the bundle. */
|
|
451
|
+
language = computed(() => languageOf(this.localeState()), /* @ts-ignore */
|
|
452
|
+
...(ngDevMode ? [{ debugName: "language" }] : /* istanbul ignore next */ []));
|
|
453
|
+
/** The bundle in force: English, under the language's bundle, under the overrides. */
|
|
454
|
+
messages = computed(() => ({
|
|
455
|
+
...NXE_MESSAGES_EN,
|
|
456
|
+
...(this.customBundle() ?? this.config.bundles?.[this.language()] ?? BUILT_IN[this.language()]),
|
|
457
|
+
...this.overrideState(),
|
|
458
|
+
}), /* @ts-ignore */
|
|
459
|
+
...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
|
|
460
|
+
/** Separators for the active locale; en-US for a language with none. */
|
|
461
|
+
separators = computed(() => {
|
|
462
|
+
const map = typeof this.config.separators === 'object' ? this.config.separators : BUILT_IN_SEPARATORS;
|
|
463
|
+
return map[this.language()] ?? NXE_EN_US_SEPARATORS;
|
|
464
|
+
}, /* @ts-ignore */
|
|
465
|
+
...(ngDevMode ? [{ debugName: "separators" }] : /* istanbul ignore next */ []));
|
|
466
|
+
luxonSync = this.config.luxon !== false;
|
|
467
|
+
luxonBefore = Settings.defaultLocale;
|
|
468
|
+
constructor() {
|
|
469
|
+
if (this.luxonSync && this.config.locale) {
|
|
470
|
+
Settings.defaultLocale = this.config.locale;
|
|
471
|
+
}
|
|
472
|
+
inject(DestroyRef).onDestroy(() => {
|
|
473
|
+
if (this.luxonSync) {
|
|
474
|
+
Settings.defaultLocale = this.luxonBefore;
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
use(localeOrBundle, locale) {
|
|
479
|
+
if (typeof localeOrBundle === 'string') {
|
|
480
|
+
this.customBundle.set(null);
|
|
481
|
+
this.setLocale(localeOrBundle);
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
this.customBundle.set(localeOrBundle);
|
|
485
|
+
if (locale) {
|
|
486
|
+
this.setLocale(locale);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
/** Layer messages over the active bundle. They merge with earlier overrides and survive {@link use}. */
|
|
491
|
+
override(messages) {
|
|
492
|
+
this.overrideState.update((current) => ({ ...current, ...messages }));
|
|
493
|
+
}
|
|
494
|
+
/** Drop every override added since bootstrap; the ones in the configuration stay. */
|
|
495
|
+
clearOverrides() {
|
|
496
|
+
this.overrideState.set(this.config.overrides ?? {});
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* One message, with `{0}`, `{1}`... replaced by `args`. Reactive: reading it in a template or a
|
|
500
|
+
* `computed` subscribes that consumer to the language.
|
|
501
|
+
*/
|
|
502
|
+
t(key, ...args) {
|
|
503
|
+
return NxeMessageService.format(this.messages()[key], args);
|
|
504
|
+
}
|
|
505
|
+
/** Fill `{n}` placeholders; one without an argument is left as written. */
|
|
506
|
+
static format(template, args) {
|
|
507
|
+
return args.length
|
|
508
|
+
? template.replace(/\{(\d+)\}/g, (match, index) => Number(index) < args.length ? String(args[Number(index)]) : match)
|
|
509
|
+
: template;
|
|
510
|
+
}
|
|
511
|
+
setLocale(locale) {
|
|
512
|
+
this.localeState.set(locale);
|
|
513
|
+
if (this.luxonSync) {
|
|
514
|
+
Settings.defaultLocale = locale;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeMessageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
518
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeMessageService, providedIn: 'root' });
|
|
519
|
+
}
|
|
520
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeMessageService, decorators: [{
|
|
521
|
+
type: Injectable,
|
|
522
|
+
args: [{ providedIn: 'root' }]
|
|
523
|
+
}], ctorParameters: () => [] });
|
|
524
|
+
/**
|
|
525
|
+
* Registers the library's messages for the application:
|
|
526
|
+
*
|
|
527
|
+
* ```ts
|
|
528
|
+
* provideNxeMessages({ locale: 'id', overrides: { 'grid.noData': 'Belum ada data' } })
|
|
529
|
+
* ```
|
|
530
|
+
*
|
|
531
|
+
* With `separators` set it also provides {@link NXE_NUMBER_SEPARATORS} as a live view of the active
|
|
532
|
+
* locale's separators, so grids and number boxes go from `1,234.5` to `1.234,5` with the language.
|
|
533
|
+
*/
|
|
534
|
+
function provideNxeMessages(config = {}) {
|
|
535
|
+
return makeEnvironmentProviders([
|
|
536
|
+
{ provide: NXE_MESSAGES, useValue: config },
|
|
537
|
+
...(config.separators
|
|
538
|
+
? [
|
|
539
|
+
{
|
|
540
|
+
provide: NXE_NUMBER_SEPARATORS,
|
|
541
|
+
useFactory: () => {
|
|
542
|
+
const service = inject(NxeMessageService);
|
|
543
|
+
return {
|
|
544
|
+
get group() {
|
|
545
|
+
return service.separators().group;
|
|
546
|
+
},
|
|
547
|
+
get decimal() {
|
|
548
|
+
return service.separators().decimal;
|
|
549
|
+
},
|
|
550
|
+
};
|
|
551
|
+
},
|
|
552
|
+
},
|
|
553
|
+
]
|
|
554
|
+
: []),
|
|
555
|
+
]);
|
|
556
|
+
}
|
|
3
557
|
|
|
4
558
|
/**
|
|
5
559
|
* Application-wide component defaults.
|
|
@@ -70,6 +624,8 @@ class NxeFormControlBase {
|
|
|
70
624
|
ngZone = inject(NgZone);
|
|
71
625
|
/** Application-wide defaults; each editor resolves `binding ?? this ?? literal`. */
|
|
72
626
|
nxeDefaults = inject(NXE_DEFAULT_OPTIONS);
|
|
627
|
+
/** The library's runtime-switchable messages; read them in a template or a computed. */
|
|
628
|
+
nxeMessages = inject(NxeMessageService);
|
|
73
629
|
/**
|
|
74
630
|
* The current value — single source of truth. Two-way bindable as `[(value)]` (DevExtreme
|
|
75
631
|
* parity) and also driven by `writeValue` for `ngModel`/`formControlName`.
|
|
@@ -84,7 +640,28 @@ class NxeFormControlBase {
|
|
|
84
640
|
disabled = computed(() => this.disabledInput() || this.disabledByForm(), /* @ts-ignore */
|
|
85
641
|
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
86
642
|
/** Read-only state (DevExtreme `[readOnly]`). */
|
|
87
|
-
|
|
643
|
+
readOnlyInput = input(false, { ...(ngDevMode ? { debugName: "readOnlyInput" } : /* istanbul ignore next */ {}), alias: 'readOnly', transform: booleanAttribute });
|
|
644
|
+
/**
|
|
645
|
+
* Lower-case spelling of {@link readOnlyInput}: it is the name Signal Forms (`[formField]`)
|
|
646
|
+
* binds a `readonly()` rule to, and the HTML attribute name.
|
|
647
|
+
*/
|
|
648
|
+
readonlyInput = input(false, { ...(ngDevMode ? { debugName: "readonlyInput" } : /* istanbul ignore next */ {}), alias: 'readonly', transform: booleanAttribute });
|
|
649
|
+
/** Effective read-only state: `readOnly` OR `readonly`. */
|
|
650
|
+
readOnly = computed(() => this.readOnlyInput() || this.readonlyInput(), /* @ts-ignore */
|
|
651
|
+
...(ngDevMode ? [{ debugName: "readOnly" }] : /* istanbul ignore next */ []));
|
|
652
|
+
/**
|
|
653
|
+
* Signal Forms hides a field with `hidden()` and binds it by this name; also usable on its own.
|
|
654
|
+
* Hides the host, like `visible=false`.
|
|
655
|
+
*/
|
|
656
|
+
hiddenInput = input(false, { ...(ngDevMode ? { debugName: "hiddenInput" } : /* istanbul ignore next */ {}), alias: 'hidden', transform: booleanAttribute });
|
|
657
|
+
/**
|
|
658
|
+
* Signal Forms state, bound by name when the editor carries `[formField]` (and settable by hand).
|
|
659
|
+
* `invalid` and `errors` put the editor in its invalid state (`nxe-invalid`, `aria-invalid`) once
|
|
660
|
+
* it is {@link touched}; the message text is `nxe-form-field`'s job.
|
|
661
|
+
*/
|
|
662
|
+
invalidInput = input(false, { ...(ngDevMode ? { debugName: "invalidInput" } : /* istanbul ignore next */ {}), alias: 'invalid', transform: booleanAttribute });
|
|
663
|
+
errorsInput = input(undefined, { ...(ngDevMode ? { debugName: "errorsInput" } : /* istanbul ignore next */ {}), alias: 'errors' });
|
|
664
|
+
touchedInput = input(false, { ...(ngDevMode ? { debugName: "touchedInput" } : /* istanbul ignore next */ {}), alias: 'touched', transform: booleanAttribute });
|
|
88
665
|
/** Validation group this editor belongs to (its nested `nxe-validator` registers here). */
|
|
89
666
|
validationGroupInput = input(undefined, { ...(ngDevMode ? { debugName: "validationGroupInput" } : /* istanbul ignore next */ {}), alias: 'validationGroup' });
|
|
90
667
|
/**
|
|
@@ -120,17 +697,33 @@ class NxeFormControlBase {
|
|
|
120
697
|
* Set by the validator rather than declared on the editor: the rule is where the requirement
|
|
121
698
|
* is actually stated, and duplicating it as an input would let the two disagree.
|
|
122
699
|
*/
|
|
123
|
-
|
|
700
|
+
requiredByRule = signal(false, /* @ts-ignore */
|
|
701
|
+
...(ngDevMode ? [{ debugName: "requiredByRule" }] : /* istanbul ignore next */ []));
|
|
702
|
+
/**
|
|
703
|
+
* A `required` input, which is what Signal Forms binds a `required()` rule to, so a form that
|
|
704
|
+
* is not driven by `nxe-validator` can state it too.
|
|
705
|
+
*/
|
|
706
|
+
requiredInput = input(false, { ...(ngDevMode ? { debugName: "requiredInput" } : /* istanbul ignore next */ {}), alias: 'required', transform: booleanAttribute });
|
|
707
|
+
/** Whether the editor is required: the `required` input OR a nested validator's `required` rule. */
|
|
708
|
+
required = computed(() => this.requiredInput() || this.requiredByRule(), /* @ts-ignore */
|
|
124
709
|
...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
|
|
125
710
|
setRequired(value) {
|
|
126
|
-
this.
|
|
711
|
+
this.requiredByRule.set(value);
|
|
127
712
|
}
|
|
128
713
|
/** The current validation error message (set by a nested `nxe-validator`), or null. */
|
|
129
714
|
validationError = signal(null, /* @ts-ignore */
|
|
130
715
|
...(ngDevMode ? [{ debugName: "validationError" }] : /* istanbul ignore next */ []));
|
|
131
|
-
|
|
132
|
-
|
|
716
|
+
touchedByUser = signal(false, /* @ts-ignore */
|
|
717
|
+
...(ngDevMode ? [{ debugName: "touchedByUser" }] : /* istanbul ignore next */ []));
|
|
718
|
+
/** Whether the control has been blurred at least once, or a form says the field was touched. */
|
|
719
|
+
touched = computed(() => this.touchedByUser() || this.touchedInput(), /* @ts-ignore */
|
|
133
720
|
...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
|
|
721
|
+
/**
|
|
722
|
+
* Whether the editor shows its invalid look: a nested validator's message, or a form's
|
|
723
|
+
* `invalid`/`errors` once the field is touched (so an untouched, empty required field is not red).
|
|
724
|
+
*/
|
|
725
|
+
invalidState = computed(() => !!this.validationError() || ((this.invalidInput() || !!this.errorsInput()?.length) && this.touched()), /* @ts-ignore */
|
|
726
|
+
...(ngDevMode ? [{ debugName: "invalidState" }] : /* istanbul ignore next */ []));
|
|
134
727
|
/** Whether the value has been changed by the user at least once. */
|
|
135
728
|
dirty = signal(false, /* @ts-ignore */
|
|
136
729
|
...(ngDevMode ? [{ debugName: "dirty" }] : /* istanbul ignore next */ []));
|
|
@@ -228,6 +821,30 @@ class NxeFormControlBase {
|
|
|
228
821
|
element: this.element,
|
|
229
822
|
});
|
|
230
823
|
}
|
|
824
|
+
/**
|
|
825
|
+
* Commit whatever the user has typed but not yet committed, now and synchronously.
|
|
826
|
+
*
|
|
827
|
+
* With the default `valueChangeEvent` a typed value commits on blur, so a programmatic submit
|
|
828
|
+
* (or anything else that reads the model while the field still has focus) would see the old
|
|
829
|
+
* value. Call this first. It is a no-op when nothing is pending, so it never emits
|
|
830
|
+
* `onValueChanged` twice for one change, and it does not blur the field or mark it touched.
|
|
831
|
+
*/
|
|
832
|
+
commit() {
|
|
833
|
+
this.commitPending(null);
|
|
834
|
+
}
|
|
835
|
+
/** Alias of {@link commit}. */
|
|
836
|
+
flush() {
|
|
837
|
+
this.commit();
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Editors with text that can be typed but not yet committed override this to commit it through
|
|
841
|
+
* {@link commitValue}. The default is for editors that commit on every interaction.
|
|
842
|
+
*/
|
|
843
|
+
commitPending(_event) { }
|
|
844
|
+
/** Whether Enter in this editor commits the pending text. Multi-line editors narrow it. */
|
|
845
|
+
commitsOnEnter(_event) {
|
|
846
|
+
return true;
|
|
847
|
+
}
|
|
231
848
|
/**
|
|
232
849
|
* Re-emit a native event in dx's shape.
|
|
233
850
|
*
|
|
@@ -238,6 +855,14 @@ class NxeFormControlBase {
|
|
|
238
855
|
const payload = { event, component: this, element: this.element };
|
|
239
856
|
switch (kind) {
|
|
240
857
|
case 'keydown':
|
|
858
|
+
// Enter is the key that submits an enclosing <form>, and the browser does that as the
|
|
859
|
+
// default action of this very event. Committing here, before the handlers below and
|
|
860
|
+
// before the default action runs, is what lets `(ngSubmit)` read the typed value.
|
|
861
|
+
if (event.key === 'Enter' &&
|
|
862
|
+
!event.isComposing &&
|
|
863
|
+
this.commitsOnEnter(event)) {
|
|
864
|
+
this.commitPending(event);
|
|
865
|
+
}
|
|
241
866
|
this.onKeyDown.emit(payload);
|
|
242
867
|
if (event.key === 'Enter') {
|
|
243
868
|
this.onEnterKey.emit(payload);
|
|
@@ -273,14 +898,14 @@ class NxeFormControlBase {
|
|
|
273
898
|
}
|
|
274
899
|
/** Mark the control as touched (e.g. on blur), notifying Angular forms exactly once. */
|
|
275
900
|
markAsTouched() {
|
|
276
|
-
if (this.
|
|
901
|
+
if (this.touchedByUser()) {
|
|
277
902
|
return;
|
|
278
903
|
}
|
|
279
|
-
this.
|
|
904
|
+
this.touchedByUser.set(true);
|
|
280
905
|
this.ngZone.run(() => this._onTouched());
|
|
281
906
|
}
|
|
282
907
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeFormControlBase, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
283
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.5", type: NxeFormControlBase, isStandalone: true, inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null },
|
|
908
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.5", type: NxeFormControlBase, isStandalone: true, inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readOnlyInput: { classPropertyName: "readOnlyInput", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null }, readonlyInput: { classPropertyName: "readonlyInput", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, hiddenInput: { classPropertyName: "hiddenInput", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null }, invalidInput: { classPropertyName: "invalidInput", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, errorsInput: { classPropertyName: "errorsInput", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, touchedInput: { classPropertyName: "touchedInput", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, validationGroupInput: { classPropertyName: "validationGroupInput", publicName: "validationGroup", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "aria-labelledby", isSignal: true, isRequired: false, transformFunction: null }, requiredInput: { classPropertyName: "requiredInput", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, valueChangeEvent: { classPropertyName: "valueChangeEvent", publicName: "valueChangeEvent", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, elementAttr: { classPropertyName: "elementAttr", publicName: "elementAttr", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onValueChanged: "onValueChanged", onKeyDown: "onKeyDown", onKeyUp: "onKeyUp", onEnterKey: "onEnterKey", onInput: "onInput", onChange: "onChange", onFocusIn: "onFocusIn", onFocusOut: "onFocusOut" }, host: { listeners: { "keydown": "emitEditorEvent(\"keydown\", $event)", "keyup": "emitEditorEvent(\"keyup\", $event)", "input": "emitEditorEvent(\"input\", $event)", "change": "emitEditorEvent(\"change\", $event)", "focusin": "emitEditorEvent(\"focusin\", $event)", "focusout": "emitEditorEvent(\"focusout\", $event)" }, properties: { "attr.title": "hint() || null", "attr.id": "elementAttrId()", "class": "elementAttrClass()", "style.width": "widthStyle()", "style.display": "visible() && !hiddenInput() ? null : \"none\"", "class.nxe-disabled": "disabled()", "class.nxe-readonly": "readOnly()", "class.nxe-state-readonly": "readOnly()" } }, ngImport: i0 });
|
|
284
909
|
}
|
|
285
910
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeFormControlBase, decorators: [{
|
|
286
911
|
type: Directive,
|
|
@@ -290,7 +915,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
290
915
|
'[attr.id]': 'elementAttrId()',
|
|
291
916
|
'[class]': 'elementAttrClass()',
|
|
292
917
|
'[style.width]': 'widthStyle()',
|
|
293
|
-
'[style.display]': 'visible() ? null : "none"',
|
|
918
|
+
'[style.display]': 'visible() && !hiddenInput() ? null : "none"',
|
|
294
919
|
// Listened for on the HOST, not on each inner control: these all bubble, so one place
|
|
295
920
|
// covers every editor and cannot collide with the handlers an editor already binds on
|
|
296
921
|
// its own input.
|
|
@@ -309,7 +934,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
309
934
|
'[class.nxe-state-readonly]': 'readOnly()',
|
|
310
935
|
},
|
|
311
936
|
}]
|
|
312
|
-
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }],
|
|
937
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readOnlyInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }], readonlyInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], hiddenInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], invalidInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errorsInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touchedInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], validationGroupInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationGroup", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-labelledby", required: false }] }], requiredInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], onValueChanged: [{ type: i0.Output, args: ["onValueChanged"] }], onKeyDown: [{ type: i0.Output, args: ["onKeyDown"] }], onKeyUp: [{ type: i0.Output, args: ["onKeyUp"] }], onEnterKey: [{ type: i0.Output, args: ["onEnterKey"] }], onInput: [{ type: i0.Output, args: ["onInput"] }], onChange: [{ type: i0.Output, args: ["onChange"] }], onFocusIn: [{ type: i0.Output, args: ["onFocusIn"] }], onFocusOut: [{ type: i0.Output, args: ["onFocusOut"] }], valueChangeEvent: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueChangeEvent", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], elementAttr: [{ type: i0.Input, args: [{ isSignal: true, alias: "elementAttr", required: false }] }] } });
|
|
313
938
|
|
|
314
939
|
/**
|
|
315
940
|
* Base for no-template nested-option components (see {@link NxeConfigOption}).
|
|
@@ -498,6 +1123,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
498
1123
|
args: [{ providedIn: 'root' }]
|
|
499
1124
|
}] });
|
|
500
1125
|
|
|
1126
|
+
/**
|
|
1127
|
+
* A byte count as the library writes it in the active language: "512 b", "1.5 Kb", "2.0 Mb" in English,
|
|
1128
|
+
* "512 B", "1,5 KB" in Indonesian (the `uploader.size*` messages). Used by the file input and by the
|
|
1129
|
+
* file-size message of `nxe-form-field`.
|
|
1130
|
+
*/
|
|
1131
|
+
function nxeFormatFileSize(bytes, messages) {
|
|
1132
|
+
if (bytes < 1024) {
|
|
1133
|
+
return messages.t('uploader.sizeB', bytes);
|
|
1134
|
+
}
|
|
1135
|
+
if (bytes < 1024 * 1024) {
|
|
1136
|
+
return messages.t('uploader.sizeKb', (bytes / 1024).toFixed(1));
|
|
1137
|
+
}
|
|
1138
|
+
return messages.t('uploader.sizeMb', (bytes / 1024 / 1024).toFixed(1));
|
|
1139
|
+
}
|
|
1140
|
+
|
|
501
1141
|
// @nexterajs/ui — core abstractions shared by every component.
|
|
502
1142
|
|
|
503
1143
|
// @nexterajs/ui/core — public API.
|
|
@@ -506,5 +1146,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
506
1146
|
* Generated bundle index. Do not edit.
|
|
507
1147
|
*/
|
|
508
1148
|
|
|
509
|
-
export { NXE_DEFAULT_OPTIONS, NXE_OPTION, NXE_VALIDATION_GROUP, NXE_VALIDATION_TARGET, NxeConfigOptionBase, NxeFormControlBase, NxeTemplateDirective, NxeValidationGroupRegistry, coerceArray, nxeOptionalBoolean, nxeResolveExpr, nxeResolveText };
|
|
1149
|
+
export { NXE_DEFAULT_OPTIONS, NXE_MESSAGES, NXE_MESSAGES_EN, NXE_MESSAGES_ID, NXE_OPTION, NXE_VALIDATION_GROUP, NXE_VALIDATION_TARGET, NxeConfigOptionBase, NxeFormControlBase, NxeMessageService, NxeTemplateDirective, NxeValidationGroupRegistry, coerceArray, nxeFormatFileSize, nxeOptionalBoolean, nxeResolveExpr, nxeResolveText, provideNxeMessages };
|
|
510
1150
|
//# sourceMappingURL=nexterajs-ui-core.mjs.map
|