@umodeapporg/ui 0.12.0 → 0.13.1

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 (37) hide show
  1. package/dist/components/Form/ControlledButton.d.ts +12 -0
  2. package/dist/components/Form/ControlledButton.js +10 -5
  3. package/dist/components/Form/ControlledButton.js.map +1 -1
  4. package/dist/components/Form/ControlledDialog.d.ts +9 -2
  5. package/dist/components/Form/ControlledDialog.js +14 -8
  6. package/dist/components/Form/ControlledDialog.js.map +1 -1
  7. package/dist/components/Form/ControlledDialogPrimitives.d.ts +23 -0
  8. package/dist/components/Form/ControlledDialogPrimitives.js +114 -5
  9. package/dist/components/Form/ControlledDialogPrimitives.js.map +1 -1
  10. package/dist/components/Form/ControlledSelect.d.ts +11 -2
  11. package/dist/components/Form/ControlledSelect.js +273 -48
  12. package/dist/components/Form/ControlledSelect.js.map +1 -1
  13. package/dist/components/Form/ControlledSwitch.d.ts +9 -2
  14. package/dist/components/Form/ControlledSwitch.js +88 -69
  15. package/dist/components/Form/ControlledSwitch.js.map +1 -1
  16. package/dist/components/Navigation/LinkButton.d.ts +26 -0
  17. package/dist/components/Navigation/LinkButton.js +82 -0
  18. package/dist/components/Navigation/LinkButton.js.map +1 -0
  19. package/dist/components/UI/ControlledMenu.d.ts +6 -0
  20. package/dist/components/UI/ControlledMenu.js +3 -4
  21. package/dist/components/UI/ControlledMenu.js.map +1 -1
  22. package/dist/components/UI/DataTable/DataTable.js +1 -0
  23. package/dist/components/UI/DataTable/DataTable.js.map +1 -1
  24. package/dist/components/UI/DataTable/DataTableBulkEditRowsPicker.js +2 -1
  25. package/dist/components/UI/DataTable/DataTableBulkEditRowsPicker.js.map +1 -1
  26. package/dist/components/UI/DataTable/DataTableFilters.js +1 -0
  27. package/dist/components/UI/DataTable/DataTableFilters.js.map +1 -1
  28. package/dist/components/UI/DataTable/DataTableToolbar.js +2 -1
  29. package/dist/components/UI/DataTable/DataTableToolbar.js.map +1 -1
  30. package/dist/components/UI/DataTable/applyDataTableFilters.js +1 -0
  31. package/dist/components/UI/DataTable/applyDataTableFilters.js.map +1 -1
  32. package/dist/components/UI/NoDataFound/index.js +1 -1
  33. package/dist/components/UI/NoDataFound/index.js.map +1 -1
  34. package/dist/index.d.ts +2 -1
  35. package/dist/index.js +50 -47
  36. package/dist/index.js.map +1 -1
  37. package/package.json +9 -2
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/components/UI/NoDataFound/index.tsx"],"sourcesContent":["import { isValidElement, memo } from 'react';\nimport classNames from 'classnames';\nimport { useTranslation } from 'react-i18next';\nimport { twMerge } from 'tailwind-merge';\n\nimport ControlledButton from '../../Form/ControlledButton';\nimport { MaterialSymbol, SymbolCodepoints } from '../MaterialSymbol';\n\nimport {\n NO_DATA_FOUND_SIZES,\n NO_DATA_FOUND_TONES,\n NO_DATA_FOUND_VARIANTS,\n TNoDataFoundSize,\n TNoDataFoundTone,\n TNoDataFoundVariant,\n} from './noDataFoundPresets';\n\nexport type { TNoDataFoundSize, TNoDataFoundTone, TNoDataFoundVariant } from './noDataFoundPresets';\n\nexport interface INoDataFoundAction {\n label: string;\n onClick: () => void;\n icon?: SymbolCodepoints;\n disabled?: boolean;\n}\n\ntype TNoDataFoundActionProp = INoDataFoundAction | React.ReactNode;\n\nexport interface INoDataFoundProps {\n variant?: TNoDataFoundVariant;\n size?: TNoDataFoundSize;\n tone?: TNoDataFoundTone;\n title?: string;\n /** Alias of `title`, kept because most call sites already speak this name. */\n message?: string;\n description?: string;\n icon?: SymbolCodepoints;\n illustration?: React.ReactNode;\n action?: TNoDataFoundActionProp;\n secondaryAction?: TNoDataFoundActionProp;\n /** Sugar for `size=\"inline\"`: a single row for dropdowns, list slots and dense panels. */\n compact?: boolean;\n children?: React.ReactNode;\n className?: string;\n /** Turn off the live region when the host already announces the state itself. */\n announce?: boolean;\n}\n\ntype TToneClasses = (typeof NO_DATA_FOUND_TONES)[TNoDataFoundTone];\ntype TSizeClasses = (typeof NO_DATA_FOUND_SIZES)[Exclude<TNoDataFoundSize, 'inline'>];\n\nconst isActionConfig = (action: TNoDataFoundActionProp): action is INoDataFoundAction =>\n typeof action === 'object' && action !== null && !isValidElement(action) && 'label' in action;\n\ninterface INoDataFoundVisualProps {\n icon: SymbolCodepoints;\n toneClasses: TToneClasses;\n sizeClasses: TSizeClasses;\n}\n\nconst NoDataFoundVisual: React.FC<INoDataFoundVisualProps> = ({ icon, toneClasses, sizeClasses }) => (\n <span\n aria-hidden\n className={classNames(\n 'relative flex shrink-0 animate-state-reveal items-center justify-center',\n sizeClasses.visual,\n )}\n >\n <span\n className={classNames(\n 'absolute inset-0 m-auto animate-state-breathe rounded-full blur-2xl',\n toneClasses.aura,\n sizeClasses.aura,\n )}\n />\n <span\n className={classNames(\n 'absolute inset-0 m-auto animate-state-halo rounded-full border',\n toneClasses.halo,\n sizeClasses.halo,\n )}\n />\n <span\n className={classNames(\n 'absolute inset-0 m-auto animate-state-halo rounded-full border [animation-delay:1.5s]',\n toneClasses.halo,\n sizeClasses.halo,\n )}\n />\n\n <span\n className={classNames(\n 'relative flex animate-state-float items-center justify-center border bg-gradient-to-br shadow-elevation3 glass-tint',\n toneClasses.tile,\n sizeClasses.tile,\n )}\n >\n <MaterialSymbol icon={icon} weight={300} className={toneClasses.icon} />\n </span>\n\n <span\n className={classNames(\n 'absolute right-[6%] top-[18%] size-1.5 animate-state-drift rounded-full',\n toneClasses.accent,\n )}\n />\n <span\n className={classNames(\n 'absolute bottom-[16%] left-[9%] size-1 animate-state-drift rounded-full [animation-delay:2.5s]',\n toneClasses.accent,\n )}\n />\n <span\n className={classNames(\n 'absolute left-[24%] top-[4%] size-1 animate-state-drift rounded-full [animation-delay:5s]',\n toneClasses.accent,\n )}\n />\n </span>\n);\n\nconst NoDataFoundActionButton: React.FC<{ action: INoDataFoundAction; isPrimary: boolean }> = ({\n action,\n isPrimary,\n}) => (\n <ControlledButton\n size=\"sm\"\n variant={isPrimary ? 'filled' : 'text'}\n tone={isPrimary ? 'brand' : 'neutral'}\n disabled={action.disabled}\n onClick={action.onClick}\n className={classNames(\n 'flex items-center gap-x-1.5 px-3.5 text-xs transition-interactive duration-base',\n isPrimary\n ? 'shadow-md shadow-brand/20 hover:-translate-y-px hover:shadow-lg hover:shadow-brand/25'\n : 'text-foreground-muted hover:bg-muted-hover',\n )}\n >\n {action.icon ? <MaterialSymbol icon={action.icon} size={16} /> : null}\n <span>{action.label}</span>\n </ControlledButton>\n);\n\nconst renderAction = (action: TNoDataFoundActionProp, isPrimary: boolean): React.ReactNode => {\n if (!action) return null;\n if (isActionConfig(action)) return <NoDataFoundActionButton action={action} isPrimary={isPrimary} />;\n\n return action;\n};\n\nconst NoDataFound: React.FC<INoDataFoundProps> = ({\n variant = 'empty',\n size = 'md',\n tone,\n title,\n message,\n description,\n icon,\n illustration,\n action,\n secondaryAction,\n compact = false,\n children,\n className,\n announce = true,\n}) => {\n const { t } = useTranslation('ui');\n\n const preset = NO_DATA_FOUND_VARIANTS[variant] ?? NO_DATA_FOUND_VARIANTS.empty;\n const toneClasses = NO_DATA_FOUND_TONES[tone ?? preset.tone] ?? NO_DATA_FOUND_TONES.neutral;\n const resolvedIcon = icon ?? preset.icon;\n const resolvedTitle = title ?? message ?? t(preset.titleKey);\n const liveRegionProps = announce ? { role: 'status', 'aria-live': 'polite' as const } : {};\n\n const primaryAction = renderAction(action, true);\n const supportAction = renderAction(secondaryAction, false);\n\n if (compact || size === 'inline') {\n return (\n <div {...liveRegionProps} className={twMerge('flex w-full items-center gap-x-1.5 text-left', className)}>\n <MaterialSymbol icon={resolvedIcon} size={16} className={classNames('shrink-0', toneClasses.icon)} />\n <span className=\"min-w-0 text-xs text-foreground-subtle\">{resolvedTitle}</span>\n\n {description ? (\n <>\n <span aria-hidden className=\"size-1 shrink-0 rounded-full bg-border-emphasis\" />\n <span className=\"min-w-0 truncate text-xs text-foreground-faint\">{description}</span>\n </>\n ) : null}\n\n {primaryAction ? <span className=\"ml-auto shrink-0\">{primaryAction}</span> : null}\n </div>\n );\n }\n\n const sizeClasses = NO_DATA_FOUND_SIZES[size] ?? NO_DATA_FOUND_SIZES.md;\n\n return (\n <div\n {...liveRegionProps}\n className={twMerge(\n classNames(\n 'relative flex w-full flex-col items-center justify-center overflow-hidden text-center @container',\n sizeClasses.root,\n ),\n className,\n )}\n >\n {sizeClasses.hasTexture ? (\n <span\n aria-hidden\n className=\"pointer-events-none absolute inset-0 [mask-image:radial-gradient(60%_55%_at_50%_35%,black,transparent)]\"\n >\n <span className=\"absolute inset-0 bg-[radial-gradient(circle,rgba(148,163,184,0.3)_1px,transparent_1px)] [background-size:1.25rem_1.25rem]\" />\n </span>\n ) : null}\n\n {illustration ?? <NoDataFoundVisual icon={resolvedIcon} toneClasses={toneClasses} sizeClasses={sizeClasses} />}\n\n <p\n className={classNames(\n 'relative animate-state-rise font-semibold tracking-[-0.01em] text-foreground [animation-delay:80ms]',\n sizeClasses.titleClass,\n )}\n >\n {resolvedTitle}\n </p>\n\n {description ? (\n <p\n className={classNames(\n 'relative animate-state-rise text-balance leading-relaxed text-foreground-subtle [animation-delay:140ms]',\n sizeClasses.descriptionClass,\n )}\n >\n {description}\n </p>\n ) : null}\n\n {children ? <div className=\"relative w-full animate-state-rise [animation-delay:180ms]\">{children}</div> : null}\n\n {primaryAction || supportAction ? (\n <div\n className={classNames(\n 'relative flex animate-state-rise flex-wrap items-center justify-center gap-2 [animation-delay:220ms]',\n sizeClasses.actions,\n )}\n >\n {primaryAction}\n {supportAction}\n </div>\n ) : null}\n </div>\n );\n};\n\nexport default memo(NoDataFound);\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/components/UI/NoDataFound/index.tsx"],"sourcesContent":["import { isValidElement, memo } from 'react';\nimport classNames from 'classnames';\nimport { useTranslation } from 'react-i18next';\nimport { twMerge } from 'tailwind-merge';\n\nimport ControlledButton from '../../Form/ControlledButton';\nimport { MaterialSymbol, SymbolCodepoints } from '../MaterialSymbol';\n\nimport {\n NO_DATA_FOUND_SIZES,\n NO_DATA_FOUND_TONES,\n NO_DATA_FOUND_VARIANTS,\n TNoDataFoundSize,\n TNoDataFoundTone,\n TNoDataFoundVariant,\n} from './noDataFoundPresets';\n\nexport type { TNoDataFoundSize, TNoDataFoundTone, TNoDataFoundVariant } from './noDataFoundPresets';\n\nexport interface INoDataFoundAction {\n label: string;\n onClick: () => void;\n icon?: SymbolCodepoints;\n disabled?: boolean;\n}\n\ntype TNoDataFoundActionProp = INoDataFoundAction | React.ReactNode;\n\nexport interface INoDataFoundProps {\n variant?: TNoDataFoundVariant;\n size?: TNoDataFoundSize;\n tone?: TNoDataFoundTone;\n title?: string;\n /** Alias of `title`, kept because most call sites already speak this name. */\n message?: string;\n description?: string;\n icon?: SymbolCodepoints;\n illustration?: React.ReactNode;\n action?: TNoDataFoundActionProp;\n secondaryAction?: TNoDataFoundActionProp;\n /** Sugar for `size=\"inline\"`: a single row for dropdowns, list slots and dense panels. */\n compact?: boolean;\n children?: React.ReactNode;\n className?: string;\n /** Turn off the live region when the host already announces the state itself. */\n announce?: boolean;\n}\n\ntype TToneClasses = (typeof NO_DATA_FOUND_TONES)[TNoDataFoundTone];\ntype TSizeClasses = (typeof NO_DATA_FOUND_SIZES)[Exclude<TNoDataFoundSize, 'inline'>];\n\nconst isActionConfig = (action: TNoDataFoundActionProp): action is INoDataFoundAction =>\n typeof action === 'object' && action !== null && !isValidElement(action) && 'label' in action;\n\ninterface INoDataFoundVisualProps {\n icon: SymbolCodepoints;\n toneClasses: TToneClasses;\n sizeClasses: TSizeClasses;\n}\n\nconst NoDataFoundVisual: React.FC<INoDataFoundVisualProps> = ({ icon, toneClasses, sizeClasses }) => (\n <span\n aria-hidden\n className={classNames(\n 'relative flex shrink-0 animate-state-reveal items-center justify-center',\n sizeClasses.visual,\n )}\n >\n <span\n className={classNames(\n 'absolute inset-0 m-auto animate-state-breathe rounded-full blur-2xl',\n toneClasses.aura,\n sizeClasses.aura,\n )}\n />\n <span\n className={classNames(\n 'absolute inset-0 m-auto animate-state-halo rounded-full border',\n toneClasses.halo,\n sizeClasses.halo,\n )}\n />\n <span\n className={classNames(\n 'absolute inset-0 m-auto animate-state-halo rounded-full border [animation-delay:1.5s]',\n toneClasses.halo,\n sizeClasses.halo,\n )}\n />\n\n <span\n className={classNames(\n 'relative flex animate-state-float items-center justify-center border bg-gradient-to-br shadow-elevation3 glass-tint',\n toneClasses.tile,\n sizeClasses.tile,\n )}\n >\n <MaterialSymbol icon={icon} weight={300} className={toneClasses.icon} />\n </span>\n\n <span\n className={classNames(\n 'absolute right-[6%] top-[18%] size-1.5 animate-state-drift rounded-full',\n toneClasses.accent,\n )}\n />\n <span\n className={classNames(\n 'absolute bottom-[16%] left-[9%] size-1 animate-state-drift rounded-full [animation-delay:2.5s]',\n toneClasses.accent,\n )}\n />\n <span\n className={classNames(\n 'absolute left-[24%] top-[4%] size-1 animate-state-drift rounded-full [animation-delay:5s]',\n toneClasses.accent,\n )}\n />\n </span>\n);\n\nconst NoDataFoundActionButton: React.FC<{ action: INoDataFoundAction; isPrimary: boolean }> = ({\n action,\n isPrimary,\n}) => (\n <ControlledButton\n size=\"sm\"\n variant={isPrimary ? 'filled' : 'text'}\n tone={isPrimary ? 'brand' : 'neutral'}\n disabled={action.disabled}\n onClick={action.onClick}\n className={classNames(\n 'flex items-center gap-x-1.5 px-3.5 text-xs transition-interactive duration-base',\n isPrimary\n ? 'shadow-md shadow-brand/20 hover:-translate-y-px hover:shadow-lg hover:shadow-brand/25'\n : 'text-foreground-muted hover:bg-muted-hover',\n )}\n >\n {action.icon ? <MaterialSymbol icon={action.icon} size={16} aria-hidden /> : null}\n <span>{action.label}</span>\n </ControlledButton>\n);\n\nconst renderAction = (action: TNoDataFoundActionProp, isPrimary: boolean): React.ReactNode => {\n if (!action) return null;\n if (isActionConfig(action)) return <NoDataFoundActionButton action={action} isPrimary={isPrimary} />;\n\n return action;\n};\n\nconst NoDataFound: React.FC<INoDataFoundProps> = ({\n variant = 'empty',\n size = 'md',\n tone,\n title,\n message,\n description,\n icon,\n illustration,\n action,\n secondaryAction,\n compact = false,\n children,\n className,\n announce = true,\n}) => {\n const { t } = useTranslation('ui');\n\n const preset = NO_DATA_FOUND_VARIANTS[variant] ?? NO_DATA_FOUND_VARIANTS.empty;\n const toneClasses = NO_DATA_FOUND_TONES[tone ?? preset.tone] ?? NO_DATA_FOUND_TONES.neutral;\n const resolvedIcon = icon ?? preset.icon;\n const resolvedTitle = title ?? message ?? t(preset.titleKey);\n const liveRegionProps = announce ? { role: 'status', 'aria-live': 'polite' as const } : {};\n\n const primaryAction = renderAction(action, true);\n const supportAction = renderAction(secondaryAction, false);\n\n if (compact || size === 'inline') {\n return (\n <div {...liveRegionProps} className={twMerge('flex w-full items-center gap-x-1.5 text-left', className)}>\n <MaterialSymbol icon={resolvedIcon} size={16} className={classNames('shrink-0', toneClasses.icon)} />\n <span className=\"min-w-0 text-xs text-foreground-subtle\">{resolvedTitle}</span>\n\n {description ? (\n <>\n <span aria-hidden className=\"size-1 shrink-0 rounded-full bg-border-emphasis\" />\n <span className=\"min-w-0 truncate text-xs text-foreground-faint\">{description}</span>\n </>\n ) : null}\n\n {primaryAction ? <span className=\"ml-auto shrink-0\">{primaryAction}</span> : null}\n </div>\n );\n }\n\n const sizeClasses = NO_DATA_FOUND_SIZES[size] ?? NO_DATA_FOUND_SIZES.md;\n\n return (\n <div\n {...liveRegionProps}\n className={twMerge(\n classNames(\n 'relative flex w-full flex-col items-center justify-center overflow-hidden text-center @container',\n sizeClasses.root,\n ),\n className,\n )}\n >\n {sizeClasses.hasTexture ? (\n <span\n aria-hidden\n className=\"pointer-events-none absolute inset-0 [mask-image:radial-gradient(60%_55%_at_50%_35%,black,transparent)]\"\n >\n <span className=\"absolute inset-0 bg-[radial-gradient(circle,rgba(148,163,184,0.3)_1px,transparent_1px)] [background-size:1.25rem_1.25rem]\" />\n </span>\n ) : null}\n\n {illustration ?? <NoDataFoundVisual icon={resolvedIcon} toneClasses={toneClasses} sizeClasses={sizeClasses} />}\n\n <p\n className={classNames(\n 'relative animate-state-rise font-semibold tracking-[-0.01em] text-foreground [animation-delay:80ms]',\n sizeClasses.titleClass,\n )}\n >\n {resolvedTitle}\n </p>\n\n {description ? (\n <p\n className={classNames(\n 'relative animate-state-rise text-balance leading-relaxed text-foreground-subtle [animation-delay:140ms]',\n sizeClasses.descriptionClass,\n )}\n >\n {description}\n </p>\n ) : null}\n\n {children ? <div className=\"relative w-full animate-state-rise [animation-delay:180ms]\">{children}</div> : null}\n\n {primaryAction || supportAction ? (\n <div\n className={classNames(\n 'relative flex animate-state-rise flex-wrap items-center justify-center gap-2 [animation-delay:220ms]',\n sizeClasses.actions,\n )}\n >\n {primaryAction}\n {supportAction}\n </div>\n ) : null}\n </div>\n );\n};\n\nexport default memo(NoDataFound);\n"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { default as ControlledButton } from './components/Form/ControlledButton';
1
+ export { default as ControlledButton, getButtonClassName } from './components/Form/ControlledButton';
2
2
  export { default as ControlledCheckbox } from './components/Form/ControlledCheckbox';
3
3
  export { default as ControlledDialog } from './components/Form/ControlledDialog';
4
4
  export { default as ControlledDrawer } from './components/Form/ControlledDrawer';
@@ -12,6 +12,7 @@ export { default as ControlledTextarea } from './components/Form/ControlledTexta
12
12
  export { default as ControlledViewToggle } from './components/Form/ControlledViewToggle';
13
13
  export { default as ControlledBreadcrumb } from './components/Navigation/ControlledBreadcrumb';
14
14
  export { default as ErrorPage } from './components/Navigation/ErrorPage';
15
+ export { default as LinkButton } from './components/Navigation/LinkButton';
15
16
  export { default as PageTitle } from './components/Navigation/PageTitle';
16
17
  export * from './components/Navigation/PageTitleNavigationContext';
17
18
  export * from './components/Navigation/UiNavigationContext';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { default as default2 } from "./components/Form/ControlledButton.js";
1
+ import { default as default2, getButtonClassName } from "./components/Form/ControlledButton.js";
2
2
  import { default as default3 } from "./components/Form/ControlledCheckbox.js";
3
3
  import { default as default4 } from "./components/Form/ControlledDialog.js";
4
4
  import { default as default5 } from "./components/Form/ControlledDrawer.js";
@@ -12,34 +12,35 @@ import { default as default11 } from "./components/Form/ControlledTextarea.js";
12
12
  import { default as default12 } from "./components/Form/ControlledViewToggle.js";
13
13
  import { default as default13 } from "./components/Navigation/ControlledBreadcrumb.js";
14
14
  import { default as default14 } from "./components/Navigation/ErrorPage.js";
15
- import { default as default15 } from "./components/Navigation/PageTitle.js";
15
+ import { default as default15 } from "./components/Navigation/LinkButton.js";
16
+ import { default as default16 } from "./components/Navigation/PageTitle.js";
16
17
  import { PageTitleNavigationProvider, usePageTitleNavigation } from "./components/Navigation/PageTitleNavigationContext.js";
17
18
  import { UiNavigationProvider, useUiNavigation } from "./components/Navigation/UiNavigationContext.js";
18
- import { default as default16 } from "./components/Search/UnifiedSearchBar.js";
19
- import { default as default17 } from "./components/Shell/AppShell.js";
20
- import { default as default18 } from "./components/Shell/LanguageToggle.js";
19
+ import { default as default17 } from "./components/Search/UnifiedSearchBar.js";
20
+ import { default as default18 } from "./components/Shell/AppShell.js";
21
+ import { default as default19 } from "./components/Shell/LanguageToggle.js";
21
22
  import { resolveSidebarItemId } from "./components/Shell/SidebarMenu.js";
22
- import { default as default19 } from "./components/Shell/ThemeToggle.js";
23
+ import { default as default20 } from "./components/Shell/ThemeToggle.js";
23
24
  import { UiAccessProvider, useUiAccess } from "./components/UI/access/UiAccessContext.js";
24
- import { default as default20 } from "./components/UI/BaseDataCard.js";
25
- import { default as default21 } from "./components/UI/Chips/OutlinedChip.js";
26
- import { default as default22 } from "./components/UI/Colors.js";
27
- import { default as default23 } from "./components/UI/ColorSwatch.js";
28
- import { default as default24 } from "./components/UI/ControlledAccordion.js";
29
- import { default as default25 } from "./components/UI/ControlledCollapse.js";
30
- import { default as default26 } from "./components/UI/ControlledMenu.js";
31
- import { default as default27 } from "./components/UI/ControlledPopover.js";
32
- import { default as default28 } from "./components/UI/ControlledProgress.js";
33
- import { default as default29 } from "./components/UI/ControlledSlider.js";
34
- import { default as default30 } from "./components/UI/ControlledTooltip.js";
35
- import { default as default31 } from "./components/UI/CustomBadge.js";
36
- import { default as default32 } from "./components/UI/CustomCard.js";
37
- import { default as default33 } from "./components/UI/InformationCard.js";
38
- import { default as default34 } from "./components/UI/LoadingSpinner.js";
25
+ import { default as default21 } from "./components/UI/BaseDataCard.js";
26
+ import { default as default22 } from "./components/UI/Chips/OutlinedChip.js";
27
+ import { default as default23 } from "./components/UI/Colors.js";
28
+ import { default as default24 } from "./components/UI/ColorSwatch.js";
29
+ import { default as default25 } from "./components/UI/ControlledAccordion.js";
30
+ import { default as default26 } from "./components/UI/ControlledCollapse.js";
31
+ import { default as default27 } from "./components/UI/ControlledMenu.js";
32
+ import { default as default28 } from "./components/UI/ControlledPopover.js";
33
+ import { default as default29 } from "./components/UI/ControlledProgress.js";
34
+ import { default as default30 } from "./components/UI/ControlledSlider.js";
35
+ import { default as default31 } from "./components/UI/ControlledTooltip.js";
36
+ import { default as default32 } from "./components/UI/CustomBadge.js";
37
+ import { default as default33 } from "./components/UI/CustomCard.js";
38
+ import { default as default34 } from "./components/UI/InformationCard.js";
39
+ import { default as default35 } from "./components/UI/LoadingSpinner.js";
39
40
  import { MaterialSymbol, combineClasses } from "./components/UI/MaterialSymbol/index.js";
40
- import { default as default35 } from "./components/UI/NoDataFound/index.js";
41
- import { default as default36 } from "./components/UI/SkeletonLoading.js";
42
- import { default as default37 } from "./components/UI/UserMenu.js";
41
+ import { default as default36 } from "./components/UI/NoDataFound/index.js";
42
+ import { default as default37 } from "./components/UI/SkeletonLoading.js";
43
+ import { default as default38 } from "./components/UI/UserMenu.js";
43
44
  import { composeRefs } from "./helpers/composeRefs.js";
44
45
  import { isPressOnLayeredSurface } from "./helpers/layeredSurfaces.js";
45
46
  import { readThemeToken, useThemeChartTokens } from "./helpers/themeTokens.js";
@@ -56,49 +57,50 @@ import { uiLocales } from "./locales/index.js";
56
57
  import { collapseVariants, createDrawerVariants, dialogVariants, fadeVariants, floatingVariants, scrimVariants, tooltipVariants } from "./motion/motionPresets.js";
57
58
  import { MOTION_OFFSET_PX, enterTransition, exitTransition, motionDuration, motionEase } from "./motion/motionTokens.js";
58
59
  export {
59
- default17 as AppShell,
60
+ default18 as AppShell,
60
61
  BREAKPOINTS,
61
- default20 as BaseDataCard,
62
- default23 as ColorSwatch,
63
- default22 as Colors,
64
- default24 as ControlledAccordion,
62
+ default21 as BaseDataCard,
63
+ default24 as ColorSwatch,
64
+ default23 as Colors,
65
+ default25 as ControlledAccordion,
65
66
  default13 as ControlledBreadcrumb,
66
67
  default2 as ControlledButton,
67
68
  default3 as ControlledCheckbox,
68
- default25 as ControlledCollapse,
69
+ default26 as ControlledCollapse,
69
70
  default4 as ControlledDialog,
70
71
  default5 as ControlledDrawer,
71
72
  default6 as ControlledImage,
72
73
  default7 as ControlledInput,
73
- default26 as ControlledMenu,
74
- default27 as ControlledPopover,
75
- default28 as ControlledProgress,
74
+ default27 as ControlledMenu,
75
+ default28 as ControlledPopover,
76
+ default29 as ControlledProgress,
76
77
  default8 as ControlledRadio,
77
78
  ControlledSelect,
78
- default29 as ControlledSlider,
79
+ default30 as ControlledSlider,
79
80
  default9 as ControlledSwitch,
80
81
  default10 as ControlledTabs,
81
82
  default11 as ControlledTextarea,
82
- default30 as ControlledTooltip,
83
+ default31 as ControlledTooltip,
83
84
  default12 as ControlledViewToggle,
84
- default31 as CustomBadge,
85
- default32 as CustomCard,
85
+ default32 as CustomBadge,
86
+ default33 as CustomCard,
86
87
  default14 as ErrorPage,
87
- default33 as InformationCard,
88
- default34 as LoadingSpinner,
88
+ default34 as InformationCard,
89
+ default15 as LinkButton,
90
+ default35 as LoadingSpinner,
89
91
  MOTION_OFFSET_PX,
90
92
  MaterialSymbol,
91
- default35 as NoDataFound,
92
- default21 as OutlinedChip,
93
- default15 as PageTitle,
93
+ default36 as NoDataFound,
94
+ default22 as OutlinedChip,
95
+ default16 as PageTitle,
94
96
  PageTitleNavigationProvider,
95
- default18 as ShellLanguageToggle,
96
- default19 as ShellThemeToggle,
97
- default36 as SkeletonLoading,
97
+ default19 as ShellLanguageToggle,
98
+ default20 as ShellThemeToggle,
99
+ default37 as SkeletonLoading,
98
100
  UiAccessProvider,
99
101
  UiNavigationProvider,
100
- default16 as UnifiedSearchBar,
101
- default37 as UserMenu,
102
+ default17 as UnifiedSearchBar,
103
+ default38 as UserMenu,
102
104
  collapseVariants,
103
105
  combineClasses,
104
106
  composeRefs,
@@ -110,6 +112,7 @@ export {
110
112
  exitTransition,
111
113
  fadeVariants,
112
114
  floatingVariants,
115
+ getButtonClassName,
113
116
  isPressOnLayeredSurface,
114
117
  motionDuration,
115
118
  motionEase,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umodeapporg/ui",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -80,6 +80,7 @@
80
80
  "typecheck": "tsc -p tsconfig.json",
81
81
  "build": "npm run typecheck && vite build",
82
82
  "lint": "eslint \"src/**/*.{ts,tsx}\" --report-unused-disable-directives --max-warnings 0",
83
+ "test": "vitest run",
83
84
  "format": "prettier --write .",
84
85
  "lint:fix": "npm run format && npm run lint -- --fix",
85
86
  "audit:theme": "node scripts/theme-audit.mjs",
@@ -177,6 +178,10 @@
177
178
  "@semantic-release/changelog": "^6.0.3",
178
179
  "@semantic-release/git": "^10.0.1",
179
180
  "@tanstack/react-table": "^8.21.3",
181
+ "@testing-library/dom": "^10.4.2",
182
+ "@testing-library/jest-dom": "^6.9.1",
183
+ "@testing-library/react": "^16.3.3",
184
+ "@testing-library/user-event": "^14.6.7",
180
185
  "@types/node": "^24.13.3",
181
186
  "@types/react": "^18.3.12",
182
187
  "@types/react-dom": "^18.3.1",
@@ -195,6 +200,7 @@
195
200
  "eslint-plugin-simple-import-sort": "^12.1.1",
196
201
  "fast-glob": "^3.3.2",
197
202
  "i18next": "^26.1.0",
203
+ "jsdom": "^26.1.0",
198
204
  "konva": "^9.3.22",
199
205
  "motion": "^13.1.0",
200
206
  "postcss": "^8.4.40",
@@ -216,7 +222,8 @@
216
222
  "tailwindcss": "^3.4.7",
217
223
  "typescript": "^5.2.2",
218
224
  "vite": "^6.3.2",
219
- "vite-plugin-dts": "^4.5.4"
225
+ "vite-plugin-dts": "^4.5.4",
226
+ "vitest": "^3.2.7"
220
227
  },
221
228
  "repository": {
222
229
  "type": "git",