uibee 3.3.0 → 3.3.2

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.
Files changed (162) hide show
  1. package/dist/components/alert/alert.d.ts +15 -0
  2. package/dist/components/alert/alert.js +29 -0
  3. package/dist/components/badge/badge.d.ts +16 -0
  4. package/dist/components/badge/badge.js +26 -0
  5. package/dist/components/buttons/button.d.ts +25 -0
  6. package/dist/components/buttons/button.js +44 -0
  7. package/dist/components/code/code.d.ts +23 -0
  8. package/dist/components/code/code.js +39 -0
  9. package/dist/components/confirm/confirmPopup.d.ts +23 -0
  10. package/dist/components/confirm/confirmPopup.js +52 -0
  11. package/dist/components/container/accordion.d.ts +25 -0
  12. package/dist/components/container/accordion.js +41 -0
  13. package/dist/components/container/card.d.ts +13 -0
  14. package/dist/components/container/card.js +10 -0
  15. package/dist/components/container/expandableCard.d.ts +32 -0
  16. package/dist/components/container/expandableCard.js +70 -0
  17. package/dist/components/container/glassCard.d.ts +13 -0
  18. package/dist/components/container/glassCard.js +10 -0
  19. package/dist/components/container/highlight.d.ts +10 -0
  20. package/dist/components/container/highlight.js +10 -0
  21. package/dist/components/container/iconBubble.d.ts +25 -0
  22. package/dist/components/container/iconBubble.js +21 -0
  23. package/dist/components/container/leftBarPanel.d.ts +15 -0
  24. package/dist/components/container/leftBarPanel.js +10 -0
  25. package/dist/components/container/page.d.ts +15 -0
  26. package/dist/components/container/page.js +16 -0
  27. package/dist/components/container/pulseDot.d.ts +12 -0
  28. package/dist/components/container/pulseDot.js +23 -0
  29. package/dist/components/container/statCard.d.ts +18 -0
  30. package/dist/components/container/statCard.js +25 -0
  31. package/dist/components/container/tabs.d.ts +35 -0
  32. package/dist/components/container/tabs.js +36 -0
  33. package/dist/components/empty/emptyState.d.ts +19 -0
  34. package/dist/components/empty/emptyState.js +27 -0
  35. package/dist/components/footer/footer.d.ts +49 -0
  36. package/dist/components/footer/footer.js +109 -0
  37. package/dist/components/footer/loginContent.d.ts +18 -0
  38. package/dist/components/footer/loginContent.js +205 -0
  39. package/dist/components/inputs/checkbox.d.ts +20 -0
  40. package/dist/components/inputs/checkbox.js +76 -0
  41. package/dist/components/inputs/fileInput.d.ts +19 -0
  42. package/dist/components/inputs/fileInput.js +108 -0
  43. package/dist/components/inputs/input.d.ts +16 -0
  44. package/dist/components/inputs/input.js +157 -0
  45. package/dist/components/inputs/multiSelect.d.ts +37 -0
  46. package/dist/components/inputs/multiSelect.js +99 -0
  47. package/dist/components/inputs/radio.d.ts +20 -0
  48. package/dist/components/inputs/radio.js +70 -0
  49. package/dist/components/inputs/range.d.ts +14 -0
  50. package/dist/components/inputs/range.js +60 -0
  51. package/dist/components/inputs/searchInput.d.ts +11 -0
  52. package/dist/components/inputs/searchInput.js +56 -0
  53. package/dist/components/inputs/select.d.ts +42 -0
  54. package/dist/components/inputs/select.js +164 -0
  55. package/dist/components/inputs/shared/colorPickerPopup.js +293 -0
  56. package/dist/components/inputs/shared/dateTimePickerPopup.js +225 -0
  57. package/dist/components/inputs/shared/fieldWrapper.js +33 -0
  58. package/dist/components/inputs/shared/index.js +6 -0
  59. package/dist/components/inputs/shared/inputError.js +17 -0
  60. package/dist/components/inputs/shared/inputInfo.js +13 -0
  61. package/dist/components/inputs/shared/inputLabel.js +19 -0
  62. package/dist/components/inputs/switch.d.ts +14 -0
  63. package/dist/components/inputs/switch.js +37 -0
  64. package/dist/components/inputs/tagInput.d.ts +31 -0
  65. package/dist/components/inputs/tagInput.js +72 -0
  66. package/dist/components/inputs/textarea.d.ts +14 -0
  67. package/dist/components/inputs/textarea.js +76 -0
  68. package/dist/components/inputs/toggle.d.ts +21 -0
  69. package/dist/components/inputs/toggle.js +33 -0
  70. package/dist/components/login/loginPage.d.ts +15 -0
  71. package/dist/components/login/loginPage.js +80 -0
  72. package/dist/components/logo/logo.d.ts +9 -0
  73. package/dist/components/logo/logo.js +138 -0
  74. package/dist/components/logo/logoSmall.d.ts +4 -0
  75. package/dist/components/logo/logoSmall.js +56 -0
  76. package/dist/components/markdownrender/markdownRender.d.ts +16 -0
  77. package/dist/components/markdownrender/markdownRender.js +59 -0
  78. package/dist/components/modal/modal.d.ts +22 -0
  79. package/dist/components/modal/modal.js +59 -0
  80. package/dist/components/navbar/bubble.js +36 -0
  81. package/dist/components/navbar/navbar.d.ts +50 -0
  82. package/dist/components/navbar/navbar.js +99 -0
  83. package/dist/components/navbar/navbarDropdown.d.ts +15 -0
  84. package/dist/components/navbar/navbarDropdown.js +63 -0
  85. package/dist/components/navbar/navbarItem.d.ts +23 -0
  86. package/dist/components/navbar/navbarItem.js +38 -0
  87. package/dist/components/sidebar/sidebar.d.ts +34 -0
  88. package/dist/components/sidebar/sidebar.js +135 -0
  89. package/dist/components/spinner/spinner.d.ts +11 -0
  90. package/dist/components/spinner/spinner.js +16 -0
  91. package/dist/components/table/body.js +253 -0
  92. package/dist/components/table/constants.js +58 -0
  93. package/dist/components/table/empty.js +24 -0
  94. package/dist/components/table/format.js +39 -0
  95. package/dist/components/table/header.js +109 -0
  96. package/dist/components/table/menu.d.ts +18 -0
  97. package/dist/components/table/menu.js +76 -0
  98. package/dist/components/table/pagination.d.ts +14 -0
  99. package/dist/components/table/pagination.js +130 -0
  100. package/dist/components/table/skeleton.js +39 -0
  101. package/dist/components/table/table.d.ts +7 -0
  102. package/dist/components/table/table.js +213 -0
  103. package/dist/components/table/types.d.ts +52 -0
  104. package/dist/components/table/utils.js +9 -0
  105. package/dist/components/toast/toastItem.js +61 -0
  106. package/dist/components/toast/toaster.d.ts +13 -0
  107. package/dist/components/toast/toaster.js +117 -0
  108. package/dist/components/toggle/language.d.ts +10 -0
  109. package/dist/components/toggle/language.js +34 -0
  110. package/dist/components/toggle/theme.d.ts +8 -0
  111. package/dist/components/toggle/theme.js +135 -0
  112. package/dist/components/version/version.d.ts +13 -0
  113. package/dist/components/version/version.js +19 -0
  114. package/dist/components/vulnerability/constants.d.ts +4 -0
  115. package/dist/components/vulnerability/constants.js +17 -0
  116. package/dist/components/vulnerability/severityPill.d.ts +15 -0
  117. package/dist/components/vulnerability/severityPill.js +20 -0
  118. package/dist/{style.css → globals.css} +210 -264
  119. package/dist/hooks/useClickOutside.d.ts +6 -0
  120. package/dist/{useClickOutside-Cmp-RsP3.js → hooks/useClickOutside.js} +1 -1
  121. package/dist/hooks/useDarkMode.d.ts +4 -0
  122. package/dist/hooks/useDarkMode.js +19 -0
  123. package/dist/hooks/useVisibility.d.ts +7 -0
  124. package/dist/hooks/useVisibility.js +25 -0
  125. package/dist/icons/icons.d.ts +420 -0
  126. package/dist/{icons-lZYQ6Vlr.js → icons/icons.js} +1 -1
  127. package/dist/src/components/index.d.ts +51 -905
  128. package/dist/src/components/index.js +49 -4179
  129. package/dist/src/hooks/index.d.ts +3 -14
  130. package/dist/src/hooks/index.js +3 -42
  131. package/dist/src/icons/index.d.ts +1 -419
  132. package/dist/src/icons/index.js +1 -1
  133. package/dist/src/styles/index.js +1 -1
  134. package/dist/src/utils/index.d.ts +5 -37
  135. package/dist/src/utils/index.js +5 -124
  136. package/dist/utils/auth/callback.d.ts +14 -0
  137. package/dist/utils/auth/callback.js +53 -0
  138. package/dist/utils/auth/getDomain.js +6 -0
  139. package/dist/utils/auth/login.d.ts +12 -0
  140. package/dist/utils/auth/login.js +17 -0
  141. package/dist/utils/auth/logout.d.ts +10 -0
  142. package/dist/utils/auth/logout.js +20 -0
  143. package/dist/utils/auth/token.d.ts +10 -0
  144. package/dist/utils/auth/token.js +30 -0
  145. package/dist/utils/logoConsoleOutput/logoConsoleOutput.d.ts +4 -0
  146. package/dist/utils/logoConsoleOutput/logoConsoleOutput.js +11 -0
  147. package/package.json +1 -1
  148. package/src/components/buttons/button.tsx +2 -0
  149. package/src/components/footer/footer.tsx +60 -42
  150. package/src/components/footer/loginContent.tsx +38 -2
  151. package/src/components/index.ts +0 -1
  152. package/src/components/inputs/checkbox.tsx +2 -0
  153. package/src/components/inputs/input.tsx +2 -0
  154. package/src/components/inputs/radio.tsx +2 -0
  155. package/src/components/inputs/range.tsx +2 -0
  156. package/src/components/inputs/shared/colorPickerPopup.tsx +2 -0
  157. package/src/components/inputs/shared/dateTimePickerPopup.tsx +2 -0
  158. package/src/components/inputs/textarea.tsx +2 -0
  159. package/src/components/inputs/toggle.tsx +2 -0
  160. package/src/components/toast/toastItem.tsx +2 -0
  161. package/src/globals.css +5 -0
  162. package/tsdown.config.ts +1 -0
@@ -0,0 +1,14 @@
1
+ import { AuthCallbackProps } from "uibee/utils";
2
+
3
+ //#region src/utils/auth/callback.d.ts
4
+ declare function authCallback({
5
+ req,
6
+ tokenURL,
7
+ clientID,
8
+ clientSecret,
9
+ redirectPath,
10
+ userInfoURL,
11
+ tokenRedirectPath
12
+ }: AuthCallbackProps): Promise<Response>;
13
+ //#endregion
14
+ export { authCallback as default };
@@ -0,0 +1,53 @@
1
+ import { getDomain } from "./getDomain.js";
2
+ import { NextResponse } from "next/server.js";
3
+ //#region src/utils/auth/callback.ts
4
+ async function authCallback({ req, tokenURL, clientID, clientSecret, redirectPath, userInfoURL, tokenRedirectPath }) {
5
+ const domain = getDomain(req);
6
+ const searchParams = new URL(req.url).searchParams;
7
+ if (!searchParams) return NextResponse.json({ error: "No search parameters found." }, { status: 400 });
8
+ const code = searchParams.get("code");
9
+ if (!code) return NextResponse.json({ error: "No authorization code found." }, { status: 400 });
10
+ try {
11
+ const tokenResponse = await fetch(tokenURL, {
12
+ method: "POST",
13
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
14
+ body: new URLSearchParams({
15
+ client_id: clientID,
16
+ client_secret: clientSecret,
17
+ code,
18
+ redirect_uri: `${domain}${redirectPath}`,
19
+ grant_type: "authorization_code"
20
+ }).toString()
21
+ });
22
+ const tokenResponseBody = await tokenResponse.text();
23
+ if (!tokenResponse.ok) return new Response(JSON.stringify(`Failed to obtain token: ${tokenResponseBody}`), {
24
+ status: 500,
25
+ headers: { "Content-Type": "application/json" }
26
+ });
27
+ const token = JSON.parse(tokenResponseBody);
28
+ const userInfoResponse = await fetch(userInfoURL, { headers: { Authorization: `Bearer ${token.access_token}` } });
29
+ if (!userInfoResponse.ok) {
30
+ const userInfoError = await userInfoResponse.text();
31
+ return new Response(`No user info found: ${userInfoError}`, {
32
+ status: 500,
33
+ headers: { "Content-Type": "application/json" }
34
+ });
35
+ }
36
+ const userInfo = await userInfoResponse.json();
37
+ const redirectUrl = new URL(`${domain}${tokenRedirectPath}`);
38
+ redirectUrl.search = new URLSearchParams({
39
+ id: userInfo.sub,
40
+ name: userInfo.name,
41
+ username: userInfo.nickname,
42
+ email: userInfo.email,
43
+ groups: userInfo.groups.join(","),
44
+ access_token: token.access_token
45
+ }).toString();
46
+ return NextResponse.redirect(redirectUrl.toString());
47
+ } catch (err) {
48
+ console.error("Error during OAuth flow:", err.message);
49
+ return NextResponse.json({ error: "Authentication failed" }, { status: 500 });
50
+ }
51
+ }
52
+ //#endregion
53
+ export { authCallback as default };
@@ -0,0 +1,6 @@
1
+ //#region src/utils/auth/getDomain.ts
2
+ function getDomain(req) {
3
+ return `${req.headers.get("x-forwarded-proto") ?? new URL(req.url).protocol.replace(":", "")}://${req.headers.get("host") ?? req.headers.get("x-forwarded-host") ?? new URL(req.url).host}`;
4
+ }
5
+ //#endregion
6
+ export { getDomain };
@@ -0,0 +1,12 @@
1
+ import { NextResponse } from "next/server.js";
2
+ import { AuthLoginProps } from "uibee/utils";
3
+
4
+ //#region src/utils/auth/login.d.ts
5
+ declare function AuthLogin({
6
+ req,
7
+ clientID,
8
+ redirectPath,
9
+ authURL
10
+ }: AuthLoginProps): Promise<NextResponse<unknown>>;
11
+ //#endregion
12
+ export { AuthLogin as default };
@@ -0,0 +1,17 @@
1
+ import { getDomain } from "./getDomain.js";
2
+ import { NextResponse } from "next/server.js";
3
+ //#region src/utils/auth/login.ts
4
+ async function AuthLogin({ req, clientID, redirectPath, authURL }) {
5
+ const domain = getDomain(req);
6
+ const state = Math.random().toString(36).substring(5);
7
+ const authQueryParams = new URLSearchParams({
8
+ client_id: clientID,
9
+ redirect_uri: `${domain}${redirectPath}`,
10
+ response_type: "code",
11
+ scope: "openid profile email",
12
+ state
13
+ }).toString();
14
+ return NextResponse.redirect(`${authURL}?${authQueryParams}`);
15
+ }
16
+ //#endregion
17
+ export { AuthLogin as default };
@@ -0,0 +1,10 @@
1
+ import { NextResponse } from "next/server.js";
2
+ import { AuthLogoutProps } from "uibee/utils";
3
+
4
+ //#region src/utils/auth/logout.d.ts
5
+ declare function AuthLogout({
6
+ req,
7
+ path
8
+ }: AuthLogoutProps): Promise<NextResponse<unknown>>;
9
+ //#endregion
10
+ export { AuthLogout as default };
@@ -0,0 +1,20 @@
1
+ import { getDomain } from "./getDomain.js";
2
+ import { NextResponse } from "next/server.js";
3
+ //#region src/utils/auth/logout.ts
4
+ async function AuthLogout({ req, path }) {
5
+ const domain = getDomain(req);
6
+ const response = NextResponse.redirect(new URL(path || "/", domain));
7
+ [
8
+ "access_token",
9
+ "user_id",
10
+ "user_name",
11
+ "user_nickname",
12
+ "user_email",
13
+ "user_groups"
14
+ ].forEach((cookieName) => {
15
+ response.cookies.delete(cookieName);
16
+ });
17
+ return response;
18
+ }
19
+ //#endregion
20
+ export { AuthLogout as default };
@@ -0,0 +1,10 @@
1
+ import { NextResponse } from "next/server.js";
2
+ import { AuthTokenProps } from "uibee/utils";
3
+
4
+ //#region src/utils/auth/token.d.ts
5
+ declare function AuthToken({
6
+ req,
7
+ redirectPath
8
+ }: AuthTokenProps): Promise<NextResponse<unknown>>;
9
+ //#endregion
10
+ export { AuthToken as default };
@@ -0,0 +1,30 @@
1
+ import { getDomain } from "./getDomain.js";
2
+ import { NextResponse } from "next/server.js";
3
+ //#region src/utils/auth/token.ts
4
+ async function AuthToken({ req, redirectPath }) {
5
+ const domain = getDomain(req);
6
+ const url = new URL(req.url);
7
+ const token = url.searchParams.get("access_token");
8
+ const btg = url.searchParams.get("btg");
9
+ const redirectCookie = req.cookies.get("redirect_after_login");
10
+ const redirect = redirectCookie ? redirectCookie.value : redirectPath || "/";
11
+ if (!token) return NextResponse.json({ error: "No access token provided" }, { status: 400 });
12
+ if (btg) return NextResponse.redirect(new URL(redirect, domain));
13
+ const accessToken = url.searchParams.get("access_token");
14
+ const userID = url.searchParams.get("id");
15
+ const username = url.searchParams.get("name");
16
+ const userNickname = url.searchParams.get("username");
17
+ const userEmail = url.searchParams.get("email");
18
+ const userGroups = url.searchParams.get("groups");
19
+ const response = NextResponse.redirect(new URL(redirect, domain));
20
+ response.cookies.set("access_token", accessToken);
21
+ response.cookies.set("user_id", userID);
22
+ response.cookies.set("user_name", username);
23
+ response.cookies.set("user_nickname", userNickname);
24
+ response.cookies.set("user_email", userEmail);
25
+ response.cookies.set("user_groups", userGroups);
26
+ if (redirectCookie) response.cookies.delete("redirect_after_login");
27
+ return response;
28
+ }
29
+ //#endregion
30
+ export { AuthToken as default };
@@ -0,0 +1,4 @@
1
+ //#region src/utils/logoConsoleOutput/logoConsoleOutput.d.ts
2
+ declare function LogoConsoleOutput(): void;
3
+ //#endregion
4
+ export { LogoConsoleOutput };
@@ -0,0 +1,11 @@
1
+ //#region src/utils/logoConsoleOutput/logoConsoleOutput.ts
2
+ function LogoConsoleOutput() {
3
+ const ua = navigator.userAgent;
4
+ if (!ua.includes("Chrome") && !ua.includes("Firefox") && !ua.includes("Safari")) {
5
+ console.log("Login - Linjeforeningen for IT");
6
+ return;
7
+ }
8
+ console.log("%c███████╗ ███████╗\n██╔════╝ ╚════██║\n██║%c ██╗ ██████╗ ██████╗ ██╗███╗ ██╗ %c██║\n╚═╝%c ██║ ██╔═══██╗██╔════╝ ██║████╗ ██║ %c╚═╝%c\n ██║ ██║ ██║██║ ███╗██║██╔██╗ ██║\n ██║ ██║ ██║██║ ██║██║██║╚██╗██║\n%c██╗%c ██████╗╚██████╔╝╚██████╔╝██║██║ ╚████║ %c██╗\n██║%c ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝╚═╝ ╚═══╝ %c██║\n███████╗%c %c███████║\n╚══════╝ ╚══════╝%c\n\n - Laget av TekKom med 🍕 og ❤️", "color: #fd8738", "", "color: #fd8738", "", "color: #fd8738", "", "color: #fd8738", "", "color: #fd8738", "", "color: #fd8738", "", "color: #fd8738", "");
9
+ }
10
+ //#endregion
11
+ export { LogoConsoleOutput };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uibee",
3
- "version": "3.3.0",
3
+ "version": "3.3.2",
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": {
@@ -1,3 +1,5 @@
1
+ 'use client'
2
+
1
3
  import Link from 'next/link'
2
4
  import type { JSX } from 'react'
3
5
 
@@ -32,6 +32,41 @@ function t(s: BilingualString, lang: Lang): string {
32
32
  return typeof s === 'string' ? s : s[lang]
33
33
  }
34
34
 
35
+ function Column({ col, lang }: { col: FooterColumn; lang: Lang }) {
36
+ return (
37
+ <div>
38
+ <h4 className='pb-2 text-sm font-medium tracking-widest text-login-100'>
39
+ {t(col.heading, lang)}
40
+ </h4>
41
+ {col.items.map((item, j) =>
42
+ item.href ? (
43
+ item.href.startsWith('/') ? (
44
+ <Link
45
+ key={j}
46
+ className='link--underscore-hover block text-login-50'
47
+ href={item.href}
48
+ >
49
+ {t(item.label, lang)}
50
+ </Link>
51
+ ) : (
52
+ <a
53
+ key={j}
54
+ className='link--underscore-hover block text-login-50'
55
+ href={item.href}
56
+ >
57
+ {t(item.label, lang)}
58
+ </a>
59
+ )
60
+ ) : (
61
+ <p key={j} className='text-login-50'>
62
+ {t(item.label, lang)}
63
+ </p>
64
+ )
65
+ )}
66
+ </div>
67
+ )
68
+ }
69
+
35
70
  export default function Footer({
36
71
  logo,
37
72
  sponsor,
@@ -43,15 +78,16 @@ export default function Footer({
43
78
  className,
44
79
  }: FooterProps) {
45
80
  const year = new Date().getFullYear()
81
+ const [firstColumn, ...restColumns] = columns ?? []
46
82
  const hasRight = (columns && columns.length > 0) || (socialLinks && socialLinks.length > 0)
47
83
 
48
84
  return (
49
- <div className={`mt-24 bg-login-950 md:mt-40 ${className || ''}`}>
85
+ <div className={`max-md:mt-24 bg-login-950 md:mt-40 ${className || ''}`}>
50
86
  <div className={`
51
- mx-auto w-full px-4 pt-16 pb-24 md:max-w-304 md:px-12 md:pt-20 md:pb-4
87
+ mx-auto w-full pb-4 max-md:px-4 max-md:pt-16 md:max-w-304 md:px-12 md:pt-20
52
88
  ${hasRight ? 'md:grid md:grid-cols-[18rem_1fr] md:gap-x-12' : ''}
53
89
  `}>
54
- <div className='mx-auto grid w-full max-w-60 gap-16 md:row-span-2 md:max-w-72 md:gap-20'>
90
+ <div className='mx-auto grid w-full max-md:max-w-60 max-md:gap-16 md:row-span-2 md:max-w-72 md:gap-20'>
55
91
  <div className='block w-full'>{logo}</div>
56
92
  {sponsor && (
57
93
  <div>
@@ -65,43 +101,26 @@ export default function Footer({
65
101
  )}
66
102
  </div>
67
103
 
68
- {columns && columns.length > 0 && (
104
+ {firstColumn && (
105
+ <div className='
106
+ grid w-full self-start max-md:mt-16
107
+ max-sm:max-w-60 sm:max-md:max-w-88
108
+ md:col-start-2 md:row-start-1 md:max-w-136 md:justify-self-center
109
+ '>
110
+ <Column col={firstColumn} lang={lang} />
111
+ </div>
112
+ )}
113
+
114
+ {restColumns.length > 0 && (
69
115
  <div className={`
70
- mt-12 grid w-full max-w-60 gap-8
71
- sm:max-w-88 sm:justify-items-end sm:justify-self-end
72
- md:col-start-2 md:row-start-1 md:mt-0 md:max-w-136 md:gap-0 md:justify-self-end
73
- ${columns.length > 1 ? 'sm:grid-cols-2' : ''}
116
+ grid w-full self-start max-md:mt-16 max-md:gap-8
117
+ max-sm:max-w-60 sm:max-md:max-w-88 sm:justify-items-end sm:justify-self-end
118
+ md:col-start-2 md:row-start-1 md:max-w-136 md:justify-self-end
119
+ ${restColumns.length > 1 ? 'sm:grid-cols-2' : ''}
74
120
  `}>
75
- {columns.map((col, i) => (
121
+ {restColumns.map((col, i) => (
76
122
  <div key={i} className='sm:justify-self-center md:justify-self-end'>
77
- <h4 className='pb-2 text-sm font-medium tracking-widest text-login-100'>
78
- {t(col.heading, lang)}
79
- </h4>
80
- {col.items.map((item, j) =>
81
- item.href ? (
82
- item.href.startsWith('/') ? (
83
- <Link
84
- key={j}
85
- className='link--underscore-hover block text-login-100'
86
- href={item.href}
87
- >
88
- {t(item.label, lang)}
89
- </Link>
90
- ) : (
91
- <a
92
- key={j}
93
- className='link--underscore-hover block text-login-100'
94
- href={item.href}
95
- >
96
- {t(item.label, lang)}
97
- </a>
98
- )
99
- ) : (
100
- <p key={j} className='text-login-100'>
101
- {t(item.label, lang)}
102
- </p>
103
- )
104
- )}
123
+ <Column col={col} lang={lang} />
105
124
  </div>
106
125
  ))}
107
126
  </div>
@@ -109,11 +128,12 @@ export default function Footer({
109
128
 
110
129
  {socialLinks && socialLinks.length > 0 && (
111
130
  <div className='md:col-start-2 md:row-start-2 md:justify-self-end'>
112
- <div className='mx-auto mt-20 mb-12 flex w-fit flex-wrap justify-center gap-6'>
131
+ <div className='mx-auto mt-20 mb-12 grid w-fit gap-6 max-sm:grid-cols-3 sm:grid-flow-col'>
113
132
  {socialLinks.map((link, i) => (
114
133
  <a
115
134
  key={i}
116
- className={`block size-8 text-login-100 transition-all duration-200 group
135
+ className={`group mx-auto flex h-8 w-8 items-center justify-center
136
+ text-login-100 transition-all duration-200
117
137
  ${link.hoverClass ?? 'hover:text-login-50'}`}
118
138
  title={link.title}
119
139
  href={link.href}
@@ -127,9 +147,7 @@ export default function Footer({
127
147
  </div>
128
148
  )}
129
149
 
130
- <div className='mt-16 flex flex-col items-start gap-4 md:col-span-2 md:row-start-3 md:mt-24 md:grid
131
- md:grid-cols-[auto_min-content] md:items-end md:gap-8'
132
- >
150
+ <div className='mt-24 grid grid-cols-[auto_min-content] items-end gap-8 md:col-span-2 md:row-start-3'>
133
151
  <p className='text-xs wrap-break-word text-login-100'>
134
152
  {lang === 'no' ? 'Opphavsrett' : 'Copyright'} © {year} {t(copyright, lang)}
135
153
  </p>
@@ -8,6 +8,41 @@ export type { FooterSocialLink as LoginSocialLinkData }
8
8
  // eslint-disable-next-line @stylistic/max-len
9
9
  const INSTAGRAM_PATH = 'M512 960Q408 960 375.0 959.5Q342 959 301 957Q260 955 230.5 949.0Q201 943 177 933Q151 923 129.0 909.0Q107 895 86 874Q65 853 51.0 831.0Q37 809 27 783Q17 759 11.0 729.5Q5 700 3 659Q1 618 0.5 585.0Q0 552 0 448Q0 344 0.5 311.0Q1 278 3 237Q5 196 11.0 166.5Q17 137 27 113Q37 87 51.0 65.0Q65 43 86 22Q107 1 129.0 -13.0Q151 -27 177 -37Q201 -47 230.5 -53.0Q260 -59 301 -61Q342 -63 375.0 -63.5Q408 -64 512 -64Q616 -64 649.0 -63.5Q682 -63 723 -61Q764 -59 793.5 -53.0Q823 -47 847 -37Q873 -27 895.0 -13.0Q917 1 938 22Q959 43 973.0 65.0Q987 87 997 113Q1007 137 1013.0 166.5Q1019 196 1021 237Q1023 278 1023.5 311.0Q1024 344 1024 448Q1024 552 1023.5 585.0Q1023 618 1021 659Q1019 700 1013.0 729.5Q1007 759 997 783Q987 809 973.0 831.0Q959 853 938 874Q917 895 895.0 909.0Q873 923 847 933Q823 943 793.5 949.0Q764 955 723 957Q682 959 649.0 959.5Q616 960 512 960ZM512 868Q614 868 646.0 867.5Q678 867 719 865Q756 863 778.0 857.5Q800 852 814 847Q832 840 845.5 831.0Q859 822 873 809Q886 795 895.0 781.5Q904 768 911 750Q916 736 921.5 714.0Q927 692 929 655Q931 614 931.5 582.5Q932 551 932 448Q932 345 931.0 313.5Q930 282 929 241Q927 204 921.5 182.0Q916 160 911 146Q903 128 894.5 114.5Q886 101 872 87Q859 74 845.0 65.0Q831 56 813 49Q800 44 777.5 38.5Q755 33 718 31Q677 29 645.0 28.5Q613 28 511 28Q408 28 376.0 29.0Q344 30 303 31Q266 33 243.5 38.5Q221 44 208 49Q190 57 176.5 65.5Q163 74 149 88Q136 101 126.5 115.0Q117 129 111 147Q105 160 100.0 182.5Q95 205 93 242Q91 282 90.5 314.0Q90 346 90 449Q90 551 90.5 583.0Q91 615 93 656Q95 693 100.0 715.5Q105 738 111 751Q117 770 126.5 783.5Q136 797 149 810Q163 824 176.5 833.0Q190 842 208 849Q221 854 243.0 859.5Q265 865 303 867Q344 868 375.5 868.5Q407 869 510 869ZM512 711Q458 711 410 690Q362 670 326.0 634.0Q290 598 270 550Q249 502 249 448Q249 394 270 346Q290 298 326.0 262.0Q362 226 410 206Q458 185 512 185Q566 185 614 206Q662 226 698.0 262.0Q734 298 754 346Q775 394 775 448Q775 502 754 550Q734 598 698.0 634.0Q662 670 614 690Q566 711 512 711ZM512 277Q441 277 391.0 327.0Q341 377 341 448Q341 519 391.0 569.0Q441 619 512 619Q583 619 633.0 569.0Q683 519 683 448Q683 377 633.0 327.0Q583 277 512 277ZM847 721Q847 696 829.0 678.0Q811 660 785 660Q760 660 742.0 678.0Q724 696 724 721Q724 747 742.0 765.0Q760 783 785 783Q811 783 829.0 765.0Q847 747 847 721Z'
10
10
 
11
+ // Norsk Tipping (Grasrotandelen) — Login's grassroots funding mark. Muted to
12
+ // greyscale by default, revealing its brand colours on hover of the parent link.
13
+ function NorskTippingIcon({ size = 24 }: LogfontIconProps) {
14
+ return (
15
+ <span
16
+ aria-hidden='true'
17
+ className='flex items-center justify-center transition-all duration-200
18
+ brightness-0 invert-[.69] group-hover:brightness-100 group-hover:invert-0'
19
+ style={{ width: size, height: size }}
20
+ >
21
+ <svg xmlns='http://www.w3.org/2000/svg' viewBox='294.1897 0 466.6665 466.6665'
22
+ width={size} height={size} className='block'>
23
+ <g transform='matrix(1.3333333,0,0,-1.3333333,0,743.83867)'>
24
+ <path transform='translate(292.1823,557.8794)' fill='#ffc000'
25
+ d='M 0,0 -71.54,-71.539 31.939,-175 -71.54,-278.461 0,-350 175,-175 Z' />
26
+ <path transform='translate(220.6427,486.3399)' fill='#96d94e'
27
+ d='M 0,0 246.539,-103.461 H 103.479 Z' />
28
+ <path transform='translate(292.1823,557.8794)' fill='#00a332'
29
+ d='M 0,0 -71.54,-71.539 175,-175 Z' />
30
+ <path transform='translate(324.1212,382.8785)' fill='#ff7337'
31
+ d='m 0,0 -31.939,-175 175,175 z' />
32
+ <path transform='translate(499.104,557.8794)' fill='#ff7ea9'
33
+ d='m 0,0 -87.243,-87.243 71.539,-71.54 87.242,87.244 z' />
34
+ <path transform='translate(499.104,557.8794)' fill='#ef2500'
35
+ d='m 0,0 -15.704,-158.783 87.242,87.244 z' />
36
+ <path transform='translate(570.6422,279.4181)' fill='#00c1ff'
37
+ d='m 0,0 -87.243,87.243 -71.54,-71.539 87.244,-87.243 z' />
38
+ <path transform='translate(499.1031,207.879)' fill='#0058ff'
39
+ d='M 0,0 -87.244,87.243 71.539,71.539 Z' />
40
+ </g>
41
+ </svg>
42
+ </span>
43
+ )
44
+ }
45
+
11
46
  function InstagramIcon({ size = 24 }: LogfontIconProps) {
12
47
  return (
13
48
  <span className='relative block' style={{ width: size, height: size }}>
@@ -65,6 +100,7 @@ export const loginSocialLinks: FooterSocialLink[] = [
65
100
  { title: 'Instagram', href: 'https://www.instagram.com/login_linjeforening/', icon: <InstagramIcon size={24} />, },
66
101
  { title: 'Facebook', href: 'https://facebook.com/LogNTNU', icon: <Facebook size={24} />, hoverClass: 'hover:text-[#2c87ff]' },
67
102
  { title: 'LinkedIn', href: 'https://www.linkedin.com/company/linjeforeningen-login/about', icon: <Linkedin size={24} />, hoverClass: 'hover:text-[#1a7bdd]' },
68
- { title: 'GitHub', href: 'https://github.com/login-linjeforeningen-for-it', icon: <Github size={24} />, hoverClass: 'hover:text-white' },
69
- { title: 'Wiki', href: 'https://outline.login.no/s/doc', icon: <Wikijs size={24} />, hoverClass: 'hover:text-login-50' },
103
+ { title: 'GitHub', href: 'https://github.com/login-linjeforeningen-for-it', icon: <Github size={24} />, hoverClass: 'hover:text-white' },
104
+ { title: 'Wiki', href: 'https://outline.login.no/s/doc', icon: <Wikijs size={24} />, hoverClass: 'hover:text-login-50' },
105
+ { title: 'Norsk Tipping', href: 'https://www.norsk-tipping.no/grasrotandelen/din-mottaker/811940372', icon: <NorskTippingIcon size={24} /> },
70
106
  ]
@@ -1,4 +1,3 @@
1
- 'use client'
2
1
  export { default as Input } from './inputs/input'
3
2
  export { default as SearchInput } from './inputs/searchInput'
4
3
  export { default as Textarea } from './inputs/textarea'
@@ -1,3 +1,5 @@
1
+ 'use client'
2
+
1
3
  import { Check } from 'lucide-react'
2
4
  import { FieldWrapper, InputLabel } from './shared'
3
5
 
@@ -1,3 +1,5 @@
1
+ 'use client'
2
+
1
3
  import React, { type ChangeEvent, type JSX, useRef, useState, useId } from 'react'
2
4
  import { Calendar, Clock } from 'lucide-react'
3
5
  import { FieldWrapper } from './shared'
@@ -1,3 +1,5 @@
1
+ 'use client'
2
+
1
3
  import { FieldWrapper, InputLabel } from './shared'
2
4
 
3
5
  export type RadioOption = {
@@ -1,3 +1,5 @@
1
+ 'use client'
2
+
1
3
  import { FieldWrapper } from './shared'
2
4
 
3
5
  export type RangeProps = Omit<React.ComponentProps<'input'>, 'name'> & {
@@ -1,3 +1,5 @@
1
+ 'use client'
2
+
1
3
  import { type JSX, useState, useEffect, useRef } from 'react'
2
4
 
3
5
  export type ColorPickerPopupProps = {
@@ -1,3 +1,5 @@
1
+ 'use client'
2
+
1
3
  import { useState, useEffect } from 'react'
2
4
  import { ChevronLeft, ChevronRight } from 'lucide-react'
3
5
 
@@ -1,3 +1,5 @@
1
+ 'use client'
2
+
1
3
  import { useState } from 'react'
2
4
  import { Eye, Pencil } from 'lucide-react'
3
5
  import { FieldWrapper } from './shared'
@@ -1,3 +1,5 @@
1
+ 'use client'
2
+
1
3
  type ToggleOption<T> = {
2
4
  value: T
3
5
  label?: string
@@ -1,3 +1,5 @@
1
+ 'use client'
2
+
1
3
  import { AlertCircle, AlertTriangle, CheckCircle, Info, X } from 'lucide-react'
2
4
  import { useEffect, useLayoutEffect, useRef, useState } from 'react'
3
5
  import type { ToastType, ToastProps } from 'uibee/components'
package/src/globals.css CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  @config '../tailwind.config.ts';
4
4
 
5
+ /* Apply hover styles on :hover directly instead of gating them behind
6
+ @media (hover: hover), which never matches on devices reporting hover: none. */
7
+ @custom-variant hover (&:hover);
8
+ @custom-variant group-hover (&:is(:where(.group):hover *));
9
+
5
10
  @theme {
6
11
  --breakpoint-100px: 100px;
7
12
  --breakpoint-200px: 200px;
package/tsdown.config.ts CHANGED
@@ -13,6 +13,7 @@ export default defineConfig({
13
13
  outExtensions: () => ({ js: '.js', dts: '.d.ts' }),
14
14
  dts: true,
15
15
  clean: true,
16
+ unbundle: true,
16
17
  deps: {
17
18
  onlyBundle: false,
18
19
  },