web-mojo 2.1.936 → 2.1.954

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 (102) 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 +122 -229
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.cjs.js.map +1 -1
  7. package/dist/auth.es.js +7 -7
  8. package/dist/auth.es.js.map +1 -1
  9. package/dist/charts.cjs.js +1 -1
  10. package/dist/charts.es.js +7 -7
  11. package/dist/chunks/ChatView-BKa78eKF.js +2 -0
  12. package/dist/chunks/ChatView-BKa78eKF.js.map +1 -0
  13. package/dist/chunks/{ChatView-DlSxjxah.js → ChatView-Dcz7LkwL.js} +89 -574
  14. package/dist/chunks/ChatView-Dcz7LkwL.js.map +1 -0
  15. package/dist/chunks/Collection-OP7c9Zyu.js +989 -0
  16. package/dist/chunks/Collection-OP7c9Zyu.js.map +1 -0
  17. package/dist/chunks/Collection-aQF5eOUH.js +2 -0
  18. package/dist/chunks/Collection-aQF5eOUH.js.map +1 -0
  19. package/dist/chunks/ContextMenu-DV_wsr9B.js +3 -0
  20. package/dist/chunks/ContextMenu-DV_wsr9B.js.map +1 -0
  21. package/dist/chunks/ContextMenu-ixMyAUGS.js +1171 -0
  22. package/dist/chunks/ContextMenu-ixMyAUGS.js.map +1 -0
  23. package/dist/chunks/DataView-CdDY9ijM.js +2 -0
  24. package/dist/chunks/{DataView-XJbTQ5q0.js.map → DataView-CdDY9ijM.js.map} +1 -1
  25. package/dist/chunks/{DataView-Vmjx4eCr.js → DataView-OUqaLmGB.js} +2 -2
  26. package/dist/chunks/{DataView-Vmjx4eCr.js.map → DataView-OUqaLmGB.js.map} +1 -1
  27. package/dist/chunks/{Dialog-D_rAf4gQ.js → Dialog-C2mRUxga.js} +8 -6
  28. package/dist/chunks/{Dialog-D_rAf4gQ.js.map → Dialog-C2mRUxga.js.map} +1 -1
  29. package/dist/chunks/Dialog-Cl6MN8if.js +2 -0
  30. package/dist/chunks/{Dialog-BinTQTfO.js.map → Dialog-Cl6MN8if.js.map} +1 -1
  31. package/dist/chunks/FormView-BSWaXDav.js +3 -0
  32. package/dist/chunks/{FormView-TPFsq8ZX.js.map → FormView-BSWaXDav.js.map} +1 -1
  33. package/dist/chunks/{FormView-CIriLDZY.js → FormView-HWvIdFkB.js} +10 -6
  34. package/dist/chunks/FormView-HWvIdFkB.js.map +1 -0
  35. package/dist/chunks/ListView-Dcz0Gs6D.js +492 -0
  36. package/dist/chunks/ListView-Dcz0Gs6D.js.map +1 -0
  37. package/dist/chunks/ListView-eXgn0F0-.js +2 -0
  38. package/dist/chunks/ListView-eXgn0F0-.js.map +1 -0
  39. package/dist/chunks/MetricsMiniChartWidget-BkTEO87S.js +2 -0
  40. package/dist/chunks/{MetricsMiniChartWidget-sONcM0pG.js.map → MetricsMiniChartWidget-BkTEO87S.js.map} +1 -1
  41. package/dist/chunks/{MetricsMiniChartWidget-BolRZ-Ja.js → MetricsMiniChartWidget-Y70IHFIe.js} +3 -3
  42. package/dist/chunks/{MetricsMiniChartWidget-BolRZ-Ja.js.map → MetricsMiniChartWidget-Y70IHFIe.js.map} +1 -1
  43. package/dist/chunks/PDFViewer-C0aMqGJL.js +2 -0
  44. package/dist/chunks/{PDFViewer-UBhinN8A.js.map → PDFViewer-C0aMqGJL.js.map} +1 -1
  45. package/dist/chunks/{PDFViewer-D6SKOl85.js → PDFViewer-DkbYnnoV.js} +3 -3
  46. package/dist/chunks/{PDFViewer-D6SKOl85.js.map → PDFViewer-DkbYnnoV.js.map} +1 -1
  47. package/dist/chunks/Page-CvbwEoLv.js +2 -0
  48. package/dist/chunks/{Page-CnvHhwLZ.js.map → Page-CvbwEoLv.js.map} +1 -1
  49. package/dist/chunks/{Page-B7L25Omb.js → Page-Deq4y2Kq.js} +2 -2
  50. package/dist/chunks/{Page-B7L25Omb.js.map → Page-Deq4y2Kq.js.map} +1 -1
  51. package/dist/chunks/Rest-BNYqGlnP.js +2 -0
  52. package/dist/chunks/Rest-BNYqGlnP.js.map +1 -0
  53. package/dist/chunks/{WebApp-El07OMHH.js → Rest-CS4jRCAs.js} +5 -1389
  54. package/dist/chunks/Rest-CS4jRCAs.js.map +1 -0
  55. package/dist/chunks/{TopNav-CPA884W7.js → TopNav-DXLRdU0o.js} +5 -5
  56. package/dist/chunks/{TopNav-CPA884W7.js.map → TopNav-DXLRdU0o.js.map} +1 -1
  57. package/dist/chunks/TopNav-GyvI31l2.js +2 -0
  58. package/dist/chunks/{TopNav-Dcmcic-i.js.map → TopNav-GyvI31l2.js.map} +1 -1
  59. package/dist/chunks/WebApp-BAadsDpO.js +2 -0
  60. package/dist/chunks/WebApp-BAadsDpO.js.map +1 -0
  61. package/dist/chunks/WebApp-BVGZC2rj.js +1388 -0
  62. package/dist/chunks/WebApp-BVGZC2rj.js.map +1 -0
  63. package/dist/css/web-mojo.css +2 -2
  64. package/dist/docit.cjs.js +1 -1
  65. package/dist/docit.cjs.js.map +1 -1
  66. package/dist/docit.es.js +12 -10
  67. package/dist/docit.es.js.map +1 -1
  68. package/dist/index.cjs.js +1 -1
  69. package/dist/index.cjs.js.map +1 -1
  70. package/dist/index.es.js +120 -116
  71. package/dist/index.es.js.map +1 -1
  72. package/dist/lightbox.cjs.js +1 -1
  73. package/dist/lightbox.cjs.js.map +1 -1
  74. package/dist/lightbox.es.js +121 -121
  75. package/dist/lightbox.es.js.map +1 -1
  76. package/dist/map.cjs.js +2 -0
  77. package/dist/map.cjs.js.map +1 -0
  78. package/dist/map.es.js +188 -0
  79. package/dist/map.es.js.map +1 -0
  80. package/dist/timeline.cjs.js +2 -0
  81. package/dist/timeline.cjs.js.map +1 -0
  82. package/dist/timeline.es.js +225 -0
  83. package/dist/timeline.es.js.map +1 -0
  84. package/package.json +9 -1
  85. package/dist/chunks/ChatView-DlSxjxah.js.map +0 -1
  86. package/dist/chunks/ChatView-DnqrGXMC.js +0 -2
  87. package/dist/chunks/ChatView-DnqrGXMC.js.map +0 -1
  88. package/dist/chunks/ContextMenu-CE77rUmn.js +0 -2155
  89. package/dist/chunks/ContextMenu-CE77rUmn.js.map +0 -1
  90. package/dist/chunks/ContextMenu-KVxd0Kgd.js +0 -3
  91. package/dist/chunks/ContextMenu-KVxd0Kgd.js.map +0 -1
  92. package/dist/chunks/DataView-XJbTQ5q0.js +0 -2
  93. package/dist/chunks/Dialog-BinTQTfO.js +0 -2
  94. package/dist/chunks/FormView-CIriLDZY.js.map +0 -1
  95. package/dist/chunks/FormView-TPFsq8ZX.js +0 -3
  96. package/dist/chunks/MetricsMiniChartWidget-sONcM0pG.js +0 -2
  97. package/dist/chunks/PDFViewer-UBhinN8A.js +0 -2
  98. package/dist/chunks/Page-CnvHhwLZ.js +0 -2
  99. package/dist/chunks/TopNav-Dcmcic-i.js +0 -2
  100. package/dist/chunks/WebApp-El07OMHH.js.map +0 -1
  101. package/dist/chunks/WebApp-b9DQWz1d.js +0 -2
  102. package/dist/chunks/WebApp-b9DQWz1d.js.map +0 -1
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./chunks/Rest-BNYqGlnP.js"),e=require("./chunks/Collection-aQF5eOUH.js");class MapView extends t.View{constructor(t={}){super({className:"map-view",...t}),this.markers=t.markers||[],this.center=t.center||null,this.zoom=t.zoom||13,this.height=t.height||400,this.showZoomControl=!1!==t.showZoomControl,this.tileLayer=t.tileLayer||"osm",this.map=null,this.leafletMarkers=[],this.template='\n <div class="map-container">\n <div id="map-{{id}}" style="height: {{height}}px; width: 100%; border-radius: 0.375rem; border: 1px solid #dee2e6;"></div>\n </div>\n '}async onAfterRender(){await this.loadLeaflet(),await this.initializeMap()}async loadLeaflet(){if(window.L)return;const t=new Promise(t=>{const e=document.createElement("link");e.rel="stylesheet",e.href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css",e.onload=t,e.onerror=t,document.head.appendChild(e)}),e=new Promise((t,e)=>{const r=document.createElement("script");r.src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js",r.onload=t,r.onerror=e,document.head.appendChild(r)});await Promise.all([t,e])}getTileLayerUrl(){const t={osm:{url:"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",attribution:"© OpenStreetMap contributors",maxZoom:19},satellite:{url:"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",attribution:"© Esri",maxZoom:19},terrain:{url:"https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",attribution:"© OpenTopoMap contributors",maxZoom:17},dark:{url:"https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png",attribution:"© OpenStreetMap contributors © CARTO",maxZoom:20},light:{url:"https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",attribution:"© OpenStreetMap contributors © CARTO",maxZoom:20},watercolor:{url:"https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg",attribution:"© Stadia Maps © Stamen Design © OpenStreetMap contributors",maxZoom:16},bw:{url:"https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png",attribution:"© OpenStreetMap contributors © CARTO",maxZoom:20},streets:{url:"https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png",attribution:"© OpenStreetMap contributors © CARTO",maxZoom:20}};return t[this.tileLayer]||t.osm}async initializeMap(){const t=this.element.querySelector(`#map-${this.id}`);if(!t||!window.L)return;let e=this.center;!e&&this.markers.length>0&&(e=[this.markers[0].lat,this.markers[0].lng]),e||(e=[0,0]),this.map=window.L.map(t,{center:e,zoom:this.zoom,zoomControl:this.showZoomControl});const r=this.getTileLayerUrl();window.L.tileLayer(r.url,{attribution:r.attribution,maxZoom:r.maxZoom}).addTo(this.map),this.addMarkers(this.markers),this.markers.length>1&&this.fitBounds(),setTimeout(()=>{this.map&&this.map.invalidateSize()},300)}addMarkers(t){this.map&&Array.isArray(t)&&t.forEach(t=>{const{lat:e,lng:r,popup:s,icon:o}=t;if(!e||!r)return;const a={};o&&(a.icon=window.L.icon(o));const i=window.L.marker([e,r],a).addTo(this.map);s&&i.bindPopup(s),this.leafletMarkers.push(i)})}fitBounds(){if(!this.map||0===this.leafletMarkers.length)return;const t=new window.L.featureGroup(this.leafletMarkers);this.map.fitBounds(t.getBounds().pad(.1))}updateMarkers(t){this.clearMarkers(),this.markers=t,this.addMarkers(t),t.length>1?this.fitBounds():1===t.length&&this.map.setView([t[0].lat,t[0].lng],this.zoom)}clearMarkers(){this.leafletMarkers.forEach(t=>{this.map.removeLayer(t)}),this.leafletMarkers=[]}setView(t,e,r=null){this.map&&this.map.setView([t,e],r||this.zoom)}setZoom(t){this.map&&this.map.setZoom(t)}async onBeforeDestroy(){this.map&&(this.map.remove(),this.map=null),await super.onBeforeDestroy()}}exports.View=t.View,exports.Collection=e.Collection,exports.Model=e.Model,exports.MapView=MapView;
2
+ //# sourceMappingURL=map.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map.cjs.js","sources":["../src/extensions/map/MapView.js"],"sourcesContent":["/**\n * MapView - Interactive map component using Leaflet\n * \n * Features:\n * - Display single or multiple markers\n * - Auto-zoom to fit markers\n * - Customizable marker popups\n * - Support for different tile layers\n * \n * @example\n * const mapView = new MapView({\n * markers: [\n * { lat: 37.422, lng: -122.084, popup: 'Mountain View, CA' }\n * ],\n * zoom: 10,\n * height: 400\n * });\n */\n\nimport View from '@core/View.js';\n\nclass MapView extends View {\n constructor(options = {}) {\n super({\n className: 'map-view',\n ...options\n });\n\n this.markers = options.markers || [];\n this.center = options.center || null;\n this.zoom = options.zoom || 13;\n this.height = options.height || 400;\n this.showZoomControl = options.showZoomControl !== false;\n this.tileLayer = options.tileLayer || 'osm'; // 'osm', 'satellite', 'terrain'\n \n this.map = null;\n this.leafletMarkers = [];\n\n this.template = `\n <div class=\"map-container\">\n <div id=\"map-{{id}}\" style=\"height: {{height}}px; width: 100%; border-radius: 0.375rem; border: 1px solid #dee2e6;\"></div>\n </div>\n `;\n }\n\n async onAfterRender() {\n await this.loadLeaflet();\n await this.initializeMap();\n }\n\n async loadLeaflet() {\n // Check if Leaflet is already loaded\n if (window.L) return;\n\n // Load Leaflet CSS and wait for it\n const cssLoaded = new Promise((resolve) => {\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';\n link.onload = resolve;\n link.onerror = resolve; // Continue even if CSS fails\n document.head.appendChild(link);\n });\n\n // Load Leaflet JS\n const jsLoaded = new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';\n script.onload = resolve;\n script.onerror = reject;\n document.head.appendChild(script);\n });\n\n // Wait for both CSS and JS to load\n await Promise.all([cssLoaded, jsLoaded]);\n }\n\n getTileLayerUrl() {\n const tileLayers = {\n // Standard street maps\n osm: {\n url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',\n attribution: '© OpenStreetMap contributors',\n maxZoom: 19\n },\n \n // Satellite imagery\n satellite: {\n url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',\n attribution: '© Esri',\n maxZoom: 19\n },\n \n // Terrain and topographic\n terrain: {\n url: 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',\n attribution: '© OpenTopoMap contributors',\n maxZoom: 17\n },\n \n // Dark mode styles\n dark: {\n url: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',\n attribution: '© OpenStreetMap contributors © CARTO',\n maxZoom: 20\n },\n \n // Light/minimal styles\n light: {\n url: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',\n attribution: '© OpenStreetMap contributors © CARTO',\n maxZoom: 20\n },\n \n // Watercolor artistic style\n watercolor: {\n url: 'https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg',\n attribution: '© Stadia Maps © Stamen Design © OpenStreetMap contributors',\n maxZoom: 16\n },\n \n // Black and white\n bw: {\n url: 'https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png',\n attribution: '© OpenStreetMap contributors © CARTO',\n maxZoom: 20\n },\n \n // Streets with labels\n streets: {\n url: 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png',\n attribution: '© OpenStreetMap contributors © CARTO',\n maxZoom: 20\n }\n };\n\n return tileLayers[this.tileLayer] || tileLayers.osm;\n }\n\n async initializeMap() {\n const mapElement = this.element.querySelector(`#map-${this.id}`);\n if (!mapElement || !window.L) return;\n\n // Determine map center\n let mapCenter = this.center;\n if (!mapCenter && this.markers.length > 0) {\n // Use first marker as center\n mapCenter = [this.markers[0].lat, this.markers[0].lng];\n }\n if (!mapCenter) {\n // Default to world view\n mapCenter = [0, 0];\n }\n\n // Create map\n this.map = window.L.map(mapElement, {\n center: mapCenter,\n zoom: this.zoom,\n zoomControl: this.showZoomControl\n });\n\n // Add tile layer\n const tileConfig = this.getTileLayerUrl();\n window.L.tileLayer(tileConfig.url, {\n attribution: tileConfig.attribution,\n maxZoom: tileConfig.maxZoom\n }).addTo(this.map);\n\n // Add markers\n this.addMarkers(this.markers);\n\n // Auto-fit bounds if multiple markers\n if (this.markers.length > 1) {\n this.fitBounds();\n }\n\n // Fix tile rendering issues by invalidating size after a delay\n // This ensures the container has proper dimensions and CSS is applied\n setTimeout(() => {\n if (this.map) {\n this.map.invalidateSize();\n }\n }, 300);\n }\n\n addMarkers(markers) {\n if (!this.map || !Array.isArray(markers)) return;\n\n markers.forEach(markerData => {\n const { lat, lng, popup, icon } = markerData;\n \n if (!lat || !lng) return;\n\n const markerOptions = {};\n \n // Custom icon if provided\n if (icon) {\n markerOptions.icon = window.L.icon(icon);\n }\n\n const marker = window.L.marker([lat, lng], markerOptions).addTo(this.map);\n\n // Add popup if provided\n if (popup) {\n marker.bindPopup(popup);\n }\n\n this.leafletMarkers.push(marker);\n });\n }\n\n fitBounds() {\n if (!this.map || this.leafletMarkers.length === 0) return;\n\n const group = new window.L.featureGroup(this.leafletMarkers);\n this.map.fitBounds(group.getBounds().pad(0.1));\n }\n\n updateMarkers(newMarkers) {\n // Clear existing markers\n this.clearMarkers();\n \n // Add new markers\n this.markers = newMarkers;\n this.addMarkers(newMarkers);\n \n // Fit bounds if multiple markers\n if (newMarkers.length > 1) {\n this.fitBounds();\n } else if (newMarkers.length === 1) {\n this.map.setView([newMarkers[0].lat, newMarkers[0].lng], this.zoom);\n }\n }\n\n clearMarkers() {\n this.leafletMarkers.forEach(marker => {\n this.map.removeLayer(marker);\n });\n this.leafletMarkers = [];\n }\n\n setView(lat, lng, zoom = null) {\n if (!this.map) return;\n this.map.setView([lat, lng], zoom || this.zoom);\n }\n\n setZoom(zoom) {\n if (!this.map) return;\n this.map.setZoom(zoom);\n }\n\n async onBeforeDestroy() {\n if (this.map) {\n this.map.remove();\n this.map = null;\n }\n await super.onBeforeDestroy();\n }\n}\n\nexport default MapView;\n"],"names":["MapView","View","constructor","options","super","className","this","markers","center","zoom","height","showZoomControl","tileLayer","map","leafletMarkers","template","onAfterRender","loadLeaflet","initializeMap","window","L","cssLoaded","Promise","resolve","link","document","createElement","rel","href","onload","onerror","head","appendChild","jsLoaded","reject","script","src","all","getTileLayerUrl","tileLayers","osm","url","attribution","maxZoom","satellite","terrain","dark","light","watercolor","bw","streets","mapElement","element","querySelector","id","mapCenter","length","lat","lng","zoomControl","tileConfig","addTo","addMarkers","fitBounds","setTimeout","invalidateSize","Array","isArray","forEach","markerData","popup","icon","markerOptions","marker","bindPopup","push","group","featureGroup","getBounds","pad","updateMarkers","newMarkers","clearMarkers","setView","removeLayer","setZoom","onBeforeDestroy","remove"],"mappings":"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"}
package/dist/map.es.js ADDED
@@ -0,0 +1,188 @@
1
+ import { V as View } from "./chunks/Rest-CS4jRCAs.js";
2
+ import { C, M } from "./chunks/Collection-OP7c9Zyu.js";
3
+ class MapView extends View {
4
+ constructor(options = {}) {
5
+ super({
6
+ className: "map-view",
7
+ ...options
8
+ });
9
+ this.markers = options.markers || [];
10
+ this.center = options.center || null;
11
+ this.zoom = options.zoom || 13;
12
+ this.height = options.height || 400;
13
+ this.showZoomControl = options.showZoomControl !== false;
14
+ this.tileLayer = options.tileLayer || "osm";
15
+ this.map = null;
16
+ this.leafletMarkers = [];
17
+ this.template = `
18
+ <div class="map-container">
19
+ <div id="map-{{id}}" style="height: {{height}}px; width: 100%; border-radius: 0.375rem; border: 1px solid #dee2e6;"></div>
20
+ </div>
21
+ `;
22
+ }
23
+ async onAfterRender() {
24
+ await this.loadLeaflet();
25
+ await this.initializeMap();
26
+ }
27
+ async loadLeaflet() {
28
+ if (window.L) return;
29
+ const cssLoaded = new Promise((resolve) => {
30
+ const link = document.createElement("link");
31
+ link.rel = "stylesheet";
32
+ link.href = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.css";
33
+ link.onload = resolve;
34
+ link.onerror = resolve;
35
+ document.head.appendChild(link);
36
+ });
37
+ const jsLoaded = new Promise((resolve, reject) => {
38
+ const script = document.createElement("script");
39
+ script.src = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js";
40
+ script.onload = resolve;
41
+ script.onerror = reject;
42
+ document.head.appendChild(script);
43
+ });
44
+ await Promise.all([cssLoaded, jsLoaded]);
45
+ }
46
+ getTileLayerUrl() {
47
+ const tileLayers = {
48
+ // Standard street maps
49
+ osm: {
50
+ url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
51
+ attribution: "© OpenStreetMap contributors",
52
+ maxZoom: 19
53
+ },
54
+ // Satellite imagery
55
+ satellite: {
56
+ url: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
57
+ attribution: "© Esri",
58
+ maxZoom: 19
59
+ },
60
+ // Terrain and topographic
61
+ terrain: {
62
+ url: "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
63
+ attribution: "© OpenTopoMap contributors",
64
+ maxZoom: 17
65
+ },
66
+ // Dark mode styles
67
+ dark: {
68
+ url: "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png",
69
+ attribution: "© OpenStreetMap contributors © CARTO",
70
+ maxZoom: 20
71
+ },
72
+ // Light/minimal styles
73
+ light: {
74
+ url: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
75
+ attribution: "© OpenStreetMap contributors © CARTO",
76
+ maxZoom: 20
77
+ },
78
+ // Watercolor artistic style
79
+ watercolor: {
80
+ url: "https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg",
81
+ attribution: "© Stadia Maps © Stamen Design © OpenStreetMap contributors",
82
+ maxZoom: 16
83
+ },
84
+ // Black and white
85
+ bw: {
86
+ url: "https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png",
87
+ attribution: "© OpenStreetMap contributors © CARTO",
88
+ maxZoom: 20
89
+ },
90
+ // Streets with labels
91
+ streets: {
92
+ url: "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png",
93
+ attribution: "© OpenStreetMap contributors © CARTO",
94
+ maxZoom: 20
95
+ }
96
+ };
97
+ return tileLayers[this.tileLayer] || tileLayers.osm;
98
+ }
99
+ async initializeMap() {
100
+ const mapElement = this.element.querySelector(`#map-${this.id}`);
101
+ if (!mapElement || !window.L) return;
102
+ let mapCenter = this.center;
103
+ if (!mapCenter && this.markers.length > 0) {
104
+ mapCenter = [this.markers[0].lat, this.markers[0].lng];
105
+ }
106
+ if (!mapCenter) {
107
+ mapCenter = [0, 0];
108
+ }
109
+ this.map = window.L.map(mapElement, {
110
+ center: mapCenter,
111
+ zoom: this.zoom,
112
+ zoomControl: this.showZoomControl
113
+ });
114
+ const tileConfig = this.getTileLayerUrl();
115
+ window.L.tileLayer(tileConfig.url, {
116
+ attribution: tileConfig.attribution,
117
+ maxZoom: tileConfig.maxZoom
118
+ }).addTo(this.map);
119
+ this.addMarkers(this.markers);
120
+ if (this.markers.length > 1) {
121
+ this.fitBounds();
122
+ }
123
+ setTimeout(() => {
124
+ if (this.map) {
125
+ this.map.invalidateSize();
126
+ }
127
+ }, 300);
128
+ }
129
+ addMarkers(markers) {
130
+ if (!this.map || !Array.isArray(markers)) return;
131
+ markers.forEach((markerData) => {
132
+ const { lat, lng, popup, icon } = markerData;
133
+ if (!lat || !lng) return;
134
+ const markerOptions = {};
135
+ if (icon) {
136
+ markerOptions.icon = window.L.icon(icon);
137
+ }
138
+ const marker = window.L.marker([lat, lng], markerOptions).addTo(this.map);
139
+ if (popup) {
140
+ marker.bindPopup(popup);
141
+ }
142
+ this.leafletMarkers.push(marker);
143
+ });
144
+ }
145
+ fitBounds() {
146
+ if (!this.map || this.leafletMarkers.length === 0) return;
147
+ const group = new window.L.featureGroup(this.leafletMarkers);
148
+ this.map.fitBounds(group.getBounds().pad(0.1));
149
+ }
150
+ updateMarkers(newMarkers) {
151
+ this.clearMarkers();
152
+ this.markers = newMarkers;
153
+ this.addMarkers(newMarkers);
154
+ if (newMarkers.length > 1) {
155
+ this.fitBounds();
156
+ } else if (newMarkers.length === 1) {
157
+ this.map.setView([newMarkers[0].lat, newMarkers[0].lng], this.zoom);
158
+ }
159
+ }
160
+ clearMarkers() {
161
+ this.leafletMarkers.forEach((marker) => {
162
+ this.map.removeLayer(marker);
163
+ });
164
+ this.leafletMarkers = [];
165
+ }
166
+ setView(lat, lng, zoom = null) {
167
+ if (!this.map) return;
168
+ this.map.setView([lat, lng], zoom || this.zoom);
169
+ }
170
+ setZoom(zoom) {
171
+ if (!this.map) return;
172
+ this.map.setZoom(zoom);
173
+ }
174
+ async onBeforeDestroy() {
175
+ if (this.map) {
176
+ this.map.remove();
177
+ this.map = null;
178
+ }
179
+ await super.onBeforeDestroy();
180
+ }
181
+ }
182
+ export {
183
+ C as Collection,
184
+ MapView,
185
+ M as Model,
186
+ View
187
+ };
188
+ //# sourceMappingURL=map.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map.es.js","sources":["../src/extensions/map/MapView.js"],"sourcesContent":["/**\n * MapView - Interactive map component using Leaflet\n * \n * Features:\n * - Display single or multiple markers\n * - Auto-zoom to fit markers\n * - Customizable marker popups\n * - Support for different tile layers\n * \n * @example\n * const mapView = new MapView({\n * markers: [\n * { lat: 37.422, lng: -122.084, popup: 'Mountain View, CA' }\n * ],\n * zoom: 10,\n * height: 400\n * });\n */\n\nimport View from '@core/View.js';\n\nclass MapView extends View {\n constructor(options = {}) {\n super({\n className: 'map-view',\n ...options\n });\n\n this.markers = options.markers || [];\n this.center = options.center || null;\n this.zoom = options.zoom || 13;\n this.height = options.height || 400;\n this.showZoomControl = options.showZoomControl !== false;\n this.tileLayer = options.tileLayer || 'osm'; // 'osm', 'satellite', 'terrain'\n \n this.map = null;\n this.leafletMarkers = [];\n\n this.template = `\n <div class=\"map-container\">\n <div id=\"map-{{id}}\" style=\"height: {{height}}px; width: 100%; border-radius: 0.375rem; border: 1px solid #dee2e6;\"></div>\n </div>\n `;\n }\n\n async onAfterRender() {\n await this.loadLeaflet();\n await this.initializeMap();\n }\n\n async loadLeaflet() {\n // Check if Leaflet is already loaded\n if (window.L) return;\n\n // Load Leaflet CSS and wait for it\n const cssLoaded = new Promise((resolve) => {\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';\n link.onload = resolve;\n link.onerror = resolve; // Continue even if CSS fails\n document.head.appendChild(link);\n });\n\n // Load Leaflet JS\n const jsLoaded = new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';\n script.onload = resolve;\n script.onerror = reject;\n document.head.appendChild(script);\n });\n\n // Wait for both CSS and JS to load\n await Promise.all([cssLoaded, jsLoaded]);\n }\n\n getTileLayerUrl() {\n const tileLayers = {\n // Standard street maps\n osm: {\n url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',\n attribution: '© OpenStreetMap contributors',\n maxZoom: 19\n },\n \n // Satellite imagery\n satellite: {\n url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',\n attribution: '© Esri',\n maxZoom: 19\n },\n \n // Terrain and topographic\n terrain: {\n url: 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',\n attribution: '© OpenTopoMap contributors',\n maxZoom: 17\n },\n \n // Dark mode styles\n dark: {\n url: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',\n attribution: '© OpenStreetMap contributors © CARTO',\n maxZoom: 20\n },\n \n // Light/minimal styles\n light: {\n url: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',\n attribution: '© OpenStreetMap contributors © CARTO',\n maxZoom: 20\n },\n \n // Watercolor artistic style\n watercolor: {\n url: 'https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg',\n attribution: '© Stadia Maps © Stamen Design © OpenStreetMap contributors',\n maxZoom: 16\n },\n \n // Black and white\n bw: {\n url: 'https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png',\n attribution: '© OpenStreetMap contributors © CARTO',\n maxZoom: 20\n },\n \n // Streets with labels\n streets: {\n url: 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png',\n attribution: '© OpenStreetMap contributors © CARTO',\n maxZoom: 20\n }\n };\n\n return tileLayers[this.tileLayer] || tileLayers.osm;\n }\n\n async initializeMap() {\n const mapElement = this.element.querySelector(`#map-${this.id}`);\n if (!mapElement || !window.L) return;\n\n // Determine map center\n let mapCenter = this.center;\n if (!mapCenter && this.markers.length > 0) {\n // Use first marker as center\n mapCenter = [this.markers[0].lat, this.markers[0].lng];\n }\n if (!mapCenter) {\n // Default to world view\n mapCenter = [0, 0];\n }\n\n // Create map\n this.map = window.L.map(mapElement, {\n center: mapCenter,\n zoom: this.zoom,\n zoomControl: this.showZoomControl\n });\n\n // Add tile layer\n const tileConfig = this.getTileLayerUrl();\n window.L.tileLayer(tileConfig.url, {\n attribution: tileConfig.attribution,\n maxZoom: tileConfig.maxZoom\n }).addTo(this.map);\n\n // Add markers\n this.addMarkers(this.markers);\n\n // Auto-fit bounds if multiple markers\n if (this.markers.length > 1) {\n this.fitBounds();\n }\n\n // Fix tile rendering issues by invalidating size after a delay\n // This ensures the container has proper dimensions and CSS is applied\n setTimeout(() => {\n if (this.map) {\n this.map.invalidateSize();\n }\n }, 300);\n }\n\n addMarkers(markers) {\n if (!this.map || !Array.isArray(markers)) return;\n\n markers.forEach(markerData => {\n const { lat, lng, popup, icon } = markerData;\n \n if (!lat || !lng) return;\n\n const markerOptions = {};\n \n // Custom icon if provided\n if (icon) {\n markerOptions.icon = window.L.icon(icon);\n }\n\n const marker = window.L.marker([lat, lng], markerOptions).addTo(this.map);\n\n // Add popup if provided\n if (popup) {\n marker.bindPopup(popup);\n }\n\n this.leafletMarkers.push(marker);\n });\n }\n\n fitBounds() {\n if (!this.map || this.leafletMarkers.length === 0) return;\n\n const group = new window.L.featureGroup(this.leafletMarkers);\n this.map.fitBounds(group.getBounds().pad(0.1));\n }\n\n updateMarkers(newMarkers) {\n // Clear existing markers\n this.clearMarkers();\n \n // Add new markers\n this.markers = newMarkers;\n this.addMarkers(newMarkers);\n \n // Fit bounds if multiple markers\n if (newMarkers.length > 1) {\n this.fitBounds();\n } else if (newMarkers.length === 1) {\n this.map.setView([newMarkers[0].lat, newMarkers[0].lng], this.zoom);\n }\n }\n\n clearMarkers() {\n this.leafletMarkers.forEach(marker => {\n this.map.removeLayer(marker);\n });\n this.leafletMarkers = [];\n }\n\n setView(lat, lng, zoom = null) {\n if (!this.map) return;\n this.map.setView([lat, lng], zoom || this.zoom);\n }\n\n setZoom(zoom) {\n if (!this.map) return;\n this.map.setZoom(zoom);\n }\n\n async onBeforeDestroy() {\n if (this.map) {\n this.map.remove();\n this.map = null;\n }\n await super.onBeforeDestroy();\n }\n}\n\nexport default MapView;\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./chunks/ListView-eXgn0F0-.js"),e=require("./chunks/Rest-BNYqGlnP.js"),i=require("./chunks/Collection-aQF5eOUH.js");class TimelineViewItem extends t.ListViewItem{constructor(t={}){super({className:"timeline-item",...t}),this.dateFormat=t.dateFormat||"date",this.dotStyle=t.dotStyle||"solid",this.showDate=!1!==t.showDate,this.theme=t.theme||"primary",this.template||(this.template='\n <div class="timeline-marker timeline-marker-{{markerType}}">\n {{#hasIcon}}\n <i class="bi {{model.icon}} text-{{displayColor}}"></i>\n {{/hasIcon}}\n {{^hasIcon}}\n <div class="timeline-dot bg-{{displayColor}}"></div>\n {{/hasIcon}}\n </div>\n \n <div class="timeline-content">\n {{#showDate}}\n <div class="timeline-date text-muted small">\n {{formattedDate}}\n </div>\n {{/showDate}}\n \n <div class="timeline-card">\n {{#model.title}}\n <h6 class="timeline-title mb-1">{{model.title}}</h6>\n {{/model.title}}\n \n {{#model.description}}\n <p class="timeline-description mb-0">{{model.description}}</p>\n {{/model.description}}\n \n {{#model.meta}}\n <div class="timeline-meta mt-2 text-muted small">\n {{model.meta}}\n </div>\n {{/model.meta}}\n </div>\n </div>\n ')}async onInit(){await super.onInit(),this.processItemData()}processItemData(){this.displayColor=this.model?.get?.("color")||this.model?.color||this.theme;const t=!(!this.model?.get?.("icon")&&!this.model?.icon)&&"icon"===this.dotStyle;this.hasIcon=t,this.markerType=t?"icon":this.dotStyle;const e=this.model?.get?.("date")||this.model?.date;this.formattedDate=this.formatDate(e)}formatDate(t){if(!t)return"";switch(this.dateFormat){case"datetime":return e.dataFormatter.pipe(t,"datetime");case"relative":return e.dataFormatter.pipe(t,"timeago");default:return e.dataFormatter.pipe(t,"date")}}async onActionSelect(t,e){t.stopPropagation(),this.emit("item:click",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:click",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model})}}class TimelineView extends t.ListView{constructor(t={}){super({className:"timeline-view",itemClass:t.itemClass||TimelineViewItem,selectionMode:"none",emptyMessage:t.emptyMessage||"No timeline events to display",template:'\n <div class="timeline-container timeline-{{position}}">\n {{#loading}}\n <div class="timeline-loading text-center py-4">\n <div class="spinner-border spinner-border-sm" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n <span class="ms-2 text-muted">Loading timeline...</span>\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class="timeline-empty text-center text-muted py-4">\n <i class="bi bi-clock-history fs-1 d-block mb-2"></i>\n <p>{{emptyMessage}}</p>\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <div class="timeline" data-container="items"></div>\n {{/isEmpty}}\n {{/loading}}\n </div>\n ',...t}),this.position=t.position||"left",this.dateFormat=t.dateFormat||"date",this.dotStyle=t.dotStyle||"solid",this.showDate=!1!==t.showDate,this.theme=t.theme||"primary",this.groupBy=t.groupBy||"none"}_createItemView(t,e){if(this.itemViews.has(t.id))return;const i=new this.itemClass({model:t,index:e,listView:this,template:this.itemTemplate,dateFormat:this.dateFormat,dotStyle:this.dotStyle,showDate:this.showDate,theme:this.theme});return this.itemViews.set(t.id,i),i.on("item:click",this._onItemClick.bind(this)),i}_onItemClick(t){this.emit("item:click",t)}setPosition(t){return"left"!==t&&"center"!==t?(console.warn('Invalid position. Use "left" or "center"'),this):(this.position=t,this.isMounted()&&this.render(),this)}setDateFormat(t){return this.dateFormat=t,this.forEachItem(e=>{e.dateFormat=t,e.processItemData(),e.isMounted()&&e.render()}),this}setDotStyle(t){return this.dotStyle=t,this.forEachItem(e=>{e.dotStyle=t,e.processItemData(),e.isMounted()&&e.render()}),this}toggleDates(t=null){return this.showDate=null!==t?t:!this.showDate,this.forEachItem(t=>{t.showDate=this.showDate,t.isMounted()&&t.render()}),this}}exports.ListView=t.ListView,exports.ListViewItem=t.ListViewItem,exports.View=e.View,exports.Collection=i.Collection,exports.Model=i.Model,exports.TimelineView=TimelineView,exports.TimelineViewItem=TimelineViewItem;
2
+ //# sourceMappingURL=timeline.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.cjs.js","sources":["../src/extensions/timeline/TimelineViewItem.js","../src/extensions/timeline/TimelineView.js"],"sourcesContent":["/**\n * TimelineViewItem - Individual timeline item view\n * \n * Extends ListViewItem to provide timeline-specific rendering.\n * Each item is its own View with its own model, allowing for\n * independent re-rendering when the model changes.\n * \n * Expected model attributes:\n * - date: Date string or Date object\n * - title: Main heading (optional)\n * - description: Body text (optional)\n * - icon: Bootstrap icon class (optional)\n * - color: Bootstrap color variant (optional)\n * - meta: Additional metadata (optional)\n * \n * @example\n * const item = new TimelineViewItem({\n * model: eventModel,\n * dateFormat: 'relative',\n * dotStyle: 'icon'\n * });\n */\n\nimport ListViewItem from '@core/views/list/ListViewItem.js';\nimport dataFormatter from '@core/utils/DataFormatter.js';\n\nclass TimelineViewItem extends ListViewItem {\n constructor(options = {}) {\n super({\n className: 'timeline-item',\n ...options\n });\n\n // Timeline-specific options\n this.dateFormat = options.dateFormat || 'date';\n this.dotStyle = options.dotStyle || 'solid';\n this.showDate = options.showDate !== false;\n this.theme = options.theme || 'primary';\n\n // Default timeline item template\n if (!this.template) {\n this.template = `\n <div class=\"timeline-marker timeline-marker-{{markerType}}\">\n {{#hasIcon}}\n <i class=\"bi {{model.icon}} text-{{displayColor}}\"></i>\n {{/hasIcon}}\n {{^hasIcon}}\n <div class=\"timeline-dot bg-{{displayColor}}\"></div>\n {{/hasIcon}}\n </div>\n \n <div class=\"timeline-content\">\n {{#showDate}}\n <div class=\"timeline-date text-muted small\">\n {{formattedDate}}\n </div>\n {{/showDate}}\n \n <div class=\"timeline-card\">\n {{#model.title}}\n <h6 class=\"timeline-title mb-1\">{{model.title}}</h6>\n {{/model.title}}\n \n {{#model.description}}\n <p class=\"timeline-description mb-0\">{{model.description}}</p>\n {{/model.description}}\n \n {{#model.meta}}\n <div class=\"timeline-meta mt-2 text-muted small\">\n {{model.meta}}\n </div>\n {{/model.meta}}\n </div>\n </div>\n `;\n }\n }\n\n async onInit() {\n await super.onInit();\n this.processItemData();\n }\n\n processItemData() {\n // Get color from model or use theme default\n this.displayColor = this.model?.get?.('color') || this.model?.color || this.theme;\n \n // Determine marker type\n const hasIcon = !!(this.model?.get?.('icon') || this.model?.icon) && this.dotStyle === 'icon';\n this.hasIcon = hasIcon;\n this.markerType = hasIcon ? 'icon' : this.dotStyle;\n \n // Format date\n const dateValue = this.model?.get?.('date') || this.model?.date;\n this.formattedDate = this.formatDate(dateValue);\n }\n\n formatDate(date) {\n if (!date) return '';\n \n switch (this.dateFormat) {\n case 'datetime':\n return dataFormatter.pipe(date, 'datetime');\n case 'relative':\n return dataFormatter.pipe(date, 'timeago');\n default:\n return dataFormatter.pipe(date, 'date');\n }\n }\n\n // Override to disable selection behavior in timeline\n async onActionSelect(event, _element) {\n event.stopPropagation();\n \n // Emit click event instead of selection\n this.emit('item:click', {\n item: this,\n model: this.model,\n index: this.index,\n data: this.model?.toJSON ? this.model.toJSON() : this.model\n });\n\n if (this.listView) {\n this.listView.emit('item:click', {\n item: this,\n model: this.model,\n index: this.index,\n data: this.model?.toJSON ? this.model.toJSON() : this.model\n });\n }\n }\n}\n\nexport default TimelineViewItem;\n","/**\n * TimelineView - Timeline component extending ListView\n * \n * Displays chronological events from a Collection with clean, modern styling.\n * Each timeline item is managed as a separate view that updates independently\n * when its model changes.\n * \n * Features:\n * - Collection-based data management\n * - Left-aligned or center-aligned layout\n * - Customizable markers (dots, icons)\n * - Date formatting and grouping\n * - Individual item re-rendering\n * - Responsive Bootstrap 5 styling\n * \n * @example\n * const timeline = new TimelineView({\n * collection: eventCollection,\n * position: 'left',\n * dotStyle: 'icon',\n * dateFormat: 'relative'\n * });\n * \n * @example\n * // Custom item template\n * const timeline = new TimelineView({\n * collection: historyCollection,\n * itemTemplate: `\n * <div class=\"custom-timeline-item\">\n * <strong>{{model.title}}</strong>\n * <p>{{model.description}}</p>\n * </div>\n * `\n * });\n */\n\nimport ListView from '@core/views/list/ListView.js';\nimport TimelineViewItem from './TimelineViewItem.js';\n\nclass TimelineView extends ListView {\n constructor(options = {}) {\n // Override ListView defaults with timeline-specific settings\n super({\n className: 'timeline-view',\n itemClass: options.itemClass || TimelineViewItem,\n selectionMode: 'none', // Timelines typically don't use selection\n emptyMessage: options.emptyMessage || 'No timeline events to display',\n template: `\n <div class=\"timeline-container timeline-{{position}}\">\n {{#loading}}\n <div class=\"timeline-loading text-center py-4\">\n <div class=\"spinner-border spinner-border-sm\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n <span class=\"ms-2 text-muted\">Loading timeline...</span>\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class=\"timeline-empty text-center text-muted py-4\">\n <i class=\"bi bi-clock-history fs-1 d-block mb-2\"></i>\n <p>{{emptyMessage}}</p>\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <div class=\"timeline\" data-container=\"items\"></div>\n {{/isEmpty}}\n {{/loading}}\n </div>\n `,\n ...options\n });\n\n // Timeline-specific options\n this.position = options.position || 'left'; // 'left' or 'center'\n this.dateFormat = options.dateFormat || 'date'; // 'date', 'datetime', 'relative'\n this.dotStyle = options.dotStyle || 'solid'; // 'solid', 'hollow', 'icon'\n this.showDate = options.showDate !== false;\n this.theme = options.theme || 'primary';\n this.groupBy = options.groupBy || 'none'; // Future: 'none', 'day', 'month', 'year'\n }\n\n /**\n * Override _createItemView to pass timeline-specific options\n */\n _createItemView(model, index) {\n // Don't create duplicate views\n if (this.itemViews.has(model.id)) return;\n\n const itemView = new this.itemClass({\n model: model,\n index: index,\n listView: this,\n template: this.itemTemplate,\n // Pass timeline-specific options to items\n dateFormat: this.dateFormat,\n dotStyle: this.dotStyle,\n showDate: this.showDate,\n theme: this.theme\n });\n\n // Store the item view\n this.itemViews.set(model.id, itemView);\n\n // Set up item event listeners\n itemView.on('item:click', this._onItemClick.bind(this));\n\n return itemView;\n }\n\n /**\n * Handle item clicks (replaces selection behavior)\n */\n _onItemClick(event) {\n this.emit('item:click', event);\n }\n\n /**\n * Update timeline position\n */\n setPosition(position) {\n if (position !== 'left' && position !== 'center') {\n console.warn('Invalid position. Use \"left\" or \"center\"');\n return this;\n }\n \n this.position = position;\n if (this.isMounted()) {\n this.render();\n }\n return this;\n }\n\n /**\n * Update date format for all items\n */\n setDateFormat(format) {\n this.dateFormat = format;\n \n // Update all items\n this.forEachItem(itemView => {\n itemView.dateFormat = format;\n itemView.processItemData();\n if (itemView.isMounted()) {\n itemView.render();\n }\n });\n \n return this;\n }\n\n /**\n * Update dot style for all items\n */\n setDotStyle(style) {\n this.dotStyle = style;\n \n // Update all items\n this.forEachItem(itemView => {\n itemView.dotStyle = style;\n itemView.processItemData();\n if (itemView.isMounted()) {\n itemView.render();\n }\n });\n \n return this;\n }\n\n /**\n * Toggle date display\n */\n toggleDates(show = null) {\n this.showDate = show !== null ? show : !this.showDate;\n \n // Update all items\n this.forEachItem(itemView => {\n itemView.showDate = this.showDate;\n if (itemView.isMounted()) {\n itemView.render();\n }\n });\n \n return this;\n }\n}\n\nexport default TimelineView;\n"],"names":["TimelineViewItem","ListViewItem","constructor","options","super","className","this","dateFormat","dotStyle","showDate","theme","template","onInit","processItemData","displayColor","model","get","color","hasIcon","icon","markerType","dateValue","date","formattedDate","formatDate","dataFormatter","pipe","onActionSelect","event","_element","stopPropagation","emit","item","index","data","toJSON","listView","TimelineView","ListView","itemClass","selectionMode","emptyMessage","position","groupBy","_createItemView","itemViews","has","id","itemView","itemTemplate","set","on","_onItemClick","bind","setPosition","console","warn","isMounted","render","setDateFormat","format","forEachItem","setDotStyle","style","toggleDates","show"],"mappings":"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"}
@@ -0,0 +1,225 @@
1
+ import { L as ListViewItem, a as ListView } from "./chunks/ListView-Dcz0Gs6D.js";
2
+ import { d as dataFormatter } from "./chunks/Rest-CS4jRCAs.js";
3
+ import { V } from "./chunks/Rest-CS4jRCAs.js";
4
+ import { C, M } from "./chunks/Collection-OP7c9Zyu.js";
5
+ class TimelineViewItem extends ListViewItem {
6
+ constructor(options = {}) {
7
+ super({
8
+ className: "timeline-item",
9
+ ...options
10
+ });
11
+ this.dateFormat = options.dateFormat || "date";
12
+ this.dotStyle = options.dotStyle || "solid";
13
+ this.showDate = options.showDate !== false;
14
+ this.theme = options.theme || "primary";
15
+ if (!this.template) {
16
+ this.template = `
17
+ <div class="timeline-marker timeline-marker-{{markerType}}">
18
+ {{#hasIcon}}
19
+ <i class="bi {{model.icon}} text-{{displayColor}}"></i>
20
+ {{/hasIcon}}
21
+ {{^hasIcon}}
22
+ <div class="timeline-dot bg-{{displayColor}}"></div>
23
+ {{/hasIcon}}
24
+ </div>
25
+
26
+ <div class="timeline-content">
27
+ {{#showDate}}
28
+ <div class="timeline-date text-muted small">
29
+ {{formattedDate}}
30
+ </div>
31
+ {{/showDate}}
32
+
33
+ <div class="timeline-card">
34
+ {{#model.title}}
35
+ <h6 class="timeline-title mb-1">{{model.title}}</h6>
36
+ {{/model.title}}
37
+
38
+ {{#model.description}}
39
+ <p class="timeline-description mb-0">{{model.description}}</p>
40
+ {{/model.description}}
41
+
42
+ {{#model.meta}}
43
+ <div class="timeline-meta mt-2 text-muted small">
44
+ {{model.meta}}
45
+ </div>
46
+ {{/model.meta}}
47
+ </div>
48
+ </div>
49
+ `;
50
+ }
51
+ }
52
+ async onInit() {
53
+ await super.onInit();
54
+ this.processItemData();
55
+ }
56
+ processItemData() {
57
+ this.displayColor = this.model?.get?.("color") || this.model?.color || this.theme;
58
+ const hasIcon = !!(this.model?.get?.("icon") || this.model?.icon) && this.dotStyle === "icon";
59
+ this.hasIcon = hasIcon;
60
+ this.markerType = hasIcon ? "icon" : this.dotStyle;
61
+ const dateValue = this.model?.get?.("date") || this.model?.date;
62
+ this.formattedDate = this.formatDate(dateValue);
63
+ }
64
+ formatDate(date) {
65
+ if (!date) return "";
66
+ switch (this.dateFormat) {
67
+ case "datetime":
68
+ return dataFormatter.pipe(date, "datetime");
69
+ case "relative":
70
+ return dataFormatter.pipe(date, "timeago");
71
+ default:
72
+ return dataFormatter.pipe(date, "date");
73
+ }
74
+ }
75
+ // Override to disable selection behavior in timeline
76
+ async onActionSelect(event, _element) {
77
+ event.stopPropagation();
78
+ this.emit("item:click", {
79
+ item: this,
80
+ model: this.model,
81
+ index: this.index,
82
+ data: this.model?.toJSON ? this.model.toJSON() : this.model
83
+ });
84
+ if (this.listView) {
85
+ this.listView.emit("item:click", {
86
+ item: this,
87
+ model: this.model,
88
+ index: this.index,
89
+ data: this.model?.toJSON ? this.model.toJSON() : this.model
90
+ });
91
+ }
92
+ }
93
+ }
94
+ class TimelineView extends ListView {
95
+ constructor(options = {}) {
96
+ super({
97
+ className: "timeline-view",
98
+ itemClass: options.itemClass || TimelineViewItem,
99
+ selectionMode: "none",
100
+ // Timelines typically don't use selection
101
+ emptyMessage: options.emptyMessage || "No timeline events to display",
102
+ template: `
103
+ <div class="timeline-container timeline-{{position}}">
104
+ {{#loading}}
105
+ <div class="timeline-loading text-center py-4">
106
+ <div class="spinner-border spinner-border-sm" role="status">
107
+ <span class="visually-hidden">Loading...</span>
108
+ </div>
109
+ <span class="ms-2 text-muted">Loading timeline...</span>
110
+ </div>
111
+ {{/loading}}
112
+ {{^loading}}
113
+ {{#isEmpty}}
114
+ <div class="timeline-empty text-center text-muted py-4">
115
+ <i class="bi bi-clock-history fs-1 d-block mb-2"></i>
116
+ <p>{{emptyMessage}}</p>
117
+ </div>
118
+ {{/isEmpty}}
119
+ {{^isEmpty}}
120
+ <div class="timeline" data-container="items"></div>
121
+ {{/isEmpty}}
122
+ {{/loading}}
123
+ </div>
124
+ `,
125
+ ...options
126
+ });
127
+ this.position = options.position || "left";
128
+ this.dateFormat = options.dateFormat || "date";
129
+ this.dotStyle = options.dotStyle || "solid";
130
+ this.showDate = options.showDate !== false;
131
+ this.theme = options.theme || "primary";
132
+ this.groupBy = options.groupBy || "none";
133
+ }
134
+ /**
135
+ * Override _createItemView to pass timeline-specific options
136
+ */
137
+ _createItemView(model, index) {
138
+ if (this.itemViews.has(model.id)) return;
139
+ const itemView = new this.itemClass({
140
+ model,
141
+ index,
142
+ listView: this,
143
+ template: this.itemTemplate,
144
+ // Pass timeline-specific options to items
145
+ dateFormat: this.dateFormat,
146
+ dotStyle: this.dotStyle,
147
+ showDate: this.showDate,
148
+ theme: this.theme
149
+ });
150
+ this.itemViews.set(model.id, itemView);
151
+ itemView.on("item:click", this._onItemClick.bind(this));
152
+ return itemView;
153
+ }
154
+ /**
155
+ * Handle item clicks (replaces selection behavior)
156
+ */
157
+ _onItemClick(event) {
158
+ this.emit("item:click", event);
159
+ }
160
+ /**
161
+ * Update timeline position
162
+ */
163
+ setPosition(position) {
164
+ if (position !== "left" && position !== "center") {
165
+ console.warn('Invalid position. Use "left" or "center"');
166
+ return this;
167
+ }
168
+ this.position = position;
169
+ if (this.isMounted()) {
170
+ this.render();
171
+ }
172
+ return this;
173
+ }
174
+ /**
175
+ * Update date format for all items
176
+ */
177
+ setDateFormat(format) {
178
+ this.dateFormat = format;
179
+ this.forEachItem((itemView) => {
180
+ itemView.dateFormat = format;
181
+ itemView.processItemData();
182
+ if (itemView.isMounted()) {
183
+ itemView.render();
184
+ }
185
+ });
186
+ return this;
187
+ }
188
+ /**
189
+ * Update dot style for all items
190
+ */
191
+ setDotStyle(style) {
192
+ this.dotStyle = style;
193
+ this.forEachItem((itemView) => {
194
+ itemView.dotStyle = style;
195
+ itemView.processItemData();
196
+ if (itemView.isMounted()) {
197
+ itemView.render();
198
+ }
199
+ });
200
+ return this;
201
+ }
202
+ /**
203
+ * Toggle date display
204
+ */
205
+ toggleDates(show = null) {
206
+ this.showDate = show !== null ? show : !this.showDate;
207
+ this.forEachItem((itemView) => {
208
+ itemView.showDate = this.showDate;
209
+ if (itemView.isMounted()) {
210
+ itemView.render();
211
+ }
212
+ });
213
+ return this;
214
+ }
215
+ }
216
+ export {
217
+ C as Collection,
218
+ ListView,
219
+ ListViewItem,
220
+ M as Model,
221
+ TimelineView,
222
+ TimelineViewItem,
223
+ V as View
224
+ };
225
+ //# sourceMappingURL=timeline.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.es.js","sources":["../src/extensions/timeline/TimelineViewItem.js","../src/extensions/timeline/TimelineView.js"],"sourcesContent":["/**\n * TimelineViewItem - Individual timeline item view\n * \n * Extends ListViewItem to provide timeline-specific rendering.\n * Each item is its own View with its own model, allowing for\n * independent re-rendering when the model changes.\n * \n * Expected model attributes:\n * - date: Date string or Date object\n * - title: Main heading (optional)\n * - description: Body text (optional)\n * - icon: Bootstrap icon class (optional)\n * - color: Bootstrap color variant (optional)\n * - meta: Additional metadata (optional)\n * \n * @example\n * const item = new TimelineViewItem({\n * model: eventModel,\n * dateFormat: 'relative',\n * dotStyle: 'icon'\n * });\n */\n\nimport ListViewItem from '@core/views/list/ListViewItem.js';\nimport dataFormatter from '@core/utils/DataFormatter.js';\n\nclass TimelineViewItem extends ListViewItem {\n constructor(options = {}) {\n super({\n className: 'timeline-item',\n ...options\n });\n\n // Timeline-specific options\n this.dateFormat = options.dateFormat || 'date';\n this.dotStyle = options.dotStyle || 'solid';\n this.showDate = options.showDate !== false;\n this.theme = options.theme || 'primary';\n\n // Default timeline item template\n if (!this.template) {\n this.template = `\n <div class=\"timeline-marker timeline-marker-{{markerType}}\">\n {{#hasIcon}}\n <i class=\"bi {{model.icon}} text-{{displayColor}}\"></i>\n {{/hasIcon}}\n {{^hasIcon}}\n <div class=\"timeline-dot bg-{{displayColor}}\"></div>\n {{/hasIcon}}\n </div>\n \n <div class=\"timeline-content\">\n {{#showDate}}\n <div class=\"timeline-date text-muted small\">\n {{formattedDate}}\n </div>\n {{/showDate}}\n \n <div class=\"timeline-card\">\n {{#model.title}}\n <h6 class=\"timeline-title mb-1\">{{model.title}}</h6>\n {{/model.title}}\n \n {{#model.description}}\n <p class=\"timeline-description mb-0\">{{model.description}}</p>\n {{/model.description}}\n \n {{#model.meta}}\n <div class=\"timeline-meta mt-2 text-muted small\">\n {{model.meta}}\n </div>\n {{/model.meta}}\n </div>\n </div>\n `;\n }\n }\n\n async onInit() {\n await super.onInit();\n this.processItemData();\n }\n\n processItemData() {\n // Get color from model or use theme default\n this.displayColor = this.model?.get?.('color') || this.model?.color || this.theme;\n \n // Determine marker type\n const hasIcon = !!(this.model?.get?.('icon') || this.model?.icon) && this.dotStyle === 'icon';\n this.hasIcon = hasIcon;\n this.markerType = hasIcon ? 'icon' : this.dotStyle;\n \n // Format date\n const dateValue = this.model?.get?.('date') || this.model?.date;\n this.formattedDate = this.formatDate(dateValue);\n }\n\n formatDate(date) {\n if (!date) return '';\n \n switch (this.dateFormat) {\n case 'datetime':\n return dataFormatter.pipe(date, 'datetime');\n case 'relative':\n return dataFormatter.pipe(date, 'timeago');\n default:\n return dataFormatter.pipe(date, 'date');\n }\n }\n\n // Override to disable selection behavior in timeline\n async onActionSelect(event, _element) {\n event.stopPropagation();\n \n // Emit click event instead of selection\n this.emit('item:click', {\n item: this,\n model: this.model,\n index: this.index,\n data: this.model?.toJSON ? this.model.toJSON() : this.model\n });\n\n if (this.listView) {\n this.listView.emit('item:click', {\n item: this,\n model: this.model,\n index: this.index,\n data: this.model?.toJSON ? this.model.toJSON() : this.model\n });\n }\n }\n}\n\nexport default TimelineViewItem;\n","/**\n * TimelineView - Timeline component extending ListView\n * \n * Displays chronological events from a Collection with clean, modern styling.\n * Each timeline item is managed as a separate view that updates independently\n * when its model changes.\n * \n * Features:\n * - Collection-based data management\n * - Left-aligned or center-aligned layout\n * - Customizable markers (dots, icons)\n * - Date formatting and grouping\n * - Individual item re-rendering\n * - Responsive Bootstrap 5 styling\n * \n * @example\n * const timeline = new TimelineView({\n * collection: eventCollection,\n * position: 'left',\n * dotStyle: 'icon',\n * dateFormat: 'relative'\n * });\n * \n * @example\n * // Custom item template\n * const timeline = new TimelineView({\n * collection: historyCollection,\n * itemTemplate: `\n * <div class=\"custom-timeline-item\">\n * <strong>{{model.title}}</strong>\n * <p>{{model.description}}</p>\n * </div>\n * `\n * });\n */\n\nimport ListView from '@core/views/list/ListView.js';\nimport TimelineViewItem from './TimelineViewItem.js';\n\nclass TimelineView extends ListView {\n constructor(options = {}) {\n // Override ListView defaults with timeline-specific settings\n super({\n className: 'timeline-view',\n itemClass: options.itemClass || TimelineViewItem,\n selectionMode: 'none', // Timelines typically don't use selection\n emptyMessage: options.emptyMessage || 'No timeline events to display',\n template: `\n <div class=\"timeline-container timeline-{{position}}\">\n {{#loading}}\n <div class=\"timeline-loading text-center py-4\">\n <div class=\"spinner-border spinner-border-sm\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n <span class=\"ms-2 text-muted\">Loading timeline...</span>\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class=\"timeline-empty text-center text-muted py-4\">\n <i class=\"bi bi-clock-history fs-1 d-block mb-2\"></i>\n <p>{{emptyMessage}}</p>\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <div class=\"timeline\" data-container=\"items\"></div>\n {{/isEmpty}}\n {{/loading}}\n </div>\n `,\n ...options\n });\n\n // Timeline-specific options\n this.position = options.position || 'left'; // 'left' or 'center'\n this.dateFormat = options.dateFormat || 'date'; // 'date', 'datetime', 'relative'\n this.dotStyle = options.dotStyle || 'solid'; // 'solid', 'hollow', 'icon'\n this.showDate = options.showDate !== false;\n this.theme = options.theme || 'primary';\n this.groupBy = options.groupBy || 'none'; // Future: 'none', 'day', 'month', 'year'\n }\n\n /**\n * Override _createItemView to pass timeline-specific options\n */\n _createItemView(model, index) {\n // Don't create duplicate views\n if (this.itemViews.has(model.id)) return;\n\n const itemView = new this.itemClass({\n model: model,\n index: index,\n listView: this,\n template: this.itemTemplate,\n // Pass timeline-specific options to items\n dateFormat: this.dateFormat,\n dotStyle: this.dotStyle,\n showDate: this.showDate,\n theme: this.theme\n });\n\n // Store the item view\n this.itemViews.set(model.id, itemView);\n\n // Set up item event listeners\n itemView.on('item:click', this._onItemClick.bind(this));\n\n return itemView;\n }\n\n /**\n * Handle item clicks (replaces selection behavior)\n */\n _onItemClick(event) {\n this.emit('item:click', event);\n }\n\n /**\n * Update timeline position\n */\n setPosition(position) {\n if (position !== 'left' && position !== 'center') {\n console.warn('Invalid position. Use \"left\" or \"center\"');\n return this;\n }\n \n this.position = position;\n if (this.isMounted()) {\n this.render();\n }\n return this;\n }\n\n /**\n * Update date format for all items\n */\n setDateFormat(format) {\n this.dateFormat = format;\n \n // Update all items\n this.forEachItem(itemView => {\n itemView.dateFormat = format;\n itemView.processItemData();\n if (itemView.isMounted()) {\n itemView.render();\n }\n });\n \n return this;\n }\n\n /**\n * Update dot style for all items\n */\n setDotStyle(style) {\n this.dotStyle = style;\n \n // Update all items\n this.forEachItem(itemView => {\n itemView.dotStyle = style;\n itemView.processItemData();\n if (itemView.isMounted()) {\n itemView.render();\n }\n });\n \n return this;\n }\n\n /**\n * Toggle date display\n */\n toggleDates(show = null) {\n this.showDate = show !== null ? show : !this.showDate;\n \n // Update all items\n this.forEachItem(itemView => {\n itemView.showDate = this.showDate;\n if (itemView.isMounted()) {\n itemView.render();\n }\n });\n \n return this;\n }\n}\n\nexport default TimelineView;\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-mojo",
3
- "version": "2.1.936",
3
+ "version": "2.1.954",
4
4
  "description": "WEB-MOJO - A lightweight JavaScript framework for building data-driven web applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs.js",
@@ -35,6 +35,14 @@
35
35
  "import": "./dist/docit.es.js",
36
36
  "require": "./dist/docit.cjs.js"
37
37
  },
38
+ "./map": {
39
+ "import": "./dist/map.es.js",
40
+ "require": "./dist/map.cjs.js"
41
+ },
42
+ "./timeline": {
43
+ "import": "./dist/timeline.es.js",
44
+ "require": "./dist/timeline.cjs.js"
45
+ },
38
46
  "./models": {
39
47
  "import": "./src/core/models/index.js",
40
48
  "require": "./src/core/models/index.js"