@controleonline/ui-default 1.0.265 → 1.0.267

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@controleonline/ui-default",
3
- "version": "1.0.265",
3
+ "version": "1.0.267",
4
4
  "author": "ControleOnline",
5
5
  "description": "ControleOnline Default Quasar UI Component",
6
6
  "license": "MIT",
@@ -1,19 +1,21 @@
1
1
  import React, { useEffect, useMemo, useState } from 'react';
2
- import { Text, TextInput, TouchableOpacity, View } from 'react-native';
2
+ import { Image, Text, TextInput, TouchableOpacity, View } from 'react-native';
3
3
  import Icon from 'react-native-vector-icons/Feather';
4
- import {
4
+ import {
5
+ buildReadPresentationStyles,
5
6
  getColumnKey,
6
7
  isDateLikeColumn,
7
8
  isEditableColumn,
8
- normalizeText,
9
- resolveCellText,
10
- resolveEditValue,
9
+ normalizeText,
10
+ resolveCellPresentation,
11
+ resolveCellText,
12
+ resolveEditValue,
11
13
  } from './defaultInputUtils';
12
14
  import DefaultDateInput from './DefaultDateInput';
13
15
  import DefaultSelect from './DefaultSelect';
14
- import styles from './DefaultInput.styles';
15
-
16
- const DefaultInput = ({
16
+ import styles from './DefaultInput.styles';
17
+
18
+ const DefaultInput = ({
17
19
  accentColor = '#2563EB',
18
20
  autoFocus = true,
19
21
  autoSave = true,
@@ -48,8 +50,19 @@ const DefaultInput = ({
48
50
  [column, row, value],
49
51
  );
50
52
  const [draftValue, setDraftValue] = useState(editValue);
51
- const resolvedLabel =
52
- displayValue ?? resolveCellText({ column, columns, row, storeName, value });
53
+ const resolvedLabel =
54
+ displayValue ?? resolveCellText({ column, columns, row, storeName, value });
55
+ const readPresentation = useMemo(
56
+ () => resolveCellPresentation({ column, columns, row, storeName, value }),
57
+ [column, columns, row, storeName, value],
58
+ );
59
+ const readPresentationStyles = useMemo(
60
+ () => buildReadPresentationStyles({
61
+ ...readPresentation,
62
+ label: resolvedLabel,
63
+ }),
64
+ [readPresentation, resolvedLabel],
65
+ );
53
66
 
54
67
  useEffect(() => {
55
68
  setDraftValue(editValue);
@@ -128,31 +141,56 @@ const DefaultInput = ({
128
141
  if (!autoSave) onChangeValue?.(nextValue);
129
142
  };
130
143
 
131
- if (!editing && !isForm) {
132
- return (
133
- <View style={[styles.wrap, containerStyle]}>
134
- {showLabel ? <Text style={styles.fieldLabel}>{label || column?.label || fieldName}</Text> : null}
135
- <TouchableOpacity
144
+ if (!editing && !isForm) {
145
+ return (
146
+ <View style={[styles.wrap, containerStyle]}>
147
+ {showLabel ? <Text style={styles.fieldLabel}>{label || column?.label || fieldName}</Text> : null}
148
+ <TouchableOpacity
136
149
  style={styles.readButton}
137
150
  activeOpacity={canEdit ? 0.78 : 1}
138
- disabled={!canEdit}
139
- onPress={() => onStartEditing?.()}
140
- >
141
- <Text
142
- style={[
143
- styles.readText,
144
- resolvedLabel === '-' ? styles.mutedText : null,
145
- readTextStyle,
146
- ]}
147
- numberOfLines={numberOfLines}
148
- >
149
- {column?.prefix || ''}
150
- {resolvedLabel || '-'}
151
- {column?.sufix || column?.suffix || ''}
152
- </Text>
153
- {saving ? (
154
- <Text style={[styles.savingText, { color: accentColor }]}>Salvando</Text>
155
- ) : canEdit ? (
151
+ disabled={!canEdit}
152
+ onPress={() => onStartEditing?.()}
153
+ >
154
+ <View
155
+ style={[
156
+ styles.readValueWrap,
157
+ readPresentationStyles.hasDecoration ? styles.readBadge : null,
158
+ readPresentationStyles.badgeStyle,
159
+ ]}
160
+ >
161
+ {readPresentation.image ? (
162
+ <Image
163
+ source={readPresentation.image}
164
+ style={styles.readImage}
165
+ resizeMode="contain"
166
+ />
167
+ ) : null}
168
+ {readPresentationStyles.hasDecoration && readPresentation.icon && readPresentationStyles.color ? (
169
+ <Icon
170
+ style={styles.readBadgeIcon}
171
+ name={readPresentation.icon}
172
+ size={12}
173
+ color={readPresentationStyles.color}
174
+ />
175
+ ) : null}
176
+ <Text
177
+ style={[
178
+ styles.readText,
179
+ readPresentationStyles.hasDecoration ? styles.readBadgeText : null,
180
+ resolvedLabel === '-' ? styles.mutedText : null,
181
+ readPresentationStyles.color ? { color: readPresentationStyles.color } : null,
182
+ readTextStyle,
183
+ ]}
184
+ numberOfLines={numberOfLines}
185
+ >
186
+ {column?.prefix || ''}
187
+ {resolvedLabel || '-'}
188
+ {column?.sufix || column?.suffix || ''}
189
+ </Text>
190
+ </View>
191
+ {saving ? (
192
+ <Text style={[styles.savingText, { color: accentColor }]}>Salvando</Text>
193
+ ) : canEdit ? (
156
194
  <Icon style={styles.editIcon} name="edit-2" size={13} color="#64748B" />
157
195
  ) : null}
158
196
  </TouchableOpacity>
@@ -13,6 +13,38 @@ const styles = StyleSheet.create({
13
13
  alignItems: 'center',
14
14
  gap: 6,
15
15
  },
16
+ readValueWrap: {
17
+ flex: 1,
18
+ minWidth: 0,
19
+ maxWidth: '100%',
20
+ flexDirection: 'row',
21
+ alignItems: 'center',
22
+ gap: 5,
23
+ },
24
+ readBadge: {
25
+ flex: 0,
26
+ alignSelf: 'flex-start',
27
+ minHeight: 22,
28
+ maxWidth: '100%',
29
+ borderWidth: 1,
30
+ borderRadius: 6,
31
+ paddingHorizontal: 7,
32
+ paddingVertical: 2,
33
+ },
34
+ readBadgeText: {
35
+ flex: 0,
36
+ flexShrink: 1,
37
+ fontSize: 11,
38
+ fontWeight: '800',
39
+ },
40
+ readBadgeIcon: {
41
+ flexShrink: 0,
42
+ },
43
+ readImage: {
44
+ width: 16,
45
+ height: 16,
46
+ flexShrink: 0,
47
+ },
16
48
  readButtonForm: {
17
49
  minHeight: 34,
18
50
  borderWidth: 1,
@@ -25,8 +57,8 @@ const styles = StyleSheet.create({
25
57
  flex: 1,
26
58
  minWidth: 0,
27
59
  color: '#0F172A',
28
- fontSize: 12,
29
- fontWeight: '700',
60
+ fontSize: 13,
61
+ fontWeight: '600',
30
62
  },
31
63
  mutedText: {
32
64
  color: '#94A3B8',
@@ -62,8 +94,8 @@ const styles = StyleSheet.create({
62
94
  borderRadius: 8,
63
95
  paddingHorizontal: 8,
64
96
  color: '#0F172A',
65
- fontSize: 12,
66
- fontWeight: '700',
97
+ fontSize: 13,
98
+ fontWeight: '600',
67
99
  backgroundColor: '#FFFFFF',
68
100
  },
69
101
  formInput: {
@@ -1,7 +1,8 @@
1
1
  import React, { useCallback, useMemo, useRef, useState } from 'react';
2
2
  import {
3
- Modal,
4
- ScrollView,
3
+ Modal,
4
+ Image,
5
+ ScrollView,
5
6
  Text,
6
7
  TextInput,
7
8
  TouchableOpacity,
@@ -10,12 +11,14 @@ import {
10
11
  import Icon from 'react-native-vector-icons/Feather';
11
12
  import { getAllStores, useStore } from '@store';
12
13
  import {
14
+ buildReadPresentationStyles,
13
15
  buildOptionsFromColumn,
14
16
  getColumnKey,
15
17
  isEditableColumn,
16
18
  mapOptions,
17
19
  normalizeOptionKey,
18
20
  normalizeText,
21
+ resolveCellPresentation,
19
22
  resolveCellText,
20
23
  resolveEditValue,
21
24
  resolveStoreNameFromList,
@@ -140,11 +143,22 @@ const DefaultSelect = ({
140
143
  ];
141
144
  }, [column, getOptionsForColumn, loadedItems, storeName]);
142
145
  const selected = options.find(option => option.key === selectedKey);
143
- const resolvedLabel =
144
- displayValue ??
145
- selected?.label ??
146
- resolveCellText({ column, columns, row, storeName, value });
147
- const canEdit = isEditableColumn(column) && typeof onStartEditing === 'function';
146
+ const resolvedLabel =
147
+ displayValue ??
148
+ selected?.label ??
149
+ resolveCellText({ column, columns, row, storeName, value });
150
+ const readPresentation = useMemo(
151
+ () => resolveCellPresentation({ column, columns, row, storeName, value }),
152
+ [column, columns, row, storeName, value],
153
+ );
154
+ const readPresentationStyles = useMemo(
155
+ () => buildReadPresentationStyles({
156
+ ...readPresentation,
157
+ label: resolvedLabel,
158
+ }),
159
+ [readPresentation, resolvedLabel],
160
+ );
161
+ const canEdit = isEditableColumn(column) && typeof onStartEditing === 'function';
148
162
  const isForm = variant === 'form';
149
163
  const filteredOptions = useMemo(() => {
150
164
  const query = normalizeText(searchText).toLowerCase();
@@ -281,20 +295,45 @@ const DefaultSelect = ({
281
295
  activeOpacity={canEdit || isForm ? 0.78 : 1}
282
296
  disabled={!canEdit && !isForm}
283
297
  onPress={open}
284
- >
285
- <Text
286
- style={[
287
- inputStyles.readText,
288
- resolvedLabel === '-' ? inputStyles.mutedText : null,
289
- readTextStyle,
290
- ]}
291
- numberOfLines={numberOfLines}
292
- >
293
- {resolvedLabel || '-'}
294
- </Text>
295
- {saving ? (
296
- <Text style={[inputStyles.savingText, { color: accentColor }]}>Salvando</Text>
297
- ) : canEdit || isForm ? (
298
+ >
299
+ <View
300
+ style={[
301
+ inputStyles.readValueWrap,
302
+ readPresentationStyles.hasDecoration ? inputStyles.readBadge : null,
303
+ readPresentationStyles.badgeStyle,
304
+ ]}
305
+ >
306
+ {readPresentation.image ? (
307
+ <Image
308
+ source={readPresentation.image}
309
+ style={inputStyles.readImage}
310
+ resizeMode="contain"
311
+ />
312
+ ) : null}
313
+ {readPresentationStyles.hasDecoration && readPresentation.icon && readPresentationStyles.color ? (
314
+ <Icon
315
+ style={inputStyles.readBadgeIcon}
316
+ name={readPresentation.icon}
317
+ size={12}
318
+ color={readPresentationStyles.color}
319
+ />
320
+ ) : null}
321
+ <Text
322
+ style={[
323
+ inputStyles.readText,
324
+ readPresentationStyles.hasDecoration ? inputStyles.readBadgeText : null,
325
+ resolvedLabel === '-' ? inputStyles.mutedText : null,
326
+ readPresentationStyles.color ? { color: readPresentationStyles.color } : null,
327
+ readTextStyle,
328
+ ]}
329
+ numberOfLines={numberOfLines}
330
+ >
331
+ {resolvedLabel || '-'}
332
+ </Text>
333
+ </View>
334
+ {saving ? (
335
+ <Text style={[inputStyles.savingText, { color: accentColor }]}>Salvando</Text>
336
+ ) : canEdit || isForm ? (
298
337
  <Icon style={inputStyles.editIcon} name="chevron-down" size={14} color="#64748B" />
299
338
  ) : null}
300
339
  </TouchableOpacity>
@@ -322,24 +361,33 @@ const DefaultSelect = ({
322
361
 
323
362
  <ScrollView keyboardShouldPersistTaps="handled">
324
363
  {filteredOptions.length > 0 ? (
325
- filteredOptions.map(option => {
326
- const isSelected = option.key === selectedKey;
327
- return (
328
- <TouchableOpacity
329
- key={`${option.key}_${option.label}`}
364
+ filteredOptions.map(option => {
365
+ const isSelected = option.key === selectedKey;
366
+ return (
367
+ <TouchableOpacity
368
+ key={`${option.key}_${option.label}`}
330
369
  style={styles.optionRow}
331
370
  activeOpacity={0.78}
332
371
  onPress={() => selectOption(option)}
333
372
  >
334
- <Icon
335
- name={isSelected ? 'check-circle' : 'circle'}
336
- size={15}
337
- color={isSelected ? accentColor : '#CBD5E1'}
338
- />
339
- <Text style={styles.optionText}>
340
- {option.label}
341
- </Text>
342
- </TouchableOpacity>
373
+ {option.image ? (
374
+ <Image
375
+ source={option.image}
376
+ style={inputStyles.readImage}
377
+ resizeMode="contain"
378
+ />
379
+ ) : null}
380
+ {isSelected || (option.icon && option.color) ? (
381
+ <Icon
382
+ name={isSelected ? 'check-circle' : option.icon}
383
+ size={15}
384
+ color={isSelected ? accentColor : option.color}
385
+ />
386
+ ) : null}
387
+ <Text style={[styles.optionText, option.color ? { color: option.color } : null]}>
388
+ {option.label}
389
+ </Text>
390
+ </TouchableOpacity>
343
391
  );
344
392
  })
345
393
  ) : (
@@ -128,7 +128,7 @@ export const resolveOptionLabel = (column, option, storeName = '') => {
128
128
  export const resolveStoreNameFromList = list =>
129
129
  typeof list === 'string' ? normalizeText(list).split('/')[0] || '' : '';
130
130
 
131
- const filterItemsByListRequestParams = (items = [], column = null) => {
131
+ const filterItemsByListRequestParams = (items = [], column = null) => {
132
132
  const requestParams =
133
133
  column?.listRequestParams &&
134
134
  typeof column.listRequestParams === 'object' &&
@@ -148,14 +148,17 @@ const filterItemsByListRequestParams = (items = [], column = null) => {
148
148
 
149
149
  return normalizeText(item?.[key]) === normalizeText(expectedValue);
150
150
  }),
151
- );
152
- };
153
-
154
- export const mapOptions = (column, items = [], storeName = '') => {
155
- const seenKeys = new Set();
156
- const options = [];
157
-
158
- (Array.isArray(items) ? items : []).forEach(item => {
151
+ );
152
+ };
153
+
154
+ const normalizeObject = value =>
155
+ value && typeof value === 'object' && !Array.isArray(value) ? value : {};
156
+
157
+ export const mapOptions = (column, items = [], storeName = '') => {
158
+ const seenKeys = new Set();
159
+ const options = [];
160
+
161
+ (Array.isArray(items) ? items : []).forEach(item => {
159
162
  const formatted =
160
163
  typeof column?.formatList === 'function'
161
164
  ? column.formatList(item, null, column)
@@ -166,12 +169,31 @@ export const mapOptions = (column, items = [], storeName = '') => {
166
169
  if (dedupeKey && seenKeys.has(dedupeKey)) return;
167
170
  if (dedupeKey) seenKeys.add(dedupeKey);
168
171
 
169
- options.push({
170
- key,
171
- label: resolveOptionLabel(column, item, storeName) || '-',
172
- raw: item,
173
- });
174
- });
172
+ const formattedMeta = normalizeObject(formatted);
173
+ const itemMeta = normalizeObject(item);
174
+ const color = formattedMeta.color ?? itemMeta.color;
175
+ const icon =
176
+ formattedMeta.icon ??
177
+ formattedMeta.iconName ??
178
+ itemMeta.icon ??
179
+ itemMeta.iconName;
180
+ const image =
181
+ formattedMeta.image ??
182
+ formattedMeta.logo ??
183
+ formattedMeta.source ??
184
+ itemMeta.image ??
185
+ itemMeta.logo ??
186
+ itemMeta.source;
187
+
188
+ options.push({
189
+ ...(color ? { color } : {}),
190
+ ...(icon ? { icon } : {}),
191
+ ...(image ? { image } : {}),
192
+ key,
193
+ label: resolveOptionLabel(column, item, storeName) || '-',
194
+ raw: item,
195
+ });
196
+ });
175
197
 
176
198
  return options;
177
199
  };
@@ -197,23 +219,148 @@ export const buildOptionsFromColumn = (column, getOptionsForColumn = null, store
197
219
  );
198
220
  };
199
221
 
200
- export const resolveCellText = ({ column, columns = [], row, storeName, value }) => {
201
- const fieldName = getColumnKey(column);
202
- const rawValue = value ?? row?.[fieldName];
203
- const formattedValue = formatStoreColumnValue({
222
+ export const resolveCellText = ({ column, columns = [], row, storeName, value }) => {
223
+ const fieldName = getColumnKey(column);
224
+ const rawValue = value ?? row?.[fieldName];
225
+ const formattedValue = formatStoreColumnValue({
204
226
  columns: columns.length ? columns : [column],
205
227
  fieldName,
206
228
  row,
207
229
  storeName,
208
230
  value: rawValue,
209
231
  });
210
-
211
- return normalizeText(formattedValue) || '-';
212
- };
213
-
214
- export const resolveEditValue = (column, row, value) => {
215
- const rawValue = value ?? row?.[getColumnKey(column)];
216
- if (column?.list) {
232
+
233
+ return normalizeText(formattedValue) || '-';
234
+ };
235
+
236
+ const resolveFormattedListMeta = (column, rawValue, row) => {
237
+ if (typeof column?.formatList !== 'function') {
238
+ return {};
239
+ }
240
+
241
+ const formatted = column.formatList(rawValue, row, column);
242
+ return normalizeObject(formatted);
243
+ };
244
+
245
+ const resolveStyleMeta = (column, row) => {
246
+ if (typeof column?.style !== 'function') {
247
+ return normalizeObject(column?.style);
248
+ }
249
+
250
+ return normalizeObject(column.style(row));
251
+ };
252
+
253
+ export const resolveCellPresentation = ({
254
+ column,
255
+ columns = [],
256
+ row = {},
257
+ storeName = '',
258
+ value,
259
+ } = {}) => {
260
+ const fieldName = getColumnKey(column);
261
+ const rawValue = value ?? row?.[fieldName];
262
+ const rawMeta = normalizeObject(rawValue);
263
+ const formattedMeta = resolveFormattedListMeta(column, rawValue, row);
264
+ const styleMeta = resolveStyleMeta(column, row);
265
+ const label = resolveCellText({
266
+ column,
267
+ columns,
268
+ row,
269
+ storeName,
270
+ value,
271
+ });
272
+
273
+ return {
274
+ backgroundColor:
275
+ formattedMeta.backgroundColor ??
276
+ formattedMeta.bgColor ??
277
+ rawMeta.backgroundColor ??
278
+ rawMeta.bgColor ??
279
+ styleMeta.backgroundColor ??
280
+ styleMeta.bgColor,
281
+ borderColor:
282
+ formattedMeta.borderColor ??
283
+ rawMeta.borderColor ??
284
+ styleMeta.borderColor,
285
+ color:
286
+ formattedMeta.color ??
287
+ rawMeta.color ??
288
+ rawMeta.statusColor ??
289
+ rawMeta.categoryColor ??
290
+ styleMeta.color,
291
+ icon:
292
+ formattedMeta.icon ??
293
+ formattedMeta.iconName ??
294
+ rawMeta.icon ??
295
+ rawMeta.iconName ??
296
+ styleMeta.icon ??
297
+ styleMeta.iconName,
298
+ image:
299
+ formattedMeta.image ??
300
+ formattedMeta.logo ??
301
+ formattedMeta.source ??
302
+ rawMeta.image ??
303
+ rawMeta.logo ??
304
+ rawMeta.source ??
305
+ styleMeta.image ??
306
+ styleMeta.logo ??
307
+ styleMeta.source,
308
+ label,
309
+ };
310
+ };
311
+
312
+ const resolveAlphaColor = (color, opacity = 0.12) => {
313
+ const normalized = normalizeText(color);
314
+ const hex = normalized.match(/^#?([a-f\d]{6})$/i)?.[1];
315
+
316
+ if (!hex) return normalized || undefined;
317
+
318
+ const red = parseInt(hex.slice(0, 2), 16);
319
+ const green = parseInt(hex.slice(2, 4), 16);
320
+ const blue = parseInt(hex.slice(4, 6), 16);
321
+
322
+ return `rgba(${red}, ${green}, ${blue}, ${opacity})`;
323
+ };
324
+
325
+ export const buildReadPresentationStyles = presentation => {
326
+ const color = normalizeText(presentation?.color);
327
+ const icon = normalizeText(presentation?.icon);
328
+ const hasDecoration = Boolean(
329
+ color ||
330
+ normalizeText(presentation?.backgroundColor) ||
331
+ normalizeText(presentation?.borderColor) ||
332
+ (icon && color),
333
+ );
334
+
335
+ if (!hasDecoration || presentation?.label === '-') {
336
+ return {
337
+ badgeStyle: null,
338
+ color: null,
339
+ hasDecoration: false,
340
+ };
341
+ }
342
+
343
+ const backgroundColor =
344
+ presentation.backgroundColor ||
345
+ (color ? resolveAlphaColor(color, 0.1) : undefined);
346
+ const borderColor =
347
+ presentation.borderColor ||
348
+ presentation.backgroundColor ||
349
+ (color ? resolveAlphaColor(color, 0.32) : undefined);
350
+
351
+ return {
352
+ badgeStyle: {
353
+ backgroundColor,
354
+ borderColor,
355
+ },
356
+ color: color || undefined,
357
+ hasDecoration: true,
358
+ };
359
+ };
360
+
361
+ export const resolveEditValue = (column, row, value) => {
362
+ const rawValue = value ?? row?.[getColumnKey(column)];
363
+ if (column?.list) {
217
364
  return normalizeOptionKey(
218
365
  typeof column?.formatList === 'function'
219
366
  ? column.formatList(rawValue, row, column)