@crystaltech/hsms-shared-ui 0.5.30 → 0.5.31-alpha-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/README.md +214 -878
- package/dist/App.d.ts +2 -0
- package/dist/components/ColorSettings.d.ts +5 -3
- package/dist/components/ThemeSetting.d.ts +4 -4
- package/dist/components/TypographySettings.d.ts +5 -3
- package/dist/index.es.js +4685 -4575
- package/dist/index.js +76 -76
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,946 +1,282 @@
|
|
|
1
|
-
#
|
|
1
|
+
# HSMS Shared UI
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
### Overview
|
|
6
|
-
|
|
7
|
-
`@crystaltech/hsms-shared-ui` is a customizable UI component library built with React and Material-UI. It provides a structured layout system with a `MainLayout` component, theme customization, and configurable navigation options for web applications.
|
|
8
|
-
|
|
9
|
-
---
|
|
3
|
+
A reusable React + MUI component library for HSMS applications, including a configurable layout, theme tools, and UI primitives.
|
|
10
4
|
|
|
11
5
|
## Installation
|
|
12
6
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
pnpm install @crystaltech/hsms-shared-ui@latest
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
or using Yarn:
|
|
20
|
-
|
|
21
|
-
```sh
|
|
22
|
-
yarn add @crystaltech/hsms-shared-ui@latest
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Usage
|
|
28
|
-
|
|
29
|
-
### Must Follow
|
|
30
|
-
|
|
31
|
-
Create a `types` folder inside the `src` directory and add the following file:
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
/src/types/hsms-shared-ui.d.ts
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
**Note:** Make sure to add the following content to the `hsms-shared-ui.d.ts` file:
|
|
7
|
+
- pnpm: `pnpm add @crystaltech/hsms-shared-ui`
|
|
8
|
+
- yarn: `yarn add @crystaltech/hsms-shared-ui`
|
|
9
|
+
- npm: `npm i @crystaltech/hsms-shared-ui`
|
|
38
10
|
|
|
39
|
-
|
|
40
|
-
declare module "@crystaltech/hsms-shared-ui";
|
|
41
|
-
```
|
|
11
|
+
Peer deps (match your app): `@mui/material`, `@mui/icons-material`, `@mui/x-date-pickers`, `@emotion/*`, `react`, `react-dom`, `dayjs`, `react-perfect-scrollbar`, `simplebar-react`.
|
|
42
12
|
|
|
43
|
-
###
|
|
13
|
+
### Using in other modules
|
|
44
14
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
import
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
background: "#000000",
|
|
63
|
-
foreground: "#000000",
|
|
64
|
-
},
|
|
65
|
-
primary: {
|
|
66
|
-
main: "#ef5350", // You can change this color
|
|
67
|
-
},
|
|
68
|
-
secondary: {
|
|
69
|
-
main: "#f8fafc",
|
|
70
|
-
},
|
|
71
|
-
background: {
|
|
72
|
-
default: "#f8fafc",
|
|
73
|
-
paper: "#ffffff",
|
|
74
|
-
},
|
|
75
|
-
text: {
|
|
76
|
-
primary: "#1e293b",
|
|
77
|
-
secondary: "#64748b",
|
|
78
|
-
},
|
|
79
|
-
neutral: {
|
|
80
|
-
100: "#f6f9fc",
|
|
81
|
-
200: "#e9ecef",
|
|
82
|
-
300: "#dee2e6",
|
|
83
|
-
400: "#ced4da",
|
|
84
|
-
500: "#adb5bd",
|
|
85
|
-
600: "#868e96",
|
|
86
|
-
700: "#495057",
|
|
87
|
-
800: "#343a40",
|
|
88
|
-
900: "#212529",
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
typography: {
|
|
92
|
-
fontFamily: "Roboto",
|
|
93
|
-
h1: {
|
|
94
|
-
fontSize: "2.5rem",
|
|
95
|
-
fontWeight: 700,
|
|
96
|
-
lineHeight: "3rem",
|
|
97
|
-
},
|
|
98
|
-
h2: {
|
|
99
|
-
fontSize: "2rem",
|
|
100
|
-
fontWeight: 700,
|
|
101
|
-
lineHeight: "2.5rem",
|
|
102
|
-
},
|
|
103
|
-
h3: {
|
|
104
|
-
fontSize: "1.75rem",
|
|
105
|
-
fontWeight: 600,
|
|
106
|
-
lineHeight: "2.25rem",
|
|
107
|
-
},
|
|
108
|
-
h4: {
|
|
109
|
-
fontSize: "1.5rem",
|
|
110
|
-
fontWeight: 600,
|
|
111
|
-
lineHeight: "2rem",
|
|
112
|
-
},
|
|
113
|
-
h5: {
|
|
114
|
-
fontSize: "1.25rem",
|
|
115
|
-
fontWeight: 500,
|
|
116
|
-
lineHeight: "1.75rem",
|
|
117
|
-
},
|
|
118
|
-
h6: {
|
|
119
|
-
fontSize: "1rem",
|
|
120
|
-
fontWeight: 500,
|
|
121
|
-
lineHeight: "1.5rem",
|
|
122
|
-
},
|
|
123
|
-
subtitle1: {
|
|
124
|
-
fontSize: "1.125rem",
|
|
125
|
-
fontWeight: 400,
|
|
126
|
-
lineHeight: "1.5rem",
|
|
127
|
-
},
|
|
128
|
-
subtitle2: {
|
|
129
|
-
fontSize: "1rem",
|
|
130
|
-
fontWeight: 400,
|
|
131
|
-
lineHeight: "1.25rem",
|
|
132
|
-
},
|
|
133
|
-
body1: {
|
|
134
|
-
fontSize: "1rem",
|
|
135
|
-
fontWeight: 400,
|
|
136
|
-
lineHeight: "1.5rem",
|
|
137
|
-
},
|
|
138
|
-
body2: {
|
|
139
|
-
fontSize: "0.875rem",
|
|
140
|
-
fontWeight: 400,
|
|
141
|
-
lineHeight: "1.25rem",
|
|
142
|
-
},
|
|
143
|
-
button: {
|
|
144
|
-
fontSize: "0.875rem",
|
|
145
|
-
fontWeight: 500,
|
|
146
|
-
lineHeight: "1.25rem",
|
|
147
|
-
textTransform: "uppercase",
|
|
148
|
-
},
|
|
149
|
-
caption: {
|
|
150
|
-
fontSize: "0.75rem",
|
|
151
|
-
fontWeight: 400,
|
|
152
|
-
lineHeight: "1rem",
|
|
153
|
-
},
|
|
154
|
-
overline: {
|
|
155
|
-
fontSize: "0.75rem",
|
|
156
|
-
fontWeight: 400,
|
|
157
|
-
lineHeight: "1rem",
|
|
158
|
-
textTransform: "uppercase",
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
};
|
|
162
|
-
const root = ReactDOM.createRoot(document.getElementById("root"));
|
|
163
|
-
root.render(
|
|
164
|
-
<ThemeCustomization defaultTheme={defaultTheme}>
|
|
165
|
-
<BrowserRouter>
|
|
166
|
-
<App />
|
|
167
|
-
</BrowserRouter>
|
|
168
|
-
</ThemeCustomization>
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
// If you want to start measuring performance in your app, pass a function
|
|
172
|
-
// to log results (for example: reportWebVitals(console.log))
|
|
173
|
-
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
|
174
|
-
reportWebVitals();
|
|
175
|
-
```
|
|
15
|
+
- Install typings (TypeScript projects):
|
|
16
|
+
- `pnpm add -D typescript @types/react @types/react-dom`
|
|
17
|
+
- Configure TypeScript:
|
|
18
|
+
- `tsconfig.json`:
|
|
19
|
+
- `compilerOptions.jsx: "react-jsx"`
|
|
20
|
+
- `compilerOptions.moduleResolution: "bundler"` (or `"node"`)
|
|
21
|
+
- `compilerOptions.skipLibCheck: true` (optional)
|
|
22
|
+
- Import components:
|
|
23
|
+
- `import { MainLayout, ThemeSetting, CustomButton } from '@crystaltech/hsms-shared-ui';`
|
|
24
|
+
- Import types (optional):
|
|
25
|
+
- `import type { ITheme } from '@crystaltech/hsms-shared-ui/dist/theme/defaultTheme';`
|
|
26
|
+
- `import type { ILayoutConfig } from '@crystaltech/hsms-shared-ui/dist/components/layout/MainLayout';`
|
|
27
|
+
- JavaScript projects:
|
|
28
|
+
- Enable type suggestions: add `// @ts-check` to files or use JSDoc typedefs.
|
|
29
|
+
- In VS Code ensure: `javascript.suggest.autoImports` and `typescript.suggest.autoImports` are enabled.
|
|
30
|
+
- Monorepo linking:
|
|
31
|
+
- Use workspace linking or `pnpm link` and restart the TS server if auto-import suggestions don’t show.
|
|
176
32
|
|
|
177
|
-
|
|
33
|
+
## Quick Start (Theme)
|
|
178
34
|
|
|
179
|
-
|
|
35
|
+
Wrap your app with `ThemeCustomization` and use `ThemeSetting` to edit and persist the theme.
|
|
180
36
|
|
|
181
37
|
```tsx
|
|
182
|
-
import {
|
|
183
|
-
import {
|
|
184
|
-
|
|
185
|
-
import { menuConfig, settingsConfig } from "./dummyData";
|
|
38
|
+
import { ThemeCustomization, ThemeSetting } from "@crystaltech/hsms-shared-ui";
|
|
39
|
+
import { defaultTheme, ITheme } from "@crystaltech/hsms-shared-ui/dist/theme/defaultTheme";
|
|
186
40
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
themeToggler: false,
|
|
191
|
-
},
|
|
192
|
-
sideDrawer: {
|
|
193
|
-
menuConfig,
|
|
194
|
-
settingsConfig: settingsConfig,
|
|
195
|
-
isMinimized: false,
|
|
196
|
-
drawerWidth: 240,
|
|
197
|
-
},
|
|
198
|
-
footerText: settingsConfig.footerText,
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
function App() {
|
|
41
|
+
export default function App() {
|
|
42
|
+
const [theme, setTheme] = useState<ITheme>(defaultTheme);
|
|
43
|
+
const handleSave = async (next: ITheme) => { setTheme(next); return next; };
|
|
202
44
|
return (
|
|
203
|
-
<
|
|
204
|
-
<
|
|
205
|
-
</
|
|
45
|
+
<ThemeCustomization defaultTheme={theme}>
|
|
46
|
+
<ThemeSetting theme={theme} onSave={handleSave} />
|
|
47
|
+
</ThemeCustomization>
|
|
206
48
|
);
|
|
207
49
|
}
|
|
208
|
-
|
|
209
|
-
export default App;
|
|
210
50
|
```
|
|
211
51
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
## Components
|
|
215
|
-
|
|
216
|
-
### 1. `MainLayout`
|
|
217
|
-
|
|
218
|
-
A flexible layout component that includes a Navbar, Sidebar, and Footer.
|
|
219
|
-
|
|
220
|
-
**Props:**
|
|
52
|
+
## Theme Model
|
|
221
53
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
| `layoutConfig` | Object | Configures navbar, sidebar, and footer. |
|
|
54
|
+
- `ITheme` lives in `src/theme/defaultTheme.ts` and includes `palette` (navbar, sidebar, primary/secondary, background, text, neutral) and `typography`.
|
|
55
|
+
- Use `defaultTheme` as a starting point; pass a new object to re-theme.
|
|
225
56
|
|
|
226
|
-
|
|
57
|
+
## Layout
|
|
227
58
|
|
|
228
|
-
###
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
59
|
+
### MainLayout
|
|
60
|
+
- Props: `title: string`, `layoutConfig: ILayoutConfig`, `logoutHandler?: () => Promise<void>`
|
|
61
|
+
- `ILayoutConfig`:
|
|
62
|
+
- `userManage?: { user: UserInfo; redirectUrl: string }`
|
|
63
|
+
- `navbar: { showHamburgerInMobile: boolean; themeToggler: boolean }`
|
|
64
|
+
- `sideDrawer: { menuConfig: IMenuConfig[]; settingsConfig: ISettingsConfig; isMinimized: boolean; drawerWidth: number }`
|
|
65
|
+
- `footerText: string`
|
|
234
66
|
|
|
67
|
+
Example:
|
|
235
68
|
```tsx
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
// pass the DefaultTheme object either manually or from api
|
|
239
|
-
<MainLayout layoutConfig={layoutConfig}>{/* Your content here */}</MainLayout>
|
|
240
|
-
</ThemeCustomization>
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
as we don't have api right now for ThemeCustomization component, we can use the given variable and pass it as a props
|
|
244
|
-
|
|
245
|
-
```ts
|
|
246
|
-
const defaultTheme = {
|
|
247
|
-
palette: {
|
|
248
|
-
mode: "light",
|
|
249
|
-
navbar: {
|
|
250
|
-
background: "#000000",
|
|
251
|
-
foreground: "#000000",
|
|
252
|
-
},
|
|
253
|
-
sidebar: {
|
|
254
|
-
background: "#000000",
|
|
255
|
-
foreground: "#000000",
|
|
256
|
-
},
|
|
257
|
-
primary: {
|
|
258
|
-
main: "#ef5350", // You can change this color
|
|
259
|
-
},
|
|
260
|
-
secondary: {
|
|
261
|
-
main: "#f8fafc",
|
|
262
|
-
},
|
|
263
|
-
background: {
|
|
264
|
-
default: "#f8fafc",
|
|
265
|
-
paper: "#ffffff",
|
|
266
|
-
},
|
|
267
|
-
text: {
|
|
268
|
-
primary: "#1e293b",
|
|
269
|
-
secondary: "#64748b",
|
|
270
|
-
},
|
|
271
|
-
neutral: {
|
|
272
|
-
100: "#f6f9fc",
|
|
273
|
-
200: "#e9ecef",
|
|
274
|
-
300: "#dee2e6",
|
|
275
|
-
400: "#ced4da",
|
|
276
|
-
500: "#adb5bd",
|
|
277
|
-
600: "#868e96",
|
|
278
|
-
700: "#495057",
|
|
279
|
-
800: "#343a40",
|
|
280
|
-
900: "#212529",
|
|
281
|
-
},
|
|
282
|
-
},
|
|
283
|
-
typography: {
|
|
284
|
-
fontFamily: "Roboto",
|
|
285
|
-
h1: {
|
|
286
|
-
fontSize: "2.5rem",
|
|
287
|
-
fontWeight: 700,
|
|
288
|
-
lineHeight: "3rem",
|
|
289
|
-
},
|
|
290
|
-
h2: {
|
|
291
|
-
fontSize: "2rem",
|
|
292
|
-
fontWeight: 700,
|
|
293
|
-
lineHeight: "2.5rem",
|
|
294
|
-
},
|
|
295
|
-
h3: {
|
|
296
|
-
fontSize: "1.75rem",
|
|
297
|
-
fontWeight: 600,
|
|
298
|
-
lineHeight: "2.25rem",
|
|
299
|
-
},
|
|
300
|
-
h4: {
|
|
301
|
-
fontSize: "1.5rem",
|
|
302
|
-
fontWeight: 600,
|
|
303
|
-
lineHeight: "2rem",
|
|
304
|
-
},
|
|
305
|
-
h5: {
|
|
306
|
-
fontSize: "1.25rem",
|
|
307
|
-
fontWeight: 500,
|
|
308
|
-
lineHeight: "1.75rem",
|
|
309
|
-
},
|
|
310
|
-
h6: {
|
|
311
|
-
fontSize: "1rem",
|
|
312
|
-
fontWeight: 500,
|
|
313
|
-
lineHeight: "1.5rem",
|
|
314
|
-
},
|
|
315
|
-
subtitle1: {
|
|
316
|
-
fontSize: "1.125rem",
|
|
317
|
-
fontWeight: 400,
|
|
318
|
-
lineHeight: "1.5rem",
|
|
319
|
-
},
|
|
320
|
-
subtitle2: {
|
|
321
|
-
fontSize: "1rem",
|
|
322
|
-
fontWeight: 400,
|
|
323
|
-
lineHeight: "1.25rem",
|
|
324
|
-
},
|
|
325
|
-
body1: {
|
|
326
|
-
fontSize: "1rem",
|
|
327
|
-
fontWeight: 400,
|
|
328
|
-
lineHeight: "1.5rem",
|
|
329
|
-
},
|
|
330
|
-
body2: {
|
|
331
|
-
fontSize: "0.875rem",
|
|
332
|
-
fontWeight: 400,
|
|
333
|
-
lineHeight: "1.25rem",
|
|
334
|
-
},
|
|
335
|
-
button: {
|
|
336
|
-
fontSize: "0.875rem",
|
|
337
|
-
fontWeight: 500,
|
|
338
|
-
lineHeight: "1.25rem",
|
|
339
|
-
textTransform: "uppercase",
|
|
340
|
-
},
|
|
341
|
-
caption: {
|
|
342
|
-
fontSize: "0.75rem",
|
|
343
|
-
fontWeight: 400,
|
|
344
|
-
lineHeight: "1rem",
|
|
345
|
-
},
|
|
346
|
-
overline: {
|
|
347
|
-
fontSize: "0.75rem",
|
|
348
|
-
fontWeight: 400,
|
|
349
|
-
lineHeight: "1rem",
|
|
350
|
-
textTransform: "uppercase",
|
|
351
|
-
},
|
|
352
|
-
},
|
|
353
|
-
};
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
---
|
|
357
|
-
|
|
358
|
-
## Configuration Options
|
|
359
|
-
|
|
360
|
-
The `layoutConfig` object provides the following options:
|
|
69
|
+
import { MainLayout } from "@crystaltech/hsms-shared-ui";
|
|
70
|
+
import { menuConfig, settingsConfig } from "@crystaltech/hsms-shared-ui/dist/components/layout/layoutConfig";
|
|
361
71
|
|
|
362
|
-
```js
|
|
363
72
|
const layoutConfig = {
|
|
364
|
-
navbar: {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
sideDrawer: {
|
|
369
|
-
menuConfig,
|
|
370
|
-
settingsConfig,
|
|
371
|
-
isMinimized: false,
|
|
372
|
-
drawerWidth: 240,
|
|
373
|
-
},
|
|
374
|
-
footerText: "Your Footer Text Here",
|
|
73
|
+
navbar: { showHamburgerInMobile: true, themeToggler: false },
|
|
74
|
+
sideDrawer: { menuConfig, settingsConfig, isMinimized: false, drawerWidth: 260 },
|
|
75
|
+
userManage: { user, redirectUrl: "/login" },
|
|
76
|
+
footerText: "© 2025 All rights reserved",
|
|
375
77
|
};
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
---
|
|
379
|
-
|
|
380
|
-
### 3. `CustomButton`
|
|
381
|
-
|
|
382
|
-
CustomButton import examples
|
|
383
|
-
|
|
384
|
-
```ts
|
|
385
|
-
<CustomButton variant="outlined">Hello</CustomButton>
|
|
386
|
-
<CustomButton variant="text">Hello</CustomButton>
|
|
387
|
-
<CustomButton variant="outlined" startIcon={<DeleteIcon />}>
|
|
388
|
-
Hello
|
|
389
|
-
</CustomButton>
|
|
390
|
-
<CustomButton endIcon={<SendIcon />}>Hello</CustomButton>
|
|
391
|
-
<CustomButton endIcon={<SendIcon />} loading>
|
|
392
|
-
Hello
|
|
393
|
-
</CustomButton>
|
|
394
78
|
|
|
79
|
+
<MainLayout title="HSMS" layoutConfig={layoutConfig}>
|
|
80
|
+
{/* children */}
|
|
81
|
+
</MainLayout>
|
|
395
82
|
```
|
|
396
83
|
|
|
397
|
-
|
|
84
|
+
### MainlayoutWithWrapper
|
|
85
|
+
- Wraps `MainLayout` with a `BrowserRouter`. Use when you don’t already have a router at the app root.
|
|
398
86
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
endIcon?: React.ReactNode;
|
|
403
|
-
loading?: boolean;
|
|
404
|
-
}
|
|
87
|
+
### RoutesConfigLayout
|
|
88
|
+
- Props: `pageTitle`, `user`, `authenticated`, `userRoles?`, `clients?`, `loginMethod`, `logoutHandler`, `redirectPath?`, `pages`, `publicRoutes?`, `Layout?`, `mainLayoutConfig`, `NotFoundPage?`.
|
|
89
|
+
- Builds routes from `pages` and protects them via `ProtectedRoute`.
|
|
405
90
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
};
|
|
91
|
+
Example (static pages map):
|
|
92
|
+
```tsx
|
|
93
|
+
import { RoutesConfigLayout } from "@crystaltech/hsms-shared-ui";
|
|
94
|
+
import Home from "./pages/index";
|
|
95
|
+
import TablePage from "./pages/table";
|
|
96
|
+
|
|
97
|
+
const pages = {
|
|
98
|
+
"/src/pages/index.tsx": { default: Home },
|
|
99
|
+
"/src/pages/table/index.tsx": { default: TablePage, roles: ["admin"] },
|
|
100
|
+
} satisfies import("@crystaltech/hsms-shared-ui/dist/routes/RoutesConfigLayout").IPages;
|
|
101
|
+
|
|
102
|
+
<RoutesConfigLayout
|
|
103
|
+
pageTitle="HSMS"
|
|
104
|
+
user={user}
|
|
105
|
+
authenticated={isAuthenticated}
|
|
106
|
+
loginMethod={login}
|
|
107
|
+
logoutHandler={logout}
|
|
108
|
+
redirectPath="/login"
|
|
109
|
+
pages={pages}
|
|
110
|
+
publicRoutes={[{ path: "/about", element: <About /> }]}
|
|
111
|
+
mainLayoutConfig={layoutConfig}
|
|
112
|
+
/>
|
|
429
113
|
```
|
|
430
114
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
`Example`
|
|
115
|
+
`UserInfo` supports `realm_access.roles` and per-client roles (`resource_access[client].roles`). Route-level `roles` override global roles if provided.
|
|
434
116
|
|
|
435
|
-
|
|
436
|
-
<CustomIconButton aria-label="delete">
|
|
437
|
-
<DeleteIcon />
|
|
438
|
-
</CustomIconButton>
|
|
439
|
-
<CustomIconButton aria-label="delete" color="primary">
|
|
440
|
-
<AlarmIcon />
|
|
441
|
-
</CustomIconButton>
|
|
442
|
-
<CustomIconButton aria-label="delete" color="secondary">
|
|
443
|
-
<AlarmIcon />
|
|
444
|
-
</CustomIconButton>
|
|
445
|
-
```
|
|
117
|
+
## Data Table
|
|
446
118
|
|
|
447
|
-
|
|
119
|
+
### MultiDynamicTable
|
|
120
|
+
- Props: `tableData: DataStructure`, `rowsPerPage: number`, `page: number`, `handleChangePage`, `handleChangeRowsPerPage`.
|
|
121
|
+
- Features: sticky headers, column visibility toggling, keyword filter (`CustomInput`), pagination.
|
|
448
122
|
|
|
123
|
+
Types (inferred):
|
|
449
124
|
```ts
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const CustomIconButton: React.FC<CustomIconButtonProps> = ({
|
|
457
|
-
children,
|
|
458
|
-
...props
|
|
459
|
-
}) => {
|
|
460
|
-
return <IconButton {...props}>{children}</IconButton>;
|
|
125
|
+
export type FieldContentActionsProps = {
|
|
126
|
+
label: string;
|
|
127
|
+
icon?: React.ReactNode;
|
|
128
|
+
onClick: (item: FieldItem) => void;
|
|
129
|
+
variant?: "outlined" | "filled";
|
|
461
130
|
};
|
|
462
|
-
|
|
463
|
-
export
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
```ts
|
|
471
|
-
const tabsData = [
|
|
472
|
-
{ label: "Tab One", value: "one" },
|
|
473
|
-
{ label: "Tab Two", value: "two" },
|
|
474
|
-
{ label: "Tab Three", value: "three" },
|
|
475
|
-
];
|
|
476
|
-
|
|
477
|
-
const [selectedTab, setSelectedTab] = useState(tabsData[0].value);
|
|
478
|
-
|
|
479
|
-
const handleTabsChange = (event: React.SyntheticEvent, value: string) => {
|
|
480
|
-
setSelectedTab(value);
|
|
131
|
+
export type DatePickerProps = { date: string; onChange?: (item: FieldItem, date: Date) => void };
|
|
132
|
+
export type FieldItem = {
|
|
133
|
+
id: number; // same across rows for a column
|
|
134
|
+
fieldName: string;
|
|
135
|
+
isVisible: boolean;
|
|
136
|
+
fieldType: "text" | "date" | "link" | "label" | "actions" | "date-picker";
|
|
137
|
+
fieldContent: string | DatePickerProps | FieldContentActionsProps[];
|
|
138
|
+
to?: string; // for link
|
|
481
139
|
};
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
tabs={tabsData}
|
|
485
|
-
value={selectedTab}
|
|
486
|
-
onChange={handleTabsChange}
|
|
487
|
-
variant="scrollable" //tabs others props can be passed
|
|
488
|
-
/>;
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
### 6. `CustomTextField`
|
|
492
|
-
|
|
493
|
-
you can pass existing Mui Textfield's pass into it too
|
|
494
|
-
|
|
495
|
-
```ts
|
|
496
|
-
<CustomTextField
|
|
497
|
-
value={name}
|
|
498
|
-
onChange={(value) => setName(value)}
|
|
499
|
-
label="Name"
|
|
500
|
-
/>
|
|
140
|
+
export type FieldItemArray = FieldItem[];
|
|
141
|
+
export type DataStructure = FieldItem[][]; // rows => columns
|
|
501
142
|
```
|
|
502
143
|
|
|
503
|
-
|
|
144
|
+
Example data:
|
|
145
|
+
```tsx
|
|
146
|
+
import { MultiDynamicTable } from "@crystaltech/hsms-shared-ui";
|
|
147
|
+
import Visibility from "@mui/icons-material/Visibility";
|
|
504
148
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
149
|
+
const dynamicTableData: DataStructure = [
|
|
150
|
+
[
|
|
151
|
+
{ id: 1, fieldName: "Name", isVisible: true, fieldType: "link", fieldContent: "Alice", to: "/users/1" },
|
|
152
|
+
{ id: 2, fieldName: "Status", isVisible: true, fieldType: "label", fieldContent: "Active" },
|
|
153
|
+
{ id: 3, fieldName: "Joined", isVisible: true, fieldType: "date", fieldContent: "2025-01-01" },
|
|
154
|
+
{ id: 4, fieldName: "Actions", isVisible: true, fieldType: "actions", fieldContent: [
|
|
155
|
+
{ label: "View", icon: <Visibility />, variant: "outlined", onClick: (item) => console.log(item) },
|
|
156
|
+
] },
|
|
157
|
+
{ id: 5, fieldName: "Reminder", isVisible: true, fieldType: "date-picker", fieldContent: {
|
|
158
|
+
date: "2025-01-05", onChange: (item, date) => console.log(item, date)
|
|
159
|
+
} },
|
|
160
|
+
],
|
|
161
|
+
[
|
|
162
|
+
{ id: 1, fieldName: "Name", isVisible: true, fieldType: "link", fieldContent: "Bob", to: "/users/2" },
|
|
163
|
+
{ id: 2, fieldName: "Status", isVisible: true, fieldType: "label", fieldContent: "Inactive" },
|
|
164
|
+
{ id: 3, fieldName: "Joined", isVisible: true, fieldType: "date", fieldContent: "2024-12-01" },
|
|
165
|
+
{ id: 4, fieldName: "Actions", isVisible: true, fieldType: "actions", fieldContent: [] },
|
|
166
|
+
{ id: 5, fieldName: "Reminder", isVisible: true, fieldType: "date-picker", fieldContent: { date: "2025-02-01" } },
|
|
167
|
+
],
|
|
509
168
|
];
|
|
510
169
|
|
|
511
|
-
<
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
/>;
|
|
518
|
-
|
|
519
|
-
if you want to see multiple section pass multiple as a prop otherwise no need to pass multiple
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
### 8. `AsyncAutoComplete`
|
|
523
|
-
|
|
524
|
-
for passing data at the time of user interaction (api fetch) use this component
|
|
525
|
-
|
|
526
|
-
```ts
|
|
527
|
-
<AsyncAutocomplete
|
|
528
|
-
label="Select a Movie"
|
|
529
|
-
fetchOptions={fetchMovies}
|
|
530
|
-
getOptionLabel={(option) => option.title}
|
|
531
|
-
isOptionEqualToValue={(option, value) => option.title === value.title}
|
|
532
|
-
onChange={(selectedMovie) => console.log("Selected movie:", selectedMovie)}
|
|
533
|
-
multiple
|
|
170
|
+
<MultiDynamicTable
|
|
171
|
+
tableData={dynamicTableData}
|
|
172
|
+
page={0}
|
|
173
|
+
rowsPerPage={10}
|
|
174
|
+
handleChangePage={(_, p) => setPage(p)}
|
|
175
|
+
handleChangeRowsPerPage={(e) => setRowsPerPage(parseInt(e.target.value, 10))}
|
|
534
176
|
/>
|
|
535
177
|
```
|
|
536
178
|
|
|
537
|
-
|
|
179
|
+
## UI Components
|
|
538
180
|
|
|
539
|
-
|
|
540
|
-
const ageOptions = [
|
|
541
|
-
{ value: 10, label: "Ten" },
|
|
542
|
-
{ value: 20, label: "Twenty" },
|
|
543
|
-
{ value: 30, label: "Thirty" },
|
|
544
|
-
];
|
|
181
|
+
All components are re-exported from `src/index.ts`.
|
|
545
182
|
|
|
546
|
-
|
|
183
|
+
- CustomButton
|
|
184
|
+
- Extends MUI `ButtonProps`; adds `startIcon`, `endIcon`, `loading`.
|
|
185
|
+
- `loading` disables the button and shows a spinner.
|
|
547
186
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
};
|
|
187
|
+
- CustomIconButton
|
|
188
|
+
- Thin wrapper over MUI `IconButton` that always renders `children` icon.
|
|
551
189
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
options={ageOptions}
|
|
556
|
-
onChange={handleAgeChange}
|
|
557
|
-
/>;
|
|
558
|
-
```
|
|
190
|
+
- CustomInput
|
|
191
|
+
- Props: `startIcon?`, `placeholder?`, `onChange?(value)`, `count?`, `onClear?`.
|
|
192
|
+
- Shows count badge and clear button when not empty.
|
|
559
193
|
|
|
560
|
-
|
|
194
|
+
- CustomCheckbox
|
|
195
|
+
- Props: `checked?`, `onChange?(checked)`, `label?`. Manages internal state.
|
|
561
196
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
{ label: "Female", value: "female" },
|
|
565
|
-
{ label: "Male", value: "male" },
|
|
566
|
-
{ label: "Other", value: "other" },
|
|
567
|
-
];
|
|
197
|
+
- CustomRadioGroup
|
|
198
|
+
- Props: `label`, `options: {label,value}[]`, `value`, `onChange(value)`.
|
|
568
199
|
|
|
569
|
-
|
|
570
|
-
|
|
200
|
+
- CustomSelect
|
|
201
|
+
- Props: `label`, `value`, `options: { value: string|number; label: string }[]`, `onChange(SelectChangeEvent)`.
|
|
571
202
|
|
|
572
|
-
|
|
203
|
+
- CustomSwitch
|
|
204
|
+
- Props: `checked`, `onChange(checked)`.
|
|
573
205
|
|
|
574
|
-
|
|
575
|
-
label
|
|
576
|
-
options={radioOptions}
|
|
577
|
-
value={selectedValue}
|
|
578
|
-
onChange={(value) => setSelectedValue(value)}
|
|
579
|
-
/>
|
|
206
|
+
- CustomTabs
|
|
207
|
+
- Props: `tabs: {label,value}[]`, `value`, `onChange(event,value)`, plus `TabsProps`.
|
|
580
208
|
|
|
581
|
-
|
|
209
|
+
- CustomDatePicker
|
|
210
|
+
- Props: `label`, `value: Dayjs|null`, `onChange(Dayjs|null|undefined)`; clearable.
|
|
582
211
|
|
|
583
|
-
|
|
212
|
+
- ControlledDatePicker
|
|
213
|
+
- Props: `label?`, `initialValue?: string`, `onChange?(Dayjs|null)`; manages its own state.
|
|
584
214
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
{ label: "Inbox", icon: <InboxIcon /> },
|
|
588
|
-
{ label: "Drafts", icon: <DraftsIcon /> },
|
|
589
|
-
{ label: "Trash" }, // No icon
|
|
590
|
-
{ label: "Spam" }, // No icon
|
|
591
|
-
];
|
|
215
|
+
- CustomColorPicker
|
|
216
|
+
- Props: `initialColor?`, `onChange(color)`; uses `react-color` `SketchPicker`.
|
|
592
217
|
|
|
593
|
-
|
|
594
|
-
|
|
218
|
+
- CustomScrollbar
|
|
219
|
+
- Props: `children`, `maxWidth?`, `maxHeight?`; wraps `simplebar-react`.
|
|
595
220
|
|
|
596
|
-
|
|
221
|
+
- AsyncAutocomplete<T>
|
|
222
|
+
- Props: `label`, `multiple?`, `fetchOptions()`, `getOptionLabel`, `isOptionEqualToValue`, `onChange?`.
|
|
223
|
+
- Fetches options on open; shows loading indicator.
|
|
224
|
+
- Example:
|
|
225
|
+
```tsx
|
|
226
|
+
<AsyncAutocomplete
|
|
227
|
+
label="Users"
|
|
228
|
+
fetchOptions={async () => [{ id:1, name:"Alice" }]}
|
|
229
|
+
getOptionLabel={(o) => o.name}
|
|
230
|
+
isOptionEqualToValue={(o,v) => o.id === v.id}
|
|
231
|
+
onChange={(sel) => console.log(sel)}
|
|
232
|
+
/>
|
|
233
|
+
```
|
|
597
234
|
|
|
598
|
-
|
|
235
|
+
- CheckboxListWithAvatar
|
|
236
|
+
- Props: `items: { id:number; label:string; avatarSrc? }[]`, `checkedItems?`, `onChange(checkedIds)`.
|
|
599
237
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
//Each Array represents as a row
|
|
603
|
-
[
|
|
604
|
-
//each object represents a coumn data
|
|
605
|
-
{
|
|
606
|
-
id: 0,
|
|
607
|
-
fieldName: "Title",
|
|
608
|
-
fieldType: "text",
|
|
609
|
-
fieldContent: "ajgobi",
|
|
610
|
-
isDefault: true,
|
|
611
|
-
isVisible: true,
|
|
612
|
-
},
|
|
613
|
-
{
|
|
614
|
-
id: 1,
|
|
615
|
-
fieldName: "B",
|
|
616
|
-
fieldType: "link",
|
|
617
|
-
fieldContent: "Click me",
|
|
618
|
-
isDefault: true,
|
|
619
|
-
isVisible: true,
|
|
620
|
-
},
|
|
621
|
-
{
|
|
622
|
-
id: 2,
|
|
623
|
-
fieldName: "Date",
|
|
624
|
-
fieldType: "date",
|
|
625
|
-
fieldContent: "Feb 20, 2025",
|
|
626
|
-
isDefault: false,
|
|
627
|
-
isVisible: false,
|
|
628
|
-
},
|
|
629
|
-
{
|
|
630
|
-
id: 3,
|
|
631
|
-
fieldName: "D",
|
|
632
|
-
fieldType: "link",
|
|
633
|
-
fieldContent: "Click me",
|
|
634
|
-
isDefault: false,
|
|
635
|
-
isVisible: false,
|
|
636
|
-
},
|
|
637
|
-
{
|
|
638
|
-
id: 4,
|
|
639
|
-
fieldName: "Actions",
|
|
640
|
-
fieldType: "actions",
|
|
641
|
-
fieldContent: [
|
|
642
|
-
{
|
|
643
|
-
label: "Edit",
|
|
644
|
-
onClick: (data) => {
|
|
645
|
-
console.log(data);
|
|
646
|
-
},
|
|
647
|
-
icon: <EditLocationAlt />,
|
|
648
|
-
variant: "contained",
|
|
649
|
-
},
|
|
650
|
-
{
|
|
651
|
-
label: "Delete",
|
|
652
|
-
onClick: (data) => {
|
|
653
|
-
console.log(data);
|
|
654
|
-
},
|
|
655
|
-
icon: <DeleteOutlineSharp />,
|
|
656
|
-
variant: "outlined",
|
|
657
|
-
},
|
|
658
|
-
],
|
|
659
|
-
isDefault: false,
|
|
660
|
-
isVisible: false,
|
|
661
|
-
},
|
|
662
|
-
{
|
|
663
|
-
id: 5,
|
|
664
|
-
fieldName: "Actions",
|
|
665
|
-
fieldType: "actions",
|
|
666
|
-
fieldContent: [
|
|
667
|
-
{
|
|
668
|
-
label: "Update",
|
|
669
|
-
onClick: (data) => {
|
|
670
|
-
console.log(data);
|
|
671
|
-
},
|
|
672
|
-
icon: <EditLocationAlt />,
|
|
673
|
-
variant: "contained",
|
|
674
|
-
},
|
|
675
|
-
],
|
|
676
|
-
|
|
677
|
-
isDefault: false,
|
|
678
|
-
isVisible: false,
|
|
679
|
-
},
|
|
680
|
-
{
|
|
681
|
-
id: 6,
|
|
682
|
-
fieldName: "G",
|
|
683
|
-
fieldType: "link",
|
|
684
|
-
fieldContent: "Click me",
|
|
685
|
-
isDefault: false,
|
|
686
|
-
isVisible: false,
|
|
687
|
-
},
|
|
688
|
-
{
|
|
689
|
-
id: 7,
|
|
690
|
-
fieldName: "H",
|
|
691
|
-
fieldType: "link",
|
|
692
|
-
fieldContent:
|
|
693
|
-
"Click me fjaf afjasfjasfj aksfjdajfajsfkjakfjaksfjaksfjkasfjaskfjk sdgs sgfsdg asfjaskfjaskjfaksjfaksfjaskfjaksjfkasfjaskfjaksdfjaskfjaskfjaskfjasfjaskfjaskfjaksjfaskjfkasfjj",
|
|
694
|
-
isDefault: false,
|
|
695
|
-
isVisible: false,
|
|
696
|
-
},
|
|
697
|
-
{
|
|
698
|
-
id: 8,
|
|
699
|
-
fieldName: "Date Picker",
|
|
700
|
-
fieldType: "date-picker",
|
|
701
|
-
fieldContent: {
|
|
702
|
-
date: "12-12-2022",
|
|
703
|
-
onChange: (parentItem: FieldItem, timeValue: Date) => {
|
|
704
|
-
console.log(parentItem, timeValue);
|
|
705
|
-
},
|
|
706
|
-
},
|
|
707
|
-
isDefault: false,
|
|
708
|
-
isVisible: false,
|
|
709
|
-
},
|
|
710
|
-
{
|
|
711
|
-
id: 9,
|
|
712
|
-
fieldName: "J",
|
|
713
|
-
fieldType: "link",
|
|
714
|
-
fieldContent: "Click me",
|
|
715
|
-
isDefault: false,
|
|
716
|
-
isVisible: false,
|
|
717
|
-
},
|
|
718
|
-
],
|
|
719
|
-
[
|
|
720
|
-
{
|
|
721
|
-
id: 0,
|
|
722
|
-
fieldName: "A",
|
|
723
|
-
fieldType: "link",
|
|
724
|
-
fieldContent: "hello",
|
|
725
|
-
isDefault: true,
|
|
726
|
-
isVisible: true,
|
|
727
|
-
},
|
|
728
|
-
{
|
|
729
|
-
id: 1,
|
|
730
|
-
fieldName: "B",
|
|
731
|
-
fieldType: "link",
|
|
732
|
-
fieldContent: "Click me",
|
|
733
|
-
isDefault: true,
|
|
734
|
-
isVisible: true,
|
|
735
|
-
},
|
|
736
|
-
{
|
|
737
|
-
id: 2,
|
|
738
|
-
fieldName: "C",
|
|
739
|
-
fieldType: "link",
|
|
740
|
-
fieldContent: "Click me",
|
|
741
|
-
isDefault: false,
|
|
742
|
-
isVisible: false,
|
|
743
|
-
},
|
|
744
|
-
{
|
|
745
|
-
id: 3,
|
|
746
|
-
fieldName: "D",
|
|
747
|
-
fieldType: "link",
|
|
748
|
-
fieldContent: "Click me",
|
|
749
|
-
isDefault: false,
|
|
750
|
-
isVisible: false,
|
|
751
|
-
},
|
|
752
|
-
{
|
|
753
|
-
id: 4,
|
|
754
|
-
fieldName: "E",
|
|
755
|
-
fieldType: "link",
|
|
756
|
-
fieldContent: "Click me",
|
|
757
|
-
isDefault: false,
|
|
758
|
-
isVisible: false,
|
|
759
|
-
},
|
|
760
|
-
{
|
|
761
|
-
id: 5,
|
|
762
|
-
fieldName: "F",
|
|
763
|
-
fieldType: "link",
|
|
764
|
-
fieldContent: "Click me",
|
|
765
|
-
isDefault: false,
|
|
766
|
-
isVisible: false,
|
|
767
|
-
},
|
|
768
|
-
{
|
|
769
|
-
id: 6,
|
|
770
|
-
fieldName: "G",
|
|
771
|
-
fieldType: "link",
|
|
772
|
-
fieldContent: "Click me",
|
|
773
|
-
isDefault: false,
|
|
774
|
-
isVisible: false,
|
|
775
|
-
},
|
|
776
|
-
{
|
|
777
|
-
id: 7,
|
|
778
|
-
fieldName: "H",
|
|
779
|
-
fieldType: "link",
|
|
780
|
-
fieldContent:
|
|
781
|
-
"Click me fjaf afjasfjasfj aksfjdajfajsfkjakfjaksfjaksfjkasfjaskfjk sdgs sgfsdg asfjaskfjaskjfaksjfaksfjaskfjaksjfkasfjaskfjaksdfjaskfjaskfjaskfjasfjaskfjaskfjaksjfaskjfkasfjj",
|
|
782
|
-
isDefault: false,
|
|
783
|
-
isVisible: false,
|
|
784
|
-
},
|
|
785
|
-
{
|
|
786
|
-
id: 8,
|
|
787
|
-
fieldName: "I",
|
|
788
|
-
fieldType: "link",
|
|
789
|
-
fieldContent: "Click me",
|
|
790
|
-
isDefault: false,
|
|
791
|
-
isVisible: false,
|
|
792
|
-
},
|
|
793
|
-
{
|
|
794
|
-
id: 9,
|
|
795
|
-
fieldName: "J",
|
|
796
|
-
fieldType: "link",
|
|
797
|
-
fieldContent: "Click me",
|
|
798
|
-
isDefault: false,
|
|
799
|
-
isVisible: false,
|
|
800
|
-
},
|
|
801
|
-
],
|
|
802
|
-
[
|
|
803
|
-
{
|
|
804
|
-
id: 0,
|
|
805
|
-
fieldName: "A",
|
|
806
|
-
fieldType: "link",
|
|
807
|
-
fieldContent: "gkkk",
|
|
808
|
-
isDefault: true,
|
|
809
|
-
isVisible: true,
|
|
810
|
-
to: "/habi-jabi",
|
|
811
|
-
},
|
|
812
|
-
{
|
|
813
|
-
id: 1,
|
|
814
|
-
fieldName: "B",
|
|
815
|
-
fieldType: "label",
|
|
816
|
-
fieldContent: "jam me",
|
|
817
|
-
isDefault: true,
|
|
818
|
-
isVisible: true,
|
|
819
|
-
},
|
|
820
|
-
{
|
|
821
|
-
id: 2,
|
|
822
|
-
fieldName: "C",
|
|
823
|
-
fieldType: "link",
|
|
824
|
-
fieldContent: "Click me",
|
|
825
|
-
isDefault: false,
|
|
826
|
-
isVisible: false,
|
|
827
|
-
},
|
|
828
|
-
{
|
|
829
|
-
id: 3,
|
|
830
|
-
fieldName: "D",
|
|
831
|
-
fieldType: "link",
|
|
832
|
-
fieldContent: "Click me",
|
|
833
|
-
isDefault: false,
|
|
834
|
-
isVisible: false,
|
|
835
|
-
},
|
|
836
|
-
{
|
|
837
|
-
id: 4,
|
|
838
|
-
fieldName: "E",
|
|
839
|
-
fieldType: "link",
|
|
840
|
-
fieldContent: "Click me",
|
|
841
|
-
isDefault: false,
|
|
842
|
-
isVisible: false,
|
|
843
|
-
},
|
|
844
|
-
{
|
|
845
|
-
id: 5,
|
|
846
|
-
fieldName: "F",
|
|
847
|
-
fieldType: "link",
|
|
848
|
-
fieldContent: "Click me",
|
|
849
|
-
isDefault: false,
|
|
850
|
-
isVisible: false,
|
|
851
|
-
},
|
|
852
|
-
{
|
|
853
|
-
id: 6,
|
|
854
|
-
fieldName: "G",
|
|
855
|
-
fieldType: "link",
|
|
856
|
-
fieldContent: "Click me",
|
|
857
|
-
isDefault: false,
|
|
858
|
-
isVisible: false,
|
|
859
|
-
},
|
|
860
|
-
{
|
|
861
|
-
id: 7,
|
|
862
|
-
fieldName: "H",
|
|
863
|
-
fieldType: "link",
|
|
864
|
-
fieldContent:
|
|
865
|
-
"Click me fjaf afjasfjasfj aksfjdajfajsfkjakfjaksfjaksfjkasfjaskfjk sdgs sgfsdg asfjaskfjaskjfaksjfaksfjaskfjaksjfkasfjaskfjaksdfjaskfjaskfjaskfjasfjaskfjaskfjaksjfaskjfkasfjj",
|
|
866
|
-
isDefault: false,
|
|
867
|
-
isVisible: false,
|
|
868
|
-
},
|
|
869
|
-
{
|
|
870
|
-
id: 8,
|
|
871
|
-
fieldName: "I",
|
|
872
|
-
fieldType: "link",
|
|
873
|
-
fieldContent: "Click me",
|
|
874
|
-
isDefault: false,
|
|
875
|
-
isVisible: false,
|
|
876
|
-
},
|
|
877
|
-
{
|
|
878
|
-
id: 9,
|
|
879
|
-
fieldName: "J",
|
|
880
|
-
fieldType: "link",
|
|
881
|
-
fieldContent: "Click me",
|
|
882
|
-
isDefault: false,
|
|
883
|
-
isVisible: false,
|
|
884
|
-
},
|
|
885
|
-
],
|
|
886
|
-
];
|
|
887
|
-
```
|
|
888
|
-
usage of the MultiDynamicTable
|
|
238
|
+
- CustomSelectableList
|
|
239
|
+
- Props: `items: { label:string; icon? }[]`, `onSelect?(index)`; supports selection and a divider after the second item.
|
|
889
240
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
import { dynamicTableData } from "./MultiDynamicTable/dynamicTableData";
|
|
241
|
+
- Loader
|
|
242
|
+
- Props: `size?`, `text?`, `fullScreen?`.
|
|
243
|
+
- Shows animated spinner with module logo; optionally full-screen overlay.
|
|
894
244
|
|
|
895
|
-
|
|
896
|
-
const [page, setPage] = useState(0);
|
|
897
|
-
const [rowsPerPage, setRowsPerPage] = useState(10);
|
|
245
|
+
## Exports
|
|
898
246
|
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
247
|
+
```ts
|
|
248
|
+
import {
|
|
249
|
+
ThemeSetting,
|
|
250
|
+
ThemeCustomization,
|
|
251
|
+
MainLayout,
|
|
252
|
+
MainlayoutWithWrapper,
|
|
253
|
+
RoutesConfigLayout,
|
|
254
|
+
MultiDynamicTable,
|
|
255
|
+
ProfilePages,
|
|
256
|
+
// UI primitives
|
|
257
|
+
AsyncAutocomplete,
|
|
258
|
+
CheckboxListWithAvatar,
|
|
259
|
+
ControlledDatePicker,
|
|
260
|
+
CustomButton,
|
|
261
|
+
CustomCheckbox,
|
|
262
|
+
CustomColorPicker,
|
|
263
|
+
CustomDatePicker,
|
|
264
|
+
CustomIconButton,
|
|
265
|
+
CustomInput,
|
|
266
|
+
CustomRadioGroup,
|
|
267
|
+
CustomScrollbar,
|
|
268
|
+
CustomSelect,
|
|
269
|
+
CustomSelectableList,
|
|
270
|
+
CustomSwitch,
|
|
271
|
+
CustomTabs,
|
|
272
|
+
CustomTextField,
|
|
273
|
+
Loader,
|
|
274
|
+
} from "@crystaltech/hsms-shared-ui";
|
|
924
275
|
```
|
|
925
|
-
### 13. `CustomTabs`
|
|
926
|
-
|
|
927
|
-
### 14. `CustomTabs`
|
|
928
|
-
|
|
929
|
-
### 15. `CustomTabs`
|
|
930
|
-
|
|
931
|
-
### 16. `CustomTabs`
|
|
932
|
-
|
|
933
|
-
### 17. `CustomTabs`
|
|
934
|
-
|
|
935
|
-
### 18. `CustomTabs`
|
|
936
|
-
|
|
937
|
-
## License
|
|
938
|
-
|
|
939
|
-
This package is licensed under the **MIT License**.
|
|
940
|
-
|
|
941
|
-
---
|
|
942
276
|
|
|
943
|
-
##
|
|
277
|
+
## Notes
|
|
944
278
|
|
|
945
|
-
|
|
946
|
-
|
|
279
|
+
- `RoutesConfigLayout` uses `ProtectedRoute` to enforce `roles` and `clients`; route-level `roles` override global ones.
|
|
280
|
+
- Column visibility toggling in `MultiDynamicTable` relies on shared `id` values across rows for a given column.
|
|
281
|
+
- Ensure peer versions of MUI and React match your app.
|
|
282
|
+
- Refer to `src/theme/defaultTheme.ts` for the complete `ITheme` shape.
|