@sikka/hawa 0.1.17 → 0.1.20
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 +2 -16
- package/dist/styles.css +78 -3
- package/es/elements/Button.d.ts +1 -1
- package/es/elements/HawaRadio.d.ts +1 -0
- package/es/elements/InterfaceSettings.d.ts +2 -0
- package/es/elements/Popover.d.ts +1 -0
- package/es/index.es.js +3 -3
- package/es/layout/AppLayout.d.ts +49 -0
- package/es/layout/Sidebar.d.ts +36 -0
- package/es/layout/Sidebar2.d.ts +20 -0
- package/es/layout/index.d.ts +2 -0
- package/lib/elements/Button.d.ts +1 -1
- package/lib/elements/HawaRadio.d.ts +1 -0
- package/lib/elements/InterfaceSettings.d.ts +2 -0
- package/lib/elements/Popover.d.ts +1 -0
- package/lib/index.js +3 -3
- package/lib/layout/AppLayout.d.ts +49 -0
- package/lib/layout/Sidebar.d.ts +36 -0
- package/lib/layout/Sidebar2.d.ts +20 -0
- package/lib/layout/index.d.ts +2 -0
- package/package.json +2 -1
- package/src/blocks/AuthForms/SignInBlock.tsx +0 -2
- package/src/blocks/AuthForms/SignInForm.tsx +1 -1
- package/src/elements/Button.tsx +1 -0
- package/src/elements/DropdownMenu.tsx +0 -2
- package/src/elements/HawaRadio.tsx +8 -2
- package/src/elements/InterfaceSettings.tsx +15 -2
- package/src/elements/Label.tsx +0 -2
- package/src/elements/Popover.tsx +28 -22
- package/src/elements/Select.tsx +0 -2
- package/src/elements/Tooltip.tsx +1 -106
- package/src/layout/AppLayout.tsx +445 -0
- package/src/layout/HawaAppLayoutSimplified.tsx +78 -98
- package/src/layout/Sidebar.tsx +224 -0
- package/src/layout/Sidebar2.tsx +77 -0
- package/src/layout/index.ts +3 -0
- package/src/styles.css +78 -3
- package/tailwind.config.js +20 -1
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react"
|
|
2
|
+
import clsx from "clsx"
|
|
3
|
+
import useDiscloser from "../hooks/useDiscloser"
|
|
4
|
+
import useBreakpoint from "../hooks/useBreakpoint"
|
|
5
|
+
import { Button, DropdownMenu, Tooltip } from "../elements"
|
|
6
|
+
import { SidebarGroup, SidebarRoot } from "./Sidebar"
|
|
7
|
+
|
|
8
|
+
type AppLayoutTypes = {
|
|
9
|
+
/** The pages of the side drawer */
|
|
10
|
+
drawerItems: Item[]
|
|
11
|
+
// The direction of the layout
|
|
12
|
+
direction?: "rtl" | "ltr"
|
|
13
|
+
// The title of the current selected page, make sure it's the same as the drawerItem slug
|
|
14
|
+
currentPage: string
|
|
15
|
+
pageTitle?: string
|
|
16
|
+
logoSymbol?: any
|
|
17
|
+
logoLink?: string
|
|
18
|
+
logoText?: any
|
|
19
|
+
children?: any
|
|
20
|
+
topBar?: boolean
|
|
21
|
+
username?: string
|
|
22
|
+
email?: string
|
|
23
|
+
drawerSize?: "sm" | "md" | "large"
|
|
24
|
+
profileMenuItems?: ProfileItem[]
|
|
25
|
+
onSettingsClick?: () => void
|
|
26
|
+
DrawerFooterActions?: any
|
|
27
|
+
texts?: {
|
|
28
|
+
expandSidebar?: string
|
|
29
|
+
collapseSidebar?: string
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
type Item = {
|
|
33
|
+
value: string
|
|
34
|
+
label: string
|
|
35
|
+
icon?: any
|
|
36
|
+
subitems?: SubItem[]
|
|
37
|
+
onClick?: () => void
|
|
38
|
+
}
|
|
39
|
+
type SubItem = {
|
|
40
|
+
value: string
|
|
41
|
+
label: string
|
|
42
|
+
icon?: any
|
|
43
|
+
onClick?: () => void
|
|
44
|
+
}
|
|
45
|
+
type ProfileSubItem = {
|
|
46
|
+
label: string
|
|
47
|
+
value: string
|
|
48
|
+
highlighted?: boolean
|
|
49
|
+
}
|
|
50
|
+
type ProfileItem = {
|
|
51
|
+
label: string
|
|
52
|
+
value: string
|
|
53
|
+
highlighted?: boolean
|
|
54
|
+
subitems?: ProfileSubItem[] // Note the use of the optional modifier
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const AppLayout: React.FunctionComponent<AppLayoutTypes> = ({
|
|
58
|
+
direction = "rtl",
|
|
59
|
+
drawerSize = "md",
|
|
60
|
+
onSettingsClick,
|
|
61
|
+
DrawerFooterActions,
|
|
62
|
+
...props
|
|
63
|
+
}) => {
|
|
64
|
+
const [openSideMenu, setOpenSideMenu] = useState(false)
|
|
65
|
+
const [openSubItem, setOpenSubitem] = useState("")
|
|
66
|
+
const { isOpen, onClose, onOpen } = useDiscloser(false)
|
|
67
|
+
const [collapsed, setCollapsed] = useState(false)
|
|
68
|
+
|
|
69
|
+
const [keepOpen, setKeepOpen] = useState(false)
|
|
70
|
+
const ref = useRef(null)
|
|
71
|
+
const isRTL = direction === "rtl"
|
|
72
|
+
let size
|
|
73
|
+
if (typeof window !== "undefined") {
|
|
74
|
+
size = useBreakpoint()
|
|
75
|
+
} else {
|
|
76
|
+
size = 1200
|
|
77
|
+
}
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
const handleClickOutside = (event) => {
|
|
80
|
+
if (ref.current && !ref.current.contains(event.target) && !keepOpen) {
|
|
81
|
+
setOpenSideMenu(false)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
document.addEventListener("click", handleClickOutside, true)
|
|
85
|
+
return () => {
|
|
86
|
+
document.removeEventListener("click", handleClickOutside, true)
|
|
87
|
+
}
|
|
88
|
+
}, [keepOpen])
|
|
89
|
+
|
|
90
|
+
let drawerDefaultStyle =
|
|
91
|
+
"fixed top-0 z-40 flex h-full flex-col justify-between overflow-x-clip bg-card transition-all"
|
|
92
|
+
//The width of the drawer when closed
|
|
93
|
+
let closeDrawerWidth = 56
|
|
94
|
+
//The width of the drawer when opened
|
|
95
|
+
let openDrawerWidth = 200
|
|
96
|
+
let drawerSizeStyle = {
|
|
97
|
+
opened: {
|
|
98
|
+
sm: "100",
|
|
99
|
+
md: openDrawerWidth,
|
|
100
|
+
lg: "250",
|
|
101
|
+
},
|
|
102
|
+
closed: {
|
|
103
|
+
sm: "56",
|
|
104
|
+
md: "56",
|
|
105
|
+
lg: "56",
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let drawerSizeCondition =
|
|
110
|
+
size > 600 ? drawerSizeStyle[keepOpen ? "opened" : "closed"][drawerSize] : 0
|
|
111
|
+
return (
|
|
112
|
+
<div className="fixed left-0">
|
|
113
|
+
{props.topBar && (
|
|
114
|
+
<div
|
|
115
|
+
className={clsx(
|
|
116
|
+
"fixed left-0 right-0 top-0 z-30 flex h-14 w-full items-center justify-between bg-primary-foreground p-2",
|
|
117
|
+
isRTL ? "flex-row-reverse" : "flex-row"
|
|
118
|
+
)}
|
|
119
|
+
>
|
|
120
|
+
{/* Nav Side Of Navbar */}
|
|
121
|
+
{size > 600 ? (
|
|
122
|
+
<div
|
|
123
|
+
className={clsx(
|
|
124
|
+
"dark:text-white",
|
|
125
|
+
isRTL
|
|
126
|
+
? [size > 600 ? "mr-14" : "mr-2", keepOpen ? "mr-40" : ""]
|
|
127
|
+
: [size > 600 ? "ml-14" : "ml-2", keepOpen ? "ml-40" : ""]
|
|
128
|
+
)}
|
|
129
|
+
style={
|
|
130
|
+
isRTL
|
|
131
|
+
? {
|
|
132
|
+
marginRight: `${
|
|
133
|
+
drawerSizeStyle[keepOpen ? "opened" : "closed"][
|
|
134
|
+
drawerSize
|
|
135
|
+
]
|
|
136
|
+
}px`,
|
|
137
|
+
}
|
|
138
|
+
: {
|
|
139
|
+
marginLeft: `${
|
|
140
|
+
drawerSizeStyle[keepOpen ? "opened" : "closed"][
|
|
141
|
+
drawerSize
|
|
142
|
+
]
|
|
143
|
+
}px`,
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
>
|
|
147
|
+
{props.pageTitle}
|
|
148
|
+
</div>
|
|
149
|
+
) : (
|
|
150
|
+
// Mobile Drawer Menu Button
|
|
151
|
+
<div
|
|
152
|
+
dir={direction}
|
|
153
|
+
className="flex items-center justify-center gap-0.5"
|
|
154
|
+
>
|
|
155
|
+
<div
|
|
156
|
+
onClick={() => setOpenSideMenu(true)}
|
|
157
|
+
className="z-40 mx-1 cursor-pointer rounded p-2 transition-all hover:bg-gray-100"
|
|
158
|
+
>
|
|
159
|
+
<svg
|
|
160
|
+
stroke="currentColor"
|
|
161
|
+
fill="currentColor"
|
|
162
|
+
stroke-width="0"
|
|
163
|
+
viewBox="0 0 20 20"
|
|
164
|
+
aria-hidden="true"
|
|
165
|
+
height="1.6em"
|
|
166
|
+
width="1.6em"
|
|
167
|
+
>
|
|
168
|
+
<path
|
|
169
|
+
fill-rule="evenodd"
|
|
170
|
+
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
|
|
171
|
+
clip-rule="evenodd"
|
|
172
|
+
></path>
|
|
173
|
+
</svg>
|
|
174
|
+
</div>
|
|
175
|
+
{/* Mobile Page Title */}
|
|
176
|
+
{props.pageTitle ? (
|
|
177
|
+
<div className="text-sm">{props.pageTitle}</div>
|
|
178
|
+
) : (
|
|
179
|
+
<div></div>
|
|
180
|
+
)}
|
|
181
|
+
</div>
|
|
182
|
+
)}
|
|
183
|
+
|
|
184
|
+
<div
|
|
185
|
+
className={clsx(
|
|
186
|
+
"flex gap-2 dark:text-white",
|
|
187
|
+
isRTL ? "flex-row-reverse" : "flex-row"
|
|
188
|
+
)}
|
|
189
|
+
>
|
|
190
|
+
{/* User Info */}
|
|
191
|
+
{size > 600 ? (
|
|
192
|
+
<div
|
|
193
|
+
className={isRTL ? "text-left text-xs" : "text-right text-xs"}
|
|
194
|
+
>
|
|
195
|
+
<div className="font-bold">{props.username}</div>{" "}
|
|
196
|
+
<div>{props.email}</div>
|
|
197
|
+
</div>
|
|
198
|
+
) : null}
|
|
199
|
+
{/* Profile Icon & Menu */}
|
|
200
|
+
<DropdownMenu
|
|
201
|
+
triggerClassname="mx-2"
|
|
202
|
+
align="end"
|
|
203
|
+
alignOffset={8}
|
|
204
|
+
side={"bottom"}
|
|
205
|
+
sideOffset={5}
|
|
206
|
+
direction={isRTL ? "rtl" : "ltr"}
|
|
207
|
+
trigger={
|
|
208
|
+
<div className="relative h-8 w-8 cursor-pointer overflow-clip rounded ring-1 ring-primary/30 dark:bg-gray-600">
|
|
209
|
+
<AvatarIcon />
|
|
210
|
+
</div>
|
|
211
|
+
}
|
|
212
|
+
items={props.profileMenuItems}
|
|
213
|
+
onItemSelect={(e) => console.log("selecting item ", e)}
|
|
214
|
+
/>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
)}
|
|
218
|
+
{/*
|
|
219
|
+
* ----------------------------------------------------------------------------------------------------
|
|
220
|
+
* Drawer Container
|
|
221
|
+
* ----------------------------------------------------------------------------------------------------
|
|
222
|
+
*/}
|
|
223
|
+
<div
|
|
224
|
+
className={clsx(
|
|
225
|
+
"fixed top-0 z-40 flex h-full flex-col justify-between overflow-x-clip transition-all",
|
|
226
|
+
isRTL ? "right-0" : "left-0"
|
|
227
|
+
)}
|
|
228
|
+
style={{
|
|
229
|
+
width:
|
|
230
|
+
size > 600
|
|
231
|
+
? openSideMenu
|
|
232
|
+
? `${drawerSizeStyle["opened"][drawerSize]}px`
|
|
233
|
+
: `${drawerSizeStyle["closed"][drawerSize]}px`
|
|
234
|
+
: openSideMenu
|
|
235
|
+
? `${drawerSizeStyle["opened"][drawerSize]}px`
|
|
236
|
+
: "0px",
|
|
237
|
+
}}
|
|
238
|
+
onMouseEnter={() => {
|
|
239
|
+
setOpenSideMenu(true)
|
|
240
|
+
setCollapsed((prev) => !prev)
|
|
241
|
+
}}
|
|
242
|
+
onMouseLeave={() => {
|
|
243
|
+
keepOpen ? setOpenSideMenu(true) : setOpenSideMenu(false)
|
|
244
|
+
setCollapsed((prev) => !prev)
|
|
245
|
+
}}
|
|
246
|
+
ref={ref}
|
|
247
|
+
>
|
|
248
|
+
{/*
|
|
249
|
+
* ----------------------------------------------------------------------------------------------------
|
|
250
|
+
* Drawer Header
|
|
251
|
+
* ----------------------------------------------------------------------------------------------------
|
|
252
|
+
*/}
|
|
253
|
+
<div
|
|
254
|
+
dir={direction}
|
|
255
|
+
className={clsx(
|
|
256
|
+
"fixed z-50 mb-2 flex h-14 w-full flex-row items-center justify-center bg-primary-foreground transition-all"
|
|
257
|
+
)}
|
|
258
|
+
style={{
|
|
259
|
+
width:
|
|
260
|
+
size > 600
|
|
261
|
+
? `${openSideMenu ? openDrawerWidth : 56}px`
|
|
262
|
+
: `${openSideMenu ? openDrawerWidth : 0}px`,
|
|
263
|
+
}}
|
|
264
|
+
>
|
|
265
|
+
{/*
|
|
266
|
+
* ----------------------------------------------------------------------------------------------------
|
|
267
|
+
* Full Logo
|
|
268
|
+
* ----------------------------------------------------------------------------------------------------
|
|
269
|
+
*/}
|
|
270
|
+
<img
|
|
271
|
+
className={clsx(
|
|
272
|
+
"h-9 opacity-0 transition-all",
|
|
273
|
+
// isRTL ? "right-2.5" : "left-2.5",
|
|
274
|
+
!openSideMenu ? "invisible opacity-0" : "visible opacity-100"
|
|
275
|
+
// size > 600 ? "" : "right-4"
|
|
276
|
+
)}
|
|
277
|
+
// className={clsx(
|
|
278
|
+
// "fixed top-2.5 h-9 transition-all",
|
|
279
|
+
// isRTL ? "right-2.5" : "left-2.5",
|
|
280
|
+
// !openSideMenu ? "invisible opacity-0" : "visible opacity-100"
|
|
281
|
+
// )}
|
|
282
|
+
src={props.logoLink}
|
|
283
|
+
/>
|
|
284
|
+
{/*
|
|
285
|
+
* ----------------------------------------------------------------------------------------------------
|
|
286
|
+
* Logo Symbol
|
|
287
|
+
* ----------------------------------------------------------------------------------------------------
|
|
288
|
+
*/}
|
|
289
|
+
{size > 600 ? (
|
|
290
|
+
<img
|
|
291
|
+
className={clsx(
|
|
292
|
+
"fixed top-2.5 h-9 transition-all",
|
|
293
|
+
isRTL ? "right-2.5" : "left-2.5",
|
|
294
|
+
openSideMenu ? "invisible opacity-0" : "visible opacity-100"
|
|
295
|
+
)}
|
|
296
|
+
src={props.logoSymbol}
|
|
297
|
+
/>
|
|
298
|
+
) : null}
|
|
299
|
+
</div>
|
|
300
|
+
{/*
|
|
301
|
+
* ----------------------------------------------------------------------------------------------------
|
|
302
|
+
* Drawer Content Container
|
|
303
|
+
* ----------------------------------------------------------------------------------------------------
|
|
304
|
+
*/}
|
|
305
|
+
<div
|
|
306
|
+
className={clsx(
|
|
307
|
+
// "no-scrollbar", TODO: make this optional to hide scrollbar or not
|
|
308
|
+
"fixed bottom-14 top-14 bg-primary-foreground p-2 py-2 transition-all",
|
|
309
|
+
// bg-yellow-400
|
|
310
|
+
openSideMenu ? "overflow-auto" : "overflow-hidden"
|
|
311
|
+
)}
|
|
312
|
+
style={{
|
|
313
|
+
height: "calc(100% - 112px)",
|
|
314
|
+
width:
|
|
315
|
+
size > 600
|
|
316
|
+
? `${openSideMenu ? openDrawerWidth : 56}px`
|
|
317
|
+
: `${openSideMenu ? openDrawerWidth : 0}px`,
|
|
318
|
+
}}
|
|
319
|
+
>
|
|
320
|
+
{/*
|
|
321
|
+
* ----------------------------------------------------------------------------------------------------
|
|
322
|
+
* Drawer Items
|
|
323
|
+
* ----------------------------------------------------------------------------------------------------
|
|
324
|
+
*/}
|
|
325
|
+
|
|
326
|
+
<SidebarRoot>
|
|
327
|
+
<SidebarGroup
|
|
328
|
+
isOpen={openSideMenu}
|
|
329
|
+
onItemClick={(values) => {
|
|
330
|
+
console.log("Clicked main item value:", values[0])
|
|
331
|
+
// setSelectedItem(values)
|
|
332
|
+
}}
|
|
333
|
+
onSubItemClick={(values) => {
|
|
334
|
+
console.log("Parent item value:", values[0])
|
|
335
|
+
console.log("Subitem value:", values[1])
|
|
336
|
+
// setSelectedItem(values)
|
|
337
|
+
}}
|
|
338
|
+
items={props.drawerItems}
|
|
339
|
+
/>
|
|
340
|
+
</SidebarRoot>
|
|
341
|
+
</div>
|
|
342
|
+
{/*
|
|
343
|
+
* ----------------------------------------------------------------------------------------------------
|
|
344
|
+
* Drawer Footer
|
|
345
|
+
* ----------------------------------------------------------------------------------------------------
|
|
346
|
+
*/}
|
|
347
|
+
<div
|
|
348
|
+
className={clsx(
|
|
349
|
+
"fixed bottom-0 flex h-14 w-full items-center justify-center gap-2 overflow-clip bg-primary-foreground transition-all",
|
|
350
|
+
direction === "rtl" ? "flex-row-reverse" : "flex-row"
|
|
351
|
+
)}
|
|
352
|
+
style={{
|
|
353
|
+
width:
|
|
354
|
+
size > 600
|
|
355
|
+
? `${openSideMenu ? openDrawerWidth : 56}px`
|
|
356
|
+
: `${openSideMenu ? openDrawerWidth : 0}px`,
|
|
357
|
+
}}
|
|
358
|
+
>
|
|
359
|
+
{DrawerFooterActions && openSideMenu ? (
|
|
360
|
+
<>{DrawerFooterActions}</>
|
|
361
|
+
) : null}
|
|
362
|
+
|
|
363
|
+
{/* Expand Button */}
|
|
364
|
+
{size > 600 && openSideMenu ? (
|
|
365
|
+
<Tooltip
|
|
366
|
+
side={"left"}
|
|
367
|
+
delayDuration={500}
|
|
368
|
+
content={
|
|
369
|
+
keepOpen
|
|
370
|
+
? props.texts?.collapseSidebar || "Collapse Sidebar"
|
|
371
|
+
: props.texts?.expandSidebar || "Expand Sidebar"
|
|
372
|
+
}
|
|
373
|
+
>
|
|
374
|
+
<Button
|
|
375
|
+
variant="light"
|
|
376
|
+
onClick={() => setKeepOpen(!keepOpen)}
|
|
377
|
+
size="smallIcon"
|
|
378
|
+
>
|
|
379
|
+
<svg
|
|
380
|
+
className={clsx(
|
|
381
|
+
"h-6 w-6 shrink-0 text-primary transition-all disabled:bg-gray-200 ",
|
|
382
|
+
keepOpen
|
|
383
|
+
? isRTL
|
|
384
|
+
? "-rotate-90"
|
|
385
|
+
: "rotate-90"
|
|
386
|
+
: isRTL
|
|
387
|
+
? "rotate-90"
|
|
388
|
+
: "-rotate-90"
|
|
389
|
+
)}
|
|
390
|
+
fill="currentColor"
|
|
391
|
+
viewBox="0 0 20 20"
|
|
392
|
+
>
|
|
393
|
+
<path
|
|
394
|
+
fillRule="evenodd"
|
|
395
|
+
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
|
396
|
+
clipRule="evenodd"
|
|
397
|
+
></path>
|
|
398
|
+
</svg>
|
|
399
|
+
</Button>
|
|
400
|
+
</Tooltip>
|
|
401
|
+
) : null}
|
|
402
|
+
</div>
|
|
403
|
+
</div>
|
|
404
|
+
{/*
|
|
405
|
+
* ----------------------------------------------------------------------------------------------------
|
|
406
|
+
* Children Container
|
|
407
|
+
* ----------------------------------------------------------------------------------------------------
|
|
408
|
+
*/}
|
|
409
|
+
<div
|
|
410
|
+
className="fixed overflow-y-auto"
|
|
411
|
+
style={
|
|
412
|
+
isRTL
|
|
413
|
+
? {
|
|
414
|
+
height: `calc(100% - ${props.topBar ? "56" : "0"}px)`,
|
|
415
|
+
width: `calc(100% - ${drawerSizeCondition}px)`,
|
|
416
|
+
left: "0px",
|
|
417
|
+
top: props.topBar ? "56px" : "0px",
|
|
418
|
+
}
|
|
419
|
+
: {
|
|
420
|
+
height: `calc(100% - ${props.topBar ? "56" : "0"}px)`,
|
|
421
|
+
width: `calc(100% - ${drawerSizeCondition}px)`,
|
|
422
|
+
left: `${drawerSizeCondition}px`,
|
|
423
|
+
top: props.topBar ? "56px" : "0px",
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
>
|
|
427
|
+
{props.children}
|
|
428
|
+
</div>
|
|
429
|
+
</div>
|
|
430
|
+
)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const AvatarIcon = () => (
|
|
434
|
+
<svg
|
|
435
|
+
className="absolute -left-1 h-10 w-10 text-gray-400"
|
|
436
|
+
fill="currentColor"
|
|
437
|
+
viewBox="0 0 20 20"
|
|
438
|
+
>
|
|
439
|
+
<path
|
|
440
|
+
fillRule="evenodd"
|
|
441
|
+
d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
|
|
442
|
+
clipRule="evenodd"
|
|
443
|
+
></path>
|
|
444
|
+
</svg>
|
|
445
|
+
)
|