@oslokommune/punkt-react 13.22.0 → 14.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.
@@ -0,0 +1,173 @@
1
+ 'use client'
2
+
3
+ import { ForwardedRef, forwardRef } from 'react'
4
+ import { PktIcon } from '../icon/Icon'
5
+ import { PktLink } from '../link/Link'
6
+ import { User, Representing, UserMenuItem } from '../header/types'
7
+ import classNames from 'classnames'
8
+
9
+ // Internal type for rendering links/buttons (supports both href and onClick)
10
+ interface IInternalMenuItemBase {
11
+ title: string
12
+ iconName?: string
13
+ }
14
+
15
+ interface InternalMenuLink extends IInternalMenuItemBase {
16
+ href: string
17
+ }
18
+
19
+ interface InternalMenuButton extends IInternalMenuItemBase {
20
+ onClick: () => void
21
+ }
22
+
23
+ type TInternalMenuItem = InternalMenuLink | InternalMenuButton
24
+
25
+ // Helper to convert UserMenuItem (with target) to internal format
26
+ const convertUserMenuItem = (item: UserMenuItem): TInternalMenuItem => {
27
+ if (typeof item.target === 'string') {
28
+ return { title: item.title, iconName: item.iconName, href: item.target }
29
+ }
30
+ return { title: item.title, iconName: item.iconName, onClick: item.target }
31
+ }
32
+
33
+ export interface IPktHeaderUserMenu {
34
+ user: User
35
+ formattedLastLoggedIn?: string
36
+ representing?: Representing
37
+ userMenu?: UserMenuItem[]
38
+ canChangeRepresentation?: boolean
39
+ changeRepresentation?: () => void
40
+ logoutOnClick?: () => void
41
+ }
42
+
43
+ const LinkOrButtonComponent = ({ item, className }: { item: TInternalMenuItem; className?: string }) => {
44
+ const isLink = 'href' in item
45
+
46
+ return isLink ? (
47
+ <PktLink
48
+ iconName={item.iconName}
49
+ href={item.href}
50
+ aria-hidden="true"
51
+ className={classNames('pkt-user-menu__link', className)}
52
+ >
53
+ {item.title}
54
+ </PktLink>
55
+ ) : (
56
+ <button
57
+ className={classNames('pkt-user-menu__link pkt-link-button pkt-link pkt-link--icon-left', className)}
58
+ type="button"
59
+ onClick={() => {
60
+ if ('onClick' in item && typeof item.onClick === 'function') {
61
+ item.onClick()
62
+ } else {
63
+ console.error('UserMenuButton item is missing onClick handler or onClick is not a function:', item)
64
+ }
65
+ }}
66
+ >
67
+ {item.iconName && <PktIcon name={item.iconName} className="pkt-link__icon" aria-hidden="true" />}
68
+ {item.title}
69
+ </button>
70
+ )
71
+ }
72
+
73
+ const LinkSection = ({ links }: { links: TInternalMenuItem[] }) => {
74
+ return (
75
+ <ul className="pkt-user-menu__sublist">
76
+ {links.map((item, index) => {
77
+ return (
78
+ <li key={index} className="pkt-user-menu__subitem">
79
+ <LinkOrButtonComponent item={item} />
80
+ </li>
81
+ )
82
+ })}
83
+ </ul>
84
+ )
85
+ }
86
+
87
+ export const PktHeaderUserMenu = forwardRef(
88
+ (
89
+ {
90
+ user,
91
+ formattedLastLoggedIn,
92
+ representing,
93
+ userMenu,
94
+ canChangeRepresentation,
95
+ changeRepresentation,
96
+ logoutOnClick,
97
+ }: IPktHeaderUserMenu,
98
+ ref: ForwardedRef<HTMLDivElement>,
99
+ ) => {
100
+ // Convert userMenu items to internal format
101
+ const internalMenuItems = userMenu?.map(convertUserMenuItem)
102
+
103
+ return (
104
+ <nav className="pkt-user-menu" ref={ref} aria-label="Meny for innlogget bruker">
105
+ <ul className="pkt-user-menu__list">
106
+ {/* User section */}
107
+ {user && (
108
+ <li className="pkt-user-menu__item">
109
+ <div className="pkt-user-menu__label">Pålogget som</div>
110
+ <div className="pkt-user-menu__name" translate="no">
111
+ {user.name}
112
+ </div>
113
+ {formattedLastLoggedIn && (
114
+ <div className="pkt-user-menu__last-logged-in">
115
+ Sist pålogget: <time>{formattedLastLoggedIn}</time>
116
+ </div>
117
+ )}
118
+ </li>
119
+ )}
120
+
121
+ {/* User menu items */}
122
+ {internalMenuItems && internalMenuItems.length > 0 && (
123
+ <li className="pkt-user-menu__item">
124
+ <LinkSection links={internalMenuItems} />
125
+ </li>
126
+ )}
127
+
128
+ {/* Representing section */}
129
+ {representing && (
130
+ <li className="pkt-user-menu__item">
131
+ <div className="pkt-user-menu__label">Representerer</div>
132
+ <div className="pkt-user-menu__name" translate="no">
133
+ {representing.name}
134
+ </div>
135
+ {representing.orgNumber && (
136
+ <div className="pkt-user-menu__org-number">Org.nr. {representing.orgNumber}</div>
137
+ )}
138
+ {canChangeRepresentation && changeRepresentation && (
139
+ <ul className="pkt-user-menu__sublist mt-size-16">
140
+ <li className="pkt-user-menu__subitem">
141
+ <LinkOrButtonComponent
142
+ item={{ title: 'Endre organisasjon', iconName: 'cogwheel', onClick: changeRepresentation }}
143
+ />
144
+ </li>
145
+ </ul>
146
+ )}
147
+ </li>
148
+ )}
149
+
150
+ {/* Change representation without representing object */}
151
+ {!representing && canChangeRepresentation && changeRepresentation && (
152
+ <li className="pkt-user-menu__item">
153
+ <ul className="pkt-user-menu__sublist">
154
+ <li className="pkt-user-menu__subitem">
155
+ <LinkOrButtonComponent
156
+ item={{ title: 'Endre organisasjon', iconName: 'cogwheel', onClick: changeRepresentation }}
157
+ />
158
+ </li>
159
+ </ul>
160
+ </li>
161
+ )}
162
+
163
+ {/* Logout */}
164
+ {logoutOnClick && (
165
+ <li className="pkt-user-menu__item">
166
+ <LinkOrButtonComponent item={{ title: 'Logg ut', iconName: 'exit', onClick: logoutOnClick }} />
167
+ </li>
168
+ )}
169
+ </ul>
170
+ </nav>
171
+ )
172
+ },
173
+ )
@@ -12,6 +12,7 @@ export { PktDatepicker } from './datepicker/Datepicker'
12
12
  export { PktFooter } from './footer/Footer'
13
13
  export { PktFooterSimple } from './footerSimple/FooterSimple'
14
14
  export { PktHeader } from './header/Header'
15
+ export { PktHeaderService } from './header/HeaderService'
15
16
  export { PktHeading } from './heading/Heading'
16
17
  export { PktHelptext } from './helptext/Helptext'
17
18
  export { PktIcon } from './icon/Icon'
@@ -11,6 +11,7 @@ export type { IPktDatepicker } from './datepicker/Datepicker'
11
11
  export type { IPktFooter } from './footer/Footer'
12
12
  export type { IPktFooterSimple } from './footerSimple/FooterSimple'
13
13
  export type { IPktHeader } from './header/Header'
14
+ export type { IPktHeaderService } from './header/HeaderService'
14
15
  export type { IPktInputWrapper } from './inputwrapper/InputWrapper'
15
16
  export type { IPktLinkCard } from './linkcard/LinkCard'
16
17
  export type { IPktLoader } from './loader/Loader'