@onehat/data 1.21.19 → 1.21.21
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/cypress/e2e/Property/Mixed.cy.js +105 -0
- package/cypress/e2e/Property/Property.cy.js +7 -1
- package/cypress/e2e/Repository/Ajax.cy.js +15 -0
- package/cypress/support/e2e.js +1 -0
- package/cypress/support/index.js +20 -0
- package/cypress.config.js +18 -16
- package/package.json +18 -19
- package/src/Entity/Entity.js +32 -21
- package/src/OneHatData.js +7 -5
- package/src/Property/Currency.js +1 -1
- package/src/Property/Json.js +1 -1
- package/src/Property/Mixed.js +458 -0
- package/src/Property/Property.js +21 -0
- package/src/Property/index.js +2 -0
- package/src/Reader/index.js +2 -2
- package/src/Repository/Ajax.js +241 -28
- package/src/Repository/OneBuild.js +73 -434
- package/src/Repository/Repository.js +41 -94
- package/src/Repository/Tree.js +452 -0
- package/src/Repository/index.js +2 -0
- package/src/Schema/Schema.js +6 -9
- package/src/Writer/index.js +2 -2
- package/src/Repository/OneBuild2.js +0 -953
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/** @module Property */
|
|
2
|
+
|
|
3
|
+
import EventEmitter from '@onehat/events';
|
|
4
|
+
import Base64Property from './Base64.js';
|
|
5
|
+
import BooleanProperty from './Boolean.js';
|
|
6
|
+
import CurrencyProperty from './Currency.js';
|
|
7
|
+
import DateProperty from './Date.js';
|
|
8
|
+
import DateTimeProperty from './DateTime.js';
|
|
9
|
+
import FileProperty from './File.js';
|
|
10
|
+
import FloatProperty from './Float.js';
|
|
11
|
+
import IntegerProperty from './Integer.js';
|
|
12
|
+
import JsonProperty, { TagProperty } from './Json.js';
|
|
13
|
+
import PercentProperty from './Percent.js';
|
|
14
|
+
import PercentIntProperty from './PercentInt.js';
|
|
15
|
+
import StringProperty from './String.js';
|
|
16
|
+
import TimeProperty from './Time.js';
|
|
17
|
+
import UuidProperty from './Uuid.js';
|
|
18
|
+
import _ from 'lodash';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Class represents a Property that can store values of multiple types.
|
|
22
|
+
* The actual type is determined dynamically based on the value being set.
|
|
23
|
+
*
|
|
24
|
+
* Usage: { name: 'date', title: 'Date', type: 'mixed', types: ['date', 'string'], },
|
|
25
|
+
* This is primarily used to allow a field that's normally one PropertyType
|
|
26
|
+
* to also accept string values (e.g. values like '2025-01-01' or 'N/A').
|
|
27
|
+
*
|
|
28
|
+
* @extends Property
|
|
29
|
+
*/
|
|
30
|
+
export default class MixedProperty extends EventEmitter {
|
|
31
|
+
|
|
32
|
+
constructor(config = {}, entity) {
|
|
33
|
+
config = _.merge({}, MixedProperty.defaults, config);
|
|
34
|
+
|
|
35
|
+
if (!config.types || !Array.isArray(config.types) || config.types.length < 2) {
|
|
36
|
+
throw Error('MixedProperty requires a types array with at least two types in its configuration.');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
super(config, entity);
|
|
40
|
+
|
|
41
|
+
this.registerEvents([
|
|
42
|
+
'change',
|
|
43
|
+
'changeValidity',
|
|
44
|
+
'destroy',
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
this.types = config.types;
|
|
48
|
+
this.currentType = this.types[0].type || this.types[0];
|
|
49
|
+
this.internalProperties = new Map();
|
|
50
|
+
|
|
51
|
+
this._createInternalProperties();
|
|
52
|
+
|
|
53
|
+
this.currentProperty = this.internalProperties.get(this.currentType);
|
|
54
|
+
|
|
55
|
+
this._proxy = new Proxy(this, {
|
|
56
|
+
get(mixedProperty, prop, receiver) {
|
|
57
|
+
if (prop in mixedProperty) {
|
|
58
|
+
const value = mixedProperty[prop];
|
|
59
|
+
if (typeof value === 'function') {
|
|
60
|
+
return value.bind(mixedProperty);
|
|
61
|
+
}
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
if (mixedProperty.currentProperty && prop in mixedProperty.currentProperty) {
|
|
65
|
+
const value = mixedProperty.currentProperty[prop];
|
|
66
|
+
if (typeof value === 'function') {
|
|
67
|
+
return value.bind(mixedProperty.currentProperty);
|
|
68
|
+
}
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
},
|
|
73
|
+
has(mixedProperty, prop) {
|
|
74
|
+
if (prop in mixedProperty) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
return mixedProperty.currentProperty ? prop in mixedProperty.currentProperty : false;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return this._proxy;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Creates internal Property instances for each configured type
|
|
86
|
+
* @private
|
|
87
|
+
*/
|
|
88
|
+
_createInternalProperties() {
|
|
89
|
+
_.each(this.types, (typeConfig) => {
|
|
90
|
+
let typeName,
|
|
91
|
+
propertyConfig;
|
|
92
|
+
if (typeof typeConfig === 'string') {
|
|
93
|
+
typeName = typeConfig;
|
|
94
|
+
propertyConfig = {};
|
|
95
|
+
} else {
|
|
96
|
+
typeName = typeConfig.type;
|
|
97
|
+
propertyConfig = _.omit(typeConfig, 'type');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const
|
|
101
|
+
PropertyClass = this._getPropertyClass(typeName),
|
|
102
|
+
|
|
103
|
+
// Create a clean config for this internal property
|
|
104
|
+
baseConfig = _.omit(this.config, ['types', 'defaultType']), // Start with base config but exclude Mixed-specific settings
|
|
105
|
+
mergedConfig = _.merge({}, baseConfig, propertyConfig, { // Merge with type-specific config, giving precedence to type-specific settings
|
|
106
|
+
name: this.name,
|
|
107
|
+
}),
|
|
108
|
+
property = new PropertyClass(mergedConfig, this.entity);
|
|
109
|
+
|
|
110
|
+
this._setupEventForwarding(property);
|
|
111
|
+
|
|
112
|
+
this.internalProperties.set(typeName, property);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Gets the Property class for a given type name
|
|
118
|
+
* @param {string} typeName - Name of the property type
|
|
119
|
+
* @returns {Class} Property class
|
|
120
|
+
* @private
|
|
121
|
+
*/
|
|
122
|
+
_getPropertyClass(typeName) {
|
|
123
|
+
const
|
|
124
|
+
typeMap = {
|
|
125
|
+
base64: Base64Property,
|
|
126
|
+
bool: BooleanProperty,
|
|
127
|
+
currency: CurrencyProperty,
|
|
128
|
+
date: DateProperty,
|
|
129
|
+
datetime: DateTimeProperty,
|
|
130
|
+
file: FileProperty,
|
|
131
|
+
float: FloatProperty,
|
|
132
|
+
int: IntegerProperty,
|
|
133
|
+
json: JsonProperty,
|
|
134
|
+
percent: PercentProperty,
|
|
135
|
+
percentint: PercentIntProperty,
|
|
136
|
+
string: StringProperty,
|
|
137
|
+
tag: TagProperty,
|
|
138
|
+
time: TimeProperty,
|
|
139
|
+
uuid: UuidProperty,
|
|
140
|
+
},
|
|
141
|
+
PropertyClass = typeMap[typeName.toLowerCase()];
|
|
142
|
+
|
|
143
|
+
if (!PropertyClass) {
|
|
144
|
+
const availableTypes = Object.keys(typeMap).join(', ');
|
|
145
|
+
throw new Error(`Unknown property type: '${typeName}'. Available types are: ${availableTypes}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return typeMap[typeName.toLowerCase()];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Sets up event forwarding from an internal property to this MixedProperty
|
|
153
|
+
* @param {Property} property - Internal property to forward events from
|
|
154
|
+
* @private
|
|
155
|
+
*/
|
|
156
|
+
_setupEventForwarding(property) {
|
|
157
|
+
// Forward all events from internal property to MixedProperty
|
|
158
|
+
const forwardEvent = (eventName, ...args) => {
|
|
159
|
+
if (property === this.currentProperty) {
|
|
160
|
+
// Replace the property reference in args with this MixedProperty
|
|
161
|
+
const modifiedArgs = args.map(arg => arg === property ? this : arg);
|
|
162
|
+
this.emit(eventName, ...modifiedArgs);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Listen to all registered events on the internal property
|
|
167
|
+
property.getRegisteredEvents().forEach((eventName) => {
|
|
168
|
+
property.on(eventName, (...args) => forwardEvent(eventName, ...args));
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Detects the appropriate property type for a given value
|
|
174
|
+
* @param {any} value - Value to analyze
|
|
175
|
+
* @returns {string} Detected type name
|
|
176
|
+
* @private
|
|
177
|
+
*/
|
|
178
|
+
_detectType(value) {
|
|
179
|
+
if (_.isNil(value)) {
|
|
180
|
+
return this.currentType || this.defaultType || (typeof this.types[0] === 'string' ? this.types[0] : this.types[0].type);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Get available type names from config
|
|
184
|
+
const availableTypes = this.types.map(typeConfig =>
|
|
185
|
+
typeof typeConfig === 'string' ? typeConfig : typeConfig.type
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
// Define precedence order - more specific types first
|
|
189
|
+
const precedenceOrder = [
|
|
190
|
+
'bool', // Most specific - only true/false/1/0
|
|
191
|
+
'int', // Integers
|
|
192
|
+
'float', // Floating point numbers
|
|
193
|
+
'currency', // Currency values
|
|
194
|
+
'percent', // Percentage values
|
|
195
|
+
'percentint',// Integer percentages
|
|
196
|
+
'date', // Date values
|
|
197
|
+
'datetime', // DateTime values
|
|
198
|
+
'time', // Time values
|
|
199
|
+
'uuid', // UUID format
|
|
200
|
+
'base64', // Base64 encoded data
|
|
201
|
+
'json', // JSON objects/arrays
|
|
202
|
+
'tag', // Tag format
|
|
203
|
+
'file', // File data
|
|
204
|
+
'string' // Most general - accepts anything
|
|
205
|
+
];
|
|
206
|
+
|
|
207
|
+
// Try types in precedence order, but only if they're in the available types
|
|
208
|
+
let typeName;
|
|
209
|
+
for(typeName of precedenceOrder) {
|
|
210
|
+
if (availableTypes.includes(typeName)) {
|
|
211
|
+
const property = this.internalProperties.get(typeName);
|
|
212
|
+
if (property && this._canParseValue(property, value)) {
|
|
213
|
+
return typeName;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
throw new Error(`No configured property type could handle value: ${value}. Available types: ${availableTypes.join(', ')}`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Tests if a property can successfully parse a value
|
|
223
|
+
* @param {Property} property - Property to test
|
|
224
|
+
* @param {any} value - Value to test
|
|
225
|
+
* @returns {boolean} Whether the property can parse the value
|
|
226
|
+
* @private
|
|
227
|
+
*/
|
|
228
|
+
_canParseValue(property, value) {
|
|
229
|
+
try {
|
|
230
|
+
switch(property.constructor.type) {
|
|
231
|
+
case 'bool':
|
|
232
|
+
return this._isBooleanValue(value);
|
|
233
|
+
case 'int':
|
|
234
|
+
return this._isIntegerValue(value);
|
|
235
|
+
case 'float':
|
|
236
|
+
return this._isFloatValue(value);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
property.parse(value);
|
|
240
|
+
return true;
|
|
241
|
+
} catch (error) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Checks if a value represents a boolean
|
|
248
|
+
* @param {any} value - Value to check
|
|
249
|
+
* @returns {boolean} Whether the value is boolean-like
|
|
250
|
+
* @private
|
|
251
|
+
*/
|
|
252
|
+
_isBooleanValue(value) {
|
|
253
|
+
switch(typeof value) {
|
|
254
|
+
case 'boolean':
|
|
255
|
+
return true;
|
|
256
|
+
case 'number':
|
|
257
|
+
return value === 0 || value === 1;
|
|
258
|
+
case 'string':
|
|
259
|
+
const lower = value.toLowerCase().trim();
|
|
260
|
+
return ['true', 'false', '1', '0'].includes(lower);
|
|
261
|
+
default:
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Checks if a value represents an integer
|
|
268
|
+
* @param {any} value - Value to check
|
|
269
|
+
* @returns {boolean} Whether the value is integer-like
|
|
270
|
+
* @private
|
|
271
|
+
*/
|
|
272
|
+
_isIntegerValue(value) {
|
|
273
|
+
switch(typeof value) {
|
|
274
|
+
case 'bigint':
|
|
275
|
+
return true;
|
|
276
|
+
case 'number':
|
|
277
|
+
return Number.isInteger(value);
|
|
278
|
+
case 'string':
|
|
279
|
+
const trimmed = value.trim();
|
|
280
|
+
if (trimmed === '') {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
const num = Number(trimmed);
|
|
284
|
+
return !isNaN(num) && Number.isInteger(num) && String(num) === trimmed;
|
|
285
|
+
}
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Checks if a value represents a float
|
|
291
|
+
* @param {any} value - Value to check
|
|
292
|
+
* @returns {boolean} Whether the value is float-like
|
|
293
|
+
* @private
|
|
294
|
+
*/
|
|
295
|
+
_isFloatValue(value) {
|
|
296
|
+
switch(typeof value) {
|
|
297
|
+
case 'number':
|
|
298
|
+
return !Number.isInteger(value) && isFinite(value);
|
|
299
|
+
case 'string':
|
|
300
|
+
const trimmed = value.trim();
|
|
301
|
+
if (trimmed === '') return false;
|
|
302
|
+
const num = Number(trimmed);
|
|
303
|
+
return !isNaN(num) && isFinite(num) && !Number.isInteger(num);
|
|
304
|
+
}
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Sets the value and determines the appropriate type
|
|
310
|
+
* @param {any} value - Value to set
|
|
311
|
+
*/
|
|
312
|
+
setValue(value) {
|
|
313
|
+
const detectedType = this._detectType(value);
|
|
314
|
+
if (this.currentType !== detectedType) {
|
|
315
|
+
this.currentType = detectedType;
|
|
316
|
+
this.currentProperty = this.internalProperties.get(this.currentType);
|
|
317
|
+
}
|
|
318
|
+
this.currentProperty.setValue(value);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Gets the current type name
|
|
323
|
+
* @returns {string} Current type name
|
|
324
|
+
*/
|
|
325
|
+
getCurrentType() {
|
|
326
|
+
return this.currentProperty.constructor.type;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Gets the className of this Property type.
|
|
331
|
+
* @return {string} className
|
|
332
|
+
*/
|
|
333
|
+
getClassName() {
|
|
334
|
+
return this.constructor.className;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Gets the internal property for a specific type
|
|
339
|
+
* @param {string} typeName - Type name
|
|
340
|
+
* @returns {Property} Internal property
|
|
341
|
+
*/
|
|
342
|
+
getInternalProperty(typeName) {
|
|
343
|
+
return this.internalProperties.get(typeName);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ______ __
|
|
347
|
+
// / ____/ _____ ____ / /______
|
|
348
|
+
// / __/ | | / / _ \/ __ \/ __/ ___/
|
|
349
|
+
// / /___ | |/ / __/ / / / /_(__ )
|
|
350
|
+
// /_____/ |___/\___/_/ /_/\__/____/
|
|
351
|
+
//
|
|
352
|
+
// (Override the EventEmitter methods to forward listener management to internal properties)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Add event listener to MixedProperty only
|
|
357
|
+
* Events from internal properties are forwarded via _setupEventForwarding
|
|
358
|
+
*/
|
|
359
|
+
on(eventName, listener) {
|
|
360
|
+
return super.on(eventName, listener);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Add one-time event listener to MixedProperty only
|
|
365
|
+
*/
|
|
366
|
+
once(eventName, listener) {
|
|
367
|
+
return super.once(eventName, listener);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Remove event listener from MixedProperty only
|
|
372
|
+
*/
|
|
373
|
+
off(eventName, listener) {
|
|
374
|
+
return super.off(eventName, listener);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Remove all event listeners from MixedProperty and all internal properties
|
|
379
|
+
*/
|
|
380
|
+
removeAllListeners(eventName) {
|
|
381
|
+
// Remove all listeners from this MixedProperty
|
|
382
|
+
super.removeAllListeners(eventName);
|
|
383
|
+
|
|
384
|
+
// Remove all listeners from internal properties
|
|
385
|
+
this.internalProperties.forEach(property => {
|
|
386
|
+
property.removeAllListeners(eventName);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
return this;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Destroys all internal properties
|
|
394
|
+
*/
|
|
395
|
+
destroy() {
|
|
396
|
+
// Clean up listener mappings
|
|
397
|
+
if (this._listenerMappings) {
|
|
398
|
+
this._listenerMappings.clear();
|
|
399
|
+
this._listenerMappings = null;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
this.internalProperties.forEach((property) => property.destroy());
|
|
403
|
+
this.internalProperties.clear();
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Returns the default configuration for this PropertyType.
|
|
408
|
+
* For MixedProperty, this delegates to the first configured type's defaults.
|
|
409
|
+
* @param {Object} defaults - The default configuration to merge with (should include 'types' array)
|
|
410
|
+
* @returns {Object} The default configuration
|
|
411
|
+
*/
|
|
412
|
+
static getStaticDefaults(defaults = {}) {
|
|
413
|
+
const mixedPropertyDefaults = {
|
|
414
|
+
name: null,
|
|
415
|
+
allowNull: true,
|
|
416
|
+
depends: null,
|
|
417
|
+
mapping: null,
|
|
418
|
+
submitAsString: false,
|
|
419
|
+
isSortable: true,
|
|
420
|
+
isTempId: false,
|
|
421
|
+
isVirtual: false,
|
|
422
|
+
title: null,
|
|
423
|
+
tooltip: null,
|
|
424
|
+
fieldGroup: null,
|
|
425
|
+
isForeignModel: false,
|
|
426
|
+
filterType: null,
|
|
427
|
+
isFilteringDisabled: false,
|
|
428
|
+
viewerType: null,
|
|
429
|
+
editorType: null,
|
|
430
|
+
isEditingDisabled: false,
|
|
431
|
+
defaultValue: null,
|
|
432
|
+
formatter: null,
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
// If types are configured, use the first type's default value
|
|
436
|
+
if (defaults.types && Array.isArray(defaults.types) && defaults.types.length > 0) {
|
|
437
|
+
const
|
|
438
|
+
firstTypeConfig = defaults.types[0],
|
|
439
|
+
firstTypeName = typeof firstTypeConfig === 'string' ? firstTypeConfig : firstTypeConfig.type;
|
|
440
|
+
if (firstTypeName) {
|
|
441
|
+
try {
|
|
442
|
+
const PropertyClass = MixedProperty.prototype._getPropertyClass(firstTypeName);
|
|
443
|
+
const typeDefaults = PropertyClass.getStaticDefaults ? PropertyClass.getStaticDefaults() : {};
|
|
444
|
+
if (typeDefaults.defaultValue !== undefined) {
|
|
445
|
+
mixedPropertyDefaults.defaultValue = typeDefaults.defaultValue;
|
|
446
|
+
}
|
|
447
|
+
} catch (e) {
|
|
448
|
+
// If we can't get the property class, just use null default
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return _.merge({}, defaults);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
MixedProperty.className = 'Mixed';
|
|
458
|
+
MixedProperty.type = 'mixed';
|
package/src/Property/Property.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** @module Property */
|
|
2
2
|
|
|
3
3
|
import EventEmitter from '@onehat/events';
|
|
4
|
+
import Formatters from '../Util/Formatters.js';
|
|
4
5
|
import _ from 'lodash';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -137,6 +138,12 @@ export default class Property extends EventEmitter {
|
|
|
137
138
|
*/
|
|
138
139
|
defaultValue: null,
|
|
139
140
|
|
|
141
|
+
/**
|
|
142
|
+
* @member {string} formatter - The name of the formatter to use for this property
|
|
143
|
+
* @private
|
|
144
|
+
*/
|
|
145
|
+
formatter: null,
|
|
146
|
+
|
|
140
147
|
};
|
|
141
148
|
|
|
142
149
|
/**
|
|
@@ -278,6 +285,9 @@ export default class Property extends EventEmitter {
|
|
|
278
285
|
if (this.isDestroyed) {
|
|
279
286
|
throw Error('this.getDisplayValue is no longer valid. Property has been destroyed.');
|
|
280
287
|
}
|
|
288
|
+
if (this.formatter) {
|
|
289
|
+
return Formatters[this.formatter](this.parsedValue);
|
|
290
|
+
}
|
|
281
291
|
return this.parsedValue;
|
|
282
292
|
}
|
|
283
293
|
|
|
@@ -428,6 +438,17 @@ export default class Property extends EventEmitter {
|
|
|
428
438
|
return value;
|
|
429
439
|
}
|
|
430
440
|
|
|
441
|
+
/**
|
|
442
|
+
* Sets the formatter for this Property.
|
|
443
|
+
* @param {*} formatter
|
|
444
|
+
*/
|
|
445
|
+
setFormatter(formatter) {
|
|
446
|
+
if (this.isDestroyed) {
|
|
447
|
+
throw Error('this.setFormatter is no longer valid. Property has been destroyed.');
|
|
448
|
+
}
|
|
449
|
+
this.formatter = formatter;
|
|
450
|
+
}
|
|
451
|
+
|
|
431
452
|
|
|
432
453
|
|
|
433
454
|
|
package/src/Property/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import FileProperty from './File.js';
|
|
|
9
9
|
import FloatProperty from './Float.js';
|
|
10
10
|
import IntegerProperty from './Integer.js';
|
|
11
11
|
import JsonProperty, { TagProperty } from './Json.js';
|
|
12
|
+
import MixedProperty from './Mixed.js';
|
|
12
13
|
import PercentProperty from './Percent.js';
|
|
13
14
|
import PercentIntProperty from './PercentInt.js';
|
|
14
15
|
import Property from './Property.js';
|
|
@@ -26,6 +27,7 @@ const PropertyTypes = {
|
|
|
26
27
|
[FloatProperty.type]: FloatProperty,
|
|
27
28
|
[IntegerProperty.type]: IntegerProperty,
|
|
28
29
|
[JsonProperty.type]: JsonProperty,
|
|
30
|
+
[MixedProperty.type]: MixedProperty,
|
|
29
31
|
[PercentProperty.type]: PercentProperty,
|
|
30
32
|
[PercentIntProperty.type]: PercentIntProperty,
|
|
31
33
|
[Property.type]: Property,
|
package/src/Reader/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/** @module Reader */
|
|
2
2
|
|
|
3
3
|
import JsonReader from './JsonReader.js';
|
|
4
|
-
import XmlReader from './XmlReader.js';
|
|
4
|
+
// import XmlReader from './XmlReader.js';
|
|
5
5
|
|
|
6
6
|
const ReaderTypes = {
|
|
7
7
|
[JsonReader.type]: JsonReader,
|
|
8
|
-
[XmlReader.type]: XmlReader,
|
|
8
|
+
// [XmlReader.type]: XmlReader,
|
|
9
9
|
};
|
|
10
10
|
export default ReaderTypes;
|