uibee 2.2.7 → 2.2.9

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
  }
@@ -539,6 +551,9 @@
539
551
  .animate-jump {
540
552
  animation: jump 0.4s 1;
541
553
  }
554
+ .cursor-none {
555
+ cursor: none;
556
+ }
542
557
  .cursor-pointer {
543
558
  cursor: pointer;
544
559
  }
@@ -598,6 +613,9 @@
598
613
  text-overflow: ellipsis;
599
614
  white-space: nowrap;
600
615
  }
616
+ .overflow-auto {
617
+ overflow: auto;
618
+ }
601
619
  .overflow-hidden {
602
620
  overflow: hidden;
603
621
  }
@@ -633,6 +651,10 @@
633
651
  border-style: var(--tw-border-style);
634
652
  border-width: 0.10rem;
635
653
  }
654
+ .border-b {
655
+ border-bottom-style: var(--tw-border-style);
656
+ border-bottom-width: 1px;
657
+ }
636
658
  .border-none {
637
659
  --tw-border-style: none;
638
660
  border-style: none;
@@ -715,6 +737,9 @@
715
737
  .object-contain {
716
738
  object-fit: contain;
717
739
  }
740
+ .p-0 {
741
+ padding: calc(var(--spacing) * 0);
742
+ }
718
743
  .p-1 {
719
744
  padding: calc(var(--spacing) * 1);
720
745
  }
@@ -920,6 +945,10 @@
920
945
  --tw-shadow: 0 0.1rem 0.5rem var(--tw-shadow-color, rgba(3,3,3,0.5));
921
946
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
922
947
  }
948
+ .shadow-lg {
949
+ --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));
950
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
951
+ }
923
952
  .shadow-md {
924
953
  --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
954
  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 +1122,11 @@
1093
1122
  top: calc(var(--spacing) * 2);
1094
1123
  }
1095
1124
  }
1125
+ .peer-focus\:block {
1126
+ &:is(:where(.peer):focus ~ *) {
1127
+ display: block;
1128
+ }
1129
+ }
1096
1130
  .peer-focus\:w-fit {
1097
1131
  &:is(:where(.peer):focus ~ *) {
1098
1132
  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
+ }
@@ -8,6 +8,6 @@ export default function LoginPage({ title, description, redirectURL, version, bt
8
8
  handleSubmit?.(new FormData(e.currentTarget));
9
9
  e.currentTarget.reset();
10
10
  }, children: [_jsx("input", { type: 'text', name: 'name', placeholder: 'Name', className: 'py-2 px-3 rounded bg-login-900 font-medium focus:outline-none', required: true }), _jsx("input", { type: 'password', name: 'token', placeholder: 'Token', className: 'py-2 px-3 rounded bg-login-900 font-medium focus:outline-none', required: true }), _jsx("button", { type: 'submit', className: 'py-2 px-4 rounded-xl bg-login font-bold text-lg ' +
11
- 'hover:bg-login/80 transition-all duration-200 mt-2', children: "Login" })] })) : (_jsx(Link, { href: redirectURL, className: 'w-full flex justify-center', children: _jsxs("button", { className: 'flex items-center justify-center gap-2 w-full max-w-xs py-3 px-6 rounded-xl bg-login font-bold text-lg ' +
12
- 'hover:bg-login/80 transition-all duration-200 mb-2 mt-2', children: ["Login", _jsx(LogIn, { className: 'w-6 h-6' })] }) })), _jsxs("span", { className: 'text-sm mt-2', children: ["v", version] })] }) }));
11
+ 'hover:bg-login/80 transition-all duration-200 mt-2', children: "Login" })] })) : (_jsx(Link, { href: redirectURL, className: 'w-full flex justify-center cursor-none', children: _jsxs("button", { className: 'flex items-center justify-center gap-2 w-full max-w-xs py-3 px-6 rounded-xl bg-login font-bold text-lg ' +
12
+ 'hover:bg-login/80 transition-all duration-200 mb-2 mt-2 cursor-pointer', children: ["Login", _jsx(LogIn, { className: 'w-6 h-6' })] }) })), _jsxs("span", { className: 'text-sm mt-2', children: ["v", version] })] }) }));
13
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uibee",
3
- "version": "2.2.7",
3
+ "version": "2.2.9",
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
+
@@ -56,11 +56,11 @@ export default function LoginPage({title, description, redirectURL, version, btg
56
56
  </button>
57
57
  </form>
58
58
  ) : (
59
- <Link href={redirectURL} className='w-full flex justify-center'>
59
+ <Link href={redirectURL} className='w-full flex justify-center cursor-none'>
60
60
  <button
61
61
  className={
62
62
  'flex items-center justify-center gap-2 w-full max-w-xs py-3 px-6 rounded-xl bg-login font-bold text-lg ' +
63
- 'hover:bg-login/80 transition-all duration-200 mb-2 mt-2'
63
+ 'hover:bg-login/80 transition-all duration-200 mb-2 mt-2 cursor-pointer'
64
64
  }
65
65
  >
66
66
  Login