@nu-art/thunderstorm-frontend 0.400.10 → 0.400.13

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.
@@ -1,7 +1,7 @@
1
1
  import { createElement as _createElement } from "react";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { BrowserRouter, Navigate, NavLink, Route, Routes } from 'react-router-dom';
4
- import { BadImplementationException, composeUrl, Module, removeItemFromArray } from '@nu-art/ts-common';
4
+ import { BadImplementationException, composeQueryParams, composeUrl, exists, Module, removeItemFromArray, _keys } from '@nu-art/ts-common';
5
5
  import { LocationChangeListener } from './LocationChangeListener.js';
6
6
  import { mouseEventHandler, stopPropagation } from '../../utils/tools.js';
7
7
  import { AwaitModules } from '../../components/AwaitModules/AwaitModules.js';
@@ -126,6 +126,159 @@ class ModuleFE_RoutingV2_Class extends Module {
126
126
  setNavigate(navigate) {
127
127
  this.navigate = navigate;
128
128
  }
129
+ // ######################## Query Param Methods ########################
130
+ /**
131
+ * Get all query parameters from the current URL (decoded)
132
+ */
133
+ getQueryParams() {
134
+ const params = this.getEncodedQueryParams();
135
+ _keys(params).forEach(key => {
136
+ const _param = params[key];
137
+ if (!exists(_param))
138
+ return;
139
+ const value = `${params[key]}`;
140
+ params[key] = decodeURIComponent(value);
141
+ });
142
+ return params;
143
+ }
144
+ /**
145
+ * Get a single query parameter from the current URL
146
+ * @param key - The query parameter key
147
+ * @returns The decoded value, null if key exists but value is empty, or undefined if key doesn't exist
148
+ */
149
+ getQueryParameter(key) {
150
+ const queryParams = this.getQueryParams();
151
+ const value = queryParams[key];
152
+ if (value === undefined && Object.keys(queryParams).includes(key))
153
+ return null;
154
+ return value;
155
+ }
156
+ /**
157
+ * Replace all query parameters on the current route
158
+ * @param queryParams - The query parameters to set
159
+ */
160
+ setQuery(queryParams) {
161
+ const encodedQueryParams = this.encodeUrlParams(queryParams);
162
+ this.updateQueryParams(encodedQueryParams);
163
+ }
164
+ /**
165
+ * Add or update a single query parameter on the current route
166
+ * @param key - The query parameter key
167
+ * @param value - The query parameter value
168
+ */
169
+ addQueryParam(key, value) {
170
+ const decodedQueryParams = this.getQueryParams();
171
+ decodedQueryParams[key] = value;
172
+ this.updateQueryParams(decodedQueryParams);
173
+ }
174
+ /**
175
+ * Remove a single query parameter from the current route
176
+ * @param key - The query parameter key to remove
177
+ */
178
+ removeQueryParam(key) {
179
+ const encodedQueryParams = this.getEncodedQueryParams();
180
+ delete encodedQueryParams[key];
181
+ this.updateQueryParams(encodedQueryParams);
182
+ }
183
+ // ######################## URL Utility Methods ########################
184
+ /**
185
+ * Get the current location object (compatible with BrowserHistory.getCurrent())
186
+ * @returns Object with pathname and search properties
187
+ */
188
+ getCurrent() {
189
+ return {
190
+ pathname: window.location.pathname,
191
+ search: window.location.search,
192
+ hash: window.location.hash,
193
+ };
194
+ }
195
+ /**
196
+ * Get the current URL pathname
197
+ * @returns The current pathname
198
+ */
199
+ getCurrentUrl() {
200
+ return window.location.pathname;
201
+ }
202
+ /**
203
+ * Get the window origin
204
+ * @returns The origin (protocol + hostname + port)
205
+ */
206
+ getOrigin() {
207
+ return window.location.origin;
208
+ }
209
+ // ######################## Navigation Methods ########################
210
+ /**
211
+ * Navigate to a pathname with optional query params (adds to history)
212
+ * @param location - Location descriptor with pathname and optional search
213
+ */
214
+ push(location) {
215
+ const url = this.composeLocationUrl(location);
216
+ this.navigate(url);
217
+ }
218
+ /**
219
+ * Replace current history entry with new pathname and optional query params
220
+ * @param location - Location descriptor with pathname and optional search
221
+ */
222
+ replace(location) {
223
+ const url = this.composeLocationUrl(location);
224
+ this.navigate(url, { replace: true });
225
+ }
226
+ // ######################## Private Helper Methods ########################
227
+ getEncodedQueryParams() {
228
+ const queryParams = {};
229
+ let queryAsString = window.location.search;
230
+ if (!queryAsString || queryAsString.length === 0)
231
+ return {};
232
+ while (queryAsString.startsWith('?') || queryAsString.startsWith('/?')) {
233
+ if (queryAsString.startsWith('?'))
234
+ queryAsString = queryAsString.substring(1);
235
+ else if (queryAsString.startsWith('/?'))
236
+ queryAsString = queryAsString.substring(2);
237
+ else
238
+ break;
239
+ }
240
+ const query = queryAsString.split('&');
241
+ return query.map(param => {
242
+ const parts = param.split('=');
243
+ return { key: parts[0], value: parts[1]?.length === 0 ? undefined : parts[1] };
244
+ }).reduce((toRet, param) => {
245
+ toRet[param.key] = param.value;
246
+ return toRet;
247
+ }, queryParams);
248
+ }
249
+ composeQuery(queryParams) {
250
+ const queryAsString = composeQueryParams(queryParams);
251
+ if (queryAsString.length === 0)
252
+ return '';
253
+ return queryAsString;
254
+ }
255
+ encodeUrlParams(queryParams) {
256
+ const encodedQueryParams = { ...queryParams };
257
+ _keys(encodedQueryParams).forEach(key => {
258
+ const value = encodedQueryParams[key];
259
+ if (!value) {
260
+ delete encodedQueryParams[key];
261
+ return;
262
+ }
263
+ encodedQueryParams[key] = encodeURIComponent(value);
264
+ });
265
+ return encodedQueryParams;
266
+ }
267
+ createLocationDataFromQueryParams(encodedQueryParams, pathname = window.location.pathname) {
268
+ const cleanPathname = !pathname.endsWith('/') ? pathname : pathname.substring(0, pathname.length - 1);
269
+ const search = encodedQueryParams ? this.composeQuery(encodedQueryParams) : '';
270
+ return search.length > 0 ? `${cleanPathname}?${search}` : cleanPathname;
271
+ }
272
+ updateQueryParams(encodedQueryParams) {
273
+ const url = this.createLocationDataFromQueryParams(encodedQueryParams);
274
+ this.navigate(url, { replace: true });
275
+ }
276
+ composeLocationUrl(location) {
277
+ const cleanPathname = !location.pathname.endsWith('/') ? location.pathname : location.pathname.substring(0, location.pathname.length - 1);
278
+ const search = location.search || '';
279
+ const hash = location.hash || '';
280
+ return `${cleanPathname}${search}${hash}`;
281
+ }
129
282
  }
130
283
  export const TS_NavLink = (props) => {
131
284
  const { route, children, ignoreClickOnSameRoute, ..._props } = props;
@@ -141,3 +294,42 @@ export const TS_NavLink = (props) => {
141
294
  }) }, children);
142
295
  };
143
296
  export const ModuleFE_RoutingV2 = new ModuleFE_RoutingV2_Class();
297
+ // ######################## Utility Functions ########################
298
+ /**
299
+ * Encode URL query parameters
300
+ * @param queryParams - Query parameters to encode
301
+ * @returns Encoded query parameters
302
+ */
303
+ export function encodeUrlParams(queryParams) {
304
+ const encodedQueryParams = { ...queryParams };
305
+ _keys(encodedQueryParams).forEach(key => {
306
+ const value = encodedQueryParams[key];
307
+ if (!value) {
308
+ delete encodedQueryParams[key];
309
+ return;
310
+ }
311
+ encodedQueryParams[key] = encodeURIComponent(value);
312
+ });
313
+ return encodedQueryParams;
314
+ }
315
+ /**
316
+ * Compose a query string from query parameters
317
+ * @param queryParams - Query parameters to compose
318
+ * @returns Query string (without leading ?)
319
+ */
320
+ export function composeQuery(queryParams) {
321
+ const queryAsString = composeQueryParams(queryParams);
322
+ if (queryAsString.length === 0)
323
+ return '';
324
+ return queryAsString;
325
+ }
326
+ /**
327
+ * Compose a full URL with query parameters
328
+ * @param url - Base URL
329
+ * @param queryParams - Optional query parameters
330
+ * @returns Full URL with query string
331
+ */
332
+ export function composeURL(url, queryParams) {
333
+ const queryAsString = composeQuery(queryParams);
334
+ return `${url}${queryAsString.length > 0 ? `?${queryAsString}` : ''}`;
335
+ }
@@ -1,5 +1,4 @@
1
1
  export * from './types.js';
2
2
  export * from './LocationChangeListener.js';
3
3
  export * from './route.js';
4
- export * from './ModuleFE_Routing.js';
5
4
  export * from './ModuleFE_RoutingV2.js';
@@ -1,5 +1,4 @@
1
1
  export * from './types.js';
2
2
  export * from './LocationChangeListener.js';
3
3
  export * from './route.js';
4
- export * from './ModuleFE_Routing.js';
5
4
  export * from './ModuleFE_RoutingV2.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nu-art/thunderstorm-frontend",
3
- "version": "0.400.10",
3
+ "version": "0.400.13",
4
4
  "description": "Thunderstorm Frontend",
5
5
  "keywords": [
6
6
  "TacB0sS",
@@ -37,11 +37,11 @@
37
37
  "linkDirectory": true
38
38
  },
39
39
  "dependencies": {
40
- "@nu-art/thunderstorm-shared": "0.400.10",
41
- "@nu-art/firebase-frontend": "0.400.10",
42
- "@nu-art/firebase-shared": "0.400.10",
43
- "@nu-art/ts-common": "0.400.10",
44
- "@nu-art/ts-styles": "0.400.10",
40
+ "@nu-art/thunderstorm-shared": "0.400.13",
41
+ "@nu-art/firebase-frontend": "0.400.13",
42
+ "@nu-art/firebase-shared": "0.400.13",
43
+ "@nu-art/ts-common": "0.400.13",
44
+ "@nu-art/ts-styles": "0.400.13",
45
45
  "abort-controller": "^3.0.0",
46
46
  "axios": "^1.13.1",
47
47
  "body-parser": "^1.19.0",
@@ -501,7 +501,7 @@ export class EditableDBItemV3 extends EditableItem {
501
501
  if (this.hasValidationError())
502
502
  return this.setStatus(EditableItemStatus_FailedValidation);
503
503
  if (this.saveError)
504
- return this.setStatus(exports.EditableItemStatus_ErrorSaving);
504
+ return this.setStatus(EditableItemStatus_ErrorSaving);
505
505
  if (this.hasChanges()) {
506
506
  return this.setStatus(EditableItemStatus_UnsavedChanges);
507
507
  }
package/utils/tools.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { Browser } from '@nu-art/thunderstorm-shared/consts';
2
+ import { DBProto } from '@nu-art/ts-common';
2
3
  import * as React from 'react';
3
4
  import { DependencyList, Dispatch, EffectCallback, SetStateAction } from 'react';
5
+ import { ModuleFE_BaseDB } from '../modules/db-api-gen/ModuleFE_BaseDB.js';
4
6
  export declare function browserType(): Browser;
5
7
  export declare function base64ToBlob(imageAsBase64: string): Promise<Blob>;
6
8
  export declare function stringToArrayBuffer(stringToConvert: string): ArrayBuffer;
@@ -28,4 +30,7 @@ type MouseClickActions = {
28
30
  };
29
31
  export declare const mouseEventHandler: (e: React.MouseEvent | MouseEvent, actions: MouseClickActions) => void | undefined;
30
32
  export declare const stringReplacer: (_content: string, _toReplace: string, replacer: (match: string, i: number) => JSX.Element) => React.ReactNode[];
33
+ export type EntityToResolve<P extends DBProto<any> = DBProto<any>> = string | P['dbType'] | undefined;
34
+ export declare const resolveId: (entity: EntityToResolve) => any;
35
+ export declare const resolveEntity: <P extends DBProto<any>>(module: ModuleFE_BaseDB<P>) => (entity: EntityToResolve<P>) => Readonly<P["dbType"]> | undefined;
31
36
  export {};
package/utils/tools.js CHANGED
@@ -136,3 +136,7 @@ export const stringReplacer = (_content, _toReplace, replacer) => {
136
136
  i++;
137
137
  }
138
138
  };
139
+ export const resolveId = (entity) => typeof entity === 'string' ? entity : entity?._id;
140
+ export const resolveEntity = (module) => (entity) => typeof entity === 'string'
141
+ ? module.cache.unique(entity)
142
+ : entity;