@udixio/ui-react 2.10.2 → 2.10.4

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 (35) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/index.cjs +3 -3
  3. package/dist/index.js +1990 -2014
  4. package/dist/lib/components/ContextMenu.d.ts +1 -1
  5. package/dist/lib/components/ContextMenu.d.ts.map +1 -1
  6. package/dist/lib/components/Menu.d.ts +1 -1
  7. package/dist/lib/components/Menu.d.ts.map +1 -1
  8. package/dist/lib/components/MenuItem.d.ts +1 -1
  9. package/dist/lib/components/MenuItem.d.ts.map +1 -1
  10. package/dist/lib/components/TextField.d.ts.map +1 -1
  11. package/dist/lib/interfaces/index.d.ts +2 -0
  12. package/dist/lib/interfaces/index.d.ts.map +1 -1
  13. package/dist/lib/interfaces/menu-item.interface.d.ts +19 -25
  14. package/dist/lib/interfaces/menu-item.interface.d.ts.map +1 -1
  15. package/dist/lib/interfaces/menu.interface.d.ts +0 -1
  16. package/dist/lib/interfaces/menu.interface.d.ts.map +1 -1
  17. package/dist/lib/interfaces/text-field.interface.d.ts +2 -5
  18. package/dist/lib/interfaces/text-field.interface.d.ts.map +1 -1
  19. package/dist/lib/styles/menu-group.style.d.ts +2 -2
  20. package/dist/lib/styles/menu-item.style.d.ts +58 -14
  21. package/dist/lib/styles/menu-item.style.d.ts.map +1 -1
  22. package/dist/lib/styles/menu.style.d.ts +0 -1
  23. package/dist/lib/styles/menu.style.d.ts.map +1 -1
  24. package/dist/lib/styles/text-field.style.d.ts +2 -10
  25. package/dist/lib/styles/text-field.style.d.ts.map +1 -1
  26. package/package.json +1 -1
  27. package/src/lib/components/ContextMenu.tsx +2 -4
  28. package/src/lib/components/Menu.tsx +1 -26
  29. package/src/lib/components/MenuItem.tsx +39 -21
  30. package/src/lib/components/TextField.tsx +55 -37
  31. package/src/lib/interfaces/index.ts +2 -0
  32. package/src/lib/interfaces/menu-item.interface.ts +20 -26
  33. package/src/lib/interfaces/menu.interface.ts +0 -1
  34. package/src/lib/interfaces/text-field.interface.ts +7 -8
  35. package/src/lib/styles/menu-item.style.ts +7 -15
@@ -2,9 +2,9 @@ import React, { useEffect, useId, useMemo, useRef, useState } from 'react';
2
2
  import { Icon } from '../icon';
3
3
  import {
4
4
  faCalendarDays,
5
- faCircleExclamation,
6
5
  faChevronDown,
7
6
  faChevronUp,
7
+ faCircleExclamation,
8
8
  } from '@fortawesome/free-solid-svg-icons';
9
9
  import { motion } from 'motion/react';
10
10
  import { DatePicker } from './DatePicker';
@@ -323,7 +323,7 @@ export const TextField = ({
323
323
  });
324
324
 
325
325
  const TextComponent = multiline ? TextareaAutosize : 'input';
326
- // For select, we want the input to be readOnly but still focusable?
326
+ // For select, we want the input to be readOnly but still focusable?
327
327
  // Actually, for better UX, standard select inputs are often readOnly text fields.
328
328
  const textComponentProps = multiline
329
329
  ? {}
@@ -345,8 +345,8 @@ export const TextField = ({
345
345
  <div ref={textFieldRef} className={styles.textField} style={style}>
346
346
  <fieldset
347
347
  onClick={() => {
348
- if (isSelectInput) handleSelectToggle();
349
- else focusInput();
348
+ if (isSelectInput) handleSelectToggle();
349
+ else focusInput();
350
350
  }}
351
351
  className={styles.content}
352
352
  role="presentation"
@@ -424,11 +424,11 @@ export const TextField = ({
424
424
  name={name}
425
425
  placeholder={isFocused ? (placeholder ?? undefined) : ''}
426
426
  onFocus={() => {
427
- if(!isSelectInput) setIsFocused(true)
427
+ if (!isSelectInput) setIsFocused(true);
428
428
  }}
429
429
  onBlur={() => {
430
- // For select, we manage focus manually with menu state usually
431
- if(!isSelectInput) setIsFocused(false)
430
+ // For select, we manage focus manually with menu state usually
431
+ if (!isSelectInput) setIsFocused(false);
432
432
  }}
433
433
  disabled={disabled}
434
434
  autoComplete={autoComplete}
@@ -531,38 +531,56 @@ export const TextField = ({
531
531
  )}
532
532
 
533
533
  {isSelectInput && showMenu && (
534
- <AnchorPositioner anchorRef={textFieldRef} position="bottom" style={{ width: textFieldRef.current?.offsetWidth }}>
535
- <div ref={menuRef}>
536
- <Menu
537
- selected={value}
538
- onItemSelect={handleSelectOption}
539
- >
540
- {children}
541
- {!children && options?.map((opt, i) => {
542
- if (opt.type === 'divider') {
543
- return <Divider key={i} className="my-1" />
544
- }
545
- if (opt.type === 'headline') {
546
- return <MenuHeadline key={i} label={opt.label} />
547
- }
548
- return (
549
- <MenuItem
550
- key={opt.value ?? i}
551
- value={opt.value ?? ''}
552
- label={opt.label}
553
- leadingIcon={opt.leadingIcon}
554
- trailingIcon={opt.trailingIcon}
555
- disabled={opt.disabled}
556
- >
557
- {opt.label}
558
- </MenuItem>
559
- )
560
- })}
561
- </Menu>
562
- </div>
534
+ <AnchorPositioner
535
+ anchorRef={textFieldRef}
536
+ position="bottom"
537
+ style={{ width: textFieldRef.current?.offsetWidth }}
538
+ >
539
+ <div ref={menuRef}>
540
+ <Menu selected={value}>
541
+ {children
542
+ ? React.Children.map(children, (child) => {
543
+ if (
544
+ React.isValidElement(child) &&
545
+ child.type === MenuItem
546
+ ) {
547
+ return React.cloneElement(child, {
548
+ onClick: (e: React.MouseEvent) => {
549
+ if (child.props.onClick) {
550
+ child.props.onClick(e);
551
+ }
552
+ handleSelectOption(child.props.value ?? '');
553
+ },
554
+ } as any);
555
+ }
556
+ return child;
557
+ })
558
+ : options?.map((opt, i) => {
559
+ if (opt.type === 'divider') {
560
+ return <Divider key={i} className="my-1" />;
561
+ }
562
+ if (opt.type === 'headline') {
563
+ return <MenuHeadline key={i} label={opt.label} />;
564
+ }
565
+ return (
566
+ <MenuItem
567
+ key={opt.value ?? i}
568
+ onClick={(e) => {
569
+ if (opt.onClick) {
570
+ opt.onClick(e);
571
+ }
572
+ handleSelectOption(opt.value ?? '');
573
+ }}
574
+ {...opt}
575
+ >
576
+ {opt.label}
577
+ </MenuItem>
578
+ );
579
+ })}
580
+ </Menu>
581
+ </div>
563
582
  </AnchorPositioner>
564
583
  )}
565
584
  </div>
566
585
  );
567
586
  };
568
-
@@ -10,6 +10,8 @@ export * from './fab.interface';
10
10
  export * from './fab-menu.interface';
11
11
  export * from './icon-button.interface';
12
12
  export * from './menu.interface';
13
+ export * from './menu-group.interface';
14
+ export * from './menu-item.interface';
13
15
  export * from './progress-indicator.interface';
14
16
  export * from './side-sheet.interface';
15
17
  export * from './slider.interface';
@@ -1,35 +1,29 @@
1
- import { ComponentInterface } from '../utils/component';
1
+ import { ActionOrLink } from '../utils/component';
2
2
 
3
- export interface MenuItemInterface extends ComponentInterface {
4
- value: string | number;
3
+ type Props = {
5
4
  label?: string;
6
5
  children?: React.ReactNode;
7
6
  leadingIcon?: any;
8
7
  trailingIcon?: any;
9
8
  disabled?: boolean;
10
- selected?: boolean; // Injected by parent
11
9
  variant?: 'standard' | 'vibrant'; // Injected by parent
12
10
  onClick?: (e?: React.MouseEvent) => void;
13
- // ComponentInterface implementation
14
- type: 'div';
15
- props: {
16
- label?: string;
17
- value: string | number;
18
- leadingIcon?: any;
19
- trailingIcon?: any;
20
- disabled?: boolean;
21
- selected?: boolean;
22
- variant?: 'standard' | 'vibrant';
23
- onItemSelect?: (value: string | number) => void; // Injected
24
- children?: React.ReactNode;
11
+ onToggle?: (activated: boolean) => void;
12
+ activated?: boolean;
13
+ };
14
+
15
+ type Elements = [
16
+ 'menuItem',
17
+ 'selectedItem',
18
+ 'itemLabel',
19
+ 'itemIcon',
20
+ 'leadingIcon',
21
+ 'trailingIcon',
22
+ ];
23
+
24
+ export type MenuItemInterface = ActionOrLink<Props> & {
25
+ states: {
26
+ isActive: boolean;
25
27
  };
26
- states: Record<string, any>;
27
- elements: [
28
- 'menuItem',
29
- 'selectedItem',
30
- 'itemLabel',
31
- 'itemIcon',
32
- 'leadingIcon',
33
- 'trailingIcon',
34
- ];
35
- }
28
+ elements: Elements;
29
+ };
@@ -3,7 +3,6 @@ export type MenuStates = Record<string, any>;
3
3
  export interface MenuProps {
4
4
  children: React.ReactNode;
5
5
  selected?: string | number | (string | number)[];
6
- onItemSelect?: (value: string | number) => void;
7
6
  className?: string;
8
7
  variant?: 'standard' | 'vibrant';
9
8
  // options prop REMOVED as requested by user ("options passed as children")
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { IconButton } from '../components/IconButton';
3
3
  import { Icon } from '../icon';
4
+ import { MenuItemInterface } from './menu-item.interface';
4
5
 
5
6
  export type TextFieldVariant = 'filled' | 'outlined';
6
7
 
@@ -22,14 +23,12 @@ type Props = {
22
23
  id?: string;
23
24
  style?: React.CSSProperties;
24
25
  variant?: TextFieldVariant;
25
- options?: Array<{
26
- label: string;
27
- value: string | number;
28
- leadingIcon?: any;
29
- trailingIcon?: any;
30
- disabled?: boolean;
31
- type?: 'divider' | 'headline';
32
- }>;
26
+ options?: Array<
27
+ {
28
+ value: string | number;
29
+ type?: 'divider' | 'headline';
30
+ } & MenuItemInterface['props']
31
+ >;
33
32
  type?: 'text' | 'password' | 'number' | 'date' | 'select';
34
33
  autoComplete?: 'on' | 'off' | string;
35
34
  autoFocus?: boolean;
@@ -9,31 +9,23 @@ import { MenuItemInterface } from '../interfaces/menu-item.interface';
9
9
  const menuItemConfig: ClassNameComponent<MenuItemInterface> = ({
10
10
  variant,
11
11
  disabled,
12
- selected,
12
+ isActive,
13
13
  }) => ({
14
14
  menuItem: classNames(
15
- 'group/menu-item overflow-hidden flex items-center h-12 px-3 cursor-pointer outline-none select-none shrink-0 ',
15
+ 'group/menu-item text-start overflow-hidden flex items-center h-12 px-3 cursor-pointer outline-none select-none shrink-0 ',
16
16
  'text-label-large',
17
17
  'transition-colors duration-200',
18
- {
19
- 'rounded-sm': selected,
20
- 'rounded-xl': !selected,
21
- },
18
+ 'rounded-xl',
19
+
22
20
  {
23
21
  'text-on-surface': !variant || variant === 'standard',
24
22
  // 'hover:bg-on-surface/[0.08] focus:bg-on-surface/[0.12]': !props?.variant || props.variant === 'standard', // Handled by State
25
23
  // 'hover:bg-on-tertiary-container/[0.08] focus:bg-on-tertiary-container/[0.12]': props?.variant === 'vibrant', // Handled by State
26
24
  'opacity-38 pointer-events-none': disabled,
27
25
  },
28
- ),
29
- selectedItem: classNames(
30
- 'bg-secondary-container text-on-secondary-container',
31
- // 'hover:bg-secondary-container/[0.8]',
32
- '[&_.menu-item-icon]:text-inherit',
33
- {
34
- // For vibrant, selected state
35
- '!bg-on-tertiary-container/[0.12]': variant === 'vibrant',
36
- },
26
+ (variant === 'vibrant' || isActive) && [
27
+ 'bg-secondary-container text-on-secondary-container',
28
+ ],
37
29
  ),
38
30
  itemLabel: classNames('flex-1 truncate'),
39
31
  itemIcon: classNames(