uibee 2.8.2 → 2.8.5
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/src/components/alert/alert.d.ts +8 -0
- package/dist/src/components/alert/alert.js +15 -0
- package/dist/src/components/index.d.ts +1 -0
- package/dist/src/components/index.js +2 -0
- package/dist/src/components/inputs/select.d.ts +2 -1
- package/dist/src/components/inputs/select.js +21 -10
- package/dist/src/components/login/loginPage.d.ts +1 -1
- package/dist/src/components/login/loginPage.js +3 -2
- package/dist/src/globals.css +46 -0
- package/package.json +1 -1
- package/src/components/alert/alert.tsx +35 -0
- package/src/components/index.ts +3 -0
- package/src/components/inputs/select.tsx +67 -33
- package/src/components/login/loginPage.tsx +18 -1
- package/src/types/components.d.ts +2 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
type AlertProps = {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
variant?: 'warning' | 'info';
|
|
5
|
+
className?: string;
|
|
6
|
+
};
|
|
7
|
+
export default function Alert({ children, variant, className, }: AlertProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CircleAlert, TriangleAlert, Info } from 'lucide-react';
|
|
3
|
+
export default function Alert({ children, variant = 'warning', className = '', }) {
|
|
4
|
+
const color = variant === 'warning' ? 'bg-red-900'
|
|
5
|
+
: variant === 'info' ? 'bg-blue-600' :
|
|
6
|
+
'bg-gray-900';
|
|
7
|
+
const Icon = variant === 'warning' ? CircleAlert
|
|
8
|
+
: variant === 'info' ? Info :
|
|
9
|
+
TriangleAlert;
|
|
10
|
+
return (_jsxs("div", { className: `
|
|
11
|
+
grid grid-cols-[min-content_auto] rounded-lg
|
|
12
|
+
p-[0.5em_1em_0.5em_0.8em] items-start w-fit text-white
|
|
13
|
+
${color} ${className}
|
|
14
|
+
`, children: [_jsx(Icon, { className: 'w-8 h-8 mr-[0.3rem] stroke-login-50' }), _jsx("div", { className: 'self-center', children: children })] }));
|
|
15
|
+
}
|
|
@@ -19,3 +19,4 @@ export { default as VersionTag } from './version/version';
|
|
|
19
19
|
export { default as LoginPage } from './login/loginPage';
|
|
20
20
|
export { default as Toaster, toast } from './toast/toaster';
|
|
21
21
|
export { default as Button } from './buttons/button';
|
|
22
|
+
export { default as Alert } from './alert/alert';
|
|
@@ -16,5 +16,6 @@ export type SelectProps = {
|
|
|
16
16
|
placeholder?: string;
|
|
17
17
|
info?: string;
|
|
18
18
|
clearable?: boolean;
|
|
19
|
+
searchable?: boolean;
|
|
19
20
|
};
|
|
20
|
-
export default function Select({ label, name, value, onChange, options, error, className, disabled, required, placeholder, info, clearable, }: SelectProps): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export default function Select({ label, name, value, onChange, options, error, className, disabled, required, placeholder, info, clearable, searchable, }: SelectProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -3,11 +3,17 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState, useEffect } from 'react';
|
|
4
4
|
import Image from 'next/image';
|
|
5
5
|
import { useClickOutside } from '../../hooks';
|
|
6
|
-
import { ChevronDown, X } from 'lucide-react';
|
|
6
|
+
import { ChevronDown, X, Search } from 'lucide-react';
|
|
7
7
|
import { FieldWrapper } from './shared';
|
|
8
|
-
export default function Select({ label, name, value, onChange, options, error, className, disabled, required, placeholder = 'Select an option', info, clearable = true, }) {
|
|
8
|
+
export default function Select({ label, name, value, onChange, options, error, className, disabled, required, placeholder = 'Select an option', info, clearable = true, searchable = true, }) {
|
|
9
9
|
const [isOpen, setIsOpen] = useState(false);
|
|
10
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
10
11
|
const [selectedOption, setSelectedOption] = useState(options.find(opt => opt.value === value));
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (!isOpen) {
|
|
14
|
+
setSearchTerm('');
|
|
15
|
+
}
|
|
16
|
+
}, [isOpen]);
|
|
11
17
|
useEffect(() => {
|
|
12
18
|
setSelectedOption(options.find(opt => opt.value === value));
|
|
13
19
|
}, [value, options]);
|
|
@@ -30,6 +36,7 @@ export default function Select({ label, name, value, onChange, options, error, c
|
|
|
30
36
|
onChange(null);
|
|
31
37
|
}
|
|
32
38
|
}
|
|
39
|
+
const filteredOptions = options.filter(option => option.label.toLowerCase().includes(searchTerm.toLowerCase()));
|
|
33
40
|
return (_jsxs(FieldWrapper, { label: label, name: name, required: required, info: info, error: error, className: className, children: [_jsxs("div", { className: 'relative', ref: containerRef, children: [_jsxs("button", { type: 'button', onClick: () => !disabled && setIsOpen(!isOpen), disabled: disabled, "aria-haspopup": 'listbox', "aria-expanded": isOpen, "aria-labelledby": label ? undefined : name, className: `
|
|
34
41
|
w-full rounded-md bg-login-500/50 border border-login-500
|
|
35
42
|
text-login-text text-left
|
|
@@ -47,13 +54,17 @@ export default function Select({ label, name, value, onChange, options, error, c
|
|
|
47
54
|
text-login-200 pointer-events-none
|
|
48
55
|
transition-transform duration-200
|
|
49
56
|
${isOpen ? 'rotate-180' : ''}
|
|
50
|
-
`, children: _jsx(ChevronDown, { className: 'w-4 h-4' }) })] })] }), isOpen && (
|
|
57
|
+
`, children: _jsx(ChevronDown, { className: 'w-4 h-4' }) })] })] }), isOpen && (_jsxs("div", { className: `
|
|
51
58
|
absolute z-50 w-full mt-1 bg-login-600 border border-login-500
|
|
52
|
-
rounded-md shadow-lg max-h-60 overflow-
|
|
53
|
-
`, children:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
rounded-md shadow-lg max-h-60 overflow-hidden flex flex-col
|
|
60
|
+
`, children: [searchable && (_jsx("div", { className: 'p-2 sticky top-0 bg-login-600 border-b border-login-500 z-10', children: _jsxs("div", { className: 'relative', children: [_jsx(Search, { className: 'absolute left-2.5 top-1/2 -translate-y-1/2 w-4 h-4 text-login-200' }), _jsx("input", { type: 'text', value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), placeholder: 'Search...', autoFocus: true, className: `
|
|
61
|
+
w-full bg-login-500/50 border border-login-500 rounded-md
|
|
62
|
+
py-1.5 pl-9 pr-3 text-sm text-login-text
|
|
63
|
+
focus:outline-none focus:border-login focus:ring-1 focus:ring-login
|
|
64
|
+
` })] }) })), _jsx("div", { className: 'overflow-auto noscroll', children: filteredOptions.length > 0 ? (_jsx("ul", { className: 'py-1', role: 'listbox', children: filteredOptions.map((option) => (_jsx("li", { role: 'option', "aria-selected": selectedOption?.value === option.value, children: _jsxs("button", { type: 'button', onClick: () => handleSelect(option), className: `
|
|
65
|
+
w-full text-left px-3 py-2 text-sm
|
|
66
|
+
hover:bg-login-500 transition-colors duration-150
|
|
67
|
+
flex items-center gap-2
|
|
68
|
+
${selectedOption?.value === option.value ? 'bg-login-500 text-login' : 'text-login-text'}
|
|
69
|
+
`, children: [option.image && (_jsx(Image, { src: option.image, alt: '', width: 75, height: 25, className: 'rounded-md object-cover shrink-0' })), _jsx("span", { className: 'truncate', children: option.label })] }) }, option.value))) })) : (_jsx("div", { className: 'px-3 py-2 text-sm text-login-200', children: searchTerm ? 'No results found' : 'No options available' })) })] }))] }), _jsx("input", { type: 'hidden', name: name, value: selectedOption?.value || '', required: required })] }));
|
|
59
70
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { LoginPageProps } from 'uibee/components';
|
|
2
|
-
export default function LoginPage({ title, description, redirectURL, version, btg, handleSubmit }: LoginPageProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export default function LoginPage({ title, description, redirectURL, version, btg, handleSubmit, guestRedirectURL, guestText }: LoginPageProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { LogIn } from 'lucide-react';
|
|
3
3
|
import Logo from '../logo/logo';
|
|
4
4
|
import Link from 'next/link';
|
|
5
|
-
export default function LoginPage({ title, description, redirectURL, version, btg, handleSubmit }) {
|
|
5
|
+
export default function LoginPage({ title, description, redirectURL, version, btg, handleSubmit, guestRedirectURL, guestText }) {
|
|
6
6
|
return (_jsx("main", { className: 'w-full h-full flex items-center justify-center bg-login-800 p-8', children: _jsxs("div", { className: 'flex flex-col justify-center items-center bg-login-600 px-4 py-12 rounded-xl w-full max-w-md gap-4 md:gap-6', children: [_jsx("div", { className: 'relative aspect-3/1 w-full', children: _jsx(Logo, { className: 'object-contain px-6 sm:px-12' }) }), _jsxs("h1", { className: 'text-3xl font-extrabold text-login text-center tracking-tight', children: [title, " ", btg ? ' - Break the Glass' : ''] }), description && (_jsx("p", { className: 'text-center font-medium text-lg mb-2 max-w-xs', children: description })), btg ? (_jsxs("form", { className: 'w-full flex flex-col gap-3 max-w-xs', onSubmit: e => {
|
|
7
7
|
e.preventDefault();
|
|
8
8
|
handleSubmit?.(new FormData(e.currentTarget));
|
|
@@ -13,5 +13,6 @@ export default function LoginPage({ title, description, redirectURL, version, bt
|
|
|
13
13
|
max-w-xs py-3 px-6 rounded-xl bg-login font-bold
|
|
14
14
|
text-lg hover:bg-login/80 transition-all
|
|
15
15
|
duration-200 mb-2 mt-2 cursor-pointer
|
|
16
|
-
`, children: ["Login", _jsx(LogIn, { className: 'w-6 h-6' })] })),
|
|
16
|
+
`, children: ["Login", _jsx(LogIn, { className: 'w-6 h-6' })] })), guestRedirectURL &&
|
|
17
|
+
_jsx(Link, { href: guestRedirectURL, className: 'text-sm font-semibold cursor-pointer opacity-50', children: guestText || 'Continue as guest' }), _jsxs("span", { className: 'text-sm mt-2', children: ["v", version] })] }) }));
|
|
17
18
|
}
|
package/dist/src/globals.css
CHANGED
|
@@ -10,12 +10,15 @@
|
|
|
10
10
|
--color-red-200: oklch(88.5% 0.062 18.334);
|
|
11
11
|
--color-red-400: oklch(70.4% 0.191 22.216);
|
|
12
12
|
--color-red-500: oklch(63.7% 0.237 25.331);
|
|
13
|
+
--color-red-900: oklch(39.6% 0.141 25.723);
|
|
13
14
|
--color-yellow-500: oklch(79.5% 0.184 86.047);
|
|
14
15
|
--color-green-500: oklch(72.3% 0.219 149.579);
|
|
15
16
|
--color-blue-500: oklch(62.3% 0.214 259.815);
|
|
17
|
+
--color-blue-600: oklch(54.6% 0.245 262.881);
|
|
16
18
|
--color-gray-200: oklch(92.8% 0.006 264.531);
|
|
17
19
|
--color-gray-300: oklch(87.2% 0.01 258.338);
|
|
18
20
|
--color-gray-400: oklch(70.7% 0.022 261.325);
|
|
21
|
+
--color-gray-900: oklch(21% 0.034 264.665);
|
|
19
22
|
--color-black: #000;
|
|
20
23
|
--color-white: #fff;
|
|
21
24
|
--spacing: 0.25rem;
|
|
@@ -293,6 +296,9 @@
|
|
|
293
296
|
.relative {
|
|
294
297
|
position: relative;
|
|
295
298
|
}
|
|
299
|
+
.sticky {
|
|
300
|
+
position: sticky;
|
|
301
|
+
}
|
|
296
302
|
.inset-0 {
|
|
297
303
|
inset: calc(var(--spacing) * 0);
|
|
298
304
|
}
|
|
@@ -347,6 +353,9 @@
|
|
|
347
353
|
.left-2 {
|
|
348
354
|
left: calc(var(--spacing) * 2);
|
|
349
355
|
}
|
|
356
|
+
.left-2\.5 {
|
|
357
|
+
left: calc(var(--spacing) * 2.5);
|
|
358
|
+
}
|
|
350
359
|
.left-3 {
|
|
351
360
|
left: calc(var(--spacing) * 3);
|
|
352
361
|
}
|
|
@@ -1004,6 +1013,9 @@
|
|
|
1004
1013
|
.mt-2 {
|
|
1005
1014
|
margin-top: calc(var(--spacing) * 2);
|
|
1006
1015
|
}
|
|
1016
|
+
.mr-\[0\.3rem\] {
|
|
1017
|
+
margin-right: 0.3rem;
|
|
1018
|
+
}
|
|
1007
1019
|
.mb-1 {
|
|
1008
1020
|
margin-bottom: calc(var(--spacing) * 1);
|
|
1009
1021
|
}
|
|
@@ -1292,6 +1304,9 @@
|
|
|
1292
1304
|
.grid-cols-8 {
|
|
1293
1305
|
grid-template-columns: repeat(8, minmax(0, 1fr));
|
|
1294
1306
|
}
|
|
1307
|
+
.grid-cols-\[min-content_auto\] {
|
|
1308
|
+
grid-template-columns: min-content auto;
|
|
1309
|
+
}
|
|
1295
1310
|
.flex-col {
|
|
1296
1311
|
flex-direction: column;
|
|
1297
1312
|
}
|
|
@@ -1310,6 +1325,9 @@
|
|
|
1310
1325
|
.items-end {
|
|
1311
1326
|
align-items: flex-end;
|
|
1312
1327
|
}
|
|
1328
|
+
.items-start {
|
|
1329
|
+
align-items: flex-start;
|
|
1330
|
+
}
|
|
1313
1331
|
.justify-between {
|
|
1314
1332
|
justify-content: space-between;
|
|
1315
1333
|
}
|
|
@@ -1344,6 +1362,9 @@
|
|
|
1344
1362
|
margin-inline-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)));
|
|
1345
1363
|
}
|
|
1346
1364
|
}
|
|
1365
|
+
.self-center {
|
|
1366
|
+
align-self: center;
|
|
1367
|
+
}
|
|
1347
1368
|
.truncate {
|
|
1348
1369
|
overflow: hidden;
|
|
1349
1370
|
text-overflow: ellipsis;
|
|
@@ -1401,6 +1422,10 @@
|
|
|
1401
1422
|
border-top-style: var(--tw-border-style);
|
|
1402
1423
|
border-top-width: 1px;
|
|
1403
1424
|
}
|
|
1425
|
+
.border-b {
|
|
1426
|
+
border-bottom-style: var(--tw-border-style);
|
|
1427
|
+
border-bottom-width: 1px;
|
|
1428
|
+
}
|
|
1404
1429
|
.border-none {
|
|
1405
1430
|
--tw-border-style: none;
|
|
1406
1431
|
border-style: none;
|
|
@@ -1429,6 +1454,12 @@
|
|
|
1429
1454
|
.bg-\[\#18181899\] {
|
|
1430
1455
|
background-color: #18181899;
|
|
1431
1456
|
}
|
|
1457
|
+
.bg-blue-600 {
|
|
1458
|
+
background-color: var(--color-blue-600);
|
|
1459
|
+
}
|
|
1460
|
+
.bg-gray-900 {
|
|
1461
|
+
background-color: var(--color-gray-900);
|
|
1462
|
+
}
|
|
1432
1463
|
.bg-login {
|
|
1433
1464
|
background-color: var(--color-login);
|
|
1434
1465
|
}
|
|
@@ -1483,6 +1514,9 @@
|
|
|
1483
1514
|
background-color: color-mix(in oklab, var(--color-login) 70%, transparent);
|
|
1484
1515
|
}
|
|
1485
1516
|
}
|
|
1517
|
+
.bg-red-900 {
|
|
1518
|
+
background-color: var(--color-red-900);
|
|
1519
|
+
}
|
|
1486
1520
|
.bg-transparent {
|
|
1487
1521
|
background-color: transparent;
|
|
1488
1522
|
}
|
|
@@ -1539,6 +1573,9 @@
|
|
|
1539
1573
|
.stroke-login {
|
|
1540
1574
|
stroke: var(--color-login);
|
|
1541
1575
|
}
|
|
1576
|
+
.stroke-login-50 {
|
|
1577
|
+
stroke: var(--color-login-50);
|
|
1578
|
+
}
|
|
1542
1579
|
.stroke-\[3\.5px\] {
|
|
1543
1580
|
stroke-width: 3.5px;
|
|
1544
1581
|
}
|
|
@@ -1563,6 +1600,9 @@
|
|
|
1563
1600
|
.p-8 {
|
|
1564
1601
|
padding: calc(var(--spacing) * 8);
|
|
1565
1602
|
}
|
|
1603
|
+
.p-\[0\.5em_1em_0\.5em_0\.8em\] {
|
|
1604
|
+
padding: 0.5em 1em 0.5em 0.8em;
|
|
1605
|
+
}
|
|
1566
1606
|
.px-2 {
|
|
1567
1607
|
padding-inline: calc(var(--spacing) * 2);
|
|
1568
1608
|
}
|
|
@@ -1578,6 +1618,9 @@
|
|
|
1578
1618
|
.py-1 {
|
|
1579
1619
|
padding-block: calc(var(--spacing) * 1);
|
|
1580
1620
|
}
|
|
1621
|
+
.py-1\.5 {
|
|
1622
|
+
padding-block: calc(var(--spacing) * 1.5);
|
|
1623
|
+
}
|
|
1581
1624
|
.py-2 {
|
|
1582
1625
|
padding-block: calc(var(--spacing) * 2);
|
|
1583
1626
|
}
|
|
@@ -1617,6 +1660,9 @@
|
|
|
1617
1660
|
.pl-4 {
|
|
1618
1661
|
padding-left: calc(var(--spacing) * 4);
|
|
1619
1662
|
}
|
|
1663
|
+
.pl-9 {
|
|
1664
|
+
padding-left: calc(var(--spacing) * 9);
|
|
1665
|
+
}
|
|
1620
1666
|
.pl-10 {
|
|
1621
1667
|
padding-left: calc(var(--spacing) * 10);
|
|
1622
1668
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
import { CircleAlert, TriangleAlert, Info } from 'lucide-react'
|
|
3
|
+
|
|
4
|
+
type AlertProps = {
|
|
5
|
+
children: ReactNode
|
|
6
|
+
variant?: 'warning' | 'info'
|
|
7
|
+
className?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function Alert({
|
|
11
|
+
children,
|
|
12
|
+
variant = 'warning',
|
|
13
|
+
className = '',
|
|
14
|
+
}: AlertProps) {
|
|
15
|
+
const color = variant === 'warning' ? 'bg-red-900'
|
|
16
|
+
: variant === 'info' ? 'bg-blue-600' :
|
|
17
|
+
'bg-gray-900'
|
|
18
|
+
|
|
19
|
+
const Icon = variant === 'warning' ? CircleAlert
|
|
20
|
+
: variant === 'info' ? Info :
|
|
21
|
+
TriangleAlert
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div
|
|
25
|
+
className={`
|
|
26
|
+
grid grid-cols-[min-content_auto] rounded-lg
|
|
27
|
+
p-[0.5em_1em_0.5em_0.8em] items-start w-fit text-white
|
|
28
|
+
${color} ${className}
|
|
29
|
+
`}
|
|
30
|
+
>
|
|
31
|
+
<Icon className='w-8 h-8 mr-[0.3rem] stroke-login-50' />
|
|
32
|
+
<div className='self-center'>{children}</div>
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
}
|
package/src/components/index.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useState, useEffect } from 'react'
|
|
4
4
|
import Image from 'next/image'
|
|
5
5
|
import { useClickOutside } from '../../hooks'
|
|
6
|
-
import { ChevronDown, X } from 'lucide-react'
|
|
6
|
+
import { ChevronDown, X, Search } from 'lucide-react'
|
|
7
7
|
import { FieldWrapper } from './shared'
|
|
8
8
|
|
|
9
9
|
export type Option = {
|
|
@@ -25,6 +25,7 @@ export type SelectProps = {
|
|
|
25
25
|
placeholder?: string
|
|
26
26
|
info?: string
|
|
27
27
|
clearable?: boolean
|
|
28
|
+
searchable?: boolean
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export default function Select({
|
|
@@ -40,12 +41,20 @@ export default function Select({
|
|
|
40
41
|
placeholder = 'Select an option',
|
|
41
42
|
info,
|
|
42
43
|
clearable = true,
|
|
44
|
+
searchable = true,
|
|
43
45
|
}: SelectProps) {
|
|
44
46
|
const [isOpen, setIsOpen] = useState(false)
|
|
47
|
+
const [searchTerm, setSearchTerm] = useState('')
|
|
45
48
|
const [selectedOption, setSelectedOption] = useState<Option | undefined>(
|
|
46
49
|
options.find(opt => opt.value === value)
|
|
47
50
|
)
|
|
48
51
|
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (!isOpen) {
|
|
54
|
+
setSearchTerm('')
|
|
55
|
+
}
|
|
56
|
+
}, [isOpen])
|
|
57
|
+
|
|
49
58
|
useEffect(() => {
|
|
50
59
|
setSelectedOption(options.find(opt => opt.value === value))
|
|
51
60
|
}, [value, options])
|
|
@@ -70,6 +79,10 @@ export default function Select({
|
|
|
70
79
|
}
|
|
71
80
|
}
|
|
72
81
|
|
|
82
|
+
const filteredOptions = options.filter(option =>
|
|
83
|
+
option.label.toLowerCase().includes(searchTerm.toLowerCase())
|
|
84
|
+
)
|
|
85
|
+
|
|
73
86
|
return (
|
|
74
87
|
<FieldWrapper
|
|
75
88
|
label={label}
|
|
@@ -141,41 +154,62 @@ export default function Select({
|
|
|
141
154
|
{isOpen && (
|
|
142
155
|
<div className={`
|
|
143
156
|
absolute z-50 w-full mt-1 bg-login-600 border border-login-500
|
|
144
|
-
rounded-md shadow-lg max-h-60 overflow-
|
|
157
|
+
rounded-md shadow-lg max-h-60 overflow-hidden flex flex-col
|
|
145
158
|
`}>
|
|
146
|
-
{
|
|
147
|
-
<
|
|
148
|
-
|
|
149
|
-
<
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
alt=''
|
|
164
|
-
width={75}
|
|
165
|
-
height={25}
|
|
166
|
-
className='rounded-md object-cover shrink-0'
|
|
167
|
-
/>
|
|
168
|
-
)}
|
|
169
|
-
<span className='truncate'>{option.label}</span>
|
|
170
|
-
</button>
|
|
171
|
-
</li>
|
|
172
|
-
))}
|
|
173
|
-
</ul>
|
|
174
|
-
) : (
|
|
175
|
-
<div className='px-3 py-2 text-sm text-login-200'>
|
|
176
|
-
No options available
|
|
159
|
+
{searchable && (
|
|
160
|
+
<div className='p-2 sticky top-0 bg-login-600 border-b border-login-500 z-10'>
|
|
161
|
+
<div className='relative'>
|
|
162
|
+
<Search className='absolute left-2.5 top-1/2 -translate-y-1/2 w-4 h-4 text-login-200' />
|
|
163
|
+
<input
|
|
164
|
+
type='text'
|
|
165
|
+
value={searchTerm}
|
|
166
|
+
onChange={(e) => setSearchTerm(e.target.value)}
|
|
167
|
+
placeholder='Search...'
|
|
168
|
+
autoFocus
|
|
169
|
+
className={`
|
|
170
|
+
w-full bg-login-500/50 border border-login-500 rounded-md
|
|
171
|
+
py-1.5 pl-9 pr-3 text-sm text-login-text
|
|
172
|
+
focus:outline-none focus:border-login focus:ring-1 focus:ring-login
|
|
173
|
+
`}
|
|
174
|
+
/>
|
|
175
|
+
</div>
|
|
177
176
|
</div>
|
|
178
177
|
)}
|
|
178
|
+
<div className='overflow-auto noscroll'>
|
|
179
|
+
{filteredOptions.length > 0 ? (
|
|
180
|
+
<ul className='py-1' role='listbox'>
|
|
181
|
+
{filteredOptions.map((option) => (
|
|
182
|
+
<li key={option.value} role='option' aria-selected={selectedOption?.value === option.value}>
|
|
183
|
+
<button
|
|
184
|
+
type='button'
|
|
185
|
+
onClick={() => handleSelect(option)}
|
|
186
|
+
className={`
|
|
187
|
+
w-full text-left px-3 py-2 text-sm
|
|
188
|
+
hover:bg-login-500 transition-colors duration-150
|
|
189
|
+
flex items-center gap-2
|
|
190
|
+
${selectedOption?.value === option.value ? 'bg-login-500 text-login': 'text-login-text'}
|
|
191
|
+
`}
|
|
192
|
+
>
|
|
193
|
+
{option.image && (
|
|
194
|
+
<Image
|
|
195
|
+
src={option.image}
|
|
196
|
+
alt=''
|
|
197
|
+
width={75}
|
|
198
|
+
height={25}
|
|
199
|
+
className='rounded-md object-cover shrink-0'
|
|
200
|
+
/>
|
|
201
|
+
)}
|
|
202
|
+
<span className='truncate'>{option.label}</span>
|
|
203
|
+
</button>
|
|
204
|
+
</li>
|
|
205
|
+
))}
|
|
206
|
+
</ul>
|
|
207
|
+
) : (
|
|
208
|
+
<div className='px-3 py-2 text-sm text-login-200'>
|
|
209
|
+
{searchTerm ? 'No results found' : 'No options available'}
|
|
210
|
+
</div>
|
|
211
|
+
)}
|
|
212
|
+
</div>
|
|
179
213
|
</div>
|
|
180
214
|
)}
|
|
181
215
|
</div>
|
|
@@ -3,7 +3,16 @@ import { LogIn } from 'lucide-react'
|
|
|
3
3
|
import Logo from '@components/logo/logo'
|
|
4
4
|
import Link from 'next/link'
|
|
5
5
|
|
|
6
|
-
export default function LoginPage({
|
|
6
|
+
export default function LoginPage({
|
|
7
|
+
title,
|
|
8
|
+
description,
|
|
9
|
+
redirectURL,
|
|
10
|
+
version,
|
|
11
|
+
btg,
|
|
12
|
+
handleSubmit,
|
|
13
|
+
guestRedirectURL,
|
|
14
|
+
guestText
|
|
15
|
+
}: LoginPageProps) {
|
|
7
16
|
return (
|
|
8
17
|
<main className='w-full h-full flex items-center justify-center bg-login-800 p-8'>
|
|
9
18
|
<div
|
|
@@ -69,6 +78,14 @@ export default function LoginPage({ title, description, redirectURL, version, bt
|
|
|
69
78
|
<LogIn className='w-6 h-6' />
|
|
70
79
|
</Link>
|
|
71
80
|
)}
|
|
81
|
+
{guestRedirectURL &&
|
|
82
|
+
<Link
|
|
83
|
+
href={guestRedirectURL}
|
|
84
|
+
className='text-sm font-semibold cursor-pointer opacity-50'
|
|
85
|
+
>
|
|
86
|
+
{guestText || 'Continue as guest'}
|
|
87
|
+
</Link>
|
|
88
|
+
}
|
|
72
89
|
<span className='text-sm mt-2'>v{version}</span>
|
|
73
90
|
</div>
|
|
74
91
|
</main>
|