@abpjs/setting-management 0.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 ADDED
@@ -0,0 +1,287 @@
1
+ // src/constants/routes.ts
2
+ var SETTING_MANAGEMENT_ROUTES = {
3
+ routes: [
4
+ {
5
+ name: "Settings",
6
+ path: "setting-management",
7
+ layout: "application",
8
+ order: 100
9
+ }
10
+ ]
11
+ };
12
+
13
+ // src/services/setting-management.service.ts
14
+ var SettingManagementService = class {
15
+ constructor() {
16
+ this._settings = [];
17
+ this._selected = null;
18
+ this._subscribers = /* @__PURE__ */ new Set();
19
+ }
20
+ /**
21
+ * Get all registered settings tabs sorted by order
22
+ */
23
+ get settings() {
24
+ return [...this._settings].sort((a, b) => a.order - b.order);
25
+ }
26
+ /**
27
+ * Get the currently selected setting tab
28
+ */
29
+ get selected() {
30
+ return this._selected;
31
+ }
32
+ /**
33
+ * Register a new setting tab
34
+ * @param tab The setting tab to register
35
+ */
36
+ addSetting(tab) {
37
+ const exists = this._settings.some((s) => s.name === tab.name);
38
+ if (!exists) {
39
+ this._settings.push(tab);
40
+ this.notifySubscribers();
41
+ }
42
+ }
43
+ /**
44
+ * Register multiple setting tabs at once
45
+ * @param tabs Array of setting tabs to register
46
+ */
47
+ addSettings(tabs) {
48
+ let changed = false;
49
+ for (const tab of tabs) {
50
+ const exists = this._settings.some((s) => s.name === tab.name);
51
+ if (!exists) {
52
+ this._settings.push(tab);
53
+ changed = true;
54
+ }
55
+ }
56
+ if (changed) {
57
+ this.notifySubscribers();
58
+ }
59
+ }
60
+ /**
61
+ * Remove a setting tab by name
62
+ * @param name The name of the setting tab to remove
63
+ */
64
+ removeSetting(name) {
65
+ const index = this._settings.findIndex((s) => s.name === name);
66
+ if (index !== -1) {
67
+ this._settings.splice(index, 1);
68
+ if (this._selected?.name === name) {
69
+ this._selected = null;
70
+ }
71
+ this.notifySubscribers();
72
+ }
73
+ }
74
+ /**
75
+ * Set the currently selected setting tab
76
+ * @param tab The tab to select (or null to clear selection)
77
+ */
78
+ setSelected(tab) {
79
+ this._selected = tab;
80
+ this.notifySubscribers();
81
+ }
82
+ /**
83
+ * Select a setting tab by name
84
+ * @param name The name of the tab to select
85
+ */
86
+ selectByName(name) {
87
+ const tab = this._settings.find((s) => s.name === name);
88
+ if (tab) {
89
+ this.setSelected(tab);
90
+ }
91
+ }
92
+ /**
93
+ * Select a setting tab by URL
94
+ * @param url The URL of the tab to select
95
+ */
96
+ selectByUrl(url) {
97
+ const tab = this._settings.find((s) => s.url === url);
98
+ if (tab) {
99
+ this.setSelected(tab);
100
+ }
101
+ }
102
+ /**
103
+ * Clear all registered settings
104
+ */
105
+ clearSettings() {
106
+ this._settings = [];
107
+ this._selected = null;
108
+ this.notifySubscribers();
109
+ }
110
+ /**
111
+ * Subscribe to changes in settings or selection
112
+ * @param callback Function to call when changes occur
113
+ * @returns Unsubscribe function
114
+ */
115
+ subscribe(callback) {
116
+ this._subscribers.add(callback);
117
+ return () => {
118
+ this._subscribers.delete(callback);
119
+ };
120
+ }
121
+ notifySubscribers() {
122
+ this._subscribers.forEach((callback) => callback());
123
+ }
124
+ };
125
+ var _instance = null;
126
+ function getSettingManagementService() {
127
+ if (!_instance) {
128
+ _instance = new SettingManagementService();
129
+ }
130
+ return _instance;
131
+ }
132
+
133
+ // src/hooks/useSettingManagement.ts
134
+ import { useState, useEffect, useCallback, useMemo } from "react";
135
+ function useSettingManagement() {
136
+ const service = useMemo(() => getSettingManagementService(), []);
137
+ const [, forceUpdate] = useState({});
138
+ useEffect(() => {
139
+ const unsubscribe = service.subscribe(() => {
140
+ forceUpdate({});
141
+ });
142
+ return unsubscribe;
143
+ }, [service]);
144
+ const addSetting = useCallback(
145
+ (tab) => {
146
+ service.addSetting(tab);
147
+ },
148
+ [service]
149
+ );
150
+ const addSettings = useCallback(
151
+ (tabs) => {
152
+ service.addSettings(tabs);
153
+ },
154
+ [service]
155
+ );
156
+ const removeSetting = useCallback(
157
+ (name) => {
158
+ service.removeSetting(name);
159
+ },
160
+ [service]
161
+ );
162
+ const setSelected = useCallback(
163
+ (tab) => {
164
+ service.setSelected(tab);
165
+ },
166
+ [service]
167
+ );
168
+ const selectByName = useCallback(
169
+ (name) => {
170
+ service.selectByName(name);
171
+ },
172
+ [service]
173
+ );
174
+ const selectByUrl = useCallback(
175
+ (url) => {
176
+ service.selectByUrl(url);
177
+ },
178
+ [service]
179
+ );
180
+ const clearSettings = useCallback(() => {
181
+ service.clearSettings();
182
+ }, [service]);
183
+ return {
184
+ settings: service.settings,
185
+ selected: service.selected,
186
+ addSetting,
187
+ addSettings,
188
+ removeSetting,
189
+ setSelected,
190
+ selectByName,
191
+ selectByUrl,
192
+ clearSettings
193
+ };
194
+ }
195
+
196
+ // src/components/SettingLayout/SettingLayout.tsx
197
+ import { useEffect as useEffect2 } from "react";
198
+ import { useLocation, useNavigate } from "react-router-dom";
199
+ import { jsx, jsxs } from "react/jsx-runtime";
200
+ function SettingLayout({
201
+ children,
202
+ onTabSelect,
203
+ className = ""
204
+ }) {
205
+ const { settings, selected, setSelected } = useSettingManagement();
206
+ const location = useLocation();
207
+ const navigate = useNavigate();
208
+ useEffect2(() => {
209
+ if (settings.length > 0) {
210
+ const matchingTab = settings.find((tab) => tab.url && location.pathname.startsWith(tab.url));
211
+ if (matchingTab) {
212
+ setSelected(matchingTab);
213
+ } else if (!selected && settings.length > 0) {
214
+ setSelected(settings[0]);
215
+ }
216
+ }
217
+ }, [location.pathname, settings, selected, setSelected]);
218
+ const handleTabClick = (tab) => {
219
+ setSelected(tab);
220
+ onTabSelect?.(tab);
221
+ if (tab.url) {
222
+ navigate(tab.url);
223
+ }
224
+ };
225
+ return /* @__PURE__ */ jsxs("div", { className: `setting-layout ${className}`, style: styles.container, children: [
226
+ /* @__PURE__ */ jsx("div", { className: "setting-layout-sidebar", style: styles.sidebar, children: /* @__PURE__ */ jsx("nav", { className: "setting-tabs", style: styles.nav, children: settings.map((tab) => /* @__PURE__ */ jsx(
227
+ "button",
228
+ {
229
+ type: "button",
230
+ onClick: () => handleTabClick(tab),
231
+ className: `setting-tab ${selected?.name === tab.name ? "active" : ""}`,
232
+ style: {
233
+ ...styles.tabButton,
234
+ ...selected?.name === tab.name ? styles.tabButtonActive : {}
235
+ },
236
+ children: tab.name
237
+ },
238
+ tab.name
239
+ )) }) }),
240
+ /* @__PURE__ */ jsx("div", { className: "setting-layout-content", style: styles.content, children })
241
+ ] });
242
+ }
243
+ var styles = {
244
+ container: {
245
+ display: "flex",
246
+ minHeight: "400px",
247
+ gap: "24px"
248
+ },
249
+ sidebar: {
250
+ width: "240px",
251
+ flexShrink: 0
252
+ },
253
+ nav: {
254
+ display: "flex",
255
+ flexDirection: "column",
256
+ gap: "4px"
257
+ },
258
+ tabButton: {
259
+ display: "block",
260
+ width: "100%",
261
+ padding: "12px 16px",
262
+ textAlign: "left",
263
+ border: "none",
264
+ borderRadius: "6px",
265
+ background: "transparent",
266
+ cursor: "pointer",
267
+ fontSize: "14px",
268
+ color: "inherit",
269
+ transition: "background-color 0.2s"
270
+ },
271
+ tabButtonActive: {
272
+ backgroundColor: "rgba(59, 130, 246, 0.1)",
273
+ color: "#3b82f6",
274
+ fontWeight: 500
275
+ },
276
+ content: {
277
+ flex: 1,
278
+ minWidth: 0
279
+ }
280
+ };
281
+ export {
282
+ SETTING_MANAGEMENT_ROUTES,
283
+ SettingLayout,
284
+ SettingManagementService,
285
+ getSettingManagementService,
286
+ useSettingManagement
287
+ };
package/package.json ADDED
@@ -0,0 +1,86 @@
1
+ {
2
+ "name": "@abpjs/setting-management",
3
+ "version": "0.9.0",
4
+ "description": "ABP Framework setting-management components for React - translated from @abp/ng.setting-management",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "keywords": [
20
+ "abp",
21
+ "react",
22
+ "framework",
23
+ "setting-management"
24
+ ],
25
+ "dependencies": {
26
+ "@chakra-ui/react": "^3.2.0",
27
+ "@emotion/react": "^11.11.0",
28
+ "@abpjs/core": "0.9.0",
29
+ "@abpjs/theme-shared": "0.9.0",
30
+ "@abpjs/permission-management": "0.9.0"
31
+ },
32
+ "devDependencies": {
33
+ "@abp/ng.setting-management": "0.9.0",
34
+ "@testing-library/jest-dom": "^6.9.1",
35
+ "@testing-library/react": "^14.0.0",
36
+ "@testing-library/user-event": "^14.6.1",
37
+ "@types/react": "^18.2.0",
38
+ "@types/react-dom": "^18.2.0",
39
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
40
+ "@typescript-eslint/parser": "^6.0.0",
41
+ "autoprefixer": "^10.4.16",
42
+ "eslint": "^8.0.0",
43
+ "eslint-config-prettier": "^9.1.2",
44
+ "eslint-plugin-react": "^7.0.0",
45
+ "eslint-plugin-react-hooks": "^4.0.0",
46
+ "jsdom": "^24.0.0",
47
+ "postcss": "^8.4.32",
48
+ "prettier": "^3.7.4",
49
+ "react": "^18.2.0",
50
+ "react-dom": "^18.2.0",
51
+ "react-router-dom": "^6.20.0",
52
+ "tailwindcss": "^3.4.0",
53
+ "tsup": "^8.0.0",
54
+ "typescript": "^5.0.0",
55
+ "vitest": "^1.0.0",
56
+ "@vitest/coverage-v8": "^1.0.0"
57
+ },
58
+ "author": "tekthar.com",
59
+ "license": "LGPL-3.0",
60
+ "publishConfig": {
61
+ "access": "public"
62
+ },
63
+ "repository": {
64
+ "type": "git",
65
+ "url": "https://github.com/abpjs/abp-react"
66
+ },
67
+ "homepage": "https://docs.abpjs.io/docs/packages/setting-management/overview",
68
+ "bugs": {
69
+ "url": "https://github.com/abpjs/abp-react/issues"
70
+ },
71
+ "peerDependencies": {
72
+ "react": ">=17.0.0",
73
+ "react-dom": ">=17.0.0",
74
+ "react-router-dom": ">=6.0.0"
75
+ },
76
+ "scripts": {
77
+ "build": "tsup",
78
+ "dev": "tsup src/index.ts --format cjs,esm --watch",
79
+ "lint": "eslint src",
80
+ "lint:fix": "eslint src --fix",
81
+ "format": "prettier --write \"src/**/*.{ts,tsx,json,md}\"",
82
+ "format:check": "prettier --check \"src/**/*.{ts,tsx,json,md}\"",
83
+ "type-check": "tsc --noEmit",
84
+ "test": "vitest"
85
+ }
86
+ }