@rovula/ui 0.1.20 → 0.1.22

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 (99) hide show
  1. package/dist/cjs/bundle.css +316 -43
  2. package/dist/cjs/bundle.js +675 -675
  3. package/dist/cjs/bundle.js.map +1 -1
  4. package/dist/cjs/types/components/Badge/Badge.d.ts +40 -0
  5. package/dist/cjs/types/components/Badge/Badge.stories.d.ts +295 -0
  6. package/dist/cjs/types/components/Badge/Badge.styles.d.ts +7 -0
  7. package/dist/cjs/types/components/Badge/index.d.ts +2 -0
  8. package/dist/cjs/types/components/Dropdown/Dropdown.d.ts +4 -8
  9. package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +1 -6
  10. package/dist/cjs/types/components/DropdownMenu/DropdownMenu.d.ts +5 -1
  11. package/dist/cjs/types/components/DropdownMenu/DropdownMenu.stories.d.ts +45 -30
  12. package/dist/cjs/types/components/Form/Form.d.ts +2 -1
  13. package/dist/cjs/types/components/Form/Form.stories.d.ts +4 -0
  14. package/dist/cjs/types/components/ScrollArea/ScrollArea.d.ts +38 -0
  15. package/dist/cjs/types/components/ScrollArea/ScrollArea.stories.d.ts +301 -0
  16. package/dist/cjs/types/index.d.ts +4 -1
  17. package/dist/cjs/types/patterns/menu/Menu.d.ts +70 -0
  18. package/dist/cjs/types/{components/Menu → patterns/menu}/Menu.stories.d.ts +17 -10
  19. package/dist/cjs/types/utils/mergeRefs.d.ts +20 -0
  20. package/dist/components/Avatar/Avatar.styles.js +2 -2
  21. package/dist/components/Badge/Badge.js +36 -0
  22. package/dist/components/Badge/Badge.stories.js +51 -0
  23. package/dist/components/Badge/Badge.styles.js +62 -0
  24. package/dist/components/Badge/index.js +2 -0
  25. package/dist/components/Dropdown/Dropdown.js +54 -163
  26. package/dist/components/Dropdown/Dropdown.stories.js +29 -0
  27. package/dist/components/DropdownMenu/DropdownMenu.js +24 -13
  28. package/dist/components/DropdownMenu/DropdownMenu.stories.js +120 -88
  29. package/dist/components/Form/Form.js +11 -4
  30. package/dist/components/Form/Form.stories.js +27 -0
  31. package/dist/components/ScrollArea/ScrollArea.js +50 -0
  32. package/dist/components/ScrollArea/ScrollArea.stories.js +56 -0
  33. package/dist/components/TextInput/TextInput.js +6 -3
  34. package/dist/esm/bundle.css +316 -43
  35. package/dist/esm/bundle.js +1545 -1545
  36. package/dist/esm/bundle.js.map +1 -1
  37. package/dist/esm/types/components/Badge/Badge.d.ts +40 -0
  38. package/dist/esm/types/components/Badge/Badge.stories.d.ts +295 -0
  39. package/dist/esm/types/components/Badge/Badge.styles.d.ts +7 -0
  40. package/dist/esm/types/components/Badge/index.d.ts +2 -0
  41. package/dist/esm/types/components/Dropdown/Dropdown.d.ts +4 -8
  42. package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +1 -6
  43. package/dist/esm/types/components/DropdownMenu/DropdownMenu.d.ts +5 -1
  44. package/dist/esm/types/components/DropdownMenu/DropdownMenu.stories.d.ts +45 -30
  45. package/dist/esm/types/components/Form/Form.d.ts +2 -1
  46. package/dist/esm/types/components/Form/Form.stories.d.ts +4 -0
  47. package/dist/esm/types/components/ScrollArea/ScrollArea.d.ts +38 -0
  48. package/dist/esm/types/components/ScrollArea/ScrollArea.stories.d.ts +301 -0
  49. package/dist/esm/types/index.d.ts +4 -1
  50. package/dist/esm/types/patterns/menu/Menu.d.ts +70 -0
  51. package/dist/esm/types/{components/Menu → patterns/menu}/Menu.stories.d.ts +17 -10
  52. package/dist/esm/types/utils/mergeRefs.d.ts +20 -0
  53. package/dist/index.d.ts +156 -74
  54. package/dist/index.js +3 -1
  55. package/dist/patterns/menu/Menu.js +95 -0
  56. package/dist/patterns/menu/Menu.stories.js +611 -0
  57. package/dist/src/theme/global.css +485 -57
  58. package/dist/utils/mergeRefs.js +42 -0
  59. package/package.json +1 -1
  60. package/src/components/Avatar/Avatar.styles.ts +2 -2
  61. package/src/components/Badge/Badge.stories.tsx +128 -0
  62. package/src/components/Badge/Badge.styles.ts +70 -0
  63. package/src/components/Badge/Badge.tsx +103 -0
  64. package/src/components/Badge/index.ts +3 -0
  65. package/src/components/Dropdown/Dropdown.stories.tsx +170 -1
  66. package/src/components/Dropdown/Dropdown.tsx +186 -276
  67. package/src/components/DropdownMenu/DropdownMenu.stories.tsx +1375 -253
  68. package/src/components/DropdownMenu/DropdownMenu.tsx +118 -55
  69. package/src/components/Form/Form.stories.tsx +70 -0
  70. package/src/components/Form/Form.tsx +23 -0
  71. package/src/components/ScrollArea/ScrollArea.stories.tsx +229 -0
  72. package/src/components/ScrollArea/ScrollArea.tsx +72 -0
  73. package/src/components/TextInput/TextInput.tsx +6 -3
  74. package/src/index.ts +4 -1
  75. package/src/patterns/menu/Menu.stories.tsx +1100 -0
  76. package/src/patterns/menu/Menu.tsx +282 -0
  77. package/src/theme/global.css +84 -11
  78. package/src/theme/themes/xspector/baseline.css +1 -1
  79. package/src/theme/themes/xspector/components/scrollbar.css +12 -0
  80. package/src/theme/tokens/baseline.css +3 -1
  81. package/src/theme/tokens/components/badge.css +54 -0
  82. package/src/theme/tokens/components/dropdown-menu.css +16 -5
  83. package/src/theme/tokens/components/scrollbar.css +18 -0
  84. package/src/utils/mergeRefs.ts +46 -0
  85. package/dist/cjs/types/components/Menu/Menu.d.ts +0 -65
  86. package/dist/cjs/types/components/Menu/helpers.d.ts +0 -19
  87. package/dist/cjs/types/components/Menu/index.d.ts +0 -4
  88. package/dist/components/Menu/Menu.js +0 -64
  89. package/dist/components/Menu/Menu.stories.js +0 -406
  90. package/dist/components/Menu/helpers.js +0 -28
  91. package/dist/components/Menu/index.js +0 -3
  92. package/dist/esm/types/components/Menu/Menu.d.ts +0 -65
  93. package/dist/esm/types/components/Menu/helpers.d.ts +0 -19
  94. package/dist/esm/types/components/Menu/index.d.ts +0 -4
  95. package/src/components/Menu/Menu.stories.tsx +0 -586
  96. package/src/components/Menu/Menu.tsx +0 -235
  97. package/src/components/Menu/helpers.ts +0 -45
  98. package/src/components/Menu/index.ts +0 -7
  99. package/src/theme/themes/xspector/components/dropdown-menu.css +0 -28
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import React__default, { ReactElement, ReactNode, CSSProperties, FC, ComponentPropsWithoutRef, HTMLAttributes, FormHTMLAttributes, ComponentType } from 'react';
2
+ import React__default, { ReactElement, ReactNode, FC, ComponentPropsWithoutRef, CSSProperties, HTMLAttributes, FormHTMLAttributes, ComponentType } from 'react';
3
3
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
4
4
  import * as class_variance_authority_dist_types from 'class-variance-authority/dist/types';
5
5
  import * as LabelPrimitive from '@radix-ui/react-label';
@@ -17,7 +17,7 @@ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
17
17
  import * as ToastPrimitives from '@radix-ui/react-toast';
18
18
  import { ToastProviderProps } from '@radix-ui/react-toast';
19
19
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
20
- import { FieldValues, UseFormReturn, DefaultValues, SubmitHandler, SubmitErrorHandler, Resolver, Mode, FieldPath, RegisterOptions, FieldPathValue } from 'react-hook-form';
20
+ import { FieldValues, UseFormReturn, DefaultValues, SubmitHandler, SubmitErrorHandler, FormState, Resolver, Mode, FieldPath, RegisterOptions, FieldPathValue } from 'react-hook-form';
21
21
  import * as yup from 'yup';
22
22
  import Button$1 from '@/components/Button/Button';
23
23
  import { ClassValue } from 'clsx';
@@ -376,70 +376,6 @@ type TabsProps = {
376
376
  };
377
377
  declare const Tabs: React__default.FC<TabsProps>;
378
378
 
379
- type MenuOption = {
380
- value: string;
381
- label: ReactNode;
382
- /**
383
- * Visual type - กำหนดว่าจะแสดง icon อะไร
384
- * - "default": ไม่มี icon (แค่ highlight background)
385
- * - "checkbox": แสดง ✓ icon
386
- * - "radio": แสดง ● icon
387
- */
388
- type?: "default" | "checkbox" | "radio";
389
- icon?: ReactNode;
390
- disabled?: boolean;
391
- danger?: boolean;
392
- checked?: boolean;
393
- onClick?: () => void;
394
- };
395
- type MenuItemType = {
396
- type: "item";
397
- item: MenuOption;
398
- } | {
399
- type: "separator";
400
- } | {
401
- type: "label";
402
- label: string;
403
- } | {
404
- type: "custom";
405
- render: () => ReactNode;
406
- };
407
- type MenuProps = {
408
- items: MenuItemType[];
409
- /**
410
- * Selected values - ใช้กับ type="item"
411
- */
412
- selectedValues?: string[];
413
- /**
414
- * Callback เมื่อเลือก item
415
- * - ถ้า item.type="checkbox" → toggle checked state
416
- * - ถ้า item.type="radio" → single select (clear others)
417
- * - ถ้า item.type="default" หรือไม่ระบุ → ตาม selectedValues
418
- */
419
- onSelect?: (value: string, item: MenuOption) => void;
420
- className?: string;
421
- style?: CSSProperties;
422
- isAbove?: boolean;
423
- };
424
- declare const Menu: React__default.ForwardRefExoticComponent<MenuProps & React__default.RefAttributes<HTMLDivElement>>;
425
- type MenuItemProps = {
426
- option: MenuOption;
427
- visualType: "default" | "checkbox" | "radio";
428
- isChecked: boolean;
429
- onSelect: () => void;
430
- className?: string;
431
- };
432
- declare const MenuItem: React__default.ForwardRefExoticComponent<MenuItemProps & React__default.RefAttributes<HTMLDivElement>>;
433
- type MenuSeparatorProps = {
434
- className?: string;
435
- };
436
- declare const MenuSeparator: React__default.ForwardRefExoticComponent<MenuSeparatorProps & React__default.RefAttributes<HTMLDivElement>>;
437
- type MenuLabelProps = {
438
- children: ReactNode;
439
- className?: string;
440
- };
441
- declare const MenuLabel: React__default.ForwardRefExoticComponent<MenuLabelProps & React__default.RefAttributes<HTMLDivElement>>;
442
-
443
379
  type RenderLabelCallbackArg$1 = {
444
380
  value: string;
445
381
  label: string;
@@ -457,7 +393,6 @@ type DropdownProps = {
457
393
  size?: "sm" | "md" | "lg";
458
394
  rounded?: "none" | "normal" | "full";
459
395
  variant?: "flat" | "outline" | "underline";
460
- defaultMenuItemType?: MenuOption["type"];
461
396
  helperText?: string;
462
397
  errorMessage?: string;
463
398
  filterMode?: boolean;
@@ -465,6 +400,7 @@ type DropdownProps = {
465
400
  disabled?: boolean;
466
401
  error?: boolean;
467
402
  required?: boolean;
403
+ /** @deprecated no longer needed */
468
404
  modal?: boolean;
469
405
  className?: string;
470
406
  optionContainerClassName?: string;
@@ -479,8 +415,6 @@ type DropdownProps = {
479
415
  optionsFiltered: Options$1[];
480
416
  selectedOption: Options$1 | null | undefined;
481
417
  onClick: (option: Options$1) => void;
482
- style?: CSSProperties;
483
- dropdownRef?: React__default.RefObject<HTMLUListElement>;
484
418
  }) => ReactNode;
485
419
  } & Omit<InputProps, "value" | "onSelect">;
486
420
  declare const Dropdown: React__default.ForwardRefExoticComponent<{
@@ -489,7 +423,6 @@ declare const Dropdown: React__default.ForwardRefExoticComponent<{
489
423
  size?: "sm" | "md" | "lg";
490
424
  rounded?: "none" | "normal" | "full";
491
425
  variant?: "flat" | "outline" | "underline";
492
- defaultMenuItemType?: MenuOption["type"];
493
426
  helperText?: string;
494
427
  errorMessage?: string;
495
428
  filterMode?: boolean;
@@ -497,6 +430,7 @@ declare const Dropdown: React__default.ForwardRefExoticComponent<{
497
430
  disabled?: boolean;
498
431
  error?: boolean;
499
432
  required?: boolean;
433
+ /** @deprecated no longer needed */
500
434
  modal?: boolean;
501
435
  className?: string;
502
436
  optionContainerClassName?: string;
@@ -511,8 +445,6 @@ declare const Dropdown: React__default.ForwardRefExoticComponent<{
511
445
  optionsFiltered: Options$1[];
512
446
  selectedOption: Options$1 | null | undefined;
513
447
  onClick: (option: Options$1) => void;
514
- style?: CSSProperties;
515
- dropdownRef?: React__default.RefObject<HTMLUListElement>;
516
448
  }) => ReactNode;
517
449
  } & Omit<InputProps, "onSelect" | "value"> & React__default.RefAttributes<HTMLInputElement>>;
518
450
 
@@ -626,6 +558,45 @@ type AvatarGroupProps = {
626
558
  };
627
559
  declare const AvatarGroup: FC<AvatarGroupProps>;
628
560
 
561
+ type BadgeColor = "default" | "success" | "warning" | "info" | "error";
562
+ type BadgeProps = {
563
+ /** Badge text label */
564
+ label: string;
565
+ /** Color variant */
566
+ color?: BadgeColor;
567
+ /**
568
+ * Show a dropdown chevron — use when the badge acts as a clickable trigger.
569
+ * Automatically adds a border to indicate interactivity.
570
+ */
571
+ clickable?: boolean;
572
+ /** Optional percentage value shown below the label */
573
+ percent?: number;
574
+ className?: string;
575
+ } & React.HTMLAttributes<HTMLSpanElement>;
576
+ declare const Badge: React.ForwardRefExoticComponent<{
577
+ /** Badge text label */
578
+ label: string;
579
+ /** Color variant */
580
+ color?: BadgeColor;
581
+ /**
582
+ * Show a dropdown chevron — use when the badge acts as a clickable trigger.
583
+ * Automatically adds a border to indicate interactivity.
584
+ */
585
+ clickable?: boolean;
586
+ /** Optional percentage value shown below the label */
587
+ percent?: number;
588
+ className?: string;
589
+ } & React.HTMLAttributes<HTMLSpanElement> & React.RefAttributes<HTMLSpanElement>>;
590
+ type SeverityLevel = "highest" | "high" | "medium" | "low" | "lowest";
591
+ type SeverityBadgeProps = {
592
+ severity: SeverityLevel;
593
+ className?: string;
594
+ } & React.HTMLAttributes<HTMLSpanElement>;
595
+ declare const SeverityBadge: React.ForwardRefExoticComponent<{
596
+ severity: SeverityLevel;
597
+ className?: string;
598
+ } & React.HTMLAttributes<HTMLSpanElement> & React.RefAttributes<HTMLSpanElement>>;
599
+
629
600
  declare const CollapsibleButton: React__default.ForwardRefExoticComponent<{
630
601
  children: React__default.ReactNode;
631
602
  className?: string;
@@ -942,9 +913,13 @@ declare const DropdownMenuSubContent: React.ForwardRefExoticComponent<Omit<Dropd
942
913
  declare const DropdownMenuContent: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
943
914
  declare const DropdownMenuItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
944
915
  inset?: boolean;
916
+ selected?: boolean;
917
+ icon?: React.ReactNode;
945
918
  } & React.RefAttributes<HTMLDivElement>>;
946
919
  declare const DropdownMenuCheckboxItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuCheckboxItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
947
- declare const DropdownMenuRadioItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuRadioItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
920
+ declare const DropdownMenuRadioItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuRadioItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
921
+ icon?: React.ReactNode;
922
+ } & React.RefAttributes<HTMLDivElement>>;
948
923
  declare const DropdownMenuLabel: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuLabelProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
949
924
  inset?: boolean;
950
925
  } & React.RefAttributes<HTMLDivElement>>;
@@ -1166,6 +1141,44 @@ type FocusedScrollViewProps = {
1166
1141
  };
1167
1142
  declare const FocusedScrollView: React__default.FC<FocusedScrollViewProps>;
1168
1143
 
1144
+ type ScrollbarSize = "m" | "s" | "xs";
1145
+ interface ScrollAreaProps extends React.HTMLAttributes<HTMLDivElement> {
1146
+ /**
1147
+ * Scrollbar thickness.
1148
+ * - `m` — 12 px (shows track border)
1149
+ * - `s` — 6 px (default, shows no track border)
1150
+ * - `xs` — 2 px (shows no track border)
1151
+ */
1152
+ scrollbarSize?: ScrollbarSize;
1153
+ /**
1154
+ * Direction(s) the area can scroll.
1155
+ * Defaults to `"vertical"`.
1156
+ */
1157
+ direction?: "vertical" | "horizontal" | "both";
1158
+ }
1159
+ /**
1160
+ * ScrollArea
1161
+ *
1162
+ * A thin wrapper that applies the design-system scrollbar style to any
1163
+ * scrollable container. Use `scrollbarSize` to pick the Figma size variant
1164
+ * and `direction` to control which axis scrolls.
1165
+ *
1166
+ * **Client usage:**
1167
+ * ```tsx
1168
+ * <ScrollArea className="max-h-[270px]">
1169
+ * {items.map(item => <div key={item.id}>{item.label}</div>)}
1170
+ * </ScrollArea>
1171
+ * ```
1172
+ *
1173
+ * For a double-scroll layout (two independent sections inside one dropdown),
1174
+ * wrap each section individually:
1175
+ * ```tsx
1176
+ * <ScrollArea className="max-h-[160px]">...section A items...</ScrollArea>
1177
+ * <ScrollArea className="max-h-[160px]">...section B items...</ScrollArea>
1178
+ * ```
1179
+ */
1180
+ declare const ScrollArea: React.ForwardRefExoticComponent<ScrollAreaProps & React.RefAttributes<HTMLDivElement>>;
1181
+
1169
1182
  declare const RadioGroup: React.ForwardRefExoticComponent<Omit<RadioGroupPrimitive.RadioGroupProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
1170
1183
  declare const RadioGroupItem: React.ForwardRefExoticComponent<Omit<RadioGroupPrimitive.RadioGroupItemProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
1171
1184
 
@@ -1184,6 +1197,7 @@ type FormProps<TFieldValues extends FieldValues> = Omit<FormHTMLAttributes<HTMLF
1184
1197
  controllerRef?: React__default.MutableRefObject<FormController<TFieldValues> | null>;
1185
1198
  onSubmit: SubmitHandler<TFieldValues>;
1186
1199
  onInvalidSubmit?: SubmitErrorHandler<TFieldValues>;
1200
+ onFormStateChange?: (formState: FormState<TFieldValues>) => void;
1187
1201
  resolver?: Resolver<TFieldValues>;
1188
1202
  validationSchema?: yup.ObjectSchema<any>;
1189
1203
  mode?: Mode;
@@ -1350,6 +1364,74 @@ type FormDialogProps = {
1350
1364
  };
1351
1365
  declare const FormDialog: React.FC<FormDialogProps>;
1352
1366
 
1367
+ type MenuOption = {
1368
+ value: string;
1369
+ label: ReactNode;
1370
+ /**
1371
+ * Visual type of the item
1372
+ * - "default": selected indicator (check icon on left)
1373
+ * - "checkbox": visual checkbox
1374
+ * - "radio": radio selection (single-select in group)
1375
+ */
1376
+ type?: "default" | "checkbox" | "radio";
1377
+ icon?: ReactNode;
1378
+ disabled?: boolean;
1379
+ danger?: boolean;
1380
+ checked?: boolean;
1381
+ onClick?: () => void;
1382
+ };
1383
+ type MenuItemType = {
1384
+ type: "item";
1385
+ item: MenuOption;
1386
+ } | {
1387
+ type: "separator";
1388
+ } | {
1389
+ type: "label";
1390
+ label: string;
1391
+ } | {
1392
+ type: "custom";
1393
+ render: () => ReactNode;
1394
+ } | {
1395
+ type: "submenu";
1396
+ label: string;
1397
+ icon?: ReactNode;
1398
+ disabled?: boolean;
1399
+ /** Nested items — rendered via DropdownMenuSub */
1400
+ items: MenuItemType[];
1401
+ };
1402
+ type MenuProps = {
1403
+ /** Trigger element — optional, ถ้าไม่ส่งให้ใช้ open/onOpenChange ควบคุมเอง */
1404
+ trigger?: ReactNode;
1405
+ items: MenuItemType[];
1406
+ selectedValues?: string[];
1407
+ /**
1408
+ * Callback on item select
1409
+ * - "checkbox" → caller toggles in selectedValues
1410
+ * - "radio" → caller sets single value in selectedValues
1411
+ * - "default" → same
1412
+ */
1413
+ onSelect?: (value: string, item: MenuOption) => void;
1414
+ /**
1415
+ * Optional header rendered above the item list — stays fixed while list scrolls.
1416
+ * ใช้สำหรับ pattern เช่น "Change Status" (title + close button)
1417
+ * หรือ "Manage Column" (title + Hide all / Show all / Done)
1418
+ */
1419
+ header?: ReactNode;
1420
+ /** Controlled open state */
1421
+ open?: boolean;
1422
+ /** Called when open state changes (controlled or uncontrolled) */
1423
+ onOpenChange?: (open: boolean) => void;
1424
+ /** Alignment of the menu content relative to the trigger */
1425
+ align?: "start" | "center" | "end";
1426
+ side?: "top" | "right" | "bottom" | "left";
1427
+ sideOffset?: number;
1428
+ contentClassName?: string;
1429
+ };
1430
+ declare const Menu: {
1431
+ ({ trigger, items, selectedValues, onSelect, header, open, onOpenChange, align, side, sideOffset, contentClassName, }: MenuProps): react_jsx_runtime.JSX.Element;
1432
+ displayName: string;
1433
+ };
1434
+
1353
1435
  declare const resloveTimestamp: (timestamp: number) => number;
1354
1436
  declare const getStartDateOfDay: (date: Date) => Date;
1355
1437
  declare const getEndDateOfDay: (date: Date) => Date;
@@ -1789,4 +1871,4 @@ declare const srgbToHex: (color: string) => string;
1789
1871
  */
1790
1872
  declare function getLucideIconNames(): Promise<string[]>;
1791
1873
 
1792
- export { ActionButton, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, Button, type ButtonProps, Calendar, Checkbox, Collapsible, ConfirmDialog, type ConfirmDialogProps, type ControlledFormFactoryOptions, type CustomSliderProps, DataTable, type DataTableProps, DatePicker, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Dropdown, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, type DropdownProps, Field, FieldMessage, type FieldMessageProps, type FieldProps, FocusedScrollView, Footer, type FooterProps, type FooterVariant, Form, type FormController, FormDialog, type FormDialogAction, type FormDialogProps, type FormProps, Icon, Input, InputFilter, type InputFilterProps, type InputProps, Label, Loading, type MaskRule, MaskedTextInput, type MaskedTextInputProps, Menu, MenuItem, type MenuItemType, MenuLabel, type MenuOption, type MenuProps, MenuSeparator, Navbar, type NavbarProps, type NavbarVariant, NumberInput, type NumberInputProps, type OptionLike, type Options$1 as Options, OtpInput, OtpInputGroup, type OtpInputGroupProps, type OtpInputProps, PasswordInput, type PasswordInputProps, Popover, PopoverContent, PopoverTrigger, ProgressBar, RadioGroup, RadioGroupItem, Search, type SearchProps, Slider, type SliderProps, Switch, THEME_COLOR_KEYS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, Text, TextArea, type TextAreaProps, TextInput, type ThemeColorKey, Toast$1 as Toast, ToastAction, type ToastActionElement, ToastClose, ToastDescription, type ToastProps, ToastProvider, ToastTitle, ToastViewport, Toaster, Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipSimple, TooltipTrigger, Tree, type TreeData, TreeItem, type TreeItemProps, type TreeProps, type UseControlledFormOptions, type UseOptionBridgeOptions, ValidationHintList, type ValidationHintListProps, type ValidationHintMode, type ValidationHintRule, type ValidationHintState, cn, createControlledForm, createYupResolver, getEndDateOfDay, getLucideIconNames, getStartDateOfDay, getStartEndTimestampOfDay, getThemeColor, getThemeColors, getTimestampUTC, reducer, resloveTimestamp, srgbToHex, toast, useControlledForm, useOptionBridge, usePrevious, useToast };
1874
+ export { ActionButton, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, Badge, type BadgeColor, type BadgeProps, Button, type ButtonProps, Calendar, Checkbox, Collapsible, ConfirmDialog, type ConfirmDialogProps, type ControlledFormFactoryOptions, type CustomSliderProps, DataTable, type DataTableProps, DatePicker, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Dropdown, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, type DropdownProps, Field, FieldMessage, type FieldMessageProps, type FieldProps, FocusedScrollView, Footer, type FooterProps, type FooterVariant, Form, type FormController, FormDialog, type FormDialogAction, type FormDialogProps, type FormProps, Icon, Input, InputFilter, type InputFilterProps, type InputProps, Label, Loading, type MaskRule, MaskedTextInput, type MaskedTextInputProps, Menu, DropdownMenuItem as MenuItem, type MenuItemType, DropdownMenuLabel as MenuLabel, type MenuOption, type MenuProps, DropdownMenuSeparator as MenuSeparator, Navbar, type NavbarProps, type NavbarVariant, NumberInput, type NumberInputProps, type OptionLike, type Options$1 as Options, OtpInput, OtpInputGroup, type OtpInputGroupProps, type OtpInputProps, PasswordInput, type PasswordInputProps, Popover, PopoverContent, PopoverTrigger, ProgressBar, RadioGroup, RadioGroupItem, ScrollArea, type ScrollAreaProps, type ScrollbarSize, Search, type SearchProps, SeverityBadge, type SeverityBadgeProps, type SeverityLevel, Slider, type SliderProps, Switch, THEME_COLOR_KEYS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, Text, TextArea, type TextAreaProps, TextInput, type ThemeColorKey, Toast$1 as Toast, ToastAction, type ToastActionElement, ToastClose, ToastDescription, type ToastProps, ToastProvider, ToastTitle, ToastViewport, Toaster, Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipSimple, TooltipTrigger, Tree, type TreeData, TreeItem, type TreeItemProps, type TreeProps, type UseControlledFormOptions, type UseOptionBridgeOptions, ValidationHintList, type ValidationHintListProps, type ValidationHintMode, type ValidationHintRule, type ValidationHintState, cn, createControlledForm, createYupResolver, getEndDateOfDay, getLucideIconNames, getStartDateOfDay, getStartEndTimestampOfDay, getThemeColor, getThemeColors, getTimestampUTC, reducer, resloveTimestamp, srgbToHex, toast, useControlledForm, useOptionBridge, usePrevious, useToast };
package/dist/index.js CHANGED
@@ -18,6 +18,7 @@ export { Navbar } from "./components/Navbar";
18
18
  export { Footer } from "./components/Footer";
19
19
  export { default as ActionButton } from "./components/ActionButton/ActionButton";
20
20
  export { Avatar, AvatarGroup } from "./components/Avatar";
21
+ export { Badge, SeverityBadge } from "./components/Badge";
21
22
  export { Collapsible } from "./components/Collapsible";
22
23
  export { Calendar } from "./components/Calendar";
23
24
  export { default as DatePicker } from "./components/DatePicker/DatePicker";
@@ -36,7 +37,6 @@ export * from "./components/InputFilter/InputFilter";
36
37
  export * from "./components/Slider/Slider";
37
38
  export * from "./components/Switch/Switch";
38
39
  export * from "./components/DropdownMenu/DropdownMenu";
39
- export * from "./components/Menu/Menu";
40
40
  export * from "./components/Tooltip/Tooltip";
41
41
  export * from "./components/Tooltip/TooltipSimple";
42
42
  export * from "./components/Toast/Toast";
@@ -44,11 +44,13 @@ export * from "./components/Toast/Toaster";
44
44
  export * from "./components/Toast/useToast";
45
45
  export * from "./components/Tree";
46
46
  export * from "./components/FocusedScrollView/FocusedScrollView";
47
+ export * from "./components/ScrollArea/ScrollArea";
47
48
  export * from "./components/RadioGroup/RadioGroup";
48
49
  export * from "./components/Form";
49
50
  // Patterns
50
51
  export * from "./patterns/confirm-dialog/ConfirmDialog";
51
52
  export * from "./patterns/form-dialog/FormDialog";
53
+ export * from "./patterns/menu/Menu";
52
54
  // UTILS
53
55
  export { resloveTimestamp, getStartDateOfDay, getEndDateOfDay, getStartEndTimestampOfDay, getTimestampUTC, } from "./utils/datetime";
54
56
  // Hooks
@@ -0,0 +1,95 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import { cn } from "@/utils/cn";
5
+ import { DropdownMenu as DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioItem, DropdownMenuRadioGroup, DropdownMenuSeparator, DropdownMenuLabel, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, } from "../../components/DropdownMenu/DropdownMenu";
6
+ // ---------------------------------------------------------------------------
7
+ // renderMenuItems — recursive, used for both root and submenu levels
8
+ // ---------------------------------------------------------------------------
9
+ function renderMenuItems(items, selectedValues, onSelect) {
10
+ const result = [];
11
+ let radioBuffer = [];
12
+ const flushRadioBuffer = (key) => {
13
+ var _a, _b;
14
+ if (radioBuffer.length === 0)
15
+ return;
16
+ const buffer = radioBuffer;
17
+ radioBuffer = [];
18
+ const radioValue = (_b = (_a = buffer.find((o) => selectedValues.includes(o.value))) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "";
19
+ result.push(_jsx(DropdownMenuRadioGroup, { value: radioValue, onValueChange: (val) => {
20
+ var _a;
21
+ const opt = buffer.find((o) => o.value === val);
22
+ if (opt) {
23
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect(val, opt);
24
+ (_a = opt.onClick) === null || _a === void 0 ? void 0 : _a.call(opt);
25
+ }
26
+ }, children: buffer.map((opt) => (_jsx(DropdownMenuRadioItem, { value: opt.value, disabled: opt.disabled, icon: opt.icon, children: opt.label }, opt.value))) }, key));
27
+ };
28
+ items.forEach((item, index) => {
29
+ var _a;
30
+ // Accumulate consecutive radio items
31
+ if (item.type === "item" && item.item.type === "radio") {
32
+ radioBuffer.push(item.item);
33
+ return;
34
+ }
35
+ // Flush radio buffer before non-radio item
36
+ flushRadioBuffer(`radio-${index}`);
37
+ if (item.type === "separator") {
38
+ result.push(_jsx(DropdownMenuSeparator, {}, `sep-${index}`));
39
+ return;
40
+ }
41
+ if (item.type === "label") {
42
+ result.push(_jsx(DropdownMenuLabel, { children: item.label }, `lbl-${index}`));
43
+ return;
44
+ }
45
+ if (item.type === "custom") {
46
+ result.push(_jsx(React.Fragment, { children: item.render() }, `custom-${index}`));
47
+ return;
48
+ }
49
+ if (item.type === "submenu") {
50
+ result.push(_jsxs(DropdownMenuSub, { children: [_jsxs(DropdownMenuSubTrigger, { disabled: item.disabled, children: [_jsxs("div", { className: "flex shrink-0 flex-row gap-1", children: [_jsx("span", { className: "size-4" }), item.icon] }), item.label] }), _jsx(DropdownMenuSubContent, { children: renderMenuItems(item.items, selectedValues, onSelect) })] }, `sub-${index}`));
51
+ return;
52
+ }
53
+ if (item.type === "item") {
54
+ const opt = item.item;
55
+ const isSelected = (_a = opt.checked) !== null && _a !== void 0 ? _a : selectedValues.includes(opt.value);
56
+ if (opt.type === "checkbox") {
57
+ result.push(_jsx(DropdownMenuCheckboxItem, { checked: isSelected, disabled: opt.disabled, onCheckedChange: () => {
58
+ var _a;
59
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect(opt.value, opt);
60
+ (_a = opt.onClick) === null || _a === void 0 ? void 0 : _a.call(opt);
61
+ }, children: opt.label }, opt.value));
62
+ return;
63
+ }
64
+ // default item
65
+ result.push(_jsx(DropdownMenuItem, { selected: isSelected, icon: opt.icon, disabled: opt.disabled, className: cn(opt.danger && "text-red-500"), onSelect: () => {
66
+ var _a;
67
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect(opt.value, opt);
68
+ (_a = opt.onClick) === null || _a === void 0 ? void 0 : _a.call(opt);
69
+ }, children: opt.label }, opt.value));
70
+ }
71
+ });
72
+ flushRadioBuffer("radio-end");
73
+ return result;
74
+ }
75
+ // ---------------------------------------------------------------------------
76
+ // Menu — top-level pattern component built on DropdownMenu
77
+ //
78
+ // ใช้งาน:
79
+ // <Menu trigger={<Button>Open</Button>} items={[...]} onSelect={...} />
80
+ //
81
+ // ได้ของฟรีจาก DropdownMenu:
82
+ // - Radix positioning (flip, scroll, sideOffset)
83
+ // - Portal (z-index, escape stacking context)
84
+ // - Escape to close, click-outside to close
85
+ // - Keyboard navigation (Arrow, Enter, Escape)
86
+ // - Full a11y / WAI-ARIA
87
+ // - Sub-menu support via DropdownMenuSub
88
+ // ---------------------------------------------------------------------------
89
+ export const Menu = ({ trigger, items, selectedValues = [], onSelect, header, open, onOpenChange, align = "start", side = "bottom", sideOffset = 4, contentClassName, }) => (_jsxs(DropdownMenuRoot, { open: open, onOpenChange: onOpenChange, children: [trigger && (_jsx(DropdownMenuTrigger, { asChild: true, children: trigger })), _jsxs(DropdownMenuContent, { align: align, side: side, sideOffset: sideOffset, className: contentClassName, children: [header && (_jsx("div", { className: "sticky top-0 z-10 bg-modal-surface border-b border-[var(--dropdown-menu-seperator-bg)]", children: header })), renderMenuItems(items, selectedValues, onSelect)] })] }));
90
+ Menu.displayName = "Menu";
91
+ // ---------------------------------------------------------------------------
92
+ // Re-exports — backward compat for consumers using Menu's sub-components
93
+ // ---------------------------------------------------------------------------
94
+ export { DropdownMenuItem as MenuItem, DropdownMenuSeparator as MenuSeparator, DropdownMenuLabel as MenuLabel, } from "../../components/DropdownMenu/DropdownMenu";
95
+ export default Menu;