@campxdev/react-blueprint 3.0.0-alpha.1 → 3.0.0-alpha.3
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/cjs/index.js +1 -1
- package/dist/cjs/types/src/components/DataDisplay/DataTable/DataTable.d.ts +3 -0
- package/dist/cjs/types/src/components/DataDisplay/DataTable/components/TableView.d.ts +3 -0
- package/dist/cjs/types/src/components/Input/DatePicker/DatePicker.d.ts +2 -2
- package/dist/cjs/types/src/components/Input/DateTimePicker/DateTimePicker.d.ts +2 -2
- package/dist/cjs/types/src/components/Input/PasswordField/PasswordField.d.ts +2 -1
- package/dist/cjs/types/src/components/Input/TextField/TextField.d.ts +2 -1
- package/dist/cjs/types/src/components/Input/Textarea/Textarea.d.ts +12 -0
- package/dist/cjs/types/src/components/Input/export.d.ts +1 -0
- package/dist/cjs/types/src/components/Layout/AppLayout/index.d.ts +1 -0
- package/dist/cjs/types/src/components/Layout/PageHeader/PageHeader.d.ts +3 -2
- package/dist/cjs/types/src/components/Navigation/Breadcrumbs/Breadcrumbs.d.ts +3 -1
- package/dist/cjs/types/src/shadcn-components/Input/Textarea/Textarea.d.ts +3 -0
- package/dist/esm/index.js +2 -2
- package/dist/esm/types/src/components/DataDisplay/DataTable/DataTable.d.ts +3 -0
- package/dist/esm/types/src/components/DataDisplay/DataTable/components/TableView.d.ts +3 -0
- package/dist/esm/types/src/components/Input/DatePicker/DatePicker.d.ts +2 -2
- package/dist/esm/types/src/components/Input/DateTimePicker/DateTimePicker.d.ts +2 -2
- package/dist/esm/types/src/components/Input/PasswordField/PasswordField.d.ts +2 -1
- package/dist/esm/types/src/components/Input/TextField/TextField.d.ts +2 -1
- package/dist/esm/types/src/components/Input/Textarea/Textarea.d.ts +12 -0
- package/dist/esm/types/src/components/Input/export.d.ts +1 -0
- package/dist/esm/types/src/components/Layout/AppLayout/index.d.ts +1 -0
- package/dist/esm/types/src/components/Layout/PageHeader/PageHeader.d.ts +3 -2
- package/dist/esm/types/src/components/Navigation/Breadcrumbs/Breadcrumbs.d.ts +3 -1
- package/dist/esm/types/src/shadcn-components/Input/Textarea/Textarea.d.ts +3 -0
- package/dist/index.d.ts +88 -21
- package/dist/styles.css +11 -14
- package/package.json +1 -1
- package/src/components/DataDisplay/DataTable/DataTable.tsx +5 -0
- package/src/components/DataDisplay/DataTable/components/TableHeaders/TableActionHeader.tsx +1 -1
- package/src/components/DataDisplay/DataTable/components/TableView.tsx +13 -8
- package/src/components/DataDisplay/DataTable/components/ViewList/ViewButton.tsx +1 -1
- package/src/components/Feedback/Snackbar/Snackbar.tsx +5 -8
- package/src/components/Input/DatePicker/DatePicker.tsx +38 -21
- package/src/components/Input/DateTimePicker/DateTimePicker.tsx +44 -27
- package/src/components/Input/MultiSelect/components/MultiSelectInput.tsx +1 -1
- package/src/components/Input/PasswordField/PasswordField.tsx +5 -2
- package/src/components/Input/SingleSelect/components/SingleInput.tsx +1 -1
- package/src/components/Input/TextField/TextField.tsx +7 -0
- package/src/components/Input/Textarea/Textarea.tsx +67 -0
- package/src/components/Input/export.ts +1 -0
- package/src/components/Layout/AppLayout/components/Sidebar/MenuBar.tsx +1 -0
- package/src/components/Layout/AppLayout/components/Sidebar/MenuItem.tsx +2 -2
- package/src/components/Layout/AppLayout/components/Sidebar/StyledComponents.tsx +1 -1
- package/src/components/Layout/AppLayout/index.ts +3 -0
- package/src/components/Layout/PageHeader/PageHeader.tsx +8 -6
- package/src/components/Navigation/Breadcrumbs/Breadcrumbs.tsx +17 -6
- package/src/components/Navigation/TabsContainer/TabsContainer.tsx +1 -1
- package/src/hooks/usePageHeader.ts +1 -1
- package/src/shadcn-components/DataDisplay/Typography/Typography.tsx +1 -1
- package/src/shadcn-components/Input/Textarea/Textarea.tsx +18 -0
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from '@/shadcn-components/Input/Popover/Popover';
|
|
8
8
|
import { format as DateFnsFormat } from 'date-fns';
|
|
9
9
|
import { CalendarDays } from 'lucide-react';
|
|
10
|
-
import
|
|
10
|
+
import { cloneElement, useEffect, useRef, useState } from 'react';
|
|
11
11
|
import { Typography } from '../../DataDisplay/Typography/Typography';
|
|
12
12
|
import { Button } from '../Button/Button';
|
|
13
13
|
import { LabelWrapper } from '../LabelWrapper/LabelWrapper';
|
|
@@ -39,6 +39,7 @@ export type DatePickerProps = {
|
|
|
39
39
|
onOpen?: () => void;
|
|
40
40
|
onClose?: () => void;
|
|
41
41
|
onBlur?: React.FocusEventHandler;
|
|
42
|
+
fullWidth?: boolean;
|
|
42
43
|
[key: string]: any;
|
|
43
44
|
};
|
|
44
45
|
|
|
@@ -66,10 +67,12 @@ export const DatePicker = ({
|
|
|
66
67
|
className,
|
|
67
68
|
onOpen,
|
|
68
69
|
onClose,
|
|
70
|
+
fullWidth,
|
|
69
71
|
...rest
|
|
70
72
|
}: DatePickerProps) => {
|
|
71
|
-
const [open, setOpen] =
|
|
72
|
-
const [date, setDate] =
|
|
73
|
+
const [open, setOpen] = useState(false);
|
|
74
|
+
const [date, setDate] = useState<Date | undefined>(value);
|
|
75
|
+
const wrapperRef = useRef<HTMLSpanElement>(null);
|
|
73
76
|
|
|
74
77
|
const formatDateString = (date: Date | undefined) => {
|
|
75
78
|
if (!date) return '';
|
|
@@ -90,32 +93,46 @@ export const DatePicker = ({
|
|
|
90
93
|
}
|
|
91
94
|
};
|
|
92
95
|
|
|
96
|
+
const [, forceUpdate] = useState({});
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
forceUpdate({});
|
|
99
|
+
}, []);
|
|
100
|
+
|
|
93
101
|
return (
|
|
94
102
|
<LabelWrapper
|
|
95
103
|
label={label}
|
|
96
104
|
required={required}
|
|
97
105
|
name={name}
|
|
98
|
-
containerProps={
|
|
106
|
+
containerProps={{
|
|
107
|
+
...(fullWidth && { style: { width: '100%' } }),
|
|
108
|
+
...containerProps,
|
|
109
|
+
}}
|
|
99
110
|
>
|
|
100
111
|
<Popover open={open} onOpenChange={handleOpenChange}>
|
|
101
112
|
<PopoverTrigger asChild>
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
'w-full justify-between text-left font-normal',
|
|
106
|
-
// !date && 'text-muted-foreground',
|
|
107
|
-
error && 'border-destructive',
|
|
108
|
-
className,
|
|
109
|
-
)}
|
|
110
|
-
disabled={disabled}
|
|
111
|
-
{...rest}
|
|
113
|
+
<span
|
|
114
|
+
ref={wrapperRef}
|
|
115
|
+
style={{ display: fullWidth ? 'block' : 'inline-block' }}
|
|
112
116
|
>
|
|
113
|
-
<
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
<Button
|
|
118
|
+
variant="input"
|
|
119
|
+
className={cn(
|
|
120
|
+
'justify-between text-left font-normal',
|
|
121
|
+
!date && 'text-muted-foreground',
|
|
122
|
+
error && 'border-destructive',
|
|
123
|
+
fullWidth ? 'w-full' : 'w-auto',
|
|
124
|
+
className,
|
|
125
|
+
)}
|
|
126
|
+
disabled={disabled}
|
|
127
|
+
{...rest}
|
|
128
|
+
>
|
|
129
|
+
<span>{date ? formatDateString(date) : placeholder}</span>
|
|
130
|
+
{Icon &&
|
|
131
|
+
cloneElement(Icon as React.ReactElement, {
|
|
132
|
+
className: 'ml-2 h-4 w-4',
|
|
133
|
+
})}
|
|
134
|
+
</Button>
|
|
135
|
+
</span>
|
|
119
136
|
</PopoverTrigger>
|
|
120
137
|
<PopoverContent className="w-auto p-0" align="start">
|
|
121
138
|
<div className="flex flex-col">
|
|
@@ -199,7 +216,7 @@ export const DatePicker = ({
|
|
|
199
216
|
variant="small"
|
|
200
217
|
className={cn('ml-1 mt-1', error && 'text-destructive')}
|
|
201
218
|
>
|
|
202
|
-
{error || helperText}
|
|
219
|
+
{typeof error === 'string' ? error : error?.message || helperText}
|
|
203
220
|
</Typography>
|
|
204
221
|
)}
|
|
205
222
|
</LabelWrapper>
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from '@/shadcn-components/Input/Popover/Popover';
|
|
9
9
|
import { format as DateFnsFormat } from 'date-fns';
|
|
10
10
|
import { CalendarDays } from 'lucide-react';
|
|
11
|
-
import
|
|
11
|
+
import { cloneElement, useEffect, useMemo, useRef, useState } from 'react';
|
|
12
12
|
import { Typography } from '../../DataDisplay/Typography/Typography';
|
|
13
13
|
import { Button } from '../Button/Button';
|
|
14
14
|
import { LabelWrapper } from '../LabelWrapper/LabelWrapper';
|
|
@@ -68,6 +68,7 @@ export type DateTimePickerProps = {
|
|
|
68
68
|
onOpen?: () => void;
|
|
69
69
|
onClose?: () => void;
|
|
70
70
|
onBlur?: React.FocusEventHandler;
|
|
71
|
+
fullWidth?: boolean;
|
|
71
72
|
[key: string]: any;
|
|
72
73
|
};
|
|
73
74
|
|
|
@@ -96,14 +97,16 @@ export const DateTimePicker = ({
|
|
|
96
97
|
className,
|
|
97
98
|
onOpen,
|
|
98
99
|
onClose,
|
|
100
|
+
fullWidth,
|
|
99
101
|
...rest
|
|
100
102
|
}: DateTimePickerProps) => {
|
|
101
|
-
const [open, setOpen] =
|
|
102
|
-
const [dateTime, setDateTime] =
|
|
103
|
-
const [timeValue, setTimeValue] =
|
|
103
|
+
const [open, setOpen] = useState(false);
|
|
104
|
+
const [dateTime, setDateTime] = useState<Date | undefined>(value);
|
|
105
|
+
const [timeValue, setTimeValue] = useState<string>('');
|
|
106
|
+
const wrapperRef = useRef<HTMLSpanElement>(null);
|
|
104
107
|
|
|
105
108
|
// Determine view configurations based on views prop
|
|
106
|
-
const viewConfig =
|
|
109
|
+
const viewConfig = useMemo(() => {
|
|
107
110
|
const hasTime =
|
|
108
111
|
views.includes('hours') ||
|
|
109
112
|
views.includes('minutes') ||
|
|
@@ -120,7 +123,7 @@ export const DateTimePicker = ({
|
|
|
120
123
|
}, [views]);
|
|
121
124
|
|
|
122
125
|
// Update time value when dateTime changes
|
|
123
|
-
|
|
126
|
+
useEffect(() => {
|
|
124
127
|
if (dateTime && viewConfig.showTime) {
|
|
125
128
|
const hours = dateTime.getHours(),
|
|
126
129
|
minutes = dateTime.getMinutes(),
|
|
@@ -184,6 +187,11 @@ export const DateTimePicker = ({
|
|
|
184
187
|
}
|
|
185
188
|
};
|
|
186
189
|
|
|
190
|
+
const [, forceUpdate] = useState({});
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
forceUpdate({});
|
|
193
|
+
}, []);
|
|
194
|
+
|
|
187
195
|
// If only time selection (no date), render native time input directly
|
|
188
196
|
if (viewConfig.showTime && !viewConfig.showDate) {
|
|
189
197
|
return (
|
|
@@ -214,7 +222,7 @@ export const DateTimePicker = ({
|
|
|
214
222
|
variant="small"
|
|
215
223
|
className={cn('ml-1 mt-1', error && 'text-destructive')}
|
|
216
224
|
>
|
|
217
|
-
{error || helperText}
|
|
225
|
+
{typeof error === 'string' ? error : error?.message || helperText}
|
|
218
226
|
</Typography>
|
|
219
227
|
)}
|
|
220
228
|
</LabelWrapper>
|
|
@@ -226,29 +234,38 @@ export const DateTimePicker = ({
|
|
|
226
234
|
label={label}
|
|
227
235
|
required={required}
|
|
228
236
|
name={name}
|
|
229
|
-
containerProps={
|
|
237
|
+
containerProps={{
|
|
238
|
+
...(fullWidth && { style: { width: '100%' } }),
|
|
239
|
+
...containerProps,
|
|
240
|
+
}}
|
|
230
241
|
>
|
|
231
242
|
<Popover open={open} onOpenChange={handleOpenChange}>
|
|
232
243
|
<PopoverTrigger asChild>
|
|
233
|
-
<
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
'w-full justify-between text-left font-normal',
|
|
237
|
-
!dateTime && 'text-muted-foreground',
|
|
238
|
-
error && 'border-destructive',
|
|
239
|
-
className,
|
|
240
|
-
)}
|
|
241
|
-
disabled={disabled}
|
|
242
|
-
{...rest}
|
|
244
|
+
<span
|
|
245
|
+
ref={wrapperRef}
|
|
246
|
+
style={{ display: fullWidth ? 'block' : 'inline-block' }}
|
|
243
247
|
>
|
|
244
|
-
<
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
248
|
+
<Button
|
|
249
|
+
variant="input"
|
|
250
|
+
className={cn(
|
|
251
|
+
'w-full justify-between text-left font-normal',
|
|
252
|
+
!dateTime && 'text-muted-foreground',
|
|
253
|
+
error && 'border-destructive',
|
|
254
|
+
fullWidth ? 'w-full' : 'w-auto',
|
|
255
|
+
className,
|
|
256
|
+
)}
|
|
257
|
+
disabled={disabled}
|
|
258
|
+
{...rest}
|
|
259
|
+
>
|
|
260
|
+
<span>
|
|
261
|
+
{dateTime ? formatDateTimeString(dateTime) : placeholder}
|
|
262
|
+
</span>
|
|
263
|
+
{Icon &&
|
|
264
|
+
cloneElement(Icon as React.ReactElement, {
|
|
265
|
+
className: 'ml-2 h-4 w-4 ',
|
|
266
|
+
})}
|
|
267
|
+
</Button>
|
|
268
|
+
</span>
|
|
252
269
|
</PopoverTrigger>
|
|
253
270
|
<PopoverContent className="w-auto p-0" align="start">
|
|
254
271
|
<div className="flex flex-col">
|
|
@@ -334,7 +351,7 @@ export const DateTimePicker = ({
|
|
|
334
351
|
variant="small"
|
|
335
352
|
className={cn('ml-1 mt-1', error && 'text-destructive')}
|
|
336
353
|
>
|
|
337
|
-
{error || helperText}
|
|
354
|
+
{typeof error === 'string' ? error : error?.message || helperText}
|
|
338
355
|
</Typography>
|
|
339
356
|
)}
|
|
340
357
|
</LabelWrapper>
|
|
@@ -232,7 +232,7 @@ export const MultiSelectInput = ({
|
|
|
232
232
|
variant="muted"
|
|
233
233
|
className={cn('text-xs', error && 'text-destructive')}
|
|
234
234
|
>
|
|
235
|
-
{error || helperText}
|
|
235
|
+
{typeof error === 'string' ? error : error?.message || helperText}
|
|
236
236
|
</Typography>
|
|
237
237
|
)}
|
|
238
238
|
</LabelWrapper>
|
|
@@ -18,6 +18,7 @@ export type PasswordFieldProps = {
|
|
|
18
18
|
error?: boolean;
|
|
19
19
|
helperText?: string;
|
|
20
20
|
className?: string;
|
|
21
|
+
fullWidth?: boolean;
|
|
21
22
|
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'>;
|
|
22
23
|
|
|
23
24
|
export const PasswordField = ({
|
|
@@ -33,6 +34,7 @@ export const PasswordField = ({
|
|
|
33
34
|
error = false,
|
|
34
35
|
helperText,
|
|
35
36
|
className,
|
|
37
|
+
fullWidth,
|
|
36
38
|
...rest
|
|
37
39
|
}: PasswordFieldProps) => {
|
|
38
40
|
const [showPassword, setShowPassword] = useState(false);
|
|
@@ -42,8 +44,8 @@ export const PasswordField = ({
|
|
|
42
44
|
};
|
|
43
45
|
|
|
44
46
|
const content = (
|
|
45
|
-
<div className=
|
|
46
|
-
<div className=
|
|
47
|
+
<div className={cn('flex flex-col gap-2', fullWidth && 'w-full')}>
|
|
48
|
+
<div className={cn('relative', fullWidth && 'w-full')}>
|
|
47
49
|
<Input
|
|
48
50
|
id={name}
|
|
49
51
|
name={name}
|
|
@@ -53,6 +55,7 @@ export const PasswordField = ({
|
|
|
53
55
|
placeholder={placeholder}
|
|
54
56
|
disabled={disabled}
|
|
55
57
|
className={cn(
|
|
58
|
+
fullWidth && 'w-full',
|
|
56
59
|
'pr-10 bg-input-background border-none ',
|
|
57
60
|
error && 'border-destructive focus-visible:ring-destructive',
|
|
58
61
|
className,
|
|
@@ -198,7 +198,7 @@ export const SingleInput = ({
|
|
|
198
198
|
variant="muted"
|
|
199
199
|
className={cn('text-xs', error && 'text-destructive')}
|
|
200
200
|
>
|
|
201
|
-
{error || helperText}
|
|
201
|
+
{typeof error === 'string' ? error : error?.message || helperText}
|
|
202
202
|
</Typography>
|
|
203
203
|
)}
|
|
204
204
|
</LabelWrapper>
|
|
@@ -11,6 +11,7 @@ export type TextFieldProps = {
|
|
|
11
11
|
required?: boolean;
|
|
12
12
|
fullWidth?: boolean;
|
|
13
13
|
error?: boolean;
|
|
14
|
+
helperText?: React.ReactNode;
|
|
14
15
|
} & React.ComponentProps<typeof Input>;
|
|
15
16
|
|
|
16
17
|
export const TextField = ({
|
|
@@ -24,6 +25,7 @@ export const TextField = ({
|
|
|
24
25
|
fullWidth,
|
|
25
26
|
className,
|
|
26
27
|
error = false,
|
|
28
|
+
helperText,
|
|
27
29
|
...rest
|
|
28
30
|
}: TextFieldProps) => {
|
|
29
31
|
return (
|
|
@@ -50,6 +52,11 @@ export const TextField = ({
|
|
|
50
52
|
aria-invalid={error}
|
|
51
53
|
{...rest}
|
|
52
54
|
/>
|
|
55
|
+
{helperText && (
|
|
56
|
+
<Typography variant="muted" className={cn(error && 'text-destructive')}>
|
|
57
|
+
{helperText}
|
|
58
|
+
</Typography>
|
|
59
|
+
)}
|
|
53
60
|
{description && (
|
|
54
61
|
<Typography variant="muted" className={cn(error && 'text-destructive')}>
|
|
55
62
|
{description}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { cn } from '@/lib/utils';
|
|
2
|
+
import { Typography } from '@/shadcn-components/DataDisplay/Typography/Typography';
|
|
3
|
+
import { Textarea as ShadcnTextarea } from '@/shadcn-components/Input/Textarea/Textarea';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { LabelWrapper } from '../LabelWrapper/LabelWrapper';
|
|
6
|
+
|
|
7
|
+
export type TextareaProps = {
|
|
8
|
+
containerProps?: React.HTMLAttributes<HTMLDivElement>;
|
|
9
|
+
description?: string;
|
|
10
|
+
label?: React.ReactNode | string;
|
|
11
|
+
required?: boolean;
|
|
12
|
+
fullWidth?: boolean;
|
|
13
|
+
error?: boolean;
|
|
14
|
+
helperText?: React.ReactNode;
|
|
15
|
+
} & React.ComponentProps<typeof ShadcnTextarea>;
|
|
16
|
+
|
|
17
|
+
export const Textarea = ({
|
|
18
|
+
name,
|
|
19
|
+
label,
|
|
20
|
+
value,
|
|
21
|
+
onChange,
|
|
22
|
+
required = false,
|
|
23
|
+
containerProps,
|
|
24
|
+
description,
|
|
25
|
+
fullWidth,
|
|
26
|
+
className,
|
|
27
|
+
error = false,
|
|
28
|
+
helperText,
|
|
29
|
+
...rest
|
|
30
|
+
}: TextareaProps) => {
|
|
31
|
+
return (
|
|
32
|
+
<LabelWrapper
|
|
33
|
+
label={label}
|
|
34
|
+
required={required}
|
|
35
|
+
name={name}
|
|
36
|
+
containerProps={{
|
|
37
|
+
...(fullWidth && { style: { width: '100%' } }),
|
|
38
|
+
...containerProps,
|
|
39
|
+
}}
|
|
40
|
+
>
|
|
41
|
+
<ShadcnTextarea
|
|
42
|
+
id={name}
|
|
43
|
+
value={value}
|
|
44
|
+
name={name}
|
|
45
|
+
onChange={onChange}
|
|
46
|
+
className={cn(
|
|
47
|
+
fullWidth && 'w-full',
|
|
48
|
+
'bg-input-background border-none placeholder:text-muted-foreground',
|
|
49
|
+
className,
|
|
50
|
+
)}
|
|
51
|
+
required={required}
|
|
52
|
+
aria-invalid={error}
|
|
53
|
+
{...rest}
|
|
54
|
+
/>
|
|
55
|
+
{helperText && (
|
|
56
|
+
<Typography variant="muted" className={cn(error && 'text-destructive')}>
|
|
57
|
+
{helperText}
|
|
58
|
+
</Typography>
|
|
59
|
+
)}
|
|
60
|
+
{description && (
|
|
61
|
+
<Typography variant="muted" className={cn(error && 'text-destructive')}>
|
|
62
|
+
{description}
|
|
63
|
+
</Typography>
|
|
64
|
+
)}
|
|
65
|
+
</LabelWrapper>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
@@ -3,7 +3,7 @@ import { useState } from 'react';
|
|
|
3
3
|
import { useMatch, useResolvedPath } from 'react-router-dom';
|
|
4
4
|
|
|
5
5
|
import { cn } from '@/lib/utils';
|
|
6
|
-
import {
|
|
6
|
+
import { ChevronDown, ChevronUp, Undo2 } from 'lucide-react';
|
|
7
7
|
import { Typography } from '../../../../DataDisplay/Typography/Typography';
|
|
8
8
|
import { Tooltip } from '../../../../Feedback/Tooltip/Tooltip';
|
|
9
9
|
import { MenuItemProps } from './interfaces';
|
|
@@ -135,7 +135,7 @@ export const MenuItem: React.FC<MenuItemProps> = ({
|
|
|
135
135
|
<Typography variant={'small'}>{name}</Typography>
|
|
136
136
|
</div>
|
|
137
137
|
<div className="flex flex-col">
|
|
138
|
-
{expanded ? <
|
|
138
|
+
{expanded ? <ChevronDown /> : <ChevronUp />}
|
|
139
139
|
</div>
|
|
140
140
|
</div>
|
|
141
141
|
)}
|
|
@@ -10,3 +10,6 @@ export { MenuToggleButton } from './components/MenuToggleButton';
|
|
|
10
10
|
|
|
11
11
|
// Reusable popup components for AppLayout right section
|
|
12
12
|
export { UserProfilePopup } from './components/UserProfilePopup';
|
|
13
|
+
|
|
14
|
+
// Sidebar interfaces
|
|
15
|
+
export * from './components/Sidebar/interfaces';
|
|
@@ -17,7 +17,8 @@ import { PageHeaderSearchBar } from './components/SearchBar';
|
|
|
17
17
|
interface PageHeaderProps {
|
|
18
18
|
uniqueId?: string;
|
|
19
19
|
pathTrimCount: number;
|
|
20
|
-
|
|
20
|
+
breadcrumbAction?: ReactElement;
|
|
21
|
+
searchBarProps?: SearchBarProps;
|
|
21
22
|
filterButtonProps?: {
|
|
22
23
|
label?: string;
|
|
23
24
|
startIcon?: ReactNode;
|
|
@@ -35,7 +36,8 @@ interface PageHeaderProps {
|
|
|
35
36
|
const PageHeader = ({
|
|
36
37
|
uniqueId,
|
|
37
38
|
pathTrimCount,
|
|
38
|
-
|
|
39
|
+
breadcrumbAction,
|
|
40
|
+
searchBarProps,
|
|
39
41
|
filterButtonProps,
|
|
40
42
|
addButtonProps,
|
|
41
43
|
actionProps,
|
|
@@ -44,7 +46,7 @@ const PageHeader = ({
|
|
|
44
46
|
const dispatch = useDispatch();
|
|
45
47
|
|
|
46
48
|
const showActionBar =
|
|
47
|
-
|
|
49
|
+
searchBarProps || filterButtonProps || addButtonProps || actionProps;
|
|
48
50
|
|
|
49
51
|
const wrapProps = (element: ReactElement): ReactElement => {
|
|
50
52
|
const { name } = element.props;
|
|
@@ -66,19 +68,19 @@ const PageHeader = ({
|
|
|
66
68
|
|
|
67
69
|
return (
|
|
68
70
|
<div>
|
|
69
|
-
<Breadcrumbs pathTrimCount={pathTrimCount} />
|
|
71
|
+
<Breadcrumbs pathTrimCount={pathTrimCount} action={breadcrumbAction} />
|
|
70
72
|
<div
|
|
71
73
|
className={cn(
|
|
72
74
|
'flex flex-col md:flex-row gap-1 md:gap-2 items-center w-full px-1 pb-2 md:p-0 border-b',
|
|
73
75
|
showActionBar ? '' : 'hidden',
|
|
74
76
|
)}
|
|
75
77
|
>
|
|
76
|
-
{
|
|
78
|
+
{searchBarProps && (
|
|
77
79
|
<PageHeaderSearchBar
|
|
78
80
|
className="w-full"
|
|
79
81
|
containerClassName="w-full md:w-md"
|
|
80
82
|
uniqueId={uniqueId}
|
|
81
|
-
{...
|
|
83
|
+
{...searchBarProps}
|
|
82
84
|
/>
|
|
83
85
|
)}
|
|
84
86
|
<div className="flex gap-2 items-center w-full px-2 md:p-0">
|
|
@@ -18,17 +18,20 @@ import {
|
|
|
18
18
|
DropdownMenuItem,
|
|
19
19
|
DropdownMenuTrigger,
|
|
20
20
|
} from '@/shadcn-components/Navigation/DropdownMenu/DropdownMenu';
|
|
21
|
+
import { ReactElement } from 'react';
|
|
21
22
|
import { getBreadcrumbsCharacter } from '../../../utils/export';
|
|
22
23
|
import { MenuToggleButton } from '../../Layout/AppLayout/components/MenuToggleButton';
|
|
23
24
|
|
|
24
25
|
export type BreadcrumbsProps = {
|
|
25
26
|
pathTrimCount: number;
|
|
26
27
|
containerClassName?: string;
|
|
28
|
+
action?: ReactElement;
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
export const Breadcrumbs = ({
|
|
30
32
|
pathTrimCount,
|
|
31
33
|
containerClassName,
|
|
34
|
+
action,
|
|
32
35
|
}: BreadcrumbsProps) => {
|
|
33
36
|
const specialCharacter = getBreadcrumbsCharacter();
|
|
34
37
|
const currentPathArray = window.location.pathname.split('/');
|
|
@@ -87,7 +90,7 @@ export const Breadcrumbs = ({
|
|
|
87
90
|
<BreadcrumbItem>
|
|
88
91
|
<DropdownMenu>
|
|
89
92
|
<DropdownMenuTrigger className="flex items-center gap-1">
|
|
90
|
-
<BreadcrumbEllipsis />
|
|
93
|
+
<BreadcrumbEllipsis className="size-5" />
|
|
91
94
|
</DropdownMenuTrigger>
|
|
92
95
|
<DropdownMenuContent align="start">
|
|
93
96
|
{middleItems.map((item, index) => (
|
|
@@ -112,14 +115,22 @@ export const Breadcrumbs = ({
|
|
|
112
115
|
return (
|
|
113
116
|
<div
|
|
114
117
|
className={cn(
|
|
115
|
-
'flex w-full flex-row items-center px-2 pt-2 gap-1 md:px-3',
|
|
118
|
+
'flex w-full flex-row items-center justify-between px-2 pt-2 gap-1 md:px-3',
|
|
116
119
|
containerClassName,
|
|
117
120
|
)}
|
|
118
121
|
>
|
|
119
|
-
<
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
<div
|
|
123
|
+
className={cn(
|
|
124
|
+
'flex w-full flex-row items-center gap-1 md:px-3',
|
|
125
|
+
containerClassName,
|
|
126
|
+
)}
|
|
127
|
+
>
|
|
128
|
+
<MenuToggleButton />
|
|
129
|
+
<Breadcrumb>
|
|
130
|
+
<BreadcrumbList>{renderBreadcrumbs()}</BreadcrumbList>
|
|
131
|
+
</Breadcrumb>
|
|
132
|
+
</div>
|
|
133
|
+
{action}
|
|
123
134
|
</div>
|
|
124
135
|
);
|
|
125
136
|
};
|
|
@@ -55,7 +55,7 @@ export const TabsContainer = ({
|
|
|
55
55
|
<Tabs
|
|
56
56
|
value={currentTab}
|
|
57
57
|
onValueChange={handleTabsChange}
|
|
58
|
-
className={
|
|
58
|
+
className={className}
|
|
59
59
|
>
|
|
60
60
|
<TabsList className={cn(tabsListClassName)} style={{ margin: '4px 8px' }}>
|
|
61
61
|
{tabs.map(({ key, label, highlight, disabled, icon }) => (
|
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
PageHeaderSingleState,
|
|
7
7
|
resetStateForUniqueId,
|
|
8
8
|
setDefaultFiltersForUniqueId,
|
|
9
|
+
setLayoutModeForUniqueId,
|
|
9
10
|
setLimitForUniqueId,
|
|
10
11
|
setOffsetForUniqueId,
|
|
11
|
-
setLayoutModeForUniqueId,
|
|
12
12
|
} from '../redux/slices/pageHeaderSlice';
|
|
13
13
|
import { RootState } from '../redux/store';
|
|
14
14
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '@/lib/utils';
|
|
4
|
+
|
|
5
|
+
function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {
|
|
6
|
+
return (
|
|
7
|
+
<textarea
|
|
8
|
+
data-slot="textarea"
|
|
9
|
+
className={cn(
|
|
10
|
+
'border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
|
11
|
+
className,
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { Textarea };
|