uibee 2.17.4 → 3.0.0
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 +34 -0
- package/dist/icons-lZYQ6Vlr.js +125 -0
- package/dist/src/components/index.d.ts +706 -30
- package/dist/src/components/index.js +3243 -44
- package/dist/src/hooks/index.d.ts +15 -3
- package/dist/src/hooks/index.js +44 -3
- package/dist/src/icons/index.d.ts +420 -0
- package/dist/src/icons/index.js +3 -0
- package/dist/src/styles/index.d.ts +1 -1
- package/dist/src/styles/index.js +2 -1
- package/dist/src/utils/index.d.ts +38 -5
- package/dist/src/utils/index.js +125 -6
- package/dist/{src/globals.css → style.css} +1226 -607
- package/dist/useClickOutside-Cmp-RsP3.js +13 -0
- package/index.d.ts +0 -1
- package/package.json +22 -23
- package/src/components/alert/alert.tsx +20 -18
- package/src/components/buttons/button.tsx +28 -35
- package/src/components/confirm/confirmPopup.tsx +15 -17
- package/src/components/container/accordion.tsx +54 -0
- package/src/components/container/glassCard.tsx +14 -0
- package/src/components/container/iconBubble.tsx +30 -0
- package/src/components/container/leftBarPanel.tsx +15 -0
- package/src/components/container/pulseDot.tsx +31 -0
- package/src/components/container/statCard.tsx +24 -0
- package/src/components/container/tabs.tsx +64 -0
- package/src/components/footer/footer.tsx +124 -0
- package/src/components/footer/loginContent.tsx +70 -0
- package/src/components/index.ts +15 -0
- package/src/components/inputs/checkbox.tsx +4 -4
- package/src/components/inputs/input.tsx +11 -13
- package/src/components/inputs/multiSelect.tsx +144 -0
- package/src/components/inputs/radio.tsx +4 -4
- package/src/components/inputs/range.tsx +6 -5
- package/src/components/inputs/searchInput.tsx +1 -1
- package/src/components/inputs/select.tsx +19 -18
- package/src/components/inputs/shared/colorPickerPopup.tsx +5 -5
- package/src/components/inputs/shared/dateTimePickerPopup.tsx +9 -9
- package/src/components/inputs/shared/inputError.tsx +1 -1
- package/src/components/inputs/switch.tsx +7 -7
- package/src/components/inputs/tagInput.tsx +6 -7
- package/src/components/inputs/textarea.tsx +8 -10
- package/src/components/inputs/toggle.tsx +6 -6
- package/src/components/markdownrender/markdownRender.tsx +4 -2
- package/src/components/table/body.tsx +6 -6
- package/src/components/table/format.ts +1 -1
- package/src/components/table/header.tsx +1 -1
- package/src/components/table/pagination.tsx +17 -31
- package/src/components/table/table.tsx +2 -2
- package/src/components/version/version.tsx +1 -1
- package/src/components/vulnerability/constants.ts +6 -6
- package/src/components/vulnerability/severityPill.tsx +6 -7
- package/src/globals.css +12 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useClickOutside.ts +5 -22
- package/src/icons/icons.tsx +431 -0
- package/src/icons/index.ts +2 -0
- package/src/index.ts +0 -1
- package/src/types/hooks.d.ts +8 -2
- package/src/utils/index.ts +1 -1
- package/src/utils/logoConsoleOutput/logoConsoleOutput.ts +31 -0
- package/tsconfig.json +3 -2
- package/tsdown.config.ts +26 -0
- package/Readme.md +0 -29
- package/bun.lock +0 -662
- package/dist/src/components/alert/alert.d.ts +0 -8
- package/dist/src/components/alert/alert.js +0 -15
- package/dist/src/components/buttons/button.d.ts +0 -13
- package/dist/src/components/buttons/button.js +0 -30
- package/dist/src/components/confirm/confirmPopup.d.ts +0 -12
- package/dist/src/components/confirm/confirmPopup.js +0 -18
- package/dist/src/components/container/highlight.d.ts +0 -4
- package/dist/src/components/container/highlight.js +0 -4
- package/dist/src/components/container/page.d.ts +0 -8
- package/dist/src/components/container/page.js +0 -4
- package/dist/src/components/inputs/checkbox.d.ts +0 -17
- package/dist/src/components/inputs/checkbox.js +0 -37
- package/dist/src/components/inputs/input.d.ts +0 -12
- package/dist/src/components/inputs/input.js +0 -124
- package/dist/src/components/inputs/radio.d.ts +0 -17
- package/dist/src/components/inputs/radio.js +0 -27
- package/dist/src/components/inputs/range.d.ts +0 -11
- package/dist/src/components/inputs/range.js +0 -22
- package/dist/src/components/inputs/searchInput.d.ts +0 -6
- package/dist/src/components/inputs/searchInput.js +0 -42
- package/dist/src/components/inputs/select.d.ts +0 -23
- package/dist/src/components/inputs/select.js +0 -70
- package/dist/src/components/inputs/shared/colorPickerPopup.d.ts +0 -8
- package/dist/src/components/inputs/shared/colorPickerPopup.js +0 -191
- package/dist/src/components/inputs/shared/dateTimePickerPopup.d.ts +0 -9
- package/dist/src/components/inputs/shared/dateTimePickerPopup.js +0 -128
- package/dist/src/components/inputs/shared/fieldWrapper.d.ts +0 -14
- package/dist/src/components/inputs/shared/fieldWrapper.js +0 -8
- package/dist/src/components/inputs/shared/index.d.ts +0 -5
- package/dist/src/components/inputs/shared/index.js +0 -5
- package/dist/src/components/inputs/shared/inputError.d.ts +0 -6
- package/dist/src/components/inputs/shared/inputError.js +0 -6
- package/dist/src/components/inputs/shared/inputInfo.d.ts +0 -5
- package/dist/src/components/inputs/shared/inputInfo.js +0 -5
- package/dist/src/components/inputs/shared/inputLabel.d.ts +0 -9
- package/dist/src/components/inputs/shared/inputLabel.js +0 -4
- package/dist/src/components/inputs/switch.d.ts +0 -11
- package/dist/src/components/inputs/switch.js +0 -14
- package/dist/src/components/inputs/tagInput.d.ts +0 -15
- package/dist/src/components/inputs/tagInput.js +0 -40
- package/dist/src/components/inputs/textarea.d.ts +0 -11
- package/dist/src/components/inputs/textarea.js +0 -36
- package/dist/src/components/inputs/toggle.d.ts +0 -14
- package/dist/src/components/inputs/toggle.js +0 -12
- package/dist/src/components/login/loginPage.d.ts +0 -2
- package/dist/src/components/login/loginPage.js +0 -18
- package/dist/src/components/logo/logo.d.ts +0 -5
- package/dist/src/components/logo/logo.js +0 -6
- package/dist/src/components/logo/logoSmall.d.ts +0 -1
- package/dist/src/components/logo/logoSmall.js +0 -6
- package/dist/src/components/markdownrender/markdownRender.d.ts +0 -6
- package/dist/src/components/markdownrender/markdownRender.js +0 -25
- package/dist/src/components/navbar/bubble.d.ts +0 -14
- package/dist/src/components/navbar/bubble.js +0 -8
- package/dist/src/components/navbar/navbar.d.ts +0 -34
- package/dist/src/components/navbar/navbar.js +0 -36
- package/dist/src/components/navbar/navbarDropdown.d.ts +0 -7
- package/dist/src/components/navbar/navbarDropdown.js +0 -19
- package/dist/src/components/navbar/navbarItem.d.ts +0 -11
- package/dist/src/components/navbar/navbarItem.js +0 -12
- package/dist/src/components/table/body.d.ts +0 -15
- package/dist/src/components/table/body.js +0 -116
- package/dist/src/components/table/format.d.ts +0 -1
- package/dist/src/components/table/format.js +0 -27
- package/dist/src/components/table/header.d.ts +0 -8
- package/dist/src/components/table/header.js +0 -47
- package/dist/src/components/table/menu.d.ts +0 -17
- package/dist/src/components/table/menu.js +0 -30
- package/dist/src/components/table/pagination.d.ts +0 -6
- package/dist/src/components/table/pagination.js +0 -86
- package/dist/src/components/table/table.d.ts +0 -14
- package/dist/src/components/table/table.js +0 -14
- package/dist/src/components/toast/toastItem.d.ts +0 -2
- package/dist/src/components/toast/toastItem.js +0 -38
- package/dist/src/components/toast/toaster.d.ts +0 -9
- package/dist/src/components/toast/toaster.js +0 -86
- package/dist/src/components/toggle/language.d.ts +0 -4
- package/dist/src/components/toggle/language.js +0 -29
- package/dist/src/components/toggle/theme.d.ts +0 -3
- package/dist/src/components/toggle/theme.js +0 -30
- package/dist/src/components/version/version.d.ts +0 -7
- package/dist/src/components/version/version.js +0 -12
- package/dist/src/components/vulnerability/constants.d.ts +0 -4
- package/dist/src/components/vulnerability/constants.js +0 -14
- package/dist/src/components/vulnerability/severityPill.d.ts +0 -8
- package/dist/src/components/vulnerability/severityPill.js +0 -8
- package/dist/src/hooks/useClickOutside.d.ts +0 -2
- package/dist/src/hooks/useClickOutside.js +0 -27
- package/dist/src/hooks/useDarkMode.d.ts +0 -1
- package/dist/src/hooks/useDarkMode.js +0 -17
- package/dist/src/hooks/useVisibility.d.ts +0 -4
- package/dist/src/hooks/useVisibility.js +0 -23
- package/dist/src/index.d.ts +0 -5
- package/dist/src/index.js +0 -5
- package/dist/src/scripts/buildCss.d.ts +0 -1
- package/dist/src/scripts/buildCss.js +0 -22
- package/dist/src/scripts/index.d.ts +0 -1
- package/dist/src/scripts/index.js +0 -1
- package/dist/src/scripts/rewriteAlias.d.ts +0 -1
- package/dist/src/scripts/rewriteAlias.js +0 -31
- package/dist/src/utils/LogoConsoleOutput/LogoConsoleOutput.d.ts +0 -1
- package/dist/src/utils/LogoConsoleOutput/LogoConsoleOutput.js +0 -44
- package/dist/src/utils/auth/callback.d.ts +0 -2
- package/dist/src/utils/auth/callback.js +0 -63
- package/dist/src/utils/auth/getDomain.d.ts +0 -2
- package/dist/src/utils/auth/getDomain.js +0 -6
- package/dist/src/utils/auth/login.d.ts +0 -3
- package/dist/src/utils/auth/login.js +0 -14
- package/dist/src/utils/auth/logout.d.ts +0 -3
- package/dist/src/utils/auth/logout.js +0 -18
- package/dist/src/utils/auth/token.d.ts +0 -3
- package/dist/src/utils/auth/token.js +0 -33
- package/dist/tailwind.config.d.ts +0 -3
- package/dist/tailwind.config.js +0 -7
- package/src/scripts/buildCss.ts +0 -25
- package/src/scripts/index.ts +0 -1
- package/src/scripts/rewriteAlias.ts +0 -39
- package/src/utils/LogoConsoleOutput/LogoConsoleOutput.ts +0 -61
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
//#region src/hooks/useClickOutside.ts
|
|
3
|
+
function useClickOutside(ref, callback) {
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
function handleClickOutside(event) {
|
|
6
|
+
if (ref.current && !ref.current.contains(event.target)) callback();
|
|
7
|
+
}
|
|
8
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
9
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
10
|
+
}, [ref, callback]);
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { useClickOutside as t };
|
package/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uibee",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Shared components, functions and hooks for reuse across Login projects",
|
|
5
5
|
"homepage": "https://github.com/Login-Linjeforening-for-IT/uibee#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -18,43 +18,42 @@
|
|
|
18
18
|
"exports": {
|
|
19
19
|
"./hooks": "./dist/src/hooks/index.js",
|
|
20
20
|
"./components": "./dist/src/components/index.js",
|
|
21
|
-
"./scripts": "./dist/src/scripts/index.js",
|
|
22
21
|
"./utils": "./dist/src/utils/index.js",
|
|
23
|
-
"./styles": "./dist/src/styles/index.js"
|
|
22
|
+
"./styles": "./dist/src/styles/index.js",
|
|
23
|
+
"./icons": "./dist/src/icons/index.js"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
|
-
"build
|
|
27
|
-
"
|
|
28
|
-
"build:rewrite": "node dist/src/scripts/rewriteAlias.js",
|
|
29
|
-
"build:ts": "tsc --declaration",
|
|
30
|
-
"build": "npm run build:clean && npm run build:css && npm run build:ts && npm run build:rewrite",
|
|
26
|
+
"build": "tsc --noEmit && tsdown",
|
|
27
|
+
"dev": "tsdown --watch",
|
|
31
28
|
"lint": "eslint ./src",
|
|
32
29
|
"lint:fix": "eslint --fix ./src"
|
|
33
30
|
},
|
|
34
31
|
"peerDependencies": {
|
|
35
|
-
"next": "^16.2.
|
|
36
|
-
"react": "^19.2.
|
|
32
|
+
"next": "^16.2.9",
|
|
33
|
+
"react": "^19.2.7"
|
|
37
34
|
},
|
|
38
35
|
"devDependencies": {
|
|
39
36
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
40
|
-
"@tailwindcss/postcss": "^4.
|
|
41
|
-
"@tailwindcss/typography": "^0.5.
|
|
42
|
-
"@
|
|
43
|
-
"@types/
|
|
37
|
+
"@tailwindcss/postcss": "^4.3.1",
|
|
38
|
+
"@tailwindcss/typography": "^0.5.20",
|
|
39
|
+
"@tsdown/css": "^0.22.3",
|
|
40
|
+
"@types/bun": "^1.3.14",
|
|
41
|
+
"@types/react": "^19.2.17",
|
|
44
42
|
"@types/react-dom": "^19.2.3",
|
|
45
|
-
"eslint": "^10.
|
|
46
|
-
"
|
|
47
|
-
"postcss": "^
|
|
48
|
-
"tailwindcss": "^4.
|
|
43
|
+
"eslint": "^10.5.0",
|
|
44
|
+
"postcss": "^8.5.15",
|
|
45
|
+
"postcss-import": "^16.1.1",
|
|
46
|
+
"tailwindcss": "^4.3.1",
|
|
47
|
+
"tsdown": "^0.22.3",
|
|
49
48
|
"typescript": "^6.0.3",
|
|
50
|
-
"typescript-eslint": "^8.
|
|
49
|
+
"typescript-eslint": "^8.61.1"
|
|
51
50
|
},
|
|
52
51
|
"dependencies": {
|
|
53
|
-
"lucide-react": "^1.
|
|
54
|
-
"react-dom": "19.2.
|
|
52
|
+
"lucide-react": "^1.21.0",
|
|
53
|
+
"react-dom": "^19.2.7",
|
|
55
54
|
"react-markdown": "^10.1.0",
|
|
56
55
|
"rehype-highlight": "^7.0.2",
|
|
57
56
|
"remark-gfm": "^4.0.1",
|
|
58
|
-
"utilbee": "^1.4.
|
|
57
|
+
"utilbee": "^1.4.7"
|
|
59
58
|
}
|
|
60
|
-
}
|
|
59
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ReactNode } from 'react'
|
|
2
|
-
import { CircleAlert,
|
|
2
|
+
import { CircleAlert, Info } from 'lucide-react'
|
|
3
|
+
import LeftBarPanel from '@components/container/leftBarPanel'
|
|
3
4
|
|
|
4
5
|
type AlertProps = {
|
|
5
6
|
children: ReactNode
|
|
@@ -7,29 +8,30 @@ type AlertProps = {
|
|
|
7
8
|
className?: string
|
|
8
9
|
}
|
|
9
10
|
|
|
11
|
+
const styles = {
|
|
12
|
+
warning: {
|
|
13
|
+
color: 'border-l-red-500',
|
|
14
|
+
iconClass: 'w-4 h-4 shrink-0 stroke-red-400 mr-3 mt-0.5',
|
|
15
|
+
Icon: CircleAlert,
|
|
16
|
+
},
|
|
17
|
+
info: {
|
|
18
|
+
color: 'border-l-blue-500',
|
|
19
|
+
iconClass: 'w-4 h-4 shrink-0 stroke-blue-400 mr-3 mt-0.5',
|
|
20
|
+
Icon: Info,
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
|
|
10
24
|
export default function Alert({
|
|
11
25
|
children,
|
|
12
26
|
variant = 'warning',
|
|
13
27
|
className = '',
|
|
14
28
|
}: AlertProps) {
|
|
15
|
-
const color = variant
|
|
16
|
-
: variant === 'info' ? 'bg-blue-600' :
|
|
17
|
-
'bg-gray-900'
|
|
18
|
-
|
|
19
|
-
const Icon = variant === 'warning' ? CircleAlert
|
|
20
|
-
: variant === 'info' ? Info :
|
|
21
|
-
TriangleAlert
|
|
29
|
+
const { color, iconClass, Icon } = styles[variant] ?? styles.warning
|
|
22
30
|
|
|
23
31
|
return (
|
|
24
|
-
<
|
|
25
|
-
className={
|
|
26
|
-
|
|
27
|
-
|
|
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>
|
|
32
|
+
<LeftBarPanel color={color} className={`flex items-start px-4 py-3 text-sm text-login-100 ${className}`}>
|
|
33
|
+
<Icon className={iconClass} />
|
|
34
|
+
<div className='leading-relaxed'>{children}</div>
|
|
35
|
+
</LeftBarPanel>
|
|
34
36
|
)
|
|
35
37
|
}
|
|
@@ -2,25 +2,31 @@ import Link from 'next/link'
|
|
|
2
2
|
import type { JSX } from 'react'
|
|
3
3
|
|
|
4
4
|
type ButtonProps = {
|
|
5
|
-
text
|
|
5
|
+
text?: string
|
|
6
6
|
className?: string
|
|
7
|
-
icon
|
|
7
|
+
icon?: string | JSX.Element
|
|
8
8
|
path?: string
|
|
9
9
|
type?: 'button' | 'submit' | 'reset'
|
|
10
10
|
variant?: 'primary' | 'secondary' | 'warning' | 'danger' | 'success' | 'info'
|
|
11
|
-
onClick?: (
|
|
11
|
+
onClick?: (e: React.MouseEvent) => void
|
|
12
12
|
disabled?: boolean
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const variants = {
|
|
16
|
-
primary:
|
|
17
|
-
secondary: 'bg-login-500
|
|
18
|
-
warning:
|
|
19
|
-
danger:
|
|
20
|
-
success:
|
|
21
|
-
info:
|
|
16
|
+
primary: 'bg-login text-white hover:brightness-110',
|
|
17
|
+
secondary: 'bg-login-500 text-login-50 hover:bg-login-400',
|
|
18
|
+
warning: 'bg-yellow-500 text-black hover:brightness-110',
|
|
19
|
+
danger: 'bg-red-600 text-white hover:brightness-110',
|
|
20
|
+
success: 'bg-green-600 text-white hover:brightness-110',
|
|
21
|
+
info: 'bg-blue-600 text-white hover:brightness-110',
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
const base = 'cursor-pointer px-4 rounded-md h-9 flex items-center justify-center gap-2 select-none text-sm font-medium transition-all duration-150 w-fit'
|
|
25
|
+
const disabledCls = 'opacity-40 cursor-not-allowed pointer-events-none'
|
|
26
|
+
|
|
27
|
+
const iconWrap = (icon: ButtonProps['icon']) =>
|
|
28
|
+
icon ? <span className='[&>svg]:w-4 [&>svg]:h-4 flex items-center'>{icon}</span> : null
|
|
29
|
+
|
|
24
30
|
export default function Button({
|
|
25
31
|
text,
|
|
26
32
|
className,
|
|
@@ -29,9 +35,10 @@ export default function Button({
|
|
|
29
35
|
variant = 'primary',
|
|
30
36
|
type,
|
|
31
37
|
onClick,
|
|
32
|
-
disabled
|
|
38
|
+
disabled,
|
|
33
39
|
}: ButtonProps) {
|
|
34
|
-
const
|
|
40
|
+
const color = variants[variant]
|
|
41
|
+
const cls = `${base} ${color} ${disabled ? disabledCls : ''} ${className || ''}`
|
|
35
42
|
|
|
36
43
|
if (!path) {
|
|
37
44
|
return (
|
|
@@ -40,42 +47,28 @@ export default function Button({
|
|
|
40
47
|
disabled={disabled}
|
|
41
48
|
onClick={onClick}
|
|
42
49
|
aria-label={text}
|
|
43
|
-
className={
|
|
44
|
-
${bg} cursor-pointer px-4 rounded-md min-h-8 h-8 flex
|
|
45
|
-
justify-evenly items-center gap-2 select-none
|
|
46
|
-
focus:outline-none border-0 outline w-fit ${className}
|
|
47
|
-
`}
|
|
50
|
+
className={cls}
|
|
48
51
|
>
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
{iconWrap(icon)}
|
|
53
|
+
{text}
|
|
51
54
|
</button>
|
|
52
55
|
)
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
if (disabled) {
|
|
56
59
|
return (
|
|
57
|
-
<div
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
justify-evenly items-center gap-2 select-none ${className}
|
|
61
|
-
`}
|
|
62
|
-
>
|
|
63
|
-
<h1 className='font-bold'>{icon || ''}</h1>
|
|
64
|
-
<h1 className='min-w-fit w-fit'>{text}</h1>
|
|
60
|
+
<div className={cls}>
|
|
61
|
+
{iconWrap(icon)}
|
|
62
|
+
{text}
|
|
65
63
|
</div>
|
|
66
64
|
)
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
return (
|
|
70
|
-
<Link
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
${bg} cursor-pointer px-4 rounded-md h-8 flex w-fit
|
|
74
|
-
justify-evenly items-center gap-2 select-none ${className}
|
|
75
|
-
`}
|
|
76
|
-
>
|
|
77
|
-
<h1 className='font-bold'>{icon || ''}</h1>
|
|
78
|
-
<h1 className='min-w-fit w-fit'>{text}</h1>
|
|
68
|
+
<Link href={path} className={`${base} ${color} ${className || ''}`}>
|
|
69
|
+
{iconWrap(icon)}
|
|
70
|
+
{text}
|
|
79
71
|
</Link>
|
|
80
72
|
)
|
|
81
73
|
}
|
|
74
|
+
|
|
@@ -25,12 +25,12 @@ export default function ConfirmPopup({
|
|
|
25
25
|
}: ConfirmPopupProps) {
|
|
26
26
|
if (!isOpen) return null
|
|
27
27
|
|
|
28
|
-
const
|
|
28
|
+
const confirmClass =
|
|
29
29
|
variant === 'danger'
|
|
30
|
-
? 'bg-red-
|
|
30
|
+
? 'bg-red-600 hover:brightness-110 text-white'
|
|
31
31
|
: variant === 'warning'
|
|
32
|
-
? 'bg-yellow-
|
|
33
|
-
: 'bg-login
|
|
32
|
+
? 'bg-yellow-500 hover:brightness-110 text-black'
|
|
33
|
+
: 'bg-login hover:brightness-110 text-white'
|
|
34
34
|
|
|
35
35
|
return (
|
|
36
36
|
<div
|
|
@@ -40,7 +40,7 @@ export default function ConfirmPopup({
|
|
|
40
40
|
className='fixed inset-0 z-50 flex items-center justify-center'
|
|
41
41
|
onClick={onCancel}
|
|
42
42
|
>
|
|
43
|
-
<div className='absolute inset-0 bg-black/
|
|
43
|
+
<div className='absolute inset-0 bg-black/50 backdrop-blur-sm' />
|
|
44
44
|
|
|
45
45
|
<div
|
|
46
46
|
className='
|
|
@@ -50,35 +50,34 @@ export default function ConfirmPopup({
|
|
|
50
50
|
'
|
|
51
51
|
onClick={(e) => e.stopPropagation()}
|
|
52
52
|
>
|
|
53
|
-
<div className='flex items-
|
|
53
|
+
<div className='flex items-start gap-3'>
|
|
54
54
|
{variant !== 'default' && (
|
|
55
55
|
<TriangleAlert
|
|
56
|
-
className={`w-
|
|
56
|
+
className={`w-5 h-5 shrink-0 mt-0.5 ${variant === 'danger' ? 'stroke-red-400' : 'stroke-yellow-400'}`}
|
|
57
57
|
/>
|
|
58
58
|
)}
|
|
59
59
|
<h2
|
|
60
60
|
id='confirm-popup-header'
|
|
61
|
-
className='text-login-50 text-
|
|
61
|
+
className='text-login-50 text-base font-semibold leading-snug'
|
|
62
62
|
>
|
|
63
63
|
{header}
|
|
64
64
|
</h2>
|
|
65
65
|
</div>
|
|
66
66
|
|
|
67
67
|
{description && (
|
|
68
|
-
<p className='text-login-
|
|
68
|
+
<p className='text-login-200 text-sm leading-relaxed'>
|
|
69
69
|
{description}
|
|
70
70
|
</p>
|
|
71
71
|
)}
|
|
72
72
|
|
|
73
|
-
<div className='flex justify-end gap-
|
|
73
|
+
<div className='flex justify-end gap-2 mt-1'>
|
|
74
74
|
<button
|
|
75
75
|
type='button'
|
|
76
76
|
onClick={onCancel}
|
|
77
77
|
className='
|
|
78
78
|
cursor-pointer px-4 py-1.5 rounded-md text-sm font-medium
|
|
79
|
-
bg-login-
|
|
80
|
-
|
|
81
|
-
transition-colors duration-150
|
|
79
|
+
bg-login-600 hover:bg-login-500 text-login-100
|
|
80
|
+
transition-colors duration-150 select-none
|
|
82
81
|
'
|
|
83
82
|
>
|
|
84
83
|
{cancelText}
|
|
@@ -88,10 +87,9 @@ export default function ConfirmPopup({
|
|
|
88
87
|
type='button'
|
|
89
88
|
onClick={onConfirm}
|
|
90
89
|
className={`
|
|
91
|
-
cursor-pointer px-4 py-1.5 rounded-md text-sm font-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
${confirmBg}
|
|
90
|
+
cursor-pointer px-4 py-1.5 rounded-md text-sm font-medium
|
|
91
|
+
transition-all duration-150 select-none
|
|
92
|
+
${confirmClass}
|
|
95
93
|
`}
|
|
96
94
|
>
|
|
97
95
|
{confirmText}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext, useState, type ReactNode } from 'react'
|
|
4
|
+
import { ChevronDown } from 'lucide-react'
|
|
5
|
+
|
|
6
|
+
const GroupContext = createContext(false)
|
|
7
|
+
|
|
8
|
+
type AccordionProps = {
|
|
9
|
+
title: ReactNode
|
|
10
|
+
children: ReactNode
|
|
11
|
+
defaultOpen?: boolean
|
|
12
|
+
className?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type AccordionGroupProps = {
|
|
16
|
+
children: ReactNode
|
|
17
|
+
className?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function AccordionGroup({ children, className = '' }: AccordionGroupProps) {
|
|
21
|
+
return (
|
|
22
|
+
<GroupContext.Provider value={true}>
|
|
23
|
+
<div className={`rounded-lg border border-login-500/40 overflow-hidden divide-y divide-login-500/25 ${className}`}>
|
|
24
|
+
{children}
|
|
25
|
+
</div>
|
|
26
|
+
</GroupContext.Provider>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default function Accordion({ title, children, defaultOpen = false, className = '' }: AccordionProps) {
|
|
31
|
+
const [open, setOpen] = useState(defaultOpen)
|
|
32
|
+
const grouped = useContext(GroupContext)
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className={grouped ? className : `rounded-lg border border-login-500/40 overflow-hidden ${className}`}>
|
|
36
|
+
<button
|
|
37
|
+
type='button'
|
|
38
|
+
onClick={() => setOpen((o) => !o)}
|
|
39
|
+
className='flex w-full cursor-pointer items-center justify-between bg-login-800 px-4 py-3 text-left transition-colors duration-150 hover:bg-login-700 select-none'
|
|
40
|
+
>
|
|
41
|
+
<div className='text-sm font-medium text-login-50'>{title}</div>
|
|
42
|
+
<ChevronDown
|
|
43
|
+
size={16}
|
|
44
|
+
className={`shrink-0 text-login-300 transition-transform duration-200 ${open ? 'rotate-180' : ''}`}
|
|
45
|
+
/>
|
|
46
|
+
</button>
|
|
47
|
+
<div className={`overflow-hidden bg-login-900 transition-all duration-200 ${open ? 'max-h-[9999px]' : 'max-h-0'}`}>
|
|
48
|
+
<div className='px-4 py-3 text-sm text-login-100'>
|
|
49
|
+
{children}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
type GlassCardProps = {
|
|
4
|
+
children: ReactNode
|
|
5
|
+
className?: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default function GlassCard({ children, className = '' }: GlassCardProps) {
|
|
9
|
+
return (
|
|
10
|
+
<section className={`rounded-xl border border-login-500/30 bg-login-800 ${className}`}>
|
|
11
|
+
{children}
|
|
12
|
+
</section>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ElementType } from 'react'
|
|
2
|
+
|
|
3
|
+
const tones = {
|
|
4
|
+
amber: 'bg-amber-500/10 text-amber-400',
|
|
5
|
+
blue: 'bg-sky-500/10 text-sky-400',
|
|
6
|
+
emerald: 'bg-emerald-500/10 text-emerald-400',
|
|
7
|
+
rose: 'bg-rose-500/10 text-rose-400',
|
|
8
|
+
slate: 'bg-login-600 text-login-100',
|
|
9
|
+
violet: 'bg-violet-500/10 text-violet-400',
|
|
10
|
+
orange: 'bg-login/10 text-login',
|
|
11
|
+
} as const
|
|
12
|
+
|
|
13
|
+
type IconBubbleProps = {
|
|
14
|
+
icon: ElementType
|
|
15
|
+
tone?: keyof typeof tones
|
|
16
|
+
size?: 'sm' | 'md' | 'lg'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type IconBubbleTone = keyof typeof tones
|
|
20
|
+
|
|
21
|
+
export default function IconBubble({ icon: Icon, tone = 'slate', size = 'md' }: IconBubbleProps) {
|
|
22
|
+
const sizeClass = size === 'sm' ? 'h-7 w-7' : size === 'lg' ? 'h-11 w-11' : 'h-9 w-9'
|
|
23
|
+
const iconSize = size === 'sm' ? 'h-3.5 w-3.5' : size === 'lg' ? 'h-5 w-5' : 'h-4 w-4'
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className={`flex shrink-0 items-center justify-center rounded-lg ${sizeClass} ${tones[tone]}`}>
|
|
27
|
+
<Icon className={iconSize} />
|
|
28
|
+
</div>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
type LeftBarPanelProps = {
|
|
4
|
+
color: string
|
|
5
|
+
children: ReactNode
|
|
6
|
+
className?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default function LeftBarPanel({ color, children, className = '' }: LeftBarPanelProps) {
|
|
10
|
+
return (
|
|
11
|
+
<div className={`bg-login-500/50 border border-login-500/30 border-l-2 rounded-sm ${color} ${className}`}>
|
|
12
|
+
{children}
|
|
13
|
+
</div>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
type PulseDotVariant = 'online' | 'offline' | 'warning' | 'unknown'
|
|
2
|
+
|
|
3
|
+
type PulseDotProps = {
|
|
4
|
+
variant?: PulseDotVariant
|
|
5
|
+
size?: 'sm' | 'md' | 'lg'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const variantColor: Record<PulseDotVariant, string> = {
|
|
9
|
+
online: 'bg-green-500',
|
|
10
|
+
offline: 'bg-red-500',
|
|
11
|
+
warning: 'bg-login',
|
|
12
|
+
unknown: 'bg-login-400',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const sizeClass = {
|
|
16
|
+
sm: 'w-1.5 h-1.5',
|
|
17
|
+
md: 'w-2 h-2',
|
|
18
|
+
lg: 'w-3 h-3',
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default function PulseDot({ variant = 'online', size = 'md' }: PulseDotProps) {
|
|
22
|
+
const color = variantColor[variant]
|
|
23
|
+
const dotSize = sizeClass[size]
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className={`relative grid place-items-center ${dotSize}`}>
|
|
27
|
+
<span className={`absolute inline-flex h-full w-full rounded-full ${color} opacity-50 animate-ping`} />
|
|
28
|
+
<span className={`relative inline-flex rounded-full ${dotSize} ${color}`} />
|
|
29
|
+
</div>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ElementType } from 'react'
|
|
2
|
+
import GlassCard from './glassCard'
|
|
3
|
+
import IconBubble, { type IconBubbleTone } from './iconBubble'
|
|
4
|
+
|
|
5
|
+
type StatCardProps = {
|
|
6
|
+
label: string
|
|
7
|
+
value: string
|
|
8
|
+
icon: ElementType
|
|
9
|
+
tone?: IconBubbleTone
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function StatCard({ label, value, icon, tone = 'slate' }: StatCardProps) {
|
|
13
|
+
return (
|
|
14
|
+
<GlassCard className='p-4'>
|
|
15
|
+
<div className='mb-3 flex items-center gap-3'>
|
|
16
|
+
<IconBubble icon={icon} tone={tone} />
|
|
17
|
+
<span className='text-sm font-medium text-login-200'>{label}</span>
|
|
18
|
+
</div>
|
|
19
|
+
<div className='truncate text-lg font-semibold text-login-50' title={value}>
|
|
20
|
+
{value}
|
|
21
|
+
</div>
|
|
22
|
+
</GlassCard>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, type ReactNode } from 'react'
|
|
4
|
+
|
|
5
|
+
type Tab = {
|
|
6
|
+
id: string
|
|
7
|
+
label: ReactNode
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type TabsProps = {
|
|
11
|
+
tabs: Tab[]
|
|
12
|
+
defaultTab?: string
|
|
13
|
+
activeTab?: string
|
|
14
|
+
onTabChange?: (id: string) => void
|
|
15
|
+
children: ReactNode
|
|
16
|
+
className?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type TabPanelProps = {
|
|
20
|
+
id: string
|
|
21
|
+
activeTab: string
|
|
22
|
+
children: ReactNode
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function Tabs({ tabs, defaultTab, activeTab: controlledTab, onTabChange, children, className = '' }: TabsProps) {
|
|
26
|
+
const [internalTab, setInternalTab] = useState(defaultTab ?? tabs[0]?.id ?? '')
|
|
27
|
+
const activeTab = controlledTab ?? internalTab
|
|
28
|
+
|
|
29
|
+
function handleTabChange(id: string) {
|
|
30
|
+
if (!controlledTab) setInternalTab(id)
|
|
31
|
+
onTabChange?.(id)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className={className}>
|
|
36
|
+
<div className='flex gap-1 rounded-md bg-login-500/50 border border-login-500/40 p-1 w-fit'>
|
|
37
|
+
{tabs.map((tab) => (
|
|
38
|
+
<button
|
|
39
|
+
key={tab.id}
|
|
40
|
+
type='button'
|
|
41
|
+
onClick={() => handleTabChange(tab.id)}
|
|
42
|
+
className={`
|
|
43
|
+
px-3 py-1.5 rounded text-sm font-medium transition-all duration-150 cursor-pointer select-none
|
|
44
|
+
${activeTab === tab.id
|
|
45
|
+
? 'bg-login text-white shadow-sm'
|
|
46
|
+
: 'text-login-200 hover:text-login-50 hover:bg-login-600'
|
|
47
|
+
}
|
|
48
|
+
`}
|
|
49
|
+
>
|
|
50
|
+
{tab.label}
|
|
51
|
+
</button>
|
|
52
|
+
))}
|
|
53
|
+
</div>
|
|
54
|
+
<div className='mt-3'>
|
|
55
|
+
{children}
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function TabPanel({ id, activeTab, children }: TabPanelProps) {
|
|
62
|
+
if (activeTab !== id) return null
|
|
63
|
+
return <div>{children}</div>
|
|
64
|
+
}
|