@abpjs/theme-shared 2.7.0 → 2.9.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/dist/index.mjs CHANGED
@@ -21,6 +21,7 @@ var Confirmation;
21
21
  })(Confirmation || (Confirmation = {}));
22
22
 
23
23
  // src/constants/styles.ts
24
+ var BOOTSTRAP = "bootstrap-{{dir}}.min.css";
24
25
  var DEFAULT_STYLES = `
25
26
  .is-invalid .form-control {
26
27
  border-color: #dc3545;
@@ -36,6 +37,11 @@ var DEFAULT_STYLES = `
36
37
  text-align: right;
37
38
  }
38
39
 
40
+ /* RTL support - @since 2.9.0 */
41
+ [dir=rtl] .data-tables-filter {
42
+ text-align: left;
43
+ }
44
+
39
45
  .pointer {
40
46
  cursor: pointer;
41
47
  }
@@ -217,10 +223,19 @@ function useHttpErrorConfig() {
217
223
  return context ?? httpErrorConfigFactory();
218
224
  }
219
225
 
226
+ // src/tokens/lazy-styles.token.ts
227
+ import { createContext as createContext3, useContext as useContext2 } from "react";
228
+ var DEFAULT_LAZY_STYLES = [BOOTSTRAP];
229
+ var LazyStylesContext = createContext3(DEFAULT_LAZY_STYLES);
230
+ function useLazyStyles() {
231
+ return useContext2(LazyStylesContext);
232
+ }
233
+ var LAZY_STYLES = DEFAULT_LAZY_STYLES;
234
+
220
235
  // src/contexts/toaster.context.tsx
221
236
  import {
222
- createContext as createContext3,
223
- useContext as useContext2,
237
+ createContext as createContext4,
238
+ useContext as useContext3,
224
239
  useCallback,
225
240
  useState,
226
241
  useRef,
@@ -228,7 +243,7 @@ import {
228
243
  useEffect
229
244
  } from "react";
230
245
  import { jsx } from "react/jsx-runtime";
231
- var ToasterContext = createContext3(null);
246
+ var ToasterContext = createContext4(null);
232
247
  var toastCounter = 0;
233
248
  function generateId() {
234
249
  toastCounter += 1;
@@ -321,21 +336,21 @@ function ToasterProvider({ children }) {
321
336
  return /* @__PURE__ */ jsx(ToasterContext.Provider, { value, children });
322
337
  }
323
338
  function useToaster() {
324
- const context = useContext2(ToasterContext);
339
+ const context = useContext3(ToasterContext);
325
340
  if (!context) {
326
341
  throw new Error("useToaster must be used within a ToasterProvider");
327
342
  }
328
343
  return context.service;
329
344
  }
330
345
  function useToasts() {
331
- const context = useContext2(ToasterContext);
346
+ const context = useContext3(ToasterContext);
332
347
  if (!context) {
333
348
  throw new Error("useToasts must be used within a ToasterProvider");
334
349
  }
335
350
  return context.toasts;
336
351
  }
337
352
  function useToasterContext() {
338
- const context = useContext2(ToasterContext);
353
+ const context = useContext3(ToasterContext);
339
354
  if (!context) {
340
355
  throw new Error("useToasterContext must be used within a ToasterProvider");
341
356
  }
@@ -344,8 +359,8 @@ function useToasterContext() {
344
359
 
345
360
  // src/contexts/confirmation.context.tsx
346
361
  import {
347
- createContext as createContext4,
348
- useContext as useContext3,
362
+ createContext as createContext5,
363
+ useContext as useContext4,
349
364
  useCallback as useCallback2,
350
365
  useState as useState2,
351
366
  useRef as useRef2,
@@ -353,7 +368,7 @@ import {
353
368
  useEffect as useEffect2
354
369
  } from "react";
355
370
  import { jsx as jsx2 } from "react/jsx-runtime";
356
- var ConfirmationContext = createContext4(null);
371
+ var ConfirmationContext = createContext5(null);
357
372
  function generateId2() {
358
373
  return `confirmation-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
359
374
  }
@@ -460,21 +475,21 @@ function ConfirmationProvider({ children }) {
460
475
  return /* @__PURE__ */ jsx2(ConfirmationContext.Provider, { value, children });
461
476
  }
462
477
  function useConfirmation() {
463
- const context = useContext3(ConfirmationContext);
478
+ const context = useContext4(ConfirmationContext);
464
479
  if (!context) {
465
480
  throw new Error("useConfirmation must be used within a ConfirmationProvider");
466
481
  }
467
482
  return context.service;
468
483
  }
469
484
  function useConfirmationState() {
470
- const context = useContext3(ConfirmationContext);
485
+ const context = useContext4(ConfirmationContext);
471
486
  if (!context) {
472
487
  throw new Error("useConfirmationState must be used within a ConfirmationProvider");
473
488
  }
474
489
  return { confirmation: context.confirmation, respond: context.respond };
475
490
  }
476
491
  function useConfirmationContext() {
477
- const context = useContext3(ConfirmationContext);
492
+ const context = useContext4(ConfirmationContext);
478
493
  if (!context) {
479
494
  throw new Error("useConfirmationContext must be used within a ConfirmationProvider");
480
495
  }
@@ -483,15 +498,15 @@ function useConfirmationContext() {
483
498
 
484
499
  // src/contexts/modal.context.tsx
485
500
  import {
486
- createContext as createContext5,
487
- useContext as useContext4,
501
+ createContext as createContext6,
502
+ useContext as useContext5,
488
503
  useCallback as useCallback3,
489
504
  useState as useState3,
490
505
  useRef as useRef3,
491
506
  useMemo as useMemo3
492
507
  } from "react";
493
508
  import { jsx as jsx3, jsxs } from "react/jsx-runtime";
494
- var ModalContext = createContext5(null);
509
+ var ModalContext = createContext6(null);
495
510
  function ModalProvider({ children }) {
496
511
  const [modalState, setModalState] = useState3(null);
497
512
  const [, setUpdateCounter] = useState3(0);
@@ -527,21 +542,21 @@ function ModalProvider({ children }) {
527
542
  ] });
528
543
  }
529
544
  function useModal() {
530
- const context = useContext4(ModalContext);
545
+ const context = useContext5(ModalContext);
531
546
  if (!context) {
532
547
  throw new Error("useModal must be used within a ModalProvider");
533
548
  }
534
549
  return context.service;
535
550
  }
536
551
  function useModalState() {
537
- const context = useContext4(ModalContext);
552
+ const context = useContext5(ModalContext);
538
553
  if (!context) {
539
554
  throw new Error("useModalState must be used within a ModalProvider");
540
555
  }
541
556
  return context.modalState;
542
557
  }
543
558
  function useModalContext() {
544
- const context = useContext4(ModalContext);
559
+ const context = useContext5(ModalContext);
545
560
  if (!context) {
546
561
  throw new Error("useModalContext must be used within a ModalProvider");
547
562
  }
@@ -2066,6 +2081,73 @@ function ThemeSharedProvider({
2066
2081
  return /* @__PURE__ */ jsx16(ChakraProvider, { value: system, children: /* @__PURE__ */ jsx16(LocaleProvider, { locale, children: /* @__PURE__ */ jsx16(ColorModeProvider, { ...colorModeProps, children: content }) }) });
2067
2082
  }
2068
2083
 
2084
+ // src/handlers/lazy-style.handler.ts
2085
+ import { useEffect as useEffect7, useRef as useRef7, useState as useState6 } from "react";
2086
+ import { LazyLoadService } from "@abpjs/core";
2087
+ function createLazyStyleHref(style, dir) {
2088
+ return style.replace("{{dir}}", dir);
2089
+ }
2090
+ function useLazyStyleHandler(options = {}) {
2091
+ const { styles = [BOOTSTRAP], initialDirection = "ltr" } = options;
2092
+ const [direction, setDirection] = useState6(initialDirection);
2093
+ const lazyLoadRef = useRef7(new LazyLoadService());
2094
+ const loadedStylesRef = useRef7(/* @__PURE__ */ new Map());
2095
+ useEffect7(() => {
2096
+ document.body.dir = direction;
2097
+ const switchCSS = async () => {
2098
+ const lazyLoad = lazyLoadRef.current;
2099
+ for (const style of styles) {
2100
+ const href = createLazyStyleHref(style, direction);
2101
+ if (lazyLoad.isLoaded(href)) {
2102
+ continue;
2103
+ }
2104
+ const oldDir = direction === "ltr" ? "rtl" : "ltr";
2105
+ const oldHref = createLazyStyleHref(style, oldDir);
2106
+ const oldLink = loadedStylesRef.current.get(oldHref);
2107
+ if (oldLink && oldLink.parentNode) {
2108
+ oldLink.parentNode.removeChild(oldLink);
2109
+ lazyLoad.remove(oldHref);
2110
+ loadedStylesRef.current.delete(oldHref);
2111
+ }
2112
+ try {
2113
+ const link = document.createElement("link");
2114
+ link.rel = "stylesheet";
2115
+ link.href = href;
2116
+ document.head.appendChild(link);
2117
+ loadedStylesRef.current.set(href, link);
2118
+ } catch (error) {
2119
+ console.warn(`Failed to load style: ${href}`, error);
2120
+ }
2121
+ }
2122
+ };
2123
+ switchCSS();
2124
+ }, [direction, styles]);
2125
+ return {
2126
+ direction,
2127
+ setDirection
2128
+ };
2129
+ }
2130
+ function getLoadedBootstrapDirection(styles = [BOOTSTRAP]) {
2131
+ for (const style of styles) {
2132
+ const ltrHref = createLazyStyleHref(style, "ltr");
2133
+ const rtlHref = createLazyStyleHref(style, "rtl");
2134
+ const links = document.querySelectorAll('link[rel="stylesheet"]');
2135
+ for (const link of links) {
2136
+ const href = link.href;
2137
+ if (href.includes(ltrHref)) return "ltr";
2138
+ if (href.includes(rtlHref)) return "rtl";
2139
+ }
2140
+ }
2141
+ return void 0;
2142
+ }
2143
+ function initLazyStyleHandler(options = {}) {
2144
+ return () => {
2145
+ const { initialDirection = "ltr" } = options;
2146
+ document.body.dir = initialDirection;
2147
+ return { direction: initialDirection };
2148
+ };
2149
+ }
2150
+
2069
2151
  // src/utils/styles.ts
2070
2152
  var THEME_SHARED_STYLES = `
2071
2153
  /* Form validation styling */
@@ -2137,6 +2219,44 @@ function injectThemeSharedStyles() {
2137
2219
  };
2138
2220
  }
2139
2221
 
2222
+ // src/utils/nav-items.ts
2223
+ var navItems = [];
2224
+ var subscribers = /* @__PURE__ */ new Set();
2225
+ function addNavItem(item) {
2226
+ navItems = [...navItems, item].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
2227
+ notifySubscribers();
2228
+ }
2229
+ function removeNavItem(item) {
2230
+ navItems = navItems.filter((i) => i !== item);
2231
+ notifySubscribers();
2232
+ }
2233
+ function clearNavItems() {
2234
+ navItems = [];
2235
+ notifySubscribers();
2236
+ }
2237
+ function getNavItemsSync() {
2238
+ return [...navItems];
2239
+ }
2240
+ function subscribeToNavItems(callback) {
2241
+ subscribers.add(callback);
2242
+ callback([...navItems]);
2243
+ return () => {
2244
+ subscribers.delete(callback);
2245
+ };
2246
+ }
2247
+ function notifySubscribers() {
2248
+ const currentItems = [...navItems];
2249
+ subscribers.forEach((callback) => callback(currentItems));
2250
+ }
2251
+ function getNavItems() {
2252
+ return {
2253
+ subscribe: (callback) => {
2254
+ const unsubscribe = subscribeToNavItems(callback);
2255
+ return { unsubscribe };
2256
+ }
2257
+ };
2258
+ }
2259
+
2140
2260
  // src/utils/validation-utils.ts
2141
2261
  var PASSWORD_SETTING_KEYS = {
2142
2262
  requiredLength: "Abp.Identity.Password.RequiredLength",
@@ -2255,6 +2375,7 @@ export {
2255
2375
  AbpModalFooter,
2256
2376
  AbpModalHeader,
2257
2377
  Alert,
2378
+ BOOTSTRAP,
2258
2379
  Button3 as Button,
2259
2380
  Dialog2 as ChakraDialog,
2260
2381
  ChangePassword,
@@ -2262,11 +2383,14 @@ export {
2262
2383
  Confirmation,
2263
2384
  ConfirmationDialog,
2264
2385
  ConfirmationProvider,
2386
+ DEFAULT_LAZY_STYLES,
2265
2387
  DEFAULT_STYLES,
2266
2388
  ErrorComponent,
2267
2389
  FormField,
2268
2390
  HTTP_ERROR_CONFIG,
2269
2391
  HttpErrorConfigContext,
2392
+ LAZY_STYLES,
2393
+ LazyStylesContext,
2270
2394
  LoaderBar,
2271
2395
  Modal,
2272
2396
  AbpModalBody as ModalBody,
@@ -2284,10 +2408,16 @@ export {
2284
2408
  Toaster,
2285
2409
  ToasterProvider,
2286
2410
  abpSystem,
2411
+ addNavItem,
2412
+ clearNavItems,
2287
2413
  createAbpSystem,
2288
2414
  createErrorInterceptor,
2415
+ createLazyStyleHref,
2289
2416
  defaultAbpConfig,
2290
2417
  defineConfig,
2418
+ getLoadedBootstrapDirection,
2419
+ getNavItems,
2420
+ getNavItemsSync,
2291
2421
  getPasswordSettings,
2292
2422
  getPasswordValidationRules,
2293
2423
  getPasswordValidators,
@@ -2295,12 +2425,17 @@ export {
2295
2425
  getSeverityBorderColor,
2296
2426
  getSeverityColorPalette as getSeverityColorScheme,
2297
2427
  httpErrorConfigFactory,
2428
+ initLazyStyleHandler,
2298
2429
  injectThemeSharedStyles,
2430
+ removeNavItem,
2431
+ subscribeToNavItems,
2299
2432
  useConfirmation,
2300
2433
  useConfirmationContext,
2301
2434
  useConfirmationState,
2302
2435
  useErrorHandler,
2303
2436
  useHttpErrorConfig,
2437
+ useLazyStyleHandler,
2438
+ useLazyStyles,
2304
2439
  useModal,
2305
2440
  useModalContext,
2306
2441
  useModalState,
@@ -3,8 +3,14 @@
3
3
  * Translated from @abp/ng.theme.shared/lib/models/common.ts
4
4
  * @since 1.1.0
5
5
  * @since 2.7.0 - Added skipHandledErrorCodes, simplified forWhichErrors type
6
+ * @since 2.9.0 - Added LocaleDirection type
6
7
  */
7
8
  import type { ComponentType } from 'react';
9
+ /**
10
+ * Locale direction for RTL/LTR support.
11
+ * @since 2.9.0
12
+ */
13
+ export type LocaleDirection = 'ltr' | 'rtl';
8
14
  /**
9
15
  * Root parameters for ThemeSharedModule configuration.
10
16
  * @since 1.1.0
@@ -9,18 +9,23 @@
9
9
  * - Removed deprecated cancelCopy/yesCopy
10
10
  *
11
11
  * @since 2.1.0 - Added Status enum (confirmation-specific, replaces Toaster.Status usage)
12
+ * @since 2.9.0 - Added dismissible property, deprecated closable
12
13
  */
13
14
  import type { Config } from '@abpjs/core';
14
15
  export declare namespace Confirmation {
15
16
  /**
16
17
  * Options for configuring a confirmation dialog.
17
18
  * @since 2.0.0 - No longer extends Toaster.Options
19
+ * @since 2.9.0 - Added dismissible, deprecated closable
18
20
  */
19
21
  interface Options {
20
22
  /** Unique identifier for the confirmation */
21
23
  id?: string | number;
22
- /** Whether the confirmation can be closed by clicking outside or pressing escape */
23
- closable?: boolean;
24
+ /**
25
+ * Whether the confirmation can be dismissed by clicking outside or pressing escape.
26
+ * @since 2.9.0
27
+ */
28
+ dismissible?: boolean;
24
29
  /** Parameters for localizing the message */
25
30
  messageLocalizationParams?: string[];
26
31
  /** Parameters for localizing the title */
@@ -33,6 +38,11 @@ export declare namespace Confirmation {
33
38
  cancelText?: Config.LocalizationParam;
34
39
  /** Custom text for the yes button */
35
40
  yesText?: Config.LocalizationParam;
41
+ /**
42
+ * Whether the confirmation can be closed by clicking outside or pressing escape.
43
+ * @deprecated Use dismissible instead. To be deleted in v3.0.
44
+ */
45
+ closable?: boolean;
36
46
  }
37
47
  /**
38
48
  * Dialog data structure for confirmation dialogs.
@@ -3,6 +3,8 @@
3
3
  * Translated from @abp/ng.theme.shared/lib/tokens
4
4
  *
5
5
  * @since 2.7.0
6
+ * @since 2.9.0 - Added LAZY_STYLES token
6
7
  */
7
8
  export * from './append-content.token';
8
9
  export * from './http-error.token';
10
+ export * from './lazy-styles.token';
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Lazy Styles Token
3
+ * Translated from @abp/ng.theme.shared/lib/tokens/lazy-styles.token.ts
4
+ *
5
+ * Provides configuration for lazy-loaded stylesheets with RTL/LTR support.
6
+ *
7
+ * @since 2.9.0
8
+ */
9
+ /**
10
+ * Default lazy styles configuration.
11
+ * @since 2.9.0
12
+ */
13
+ export declare const DEFAULT_LAZY_STYLES: string[];
14
+ /**
15
+ * Context for lazy styles configuration.
16
+ * @since 2.9.0
17
+ */
18
+ export declare const LazyStylesContext: import("react").Context<string[]>;
19
+ /**
20
+ * Hook to get lazy styles configuration.
21
+ * @returns Array of style patterns with {{dir}} placeholder
22
+ * @since 2.9.0
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * function MyComponent() {
27
+ * const lazyStyles = useLazyStyles();
28
+ * // lazyStyles = ['bootstrap-{{dir}}.min.css']
29
+ * }
30
+ * ```
31
+ */
32
+ export declare function useLazyStyles(): string[];
33
+ /**
34
+ * LAZY_STYLES constant for backwards compatibility.
35
+ * In React, use LazyStylesContext or useLazyStyles() hook instead.
36
+ * @since 2.9.0
37
+ */
38
+ export declare const LAZY_STYLES: string[];
@@ -1,2 +1,3 @@
1
1
  export * from './styles';
2
+ export * from './nav-items';
2
3
  export * from './validation-utils';
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Nav Items Utility
3
+ * Translated from @abp/ng.theme.shared/lib/utils/nav-items.ts
4
+ *
5
+ * Provides functions for managing navigation items dynamically.
6
+ *
7
+ * @since 2.9.0
8
+ */
9
+ import { ComponentType } from 'react';
10
+ /**
11
+ * Navigation item configuration.
12
+ * @since 2.9.0
13
+ */
14
+ export interface NavItem {
15
+ /**
16
+ * React component to render for this nav item.
17
+ */
18
+ component?: ComponentType<any>;
19
+ /**
20
+ * Raw HTML string to render (use with caution for XSS).
21
+ */
22
+ html?: string;
23
+ /**
24
+ * Action to execute when the nav item is clicked.
25
+ */
26
+ action?: () => void;
27
+ /**
28
+ * Order for sorting nav items. Lower numbers appear first.
29
+ */
30
+ order?: number;
31
+ /**
32
+ * Permission required to show this nav item.
33
+ */
34
+ permission?: string;
35
+ }
36
+ type NavItemSubscriber = (items: NavItem[]) => void;
37
+ /**
38
+ * Add a navigation item.
39
+ * @param item - The nav item to add
40
+ * @since 2.9.0
41
+ *
42
+ * @example
43
+ * ```tsx
44
+ * addNavItem({
45
+ * component: MyComponent,
46
+ * order: 10,
47
+ * permission: 'AbpIdentity.Users',
48
+ * });
49
+ * ```
50
+ */
51
+ export declare function addNavItem(item: NavItem): void;
52
+ /**
53
+ * Remove a navigation item.
54
+ * @param item - The nav item to remove
55
+ * @since 2.9.0
56
+ */
57
+ export declare function removeNavItem(item: NavItem): void;
58
+ /**
59
+ * Clear all navigation items.
60
+ * @since 2.9.0
61
+ */
62
+ export declare function clearNavItems(): void;
63
+ /**
64
+ * Get current navigation items.
65
+ * @returns Array of nav items
66
+ * @since 2.9.0
67
+ */
68
+ export declare function getNavItemsSync(): NavItem[];
69
+ /**
70
+ * Subscribe to navigation item changes.
71
+ * Returns an unsubscribe function.
72
+ *
73
+ * @param callback - Function to call when nav items change
74
+ * @returns Unsubscribe function
75
+ * @since 2.9.0
76
+ *
77
+ * @example
78
+ * ```tsx
79
+ * const unsubscribe = subscribeToNavItems((items) => {
80
+ * console.log('Nav items changed:', items);
81
+ * });
82
+ *
83
+ * // Later, to unsubscribe:
84
+ * unsubscribe();
85
+ * ```
86
+ */
87
+ export declare function subscribeToNavItems(callback: NavItemSubscriber): () => void;
88
+ /**
89
+ * Get navigation items as an observable-like interface.
90
+ * Compatible with Angular's Observable pattern.
91
+ *
92
+ * @returns Object with subscribe method
93
+ * @since 2.9.0
94
+ *
95
+ * @example
96
+ * ```tsx
97
+ * const subscription = getNavItems().subscribe((items) => {
98
+ * console.log('Nav items:', items);
99
+ * });
100
+ *
101
+ * // Later, to unsubscribe:
102
+ * subscription.unsubscribe();
103
+ * ```
104
+ */
105
+ export declare function getNavItems(): {
106
+ subscribe: (callback: NavItemSubscriber) => {
107
+ unsubscribe: () => void;
108
+ };
109
+ };
110
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abpjs/theme-shared",
3
- "version": "2.7.0",
3
+ "version": "2.9.0",
4
4
  "description": "ABP Framework Theme Shared components for React - translated from @abp/ng.theme.shared",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -27,10 +27,10 @@
27
27
  "next-themes": "^0.4.6",
28
28
  "react-hook-form": "^7.48.0",
29
29
  "react-icons": "^5.5.0",
30
- "@abpjs/core": "2.7.0"
30
+ "@abpjs/core": "2.9.0"
31
31
  },
32
32
  "devDependencies": {
33
- "@abp/ng.theme.shared": "2.7.0",
33
+ "@abp/ng.theme.shared": "2.9.0",
34
34
  "@vitest/coverage-v8": "^3.2.0"
35
35
  },
36
36
  "author": "tekthar.com",