@sikka/hawa 0.0.260 → 0.0.262

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.
@@ -0,0 +1,516 @@
1
+ import React, { useEffect, useRef, useState } from "react"
2
+ import clsx from "clsx"
3
+ import { HiMenu } from "react-icons/hi"
4
+ import { FaChevronRight } from "react-icons/fa"
5
+ import { FiSettings } from "react-icons/fi"
6
+ import useDiscloser from "../hooks/useDiscloser"
7
+ import useBreakpoint from "../hooks/useBreakpoint"
8
+ import { HawaButton, HawaMenu } from "../elements"
9
+
10
+ // TODO: when no pagetitle, navbar gets messy
11
+
12
+ type HawaAppLayoutTypes = {
13
+ drawerItems: {
14
+ label: string
15
+ icon: any
16
+ slug: string
17
+ action: () => void
18
+ subItems?: any
19
+ }[][]
20
+ direction?: "rtl" | "ltr"
21
+ currentPage: string
22
+ pageTitle?: string
23
+ logoSymbol?: any
24
+ logoLink?: string
25
+ logoText?: any
26
+ children?: any
27
+ topBar?: boolean
28
+ username?: string
29
+ email?: string
30
+ drawerSize?: "sm" | "md" | "large"
31
+ profileMenuItems?: MenuItems[][]
32
+ onSettingsClick?: () => void
33
+ }
34
+
35
+ type MenuItems = {
36
+ icon?: JSX.Element
37
+ label: string
38
+ action?: (e: any) => void
39
+ isButton?: boolean
40
+ }
41
+ export const HawaAppLayoutSimplified: React.FunctionComponent<
42
+ HawaAppLayoutTypes
43
+ > = ({ direction = "rtl", drawerSize = "md", onSettingsClick, ...props }) => {
44
+ const [openSideMenu, setOpenSideMenu] = useState(false)
45
+ const [openSubItem, setOpenSubitem] = useState("")
46
+ const { isOpen, onClose, onOpen } = useDiscloser(false)
47
+ const [keepOpen, setKeepOpen] = useState(false)
48
+ const ref = useRef(null)
49
+ const isRTL = direction === "rtl"
50
+ let size
51
+ if (typeof window !== "undefined") {
52
+ size = useBreakpoint()
53
+ } else {
54
+ size = 1200
55
+ }
56
+ useEffect(() => {
57
+ const handleClickOutside = (event) => {
58
+ if (ref.current && !ref.current.contains(event.target) && !keepOpen) {
59
+ setOpenSideMenu(false)
60
+ }
61
+ }
62
+ document.addEventListener("click", handleClickOutside, true)
63
+ return () => {
64
+ document.removeEventListener("click", handleClickOutside, true)
65
+ }
66
+ }, [keepOpen])
67
+
68
+ let drawerDefaultStyle =
69
+ "fixed top-0 z-40 flex h-full flex-col justify-between overflow-x-clip bg-layoutPrimary-500 transition-all"
70
+
71
+ let drawerSizeStyle = {
72
+ opened: {
73
+ sm: "100",
74
+ md: "160",
75
+ lg: "250",
76
+ },
77
+ closed: {
78
+ sm: "56",
79
+ md: "56",
80
+ lg: "56",
81
+ },
82
+ }
83
+ let drawerSizeCondition =
84
+ size > 600 ? drawerSizeStyle[keepOpen ? "opened" : "closed"][drawerSize] : 0
85
+ return (
86
+ <div className="fixed left-0 bg-red-500">
87
+ {props.topBar && (
88
+ <div
89
+ className={clsx(
90
+ "fixed left-0 right-0 top-0 z-30 flex h-14 w-full items-center justify-between bg-layoutPrimary-500 p-2",
91
+ isRTL ? "flex-row-reverse" : "flex-row"
92
+ )}
93
+ >
94
+ {/* Nav Side Of Navbar */}
95
+ {size > 600 ? (
96
+ props.pageTitle ? (
97
+ // Title of the page
98
+ <div
99
+ className={clsx(
100
+ isRTL
101
+ ? [size > 600 ? "mr-14" : "mr-2", keepOpen ? "mr-40" : ""]
102
+ : [size > 600 ? "ml-14" : "ml-2", keepOpen ? "ml-40" : ""]
103
+ )}
104
+ style={
105
+ isRTL
106
+ ? {
107
+ marginRight: `${
108
+ drawerSizeStyle[keepOpen ? "opened" : "closed"][
109
+ drawerSize
110
+ ]
111
+ }px`,
112
+ }
113
+ : {
114
+ marginLeft: `${
115
+ drawerSizeStyle[keepOpen ? "opened" : "closed"][
116
+ drawerSize
117
+ ]
118
+ }px`,
119
+ }
120
+ }
121
+ >
122
+ {props.pageTitle}
123
+ </div>
124
+ ) : null
125
+ ) : (
126
+ // Mobile Drawer Menu Button
127
+ <div
128
+ dir={direction}
129
+ className="flex items-center justify-center gap-0.5 "
130
+ >
131
+ <div
132
+ onClick={() => setOpenSideMenu(true)}
133
+ className="z-40 mx-1 cursor-pointer rounded p-2 transition-all hover:bg-gray-100"
134
+ >
135
+ <HiMenu size={25} />
136
+ </div>
137
+ {/* Mobile Page Title */}
138
+ {props.pageTitle ? (
139
+ <div className="text-sm">{props.pageTitle}</div>
140
+ ) : (
141
+ <div></div>
142
+ )}
143
+ </div>
144
+ )}
145
+
146
+ <div
147
+ className={clsx(
148
+ "flex gap-2",
149
+ isRTL ? "flex-row-reverse" : "flex-row"
150
+ )}
151
+ // dir={direction}
152
+ >
153
+ {/* User Info */}
154
+ {size > 600 ? (
155
+ <div
156
+ className={clsx(
157
+ isRTL ? "text-left text-xs" : "text-right text-xs"
158
+ )}
159
+ >
160
+ <div className="font-bold">{props.username}</div>{" "}
161
+ <div>{props.email}</div>
162
+ </div>
163
+ ) : null}
164
+
165
+ {/* Profile Icon & Menu */}
166
+ <HawaMenu
167
+ direction={direction}
168
+ // buttonPosition={isRTL ? "top-left" : "top-right"}
169
+ withHeader={size > 600 ? false : true}
170
+ headerTitle={size > 600 ? "" : props.username}
171
+ headerSubtitle={size > 600 ? "" : props.email}
172
+ menuItems={props.profileMenuItems}
173
+ // handleClose={onClose}
174
+ // handleOpen={onOpen}
175
+ // open={isOpen}
176
+ position={"bottom-left"}
177
+ // position={isRTL ? "bottom-left" : "bottom-left"}
178
+ // position={isRTL ? "right-bottom" : "bottom-left"}
179
+ >
180
+ <div className="relative h-8 w-8 cursor-pointer overflow-hidden rounded-full ring-1 ring-buttonPrimary-500 dark:bg-gray-600">
181
+ <AvatarIcon />
182
+ </div>
183
+ </HawaMenu>
184
+ </div>
185
+ </div>
186
+ )}
187
+ {/* Drawer Container */}
188
+ <div
189
+ className={clsx(
190
+ "fixed top-0 z-40 flex h-full flex-col justify-between overflow-x-clip transition-all",
191
+ // drawerDefaultStyle,
192
+ "bg-green-400"
193
+ // drawerSizeStyle[drawerSize],
194
+ // isRTL ? "right-0" : "left-0"
195
+ )}
196
+ style={{
197
+ width:
198
+ size > 600
199
+ ? openSideMenu
200
+ ? `${drawerSizeStyle["opened"][drawerSize]}px`
201
+ : `${drawerSizeStyle["closed"][drawerSize]}px`
202
+ : openSideMenu
203
+ ? `${drawerSizeStyle["opened"][drawerSize]}px`
204
+ : "0px",
205
+ }}
206
+ onMouseEnter={() => {
207
+ setOpenSideMenu(true)
208
+ }}
209
+ onMouseLeave={() =>
210
+ keepOpen ? setOpenSideMenu(true) : setOpenSideMenu(false)
211
+ }
212
+ ref={ref}
213
+ >
214
+ {/* Drawer Header */}
215
+ <div
216
+ dir={direction}
217
+ className={clsx(
218
+ "fixed z-50 flex h-14 w-full flex-row items-center bg-blue-400 transition-all",
219
+ "mb-2"
220
+ // openSideMenu ? "w-[56px]" : "w-[160px]"
221
+ // size > 600 || openSideMenu
222
+ // ? "bg-layoutPrimary-500"
223
+ // : "w-0 bg-transparent"
224
+ )}
225
+ style={{
226
+ width:
227
+ size > 600
228
+ ? `${
229
+ // drawerSizeStyle[openSideMenu ? "opened" : "closed"][
230
+ // drawerSize
231
+ // ] - 16
232
+ openSideMenu ? 160 : 56
233
+ }px`
234
+ : "full",
235
+ }}
236
+ >
237
+ {/* Full Logo */}
238
+ <img
239
+ className={clsx(
240
+ "fixed top-2.5 h-9 opacity-0 transition-all",
241
+ isRTL ? "right-2.5" : "left-2.5",
242
+ !openSideMenu ? "invisible opacity-0" : "visible opacity-100"
243
+ // size > 600 ? "" : "right-4"
244
+ )}
245
+ // className={clsx(
246
+ // "fixed top-2.5 h-9 transition-all",
247
+ // isRTL ? "right-2.5" : "left-2.5",
248
+ // !openSideMenu ? "invisible opacity-0" : "visible opacity-100"
249
+ // )}
250
+ src={props.logoLink}
251
+ />
252
+
253
+ {/* Logo Symbol */}
254
+ {size > 600 ? (
255
+ <img
256
+ className={clsx(
257
+ "fixed top-2.5 h-9 transition-all",
258
+ isRTL ? "right-2.5" : "left-2.5",
259
+ openSideMenu ? "invisible opacity-0" : "visible opacity-100"
260
+ )}
261
+ src={props.logoSymbol}
262
+ />
263
+ ) : null}
264
+ </div>
265
+ {/* Drawer Content Container */}
266
+ <div
267
+ className={clsx(
268
+ // "no-scrollbar",
269
+ "fixed bottom-14 top-14 bg-yellow-400 py-2 transition-all",
270
+ // props.topBar ? "" : "mt-2",
271
+ openSideMenu ? "overflow-auto" : "overflow-hidden"
272
+ )}
273
+ style={{
274
+ height: "calc(100% - 112px)",
275
+ width:
276
+ size > 600
277
+ ? `${
278
+ // drawerSizeStyle[openSideMenu ? "opened" : "closed"][
279
+ // drawerSize
280
+ // ] - 16
281
+ openSideMenu ? 160 : 56
282
+ }px`
283
+ : "full",
284
+ }}
285
+ >
286
+ {/* Drawer Items */}
287
+ {/* <div className="mb-10 mt-14"> */}
288
+ {props.drawerItems?.map((dSection, dIndex) => (
289
+ <div
290
+ key={dIndex}
291
+ className={clsx("flex flex-col items-stretch justify-center")}
292
+ >
293
+ {dSection?.map((dItem, i) => {
294
+ return (
295
+ <div key={i} id={"test"} className="flex flex-col">
296
+ <div
297
+ onClick={() => {
298
+ dItem.subItems
299
+ ? openSubItem === dItem.slug
300
+ ? // ||
301
+ // dItem.subItems.find(
302
+ // (e) => e.slug === props.currentPage
303
+ // )
304
+ setOpenSubitem("")
305
+ : setOpenSubitem(dItem.slug)
306
+ : dItem.action()
307
+ }}
308
+ className={clsx(
309
+ props.currentPage === dItem.slug ||
310
+ dItem.subItems?.find(
311
+ (e) => e.slug === props.currentPage
312
+ )
313
+ ? "bg-buttonPrimary-500 text-white"
314
+ : "hover:bg-layoutPrimary-300",
315
+ "m-2 my-1 flex cursor-pointer flex-row items-center justify-between overflow-x-clip rounded p-2 pl-3 transition-all ",
316
+ isRTL ? "flex-row-reverse pr-3" : ""
317
+ )}
318
+ >
319
+ <div className="flex flex-row" dir={direction}>
320
+ <div className="flex items-center justify-center">
321
+ {dItem.icon}
322
+ </div>
323
+ <div
324
+ className={clsx(
325
+ "mx-2 whitespace-nowrap text-sm transition-all",
326
+ openSideMenu ? "opacity-100" : "opacity-0"
327
+ )}
328
+ >
329
+ {dItem.label}
330
+ </div>
331
+ </div>
332
+ {dItem.subItems && (
333
+ <div
334
+ className={clsx(
335
+ openSubItem && dItem.slug === openSubItem
336
+ ? "-rotate-90"
337
+ : "rotate-90"
338
+ )}
339
+ >
340
+ <FaChevronRight fontSize={11} />
341
+ </div>
342
+ )}
343
+ </div>
344
+
345
+ {dItem.subItems && (
346
+ <div
347
+ className={clsx(
348
+ "m-1 flex cursor-pointer flex-col gap-0 overflow-clip whitespace-nowrap rounded bg-layoutPrimary-300 p-1 transition-all",
349
+ openSubItem == dItem.slug && openSideMenu
350
+ ? ""
351
+ : "my-0 py-0",
352
+ isRTL ? "text-right" : "text-left"
353
+ )}
354
+ style={{
355
+ height:
356
+ openSubItem == dItem.slug && openSideMenu
357
+ ? 6 + 33 * dItem.subItems?.length
358
+ : 0,
359
+ }}
360
+ >
361
+ {dItem.subItems?.map((subIt, s) => (
362
+ <div
363
+ key={s}
364
+ className={clsx(
365
+ "flex flex-row gap-2 overflow-x-clip rounded p-2 px-2 text-xs",
366
+ isRTL ? "text-right" : "text-left",
367
+ props.currentPage === subIt.slug
368
+ ? "bg-buttonPrimary-500 text-white hover:bg-buttonPrimary-500"
369
+ : "hover:bg-layoutPrimary-500"
370
+ )}
371
+ dir={direction}
372
+ onClick={() => {
373
+ subIt.action()
374
+ // setOpenSubitem(dItem.slug)
375
+ }}
376
+ >
377
+ <div className="flex items-center justify-center">
378
+ {subIt.icon}
379
+ </div>
380
+ <div className="flex flex-row justify-between">
381
+ {subIt.label}
382
+ </div>
383
+ </div>
384
+ ))}
385
+ </div>
386
+ )}
387
+ </div>
388
+ )
389
+ })}
390
+ {dIndex !== props.drawerItems.length - 1 && (
391
+ <div className="my-2 h-[1px] w-10/12 self-center bg-buttonPrimary-500 text-center "></div>
392
+ )}
393
+ </div>
394
+ ))}
395
+ </div>
396
+ {/* Drawer Footer */}
397
+ <div
398
+ className="fixed bottom-0 flex h-14 w-full items-center justify-center bg-stone-600 transition-all"
399
+ style={{
400
+ width: `${
401
+ openSideMenu ? 160 : 56
402
+
403
+ // drawerSizeStyle[openSideMenu ? "opened" : "closed"][drawerSize] -
404
+ // 16
405
+ }px`,
406
+ }}
407
+ >
408
+ {onSettingsClick && (
409
+ <div
410
+ className=" cursor-pointer rounded p-2 transition-all hover:bg-layoutPrimary-700"
411
+ onClick={() => onSettingsClick()}
412
+ >
413
+ <FiSettings />
414
+ </div>
415
+ )}
416
+ {/* Expand Button */}
417
+ {size > 600 ? (
418
+ <div
419
+ className={clsx("w-fit transition-all")}
420
+ // style={isRTL ? {} : {left: }}
421
+ style={
422
+ isRTL
423
+ ? {
424
+ right: `${
425
+ drawerSizeStyle[openSideMenu ? "opened" : "closed"][
426
+ drawerSize
427
+ ] - 35
428
+ }px`,
429
+ }
430
+ : {
431
+ left: `${
432
+ drawerSizeStyle[openSideMenu ? "opened" : "closed"][
433
+ drawerSize
434
+ ] - 35
435
+ }px`,
436
+ }
437
+ }
438
+ >
439
+ <div
440
+ dir={direction}
441
+ className={clsx(
442
+ "relative left-0 top-0 transition-all",
443
+ openSideMenu ? " opacity-100" : " opacity-0"
444
+ )}
445
+ >
446
+ <div
447
+ onClick={() => setKeepOpen(!keepOpen)}
448
+ className={
449
+ "w-fit cursor-pointer rounded bg-gray-300 p-2 transition-all hover:bg-gray-400"
450
+ }
451
+ >
452
+ <ArrowIcon pointing={keepOpen ? "left" : "right"} />
453
+ </div>
454
+ </div>
455
+ </div>
456
+ ) : null}
457
+ </div>
458
+ </div>
459
+ {/*
460
+
461
+ {/* Children Container */}
462
+ <div
463
+ className="fixed overflow-y-auto bg-yellow-500 p-2"
464
+ style={
465
+ isRTL
466
+ ? {
467
+ height: `calc(100% - ${props.topBar ? "56" : "0"}px)`,
468
+ width: `calc(100% - ${drawerSizeCondition}px)`,
469
+ left: "0px",
470
+ top: props.topBar ? "56px" : "0px",
471
+ }
472
+ : {
473
+ height: `calc(100% - ${props.topBar ? "56" : "0"}px)`,
474
+ width: `calc(100% - ${drawerSizeCondition}px)`,
475
+ left: `${drawerSizeCondition}px`,
476
+ top: props.topBar ? "56px" : "0px",
477
+ }
478
+ }
479
+ >
480
+ {props.children}
481
+ </div>
482
+ </div>
483
+ )
484
+ }
485
+
486
+ const AvatarIcon = () => (
487
+ <svg
488
+ className="absolute -left-1 h-10 w-10 text-gray-400"
489
+ fill="currentColor"
490
+ viewBox="0 0 20 20"
491
+ xmlns="http://www.w3.org/2000/svg"
492
+ >
493
+ <path
494
+ fillRule="evenodd"
495
+ d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
496
+ clipRule="evenodd"
497
+ ></path>
498
+ </svg>
499
+ )
500
+
501
+ const ArrowIcon = ({ pointing }) => (
502
+ <svg
503
+ className={clsx(
504
+ "h-6 w-6 shrink-0 transition-all disabled:bg-gray-200",
505
+ pointing === "right" ? "-rotate-90" : "rotate-90"
506
+ )}
507
+ viewBox="0 0 20 20"
508
+ xmlns="http://www.w3.org/2000/svg"
509
+ >
510
+ <path
511
+ fillRule="evenodd"
512
+ 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"
513
+ clipRule="evenodd"
514
+ ></path>
515
+ </svg>
516
+ )
@@ -2,6 +2,7 @@ export * from "./Box"
2
2
  export * from "./HawaBottomAppBar"
3
3
  export * from "./HawaSiteLayout"
4
4
  export * from "./HawaAppLayout"
5
+ export * from "./HawaAppLayoutSimplified"
5
6
  export * from "./HawaContainer"
6
7
  export * from "./AppSidebar"
7
8
  export * from "./Footer"