@fto-consult/expo-ui 2.7.13 → 2.9.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.
@@ -1,552 +0,0 @@
1
-
2
- import {isNonNullString,defaultStr,defaultArray,isObjOrArray,isObj} from "$utils";
3
- import DateLib from "$date";
4
- import mangoParser from "mongo-parse";
5
-
6
- export const filterTextTypes = ['text','number','email','search','tel','url','password',"id","idfield",'piecefield','piece'];
7
-
8
- export const regexActions = {
9
- $regexequals : 'Egal à',
10
- $regexcontains : 'Contient',
11
- $regexnotcontains : 'Ne contient pas',
12
- $regexnotequals : 'Différent de',
13
- $regexstartswith : 'Commence par',
14
- $regexendswith : 'Se termine par'
15
- }
16
- export const escapeRegexChars = (value)=>{
17
- //if(value === undefined || value ===null || value === '' || value ==='undefined') return '';
18
- //value+='';
19
- if(!isNonNullString(value)) return '';
20
- let escapeChars = ['!', '^', '$', '(', ')', '[', ']', '{', '}', '?', '+', '*', '.', '/', '\\', '|']
21
- for(var i in escapeChars){
22
- value.replace(escapeChars[i],'\\'+escapeChars[i]);
23
- }
24
- return value.toString();
25
- }
26
-
27
- export const getFilterStateValues = (state)=>{
28
- state = defaultObj(state);
29
- const r = {};
30
- ['defaultValue','action','actions','operator','operators','ignoreCase','value','manualRun'].map((v)=>{
31
- r[v] = state[v];
32
- })
33
- return r;
34
-
35
- }
36
-
37
- /**** si le filtre peut être pris en compte */
38
- export const canHandleFilter = (f)=>{
39
- if(!isObj(f)) return false;
40
- if(!isNonNullString(f.operator) || !isNonNullString(f.action)) {
41
- return false;
42
- }
43
- if(f.value === undefined || f.value === 'undefined' || f.value ==='' || f.value ===null) {
44
- return false;
45
- }
46
- if(f.value instanceof RegExp){
47
- return true;
48
- }
49
- if(isObjOrArray(f.value) && Object.size(f.value,true) <=0) return false;
50
- return true;
51
-
52
- }
53
-
54
- export const regexExpressions = {
55
- notcontains : {
56
- left : "^((?!(",
57
- right : ")).)*$",
58
- /***@see : https://www.w3schools.com/sql/sql_like.asp */
59
- sql : (string)=>{
60
- return "%"+defaultStr(string).ltrim("%").rtrim("%")+"%";
61
- }
62
- },
63
- notequals : {
64
- left : "^(?!",
65
- right : "$).*$",
66
- },
67
- equals : {
68
- left : "/^",
69
- right : '$/'
70
- },
71
- startswith : {
72
- left : "/^",
73
- right : '/',
74
- sql : (string)=>{
75
- return defaultStr(string).rtrim("%")+"%";
76
- }
77
- },
78
- endswith : {
79
- left : "",
80
- right : '$/',
81
- sql : (string)=>{
82
- return "%"+defaultStr(string).ltrim("%");
83
- }
84
- },
85
- contains : {
86
- left : "/",
87
- right : '/',
88
- sql : (string)=>{
89
- return "%"+defaultStr(string).ltrim("%").rtrim("%")+"%";
90
- }
91
- },
92
- }
93
- /**** prend en paramètre une chaine de caractère et un nom d'opérateur et génère
94
- * la chaine de caracètre mango regex correspondant
95
- */
96
- const toMangoRegex = (val,operator)=>{
97
- if(!isNonNullString(val) || !isNonNullString(operator) || !regexExpressions[operator]) return "";
98
- const rOp = regexExpressions[operator];
99
- return rOp.left+escapeRegexChars(val)+rOp.right;
100
- }
101
- export const regexParser = {
102
- equals : (val)=> {
103
- return toMangoRegex(val,"equals");
104
- },
105
- startswith : (val) => {
106
- return toMangoRegex(val,"startswith");
107
- },
108
- endswith : (val) => {
109
- return toMangoRegex(val,"endswith");
110
- },
111
- contains : (val) => {
112
- return toMangoRegex(val,"contains");
113
- },
114
- notequals : (val) => {
115
- return toMangoRegex(val,"notequals");
116
- },
117
- notcontains : (val) =>{
118
- return toMangoRegex(val,"notcontains");
119
- }
120
- }
121
- /*** prend une expression regulière et le convertis en operateur, valeur
122
- * @param {string} la valeur otenue au format regex
123
- */
124
- export const regexDeparser = (regexValue)=>{
125
- if(!regexValue) return null;
126
- regexValue = regexValue.toString();
127
- if(regexValue.endsWith("/i")){
128
- regexValue = regexValue.rtrim("/i")+"/";
129
- }
130
- if(!isNonNullString(regexValue)) return null;
131
- for(let i in regexExpressions){
132
- const p = regexExpressions[i];
133
- let rVal = regexValue.ltrim("/").rtrim("/");
134
- const pLeft = p.left.ltrim("/"),pRight = p.right.rtrim("/");
135
- if(rVal.startsWith(pLeft) && rVal.endsWith(pRight)){
136
- return {
137
- operator : i,
138
- value : rVal.ltrim(pLeft).rtrim(pRight),
139
- regexValue : rVal,
140
- }
141
- }
142
- }
143
- return null;
144
- }
145
- export const matchPouchDBOperator = (operator,value)=>{
146
- if(!isNonNullString(operator)) return null;
147
- if(!(value) && !isDecimal(value)) return null;
148
- switch(operator){
149
- case "MATCH":
150
- if(isDecimal(value)) value +="";
151
- return {operator:"$regex",value:RegExp(defaultStr(regexParser.contains(value)).ltrim("/").rtrim("/"),'i')}
152
- case "NOTMATCH":
153
- if(isDecimal(value)) value +="";
154
- return {operator:"$regex",value:RegExp(defaultStr(regexParser.notcontains(value)).ltrim("/").rtrim("/"),'i')}
155
- case "EQ" :
156
- return {operator:'$eq',value}
157
- case "NEQ" :
158
- return {operator:"$ne",value}
159
- case "GT":
160
- return {operator:"$gt",value}
161
- case "GTE":
162
- return {operator:"$gte",value}
163
- case "LT":
164
- return {operator:"$lt",value}
165
- case "LTE":
166
- return {operator:"$lte",value}
167
- case "IN":
168
- return {operator:"$in",value}
169
- case "NOT IN":
170
- return {operator:"$in",value}
171
- }
172
- }
173
-
174
- let Operators = {
175
- NONE: null,
176
- MATCH: {
177
- name: 'contient',
178
- func: function(value, term) {
179
- if(value) {
180
- return value.toString().search(utils.isRegExp(term) ? term : new RegExp(term, 'i')) >= 0;
181
- } else {
182
- return !(!!term);
183
- }
184
- },
185
- regexpSupported: true
186
- },
187
- NOTMATCH: {
188
- name: 'ne contient pas',
189
- func: function(value, term) {
190
- if(value) {
191
- return value.toString().search(utils.isRegExp(term) ? term : new RegExp(term, 'i')) < 0;
192
- } else {
193
- return !!term;
194
- }
195
- },
196
- regexpSupported: true
197
- },
198
- EQ: {
199
- name: '=',
200
- func: function(value, term) {
201
- return value == term;
202
- },
203
- regexpSupported: false
204
- },
205
- NEQ: {
206
- name: '<>',
207
- func: function(value, term) {
208
- return value != term;
209
- },
210
- regexpSupported: false
211
- },
212
- GT: {
213
- name: '>',
214
- func: function(value, term) {
215
- return value > term;
216
- },
217
- regexpSupported: false
218
- },
219
- GTE: {
220
- name: '>=',
221
- func: function(value, term) {
222
- return value >= term;
223
- },
224
- regexpSupported: false
225
- },
226
- LT: {
227
- name: '<',
228
- func: function(value, term) {
229
- return value < term;
230
- },
231
- regexpSupported: false
232
- },
233
- LTE: {
234
- name: '<=',
235
- func: function(value, term) {
236
- return value <= term;
237
- },
238
- regexpSupported: false
239
- }
240
- };
241
- const utils = {
242
- ALL: '#All#',
243
- NONE: '#None#',
244
- BLANK: '#Blank#"',
245
- expressionFilter : function(operator, term, staticValue, excludeStatic) {
246
- var self = this;
247
-
248
- this.operator = Operators.get(operator);
249
- this.regexpMode = false;
250
- this.term = term || null;
251
- if(this.term && this.operator && this.operator.regexpSupported) {
252
- if(utils.isRegExp(this.term)) {
253
- this.regexpMode = true;
254
- if(!this.term.ignoreCase) {
255
- this.term = new RegExp(this.term.source, 'i');
256
- }
257
- }
258
- }
259
-
260
- this.staticValue = staticValue;
261
- this.excludeStatic = excludeStatic;
262
-
263
- this.test = function(value) {
264
- if(Array.isArray(self.staticValue)) {
265
- var found = self.staticValue.indexOf(value) >= 0;
266
- return (self.excludeStatic && !found) || (!self.excludeStatic && found);
267
- } else if(self.term) {
268
- return self.operator.func(value, self.term);
269
- } else if(self.staticValue === true || self.staticValue === utils.ALL) {
270
- return true;
271
- } else if(self.staticValue === false || self.staticValue === utils.NONE) {
272
- return false;
273
- } else {
274
- return true;
275
- }
276
- };
277
-
278
- this.isAlwaysTrue = function() {
279
- return !(self.term || Array.isArray(self.staticValue) || self.staticValue === utils.NONE || self.staticValue === false);
280
- };
281
- },
282
- defaultOperator : 'MATCH',
283
- Operators,
284
- constants : {},
285
- items : {
286
- EQ: Operators.EQ.name,
287
- MATCH : Operators.MATCH.name,
288
- NOTMATCH : Operators.NOTMATCH.name,
289
- NEQ : Operators.NEQ.name,
290
- GT : Operators.GT.name,
291
- GTE : Operators.GTE.name,
292
- LT : Operators.LT.name,
293
- LTE : Operators.LTE.name
294
- },
295
- isRegExp: function(obj) {
296
- return Object.prototype.toString.apply(obj) === '[object RegExp]';
297
- },
298
- /**
299
- * Escapes all RegExp special characters.
300
- */
301
- escapeRegex: function(re) {
302
- return re.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
303
- },
304
- /**** vérifie si la valeur {value}, match la valeur {filterText}
305
- * @param {object : {
306
- * value : la valeur à vérifier
307
- * filterText : le texte à vérifier si value le match
308
- * operateur : chaine de caractère dans la liste des opérateurs :, props Operators, ci-desous
309
- * useRegex : Spécifie si l'expression régulière sera utilisée pour la recherche
310
- * }}
311
- */
312
- match : ({value,filterText,operator,useRegex})=>{
313
- if(isNonNullString(operator)){
314
- operator = Operators[operator]
315
- }
316
- filterText = defaultStr(filterText);
317
- operator = defaultObj(operator);
318
- let isSearchMode = filterText !== '';
319
- if(operator && isFunction(operator.func)){
320
- let opterm = operator.regexpSupported && isSearchMode ? (useRegex ? filterText : utils.escapeRegex(filterText)) : filterText;
321
- return !isSearchMode || operator.func(value, opterm)
322
- }
323
- return !isSearchMode ? true : defaultStr(value).toLowerCase().contains(filterText.toLowerCase());
324
- },
325
- /**
326
- * Returns the first element in the array that satisfies the given predicate
327
- * @param {Array} array the array to search
328
- * @param {function} predicate Function to apply to each element until it returns true
329
- * @return {Object} The first object in the array that satisfies the predicate or undefined.
330
- */
331
- findInArray: function(array, predicate) {
332
- if (this.isArray(array) && predicate) {
333
- for (var i = 0; i < array.length; i++) {
334
- var item = array[i];
335
- if (predicate(item)) {
336
- return item;
337
- }
338
- }
339
- }
340
- return undefined;
341
- },
342
- /**
343
- * Returns a JSON string represenation of an object
344
- * @param {object} obj
345
- * @return {string}
346
- */
347
- jsonStringify: function(obj, censorKeywords) {
348
- function censor(key, value) {
349
- return censorKeywords && censorKeywords.indexOf(key) > -1 ? undefined : value;
350
- }
351
- return JSON.stringify(obj, censor, 2);
352
- }
353
- }
354
-
355
- for(let i in utils.items){
356
- utils.constants[i] = i;
357
- }
358
-
359
- /*** prend en paramètre un objet de filtres et les prépare pour la requête distante
360
- * @param {object} filtersToPrepare les filtres à exploiter pour la requête distante
361
- * le tableau des filtres
362
- * @param {function|object} filter|options, la méthode utilisée pour effectuer un filtre sur les les élément à prendre en compte
363
- *
364
- */
365
- export const prepareFilters = (filtersToPrepare,opts)=>{
366
- if(typeof opts =='function'){
367
- opts = {filter:opts};
368
- }
369
- let {filter,convertToSQL:convToSQL} = opts;
370
- const filters = {}
371
- filter = typeof filter =='function'? filter : x=>true;
372
- Object.map(filtersToPrepare,(f,i)=>{
373
- if(!isObj(f) || !filter(f) || !isNonNullString(f.operator) || !isNonNullString(f.action)) {
374
- return;
375
- }
376
- f.field = defaultStr(f.field,i).trim();
377
- f.action = defaultStr(f.action).toLowerCase().trim();
378
- if(f.action =="$today"){
379
- f.operator = "$and";
380
- f.action = "$eq"
381
- }
382
- filters[f.operator] = defaultArray(filters[f.operator]);
383
- const ob = {};
384
- ob[f.field] = {}
385
- if(f.action == "$period"){
386
- let sp = defaultStr(f.value);
387
- if(sp){
388
- sp = sp.split("=>");
389
- const isValid1 = DateLib.isValidSQLDate(sp[0]) || DateLib.isValidSQLDateTime(sp[0]);
390
- const isValid2 = DateLib.isValidSQLDate(sp[1]) || DateLib.isValidSQLDateTime(sp[1]);
391
- if(isValid1 && isValid2){
392
- filters[f.operator].push({
393
- [f.field] : {$gte:sp[0]}
394
- })
395
- filters[f.operator].push({
396
- [f.field] : {$lte:sp[1]}
397
- })
398
- }
399
- }
400
- } else {
401
- ob[f.field][f.action] = f.value;
402
- filters[f.operator].push(ob)
403
- }
404
- });
405
- return convToSQL ? convertToSQL(filters) : filters;
406
- }
407
-
408
- /*** la liste des actions supportés par les filtres
409
- * @see : https://github.com/cloudant/mango from mango query mapping
410
- *
411
- */
412
- export const actions = ["$lt", "$gt", "$lte", "$gte", "$eq", "$exists", "$type", "$in", "$nin", "$all", "$size", "$or", "$nor", "$not", "$mod", "$regex", "$elemMatch"]
413
-
414
- export default utils;
415
-
416
- /*** convertis les filtres pris au format mangoesQuey, au format de sortie SQL
417
- * @see : https://github.com/gordonBusyman/mongo-to-sql-converter
418
- * @param {object} filters, les filtres au format mango query à convertir au format SQL
419
- */
420
- export const convertToSQL = (filters)=>{
421
- if(!isObjOrArray(filters)) return [];
422
- const whereParsed = mangoParser.parse(filters)
423
- return whereParsed.parts.reduce((prev, curr) => {
424
- const c = whereClauseToSQL(curr);
425
- return c ? [...prev,c] : prev;
426
- }, []).map((filter)=>{
427
- if(filter.field === MANGO_QUERY_OPERATOR){
428
- delete filter.field;
429
- }
430
- return filter;
431
- });
432
- }
433
- export const MANGO_QUERY_OPERATOR = "MANGO_QUERY_OPERATOR";
434
- const whereClauseToSQL = (currentMongoParserElement) => {
435
- let { field, operator, operand } = currentMongoParserElement;
436
- if(!isNonNullString(operator) || !operatorsMap[operator]) return null;
437
- operator = operatorsMap[operator]
438
- if(operator == 'LIKE'){
439
- const deparsed = regexDeparser(operand);
440
- if(!isObj(deparsed)){
441
- return null;
442
- }
443
- operand = deparsed.value;
444
- const deparsedOp = deparsed.operator;
445
- if(deparsedOp =='notcontains'){
446
- operator = "NOT LIKE";
447
- }
448
- if(deparsedOp =='equals'){
449
- operator = operatorsMap.$eq;
450
- } else if(deparsedOp =='notequals'){
451
- operator = operatorsMap.$ne;
452
- } else{
453
- const opMap = regexExpressions[deparsedOp];
454
- if(!opMap || typeof opMap.sql !=='function') return null;
455
- operand = opMap.sql(operand);
456
- }
457
- }
458
- // AND or OR operators with nested elements
459
- if (typeof field === 'undefined') {
460
- // parse nested elements
461
- const nested = operand.reduce((prev, curr) => {
462
- const parsed = mangoParser.parse(curr);
463
- const prepared = whereClauseToSQL(parsed.parts[0]);
464
- if(prepared){
465
- return [...prev,prepared]
466
- }
467
- return prev;
468
- }, [])
469
-
470
- // nested WHERE element
471
- return {
472
- field: MANGO_QUERY_OPERATOR,
473
- operator,
474
- operand: nested
475
- }
476
- }
477
- // simple WHERE element
478
- return {
479
- field,
480
- operator,
481
- operand
482
- }
483
- }
484
-
485
- // map mongoDB -> SQL operators
486
- const operatorsMap = {
487
- $or: 'OR',
488
- $and: 'AND',
489
- $lt: '<',
490
- $lte: '<=',
491
- $gt: '>',
492
- $gte: '>=',
493
- $ne: '!=',
494
- $in: 'IN',
495
- $nin : 'NOT IN',
496
- $eq: '=',
497
- $regex : "LIKE"
498
- }
499
-
500
- /**
501
- *
502
- * @param {*} operand
503
- * @param {*} operator
504
- * @param {*} field
505
- *
506
- * Return the operand in right format
507
- */
508
- export const getTyppedOperand = (operand, operator, field) => {
509
- if (typeof operand === 'string') { // wrap strings in double quots
510
- return "'" +(operand) + "'"
511
- } else if (operator === 'IN' || operator =='NOT IN') { // wrap IN arguments in brackers
512
- operand = Array.isArray(operand)? operand.map(op => getTyppedOperand(op, null, null))
513
- .join(', '): operand;
514
- return '(' + operand + ')'
515
- } else if (!isNonNullString(field) && Array.isArray(operand)) { // AND or OR elements
516
- // recursively call 'buildWhereElement' for nested elements
517
- // join each element with operator AND or OR
518
- return operand.reduce((prev, curr) => {
519
- return [...prev, buildWhereElement(curr)]
520
- }, []).join(' ' + operator + ' ')
521
- } else {
522
- return operand
523
- }
524
- }
525
-
526
- /**
527
- *
528
- * @param {*} elem
529
- *
530
- * Return element of WHERE clause
531
- */
532
- export const buildWhereElement = (elem) => {
533
- const { field, operator, operand } = elem
534
- if (!isNonNullString(field)) { // nested WHERE element
535
- return '(' + getTyppedOperand(operand, operator, field) + ')'
536
- } else { // simple WHERE element
537
- return field + ' ' + operator + ' ' + getTyppedOperand(operand, operator, field)
538
- }
539
- }
540
-
541
- /**** construit une requête Where à partir des filtres préparé dans le tableau whereClausePrepared
542
- * @see : https://github.com/gordonBusyman/mongo-to-sql-converter
543
- * @parm {array} whereClausePrepared, les filtres préparés avec la méthode prepareFilters puis convertis avec la fonction convertToSQL
544
- @return {string}, la requête Where correspondante
545
- */
546
- export const buildWhere = (whereClausePrepared)=>{
547
- if(!Array.isArray(whereClausePrepared)) return null;
548
- // build WHERE string clause by adding each element of array to it, separated with AND
549
- return whereClausePrepared.reduce((prev, curr) => [
550
- ...prev, buildWhereElement(curr)
551
- ], []).join(' AND ');
552
- }
@@ -1,4 +0,0 @@
1
- // Copyright 2022 @fto-consult/Boris Fouomene. All rights reserved.
2
- // Use of this source code is governed by a BSD-style
3
- // license that can be found in the LICENSE file.
4
-