web-mojo 2.2.57 → 2.2.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.es.js +1 -10105
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.es.js +1 -588
  7. package/dist/auth.es.js.map +1 -1
  8. package/dist/charts.cjs.js +1 -1
  9. package/dist/charts.es.js +1 -571
  10. package/dist/charts.es.js.map +1 -1
  11. package/dist/chunks/ChatView-D4A9rIX3.js +2 -0
  12. package/dist/chunks/ChatView-D4A9rIX3.js.map +1 -0
  13. package/dist/chunks/ChatView-nxaq8aIo.js +2 -0
  14. package/dist/chunks/ChatView-nxaq8aIo.js.map +1 -0
  15. package/dist/chunks/Collection-1sPoIFvQ.js +2 -0
  16. package/dist/chunks/{Collection-DaiL0uGl.js.map → Collection-1sPoIFvQ.js.map} +1 -1
  17. package/dist/chunks/{Collection-CxbNKOas.js → Collection-DSBRXpwK.js} +2 -2
  18. package/dist/chunks/{Collection-CxbNKOas.js.map → Collection-DSBRXpwK.js.map} +1 -1
  19. package/dist/chunks/{ContextMenu-ClwHEbbD.js → ContextMenu-BWy7WqF4.js} +2 -2
  20. package/dist/chunks/{ContextMenu-ClwHEbbD.js.map → ContextMenu-BWy7WqF4.js.map} +1 -1
  21. package/dist/chunks/ContextMenu-BvniQz-N.js +3 -0
  22. package/dist/chunks/{ContextMenu-sgvgSACY.js.map → ContextMenu-BvniQz-N.js.map} +1 -1
  23. package/dist/chunks/DataView--nUWtq6r.js +2 -0
  24. package/dist/chunks/{DataView-Dzo0jbs2.js.map → DataView--nUWtq6r.js.map} +1 -1
  25. package/dist/chunks/{DataView-1xh3GFeC.js → DataView-CK3Z0TJH.js} +2 -2
  26. package/dist/chunks/{DataView-1xh3GFeC.js.map → DataView-CK3Z0TJH.js.map} +1 -1
  27. package/dist/chunks/Dialog-BcgSR01Z.js +2 -0
  28. package/dist/chunks/{Dialog-DOGDalUq.js.map → Dialog-BcgSR01Z.js.map} +1 -1
  29. package/dist/chunks/{Dialog-CQlTDhZS.js → Dialog-DwCTFV6O.js} +2 -2
  30. package/dist/chunks/{Dialog-CQlTDhZS.js.map → Dialog-DwCTFV6O.js.map} +1 -1
  31. package/dist/chunks/FormPlugins-DvQ-G5J5.js +2 -0
  32. package/dist/chunks/{FormPlugins-DY6e88YT.js.map → FormPlugins-DvQ-G5J5.js.map} +1 -1
  33. package/dist/chunks/{FormView-DaKA4Sys.js → FormView-CRmEReTC.js} +3 -3
  34. package/dist/chunks/{FormView-DaKA4Sys.js.map → FormView-CRmEReTC.js.map} +1 -1
  35. package/dist/chunks/FormView-OLA7t-yv.js +3 -0
  36. package/dist/chunks/{FormView-Dz3mYasQ.js.map → FormView-OLA7t-yv.js.map} +1 -1
  37. package/dist/chunks/ListView-6JQ6tRXs.js +2 -0
  38. package/dist/chunks/{ListView-X5w5jf51.js.map → ListView-6JQ6tRXs.js.map} +1 -1
  39. package/dist/chunks/{ListView-CDzKIpd8.js → ListView-DVStKiMi.js} +2 -2
  40. package/dist/chunks/{ListView-CDzKIpd8.js.map → ListView-DVStKiMi.js.map} +1 -1
  41. package/dist/chunks/{MetricsCountryMapView-Dx2cw7ya.js → MetricsCountryMapView-CnAEbUw_.js} +2 -2
  42. package/dist/chunks/{MetricsCountryMapView-Dx2cw7ya.js.map → MetricsCountryMapView-CnAEbUw_.js.map} +1 -1
  43. package/dist/chunks/MetricsCountryMapView-J067qrrt.js +2 -0
  44. package/dist/chunks/{MetricsCountryMapView-B2xz6zUw.js.map → MetricsCountryMapView-J067qrrt.js.map} +1 -1
  45. package/dist/chunks/{MetricsMiniChartWidget-CBuso0OE.js → MetricsMiniChartWidget-BeD1slGs.js} +2 -2
  46. package/dist/chunks/{MetricsMiniChartWidget-CBuso0OE.js.map → MetricsMiniChartWidget-BeD1slGs.js.map} +1 -1
  47. package/dist/chunks/MetricsMiniChartWidget-x2gFjHOU.js +2 -0
  48. package/dist/chunks/{MetricsMiniChartWidget-DvKd7Qrk.js.map → MetricsMiniChartWidget-x2gFjHOU.js.map} +1 -1
  49. package/dist/chunks/PDFViewer-CsyKn-gh.js +2 -0
  50. package/dist/chunks/{PDFViewer-EJ9cOfPF.js.map → PDFViewer-CsyKn-gh.js.map} +1 -1
  51. package/dist/chunks/{PDFViewer-ofMGdSaj.js → PDFViewer-DSa4BZCm.js} +2 -2
  52. package/dist/chunks/{PDFViewer-ofMGdSaj.js.map → PDFViewer-DSa4BZCm.js.map} +1 -1
  53. package/dist/chunks/Rest-DHbszkuP.js +2 -0
  54. package/dist/chunks/Rest-DHbszkuP.js.map +1 -0
  55. package/dist/chunks/Rest-Ds9e8tN8.js +2 -0
  56. package/dist/chunks/Rest-Ds9e8tN8.js.map +1 -0
  57. package/dist/chunks/TokenManager-D6SjKgPZ.js +2 -0
  58. package/dist/chunks/{TokenManager-DoN9e6q6.js.map → TokenManager-D6SjKgPZ.js.map} +1 -1
  59. package/dist/chunks/{TokenManager-Gqvj7SDX.js → TokenManager-REbha1Le.js} +2 -2
  60. package/dist/chunks/{TokenManager-Gqvj7SDX.js.map → TokenManager-REbha1Le.js.map} +1 -1
  61. package/dist/chunks/WebApp-CULZpO_0.js +2 -0
  62. package/dist/chunks/{WebApp-6qvqmOts.js.map → WebApp-CULZpO_0.js.map} +1 -1
  63. package/dist/chunks/{WebApp-_dgpwtFw.js → WebApp-DovLtA60.js} +2 -2
  64. package/dist/chunks/{WebApp-_dgpwtFw.js.map → WebApp-DovLtA60.js.map} +1 -1
  65. package/dist/chunks/WebSocketClient-B-wc3mez.js +2 -0
  66. package/dist/chunks/{WebSocketClient-DG2olXpH.js.map → WebSocketClient-B-wc3mez.js.map} +1 -1
  67. package/dist/chunks/{WebSocketClient-MFkFlSue.js → WebSocketClient-BdZ9QYll.js} +2 -2
  68. package/dist/chunks/{WebSocketClient-MFkFlSue.js.map → WebSocketClient-BdZ9QYll.js.map} +1 -1
  69. package/dist/chunks/version-C3dnl1bg.js +2 -0
  70. package/dist/chunks/version-C3dnl1bg.js.map +1 -0
  71. package/dist/chunks/{version-BVADfTA5.js → version-ioN546cp.js} +2 -2
  72. package/dist/chunks/{version-BVADfTA5.js.map → version-ioN546cp.js.map} +1 -1
  73. package/dist/css/web-mojo.css +1 -1
  74. package/dist/docit.cjs.js +1 -1
  75. package/dist/docit.es.js +1 -957
  76. package/dist/docit.es.js.map +1 -1
  77. package/dist/index.cjs.js +1 -1
  78. package/dist/index.es.js +1 -3252
  79. package/dist/index.es.js.map +1 -1
  80. package/dist/lightbox.cjs.js +1 -1
  81. package/dist/lightbox.es.js +1 -3737
  82. package/dist/lightbox.es.js.map +1 -1
  83. package/dist/loader.umd.js +2 -2
  84. package/dist/map.cjs.js +1 -1
  85. package/dist/map.es.js +1 -1032
  86. package/dist/map.es.js.map +1 -1
  87. package/dist/mojo-auth.es.js +338 -0
  88. package/dist/mojo-auth.umd.js +1 -0
  89. package/dist/timeline.cjs.js +1 -1
  90. package/dist/timeline.es.js +1 -224
  91. package/dist/timeline.es.js.map +1 -1
  92. package/dist/web-mojo.lite.iife.js +14 -3
  93. package/dist/web-mojo.lite.iife.js.map +1 -1
  94. package/dist/web-mojo.lite.iife.min.js +6 -6
  95. package/dist/web-mojo.lite.iife.min.js.map +1 -1
  96. package/package.json +2 -2
  97. package/dist/chunks/ChatView-9k6xBWXk.js +0 -7632
  98. package/dist/chunks/ChatView-9k6xBWXk.js.map +0 -1
  99. package/dist/chunks/ChatView-CdtuCDYm.js +0 -2
  100. package/dist/chunks/ChatView-CdtuCDYm.js.map +0 -1
  101. package/dist/chunks/Collection-DaiL0uGl.js +0 -1014
  102. package/dist/chunks/ContextMenu-sgvgSACY.js +0 -1535
  103. package/dist/chunks/DataView-Dzo0jbs2.js +0 -862
  104. package/dist/chunks/Dialog-DOGDalUq.js +0 -1579
  105. package/dist/chunks/FormPlugins-DY6e88YT.js +0 -124
  106. package/dist/chunks/FormView-Dz3mYasQ.js +0 -8636
  107. package/dist/chunks/ListView-X5w5jf51.js +0 -495
  108. package/dist/chunks/MetricsCountryMapView-B2xz6zUw.js +0 -1054
  109. package/dist/chunks/MetricsMiniChartWidget-DvKd7Qrk.js +0 -3283
  110. package/dist/chunks/PDFViewer-EJ9cOfPF.js +0 -946
  111. package/dist/chunks/Rest-CgSjfMaU.js +0 -2
  112. package/dist/chunks/Rest-CgSjfMaU.js.map +0 -1
  113. package/dist/chunks/Rest-W-sPfGh9.js +0 -4375
  114. package/dist/chunks/Rest-W-sPfGh9.js.map +0 -1
  115. package/dist/chunks/TokenManager-DoN9e6q6.js +0 -1423
  116. package/dist/chunks/WebApp-6qvqmOts.js +0 -1386
  117. package/dist/chunks/WebSocketClient-DG2olXpH.js +0 -209
  118. package/dist/chunks/version-OyPGnx30.js +0 -38
  119. package/dist/chunks/version-OyPGnx30.js.map +0 -1
@@ -1,862 +0,0 @@
1
- import { V as View, d as dataFormatter } from "./Rest-W-sPfGh9.js";
2
- class DataView extends View {
3
- constructor(options = {}) {
4
- const {
5
- data,
6
- model,
7
- fields,
8
- columns,
9
- responsive,
10
- showEmptyValues,
11
- emptyValueText,
12
- ...viewOptions
13
- } = options;
14
- super({
15
- tagName: "div",
16
- className: "data-view",
17
- ...viewOptions
18
- });
19
- this.data = data || {};
20
- this.fields = fields || [];
21
- this.model = model || null;
22
- if (this.model) {
23
- this.data = this.model;
24
- }
25
- this.dataViewOptions = {
26
- columns: columns || 2,
27
- responsive: responsive !== false,
28
- // Default to true
29
- showEmptyValues: showEmptyValues || false,
30
- emptyValueText: emptyValueText || "—",
31
- rowClass: "row g-3",
32
- itemClass: "data-view-item",
33
- labelClass: "data-view-label fw-semibold text-muted small text-uppercase",
34
- valueClass: "data-view-value"
35
- };
36
- }
37
- /**
38
- * Lifecycle hook - prepare data and fields before rendering
39
- */
40
- async onBeforeRender() {
41
- if (this.fields.length === 0 && this.getData()) {
42
- this.generateFieldsFromData();
43
- }
44
- }
45
- /**
46
- * Override renderTemplate to generate HTML directly
47
- * @returns {string} Complete HTML string
48
- */
49
- async renderTemplate() {
50
- const items = this.buildItemsHTML();
51
- return `
52
- <div class="${this.dataViewOptions.rowClass}">
53
- ${items}
54
- </div>
55
- `;
56
- }
57
- /**
58
- * Auto-generate field definitions from data object with intelligent type inference
59
- */
60
- generateFieldsFromData() {
61
- const dataObj = this.getData();
62
- if (dataObj && typeof dataObj === "object") {
63
- this.fields = Object.keys(dataObj).map((key) => {
64
- const value = dataObj[key];
65
- const fieldType = this.inferFieldType(value, key);
66
- const formatter = this.inferFormatter(value, key, fieldType);
67
- return {
68
- name: key,
69
- label: this.formatLabel(key),
70
- type: fieldType,
71
- format: formatter,
72
- formatter
73
- // Alias for consistency with TableView
74
- };
75
- });
76
- }
77
- }
78
- /**
79
- * Format field name into a readable label
80
- * @param {string} name - Field name
81
- * @returns {string} Formatted label
82
- */
83
- formatLabel(name) {
84
- return name.replace(/([A-Z])/g, " $1").replace(/[_-]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()).trim();
85
- }
86
- /**
87
- * Infer field type from value and key with improved intelligence
88
- * @param {*} value - Field value
89
- * @param {string} key - Field key
90
- * @returns {string} Field type
91
- */
92
- inferFieldType(value, key = "") {
93
- if (value === null || value === void 0) return "text";
94
- const keyLower = key.toLowerCase();
95
- const type = typeof value;
96
- if (keyLower.includes("date") || keyLower.includes("time") || keyLower.includes("created") || keyLower.includes("updated") || keyLower.includes("modified") || keyLower.includes("last_login") || keyLower.includes("expires") || keyLower.includes("last_activity")) {
97
- return "datetime";
98
- }
99
- if (keyLower.includes("email") || keyLower.includes("mail")) {
100
- return "email";
101
- }
102
- if (keyLower.includes("url") || keyLower.includes("link") || keyLower.includes("website") || keyLower.includes("homepage")) {
103
- return "url";
104
- }
105
- if (keyLower.includes("phone") || keyLower.includes("tel") || keyLower.includes("mobile") || keyLower.includes("cell")) {
106
- return "phone";
107
- }
108
- if (keyLower.includes("price") || keyLower.includes("cost") || keyLower.includes("amount") || keyLower.includes("fee") || keyLower.includes("salary") || keyLower.includes("revenue")) {
109
- return "currency";
110
- }
111
- if (keyLower.includes("size") || keyLower.includes("bytes")) {
112
- return "filesize";
113
- }
114
- if (keyLower.includes("percent") || keyLower.includes("rate") || keyLower.includes("ratio") && type === "number") {
115
- return "percent";
116
- }
117
- if (type === "boolean") return "boolean";
118
- if (type === "number") return "number";
119
- if (type === "object") {
120
- if (Array.isArray(value)) return "array";
121
- if (value && value.renditions) return "file";
122
- if (this.shouldUseDataView(value, keyLower)) {
123
- return "dataview";
124
- }
125
- return "object";
126
- }
127
- if (type === "string") {
128
- if (value.includes("@") && value.includes(".")) return "email";
129
- if (value.match(/^\d{4}-\d{2}-\d{2}/)) return "date";
130
- if (value.match(/^https?:\/\//)) return "url";
131
- if (value.match(/^\+?[\d\s\-\(\)]+$/)) return "phone";
132
- }
133
- return "text";
134
- }
135
- /**
136
- * Infer appropriate formatter based on type and context
137
- * @param {*} value - Field value
138
- * @param {string} key - Field key
139
- * @param {string} fieldType - Inferred field type
140
- * @returns {string|null} Formatter pipe string
141
- */
142
- inferFormatter(value, key, fieldType) {
143
- const keyLower = key.toLowerCase();
144
- const formatters = [];
145
- switch (fieldType) {
146
- case "datetime":
147
- if (keyLower.includes("time") && !keyLower.includes("date")) {
148
- formatters.push("time");
149
- } else if (keyLower.includes("relative") || keyLower.includes("ago") || keyLower.includes("last_")) {
150
- formatters.push("relative");
151
- } else if (keyLower.includes("created") || keyLower.includes("updated") || keyLower.includes("modified")) {
152
- formatters.push('date("MMM D, YYYY")');
153
- } else {
154
- formatters.push('date("MMMM D, YYYY")');
155
- }
156
- break;
157
- case "date":
158
- if (keyLower.includes("birth") || keyLower.includes("dob")) {
159
- formatters.push('date("MMMM D, YYYY")');
160
- } else {
161
- formatters.push('date("MMM D, YYYY")');
162
- }
163
- break;
164
- case "email":
165
- break;
166
- case "url":
167
- break;
168
- case "phone":
169
- formatters.push("phone");
170
- break;
171
- case "currency":
172
- formatters.push("currency");
173
- if (keyLower.includes("eur") || keyLower.includes("euro")) {
174
- formatters[formatters.length - 1] = 'currency("EUR")';
175
- } else if (keyLower.includes("gbp") || keyLower.includes("pound")) {
176
- formatters[formatters.length - 1] = 'currency("GBP")';
177
- }
178
- break;
179
- case "filesize":
180
- formatters.push("filesize");
181
- break;
182
- case "percent":
183
- formatters.push("percent");
184
- break;
185
- case "number":
186
- if (typeof value === "number") {
187
- if (keyLower.includes("count") || keyLower.includes("total") || keyLower.includes("followers") || keyLower.includes("views")) {
188
- if (value >= 1e3) {
189
- formatters.push("compact");
190
- } else {
191
- formatters.push("number");
192
- }
193
- } else if (keyLower.includes("score") || keyLower.includes("rating")) {
194
- formatters.push("number");
195
- if (value % 1 !== 0) {
196
- formatters[formatters.length - 1] = "number(1)";
197
- }
198
- } else if (keyLower.includes("version") || keyLower.includes("id")) {
199
- return null;
200
- } else {
201
- formatters.push("number");
202
- }
203
- }
204
- break;
205
- case "boolean":
206
- break;
207
- case "text":
208
- if (typeof value === "string") {
209
- if (keyLower.includes("description") || keyLower.includes("content") || keyLower.includes("body")) {
210
- if (value.length > 200) {
211
- formatters.push("truncate(200)");
212
- } else if (value.length > 100) {
213
- formatters.push("truncate(100)");
214
- }
215
- } else if (keyLower.includes("summary") || keyLower.includes("excerpt")) {
216
- if (value.length > 150) {
217
- formatters.push("truncate(150)");
218
- }
219
- } else if (keyLower.includes("name") || keyLower.includes("title") || keyLower.includes("label")) {
220
- formatters.push("capitalize");
221
- if (value.length > 50) {
222
- formatters.unshift("truncate(50)");
223
- }
224
- } else if (keyLower.includes("slug") || keyLower.includes("handle") || keyLower.includes("username")) {
225
- formatters.push("slug");
226
- } else if (keyLower.includes("code") || keyLower.includes("token") || keyLower.includes("key")) {
227
- if (value.length > 20) {
228
- formatters.push("mask");
229
- }
230
- } else {
231
- if (value.length > 100) {
232
- formatters.push("truncate(100)");
233
- }
234
- }
235
- }
236
- break;
237
- case "array":
238
- case "object":
239
- break;
240
- case "dataview":
241
- break;
242
- default:
243
- if (typeof value === "string" && value.length > 100) {
244
- formatters.push("truncate(100)");
245
- }
246
- break;
247
- }
248
- return formatters.length > 0 ? formatters.join("|") : null;
249
- }
250
- /**
251
- * Determine if an object should be displayed as nested DataView vs JSON
252
- * @param {object} value - Object value to check
253
- * @param {string} keyLower - Lowercase field key
254
- * @returns {boolean} True if should use DataView
255
- */
256
- shouldUseDataView(value, keyLower) {
257
- if (!value || typeof value !== "object" || Array.isArray(value)) {
258
- return false;
259
- }
260
- const dataViewPatterns = [
261
- "permissions",
262
- "perms",
263
- "access",
264
- "rights",
265
- "settings",
266
- "config",
267
- "configuration",
268
- "options",
269
- "profile",
270
- "info",
271
- "details",
272
- "data",
273
- "metadata",
274
- "meta",
275
- "attributes",
276
- "props",
277
- "preferences",
278
- "prefs",
279
- "user_data",
280
- "contact",
281
- "address",
282
- "location",
283
- "stats",
284
- "statistics",
285
- "metrics",
286
- "counts"
287
- ];
288
- if (window.utils && window.utils.isObject(value) && value.id) {
289
- return true;
290
- }
291
- const matchesPattern = dataViewPatterns.some((pattern) => keyLower.includes(pattern));
292
- if (matchesPattern) {
293
- const keys = Object.keys(value);
294
- if (keys.length >= 2 && keys.length <= 20) {
295
- const hasComplexNesting = keys.some(
296
- (k) => typeof value[k] === "object" && value[k] !== null && !Array.isArray(value[k]) && Object.keys(value[k]).length > 3
297
- );
298
- if (!hasComplexNesting) {
299
- return true;
300
- }
301
- }
302
- }
303
- return false;
304
- }
305
- /**
306
- * Get data object (handles both raw objects and Models)
307
- * @returns {object} Data object
308
- */
309
- getData() {
310
- if (this.model && this.model.attributes) {
311
- return { ...this.model.attributes };
312
- }
313
- return this.data || {};
314
- }
315
- /**
316
- * Get field value with formatting support
317
- * @param {object} field - Field definition
318
- * @returns {*} Field value (formatted if specified)
319
- */
320
- getFieldValue(field) {
321
- let value;
322
- let key = field.name || field.key;
323
- let formatString = field.format || field.formatter;
324
- if (!key) return null;
325
- if (key && key.includes("|")) {
326
- const parts = key.split("|");
327
- key = parts[0].trim();
328
- if (!formatString) {
329
- formatString = parts.slice(1).join("|").trim();
330
- }
331
- }
332
- if (this.model && typeof this.model.get === "function") {
333
- value = this.model.get(key);
334
- } else {
335
- value = this.getData()[key];
336
- }
337
- if (formatString) {
338
- value = dataFormatter.pipe(value, formatString);
339
- }
340
- if (value === null || value === void 0 || value === "") {
341
- return this.dataViewOptions.showEmptyValues ? this.dataViewOptions.emptyValueText : null;
342
- }
343
- if (field.template) {
344
- const modelData = this.model ? this.model : this.data;
345
- return this.renderTemplateString(field.template, modelData);
346
- }
347
- return value;
348
- }
349
- /**
350
- * Render a template string with data from a model or object.
351
- * Replaces {{key}} and {{nested.key}} placeholders.
352
- * @param {string} templateString - The template string.
353
- * @param {object} data - The data object or model.
354
- * @returns {string} The rendered string.
355
- */
356
- renderTemplateString(templateString, data) {
357
- if (!templateString || !data) {
358
- return "";
359
- }
360
- return templateString.replace(/\{\{([^}]+)\}\}/g, (match, key) => {
361
- const trimmedKey = key.trim();
362
- let value;
363
- const parts = trimmedKey.split("|");
364
- const dataKey = parts[0];
365
- const formatters = parts.slice(1).join("|");
366
- if (this.model && typeof this.model.get === "function") {
367
- value = this.model.get(dataKey);
368
- } else {
369
- value = dataKey.split(".").reduce((o, i) => o ? o[i] : void 0, data);
370
- }
371
- if (formatters) {
372
- value = dataFormatter.pipe(value, formatters);
373
- }
374
- return value !== void 0 && value !== null ? value : "";
375
- });
376
- }
377
- /**
378
- * Generate column classes based on configuration
379
- * @param {object} field - Field definition
380
- * @returns {string} CSS classes
381
- */
382
- getColumnClasses(field) {
383
- let classes = this.getColumnSizeClasses(field);
384
- if (field.justify == "right") {
385
- classes += ` d-flex justify-content-end`;
386
- } else if (field.justify == "center") {
387
- classes += ` d-flex justify-content-center`;
388
- }
389
- return classes;
390
- }
391
- /**
392
- * Generate column size classes based on configuration
393
- * @param {object} field - Field definition
394
- * @returns {string} CSS classes
395
- */
396
- getColumnSizeClasses(field) {
397
- if (field.type === "array" || field.type === "object" || field.type === "dataview") {
398
- return "col-12";
399
- }
400
- const colSize = field.columns || field.colSize || field.cols || Math.floor(12 / this.dataViewOptions.columns);
401
- if (this.dataViewOptions.responsive) {
402
- return `col-12 col-md-${colSize}`;
403
- }
404
- return `col-${colSize}`;
405
- }
406
- /**
407
- * Build HTML for all data items
408
- * @returns {string} Items HTML
409
- */
410
- buildItemsHTML() {
411
- return this.fields.map((field) => this.buildItemHTML(field)).filter(Boolean).join("");
412
- }
413
- /**
414
- * Build HTML for a single data item
415
- * @param {object} field - Field definition
416
- * @returns {string} Item HTML
417
- */
418
- buildItemHTML(field) {
419
- const value = this.getFieldValue(field);
420
- if (value === null && !this.dataViewOptions.showEmptyValues) {
421
- return "";
422
- }
423
- const label = field.label || this.formatLabel(field.name);
424
- const colClasses = this.getColumnClasses(field);
425
- return `
426
- <div class="${colClasses}">
427
- <div class="${this.dataViewOptions.itemClass} ${field.className}" data-field="${field.name}">
428
- ${this.buildLabelHTML(label, field)}
429
- ${this.buildValueHTML(value, field)}
430
- </div>
431
- </div>
432
- `;
433
- }
434
- /**
435
- * Build label HTML
436
- * @param {string} label - Label text
437
- * @param {object} field - Field definition
438
- * @returns {string} Label HTML
439
- */
440
- buildLabelHTML(label, field) {
441
- const labelClass = field.labelClass || this.dataViewOptions.labelClass;
442
- return `<div class="${labelClass}">${this.escapeHtml(label)}:</div>`;
443
- }
444
- /**
445
- * Build value HTML with type-specific formatting
446
- * @param {*} value - Field value
447
- * @param {object} field - Field definition
448
- * @returns {string} Value HTML
449
- */
450
- buildValueHTML(value, field) {
451
- const valueClass = field.valueClass || this.dataViewOptions.valueClass;
452
- const displayValue = this.formatDisplayValue(value, field);
453
- return `<div class="${valueClass}">${displayValue}</div>`;
454
- }
455
- /**
456
- * Format value for display with enhanced type handling
457
- * @param {*} value - Formatted value from DataFormatter (or raw if no format)
458
- * @param {object} field - Field definition
459
- * @returns {string} Formatted display value with HTML markup
460
- */
461
- formatDisplayValue(value, field) {
462
- if (value === null || value === void 0) {
463
- return this.dataViewOptions.emptyValueText;
464
- }
465
- if (field.template) {
466
- return String(value);
467
- }
468
- const formatString = field.format || field.formatter;
469
- if (formatString) {
470
- return String(value);
471
- }
472
- const rawValue = this.getData()[field.name];
473
- switch (field.type) {
474
- case "boolean":
475
- return rawValue ? '<span class="badge bg-success">Yes</span>' : '<span class="badge bg-secondary">No</span>';
476
- case "email":
477
- const emailStr = String(value);
478
- return `<a href="mailto:${this.escapeHtml(emailStr)}" class="text-decoration-none">${this.escapeHtml(emailStr)}</a>`;
479
- case "url":
480
- const urlStr = String(value);
481
- return `<a href="${this.escapeHtml(urlStr)}" target="_blank" rel="noopener" class="text-decoration-none">${this.escapeHtml(urlStr)} <i class="bi bi-box-arrow-up-right"></i></a>`;
482
- case "array":
483
- case "object":
484
- return this.formatAsJson(rawValue);
485
- case "dataview":
486
- return this.formatAsDataView(rawValue, field);
487
- case "phone":
488
- const phoneStr = String(value);
489
- const telHref = phoneStr.replace(/[^\d\+]/g, "");
490
- return `<a href="tel:${telHref}" class="text-decoration-none">${this.escapeHtml(phoneStr)}</a>`;
491
- default:
492
- return this.escapeHtml(String(value));
493
- }
494
- }
495
- /**
496
- * Format object/array values as styled JSON
497
- * @param {*} value - Object or array value
498
- * @returns {string} Formatted JSON HTML
499
- */
500
- formatAsJson(value) {
501
- try {
502
- const jsonString = JSON.stringify(value, null, 2);
503
- const escapedJson = this.escapeHtml(jsonString);
504
- const lines = jsonString.split("\n").length;
505
- const isLarge = lines > 10 || jsonString.length > 500;
506
- const uniqueId = `json-${Math.random().toString(36).substr(2, 9)}`;
507
- if (isLarge) {
508
- const preview = JSON.stringify(value).substring(0, 100) + (JSON.stringify(value).length > 100 ? "..." : "");
509
- const escapedPreview = this.escapeHtml(preview);
510
- return `
511
- <div class="json-container">
512
- <div class="d-flex align-items-center justify-content-between mb-1">
513
- <small class="text-muted">${Array.isArray(value) ? "Array" : "Object"} (${lines} lines)</small>
514
- <div class="btn-group btn-group-sm" role="group">
515
- <button type="button" class="btn btn-outline-secondary btn-sm json-toggle" data-bs-toggle="collapse" data-bs-target="#${uniqueId}" aria-expanded="false">
516
- <i class="bi bi-eye"></i> Show
517
- </button>
518
- <button type="button" class="btn btn-outline-secondary btn-sm json-copy" data-json='${this.escapeHtml(jsonString)}' title="Copy JSON">
519
- <i class="bi bi-clipboard"></i>
520
- </button>
521
- </div>
522
- </div>
523
- <div class="json-preview bg-light p-2 rounded small border" style="font-family: 'Courier New', monospace;">
524
- <code class="text-muted">${escapedPreview}</code>
525
- </div>
526
- <div class="collapse mt-2" id="${uniqueId}">
527
- <pre class="json-display p-3 rounded small mb-0" style="max-height: 400px; overflow-y: auto; white-space: pre-wrap; font-family: 'Courier New', monospace;"><code>${this.syntaxHighlightJson(escapedJson)}</code></pre>
528
- </div>
529
- </div>
530
- `;
531
- } else {
532
- return `
533
- <div class="json-container">
534
- <div class="d-flex align-items-center justify-content-between mb-1">
535
- <small class="text-muted">${Array.isArray(value) ? "Array" : "Object"}</small>
536
- <button type="button" class="btn btn-outline-secondary btn-sm json-copy" data-json='${this.escapeHtml(jsonString)}' title="Copy JSON">
537
- <i class="bi bi-clipboard"></i>
538
- </button>
539
- </div>
540
- <pre class="json-display bg-light p-2 rounded small mb-0 border" style="white-space: pre-wrap; font-family: 'Courier New', monospace;"><code>${this.syntaxHighlightJson(escapedJson)}</code></pre>
541
- </div>
542
- `;
543
- }
544
- } catch (error) {
545
- return `<span class="text-muted fst-italic">[Object: ${typeof value}] - Cannot display as JSON</span>`;
546
- }
547
- }
548
- /**
549
- * Apply basic syntax highlighting to JSON
550
- * @param {string} json - Escaped JSON string
551
- * @returns {string} JSON with basic syntax highlighting
552
- */
553
- syntaxHighlightJson(json) {
554
- return json.replace(/("([^"\\]|\\.)*")\s*:/g, '<span style="color: #0969da;">$1</span>:').replace(/:\s*("([^"\\]|\\.)*")/g, ': <span style="color: #0a3069;">$1</span>').replace(/:\s*(true|false)/g, ': <span style="color: #8250df;">$1</span>').replace(/:\s*(null)/g, ': <span style="color: #656d76;">$1</span>').replace(/:\s*(-?\d+\.?\d*)/g, ': <span style="color: #0550ae;">$1</span>');
555
- }
556
- /**
557
- * Bind events including JSON interaction handlers
558
- */
559
- bindEvents() {
560
- super.bindEvents();
561
- if (!this.element) return;
562
- this.element.addEventListener("click", (e) => {
563
- const fieldElement = e.target.closest("[data-field]");
564
- if (fieldElement) {
565
- const fieldName = fieldElement.dataset.field;
566
- const field = this.fields.find((f) => f.name === fieldName);
567
- this.emit("field:click", { field, fieldName, element: fieldElement, event: e });
568
- }
569
- if (e.target.closest(".json-copy")) {
570
- e.preventDefault();
571
- e.stopPropagation();
572
- this.handleJsonCopy(e.target.closest(".json-copy"));
573
- }
574
- if (e.target.closest(".json-toggle")) {
575
- this.handleJsonToggle(e.target.closest(".json-toggle"));
576
- }
577
- });
578
- }
579
- /**
580
- * Handle copying JSON to clipboard
581
- * @param {HTMLElement} button - Copy button element
582
- */
583
- handleJsonCopy(button) {
584
- const jsonData = button.getAttribute("data-json");
585
- if (!jsonData) return;
586
- try {
587
- if (navigator.clipboard && window.isSecureContext) {
588
- navigator.clipboard.writeText(jsonData).then(() => {
589
- this.showCopyFeedback(button);
590
- });
591
- } else {
592
- const textarea = document.createElement("textarea");
593
- textarea.value = jsonData;
594
- document.body.appendChild(textarea);
595
- textarea.select();
596
- document.execCommand("copy");
597
- document.body.removeChild(textarea);
598
- this.showCopyFeedback(button);
599
- }
600
- } catch (error) {
601
- console.warn("Failed to copy JSON:", error);
602
- }
603
- }
604
- /**
605
- * Handle JSON toggle button state
606
- * @param {HTMLElement} button - Toggle button element
607
- */
608
- handleJsonToggle(button) {
609
- const icon = button.querySelector("i");
610
- const isExpanded = button.getAttribute("aria-expanded") === "true";
611
- setTimeout(() => {
612
- if (isExpanded) {
613
- icon.className = "bi bi-eye-slash";
614
- button.innerHTML = '<i class="bi bi-eye-slash"></i> Hide';
615
- } else {
616
- icon.className = "bi bi-eye";
617
- button.innerHTML = '<i class="bi bi-eye"></i> Show';
618
- }
619
- }, 10);
620
- }
621
- /**
622
- * Show visual feedback for successful copy
623
- * @param {HTMLElement} button - Copy button element
624
- */
625
- showCopyFeedback(button) {
626
- const originalIcon = button.querySelector("i").className;
627
- const icon = button.querySelector("i");
628
- icon.className = "bi bi-check text-success";
629
- button.classList.add("btn-success");
630
- button.classList.remove("btn-outline-secondary");
631
- setTimeout(() => {
632
- icon.className = originalIcon;
633
- button.classList.remove("btn-success");
634
- button.classList.add("btn-outline-secondary");
635
- }, 1e3);
636
- }
637
- /**
638
- * Format complex objects as nested DataView
639
- * @param {object} value - Object value to display as DataView
640
- * @param {object} field - Field definition
641
- * @returns {string} Formatted DataView HTML
642
- */
643
- formatAsDataView(value, field) {
644
- if (!value || typeof value !== "object") {
645
- return `<span class="text-muted fst-italic">No data available</span>`;
646
- }
647
- try {
648
- const nestedView = new this.constructor({
649
- data: value,
650
- columns: field.dataViewColumns || 2,
651
- showEmptyValues: field.showEmptyValues ?? true,
652
- emptyValueText: field.emptyValueText || "Not set",
653
- // Pass any other dataView-specific options from field config
654
- ...field.dataViewOptions || {}
655
- });
656
- nestedView.onInit();
657
- nestedView.generateFieldsFromData();
658
- const nestedHtml = nestedView.buildItemsHTML();
659
- return `
660
- <div class="nested-dataview border rounded p-3 bg-light">
661
- <div class="${nestedView.dataViewOptions.rowClass}">
662
- ${nestedHtml}
663
- </div>
664
- </div>
665
- `;
666
- } catch (error) {
667
- console.error("Error creating nested DataView:", error);
668
- return `<span class="text-danger">Error displaying nested data</span>`;
669
- }
670
- }
671
- /**
672
- * Update the data and re-render
673
- * @param {object} newData - New data object
674
- * @returns {Promise<DataView>} Promise resolving to this instance
675
- */
676
- async updateData(newData) {
677
- this.data = newData;
678
- if (this.model && typeof this.model.set === "function") {
679
- this.model.set(newData);
680
- }
681
- if (this.fields.length > 0 && !this.options.fields) {
682
- this.fields = [];
683
- }
684
- await this.render();
685
- this.emit("data:updated", { data: newData });
686
- return this;
687
- }
688
- /**
689
- * Update field configuration and re-render
690
- * @param {array} newFields - New field configuration
691
- * @returns {Promise<DataView>} Promise resolving to this instance
692
- */
693
- async updateFields(newFields) {
694
- this.fields = newFields;
695
- await this.render();
696
- this.emit("fields:updated", { fields: newFields });
697
- return this;
698
- }
699
- /**
700
- * Update configuration and re-render
701
- * @param {object} newOptions - New configuration options
702
- * @returns {Promise<DataView>} Promise resolving to this instance
703
- */
704
- async updateConfig(newOptions) {
705
- this.dataViewOptions = { ...this.dataViewOptions, ...newOptions };
706
- await this.render();
707
- this.emit("config:updated", { options: this.dataViewOptions });
708
- return this;
709
- }
710
- /**
711
- * Refresh data from model if available
712
- * @returns {Promise<DataView>} Promise resolving to this instance
713
- */
714
- async refresh() {
715
- if (this.model && typeof this.model.fetch === "function") {
716
- try {
717
- await this.model.fetch();
718
- this.emit("data:refreshed", { model: this.model });
719
- } catch (error) {
720
- this.emit("error", { error, message: "Failed to refresh data" });
721
- throw error;
722
- }
723
- }
724
- return this;
725
- }
726
- /**
727
- * Get current data
728
- * @returns {object} Current data object
729
- */
730
- getCurrentData() {
731
- return this.getData();
732
- }
733
- /**
734
- * Get field definition by name
735
- * @param {string} name - Field name
736
- * @returns {object|null} Field definition
737
- */
738
- getField(name) {
739
- return this.fields.find((field) => field.name === name) || null;
740
- }
741
- /**
742
- * Set custom format for a specific field
743
- * @param {string} fieldName - Name of the field
744
- * @param {string} format - Pipe format string (e.g., "currency|uppercase")
745
- * @returns {DataView} This instance for chaining
746
- */
747
- setFieldFormat(fieldName, format) {
748
- const field = this.getField(fieldName);
749
- if (field) {
750
- field.format = format;
751
- field.formatter = format;
752
- } else {
753
- this.fields.push({
754
- name: fieldName,
755
- label: this.formatLabel(fieldName),
756
- type: this.inferFieldType(this.getData()[fieldName], fieldName),
757
- format,
758
- formatter: format
759
- // Keep both in sync
760
- });
761
- }
762
- return this;
763
- }
764
- /**
765
- * Add additional formatter to existing field format pipe chain
766
- * @param {string} fieldName - Name of the field
767
- * @param {string} formatter - Formatter to add (e.g., "uppercase", "truncate(50)")
768
- * @returns {DataView} This instance for chaining
769
- */
770
- addFormatPipe(fieldName, formatter) {
771
- const field = this.getField(fieldName);
772
- if (field) {
773
- if (field.format) {
774
- field.format += `|${formatter}`;
775
- field.formatter = field.format;
776
- } else {
777
- field.format = formatter;
778
- field.formatter = formatter;
779
- }
780
- }
781
- return this;
782
- }
783
- /**
784
- * Clear custom format for a field (revert to auto-inferred format)
785
- * @param {string} fieldName - Name of the field
786
- * @returns {DataView} This instance for chaining
787
- */
788
- clearFieldFormat(fieldName) {
789
- const field = this.getField(fieldName);
790
- if (field) {
791
- const data = this.getData();
792
- const inferred = this.inferFormatter(data[fieldName], fieldName, field.type);
793
- field.format = inferred;
794
- field.formatter = inferred;
795
- }
796
- return this;
797
- }
798
- /**
799
- * Get formatted value for a specific field without rendering
800
- * @param {string} fieldName - Name of the field
801
- * @param {*} value - Optional value to format (uses current data if not provided)
802
- * @returns {*} Formatted value
803
- */
804
- getFormattedValue(fieldName, value = null) {
805
- const field = this.getField(fieldName);
806
- if (!field) return null;
807
- const targetValue = value !== null ? value : this.getData()[fieldName];
808
- const formatString = field.format || field.formatter;
809
- if (formatString && targetValue != null) {
810
- return dataFormatter.pipe(targetValue, formatString);
811
- }
812
- return targetValue;
813
- }
814
- /**
815
- * Set multiple field formats at once
816
- * @param {object} formats - Object mapping field names to format strings
817
- * @returns {DataView} This instance for chaining
818
- */
819
- setFieldFormats(formats) {
820
- Object.entries(formats).forEach(([fieldName, format]) => {
821
- this.setFieldFormat(fieldName, format);
822
- });
823
- return this;
824
- }
825
- /**
826
- * Get all current field formats as an object
827
- * @returns {object} Object mapping field names to their current formats
828
- */
829
- getFieldFormats() {
830
- const formats = {};
831
- this.fields.forEach((field) => {
832
- const formatString = field.format || field.formatter;
833
- if (formatString) {
834
- formats[field.name] = formatString;
835
- }
836
- });
837
- return formats;
838
- }
839
- /**
840
- * Set up model event listeners if model is provided
841
- */
842
- onInit() {
843
- super.onInit();
844
- if (this.model && typeof this.model.on === "function") {
845
- this.model.on("change", () => {
846
- this.render();
847
- });
848
- }
849
- }
850
- /**
851
- * Static factory method
852
- * @param {object} options - DataView options
853
- * @returns {DataView} New DataView instance
854
- */
855
- static create(options = {}) {
856
- return new DataView(options);
857
- }
858
- }
859
- export {
860
- DataView as default
861
- };
862
- //# sourceMappingURL=DataView-Dzo0jbs2.js.map