@cocreate/pwa 1.20.0 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [1.22.0](https://github.com/CoCreate-app/CoCreate-pwa/compare/v1.21.0...v1.22.0) (2025-01-18)
2
+
3
+
4
+ ### Features
5
+
6
+ * geolocation intergration ([4bc95fb](https://github.com/CoCreate-app/CoCreate-pwa/commit/4bc95fb1192efec090da1a45698f347376427d44))
7
+
8
+ # [1.21.0](https://github.com/CoCreate-app/CoCreate-pwa/compare/v1.20.0...v1.21.0) (2024-12-14)
9
+
10
+
11
+ ### Features
12
+
13
+ * dynamic theme handling ([c2688f2](https://github.com/CoCreate-app/CoCreate-pwa/commit/c2688f2f148f0c68e3693cf932a24976658eb475))
14
+
1
15
  # [1.20.0](https://github.com/CoCreate-app/CoCreate-pwa/compare/v1.19.4...v1.20.0) (2024-11-04)
2
16
 
3
17
 
@@ -0,0 +1,73 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Geolocation Web API Demo</title>
7
+ </head>
8
+ <body>
9
+ <form id="locationForm">
10
+ <input
11
+ geolocation="getCurrentPosition"
12
+ geolocation-key="coords.latitude"
13
+ placeholder="Latitude" />
14
+ <input
15
+ geolocation="getCurrentPosition"
16
+ geolocation-key="coords.longitude"
17
+ placeholder="Longitude" />
18
+ <input
19
+ geolocation="getCurrentPosition"
20
+ geolocation-key="coords.accuracy"
21
+ placeholder="Accuracy (meters)" />
22
+ <button actions="geoLocation.getCurrentPosition">
23
+ Get Location
24
+ </button>
25
+ </form>
26
+
27
+ <form id="trackLocationForm">
28
+ <input
29
+ geolocation="watchPosition"
30
+ geolocation-key="id"
31
+ placeholder="id"
32
+ input-next
33
+ input-attribute="value" />
34
+ <input
35
+ geolocation="clearWatch"
36
+ geolocation-key="id"
37
+ placeholder="id" />
38
+ <input
39
+ geolocation="watchPosition"
40
+ geolocation-key="coords.latitude"
41
+ placeholder="Latitude" />
42
+ <input
43
+ geolocation="watchPosition"
44
+ geolocation-key="coords.longitude"
45
+ placeholder="Longitude" />
46
+ <input
47
+ geolocation="watchPosition"
48
+ geolocation-key="coords.speed"
49
+ placeholder="Speed (m/s)" />
50
+ <button actions="geoLocation.watchPosition">Start Tracking</button>
51
+ <button actions="geoLocation.clearWatch">Stop Tracking</button>
52
+ </form>
53
+
54
+ <form id="errorForm">
55
+ <input
56
+ geolocation="getCurrentPosition"
57
+ geolocation-key="coords.latitude"
58
+ placeholder="Latitude" />
59
+ <input
60
+ geolocation="getCurrentPosition"
61
+ geolocation-key="coords.longitude"
62
+ placeholder="Longitude" />
63
+ <input
64
+ geolocation="getCurrentPosition"
65
+ geolocation-key="error.message"
66
+ placeholder="Error Message" />
67
+ <button actions="geoLocation.getCurrentPosition">
68
+ Get Location
69
+ </button>
70
+ </form>
71
+ <script src="./dist/CoCreate.js"></script>
72
+ </body>
73
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/pwa",
3
- "version": "1.20.0",
3
+ "version": "1.22.0",
4
4
  "description": "A simple pwa component in vanilla javascript. Easily configured using HTML5 data-attributes and/or JavaScript API.",
5
5
  "keywords": [
6
6
  "pwa",
@@ -0,0 +1,92 @@
1
+ import Actions from "@cocreate/actions";
2
+ import Api from "@cocreate/api";
3
+
4
+ const geolocationHandlers = {
5
+ getCurrentPosition,
6
+ watchPosition,
7
+ clearWatch
8
+ };
9
+
10
+ // Get Current Position
11
+ async function getCurrentPosition(action) {
12
+ if (action.element) action.options = await Api.getData(action);
13
+
14
+ navigator.geolocation.getCurrentPosition(
15
+ (position) => {
16
+ action.data = {
17
+ geoLocation: { coords: position.coords }
18
+ };
19
+ Api.setData(action);
20
+ dispatchEvent(action);
21
+ },
22
+ (error) => handleError(error, action),
23
+ action.options
24
+ );
25
+ }
26
+
27
+ // Watch Position
28
+ async function watchPosition(action) {
29
+ if (action.element) action.options = await Api.getData(action);
30
+
31
+ const watchId = navigator.geolocation.watchPosition(
32
+ (position) => {
33
+ // Use a closure to access `watchId`
34
+ action.data = {
35
+ geoLocation: {
36
+ id: watchId, // Access `watchId` from outer scope
37
+ coords: position.coords
38
+ }
39
+ };
40
+
41
+ Api.setData(action);
42
+ dispatchEvent(action);
43
+ },
44
+ (error) => handleError(error, action),
45
+ action.options
46
+ );
47
+ }
48
+
49
+ // Clear Watch
50
+ async function clearWatch(action) {
51
+ if (action.element) action.options = await Api.getData(action);
52
+
53
+ if (action.options.id) {
54
+ navigator.geolocation.clearWatch(Number(action.options.id));
55
+ dispatchEvent(action);
56
+ }
57
+ }
58
+
59
+ // Handle Errors
60
+ function handleError(error, action) {
61
+ if (!action.data) action.data = { error };
62
+ else action.data.error = error;
63
+ console.error(`Geolocation error: ${error.message}`);
64
+ Api.setData(action);
65
+ }
66
+
67
+ function dispatchEvent(action) {
68
+ document.dispatchEvent(
69
+ new CustomEvent(action.endEvent, {
70
+ detail: {
71
+ data: action
72
+ }
73
+ })
74
+ );
75
+ }
76
+
77
+ // Actions Integration
78
+ Actions.init([
79
+ {
80
+ name: "geoLocation",
81
+ endEvent: "geolocation",
82
+ callback(action) {
83
+ geolocationHandlers[action.method](action);
84
+ }
85
+ }
86
+ ]);
87
+
88
+ export default {
89
+ getCurrentPosition,
90
+ watchPosition,
91
+ clearWatch
92
+ };
package/src/index.js CHANGED
@@ -22,10 +22,11 @@
22
22
  * For details, visit <https://cocreate.app/licenses/> or contact us at sales@cocreate.app.
23
23
  */
24
24
 
25
- import observer from '@cocreate/observer'
26
- import localStorage from '@cocreate/local-storage'
27
- import './web-share'
28
-
25
+ import observer from "@cocreate/observer";
26
+ import localStorage from "@cocreate/local-storage";
27
+ import "./web-share";
28
+ import "./geolocation";
29
+ // import "./theme";
29
30
 
30
31
  /**
31
32
  * Warn the page must be served over HTTPS
@@ -41,104 +42,114 @@ import './web-share'
41
42
  // }
42
43
 
43
44
  function init() {
44
- const button = document.querySelector('[actions*="install"]');
45
-
46
- window.addEventListener('beforeinstallprompt', (event) => {
47
- window.deferredPrompt = event;
48
- if (button)
49
- button.removeAttribute('hidden');
50
- });
51
-
52
- if (button) {
53
- button.addEventListener('click', async () => {
54
- // console.log('👍', 'installBtn-clicked');
55
- const promptEvent = window.deferredPrompt;
56
- if (!promptEvent) {
57
- // The deferred prompt isn't available.
58
- return;
59
- }
60
- // Show the install prompt.
61
- promptEvent.prompt();
62
- // Log the result
63
- const result = await promptEvent.userChoice;
64
- console.log('👍', 'userChoice', result);
65
- // Reset the deferred prompt variable, since
66
- // prompt() can only be called once.
67
- window.deferredPrompt = null;
68
- // Hide the install button.
69
- button.setAttribute('hidden', '');
70
- });
71
- }
72
-
73
- window.addEventListener('appinstalled', (event) => {
74
- // console.log('👍', 'appinstalled', event);
75
- // Clear the deferredPrompt so it can be garbage collected
76
- window.deferredPrompt = null;
77
- });
78
-
79
- if ('serviceWorker' in navigator) {
80
- let workerPath
81
- if (window.CoCreateConfig && window.CoCreateConfig.serviceWorker)
82
- workerPath = window.CoCreateConfig.serviceWorker
83
- else
84
- workerPath = localStorage.getItem("serviceWorker") || '/service-worker.js'
85
-
86
- localStorage.setItem("serviceWorker", workerPath);
87
-
88
- let cache
89
- if (window.CoCreateConfig && window.CoCreateConfig.cache)
90
- cache = window.CoCreateConfig.cache
91
- else
92
- cache = localStorage.getItem("cache") || 'true'
93
-
94
- localStorage.setItem("cache", cache);
95
- workerPath += `?cache=${cache}`
96
-
97
- let isPwa = true
98
- if (workerPath) {
99
- navigator.serviceWorker.getRegistrations().then(registrations => {
100
- if (registrations.length === 0 || isPwa == true)
101
- window.addEventListener("load", function () {
102
- navigator.serviceWorker.getRegistration(workerPath).then(registration => {
103
- if (registration && registration.active && registration.active.scriptURL.includes(workerPath)) {
104
- console.log('Service Worker Active')
105
- } else {
106
- navigator.serviceWorker.register(workerPath)
107
- .then(reg => {
108
- reg.onupdatefound = () => {
109
- const installingWorker = reg.installing;
110
- installingWorker.onstatechange = () => {
111
- // console.log('Service Worker', installingWorker.state);
112
- }
113
- }
114
- })
115
- .catch(err => console.log('SW ERROR', err));
116
- }
117
- });
118
- });
119
- });
120
-
121
- }
122
- }
45
+ const button = document.querySelector('[actions*="install"]');
46
+
47
+ window.addEventListener("beforeinstallprompt", (event) => {
48
+ window.deferredPrompt = event;
49
+ if (button) button.removeAttribute("hidden");
50
+ });
51
+
52
+ if (button) {
53
+ button.addEventListener("click", async () => {
54
+ // console.log('👍', 'installBtn-clicked');
55
+ const promptEvent = window.deferredPrompt;
56
+ if (!promptEvent) {
57
+ // The deferred prompt isn't available.
58
+ return;
59
+ }
60
+ // Show the install prompt.
61
+ promptEvent.prompt();
62
+ // Log the result
63
+ const result = await promptEvent.userChoice;
64
+ console.log("👍", "userChoice", result);
65
+ // Reset the deferred prompt variable, since
66
+ // prompt() can only be called once.
67
+ window.deferredPrompt = null;
68
+ // Hide the install button.
69
+ button.setAttribute("hidden", "");
70
+ });
71
+ }
72
+
73
+ window.addEventListener("appinstalled", (event) => {
74
+ // console.log('👍', 'appinstalled', event);
75
+ // Clear the deferredPrompt so it can be garbage collected
76
+ window.deferredPrompt = null;
77
+ });
78
+
79
+ if ("serviceWorker" in navigator) {
80
+ let workerPath;
81
+ if (window.CoCreateConfig && window.CoCreateConfig.serviceWorker)
82
+ workerPath = window.CoCreateConfig.serviceWorker;
83
+ else
84
+ workerPath =
85
+ localStorage.getItem("serviceWorker") || "/service-worker.js";
86
+
87
+ localStorage.setItem("serviceWorker", workerPath);
88
+
89
+ let cache;
90
+ if (window.CoCreateConfig && window.CoCreateConfig.cache)
91
+ cache = window.CoCreateConfig.cache;
92
+ else cache = localStorage.getItem("cache") || "true";
93
+
94
+ localStorage.setItem("cache", cache);
95
+ workerPath += `?cache=${cache}`;
96
+
97
+ let isPwa = true;
98
+ if (workerPath) {
99
+ navigator.serviceWorker.getRegistrations().then((registrations) => {
100
+ if (registrations.length === 0 || isPwa == true)
101
+ window.addEventListener("load", function () {
102
+ navigator.serviceWorker
103
+ .getRegistration(workerPath)
104
+ .then((registration) => {
105
+ if (
106
+ registration &&
107
+ registration.active &&
108
+ registration.active.scriptURL.includes(
109
+ workerPath
110
+ )
111
+ ) {
112
+ console.log("Service Worker Active");
113
+ } else {
114
+ navigator.serviceWorker
115
+ .register(workerPath)
116
+ .then((reg) => {
117
+ reg.onupdatefound = () => {
118
+ const installingWorker =
119
+ reg.installing;
120
+ installingWorker.onstatechange =
121
+ () => {
122
+ // console.log('Service Worker', installingWorker.state);
123
+ };
124
+ };
125
+ })
126
+ .catch((err) =>
127
+ console.log("SW ERROR", err)
128
+ );
129
+ }
130
+ });
131
+ });
132
+ });
133
+ }
134
+ }
123
135
  }
124
136
 
125
- init()
137
+ init();
126
138
 
127
139
  async function persistData() {
128
- if (navigator.storage && navigator.storage.persist) {
129
- const result = await navigator.storage.persist();
130
- // console.log(`Data persisted: ${result}`);
131
- }
140
+ if (navigator.storage && navigator.storage.persist) {
141
+ const result = await navigator.storage.persist();
142
+ // console.log(`Data persisted: ${result}`);
143
+ }
132
144
  }
133
145
 
134
146
  observer.init({
135
- name: 'initPwa',
136
- observe: ['addedNodes'],
137
- selector: '[actions="install"]',
138
- callback: (mutation) => {
139
- init()
140
- }
147
+ name: "initPwa",
148
+ observe: ["addedNodes"],
149
+ selector: '[actions="install"]',
150
+ callback: (mutation) => {
151
+ init();
152
+ }
141
153
  });
142
154
 
143
-
144
- export default { persistData }
155
+ export default { persistData };
package/src/theme.js ADDED
@@ -0,0 +1,57 @@
1
+ function applyThemeAttributes() {
2
+ // Detect the current theme (light or dark)
3
+ const isDarkMode = window.matchMedia(
4
+ "(prefers-color-scheme: dark)"
5
+ ).matches;
6
+
7
+ // Select all elements with light or dark attributes
8
+ const elements = document.querySelectorAll("[light], [dark]");
9
+
10
+ elements.forEach((element) => {
11
+ // Determine the attribute to use based on the theme
12
+ const themeAttr = isDarkMode ? "dark" : "light";
13
+ const themeValue = element.getAttribute(themeAttr);
14
+
15
+ // Determine the current value of the attribute to update
16
+ let attributeToUpdate;
17
+ if (element.tagName === "LINK" && element.rel === "manifest") {
18
+ attributeToUpdate = "href";
19
+ } else if (
20
+ element.tagName === "META" &&
21
+ element.name === "theme-color"
22
+ ) {
23
+ attributeToUpdate = "content";
24
+ }
25
+
26
+ if (attributeToUpdate && themeValue) {
27
+ const currentValue = element.getAttribute(attributeToUpdate);
28
+
29
+ // Update only if the current value does not match the new value
30
+ if (currentValue !== themeValue) {
31
+ if (element.tagName === "LINK" && element.rel === "manifest") {
32
+ // Handle manifest link replacement
33
+ const newElement = element.cloneNode(); // Clone the element
34
+ newElement.setAttribute(attributeToUpdate, themeValue); // Update href
35
+
36
+ // Replace the old link with the new one to force fetch
37
+ element.parentNode.replaceChild(newElement, element);
38
+ console.log(`Manifest updated: ${themeValue}`);
39
+ } else {
40
+ // For other elements like meta, update the attribute directly
41
+ element.setAttribute(attributeToUpdate, themeValue);
42
+ if (element.tagName === "META") {
43
+ console.log(`Theme color updated: ${themeValue}`);
44
+ }
45
+ }
46
+ }
47
+ }
48
+ });
49
+ }
50
+
51
+ // Apply theme attributes on page load
52
+ applyThemeAttributes();
53
+
54
+ // Listen for changes in the user's theme preference
55
+ window
56
+ .matchMedia("(prefers-color-scheme: dark)")
57
+ .addEventListener("change", applyThemeAttributes);