uibee 2.2.6 → 2.2.8

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/globals.css CHANGED
@@ -254,6 +254,9 @@
254
254
  .relative {
255
255
  position: relative;
256
256
  }
257
+ .sticky {
258
+ position: sticky;
259
+ }
257
260
  .start-2 {
258
261
  inset-inline-start: calc(var(--spacing) * 2);
259
262
  }
@@ -278,6 +281,9 @@
278
281
  .top-16 {
279
282
  top: calc(var(--spacing) * 16);
280
283
  }
284
+ .top-full {
285
+ top: 100%;
286
+ }
281
287
  .right-0 {
282
288
  right: calc(var(--spacing) * 0);
283
289
  }
@@ -323,6 +329,9 @@
323
329
  .-mt-0\.5 {
324
330
  margin-top: calc(var(--spacing) * -0.5);
325
331
  }
332
+ .mt-1 {
333
+ margin-top: calc(var(--spacing) * 1);
334
+ }
326
335
  .mt-2 {
327
336
  margin-top: calc(var(--spacing) * 2);
328
337
  }
@@ -395,6 +404,9 @@
395
404
  .max-h-0 {
396
405
  max-height: calc(var(--spacing) * 0);
397
406
  }
407
+ .max-h-72 {
408
+ max-height: calc(var(--spacing) * 72);
409
+ }
398
410
  .max-h-\[calc\(100vh-4rem\)\] {
399
411
  max-height: calc(100vh - 4rem);
400
412
  }
@@ -598,6 +610,9 @@
598
610
  text-overflow: ellipsis;
599
611
  white-space: nowrap;
600
612
  }
613
+ .overflow-auto {
614
+ overflow: auto;
615
+ }
601
616
  .overflow-hidden {
602
617
  overflow: hidden;
603
618
  }
@@ -633,6 +648,10 @@
633
648
  border-style: var(--tw-border-style);
634
649
  border-width: 0.10rem;
635
650
  }
651
+ .border-b {
652
+ border-bottom-style: var(--tw-border-style);
653
+ border-bottom-width: 1px;
654
+ }
636
655
  .border-none {
637
656
  --tw-border-style: none;
638
657
  border-style: none;
@@ -715,6 +734,9 @@
715
734
  .object-contain {
716
735
  object-fit: contain;
717
736
  }
737
+ .p-0 {
738
+ padding: calc(var(--spacing) * 0);
739
+ }
718
740
  .p-1 {
719
741
  padding: calc(var(--spacing) * 1);
720
742
  }
@@ -920,6 +942,10 @@
920
942
  --tw-shadow: 0 0.1rem 0.5rem var(--tw-shadow-color, rgba(3,3,3,0.5));
921
943
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
922
944
  }
945
+ .shadow-lg {
946
+ --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
947
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
948
+ }
923
949
  .shadow-md {
924
950
  --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
925
951
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -1093,6 +1119,11 @@
1093
1119
  top: calc(var(--spacing) * 2);
1094
1120
  }
1095
1121
  }
1122
+ .peer-focus\:block {
1123
+ &:is(:where(.peer):focus ~ *) {
1124
+ display: block;
1125
+ }
1126
+ }
1096
1127
  .peer-focus\:w-fit {
1097
1128
  &:is(:where(.peer):focus ~ *) {
1098
1129
  width: fit-content;
@@ -2,6 +2,7 @@ export { default as Input } from './inputs/input';
2
2
  export { default as SwitchInput } from './inputs/switch';
3
3
  export { default as TagInput } from './inputs/tag';
4
4
  export { default as Textarea } from './inputs/markdown';
5
+ export { default as Select } from './inputs/select';
5
6
  export { default as Logo } from './logo/logo';
6
7
  export { default as LogoSmall } from './logo/logoSmall';
7
8
  export { default as ThemeToggle } from './toggle/theme';
@@ -3,6 +3,7 @@ export { default as Input } from './inputs/input';
3
3
  export { default as SwitchInput } from './inputs/switch';
4
4
  export { default as TagInput } from './inputs/tag';
5
5
  export { default as Textarea } from './inputs/markdown';
6
+ export { default as Select } from './inputs/select';
6
7
  // Logos
7
8
  export { default as Logo } from './logo/logo';
8
9
  export { default as LogoSmall } from './logo/logoSmall';
@@ -0,0 +1,19 @@
1
+ type Option = {
2
+ value: string | number;
3
+ label: string;
4
+ image?: string;
5
+ };
6
+ type SelectProps = {
7
+ name: string;
8
+ label: string;
9
+ value: string | number;
10
+ setValue: (_: string | number) => void;
11
+ options: Option[];
12
+ className?: string;
13
+ tooltip?: string;
14
+ required?: boolean;
15
+ children?: React.ReactNode;
16
+ color?: string;
17
+ };
18
+ export default function Select({ name, label, value, options, className, tooltip, required, children, setValue, color }: SelectProps): import("react/jsx-runtime").JSX.Element;
19
+ export {};
@@ -0,0 +1,53 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useRef, useState } from 'react';
4
+ import ToolTip from './tooltip';
5
+ import Label from './label';
6
+ import EraseButton from './erase';
7
+ export default function Select({ name, label, value, options, className, tooltip, required, children, setValue, color }) {
8
+ const [hasBlured, setHasBlured] = useState(false);
9
+ const selectRef = useRef(null);
10
+ const selectedOption = options.find((o) => o.value === value);
11
+ function handleChoose(value) {
12
+ setValue(value);
13
+ setHasBlured(true);
14
+ if (selectRef.current) {
15
+ selectRef.current.value = String(value);
16
+ selectRef.current.blur();
17
+ }
18
+ }
19
+ return (_jsxs("div", { className: `w-full ${className}`, children: [_jsxs("div", { className: 'relative flex items-center', children: [_jsxs("select", { ref: selectRef, name: name, className: 'peer cursor-pointer block px-2.5 pb-2.5 pt-4 ' +
20
+ 'w-full text-sm rounded-lg border-[0.10rem] ' +
21
+ 'appearance-none border-login-200 focus:ring-0 ' +
22
+ 'focus:outline-none focus:border-login-50 ' +
23
+ `${color ? color : 'bg-login-800'}`, value: value, onChange: (e) => {
24
+ setValue(e.target.value);
25
+ setHasBlured(true);
26
+ }, onBlur: () => setHasBlured(true), onMouseDown: (e) => {
27
+ e.preventDefault();
28
+ selectRef.current?.focus();
29
+ }, required: required, children: [_jsx("option", { value: '', hidden: true }), options.map((option) => (_jsx("option", { value: option.value, children: option.label }, option.value)))] }), _jsx(Label, { label: label, value: value, required: required, color: color, showRequired: required && !value && hasBlured }), value && (_jsx(EraseButton, { setData: (v) => {
30
+ setValue(v);
31
+ setHasBlured(true);
32
+ } })), !value && tooltip && _jsx(ToolTip, { info: tooltip }), _jsx(SelectContent, { options: options, value: value, selectedOption: selectedOption, handleChoose: handleChoose, color: color })] }), children] }));
33
+ }
34
+ function SelectContent({ options, value, selectedOption, handleChoose, color }) {
35
+ return (_jsx("div", { className: 'hidden peer-focus:block absolute left-0 ' +
36
+ 'right-0 top-full mt-1 z-50', children: _jsx("div", { className: `${color ? color : 'bg-login-800'}` + ' border-[0.10rem] border-login-200 ' +
37
+ 'rounded-lg shadow-lg p-0 max-h-72 overflow-hidden', children: _jsxs("div", { className: 'max-h-72 overflow-auto', children: [_jsx(SelectedOption, { value: value, selectedOption: selectedOption }), _jsx("div", { className: 'p-2', children: options
38
+ .filter((o) => o.value !== value)
39
+ .map((opt) => (_jsx("button", { type: 'button', className: 'cursor-pointer w-full flex ' +
40
+ 'items-center gap-3 px-2 py-2 ' +
41
+ 'text-sm hover:bg-surface ' +
42
+ 'rounded hover:bg-login-600', onMouseDown: (e) => {
43
+ e.preventDefault();
44
+ handleChoose(opt.value);
45
+ }, children: _jsx("span", { className: 'text-left', children: opt.label }) }, opt.value))) })] }) }) }));
46
+ }
47
+ function SelectedOption({ value, selectedOption }) {
48
+ if (!value) {
49
+ return _jsx(_Fragment, {});
50
+ }
51
+ return (_jsx("div", { className: 'sticky top-0 bg-surface px-2 py-2 z-10 border-b ' +
52
+ 'border-login-200 bg-login-600', children: _jsx("div", { className: 'flex items-center gap-3', children: _jsx("span", { className: 'font-medium text-left', children: selectedOption?.label }) }) }));
53
+ }
@@ -12,7 +12,7 @@ export default async function AuthToken({ req, frontendURL, redirectPath }) {
12
12
  const accessToken = url.searchParams.get('access_token');
13
13
  const userID = url.searchParams.get('id');
14
14
  const username = url.searchParams.get('name');
15
- const userNickname = url.searchParams.get('nickname');
15
+ const userNickname = url.searchParams.get('username');
16
16
  const userEmail = url.searchParams.get('email');
17
17
  const userGroups = url.searchParams.get('groups');
18
18
  const response = NextResponse.redirect(new URL(redirectPath || '/', frontendURL));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uibee",
3
- "version": "2.2.6",
3
+ "version": "2.2.8",
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": {
@@ -3,6 +3,7 @@ export { default as Input } from './inputs/input'
3
3
  export { default as SwitchInput } from './inputs/switch'
4
4
  export { default as TagInput } from './inputs/tag'
5
5
  export { default as Textarea } from './inputs/markdown'
6
+ export { default as Select } from './inputs/select'
6
7
 
7
8
  // Logos
8
9
  export { default as Logo } from './logo/logo'
@@ -0,0 +1,204 @@
1
+ 'use client'
2
+
3
+ import { useRef, useState } from 'react'
4
+ import ToolTip from './tooltip'
5
+ import Label from './label'
6
+ import EraseButton from './erase'
7
+
8
+ type Option = {
9
+ value: string | number;
10
+ label: string;
11
+ image?: string
12
+ }
13
+
14
+
15
+ type SelectProps = {
16
+ name: string
17
+ label: string
18
+ value: string | number
19
+ setValue: (_: string | number) => void
20
+ options: Option[]
21
+ className?: string
22
+ tooltip?: string
23
+ required?: boolean
24
+ children?: React.ReactNode
25
+ color?: string
26
+ }
27
+
28
+ type SelectedOptionProps = {
29
+ value: string | number
30
+ selectedOption: Option | undefined
31
+ }
32
+
33
+ export default function Select({
34
+ name,
35
+ label,
36
+ value,
37
+ options,
38
+ className,
39
+ tooltip,
40
+ required,
41
+ children,
42
+ setValue,
43
+ color
44
+ }: SelectProps) {
45
+ const [hasBlured, setHasBlured] = useState(false)
46
+ const selectRef = useRef<HTMLSelectElement | null>(null)
47
+ const selectedOption = options.find((o) => o.value === value)
48
+
49
+ function handleChoose(value: string | number) {
50
+ setValue(value)
51
+ setHasBlured(true)
52
+ if (selectRef.current) {
53
+ selectRef.current.value = String(value)
54
+ selectRef.current.blur()
55
+ }
56
+ }
57
+
58
+ return (
59
+ <div className={`w-full ${className}`}>
60
+ <div className='relative flex items-center'>
61
+ <select
62
+ ref={selectRef}
63
+ name={name}
64
+ className={
65
+ 'peer cursor-pointer block px-2.5 pb-2.5 pt-4 ' +
66
+ 'w-full text-sm rounded-lg border-[0.10rem] ' +
67
+ 'appearance-none border-login-200 focus:ring-0 ' +
68
+ 'focus:outline-none focus:border-login-50 ' +
69
+ `${color ? color : 'bg-login-800'}`
70
+ }
71
+ value={value}
72
+ onChange={(e) => {
73
+ setValue(e.target.value)
74
+ setHasBlured(true)
75
+ }}
76
+ onBlur={() => setHasBlured(true)}
77
+ onMouseDown={(e) => {
78
+ e.preventDefault()
79
+ selectRef.current?.focus()
80
+ }}
81
+ required={required}
82
+ >
83
+ <option value='' hidden />
84
+ {options.map((option) => (
85
+ <option key={option.value} value={option.value}>
86
+ {option.label}
87
+ </option>
88
+ ))}
89
+ </select>
90
+
91
+ <Label
92
+ label={label}
93
+ value={value}
94
+ required={required}
95
+ color={color}
96
+ showRequired={required && !value && hasBlured}
97
+ />
98
+
99
+ {value && (
100
+ <EraseButton
101
+ setData={(v: string) => {
102
+ setValue(v)
103
+ setHasBlured(true)
104
+ }}
105
+ />
106
+ )}
107
+ {!value && tooltip && <ToolTip info={tooltip} />}
108
+
109
+ <SelectContent
110
+ options={options}
111
+ value={value}
112
+ selectedOption={selectedOption}
113
+ handleChoose={handleChoose}
114
+ color={color}
115
+ />
116
+ </div>
117
+ {children}
118
+ </div>
119
+ )
120
+ }
121
+
122
+ function SelectContent({
123
+ options,
124
+ value,
125
+ selectedOption,
126
+ handleChoose,
127
+ color
128
+ }: {
129
+ options: Option[]
130
+ value: string | number
131
+ selectedOption: Option | undefined
132
+ handleChoose: (value: string | number) => void
133
+ color?: string
134
+ }) {
135
+ return (
136
+ <div
137
+ className={
138
+ 'hidden peer-focus:block absolute left-0 ' +
139
+ 'right-0 top-full mt-1 z-50'
140
+ }
141
+ >
142
+ <div
143
+ className={
144
+ `${color ? color : 'bg-login-800'}` + ' border-[0.10rem] border-login-200 ' +
145
+ 'rounded-lg shadow-lg p-0 max-h-72 overflow-hidden'
146
+ }
147
+ >
148
+ <div className='max-h-72 overflow-auto'>
149
+ <SelectedOption
150
+ value={value}
151
+ selectedOption={selectedOption}
152
+ />
153
+ <div className='p-2'>
154
+ {options
155
+ .filter((o) => o.value !== value)
156
+ .map((opt) => (
157
+ <button
158
+ key={opt.value}
159
+ type='button'
160
+ className={
161
+ 'cursor-pointer w-full flex ' +
162
+ 'items-center gap-3 px-2 py-2 ' +
163
+ 'text-sm hover:bg-surface ' +
164
+ 'rounded hover:bg-login-600'
165
+ }
166
+ onMouseDown={(e) => {
167
+ e.preventDefault()
168
+ handleChoose(opt.value)
169
+ }}
170
+ >
171
+ <span className='text-left'>
172
+ {opt.label}
173
+ </span>
174
+ </button>
175
+ ))}
176
+ </div>
177
+ </div>
178
+ </div>
179
+ </div>
180
+ )
181
+ }
182
+
183
+ function SelectedOption({ value, selectedOption }: SelectedOptionProps) {
184
+ if (!value) {
185
+ return <></>
186
+ }
187
+
188
+ return (
189
+ <div
190
+ className={
191
+ 'sticky top-0 bg-surface px-2 py-2 z-10 border-b ' +
192
+ 'border-login-200 bg-login-600'
193
+ }
194
+ >
195
+ <div className='flex items-center gap-3'>
196
+ <span className='font-medium text-left'>
197
+ {selectedOption?.label}
198
+ </span>
199
+ </div>
200
+ </div>
201
+ )
202
+ }
203
+
204
+
@@ -16,7 +16,7 @@ export default async function AuthToken({ req, frontendURL, redirectPath }: Auth
16
16
  const accessToken = url.searchParams.get('access_token')!
17
17
  const userID = url.searchParams.get('id')!
18
18
  const username = url.searchParams.get('name')!
19
- const userNickname = url.searchParams.get('nickname')!
19
+ const userNickname = url.searchParams.get('username')!
20
20
  const userEmail = url.searchParams.get('email')!
21
21
  const userGroups = url.searchParams.get('groups')!
22
22