@chayns-ui/core 0.5.0 → 0.7.0

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 (125) hide show
  1. package/README.md +9 -3
  2. package/dist/accordion.css +66 -2
  3. package/dist/badge.css +3 -3
  4. package/dist/banner.css +89 -0
  5. package/dist/button.css +16 -2
  6. package/dist/card.css +29 -2
  7. package/dist/checkbox.css +24 -13
  8. package/dist/combo-box.css +20 -2
  9. package/dist/components/accordion/Accordion.d.ts +10 -2
  10. package/dist/components/accordion/Accordion.d.ts.map +1 -1
  11. package/dist/components/accordion/Accordion.js +120 -46
  12. package/dist/components/accordion/Accordion.js.map +1 -1
  13. package/dist/components/accordion/Accordion.types.d.ts +24 -1
  14. package/dist/components/accordion/Accordion.types.d.ts.map +1 -1
  15. package/dist/components/accordion/Accordion.types.js +6 -0
  16. package/dist/components/accordion/Accordion.types.js.map +1 -0
  17. package/dist/components/accordion/AccordionContext.d.ts +9 -0
  18. package/dist/components/accordion/AccordionContext.d.ts.map +1 -1
  19. package/dist/components/accordion/AccordionContext.js +8 -2
  20. package/dist/components/accordion/AccordionContext.js.map +1 -1
  21. package/dist/components/accordion-group/AccordionGroup.d.ts +1 -1
  22. package/dist/components/accordion-group/AccordionGroup.d.ts.map +1 -1
  23. package/dist/components/accordion-group/AccordionGroup.js +3 -3
  24. package/dist/components/accordion-group/AccordionGroup.js.map +1 -1
  25. package/dist/components/avatar/Avatar.js +1 -1
  26. package/dist/components/avatar-group/AvatarGroup.js +1 -1
  27. package/dist/components/badge/Badge.js +1 -1
  28. package/dist/components/banner/Banner.d.ts +3 -0
  29. package/dist/components/banner/Banner.d.ts.map +1 -0
  30. package/dist/components/banner/Banner.js +46 -0
  31. package/dist/components/banner/Banner.js.map +1 -0
  32. package/dist/components/banner/Banner.types.d.ts +24 -0
  33. package/dist/components/banner/Banner.types.d.ts.map +1 -0
  34. package/dist/components/banner/Banner.types.js +11 -0
  35. package/dist/components/banner/Banner.types.js.map +1 -0
  36. package/dist/components/button/Button.d.ts +1 -1
  37. package/dist/components/button/Button.d.ts.map +1 -1
  38. package/dist/components/button/Button.js +8 -3
  39. package/dist/components/button/Button.js.map +1 -1
  40. package/dist/components/button/Button.types.d.ts +15 -11
  41. package/dist/components/button/Button.types.d.ts.map +1 -1
  42. package/dist/components/button/Button.types.js.map +1 -1
  43. package/dist/components/button/button-icon/ButtonIcon.d.ts.map +1 -1
  44. package/dist/components/button/button-icon/ButtonIcon.js +18 -12
  45. package/dist/components/button/button-icon/ButtonIcon.js.map +1 -1
  46. package/dist/components/card/Card.d.ts +4 -2
  47. package/dist/components/card/Card.d.ts.map +1 -1
  48. package/dist/components/card/Card.js +24 -8
  49. package/dist/components/card/Card.js.map +1 -1
  50. package/dist/components/card/Card.types.d.ts +10 -12
  51. package/dist/components/card/Card.types.d.ts.map +1 -1
  52. package/dist/components/checkbox/Checkbox.d.ts +1 -1
  53. package/dist/components/checkbox/Checkbox.d.ts.map +1 -1
  54. package/dist/components/checkbox/Checkbox.js +15 -6
  55. package/dist/components/checkbox/Checkbox.js.map +1 -1
  56. package/dist/components/checkbox/Checkbox.types.d.ts +2 -0
  57. package/dist/components/checkbox/Checkbox.types.d.ts.map +1 -1
  58. package/dist/components/combo-box/ComboBox.d.ts.map +1 -1
  59. package/dist/components/combo-box/ComboBox.js +19 -2
  60. package/dist/components/combo-box/ComboBox.js.map +1 -1
  61. package/dist/components/date-time-picker/DateTimePicker.d.ts +4 -0
  62. package/dist/components/date-time-picker/DateTimePicker.d.ts.map +1 -0
  63. package/dist/components/date-time-picker/DateTimePicker.js +339 -0
  64. package/dist/components/date-time-picker/DateTimePicker.js.map +1 -0
  65. package/dist/components/date-time-picker/DateTimePicker.types.d.ts +26 -0
  66. package/dist/components/date-time-picker/DateTimePicker.types.d.ts.map +1 -0
  67. package/dist/components/date-time-picker/DateTimePicker.types.js +12 -0
  68. package/dist/components/date-time-picker/DateTimePicker.types.js.map +1 -0
  69. package/dist/components/icon-button/IconButton.d.ts +7 -2
  70. package/dist/components/icon-button/IconButton.d.ts.map +1 -1
  71. package/dist/components/icon-button/IconButton.js +8 -3
  72. package/dist/components/icon-button/IconButton.js.map +1 -1
  73. package/dist/components/icon-button/IconButton.types.d.ts +7 -4
  74. package/dist/components/icon-button/IconButton.types.d.ts.map +1 -1
  75. package/dist/components/list/List.js +1 -1
  76. package/dist/components/popup/Popup.js +1 -1
  77. package/dist/components/popup/PopupList.js +1 -1
  78. package/dist/components/radio-group/RadioGroup.d.ts.map +1 -1
  79. package/dist/components/radio-group/RadioGroup.js +17 -8
  80. package/dist/components/radio-group/RadioGroup.js.map +1 -1
  81. package/dist/components/radio-group/RadioGroup.types.d.ts +5 -3
  82. package/dist/components/radio-group/RadioGroup.types.d.ts.map +1 -1
  83. package/dist/components/segmented-control/SegmentedControl.d.ts.map +1 -1
  84. package/dist/components/segmented-control/SegmentedControl.js +12 -5
  85. package/dist/components/segmented-control/SegmentedControl.js.map +1 -1
  86. package/dist/components/segmented-control/SegmentedControl.types.d.ts +5 -2
  87. package/dist/components/segmented-control/SegmentedControl.types.d.ts.map +1 -1
  88. package/dist/components/split-button/SplitButton.d.ts +1 -1
  89. package/dist/components/split-button/SplitButton.d.ts.map +1 -1
  90. package/dist/components/split-button/SplitButton.js +9 -17
  91. package/dist/components/split-button/SplitButton.js.map +1 -1
  92. package/dist/components/switch/Switch.d.ts +1 -1
  93. package/dist/components/switch/Switch.d.ts.map +1 -1
  94. package/dist/components/switch/Switch.js +13 -5
  95. package/dist/components/switch/Switch.js.map +1 -1
  96. package/dist/components/switch/Switch.types.d.ts +2 -0
  97. package/dist/components/switch/Switch.types.d.ts.map +1 -1
  98. package/dist/components/text-area/TextArea.d.ts +1 -1
  99. package/dist/components/text-area/TextArea.d.ts.map +1 -1
  100. package/dist/components/text-area/TextArea.js +5 -4
  101. package/dist/components/text-area/TextArea.js.map +1 -1
  102. package/dist/components/text-field/TextField.d.ts +1 -1
  103. package/dist/components/text-field/TextField.d.ts.map +1 -1
  104. package/dist/components/text-field/TextField.js +5 -4
  105. package/dist/components/text-field/TextField.js.map +1 -1
  106. package/dist/date-time-picker.css +121 -0
  107. package/dist/index.d.ts +9 -5
  108. package/dist/index.d.ts.map +1 -1
  109. package/dist/index.js +6 -2
  110. package/dist/list.css +4 -4
  111. package/dist/radio-group.css +13 -1
  112. package/dist/segmented-control.css +24 -7
  113. package/dist/split-button.css +1 -1
  114. package/dist/styles.css +2 -1
  115. package/dist/switch.css +18 -5
  116. package/dist/text-area.css +2 -2
  117. package/dist/text-field.css +1 -1
  118. package/package.json +3 -2
  119. package/dist/components/message-box/MessageBox.d.ts +0 -4
  120. package/dist/components/message-box/MessageBox.d.ts.map +0 -1
  121. package/dist/components/message-box/MessageBox.js +0 -19
  122. package/dist/components/message-box/MessageBox.js.map +0 -1
  123. package/dist/components/message-box/MessageBox.types.d.ts +0 -13
  124. package/dist/components/message-box/MessageBox.types.d.ts.map +0 -1
  125. package/dist/message-box.css +0 -26
@@ -1 +1 @@
1
- {"version":3,"file":"Card.js","names":[],"sources":["../../../src/components/card/Card.tsx"],"sourcesContent":["import type { CardProps } from './Card.types.js';\n\nconst Card = ({ children, className, elevated = false, ...cardProps }: CardProps) => {\n const resolvedClassName = ['chayns-card', elevated ? 'chayns-card--elevated' : null, className]\n .filter(Boolean)\n .join(' ');\n\n return (\n <div {...cardProps} className={resolvedClassName}>\n {children}\n </div>\n );\n};\n\nexport default Card;\n"],"mappings":";;AAEA,IAAM,QAAQ,EAAE,UAAU,WAAW,WAAW,OAAO,GAAG,gBAA2B;CACnF,MAAM,oBAAoB;EAAC;EAAe,WAAW,0BAA0B;EAAM;CAAS,CAAC,CAC5F,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CAEX,OACE,oBAAC,OAAD;EAAK,GAAI;EAAW,WAAW;EAC5B;CACE,CAAA;AAET"}
1
+ {"version":3,"file":"Card.js","names":[],"sources":["../../../src/components/card/Card.tsx"],"sourcesContent":["import { forwardRef } from 'react';\n\nimport ButtonIcon from '../button/button-icon/ButtonIcon.js';\nimport type { CardHeaderProps, CardProps } from './Card.types.js';\n\nconst CardHeader = forwardRef<HTMLElement, CardHeaderProps>(function CardHeader(\n { children, className, icon, ...headerProps },\n ref,\n) {\n const resolvedClassName = ['chayns-card__header', className].filter(Boolean).join(' ');\n\n return (\n <header {...headerProps} className={resolvedClassName} ref={ref}>\n {icon ? (\n <span aria-hidden=\"true\" className=\"chayns-card__header-icon\">\n <ButtonIcon icon={icon} />\n </span>\n ) : null}\n <span className=\"chayns-card__header-content\">{children}</span>\n </header>\n );\n});\n\nconst CardRoot = forwardRef<HTMLDivElement, CardProps>(function Card(\n { children, className, ...cardProps },\n ref,\n) {\n const resolvedClassName = ['chayns-card', className].filter(Boolean).join(' ');\n\n return (\n <div {...cardProps} className={resolvedClassName} ref={ref}>\n {children}\n </div>\n );\n});\n\nconst Card = Object.assign(CardRoot, { Header: CardHeader });\n\nexport default Card;\n"],"mappings":";;;;AAKA,IAAM,aAAa,WAAyC,SAAS,WACnE,EAAE,UAAU,WAAW,MAAM,GAAG,eAChC,KACA;CACA,MAAM,oBAAoB,CAAC,uBAAuB,SAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAErF,OACE,qBAAC,UAAD;EAAQ,GAAI;EAAa,WAAW;EAAwB;EAA5D,UAAA,CACG,OACC,oBAAC,QAAD;GAAM,eAAY;GAAO,WAAU;GACjC,UAAA,oBAAC,YAAD,EAAkB,KAAO,CAAA;EACrB,CAAA,IACJ,MACJ,oBAAC,QAAD;GAAM,WAAU;GAA+B;EAAe,CAAA,CACxD;;AAEZ,CAAC;AAED,IAAM,WAAW,WAAsC,SAAS,KAC9D,EAAE,UAAU,WAAW,GAAG,aAC1B,KACA;CACA,MAAM,oBAAoB,CAAC,eAAe,SAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAE7E,OACE,oBAAC,OAAD;EAAK,GAAI;EAAW,WAAW;EAAwB;EACpD;CACE,CAAA;AAET,CAAC;AAED,IAAM,OAAO,OAAO,OAAO,UAAU,EAAE,QAAQ,WAAW,CAAC"}
@@ -1,22 +1,20 @@
1
1
  import type { ComponentPropsWithRef, ReactNode } from 'react';
2
+ import type { ButtonIcon } from '../button/Button.types.js';
2
3
  /**
3
4
  * @description Props for the presentational surface container. `Card` is
4
- * purely presentational: it exposes no click or keyboard semantics and no
5
- * intrinsic padding. Forwards all remaining native `div` props, `className`
6
- * and `ref`.
5
+ * purely presentational: it exposes no click or keyboard semantics. Forwards
6
+ * all remaining native `div` props, `className` and `ref`.
7
7
  */
8
8
  export interface CardProps extends ComponentPropsWithRef<'div'> {
9
9
  /**
10
- * @description Content rendered inside the card surface. Any content may be
11
- * composed inside; internal spacing is owned by that content or a
12
- * container, not by `Card`.
10
+ * @description Content rendered inside the card surface. Card owns the
11
+ * standard Design System inner spacing around this content.
13
12
  */
14
13
  children?: ReactNode;
15
- /**
16
- * @description Adds the subtle resting card elevation token on top of the
17
- * Design System `.card` surface treatment.
18
- * @default false
19
- */
20
- elevated?: boolean;
14
+ }
15
+ export interface CardHeaderProps extends ComponentPropsWithRef<'header'> {
16
+ /** Optional leading icon rendered in the semantic header area. */
17
+ icon?: ButtonIcon;
18
+ children?: ReactNode;
21
19
  }
22
20
  //# sourceMappingURL=Card.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Card.types.d.ts","sourceRoot":"","sources":["../../../src/components/card/Card.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,WAAW,SAAU,SAAQ,qBAAqB,CAAC,KAAK,CAAC;IAC7D;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB"}
1
+ {"version":3,"file":"Card.types.d.ts","sourceRoot":"","sources":["../../../src/components/card/Card.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D;;;;GAIG;AACH,MAAM,WAAW,SAAU,SAAQ,qBAAqB,CAAC,KAAK,CAAC;IAC7D;;;OAGG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,WAAW,eAAgB,SAAQ,qBAAqB,CAAC,QAAQ,CAAC;IACtE,kEAAkE;IAClE,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB"}
@@ -1,4 +1,4 @@
1
1
  import type { CheckboxProps } from './Checkbox.types.js';
2
- declare function Checkbox({ children, className, ...inputProps }: CheckboxProps): import("react").JSX.Element;
2
+ declare const Checkbox: import("react").ForwardRefExoticComponent<Omit<CheckboxProps, "ref"> & import("react").RefAttributes<HTMLInputElement>>;
3
3
  export default Checkbox;
4
4
  //# sourceMappingURL=Checkbox.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../../src/components/checkbox/Checkbox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,iBAAS,QAAQ,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,UAAU,EAAE,EAAE,aAAa,+BAUtE;AAED,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../../src/components/checkbox/Checkbox.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,QAAA,MAAM,QAAQ,yHAoBZ,CAAC;AAEH,eAAe,QAAQ,CAAC"}
@@ -1,6 +1,7 @@
1
+ import { forwardRef } from "react";
1
2
  import { jsx, jsxs } from "react/jsx-runtime";
2
3
  //#region src/components/checkbox/Checkbox.tsx
3
- function Checkbox({ children, className, ...inputProps }) {
4
+ var Checkbox = forwardRef(function Checkbox({ children, className, description, ...inputProps }, ref) {
4
5
  const inputClassName = ["chayns-checkbox__input", className].filter(Boolean).join(" ");
5
6
  return /* @__PURE__ */ jsxs("label", {
6
7
  className: "chayns-checkbox",
@@ -8,19 +9,27 @@ function Checkbox({ children, className, ...inputProps }) {
8
9
  /* @__PURE__ */ jsx("input", {
9
10
  ...inputProps,
10
11
  className: inputClassName,
12
+ ref,
11
13
  type: "checkbox"
12
14
  }),
13
15
  /* @__PURE__ */ jsx("span", {
14
16
  "aria-hidden": "true",
15
- className: "chayns-checkbox__control"
17
+ className: "chayns-checkbox__control",
18
+ children: /* @__PURE__ */ jsx("i", { className: "far fa-check" })
16
19
  }),
17
- /* @__PURE__ */ jsx("span", {
18
- className: "chayns-checkbox__label",
19
- children
20
+ /* @__PURE__ */ jsxs("span", {
21
+ className: "chayns-checkbox__content",
22
+ children: [/* @__PURE__ */ jsx("span", {
23
+ className: "chayns-checkbox__label",
24
+ children
25
+ }), description !== void 0 ? /* @__PURE__ */ jsx("span", {
26
+ className: "chayns-checkbox__description",
27
+ children: description
28
+ }) : null]
20
29
  })
21
30
  ]
22
31
  });
23
- }
32
+ });
24
33
  //#endregion
25
34
  export { Checkbox as default };
26
35
 
@@ -1 +1 @@
1
- {"version":3,"file":"Checkbox.js","names":[],"sources":["../../../src/components/checkbox/Checkbox.tsx"],"sourcesContent":["import type { CheckboxProps } from './Checkbox.types.js';\n\nfunction Checkbox({ children, className, ...inputProps }: CheckboxProps) {\n const inputClassName = ['chayns-checkbox__input', className].filter(Boolean).join(' ');\n\n return (\n <label className=\"chayns-checkbox\">\n <input {...inputProps} className={inputClassName} type=\"checkbox\" />\n <span aria-hidden=\"true\" className=\"chayns-checkbox__control\" />\n <span className=\"chayns-checkbox__label\">{children}</span>\n </label>\n );\n}\n\nexport default Checkbox;\n"],"mappings":";;AAEA,SAAS,SAAS,EAAE,UAAU,WAAW,GAAG,cAA6B;CACvE,MAAM,iBAAiB,CAAC,0BAA0B,SAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAErF,OACE,qBAAC,SAAD;EAAO,WAAU;EAAjB,UAAA;GACE,oBAAC,SAAD;IAAO,GAAI;IAAY,WAAW;IAAgB,MAAK;GAAY,CAAA;GACnE,oBAAC,QAAD;IAAM,eAAY;IAAO,WAAU;GAA4B,CAAA;GAC/D,oBAAC,QAAD;IAAM,WAAU;IAA0B;GAAe,CAAA;EACpD;;AAEX"}
1
+ {"version":3,"file":"Checkbox.js","names":[],"sources":["../../../src/components/checkbox/Checkbox.tsx"],"sourcesContent":["import { forwardRef } from 'react';\n\nimport type { CheckboxProps } from './Checkbox.types.js';\n\nconst Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(function Checkbox(\n { children, className, description, ...inputProps },\n ref,\n) {\n const inputClassName = ['chayns-checkbox__input', className].filter(Boolean).join(' ');\n\n return (\n <label className=\"chayns-checkbox\">\n <input {...inputProps} className={inputClassName} ref={ref} type=\"checkbox\" />\n <span aria-hidden=\"true\" className=\"chayns-checkbox__control\">\n <i className=\"far fa-check\" />\n </span>\n <span className=\"chayns-checkbox__content\">\n <span className=\"chayns-checkbox__label\">{children}</span>\n {description !== undefined ? (\n <span className=\"chayns-checkbox__description\">{description}</span>\n ) : null}\n </span>\n </label>\n );\n});\n\nexport default Checkbox;\n"],"mappings":";;;AAIA,IAAM,WAAW,WAA4C,SAAS,SACpE,EAAE,UAAU,WAAW,aAAa,GAAG,cACvC,KACA;CACA,MAAM,iBAAiB,CAAC,0BAA0B,SAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAErF,OACE,qBAAC,SAAD;EAAO,WAAU;EAAjB,UAAA;GACE,oBAAC,SAAD;IAAO,GAAI;IAAY,WAAW;IAAqB;IAAK,MAAK;GAAY,CAAA;GAC7E,oBAAC,QAAD;IAAM,eAAY;IAAO,WAAU;IACjC,UAAA,oBAAC,KAAD,EAAG,WAAU,eAAgB,CAAA;GACzB,CAAA;GACN,qBAAC,QAAD;IAAM,WAAU;IAAhB,UAAA,CACE,oBAAC,QAAD;KAAM,WAAU;KAA0B;IAAe,CAAA,GACxD,gBAAgB,KAAA,IACf,oBAAC,QAAD;KAAM,WAAU;KAAgC,UAAA;IAAkB,CAAA,IAChE,IACA;;EACD;;AAEX,CAAC"}
@@ -3,6 +3,8 @@ import type { ComponentPropsWithRef, ReactNode } from 'react';
3
3
  export interface CheckboxProps extends Omit<ComponentPropsWithRef<'input'>, 'children' | 'type'> {
4
4
  /** Visible, localized label associated with the native checkbox. */
5
5
  children: Exclude<ReactNode, boolean | null | undefined>;
6
+ /** Optional localized supporting text associated with the checkbox label. */
7
+ description?: ReactNode;
6
8
  /** Checkbox owns its native input type. */
7
9
  type?: never;
8
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Checkbox.types.d.ts","sourceRoot":"","sources":["../../../src/components/checkbox/Checkbox.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D,iDAAiD;AACjD,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAC9F,oEAAoE;IACpE,QAAQ,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAEzD,2CAA2C;IAC3C,IAAI,CAAC,EAAE,KAAK,CAAC;CACd"}
1
+ {"version":3,"file":"Checkbox.types.d.ts","sourceRoot":"","sources":["../../../src/components/checkbox/Checkbox.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D,iDAAiD;AACjD,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAC9F,oEAAoE;IACpE,QAAQ,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAEzD,6EAA6E;IAC7E,WAAW,CAAC,EAAE,SAAS,CAAC;IAExB,2CAA2C;IAC3C,IAAI,CAAC,EAAE,KAAK,CAAC;CACd"}
@@ -1 +1 @@
1
- {"version":3,"file":"ComboBox.d.ts","sourceRoot":"","sources":["../../../src/components/combo-box/ComboBox.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,qBAAqB,CAAC;AA2T9E,QAAA,MAAM,QAAQ;;CAA0C,CAAC;AAEzD,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"ComboBox.d.ts","sourceRoot":"","sources":["../../../src/components/combo-box/ComboBox.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,qBAAqB,CAAC;AA2U9E,QAAA,MAAM,QAAQ;;CAA0C,CAAC;AAEzD,eAAe,QAAQ,CAAC"}
@@ -1,6 +1,7 @@
1
+ import ButtonIcon from "../button/button-icon/ButtonIcon.js";
1
2
  import { ComboBoxContext, getOptionLabel, useComboBoxContext } from "./ComboBoxContext.js";
2
- import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { Children, Fragment, forwardRef, isValidElement, useEffect, useId, useMemo, useRef, useState } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
4
5
  //#region src/components/combo-box/ComboBox.tsx
5
6
  function getOptionElements(children) {
6
7
  const options = [];
@@ -29,9 +30,20 @@ var Option = forwardRef(function Option({ children, className, value, ...optionP
29
30
  className: resolvedClassName,
30
31
  id: comboBox.optionId(value),
31
32
  onMouseDown: (event) => event.preventDefault(),
32
- onClick: () => comboBox.select(value),
33
+ onClick: (event) => {
34
+ optionProps.onClick?.(event);
35
+ if (!event.defaultPrevented) comboBox.select(value);
36
+ },
37
+ onKeyDown: (event) => {
38
+ optionProps.onKeyDown?.(event);
39
+ if (!event.defaultPrevented && (event.key === "Enter" || event.key === " ")) {
40
+ event.preventDefault();
41
+ comboBox.select(value);
42
+ }
43
+ },
33
44
  ref,
34
45
  role: "option",
46
+ tabIndex: -1,
35
47
  children: [comboBox.multiple ? /* @__PURE__ */ jsx("span", {
36
48
  "aria-hidden": "true",
37
49
  className: "chayns-combo-box__checkbox",
@@ -199,6 +211,11 @@ var ComboBoxRoot = forwardRef(function ComboBoxRoot(props, ref) {
199
211
  id: describedLabel,
200
212
  children: placeholder
201
213
  }) : null,
214
+ /* @__PURE__ */ jsx("span", {
215
+ "aria-hidden": "true",
216
+ className: "chayns-combo-box__chevron",
217
+ children: /* @__PURE__ */ jsx(ButtonIcon, { icon: "fa-chevron-down" })
218
+ }),
202
219
  open ? /* @__PURE__ */ jsx("div", {
203
220
  "aria-multiselectable": multiple || void 0,
204
221
  className: "chayns-combo-box__popup",
@@ -1 +1 @@
1
- {"version":3,"file":"ComboBox.js","names":[],"sources":["../../../src/components/combo-box/ComboBox.tsx"],"sourcesContent":["import {\n Children,\n forwardRef,\n Fragment,\n isValidElement,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { ChangeEvent, FocusEvent, KeyboardEvent, ReactElement, ReactNode } from 'react';\n\nimport { ComboBoxContext, getOptionLabel, useComboBoxContext } from './ComboBoxContext.js';\nimport type { ComboBoxOptionProps, ComboBoxProps } from './ComboBox.types.js';\n\nfunction getOptionElements(children: ComboBoxProps['children']) {\n const options: ReactElement<ComboBoxOptionProps>[] = [];\n\n Children.forEach(children, (child) => {\n if (!isValidElement(child)) {\n return;\n }\n\n const element = child as ReactElement<{ children?: ReactNode; value?: string }>;\n\n if (element.type === Fragment) {\n options.push(...getOptionElements(element.props.children));\n } else if (typeof element.props.value === 'string') {\n options.push(element as ReactElement<ComboBoxOptionProps>);\n }\n });\n\n return options;\n}\n\nconst Option = forwardRef<HTMLDivElement, ComboBoxOptionProps>(function Option(\n { children, className, value, ...optionProps },\n ref,\n) {\n const comboBox = useComboBoxContext();\n const isVisible = comboBox.visibleValues.includes(value);\n const isSelected = comboBox.isSelected(value);\n const resolvedClassName = [\n 'chayns-combo-box__option',\n isSelected ? 'chayns-combo-box__option--selected' : '',\n comboBox.activeValue === value ? 'chayns-combo-box__option--active' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n if (!isVisible) {\n return null;\n }\n\n return (\n <div\n {...optionProps}\n aria-selected={isSelected}\n className={resolvedClassName}\n id={comboBox.optionId(value)}\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => comboBox.select(value)}\n ref={ref}\n role=\"option\"\n >\n {comboBox.multiple ? (\n <span\n aria-hidden=\"true\"\n className=\"chayns-combo-box__checkbox\"\n data-checked={isSelected || undefined}\n />\n ) : null}\n {children}\n </div>\n );\n});\n\nconst ComboBoxRoot = forwardRef<HTMLInputElement, ComboBoxProps>(function ComboBoxRoot(props, ref) {\n const {\n 'aria-label': ariaLabel,\n children,\n className,\n defaultValue,\n disabled,\n id,\n multiple = false,\n onValueChange,\n openOnFocus = true,\n placeholder,\n value,\n ...inputProps\n } = props;\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const listboxId = `${inputId}-listbox`;\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const optionElements = useMemo(() => getOptionElements(children), [children]);\n const optionMap = useMemo(\n () => new Map(optionElements.map((option) => [option.props.value, option])),\n [optionElements],\n );\n const defaultValues = multiple\n ? ((defaultValue as ReactElement<ComboBoxOptionProps>[] | undefined)?.map(\n (option) => option.props.value,\n ) ?? [])\n : [];\n const controlledValues = multiple\n ? ((value as ReactElement<ComboBoxOptionProps>[] | undefined)?.map(\n (option) => option.props.value,\n ) ?? [])\n : [];\n const [uncontrolledValue, setUncontrolledValue] = useState(\n multiple ? '' : ((defaultValue as string | undefined) ?? ''),\n );\n const [uncontrolledValues, setUncontrolledValues] = useState(defaultValues);\n const [inputText, setInputText] = useState(\n multiple ? '' : ((defaultValue as string | undefined) ?? ''),\n );\n const [open, setOpen] = useState(false);\n const [activeValue, setActiveValue] = useState<string>();\n const isControlled = value !== undefined;\n const selectedValue = multiple ? '' : isControlled ? (value as string) : uncontrolledValue;\n const selectedValues = multiple ? (isControlled ? controlledValues : uncontrolledValues) : [];\n const normalizedFilter = inputText.trim().toLocaleLowerCase();\n const visibleValues = optionElements\n .filter((option) => {\n const label = getOptionLabel(option.props.children).toLocaleLowerCase();\n return normalizedFilter === '' || label.includes(normalizedFilter);\n })\n .map((option) => option.props.value);\n\n useEffect(() => {\n if (open && activeValue === undefined) {\n setActiveValue(visibleValues[0]);\n }\n }, [activeValue, open, visibleValues]);\n\n useEffect(() => {\n if (!open) {\n return undefined;\n }\n\n function closeOnOutsidePress(event: PointerEvent) {\n if (event.target instanceof Node && !rootRef.current?.contains(event.target)) {\n commitInput();\n setOpen(false);\n }\n }\n\n document.addEventListener('pointerdown', closeOnOutsidePress);\n return () => document.removeEventListener('pointerdown', closeOnOutsidePress);\n });\n\n function setInputElement(element: HTMLInputElement | null) {\n inputRef.current = element;\n\n if (typeof ref === 'function') {\n ref(element);\n } else if (ref !== null) {\n ref.current = element;\n }\n }\n\n function emitValue(nextValue: string) {\n if (!isControlled) {\n setUncontrolledValue(nextValue);\n }\n\n (onValueChange as ((next: string) => void) | undefined)?.(nextValue);\n }\n\n function emitValues(nextValues: string[]) {\n if (!isControlled) {\n setUncontrolledValues(nextValues);\n }\n\n const nextOptions = nextValues\n .map((nextValue) => optionMap.get(nextValue))\n .filter((option): option is ReactElement<ComboBoxOptionProps> => option !== undefined);\n (onValueChange as ((next: ReactElement<ComboBoxOptionProps>[]) => void) | undefined)?.(\n nextOptions,\n );\n }\n\n function commitInput() {\n if (!multiple && inputText !== selectedValue) {\n emitValue(inputText);\n }\n }\n\n function select(nextValue: string) {\n if (multiple) {\n const nextValues = selectedValues.includes(nextValue)\n ? selectedValues.filter((selected) => selected !== nextValue)\n : [...selectedValues, nextValue];\n emitValues(nextValues);\n setInputText('');\n setActiveValue(undefined);\n setOpen(true);\n return;\n }\n\n const option = optionMap.get(nextValue);\n emitValue(nextValue);\n setInputText(option === undefined ? nextValue : getOptionLabel(option.props.children));\n setActiveValue(undefined);\n setOpen(false);\n }\n\n function handleChange(event: ChangeEvent<HTMLInputElement>) {\n setInputText(event.target.value);\n setActiveValue(undefined);\n setOpen(true);\n props.onChange?.(event);\n }\n\n function handleBlur(event: FocusEvent<HTMLInputElement>) {\n props.onBlur?.(event);\n if (!rootRef.current?.contains(event.relatedTarget)) {\n commitInput();\n setOpen(false);\n }\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n const currentIndex = activeValue === undefined ? -1 : visibleValues.indexOf(activeValue);\n let nextIndex = currentIndex;\n\n if (event.key === 'ArrowDown') nextIndex = Math.min(currentIndex + 1, visibleValues.length - 1);\n if (event.key === 'ArrowUp') nextIndex = Math.max(currentIndex - 1, 0);\n if (event.key === 'Home') nextIndex = 0;\n if (event.key === 'End') nextIndex = visibleValues.length - 1;\n\n if (nextIndex !== currentIndex && visibleValues[nextIndex] !== undefined) {\n event.preventDefault();\n setActiveValue(visibleValues[nextIndex]);\n setOpen(true);\n } else if (event.key === 'Enter' && activeValue !== undefined) {\n event.preventDefault();\n select(activeValue);\n } else if (event.key === 'Escape') {\n event.preventDefault();\n setInputText(multiple ? '' : selectedValue);\n setActiveValue(undefined);\n setOpen(false);\n }\n\n props.onKeyDown?.(event);\n }\n\n const resolvedClassName = ['chayns-combo-box', open ? 'chayns-combo-box--open' : '']\n .filter(Boolean)\n .join(' ');\n const describedLabel = placeholder === undefined ? undefined : `${inputId}-label`;\n const selectedOptions = selectedValues\n .map((selected) => optionMap.get(selected))\n .filter((option): option is ReactElement<ComboBoxOptionProps> => option !== undefined);\n const inputClassName = ['chayns-combo-box__input', className].filter(Boolean).join(' ');\n\n return (\n <ComboBoxContext.Provider\n value={{\n activeValue,\n close: () => setOpen(false),\n inputText,\n isSelected: (optionValue) =>\n multiple ? selectedValues.includes(optionValue) : selectedValue === optionValue,\n multiple,\n optionId: (optionValue) => `${listboxId}-${optionValue.replace(/[^a-zA-Z0-9_-]/g, '-')}`,\n select,\n visibleValues,\n }}\n >\n <div className={resolvedClassName} ref={rootRef}>\n {multiple ? (\n <div className=\"chayns-combo-box__chips\" aria-hidden=\"true\">\n {selectedOptions.map((option) => (\n <span className=\"chayns-combo-box__chip\" key={option.props.value}>\n {option.props.children}\n </span>\n ))}\n </div>\n ) : null}\n <input\n {...inputProps}\n aria-activedescendant={\n open && activeValue\n ? `${listboxId}-${activeValue.replace(/[^a-zA-Z0-9_-]/g, '-')}`\n : undefined\n }\n aria-autocomplete=\"list\"\n aria-controls={listboxId}\n aria-expanded={open}\n aria-label={ariaLabel}\n className={inputClassName}\n disabled={disabled}\n id={inputId}\n onBlur={handleBlur}\n onChange={handleChange}\n onFocus={() => openOnFocus && setOpen(true)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder === undefined ? undefined : ' '}\n ref={setInputElement}\n role=\"combobox\"\n value={inputText}\n />\n {placeholder !== undefined ? (\n <label className=\"chayns-combo-box__label\" htmlFor={inputId} id={describedLabel}>\n {placeholder}\n </label>\n ) : null}\n {open ? (\n <div\n aria-multiselectable={multiple || undefined}\n className=\"chayns-combo-box__popup\"\n id={listboxId}\n role=\"listbox\"\n >\n {children}\n </div>\n ) : null}\n </div>\n </ComboBoxContext.Provider>\n );\n});\n\nconst ComboBox = Object.assign(ComboBoxRoot, { Option });\n\nexport default ComboBox;\n"],"mappings":"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"}
1
+ {"version":3,"file":"ComboBox.js","names":[],"sources":["../../../src/components/combo-box/ComboBox.tsx"],"sourcesContent":["import {\n Children,\n forwardRef,\n Fragment,\n isValidElement,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { ChangeEvent, FocusEvent, KeyboardEvent, ReactElement, ReactNode } from 'react';\n\nimport ButtonIcon from '../button/button-icon/ButtonIcon.js';\nimport { ComboBoxContext, getOptionLabel, useComboBoxContext } from './ComboBoxContext.js';\nimport type { ComboBoxOptionProps, ComboBoxProps } from './ComboBox.types.js';\n\nfunction getOptionElements(children: ComboBoxProps['children']) {\n const options: ReactElement<ComboBoxOptionProps>[] = [];\n\n Children.forEach(children, (child) => {\n if (!isValidElement(child)) {\n return;\n }\n\n const element = child as ReactElement<{ children?: ReactNode; value?: string }>;\n\n if (element.type === Fragment) {\n options.push(...getOptionElements(element.props.children));\n } else if (typeof element.props.value === 'string') {\n options.push(element as ReactElement<ComboBoxOptionProps>);\n }\n });\n\n return options;\n}\n\nconst Option = forwardRef<HTMLDivElement, ComboBoxOptionProps>(function Option(\n { children, className, value, ...optionProps },\n ref,\n) {\n const comboBox = useComboBoxContext();\n const isVisible = comboBox.visibleValues.includes(value);\n const isSelected = comboBox.isSelected(value);\n const resolvedClassName = [\n 'chayns-combo-box__option',\n isSelected ? 'chayns-combo-box__option--selected' : '',\n comboBox.activeValue === value ? 'chayns-combo-box__option--active' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n if (!isVisible) {\n return null;\n }\n\n return (\n <div\n {...optionProps}\n aria-selected={isSelected}\n className={resolvedClassName}\n id={comboBox.optionId(value)}\n onMouseDown={(event) => event.preventDefault()}\n onClick={(event) => {\n optionProps.onClick?.(event);\n if (!event.defaultPrevented) {\n comboBox.select(value);\n }\n }}\n onKeyDown={(event) => {\n optionProps.onKeyDown?.(event);\n if (!event.defaultPrevented && (event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n comboBox.select(value);\n }\n }}\n ref={ref}\n role=\"option\"\n tabIndex={-1}\n >\n {comboBox.multiple ? (\n <span\n aria-hidden=\"true\"\n className=\"chayns-combo-box__checkbox\"\n data-checked={isSelected || undefined}\n />\n ) : null}\n {children}\n </div>\n );\n});\n\nconst ComboBoxRoot = forwardRef<HTMLInputElement, ComboBoxProps>(function ComboBoxRoot(props, ref) {\n const {\n 'aria-label': ariaLabel,\n children,\n className,\n defaultValue,\n disabled,\n id,\n multiple = false,\n onValueChange,\n openOnFocus = true,\n placeholder,\n value,\n ...inputProps\n } = props;\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const listboxId = `${inputId}-listbox`;\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const optionElements = useMemo(() => getOptionElements(children), [children]);\n const optionMap = useMemo(\n () => new Map(optionElements.map((option) => [option.props.value, option])),\n [optionElements],\n );\n const defaultValues = multiple\n ? ((defaultValue as ReactElement<ComboBoxOptionProps>[] | undefined)?.map(\n (option) => option.props.value,\n ) ?? [])\n : [];\n const controlledValues = multiple\n ? ((value as ReactElement<ComboBoxOptionProps>[] | undefined)?.map(\n (option) => option.props.value,\n ) ?? [])\n : [];\n const [uncontrolledValue, setUncontrolledValue] = useState(\n multiple ? '' : ((defaultValue as string | undefined) ?? ''),\n );\n const [uncontrolledValues, setUncontrolledValues] = useState(defaultValues);\n const [inputText, setInputText] = useState(\n multiple ? '' : ((defaultValue as string | undefined) ?? ''),\n );\n const [open, setOpen] = useState(false);\n const [activeValue, setActiveValue] = useState<string>();\n const isControlled = value !== undefined;\n const selectedValue = multiple ? '' : isControlled ? (value as string) : uncontrolledValue;\n const selectedValues = multiple ? (isControlled ? controlledValues : uncontrolledValues) : [];\n const normalizedFilter = inputText.trim().toLocaleLowerCase();\n const visibleValues = optionElements\n .filter((option) => {\n const label = getOptionLabel(option.props.children).toLocaleLowerCase();\n return normalizedFilter === '' || label.includes(normalizedFilter);\n })\n .map((option) => option.props.value);\n\n useEffect(() => {\n if (open && activeValue === undefined) {\n setActiveValue(visibleValues[0]);\n }\n }, [activeValue, open, visibleValues]);\n\n useEffect(() => {\n if (!open) {\n return undefined;\n }\n\n function closeOnOutsidePress(event: PointerEvent) {\n if (event.target instanceof Node && !rootRef.current?.contains(event.target)) {\n commitInput();\n setOpen(false);\n }\n }\n\n document.addEventListener('pointerdown', closeOnOutsidePress);\n return () => document.removeEventListener('pointerdown', closeOnOutsidePress);\n });\n\n function setInputElement(element: HTMLInputElement | null) {\n inputRef.current = element;\n\n if (typeof ref === 'function') {\n ref(element);\n } else if (ref !== null) {\n ref.current = element;\n }\n }\n\n function emitValue(nextValue: string) {\n if (!isControlled) {\n setUncontrolledValue(nextValue);\n }\n\n (onValueChange as ((next: string) => void) | undefined)?.(nextValue);\n }\n\n function emitValues(nextValues: string[]) {\n if (!isControlled) {\n setUncontrolledValues(nextValues);\n }\n\n const nextOptions = nextValues\n .map((nextValue) => optionMap.get(nextValue))\n .filter((option): option is ReactElement<ComboBoxOptionProps> => option !== undefined);\n (onValueChange as ((next: ReactElement<ComboBoxOptionProps>[]) => void) | undefined)?.(\n nextOptions,\n );\n }\n\n function commitInput() {\n if (!multiple && inputText !== selectedValue) {\n emitValue(inputText);\n }\n }\n\n function select(nextValue: string) {\n if (multiple) {\n const nextValues = selectedValues.includes(nextValue)\n ? selectedValues.filter((selected) => selected !== nextValue)\n : [...selectedValues, nextValue];\n emitValues(nextValues);\n setInputText('');\n setActiveValue(undefined);\n setOpen(true);\n return;\n }\n\n const option = optionMap.get(nextValue);\n emitValue(nextValue);\n setInputText(option === undefined ? nextValue : getOptionLabel(option.props.children));\n setActiveValue(undefined);\n setOpen(false);\n }\n\n function handleChange(event: ChangeEvent<HTMLInputElement>) {\n setInputText(event.target.value);\n setActiveValue(undefined);\n setOpen(true);\n props.onChange?.(event);\n }\n\n function handleBlur(event: FocusEvent<HTMLInputElement>) {\n props.onBlur?.(event);\n if (!rootRef.current?.contains(event.relatedTarget)) {\n commitInput();\n setOpen(false);\n }\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n const currentIndex = activeValue === undefined ? -1 : visibleValues.indexOf(activeValue);\n let nextIndex = currentIndex;\n\n if (event.key === 'ArrowDown') nextIndex = Math.min(currentIndex + 1, visibleValues.length - 1);\n if (event.key === 'ArrowUp') nextIndex = Math.max(currentIndex - 1, 0);\n if (event.key === 'Home') nextIndex = 0;\n if (event.key === 'End') nextIndex = visibleValues.length - 1;\n\n if (nextIndex !== currentIndex && visibleValues[nextIndex] !== undefined) {\n event.preventDefault();\n setActiveValue(visibleValues[nextIndex]);\n setOpen(true);\n } else if (event.key === 'Enter' && activeValue !== undefined) {\n event.preventDefault();\n select(activeValue);\n } else if (event.key === 'Escape') {\n event.preventDefault();\n setInputText(multiple ? '' : selectedValue);\n setActiveValue(undefined);\n setOpen(false);\n }\n\n props.onKeyDown?.(event);\n }\n\n const resolvedClassName = ['chayns-combo-box', open ? 'chayns-combo-box--open' : '']\n .filter(Boolean)\n .join(' ');\n const describedLabel = placeholder === undefined ? undefined : `${inputId}-label`;\n const selectedOptions = selectedValues\n .map((selected) => optionMap.get(selected))\n .filter((option): option is ReactElement<ComboBoxOptionProps> => option !== undefined);\n const inputClassName = ['chayns-combo-box__input', className].filter(Boolean).join(' ');\n\n return (\n <ComboBoxContext.Provider\n value={{\n activeValue,\n close: () => setOpen(false),\n inputText,\n isSelected: (optionValue) =>\n multiple ? selectedValues.includes(optionValue) : selectedValue === optionValue,\n multiple,\n optionId: (optionValue) => `${listboxId}-${optionValue.replace(/[^a-zA-Z0-9_-]/g, '-')}`,\n select,\n visibleValues,\n }}\n >\n <div className={resolvedClassName} ref={rootRef}>\n {multiple ? (\n <div className=\"chayns-combo-box__chips\" aria-hidden=\"true\">\n {selectedOptions.map((option) => (\n <span className=\"chayns-combo-box__chip\" key={option.props.value}>\n {option.props.children}\n </span>\n ))}\n </div>\n ) : null}\n <input\n {...inputProps}\n aria-activedescendant={\n open && activeValue\n ? `${listboxId}-${activeValue.replace(/[^a-zA-Z0-9_-]/g, '-')}`\n : undefined\n }\n aria-autocomplete=\"list\"\n aria-controls={listboxId}\n aria-expanded={open}\n aria-label={ariaLabel}\n className={inputClassName}\n disabled={disabled}\n id={inputId}\n onBlur={handleBlur}\n onChange={handleChange}\n onFocus={() => openOnFocus && setOpen(true)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder === undefined ? undefined : ' '}\n ref={setInputElement}\n role=\"combobox\"\n value={inputText}\n />\n {placeholder !== undefined ? (\n <label className=\"chayns-combo-box__label\" htmlFor={inputId} id={describedLabel}>\n {placeholder}\n </label>\n ) : null}\n <span aria-hidden=\"true\" className=\"chayns-combo-box__chevron\">\n <ButtonIcon icon=\"fa-chevron-down\" />\n </span>\n {open ? (\n <div\n aria-multiselectable={multiple || undefined}\n className=\"chayns-combo-box__popup\"\n id={listboxId}\n role=\"listbox\"\n >\n {children}\n </div>\n ) : null}\n </div>\n </ComboBoxContext.Provider>\n );\n});\n\nconst ComboBox = Object.assign(ComboBoxRoot, { Option });\n\nexport default ComboBox;\n"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type DateTimePickerProps } from './DateTimePicker.types.js';
2
+ declare const DateTimePicker: import("react").ForwardRefExoticComponent<Omit<DateTimePickerProps, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
3
+ export default DateTimePicker;
4
+ //# sourceMappingURL=DateTimePicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DateTimePicker.d.ts","sourceRoot":"","sources":["../../../src/components/date-time-picker/DateTimePicker.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAiC,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AA8LpG,QAAA,MAAM,cAAc,gIAyRlB,CAAC;AAEH,eAAe,cAAc,CAAC"}
@@ -0,0 +1,339 @@
1
+ import { DATE_TIME_PICKER_MINUTE_STEPS } from "./DateTimePicker.types.js";
2
+ import { forwardRef, useEffect, useId, useMemo, useRef, useState } from "react";
3
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
4
+ //#region src/components/date-time-picker/DateTimePicker.tsx
5
+ var WHEEL_LOOP_CYCLES = 11;
6
+ var WHEEL_CENTER_CYCLE = Math.floor(WHEEL_LOOP_CYCLES / 2);
7
+ function dateAtMidnight(value) {
8
+ return new Date(value.getFullYear(), value.getMonth(), value.getDate());
9
+ }
10
+ function clampDate(value, minDate, maxDate) {
11
+ const date = dateAtMidnight(value);
12
+ if (minDate !== void 0 && date < dateAtMidnight(minDate)) return new Date(minDate);
13
+ if (maxDate !== void 0 && date > dateAtMidnight(maxDate)) return new Date(maxDate);
14
+ return value;
15
+ }
16
+ function normalizeMinute(value, minuteStep) {
17
+ const normalized = new Date(value);
18
+ normalized.setMinutes(Math.floor(normalized.getMinutes() / minuteStep) * minuteStep);
19
+ return normalized;
20
+ }
21
+ function Wheel({ ariaLabel, onEscape, onSelect, options, selected }) {
22
+ const wheelRef = useRef(null);
23
+ const selectedRef = useRef(null);
24
+ const dragRef = useRef(void 0);
25
+ const ignoreClickRef = useRef(false);
26
+ useEffect(() => {
27
+ if (typeof selectedRef.current?.scrollIntoView === "function") selectedRef.current.scrollIntoView({ block: "center" });
28
+ }, []);
29
+ function applyPerspective() {
30
+ const wheel = wheelRef.current;
31
+ const itemHeight = selectedRef.current?.offsetHeight;
32
+ if (!wheel || !itemHeight) return;
33
+ const position = wheel.scrollTop / itemHeight;
34
+ for (const option of wheel.querySelectorAll(".chayns-date-time-picker__wheel-option")) {
35
+ const index = Number(option.dataset.wheelIndex);
36
+ const distance = Math.max(-2.5, Math.min(2.5, index - position));
37
+ const progress = Math.min(1, Math.abs(distance) / 2.5);
38
+ option.style.color = progress < .2 ? "var(--text)" : "var(--text-3)";
39
+ option.style.fontWeight = progress < .2 ? "600" : "400";
40
+ option.style.opacity = String(1 - progress * .7);
41
+ option.style.transform = `rotateX(${distance * 22}deg) scale(${1 - progress * .24})`;
42
+ }
43
+ }
44
+ function selectCenteredValue() {
45
+ const wheel = wheelRef.current;
46
+ const itemHeight = selectedRef.current?.offsetHeight;
47
+ if (!wheel || !itemHeight) return;
48
+ const option = options[(Math.round(wheel.scrollTop / itemHeight) % options.length + options.length) % options.length];
49
+ if (option !== void 0 && option.value !== selected) onSelect(option.value);
50
+ }
51
+ function handleScroll() {
52
+ const wheel = wheelRef.current;
53
+ const itemHeight = selectedRef.current?.offsetHeight;
54
+ if (!wheel || !itemHeight) return;
55
+ const index = Math.round(wheel.scrollTop / itemHeight);
56
+ const normalizedIndex = (index % options.length + options.length) % options.length;
57
+ if (index < options.length * 2 || index >= options.length * 9) wheel.scrollTop = (WHEEL_CENTER_CYCLE * options.length + normalizedIndex) * itemHeight;
58
+ applyPerspective();
59
+ selectCenteredValue();
60
+ }
61
+ function handleKeyDown(event) {
62
+ if (event.key === "Escape") {
63
+ event.preventDefault();
64
+ onEscape();
65
+ return;
66
+ }
67
+ const index = options.findIndex((option) => option.value === selected);
68
+ let nextIndex = index;
69
+ if (event.key === "ArrowDown") nextIndex = Math.min(index + 1, options.length - 1);
70
+ if (event.key === "ArrowUp") nextIndex = Math.max(index - 1, 0);
71
+ if (event.key === "Home") nextIndex = 0;
72
+ if (event.key === "End") nextIndex = options.length - 1;
73
+ const nextOption = options[nextIndex];
74
+ if (nextIndex !== index && nextOption !== void 0) {
75
+ event.preventDefault();
76
+ onSelect(nextOption.value);
77
+ }
78
+ }
79
+ function handlePointerDown(event) {
80
+ if (event.button !== 0) return;
81
+ if (typeof event.currentTarget.setPointerCapture === "function") event.currentTarget.setPointerCapture(event.pointerId);
82
+ dragRef.current = {
83
+ pointerId: event.pointerId,
84
+ scrollTop: event.currentTarget.scrollTop,
85
+ startY: event.clientY
86
+ };
87
+ ignoreClickRef.current = false;
88
+ }
89
+ function handlePointerMove(event) {
90
+ const drag = dragRef.current;
91
+ if (drag?.pointerId !== event.pointerId) return;
92
+ const distance = event.clientY - drag.startY;
93
+ if (Math.abs(distance) > 3) {
94
+ ignoreClickRef.current = true;
95
+ event.currentTarget.scrollTop = drag.scrollTop - distance;
96
+ }
97
+ }
98
+ function handlePointerUp(event) {
99
+ if (dragRef.current?.pointerId !== event.pointerId) return;
100
+ dragRef.current = void 0;
101
+ if (typeof event.currentTarget.releasePointerCapture === "function") event.currentTarget.releasePointerCapture(event.pointerId);
102
+ selectCenteredValue();
103
+ }
104
+ return /* @__PURE__ */ jsx("div", {
105
+ "aria-label": ariaLabel,
106
+ className: "chayns-date-time-picker__wheel",
107
+ onKeyDown: handleKeyDown,
108
+ onPointerDown: handlePointerDown,
109
+ onPointerMove: handlePointerMove,
110
+ onPointerUp: handlePointerUp,
111
+ onScroll: handleScroll,
112
+ ref: wheelRef,
113
+ role: "listbox",
114
+ tabIndex: 0,
115
+ children: Array.from({ length: WHEEL_LOOP_CYCLES }, (_, cycle) => options.map((option, optionIndex) => /* @__PURE__ */ jsx("button", {
116
+ "aria-selected": option.value === selected,
117
+ className: "chayns-date-time-picker__wheel-option",
118
+ "data-wheel-index": cycle * options.length + optionIndex,
119
+ onClick: (event) => {
120
+ if (ignoreClickRef.current) {
121
+ event.preventDefault();
122
+ event.stopPropagation();
123
+ ignoreClickRef.current = false;
124
+ return;
125
+ }
126
+ onSelect(option.value);
127
+ },
128
+ ref: cycle === WHEEL_CENTER_CYCLE && option.value === selected ? selectedRef : void 0,
129
+ role: "option",
130
+ type: "button",
131
+ children: option.label
132
+ }, `${cycle}-${option.value}`)))
133
+ });
134
+ }
135
+ var DateTimePicker = forwardRef(function DateTimePicker({ className, disabled, id, label, locale, maxDate, minDate, minuteStep = 1, mode = "time", onChange, onClick, onKeyDown, placeholder, value, wheelLabels, ...buttonProps }, ref) {
136
+ if (!DATE_TIME_PICKER_MINUTE_STEPS.includes(minuteStep)) throw new Error("DateTimePicker minuteStep must be one of: 1, 5, 15, 30.");
137
+ if (minDate !== void 0 && maxDate !== void 0 && dateAtMidnight(minDate) > dateAtMidnight(maxDate)) throw new Error("DateTimePicker requires minDate to be earlier than or equal to maxDate.");
138
+ const generatedId = useId();
139
+ const triggerId = id ?? generatedId;
140
+ const popupId = `${triggerId}-popup`;
141
+ const rootRef = useRef(null);
142
+ const triggerRef = useRef(null);
143
+ const [open, setOpen] = useState(false);
144
+ const displayValue = value === null ? null : mode === "date" ? clampDate(value, minDate, maxDate) : normalizeMinute(value, minuteStep);
145
+ const activeValue = displayValue ?? /* @__PURE__ */ new Date();
146
+ const isTwelveHour = new Intl.DateTimeFormat(locale, { hour: "numeric" }).resolvedOptions().hour12 === true;
147
+ useEffect(() => {
148
+ if (!open) return void 0;
149
+ function closeOnOutsidePress(event) {
150
+ if (event.target instanceof Node && !rootRef.current?.contains(event.target)) close();
151
+ }
152
+ document.addEventListener("pointerdown", closeOnOutsidePress);
153
+ return () => document.removeEventListener("pointerdown", closeOnOutsidePress);
154
+ });
155
+ const years = useMemo(() => {
156
+ const today = (/* @__PURE__ */ new Date()).getFullYear();
157
+ const first = minDate?.getFullYear() ?? today - 100;
158
+ const last = maxDate?.getFullYear() ?? today + 100;
159
+ return Array.from({ length: last - first + 1 }, (_, index) => first + index);
160
+ }, [maxDate, minDate]);
161
+ function close() {
162
+ setOpen(false);
163
+ triggerRef.current?.focus();
164
+ }
165
+ function emit(nextValue) {
166
+ onChange(mode === "date" ? clampDate(nextValue, minDate, maxDate) : nextValue);
167
+ }
168
+ function updateTime(part, nextValue) {
169
+ const next = new Date(activeValue);
170
+ if (part === "hours") {
171
+ const hour = isTwelveHour ? nextValue % 12 + (next.getHours() >= 12 ? 12 : 0) : nextValue;
172
+ next.setHours(hour);
173
+ } else next.setMinutes(nextValue);
174
+ emit(next);
175
+ }
176
+ function updatePeriod(period) {
177
+ const next = new Date(activeValue);
178
+ next.setHours(next.getHours() % 12 + period * 12);
179
+ emit(next);
180
+ }
181
+ function updateDate(part, nextValue) {
182
+ const next = new Date(activeValue);
183
+ const originalDay = next.getDate();
184
+ next.setDate(1);
185
+ if (part === "fullYear") next.setFullYear(nextValue);
186
+ if (part === "month") next.setMonth(nextValue);
187
+ if (part === "date") next.setDate(nextValue);
188
+ if (part !== "date") next.setDate(Math.min(originalDay, new Date(next.getFullYear(), next.getMonth() + 1, 0).getDate()));
189
+ emit(next);
190
+ }
191
+ const formattedValue = displayValue === null ? placeholder : mode === "time" ? `${new Intl.DateTimeFormat(locale, {
192
+ hour: "numeric",
193
+ minute: "2-digit"
194
+ }).format(displayValue)}${locale.toLowerCase().startsWith("de") ? " Uhr" : ""}` : new Intl.DateTimeFormat(locale, {
195
+ day: "numeric",
196
+ month: "long",
197
+ year: "numeric"
198
+ }).format(displayValue);
199
+ const hours = Array.from({ length: isTwelveHour ? 12 : 24 }, (_, index) => isTwelveHour ? index + 1 : index).map((hour) => ({
200
+ label: String(hour).padStart(2, "0"),
201
+ value: hour
202
+ }));
203
+ const minutes = Array.from({ length: 60 / minuteStep }, (_, index) => {
204
+ const minute = index * minuteStep;
205
+ return {
206
+ label: String(minute).padStart(2, "0"),
207
+ value: minute
208
+ };
209
+ });
210
+ const months = Array.from({ length: 12 }, (_, month) => ({
211
+ label: new Intl.DateTimeFormat(locale, { month: "long" }).format(new Date(2026, month, 1)),
212
+ value: month
213
+ })).filter((month) => {
214
+ const first = new Date(activeValue.getFullYear(), month.value, 1);
215
+ const last = new Date(activeValue.getFullYear(), month.value + 1, 0);
216
+ return (minDate === void 0 || last >= dateAtMidnight(minDate)) && (maxDate === void 0 || first <= dateAtMidnight(maxDate));
217
+ });
218
+ const days = Array.from({ length: new Date(activeValue.getFullYear(), activeValue.getMonth() + 1, 0).getDate() }, (_, index) => ({
219
+ label: String(index + 1).padStart(2, "0"),
220
+ value: index + 1
221
+ })).filter((day) => {
222
+ const candidate = new Date(activeValue.getFullYear(), activeValue.getMonth(), day.value);
223
+ return (minDate === void 0 || candidate >= dateAtMidnight(minDate)) && (maxDate === void 0 || candidate <= dateAtMidnight(maxDate));
224
+ });
225
+ const datePartOrder = new Intl.DateTimeFormat(locale, {
226
+ day: "numeric",
227
+ month: "numeric",
228
+ year: "numeric"
229
+ }).formatToParts(activeValue).flatMap((part) => {
230
+ if (part.type === "day") return ["date"];
231
+ if (part.type === "month") return ["month"];
232
+ if (part.type === "year") return ["fullYear"];
233
+ return [];
234
+ });
235
+ const yearOptions = years.map((year) => ({
236
+ label: String(year),
237
+ value: year
238
+ }));
239
+ function dateOptions(part) {
240
+ if (part === "date") return days;
241
+ if (part === "month") return months;
242
+ return yearOptions;
243
+ }
244
+ function dateValue(part) {
245
+ if (part === "date") return activeValue.getDate();
246
+ if (part === "month") return activeValue.getMonth();
247
+ return activeValue.getFullYear();
248
+ }
249
+ const dayPeriods = [0, 1].map((period) => ({
250
+ label: new Intl.DateTimeFormat(locale, { hour: "numeric" }).formatToParts(new Date(2026, 0, 1, period === 0 ? 1 : 13)).find((part) => part.type === "dayPeriod")?.value ?? (period === 0 ? "AM" : "PM"),
251
+ value: period
252
+ }));
253
+ const resolvedClassName = [
254
+ "chayns-date-time-picker",
255
+ `chayns-date-time-picker--${mode}`,
256
+ open ? "chayns-date-time-picker--open" : "",
257
+ displayValue !== null ? "chayns-date-time-picker--filled" : ""
258
+ ].filter(Boolean).join(" ");
259
+ return /* @__PURE__ */ jsxs("div", {
260
+ className: resolvedClassName,
261
+ ref: rootRef,
262
+ children: [
263
+ /* @__PURE__ */ jsx("button", {
264
+ ...buttonProps,
265
+ "aria-controls": open ? popupId : void 0,
266
+ "aria-expanded": open,
267
+ "aria-haspopup": "dialog",
268
+ className: ["chayns-date-time-picker__trigger", className].filter(Boolean).join(" "),
269
+ disabled,
270
+ id: triggerId,
271
+ onClick: (event) => {
272
+ onClick?.(event);
273
+ if (!event.defaultPrevented) setOpen((current) => !current);
274
+ },
275
+ onKeyDown: (event) => {
276
+ onKeyDown?.(event);
277
+ if (event.key === "Escape" && open && !event.defaultPrevented) {
278
+ event.preventDefault();
279
+ close();
280
+ }
281
+ },
282
+ ref: (element) => {
283
+ triggerRef.current = element;
284
+ if (typeof ref === "function") ref(element);
285
+ else if (ref !== null) ref.current = element;
286
+ },
287
+ type: "button",
288
+ children: formattedValue
289
+ }),
290
+ /* @__PURE__ */ jsx("label", {
291
+ className: "chayns-date-time-picker__label",
292
+ htmlFor: triggerId,
293
+ children: label
294
+ }),
295
+ open && !disabled ? /* @__PURE__ */ jsxs("div", {
296
+ "aria-label": label,
297
+ className: "chayns-date-time-picker__popup",
298
+ id: popupId,
299
+ role: "dialog",
300
+ children: [/* @__PURE__ */ jsx("div", { className: "chayns-date-time-picker__selection" }), /* @__PURE__ */ jsx("div", {
301
+ className: "chayns-date-time-picker__wheels",
302
+ children: mode === "time" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
303
+ /* @__PURE__ */ jsx(Wheel, {
304
+ ariaLabel: wheelLabels.hour,
305
+ onEscape: close,
306
+ onSelect: (hour) => updateTime("hours", hour),
307
+ options: hours,
308
+ selected: isTwelveHour ? activeValue.getHours() % 12 || 12 : activeValue.getHours()
309
+ }),
310
+ /* @__PURE__ */ jsx(Wheel, {
311
+ ariaLabel: wheelLabels.minute,
312
+ onEscape: close,
313
+ onSelect: (minute) => updateTime("minutes", minute),
314
+ options: minutes,
315
+ selected: activeValue.getMinutes()
316
+ }),
317
+ isTwelveHour ? /* @__PURE__ */ jsx(Wheel, {
318
+ ariaLabel: wheelLabels.dayPeriod,
319
+ onEscape: close,
320
+ onSelect: updatePeriod,
321
+ options: dayPeriods,
322
+ selected: activeValue.getHours() >= 12 ? 1 : 0
323
+ }) : null
324
+ ] }) : datePartOrder.map((part) => /* @__PURE__ */ jsx(Wheel, {
325
+ ariaLabel: part === "date" ? wheelLabels.day : part === "month" ? wheelLabels.month : wheelLabels.year,
326
+ onEscape: close,
327
+ onSelect: (nextValue) => updateDate(part, nextValue),
328
+ options: dateOptions(part),
329
+ selected: dateValue(part)
330
+ }, part))
331
+ })]
332
+ }) : null
333
+ ]
334
+ });
335
+ });
336
+ //#endregion
337
+ export { DateTimePicker as default };
338
+
339
+ //# sourceMappingURL=DateTimePicker.js.map