@agility/plenum-ui 2.0.0-rc36 → 2.0.0-rc37

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/tailwind.css CHANGED
@@ -480,6 +480,7 @@ input::placeholder,textarea::placeholder {
480
480
 
481
481
  ::-webkit-date-and-time-value {
482
482
  min-height: 1.5em;
483
+ text-align: inherit;
483
484
  }
484
485
 
485
486
  ::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agility/plenum-ui",
3
- "version": "2.0.0-rc36",
3
+ "version": "2.0.0-rc37",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -74,7 +74,7 @@ const _Button = (
74
74
  target: asLink.target,
75
75
  title: asLink.title,
76
76
  className: cn(
77
- "inline-flex items-center justify-center gap-x-2 font-medium rounded-[3px] !ring-offset-white outline-none focus-visible:ring-2 focus-visible:ring-purple-600 focus-visible:ring-offset-2 focus-within:ring-2 focus-within:ring-purple-600 focus-within:ring-offset-2 focus:ring-2 focus:ring-purple-600 focus:ring-offset-2 active:ring-2 active:ring-purple-600 active:ring-offset-2 transition-all",
77
+ "inline-flex items-center justify-center gap-x-2 font-medium p-2 rounded-[3px] !ring-offset-white outline-none focus-visible:ring-2 focus-visible:ring-purple-600 focus-visible:ring-offset-2 focus-within:ring-2 focus-within:ring-purple-600 focus-within:ring-offset-2 focus:ring-2 focus:ring-purple-600 focus:ring-offset-2 active:ring-2 active:ring-purple-600 active:ring-offset-2 transition-all",
78
78
  { "w-full": fullWidth },
79
79
  { "px-[11px] py-[7px] text-xs": size === "xs" },
80
80
  { "px-[13px] py-[9px] text-sm": size === "sm" },
@@ -149,7 +149,7 @@ const _Button = (
149
149
  <button
150
150
  type="button"
151
151
  className={cn(
152
- "inline-flex items-center justify-center gap-x-2 font-medium rounded-[3px] !ring-offset-white outline-none focus-visible:ring-2 focus-visible:ring-purple-600 focus-visible:ring-offset-2 focus-within:ring-2 focus-within:ring-purple-600 focus-within:ring-offset-2 focus:ring-2 focus:ring-purple-600 focus:ring-offset-2 active:ring-2 active:ring-purple-600 active:ring-offset-2 transition-all",
152
+ "inline-flex items-center justify-center gap-x-2 font-medium p-2 rounded-[3px] !ring-offset-white outline-none focus-visible:ring-2 focus-visible:ring-purple-600 focus-visible:ring-offset-2 focus-within:ring-2 focus-within:ring-purple-600 focus-within:ring-offset-2 focus:ring-2 focus:ring-purple-600 focus:ring-offset-2 active:ring-2 active:ring-purple-600 active:ring-offset-2 transition-all",
153
153
  { "w-full": fullWidth },
154
154
  { "px-[11px] py-[7px] text-xs": size === "xs" },
155
155
  { "px-[13px] py-[9px] text-sm": size === "sm" },
@@ -1,4 +1,4 @@
1
- import React, { HTMLAttributes, useState } from "react"
1
+ import React, { HTMLAttributes, useEffect, useMemo, useRef, useState } from "react"
2
2
  import { default as cn } from "classnames"
3
3
  import {
4
4
  useFloating,
@@ -12,12 +12,15 @@ import {
12
12
  autoPlacement,
13
13
  shift,
14
14
  FloatingPortal,
15
+ FloatingList,
15
16
  useTransitionStyles,
16
- Placement
17
+ Placement,
18
+ useListNavigation
17
19
  } from "@floating-ui/react"
18
20
 
19
21
  import { ClassNameWithAutocomplete } from "utils/types"
20
22
  import { DynamicIcon, IDynamicIconProps, UnifiedIconName } from "@/stories/atoms/icons"
23
+ import { list } from "postcss"
21
24
 
22
25
  export interface IItemProp {
23
26
  //Don't think this needs to extend HtmlButton... extends HTMLAttributes<HTMLButtonElement> {
@@ -83,11 +86,19 @@ const Dropdown: React.FC<IDropdownProps> = ({
83
86
  }: IDropdownProps): JSX.Element | null => {
84
87
  const [isOpen, setIsOpen] = useState(false)
85
88
  const [activeItem, setActiveItem] = useState<React.Key | null>(null)
89
+ const [activeIndex, setActiveIndex] = useState<number | null>(null)
90
+
91
+ const listRef = useRef<(HTMLButtonElement | null)[]>([])
86
92
 
87
93
  // Floating UI logic
88
94
  const { refs, floatingStyles, context } = useFloating({
89
95
  open: isOpen,
90
- onOpenChange: setIsOpen,
96
+ onOpenChange: (bool) => {
97
+ console.log("onOpenChange", bool)
98
+ listRef.current = []
99
+ setActiveIndex(null)
100
+ setIsOpen(bool)
101
+ },
91
102
  placement,
92
103
  middleware: [
93
104
  offset(offsetOptions ?? 10),
@@ -101,20 +112,157 @@ const Dropdown: React.FC<IDropdownProps> = ({
101
112
  const click = useClick(context)
102
113
  const dismiss = useDismiss(context)
103
114
  const role = useRole(context)
104
- const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss, role])
115
+ const listNavigation = useListNavigation(context, {
116
+ listRef,
117
+ activeIndex,
118
+ onNavigate: (index) => {
119
+ if (index !== null && listRef.current[index]) {
120
+ setActiveIndex(index)
121
+ listRef.current[index]?.focus()
122
+ }
123
+ }
124
+ })
125
+
126
+ const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([
127
+ click,
128
+ dismiss,
129
+ role,
130
+ listNavigation
131
+ ])
132
+
133
+ const ItemComponents = useMemo(
134
+ () =>
135
+ items.map((itemStack, stackIndex) => {
136
+ return itemStack.map((item, itemIndex) => {
137
+ const { key, label, icon, iconObj, iconPosition, isEmphasized, onClick, ...rest } = item
138
+ const active = activeItem && activeItem === key
139
+ const itemClass = cn(
140
+ defaultClassNames.itemClassname,
141
+ itemClassname,
142
+ "group flex cursor-pointer items-center px-4 py-2 text-sm transition-all",
143
+ {
144
+ "text-red-500": isEmphasized
145
+ },
146
+ {
147
+ "text-gray-900": !isEmphasized
148
+ },
149
+ {
150
+ "bg-gray-100 text-gray-900": active
151
+ },
152
+ active ? cn(defaultClassNames.activeItemClassname, activeItemClassname) : "",
153
+ {
154
+ "bg-gray-100 text-red-500 hover:text-red-500": active && isEmphasized
155
+ }
156
+ )
157
+ return (
158
+ <button
159
+ {...{
160
+ onClick: () => {
161
+ onClick && onClick()
162
+ setTimeout(() => {
163
+ //hide the dropdown after click
164
+ setIsOpen(false)
165
+ }, 150)
166
+ },
167
+ key: key,
168
+ id: key.toString(),
169
+ className: cn(itemClass, "w-full"),
170
+ ...rest,
171
+ ...getItemProps()
172
+ }}
173
+ ref={(node) => {
174
+ //If the list ref already contains a node with the same id do nothing, otherwise add it
175
+ if (listRef.current.some((item) => item?.id === key)) {
176
+ return
177
+ }
178
+ listRef.current.push(node)
179
+ }}
180
+ key={key}
181
+ >
182
+ <div className={cn(defaultClassNames.iconSpacingClassname, iconSpacingClassname)}>
183
+ {iconObj && (iconPosition === "leading" || iconPosition === undefined) && (
184
+ <>{iconObj}</>
185
+ )}
186
+ {icon &&
187
+ (iconPosition === "leading" || iconPosition === undefined) &&
188
+ (typeof icon === "string" ? (
189
+ <DynamicIcon
190
+ {...{
191
+ icon: icon,
192
+ className: cn(
193
+ {
194
+ "text-red-500": isEmphasized
195
+ },
196
+ "opacity-60 group"
197
+ )
198
+ }}
199
+ />
200
+ ) : (
201
+ <DynamicIcon
202
+ {...{
203
+ ...icon,
204
+ className: cn(
205
+ icon.className,
206
+ {
207
+ "text-red-500": isEmphasized
208
+ },
209
+ "opacity-60 group"
210
+ )
211
+ }}
212
+ />
213
+ ))}
214
+ <div className="whitespace-nowrap">{label}</div>
215
+ {iconObj && iconPosition === "trailing" && <>{iconObj}</>}
216
+ {icon &&
217
+ iconPosition === "trailing" &&
218
+ (typeof icon === "string" ? (
219
+ <DynamicIcon
220
+ {...{
221
+ icon: icon,
222
+ className: cn(
223
+ {
224
+ "text-red-500": isEmphasized
225
+ },
226
+ "opacity-60 group"
227
+ )
228
+ }}
229
+ />
230
+ ) : (
231
+ <DynamicIcon
232
+ {...{
233
+ ...icon,
234
+ className: cn(
235
+ icon.className,
236
+ {
237
+ "text-red-500": isEmphasized
238
+ },
239
+ "opacity-60 group"
240
+ )
241
+ }}
242
+ />
243
+ ))}
244
+ </div>
245
+ </button>
246
+ )
247
+ })
248
+ }),
249
+ [activeItem, activeItemClassname, getItemProps, iconSpacingClassname, itemClassname, items]
250
+ )
251
+
105
252
  const { isMounted, styles: transitionStyles } = useTransitionStyles(context, {
106
253
  duration: {
107
254
  open: 200,
108
255
  close: 200
109
256
  },
110
257
  initial: {
111
- opacity: 0
258
+ opacity: 0,
259
+ scale: 95
112
260
  },
113
261
  open: {
114
- opacity: 1
262
+ opacity: 1,
263
+ scale: 100
115
264
  }
116
265
  })
117
-
118
266
  return (
119
267
  <div
120
268
  {...{
@@ -152,168 +300,33 @@ const Dropdown: React.FC<IDropdownProps> = ({
152
300
  </button>
153
301
 
154
302
  {isMounted && items.length > 0 && isOpen && (
155
- <FloatingPortal>
156
- <FloatingFocusManager context={context} modal={true}>
157
- <div
158
- {...getFloatingProps()}
159
- className={cn(defaultClassNames.itemsClassname, itemsClassname)}
160
- ref={refs.setFloating}
161
- aria-labelledby={label}
162
- style={{
163
- position: context.strategy,
164
- top: Math.round(context.y ?? 0),
165
- left: Math.round(context.x ?? 0),
166
- width: "max-content",
167
- maxWidth: "min(calc(100vw - 10px), 25rem)",
168
- ...floatingStyles
169
- }}
170
- >
171
- <ul id={`${id}-list`} role="listbox" style={{ ...transitionStyles }}>
172
- {items.map((itemStack, idx) => {
173
- return (
174
- <React.Fragment key={`${idx}-list-${id}`}>
175
- {itemStack.map(
176
- (
177
- {
178
- onClick,
179
- label,
180
- key,
181
- isEmphasized,
182
- icon,
183
- iconPosition,
184
- iconObj,
185
- ...rest
186
- },
187
- idx
188
- ) => {
189
- const active = activeItem && activeItem === key
190
- const itemClass = cn(
191
- defaultClassNames.itemClassname,
192
- itemClassname,
193
- "group flex cursor-pointer items-center px-4 py-2 text-sm transition-all",
194
- {
195
- "text-red-500": isEmphasized
196
- },
197
- {
198
- "text-gray-900": !isEmphasized
199
- },
200
- {
201
- "bg-gray-100 text-gray-900": active
202
- },
203
- active
204
- ? cn(
205
- defaultClassNames.activeItemClassname,
206
- activeItemClassname
207
- )
208
- : "",
209
- {
210
- "bg-gray-100 text-red-500 hover:text-red-500":
211
- active && isEmphasized
212
- }
213
- )
214
- return (
215
- <li key={`${key}-${idx}`}>
216
- <button
217
- {...{
218
- onClick: () => {
219
- onClick && onClick()
220
- setTimeout(() => {
221
- //hide the dropdown after click
222
- setIsOpen(false)
223
- }, 150)
224
- },
225
- key: key,
226
- className: cn(itemClass, "w-full"),
227
- ...rest
228
- }}
229
- >
230
- <div
231
- className={cn(
232
- defaultClassNames.iconSpacingClassname,
233
- iconSpacingClassname
234
- )}
235
- >
236
- {iconObj &&
237
- (iconPosition === "leading" ||
238
- iconPosition === undefined) && (
239
- <>{iconObj}</>
240
- )}
241
-
242
- {icon &&
243
- (iconPosition === "leading" ||
244
- iconPosition === undefined) &&
245
- (typeof icon === "string" ? (
246
- <DynamicIcon
247
- {...{
248
- icon: icon,
249
- className: cn(
250
- {
251
- "text-red-500": isEmphasized
252
- },
253
- "opacity-60 group"
254
- )
255
- }}
256
- />
257
- ) : (
258
- <DynamicIcon
259
- {...{
260
- ...icon,
261
- className: cn(
262
- icon.className,
263
- {
264
- "text-red-500": isEmphasized
265
- },
266
- "opacity-60 group"
267
- )
268
- }}
269
- />
270
- ))}
271
- <div className="whitespace-nowrap">{label}</div>
272
- {iconObj && iconPosition === "trailing" && (
273
- <>{iconObj}</>
274
- )}
275
-
276
- {icon &&
277
- iconPosition === "trailing" &&
278
- (typeof icon === "string" ? (
279
- <DynamicIcon
280
- {...{
281
- icon: icon,
282
- className: cn(
283
- {
284
- "text-red-500": isEmphasized
285
- },
286
- "opacity-60 group"
287
- )
288
- }}
289
- />
290
- ) : (
291
- <DynamicIcon
292
- {...{
293
- ...icon,
294
- className: cn(
295
- icon.className,
296
- {
297
- "text-red-500": isEmphasized
298
- },
299
- "opacity-60 group"
300
- )
301
- }}
302
- />
303
- ))}
304
- </div>
305
- </button>
306
- </li>
307
- )
308
- }
309
- )}
310
- </React.Fragment>
311
- )
312
- })}
313
- </ul>
314
- </div>
315
- </FloatingFocusManager>
316
- </FloatingPortal>
303
+ <FloatingList
304
+ {...{
305
+ elementsRef: listRef
306
+ }}
307
+ >
308
+ <FloatingPortal>
309
+ <FloatingFocusManager context={context} modal={true}>
310
+ <div
311
+ {...getFloatingProps()}
312
+ className={cn(defaultClassNames.itemsClassname, itemsClassname)}
313
+ ref={refs.setFloating}
314
+ aria-labelledby={label}
315
+ style={{
316
+ position: context.strategy,
317
+ top: Math.round(context.y ?? 0),
318
+ left: Math.round(context.x ?? 0),
319
+ width: "max-content",
320
+ maxWidth: "min(calc(100vw - 10px), 25rem)",
321
+ ...floatingStyles,
322
+ ...transitionStyles
323
+ }}
324
+ >
325
+ {ItemComponents}
326
+ </div>
327
+ </FloatingFocusManager>
328
+ </FloatingPortal>
329
+ </FloatingList>
317
330
  )}
318
331
  </div>
319
332
  )
@@ -14,9 +14,8 @@ export const dropdownDataBase: IItemProp[][] = [
14
14
  onClick: () => {
15
15
  console.log("Copy action")
16
16
  }
17
- }
18
- ],
19
- [
17
+ },
18
+
20
19
  {
21
20
  icon: {
22
21
  icon: "IconPlus"
@@ -42,9 +41,7 @@ export const dropdownDataBase: IItemProp[][] = [
42
41
  onClick: () => {
43
42
  console.log("View Batch action")
44
43
  }
45
- }
46
- ],
47
- [
44
+ },
48
45
  {
49
46
  icon: {
50
47
  icon: "IconTrash"