SidePanel.tsx170 lines · main
1'use client'
2
3import { Dialog } from 'radix-ui'
4import React from 'react'
5
6import { Button } from '../../components/Button/Button'
7import { Tooltip, TooltipContent, TooltipTrigger } from '../../components/shadcn/ui/tooltip'
8import styleHandler from '../../lib/theme/styleHandler'
9
10export type SidePanelProps = RadixProps & CustomProps
11
12interface RadixProps
13 extends
14 Dialog.DialogProps,
15 Pick<
16 Dialog.DialogContentProps,
17 | 'onOpenAutoFocus'
18 | 'onCloseAutoFocus'
19 | 'onEscapeKeyDown'
20 | 'onPointerDownOutside'
21 | 'onInteractOutside'
22 > {}
23
24interface CustomProps {
25 id?: String | undefined
26 disabled?: boolean
27 className?: String
28 children?: React.ReactNode
29 header?: string | React.ReactNode
30 visible: boolean
31 size?: 'medium' | 'large' | 'xlarge' | 'xxlarge' | 'xxxlarge' | 'xxxxlarge'
32 loading?: boolean
33 align?: 'right' | 'left'
34 hideFooter?: boolean
35 customFooter?: React.ReactNode
36 onCancel?: () => void
37 cancelText?: String
38 onConfirm?: () => void
39 confirmText?: String
40 triggerElement?: React.ReactNode
41 tooltip?: string
42}
43
44const SidePanel = ({
45 id,
46 disabled,
47 className,
48 children,
49 header,
50 visible,
51 open,
52 size = 'medium',
53 loading,
54 align = 'right',
55 hideFooter = false,
56 customFooter = undefined,
57 onConfirm,
58 onCancel,
59 confirmText = 'Confirm',
60 cancelText = 'Cancel',
61 triggerElement,
62 defaultOpen,
63 tooltip,
64 ...props
65}: SidePanelProps) => {
66 const __styles = styleHandler('sidepanel')
67
68 const footerContent = customFooter ? (
69 customFooter
70 ) : (
71 <div className={__styles.footer}>
72 <div>
73 <Button disabled={loading} type="default" onClick={() => (onCancel ? onCancel() : null)}>
74 {cancelText}
75 </Button>
76 </div>
77 {!!onConfirm && (
78 <Tooltip>
79 <TooltipTrigger asChild>
80 <span className="inline-block">
81 <Button
82 htmlType="submit"
83 disabled={disabled || loading}
84 loading={loading}
85 onClick={onConfirm}
86 >
87 {confirmText}
88 </Button>
89 </span>
90 </TooltipTrigger>
91 {tooltip !== undefined && <TooltipContent side="bottom">{tooltip}</TooltipContent>}
92 </Tooltip>
93 )}
94 </div>
95 )
96
97 function handleOpenChange(open: boolean) {
98 if (visible !== undefined && !open) {
99 // controlled component behaviour
100 if (onCancel) onCancel()
101 } else {
102 // un-controlled component behaviour
103 // setOpen(open)
104 }
105 }
106
107 open = open || visible
108
109 const {
110 onOpenAutoFocus,
111 onCloseAutoFocus,
112 onEscapeKeyDown,
113 onPointerDownOutside,
114 onInteractOutside,
115 } = props
116
117 return (
118 <Dialog.Root open={open} onOpenChange={handleOpenChange} defaultOpen={defaultOpen}>
119 {triggerElement && <Dialog.Trigger asChild>{triggerElement}</Dialog.Trigger>}
120
121 <Dialog.Portal>
122 <Dialog.Overlay className={__styles.overlay} />
123 <Dialog.Content
124 className={[
125 __styles.base,
126 __styles.size[size],
127 __styles.align[align],
128 className && className,
129 ].join(' ')}
130 onOpenAutoFocus={onOpenAutoFocus}
131 onCloseAutoFocus={onCloseAutoFocus}
132 onEscapeKeyDown={onEscapeKeyDown}
133 onPointerDownOutside={onPointerDownOutside}
134 onInteractOutside={(event) => {
135 const isToast = (event.target as Element)?.closest('#toast')
136 if (isToast) event.preventDefault()
137 if (onInteractOutside) onInteractOutside(event)
138 }}
139 {...props}
140 >
141 {header && <header className={__styles.header}>{header}</header>}
142 <div className={__styles.contents}>{children}</div>
143 {!hideFooter && footerContent}
144 </Dialog.Content>
145 </Dialog.Portal>
146 </Dialog.Root>
147 )
148}
149
150export function Separator() {
151 let __styles = styleHandler('sidepanel')
152
153 return <div className={__styles.separator}></div>
154}
155
156export function Content({
157 children,
158 className,
159}: {
160 children: React.ReactNode
161 className?: string
162}) {
163 let __styles = styleHandler('sidepanel')
164
165 return <div className={[__styles.content, className].join(' ').trim()}>{children}</div>
166}
167
168SidePanel.Content = Content
169SidePanel.Separator = Separator
170export default SidePanel