universe-code 0.0.76 → 0.0.78

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/dist/index.d.ts CHANGED
@@ -2,4 +2,5 @@ declare module 'universe-code';
2
2
  declare module 'universe-code/core';
3
3
  declare module 'universe-code/indexdb';
4
4
  declare module 'universe-code/react';
5
- declare module 'universe-code/angular';
5
+ declare module 'universe-code/angular';
6
+ declare module 'universe-code/uiux';
@@ -1,19 +1,30 @@
1
1
  import { connectDB } from "./idbStore.js";
2
+
2
3
  let config = null;
3
4
 
5
+ // 🔹 promise that resolves once IndexedDB is configured
6
+ let resolveReady;
7
+ export const configReady = new Promise((res) => (resolveReady = res));
8
+
4
9
  export const configureIdb = (options) => {
5
- if (config) {
6
- return;
7
- }
8
- config = options;
10
+ if (config) return;
11
+
12
+ config = options;
13
+
14
+ // notify others that config is ready
15
+ resolveReady?.();
16
+
17
+ // optional: eager connection
18
+ if (typeof window !== "undefined" && typeof indexedDB !== "undefined") {
9
19
  connectDB();
20
+ }
10
21
  };
11
22
 
12
23
  export const getConfig = () => {
13
- if (!config) {
14
- throw new Error(
15
- "IndexedDB not configured. Call configureIdb() before using the store."
16
- );
17
- }
18
- return config;
19
- };
24
+ if (!config) {
25
+ throw new Error(
26
+ "IndexedDB not configured. Call configureIdb() before using the store."
27
+ );
28
+ }
29
+ return config;
30
+ };
@@ -1,39 +1,62 @@
1
1
  import { DBManager, DB } from "universe-code";
2
- import { getConfig } from "./config.js";
2
+ import { getConfig, configReady } from "./config.js";
3
3
 
4
4
  let db = null;
5
5
  let store = null;
6
6
  let manager = null;
7
7
 
8
- // index db connection
8
+ // 🔹 Helper: only run in browser
9
+ const isBrowser = () =>
10
+ typeof window !== "undefined" && typeof indexedDB !== "undefined";
11
+
12
+ // ✅ connect to IndexedDB
9
13
  export const connectDB = async () => {
10
- const { dbName, dbVersion, storeName } = getConfig(); // ✅ FIXED
14
+ // 🚫 Ignore during SSR / Node
15
+ if (!isBrowser()) return null;
16
+
17
+ // 🔥 wait for configureIdb() to be called
18
+ await configReady;
19
+
20
+ const { dbName, dbVersion, storeName } = getConfig();
11
21
 
12
22
  if (!manager) {
13
23
  manager = new DBManager(dbName, dbVersion);
14
24
  }
15
25
 
16
26
  if (!db) {
17
- db = await manager.connect([{ name: storeName }]); // ✅ FIXED
27
+ db = await manager.connect([{ name: storeName }]);
18
28
  }
19
29
 
20
30
  return db;
21
31
  };
22
32
 
33
+ // ✅ Return usable store object
23
34
  export const getIdbStore = async () => {
24
- if (!store) {
25
- const { storeName } = getConfig(); // ✅ FIXED
26
- const database = await connectDB();
27
-
28
- store = {
29
- get: (key) => DB.get(database, storeName, key),
30
- getWithExpiry: (key, ttl, api) =>
31
- DB.getWithExpiry(database, storeName, key, ttl, api),
32
- put: (data) => DB.put(database, storeName, data),
33
- remove: (key) => DB.remove(database, storeName, key),
34
- clear: () => DB.clear(database, storeName),
35
- };
36
- }
35
+ // 🚫 Ignore during SSR / Node
36
+ if (!isBrowser()) return null;
37
+
38
+ // already created? return cached store
39
+ if (store) return store;
40
+
41
+ // 🔥 wait for configureIdb()
42
+ await configReady;
43
+
44
+ const { storeName } = getConfig();
45
+
46
+ const database = await connectDB();
47
+
48
+ store = {
49
+ get: (key) => DB.get(database, storeName, key),
50
+
51
+ getWithExpiry: (key, ttl, api) =>
52
+ DB.getWithExpiry(database, storeName, key, ttl, api),
53
+
54
+ put: (data) => DB.put(database, storeName, data),
55
+
56
+ remove: (key) => DB.remove(database, storeName, key),
57
+
58
+ clear: () => DB.clear(database, storeName),
59
+ };
37
60
 
38
61
  return store;
39
- };
62
+ };
@@ -0,0 +1 @@
1
+ export { UniverseToaster, toaster } from "./toaster.js";
@@ -0,0 +1,239 @@
1
+ class UniverseToaster {
2
+ constructor() {
3
+ if (UniverseToaster.instance) return UniverseToaster.instance;
4
+
5
+ this.container = null;
6
+ this.toasts = [];
7
+ this.config = {
8
+ position: "top-right",
9
+ duration: 4000,
10
+ maxToasts: 5,
11
+ };
12
+ this.initialized = false;
13
+
14
+ UniverseToaster.instance = this;
15
+ }
16
+
17
+ /* ---------------- INIT ---------------- */
18
+ init() {
19
+ if (this.initialized) return;
20
+
21
+ this.container = document.createElement("div");
22
+ this.container.id = "universe-toaster-container";
23
+ this.container.className = `universe-toaster-container ${this.config.position}`;
24
+ document.body.appendChild(this.container);
25
+
26
+ this.injectStyles();
27
+ this.initialized = true;
28
+ }
29
+
30
+ /* ---------------- CONFIG ---------------- */
31
+ configure(options = {}) {
32
+ this.config = { ...this.config, ...options };
33
+ }
34
+
35
+ /* ---------------- STYLES ---------------- */
36
+ injectStyles() {
37
+ if (document.getElementById("universe-toaster-styles")) return;
38
+
39
+ const style = document.createElement("style");
40
+ style.id = "universe-toaster-styles";
41
+ style.textContent = `
42
+ @keyframes progress {
43
+ from { width: 100%; }
44
+ to { width: 0%; }
45
+ }
46
+
47
+ .universe-toaster-container {
48
+ position: fixed;
49
+ top: 20px;
50
+ right: 20px;
51
+ z-index: 9999;
52
+ max-width: 380px;
53
+ font-family: Arial, sans-serif;
54
+ }
55
+
56
+ .universe-toast {
57
+ background: #fff;
58
+ margin: 12px 0;
59
+ padding: 16px 18px;
60
+ border-radius: 12px;
61
+ box-shadow: 0 10px 35px rgba(0,0,0,0.25);
62
+ opacity: 0;
63
+ transform: translateX(100%);
64
+ transition: all 0.3s ease;
65
+ position: relative;
66
+ overflow: hidden;
67
+ }
68
+
69
+ .universe-toast.show {
70
+ opacity: 1;
71
+ transform: translateX(0);
72
+ }
73
+
74
+ .universe-toast.removing {
75
+ opacity: 0;
76
+ transform: translateX(100%);
77
+ }
78
+
79
+ .universe-toast-success { border-left: 5px solid #28a745; }
80
+ .universe-toast-error { border-left: 5px solid #dc3545; }
81
+ .universe-toast-warning { border-left: 5px solid #ffc107; }
82
+ .universe-toast-info { border-left: 5px solid #17a2b8; }
83
+
84
+ .universe-toast-header {
85
+ display: flex;
86
+ justify-content: space-between;
87
+ margin-bottom: 6px;
88
+ }
89
+
90
+ .universe-toast-title {
91
+ font-weight: 600;
92
+ font-size: 14px;
93
+ display: flex;
94
+ align-items: center;
95
+ }
96
+
97
+ .universe-toast-icon {
98
+ margin-right: 8px;
99
+ }
100
+
101
+ .universe-toast-message {
102
+ font-size: 13px;
103
+ color: #333;
104
+ }
105
+
106
+ .universe-toast-close {
107
+ background: none;
108
+ border: none;
109
+ font-size: 18px;
110
+ cursor: pointer;
111
+ color: #999;
112
+ }
113
+
114
+ .universe-toast-progress {
115
+ position: absolute;
116
+ bottom: 0;
117
+ left: 0;
118
+ height: 3px;
119
+ width: 100%;
120
+ opacity: 0.4;
121
+ }
122
+
123
+ .universe-toast-success .universe-toast-progress { background: #28a745; }
124
+ .universe-toast-error .universe-toast-progress { background: #dc3545; }
125
+ .universe-toast-warning .universe-toast-progress { background: #ffc107; }
126
+ .universe-toast-info .universe-toast-progress { background: #17a2b8; }
127
+ `;
128
+ document.head.appendChild(style);
129
+ }
130
+
131
+ /* ---------------- UTIL ---------------- */
132
+ getIcon(type) {
133
+ return { success: "✓", error: "✗", warning: "⚠", info: "ℹ" }[type] || "ℹ";
134
+ }
135
+
136
+ escape(text = "") {
137
+ const div = document.createElement("div");
138
+ div.textContent = text;
139
+ return div.innerHTML;
140
+ }
141
+
142
+ /* ---------------- SHOW ---------------- */
143
+ show(message, type = "info", options = {}) {
144
+ if (!this.initialized) this.init();
145
+
146
+ if (this.toasts.length >= this.config.maxToasts) {
147
+ this.remove(this.toasts[0].id);
148
+ }
149
+
150
+ const id = `toast-${Date.now()}`;
151
+ const duration =
152
+ typeof options.duration === "number"
153
+ ? options.duration
154
+ : this.config.duration;
155
+
156
+ const toast = document.createElement("div");
157
+ toast.className = `universe-toast universe-toast-${type}`;
158
+ toast.dataset.id = id;
159
+
160
+ toast.innerHTML = `
161
+ <div class="universe-toast-header">
162
+ <div class="universe-toast-title">
163
+ <span class="universe-toast-icon">${this.getIcon(type)}</span>
164
+ ${this.escape(options.title || "")}
165
+ </div>
166
+ <button class="universe-toast-close">&times;</button>
167
+ </div>
168
+ <div class="universe-toast-message">${this.escape(message)}</div>
169
+ <div class="universe-toast-progress"></div>
170
+ `;
171
+
172
+ const progress = toast.querySelector(".universe-toast-progress");
173
+ toast.querySelector(".universe-toast-close").onclick = () =>
174
+ this.remove(id);
175
+
176
+ if (duration > 0) {
177
+ progress.style.animation = `progress ${duration}ms linear forwards`;
178
+ } else {
179
+ progress.style.display = "none";
180
+ }
181
+
182
+ this.container.appendChild(toast);
183
+ this.toasts.push({ id, el: toast });
184
+
185
+ setTimeout(() => toast.classList.add("show"), 20);
186
+
187
+ if (duration > 0) {
188
+ setTimeout(() => this.remove(id), duration);
189
+ }
190
+ }
191
+
192
+ /* ---------------- REMOVE ---------------- */
193
+ remove(id) {
194
+ const toast = this.toasts.find(t => t.id === id);
195
+ if (!toast) return;
196
+
197
+ toast.el.classList.add("removing");
198
+
199
+ setTimeout(() => {
200
+ toast.el.remove();
201
+ this.toasts = this.toasts.filter(t => t.id !== id);
202
+ }, 300);
203
+ }
204
+
205
+ /* ---------------- HELPERS ---------------- */
206
+ success(msg, o) { this.show(msg, "success", o); }
207
+ error(msg, o) { this.show(msg, "error", o); }
208
+ warning(msg, o) { this.show(msg, "warning", o); }
209
+ info(msg, o) { this.show(msg, "info", o); }
210
+
211
+ clear() {
212
+ this.toasts.forEach(t => t.el.remove());
213
+ this.toasts = [];
214
+ }
215
+ }
216
+
217
+ /* ================= CALLABLE WRAPPER ================= */
218
+
219
+ const instance = new UniverseToaster();
220
+
221
+ /**
222
+ * toaster("success", "Message", { title, duration })
223
+ */
224
+ function toaster(type, message, options = {}) {
225
+ const allowed = ["success", "error", "warning", "info"];
226
+ const finalType = allowed.includes(type) ? type : "info";
227
+ instance.show(message, finalType, options);
228
+ }
229
+
230
+ /* Attach helpers */
231
+ toaster.success = (m, o) => instance.success(m, o);
232
+ toaster.error = (m, o) => instance.error(m, o);
233
+ toaster.warning = (m, o) => instance.warning(m, o);
234
+ toaster.info = (m, o) => instance.info(m, o);
235
+
236
+ toaster.configure = (o) => instance.configure(o);
237
+ toaster.clear = () => instance.clear();
238
+
239
+ export { UniverseToaster, toaster };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "universe-code",
3
- "version": "0.0.76",
3
+ "version": "0.0.78",
4
4
  "description": "Universal utility functions for all JS frameworks",
5
5
  "license": "ISC",
6
6
  "type": "module",
@@ -31,6 +31,10 @@
31
31
  "./core": {
32
32
  "import": "./dist/core/index.js",
33
33
  "types": "./dist/index.d.ts"
34
+ },
35
+ "./uiux": {
36
+ "import": "./dist/uiux/index.js",
37
+ "types": "./dist/index.d.ts"
34
38
  }
35
39
  },
36
40
  "files": [