uibee 1.6.12 → 2.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 +0 -1
- package/dist/globals.css +710 -8
- package/dist/src/components/index.d.ts +10 -2
- package/dist/src/components/index.js +13 -2
- package/dist/src/components/login/loginPage.js +5 -6
- package/dist/src/components/logo/logo.d.ts +5 -0
- package/dist/src/components/logo/logo.js +6 -0
- package/dist/src/components/logo/logoSmall.d.ts +1 -0
- package/dist/src/components/logo/logoSmall.js +6 -0
- package/dist/src/components/navbar/navbar.d.ts +10 -0
- package/dist/src/components/navbar/navbar.js +36 -0
- package/dist/src/components/navbar/navbarDropdown.d.ts +6 -0
- package/dist/src/components/navbar/navbarDropdown.js +21 -0
- package/dist/src/components/navbar/navbarItem.d.ts +11 -0
- package/dist/src/components/navbar/navbarItem.js +12 -0
- package/dist/src/components/toggle/language.d.ts +4 -0
- package/dist/src/components/toggle/language.js +29 -0
- package/dist/src/components/toggle/theme.d.ts +3 -0
- package/dist/src/components/toggle/theme.js +30 -0
- package/dist/src/components/version/version.d.ts +7 -0
- package/dist/src/components/version/version.js +12 -0
- package/dist/src/scripts/rewriteAlias.js +17 -17
- package/package.json +8 -6
- package/src/components/index.ts +17 -3
- package/src/components/login/loginPage.tsx +8 -14
- package/src/components/logo/logo.tsx +45 -0
- package/src/components/logo/logoSmall.tsx +19 -0
- package/src/components/navbar/navbar.tsx +130 -0
- package/src/components/navbar/navbarDropdown.tsx +77 -0
- package/src/components/navbar/navbarItem.tsx +47 -0
- package/src/components/toggle/language.tsx +45 -0
- package/src/components/toggle/theme.tsx +150 -0
- package/src/components/version/version.tsx +34 -0
- package/src/globals.css +47 -0
- package/src/scripts/rewriteAlias.ts +19 -17
- package/src/types/components.d.ts +4 -0
- package/tsconfig.json +8 -2
- package/dist/images/logo-tekst-white.svg +0 -171
- package/dist/src/types/svg.d.ts +0 -4
- package/dist/src/types/svg.js +0 -1
- package/images/logo-tekst-white.svg +0 -171
- package/src/types/svg.ts +0 -4
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react'
|
|
4
|
+
import Link from 'next/link'
|
|
5
|
+
import LogoSmall from '@components/logo/logoSmall'
|
|
6
|
+
import LanguageToggle from '@components/toggle/language'
|
|
7
|
+
import ThemeToggle from '@components/toggle/theme'
|
|
8
|
+
import { Language } from 'uibee/components'
|
|
9
|
+
import { LogOut } from 'lucide-react'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
function hamburgerStyle (isOpen: boolean, isSecond?: boolean) {
|
|
13
|
+
return `bg-login-50 h-0.5 absolute w-8 transition-all duration-[400ms] left-2 ${
|
|
14
|
+
isOpen
|
|
15
|
+
? `top-6 ${isSecond ? 'rotate-45' : '-rotate-45'}`
|
|
16
|
+
: isSecond ? 'top-7' : 'top-4'
|
|
17
|
+
}`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type NavbarProps = {
|
|
21
|
+
lang: Language
|
|
22
|
+
onlyLogo?: boolean
|
|
23
|
+
theme: string
|
|
24
|
+
token: string | null
|
|
25
|
+
children: React.ReactNode
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default function Navbar({ lang, onlyLogo, token, children }: NavbarProps) {
|
|
29
|
+
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className={`${isMobileMenuOpen ? 'bg-[#181818f0]' : 'bg-[#18181899]'} backdrop-blur-xl fixed top-0 z-900 w-full`}>
|
|
33
|
+
<div className={`flex w-full max-w-6xl m-auto p-2 transition duration-500 800px:justify-between 800px:p-4 ${
|
|
34
|
+
isMobileMenuOpen ? 'h-screen bg-login-900/20 800px:h-20' : ''
|
|
35
|
+
}`}>
|
|
36
|
+
{/* Logo */}
|
|
37
|
+
<div className='block h-12 p-1 800px:p-0'>
|
|
38
|
+
<Link
|
|
39
|
+
href='/'
|
|
40
|
+
onClick={() => setIsMobileMenuOpen(false)}
|
|
41
|
+
>
|
|
42
|
+
<LogoSmall />
|
|
43
|
+
</Link>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
{onlyLogo ? null : (
|
|
47
|
+
<>
|
|
48
|
+
{/* Desktop Navigation */}
|
|
49
|
+
<nav className='hidden 800px:flex 800px:justify-between 800px:items-center 800px:w-fill max-w-[50rem]'>
|
|
50
|
+
{children}
|
|
51
|
+
</nav>
|
|
52
|
+
|
|
53
|
+
{/* Controls */}
|
|
54
|
+
<nav className='flex w-[calc(100vw-8rem)] justify-end h-12 800px:w-fit'>
|
|
55
|
+
<ThemeToggle />
|
|
56
|
+
<LanguageToggle language={lang} />
|
|
57
|
+
<AuthButton token={token} />
|
|
58
|
+
</nav>
|
|
59
|
+
|
|
60
|
+
{/* Mobile Menu Button */}
|
|
61
|
+
<button
|
|
62
|
+
className='w-12 h-12 relative cursor-pointer bg-none border-none 800px:hidden'
|
|
63
|
+
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
|
64
|
+
>
|
|
65
|
+
<div className={hamburgerStyle(isMobileMenuOpen)} />
|
|
66
|
+
<div className={hamburgerStyle(isMobileMenuOpen, true)} />
|
|
67
|
+
</button>
|
|
68
|
+
|
|
69
|
+
{/* Mobile Navigation */}
|
|
70
|
+
<nav className={`fixed top-16 w-[calc(100%-2rem)] max-w-[35rem] mx-auto left-0 right-0 800px:hidden
|
|
71
|
+
transition-all duration-500 ease-in-out overflow-hidden
|
|
72
|
+
${isMobileMenuOpen ? 'max-h-[calc(100vh-4rem)] opacity-100' : 'max-h-0 opacity-0'}`}
|
|
73
|
+
onClick={() => setIsMobileMenuOpen(false)}>
|
|
74
|
+
{React.Children.map(children, (child, index) => (
|
|
75
|
+
<div
|
|
76
|
+
key={index}
|
|
77
|
+
className={`transition-all duration-500 ease-out ${
|
|
78
|
+
isMobileMenuOpen
|
|
79
|
+
? 'opacity-100 transform translate-y-0'
|
|
80
|
+
: 'opacity-0 transform -translate-y-4'
|
|
81
|
+
}`}
|
|
82
|
+
style={{
|
|
83
|
+
transitionDelay: isMobileMenuOpen ? `${index * 80}ms` : '0ms'
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
{child}
|
|
87
|
+
</div>
|
|
88
|
+
))}
|
|
89
|
+
</nav>
|
|
90
|
+
</>
|
|
91
|
+
)}
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function AuthButton({ token }: { token: string | null }) {
|
|
98
|
+
return (
|
|
99
|
+
<div className='rounded-[0.3rem] hover:bg-[#6464641a] h-12 w-12'>
|
|
100
|
+
{token ? (
|
|
101
|
+
<Link
|
|
102
|
+
href='/api/logout'
|
|
103
|
+
prefetch={false}
|
|
104
|
+
onClick={(e) => {
|
|
105
|
+
e.preventDefault()
|
|
106
|
+
window.location.href = '/api/logout'
|
|
107
|
+
}}
|
|
108
|
+
className='grid items-center justify-center h-full w-full'
|
|
109
|
+
>
|
|
110
|
+
<LogOut size={24} />
|
|
111
|
+
</Link>
|
|
112
|
+
) : (
|
|
113
|
+
<Link
|
|
114
|
+
href='/api/login'
|
|
115
|
+
className='grid items-center justify-center h-full w-full'
|
|
116
|
+
>
|
|
117
|
+
<div className={`relative w-[30px] h-5
|
|
118
|
+
before:content-[""] before:absolute before:top-0 before:left-1/2 before:-translate-x-1/2
|
|
119
|
+
before:w-[10px] before:h-[10px] before:border-2 before:border-login-50
|
|
120
|
+
before:rounded-full before:bg-transparent
|
|
121
|
+
after:content-[""] after:absolute after:bottom-0 after:left-1/2 after:-translate-x-1/2
|
|
122
|
+
after:w-[18px] after:h-2 after:border-2 after:border-login-50
|
|
123
|
+
after:rounded-t-[22px] after:border-b-0 after:bg-transparent
|
|
124
|
+
`}
|
|
125
|
+
/>
|
|
126
|
+
</Link>
|
|
127
|
+
)}
|
|
128
|
+
</div>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { ChevronDown } from 'lucide-react'
|
|
4
|
+
import React, { ReactNode, useRef, useState } from 'react'
|
|
5
|
+
|
|
6
|
+
export type NavDropdownProps = {
|
|
7
|
+
children: ReactNode
|
|
8
|
+
title: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function NavDropdown({ children, title }: NavDropdownProps) {
|
|
12
|
+
const [isMobileDropdownOpen, setIsMobileDropdownOpen] = useState(false)
|
|
13
|
+
const navItemRef = useRef<HTMLDivElement>(null)
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<>
|
|
17
|
+
{/* Desktop Dropdown */}
|
|
18
|
+
<div className={'relative group hidden 800px:block'}>
|
|
19
|
+
<div className='outline-none' tabIndex={0} ref={navItemRef}>
|
|
20
|
+
<div className={`list-none no-underline text-base leading-4 p-3 font-bold cursor-pointer flex flex-row items-center
|
|
21
|
+
transition-colors`
|
|
22
|
+
}>
|
|
23
|
+
{title}
|
|
24
|
+
<ChevronDown className={'w-6 h-6 stroke-login ml-1 text-2xl transition-transform duration-300 ease-in-out'} />
|
|
25
|
+
</div>
|
|
26
|
+
<div className={`absolute pt-2 -ml-4 -translate-y-4 opacity-0 pointer-events-none
|
|
27
|
+
transition-all duration-200 ease-in-out z-10
|
|
28
|
+
group-hover:opacity-100 group-hover:pointer-events-auto group-hover:translate-y-0
|
|
29
|
+
group-focus-within:opacity-100 group-focus-within:pointer-events-auto group-focus-within:translate-y-0`}
|
|
30
|
+
>
|
|
31
|
+
<ul className={'p-3 px-6 pb-4 rounded-[0.4rem] shadow-[0_0.1rem_0.5rem_rgba(3,3,3,0.5)] bg-login-700/98'}>
|
|
32
|
+
{React.Children.map(children, (child, index) => (
|
|
33
|
+
<div
|
|
34
|
+
key={index}
|
|
35
|
+
onClick={() => navItemRef.current?.blur()}
|
|
36
|
+
className='group dropdown'
|
|
37
|
+
>
|
|
38
|
+
{child}
|
|
39
|
+
</div>
|
|
40
|
+
))}
|
|
41
|
+
</ul>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
{/* Mobile Dropdown */}
|
|
47
|
+
<div className={'block 800px:hidden'}>
|
|
48
|
+
<button
|
|
49
|
+
className={'bg-none border-none cursor-pointer w-full text-left'}
|
|
50
|
+
onClick={(e) => {
|
|
51
|
+
e.stopPropagation()
|
|
52
|
+
setIsMobileDropdownOpen(!isMobileDropdownOpen)
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
<li className={`list-none no-underline text-2xl leading-6 overflow-hidden
|
|
56
|
+
w-full pl-4 pr-4 rounded-[0.3rem] transition-all duration-[600ms]
|
|
57
|
+
flex items-center gap-2 opacity-100 min-h-16 py-5 `}
|
|
58
|
+
>
|
|
59
|
+
<span>{title}</span>
|
|
60
|
+
<ChevronDown className={`w-6 h-6 transition-transform duration-400 flex-shrink-0
|
|
61
|
+
${isMobileDropdownOpen ? 'rotate-180' : ''}`}
|
|
62
|
+
/>
|
|
63
|
+
</li>
|
|
64
|
+
</button>
|
|
65
|
+
<div className={`list-none no-underline text-xl px-6 ${isMobileDropdownOpen ? 'pb-4' : ''}`}>
|
|
66
|
+
{React.Children.map(children, (child, index) => (
|
|
67
|
+
<div key={index} className={`leading-6 transition-all duration-500 group dropdown
|
|
68
|
+
${isMobileDropdownOpen ? 'h-11 opacity-100' : 'h-0 opacity-0'}
|
|
69
|
+
`}>
|
|
70
|
+
{child}
|
|
71
|
+
</div>
|
|
72
|
+
))}
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { ArrowUpRight } from 'lucide-react'
|
|
4
|
+
import Link from 'next/link'
|
|
5
|
+
import { ReactNode } from 'react'
|
|
6
|
+
|
|
7
|
+
export type NavItemProps = {
|
|
8
|
+
href: string
|
|
9
|
+
children: ReactNode
|
|
10
|
+
external?: boolean
|
|
11
|
+
target?: string
|
|
12
|
+
rel?: string
|
|
13
|
+
title?: string
|
|
14
|
+
icon?: ReactNode
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const commonStyling = 'list-none flex no-underline items-center gap-2 whitespace-nowrap cursor-pointer'
|
|
18
|
+
|
|
19
|
+
export default function NavItem({ href, children, external = false, target, rel, title, icon }: NavItemProps) {
|
|
20
|
+
const linkProps = { href, target, rel, title }
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<>
|
|
24
|
+
{/* Desktop version */}
|
|
25
|
+
<Link {...linkProps} className='hidden 800px:block'>
|
|
26
|
+
<li className={`${commonStyling} text-base leading-4 p-3 font-bold transition-colors link-corner-hover
|
|
27
|
+
group-[.dropdown]:p-2.5 group-[.dropdown]:pr-3 group-[.dropdown]:pl-1`
|
|
28
|
+
}>
|
|
29
|
+
{icon}
|
|
30
|
+
{children}
|
|
31
|
+
{external && <ArrowUpRight className='w-6 h-6 stroke-login' />}
|
|
32
|
+
</li>
|
|
33
|
+
</Link>
|
|
34
|
+
|
|
35
|
+
{/* Mobile version */}
|
|
36
|
+
<Link {...linkProps} className='block 800px:hidden'>
|
|
37
|
+
<li className={`${commonStyling} text-2xl leading-6 overflow-hidden w-auto pl-4 rounded-[0.3rem] transition-all
|
|
38
|
+
duration-[600ms] opacity-100 h-16 py-5 group-[.dropdown]:p-0 group-[.dropdown]:text-lg group-[.dropdown]:h-auto
|
|
39
|
+
group-[.dropdown]:py-2.5 group-[.dropdown]:pl-4`
|
|
40
|
+
}>
|
|
41
|
+
{children}
|
|
42
|
+
{external && <ArrowUpRight className='w-6 h-6 stroke-login' />}
|
|
43
|
+
</li>
|
|
44
|
+
</Link>
|
|
45
|
+
</>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
4
|
+
import { getCookie, setCookie } from '@utils/cookies/cookies'
|
|
5
|
+
import { useRouter } from 'next/navigation'
|
|
6
|
+
import { Language } from 'uibee/components'
|
|
7
|
+
import { Globe } from 'lucide-react'
|
|
8
|
+
|
|
9
|
+
export default function LanguageToggle({language}: {language: Language}) {
|
|
10
|
+
const [lang, setLang] = useState<Language>(language)
|
|
11
|
+
const [jump, setJump] = useState(false)
|
|
12
|
+
|
|
13
|
+
const router = useRouter()
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const savedLang = getCookie('lang') as Language
|
|
17
|
+
if (savedLang) {
|
|
18
|
+
setLang(savedLang)
|
|
19
|
+
}
|
|
20
|
+
}, [])
|
|
21
|
+
|
|
22
|
+
function handleClick() {
|
|
23
|
+
const newLang = lang === 'no' ? 'en' : 'no'
|
|
24
|
+
setCookie('lang', newLang)
|
|
25
|
+
setLang(newLang)
|
|
26
|
+
language = newLang
|
|
27
|
+
setJump(true)
|
|
28
|
+
setTimeout(() => setJump(false), 400)
|
|
29
|
+
router.refresh()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const buttonClasses = `cursor-pointer p-2 leading-8 text-base w-[4.3rem] text-center rounded
|
|
33
|
+
bg-transparent border-none hover:bg-gray-400/10 flex flex-row items-center justify-center gap-1`
|
|
34
|
+
|
|
35
|
+
return(
|
|
36
|
+
<button
|
|
37
|
+
value={lang}
|
|
38
|
+
onClick={handleClick}
|
|
39
|
+
className={buttonClasses}
|
|
40
|
+
>
|
|
41
|
+
<Globe className={`text-xl leading-8 -mt-0.5 ${jump ? 'animate-jump' : ''}`}/>
|
|
42
|
+
{' ' + lang}
|
|
43
|
+
</button>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
4
|
+
import { getCookie, setCookie } from '@utils/cookies/cookies'
|
|
5
|
+
import { useRouter } from 'next/navigation'
|
|
6
|
+
|
|
7
|
+
export default function ThemeToggle({className}: {className?: string}) {
|
|
8
|
+
const router = useRouter()
|
|
9
|
+
const [theme, setTheme] = useState<'dark' | 'light'>('dark')
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const savedTheme = getCookie('theme') as 'dark' | 'light'
|
|
13
|
+
if (savedTheme) {
|
|
14
|
+
setTheme(savedTheme)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
document.documentElement.classList.remove('dark', 'light')
|
|
18
|
+
document.documentElement.classList.add(theme)
|
|
19
|
+
}, [theme])
|
|
20
|
+
|
|
21
|
+
function toggleTheme() {
|
|
22
|
+
const newTheme = theme === 'dark' ? 'light' : 'dark'
|
|
23
|
+
setCookie('theme', newTheme)
|
|
24
|
+
setTheme(newTheme)
|
|
25
|
+
router.refresh()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className={`grid place-items-center justify-end rounded-md hover:bg-login-300/20 w-fit ${className}`}>
|
|
30
|
+
<label className='cursor-pointer'>
|
|
31
|
+
<input
|
|
32
|
+
type='checkbox'
|
|
33
|
+
checked={theme === 'light'}
|
|
34
|
+
onChange={toggleTheme}
|
|
35
|
+
className='sr-only'
|
|
36
|
+
/>
|
|
37
|
+
<ThemeIcon theme={theme} />
|
|
38
|
+
</label>
|
|
39
|
+
</div>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function ThemeIcon({ theme }: { theme: 'dark' | 'light' }) {
|
|
44
|
+
const sunrayClass = `fill-white transition-opacity duration-400 ${
|
|
45
|
+
theme === 'light' ? 'opacity-0' : 'opacity-100'
|
|
46
|
+
}`
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<svg
|
|
50
|
+
className='h-12 p-3'
|
|
51
|
+
viewBox='0 0 100 100'
|
|
52
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
53
|
+
>
|
|
54
|
+
{/* Sun */}
|
|
55
|
+
<defs>
|
|
56
|
+
<mask id='theme-toggle_clip-path'>
|
|
57
|
+
<rect x='0' y='0' width='100' height='100' fill='white' />
|
|
58
|
+
<circle
|
|
59
|
+
className={`transition-transform duration-400 ${
|
|
60
|
+
theme === 'dark' ? 'translate-x-8 -translate-y-4' : ''
|
|
61
|
+
}`}
|
|
62
|
+
cx='68'
|
|
63
|
+
cy='40'
|
|
64
|
+
r='18'
|
|
65
|
+
/>
|
|
66
|
+
</mask>
|
|
67
|
+
</defs>
|
|
68
|
+
{/* Moon */}
|
|
69
|
+
<circle
|
|
70
|
+
className={`origin-center transition-all duration-400 ${
|
|
71
|
+
theme === 'light'
|
|
72
|
+
? 'scale-[1.9] fill-black'
|
|
73
|
+
: 'scale-100 fill-white'
|
|
74
|
+
}`}
|
|
75
|
+
mask={'url(#theme-toggle_clip-path)'}
|
|
76
|
+
cx='50'
|
|
77
|
+
cy='50'
|
|
78
|
+
r='23'
|
|
79
|
+
/>
|
|
80
|
+
{/* Sunrays */}
|
|
81
|
+
<rect
|
|
82
|
+
className={sunrayClass}
|
|
83
|
+
x='86'
|
|
84
|
+
y='47'
|
|
85
|
+
width='14'
|
|
86
|
+
height='6'
|
|
87
|
+
rx='3'
|
|
88
|
+
/>
|
|
89
|
+
<rect
|
|
90
|
+
className={sunrayClass}
|
|
91
|
+
y='47'
|
|
92
|
+
width='14'
|
|
93
|
+
height='6'
|
|
94
|
+
rx='3'
|
|
95
|
+
/>
|
|
96
|
+
<rect
|
|
97
|
+
className={sunrayClass}
|
|
98
|
+
x='47'
|
|
99
|
+
y='86'
|
|
100
|
+
width='6'
|
|
101
|
+
height='14'
|
|
102
|
+
rx='3'
|
|
103
|
+
/>
|
|
104
|
+
<rect
|
|
105
|
+
className={sunrayClass}
|
|
106
|
+
x='75'
|
|
107
|
+
y='75'
|
|
108
|
+
width='6'
|
|
109
|
+
height='14'
|
|
110
|
+
rx='3'
|
|
111
|
+
transform='rotate(-45 78 78)'
|
|
112
|
+
/>
|
|
113
|
+
<rect
|
|
114
|
+
className={sunrayClass}
|
|
115
|
+
x='84.8995'
|
|
116
|
+
y='12'
|
|
117
|
+
width='6'
|
|
118
|
+
height='14'
|
|
119
|
+
rx='3'
|
|
120
|
+
transform='rotate(45 84.8995 12)'
|
|
121
|
+
/>
|
|
122
|
+
<rect
|
|
123
|
+
className={sunrayClass}
|
|
124
|
+
x='22.8995'
|
|
125
|
+
y='74'
|
|
126
|
+
width='6'
|
|
127
|
+
height='14'
|
|
128
|
+
rx='3'
|
|
129
|
+
transform='rotate(45 22.8995 74)'
|
|
130
|
+
/>
|
|
131
|
+
<rect
|
|
132
|
+
className={sunrayClass}
|
|
133
|
+
x='13'
|
|
134
|
+
y='16.2426'
|
|
135
|
+
width='6'
|
|
136
|
+
height='14'
|
|
137
|
+
rx='3'
|
|
138
|
+
transform='rotate(-45 13 16.2426)'
|
|
139
|
+
/>
|
|
140
|
+
<rect
|
|
141
|
+
className={sunrayClass}
|
|
142
|
+
x='47'
|
|
143
|
+
y='0'
|
|
144
|
+
width='6'
|
|
145
|
+
height='14'
|
|
146
|
+
rx='3'
|
|
147
|
+
/>
|
|
148
|
+
</svg>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import Link from 'next/link'
|
|
2
|
+
|
|
3
|
+
type VersionTagProps = {
|
|
4
|
+
version?: string
|
|
5
|
+
url?: string
|
|
6
|
+
className?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default function VersionTag({version, url, className}: VersionTagProps) {
|
|
10
|
+
if (!version) {
|
|
11
|
+
return
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const style =
|
|
15
|
+
`w-fit bg-login-600 px-2 py-1 rounded-md text-white tracking-[0.05em] font-semibold text-lg ${className}`
|
|
16
|
+
|
|
17
|
+
if (url) {
|
|
18
|
+
return (
|
|
19
|
+
<Link
|
|
20
|
+
className={style}
|
|
21
|
+
target='_blank'
|
|
22
|
+
href={url}
|
|
23
|
+
>
|
|
24
|
+
v{version}
|
|
25
|
+
</Link>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className={style}>
|
|
31
|
+
v{version}
|
|
32
|
+
</div>
|
|
33
|
+
)
|
|
34
|
+
}
|
package/src/globals.css
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
3
3
|
@config '../tailwind.config.ts';
|
|
4
4
|
|
|
5
5
|
@theme {
|
|
6
|
+
--breakpoint-100px: 100px;
|
|
7
|
+
--breakpoint-200px: 200px;
|
|
8
|
+
--breakpoint-300px: 300px;
|
|
9
|
+
--breakpoint-432px: 432px;
|
|
10
|
+
--breakpoint-350px: 350px;
|
|
11
|
+
--breakpoint-400px: 400px;
|
|
12
|
+
--breakpoint-450px: 450px;
|
|
13
|
+
--breakpoint-500px: 500px;
|
|
14
|
+
--breakpoint-600px: 600px;
|
|
15
|
+
--breakpoint-700px: 700px;
|
|
16
|
+
--breakpoint-800px: 800px;
|
|
17
|
+
--breakpoint-900px: 900px;
|
|
18
|
+
--breakpoint-1000px: 1000px;
|
|
19
|
+
--breakpoint-1200px: 1200px;
|
|
20
|
+
|
|
6
21
|
--breakpoint-xs: 30rem;
|
|
7
22
|
--breakpoint-2xl: 100rem;
|
|
8
23
|
--breakpoint-3xl: 120rem;
|
|
@@ -82,3 +97,35 @@ body {
|
|
|
82
97
|
background: var(--background);
|
|
83
98
|
font-family: Arial, Helvetica, sans-serif;
|
|
84
99
|
}
|
|
100
|
+
|
|
101
|
+
@keyframes jump {
|
|
102
|
+
40% {
|
|
103
|
+
transform: translateY(-0.3rem);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@utility animate-jump {
|
|
108
|
+
animation: jump 0.4s 1;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@utility link-corner-hover {
|
|
112
|
+
position: relative;
|
|
113
|
+
|
|
114
|
+
&::after {
|
|
115
|
+
content: ' ';
|
|
116
|
+
position: absolute;
|
|
117
|
+
width: .8em;
|
|
118
|
+
height: .8em;
|
|
119
|
+
bottom: .4em;
|
|
120
|
+
right: .4em;
|
|
121
|
+
border-right: .2em solid transparent;
|
|
122
|
+
border-bottom: .2em solid transparent;
|
|
123
|
+
transition: .2s;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
&:hover::after {
|
|
127
|
+
border-color: var(--color-login);
|
|
128
|
+
right: 0;
|
|
129
|
+
bottom: 0;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -3,35 +3,37 @@ import path from 'path'
|
|
|
3
3
|
import { globSync } from 'glob'
|
|
4
4
|
|
|
5
5
|
const DIST_DIR = path.resolve('dist')
|
|
6
|
-
const
|
|
6
|
+
const SRC_DIR = path.join(DIST_DIR, 'src')
|
|
7
7
|
|
|
8
8
|
const jsFiles = globSync(`${DIST_DIR}/**/*.js`, { nodir: true })
|
|
9
9
|
|
|
10
10
|
jsFiles.forEach((file) => {
|
|
11
11
|
let content = fs.readFileSync(file, 'utf-8')
|
|
12
12
|
const fileDir = path.dirname(file)
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
|
|
14
|
+
// Handle @utils/* imports
|
|
15
|
+
content = content.replace(/from ['"]@utils\/(.*?)['"]/g, (_, p1) => {
|
|
16
|
+
const targetPath = path.join(SRC_DIR, 'utils', p1)
|
|
17
|
+
const relative = path.relative(fileDir, targetPath).replace(/\\/g, '/')
|
|
18
|
+
return `from '${relative}'`
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// Handle @components/* imports
|
|
22
|
+
content = content.replace(/from ['"]@components\/(.*?)['"]/g, (_, p1) => {
|
|
23
|
+
const targetPath = path.join(SRC_DIR, 'components', p1)
|
|
15
24
|
const relative = path.relative(fileDir, targetPath).replace(/\\/g, '/')
|
|
16
25
|
return `from '${relative}'`
|
|
17
26
|
})
|
|
18
27
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const resolvedPath = path.join(IMAGES_DIR, imgRelativePath)
|
|
26
|
-
console.error(`⚠️ Detected inline image in ${file}: ${match}`)
|
|
27
|
-
console.error(` Resolved file path: ${resolvedPath}`)
|
|
28
|
-
console.error(` This is not allowed. Use 'import image from '@images/${imgRelativePath}'' instead.`)
|
|
29
|
-
}
|
|
30
|
-
})
|
|
31
|
-
}
|
|
28
|
+
// Handle @hooks/* imports
|
|
29
|
+
content = content.replace(/from ['"]@hooks\/(.*?)['"]/g, (_, p1) => {
|
|
30
|
+
const targetPath = path.join(SRC_DIR, 'hooks', p1)
|
|
31
|
+
const relative = path.relative(fileDir, targetPath).replace(/\\/g, '/')
|
|
32
|
+
return `from '${relative}'`
|
|
33
|
+
})
|
|
32
34
|
|
|
33
35
|
fs.writeFileSync(file, content, 'utf-8')
|
|
34
36
|
})
|
|
35
37
|
|
|
36
|
-
console.log('🐝
|
|
38
|
+
console.log('🐝 All path aliases rewritten')
|
|
37
39
|
console.log('🐝 Build complete')
|
|
@@ -27,6 +27,10 @@ declare module 'uibee/components' {
|
|
|
27
27
|
(event: ToastEventProps): void
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export type Language = 'no' | 'en'
|
|
31
|
+
|
|
30
32
|
export default function LoginPage(props: LoginPageProps): JSX.Element;
|
|
31
33
|
export default function Toaster(props: { toasts: ToastProps[] }): JSX.Element;
|
|
34
|
+
export default function LanguageToggle(props: { lang: Language }): JSX.Element;
|
|
35
|
+
export default function ThemeSwitch(props: { className?: string }): JSX.Element;
|
|
32
36
|
}
|
package/tsconfig.json
CHANGED
|
@@ -13,8 +13,14 @@
|
|
|
13
13
|
"jsx": "react-jsx",
|
|
14
14
|
"resolveJsonModule": true,
|
|
15
15
|
"paths": {
|
|
16
|
-
"@
|
|
17
|
-
"./
|
|
16
|
+
"@components/*": [
|
|
17
|
+
"./src/components/*"
|
|
18
|
+
],
|
|
19
|
+
"@utils/*": [
|
|
20
|
+
"./src/utils/*"
|
|
21
|
+
],
|
|
22
|
+
"@hooks/*": [
|
|
23
|
+
"./src/hooks/*"
|
|
18
24
|
]
|
|
19
25
|
}
|
|
20
26
|
},
|