@hawiyah/react 0.0.0-stage → 0.2.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 (77) hide show
  1. package/LICENSE +73 -0
  2. package/NOTICE +19 -0
  3. package/PORTAL.md +74 -0
  4. package/README.md +461 -2
  5. package/dist/calendar.d.ts +52 -0
  6. package/dist/calendar.d.ts.map +1 -0
  7. package/dist/calendar.js +148 -0
  8. package/dist/calendar.js.map +1 -0
  9. package/dist/content-navigation.d.ts +15 -0
  10. package/dist/content-navigation.d.ts.map +1 -0
  11. package/dist/content-navigation.js +29 -0
  12. package/dist/content-navigation.js.map +1 -0
  13. package/dist/display.d.ts +421 -0
  14. package/dist/display.d.ts.map +1 -0
  15. package/dist/display.js +208 -0
  16. package/dist/display.js.map +1 -0
  17. package/dist/expanded-content.d.ts +217 -0
  18. package/dist/expanded-content.d.ts.map +1 -0
  19. package/dist/expanded-content.js +124 -0
  20. package/dist/expanded-content.js.map +1 -0
  21. package/dist/expanded-controls.d.ts +107 -0
  22. package/dist/expanded-controls.d.ts.map +1 -0
  23. package/dist/expanded-controls.js +142 -0
  24. package/dist/expanded-controls.js.map +1 -0
  25. package/dist/expanded-interactive.d.ts +116 -0
  26. package/dist/expanded-interactive.d.ts.map +1 -0
  27. package/dist/expanded-interactive.js +204 -0
  28. package/dist/expanded-interactive.js.map +1 -0
  29. package/dist/expanded-layout.d.ts +80 -0
  30. package/dist/expanded-layout.d.ts.map +1 -0
  31. package/dist/expanded-layout.js +26 -0
  32. package/dist/expanded-layout.js.map +1 -0
  33. package/dist/expanded-shared.d.ts +3 -0
  34. package/dist/expanded-shared.d.ts.map +1 -0
  35. package/dist/expanded-shared.js +14 -0
  36. package/dist/expanded-shared.js.map +1 -0
  37. package/dist/file-upload-list.d.ts +10 -0
  38. package/dist/file-upload-list.d.ts.map +1 -0
  39. package/dist/file-upload-list.js +7 -0
  40. package/dist/file-upload-list.js.map +1 -0
  41. package/dist/forms.d.ts +34 -0
  42. package/dist/forms.d.ts.map +1 -0
  43. package/dist/forms.js +65 -0
  44. package/dist/forms.js.map +1 -0
  45. package/dist/index.d.ts +20 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +17 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/interactive.d.ts +60 -0
  50. package/dist/interactive.d.ts.map +1 -0
  51. package/dist/interactive.js +321 -0
  52. package/dist/interactive.js.map +1 -0
  53. package/dist/mobile-top-bar.d.ts +14 -0
  54. package/dist/mobile-top-bar.d.ts.map +1 -0
  55. package/dist/mobile-top-bar.js +8 -0
  56. package/dist/mobile-top-bar.js.map +1 -0
  57. package/dist/navigation-drawer.d.ts +20 -0
  58. package/dist/navigation-drawer.d.ts.map +1 -0
  59. package/dist/navigation-drawer.js +37 -0
  60. package/dist/navigation-drawer.js.map +1 -0
  61. package/dist/portal.d.ts +27 -0
  62. package/dist/portal.d.ts.map +1 -0
  63. package/dist/portal.js +16 -0
  64. package/dist/portal.js.map +1 -0
  65. package/dist/search-field.d.ts +15 -0
  66. package/dist/search-field.d.ts.map +1 -0
  67. package/dist/search-field.js +113 -0
  68. package/dist/search-field.js.map +1 -0
  69. package/dist/shared.d.ts +35 -0
  70. package/dist/shared.d.ts.map +1 -0
  71. package/dist/shared.js +78 -0
  72. package/dist/shared.js.map +1 -0
  73. package/dist/site-verification.d.ts +10 -0
  74. package/dist/site-verification.d.ts.map +1 -0
  75. package/dist/site-verification.js +16 -0
  76. package/dist/site-verification.js.map +1 -0
  77. package/package.json +54 -3
@@ -0,0 +1,34 @@
1
+ import type { ComponentPropsWithRef, ReactNode } from 'react';
2
+ import type { FieldDecoration } from './shared.js';
3
+ import { type Option } from '@hawiyah/core';
4
+ export interface ButtonProps extends ComponentPropsWithRef<'button'> {
5
+ variant?: 'primary' | 'default' | 'secondary' | 'outline' | 'outline-white' | 'light' | 'danger' | 'ghost' | 'soft' | 'link';
6
+ size?: 'sm' | 'md' | 'lg';
7
+ loading?: boolean;
8
+ }
9
+ export declare function Button({ variant, size, loading, disabled, type, className, children, ...props }: ButtonProps): import("react").JSX.Element;
10
+ export interface TextFieldProps extends ComponentPropsWithRef<'input'>, FieldDecoration {
11
+ }
12
+ export declare function TextField({ label, hint, error, ...props }: TextFieldProps): import("react").JSX.Element;
13
+ export interface TextareaProps extends ComponentPropsWithRef<'textarea'>, FieldDecoration {
14
+ }
15
+ export declare function Textarea({ label, hint, error, ...props }: TextareaProps): import("react").JSX.Element;
16
+ export interface SelectProps extends ComponentPropsWithRef<'select'>, FieldDecoration {
17
+ options?: readonly Option[];
18
+ placeholder?: string;
19
+ }
20
+ export declare function Select({ label, hint, error, ...props }: SelectProps): import("react").JSX.Element;
21
+ export interface CheckboxProps extends Omit<ComponentPropsWithRef<'input'>, 'type'>, FieldDecoration {
22
+ indeterminate?: boolean;
23
+ }
24
+ export declare function Checkbox(props: CheckboxProps): import("react").JSX.Element;
25
+ export type RadioProps = Omit<CheckboxProps, 'indeterminate'>;
26
+ export declare function Radio(props: RadioProps): import("react").JSX.Element;
27
+ export type SwitchProps = Omit<CheckboxProps, 'indeterminate'>;
28
+ export declare function Switch(props: SwitchProps): import("react").JSX.Element;
29
+ export interface FileUploadProps extends Omit<TextFieldProps, 'type' | 'value' | 'defaultValue'> {
30
+ onFilesChange?: (files: File[]) => void;
31
+ children?: ReactNode;
32
+ }
33
+ export declare function FileUpload({ label, hint, error, onFilesChange, onChange, children, className, ...props }: FileUploadProps): import("react").JSX.Element;
34
+ //# sourceMappingURL=forms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forms.d.ts","sourceRoot":"","sources":["../src/forms.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAqC,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAE/E,MAAM,WAAW,WAAY,SAAQ,qBAAqB,CAAC,QAAQ,CAAC;IAClE,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAC7H,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AACD,wBAAgB,MAAM,CAAC,EAAE,OAAmB,EAAE,IAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAe,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,WAAW,+BAE1I;AACD,MAAM,WAAW,cAAe,SAAQ,qBAAqB,CAAC,OAAO,CAAC,EAAE,eAAe;CAAG;AAM1F,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,+BAGzE;AACD,MAAM,WAAW,aAAc,SAAQ,qBAAqB,CAAC,UAAU,CAAC,EAAE,eAAe;CAAG;AAM5F,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,+BAGvE;AACD,MAAM,WAAW,WAAY,SAAQ,qBAAqB,CAAC,QAAQ,CAAC,EAAE,eAAe;IAAG,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE;AAO3I,wBAAgB,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,WAAW,+BAGnE;AACD,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,EAAE,eAAe;IAAG,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE;AAyBhI,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,+BAAmD;AAChG,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;AAC9D,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,+BAAgD;AACvF,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;AAC/D,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,+BAAiD;AAC1F,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,GAAG,cAAc,CAAC;IAC9F,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AACD,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,+BAMzH"}
package/dist/forms.js ADDED
@@ -0,0 +1,65 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Children, isValidElement, useEffect, useId, useRef } from 'react';
3
+ import { assignRef, cx, ControlField, Field, useFieldControl, useFieldValueRef } from './shared.js';
4
+ import { observeReadonlyChoice, isDisabled } from '@hawiyah/core';
5
+ export function Button({ variant = 'primary', size = 'md', loading, disabled, type = 'button', className, children, ...props }) {
6
+ return _jsx("button", { ...props, type: type, disabled: disabled || loading, "aria-busy": loading || props['aria-busy'], className: cx('hw-button', className), "data-variant": variant, "data-size": size, children: children });
7
+ }
8
+ function InputControl(props) {
9
+ const field = useFieldControl(props);
10
+ const ref = useFieldValueRef(props.ref);
11
+ return _jsx("input", { ...props, ...field, ref: ref, className: cx('hw-input', props.className) });
12
+ }
13
+ export function TextField({ label, hint, error, ...props }) {
14
+ if (label == null && hint == null && error == null)
15
+ return _jsx(InputControl, { ...props });
16
+ return _jsx(ControlField, { label: label, hint: hint, error: error, required: props.required, controlId: props.id, children: _jsx(InputControl, { ...props }) });
17
+ }
18
+ function TextareaControl(props) {
19
+ const field = useFieldControl(props);
20
+ const ref = useFieldValueRef(props.ref);
21
+ return _jsx("textarea", { ...props, ...field, ref: ref, className: cx('hw-textarea', props.className) });
22
+ }
23
+ export function Textarea({ label, hint, error, ...props }) {
24
+ if (label == null && hint == null && error == null)
25
+ return _jsx(TextareaControl, { ...props });
26
+ return _jsx(ControlField, { label: label, hint: hint, error: error, required: props.required, controlId: props.id, children: _jsx(TextareaControl, { ...props }) });
27
+ }
28
+ function SelectControl({ options, children, placeholder, ...props }) {
29
+ const field = useFieldControl(props);
30
+ const ref = useFieldValueRef(props.ref);
31
+ const hasEmptyOption = options?.some(option => option.value === '') || Children.toArray(children).some(child => isValidElement(child) && child.type === 'option' && child.props.value === '');
32
+ return _jsxs("select", { ...props, ...field, ref: ref, className: cx('hw-select', props.className), children: [placeholder && !props.multiple && !hasEmptyOption && _jsx("option", { value: "", children: placeholder }), options?.map(option => _jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)), children] });
33
+ }
34
+ export function Select({ label, hint, error, ...props }) {
35
+ if (label == null && hint == null && error == null)
36
+ return _jsx(SelectControl, { ...props });
37
+ return _jsx(ControlField, { label: label, hint: hint, error: error, required: props.required, controlId: props.id, children: _jsx(SelectControl, { ...props }) });
38
+ }
39
+ function Choice({ kind, label, hint, error, className, indeterminate, ref, onClick, onKeyDown, onChange, ...props }) {
40
+ const field = useFieldControl(props);
41
+ const input = useRef(null);
42
+ const hintId = hint ? `${field.id}-hint` : undefined;
43
+ const errorId = error ? `${field.id}-error` : undefined;
44
+ useEffect(() => { if (input.current)
45
+ input.current.indeterminate = Boolean(indeterminate); }, [indeterminate]);
46
+ useEffect(() => {
47
+ if (!input.current)
48
+ return;
49
+ const guard = observeReadonlyChoice(input.current);
50
+ return () => guard.destroy();
51
+ });
52
+ return _jsxs("div", { className: "hw-field", children: [_jsxs("label", { className: "hw-choice", htmlFor: field.id, children: [_jsx("input", { ...props, ...field, ref: node => { input.current = node; assignRef(ref, node); }, type: kind === 'radio' ? 'radio' : 'checkbox', role: kind === 'switch' ? 'switch' : props.role, className: cx(`hw-${kind}`, className), "aria-disabled": props.readOnly || props['aria-disabled'], "aria-invalid": error ? true : field['aria-invalid'], "aria-describedby": [field['aria-describedby'], hintId, errorId].filter(Boolean).join(' ') || undefined, onClick: event => { onClick?.(event); if (props.readOnly || isDisabled(event.currentTarget)) {
53
+ event.preventDefault();
54
+ } }, onKeyDown: event => { onKeyDown?.(event); if (props.readOnly && (event.key === ' ' || kind === 'radio' && ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.key)))
55
+ event.preventDefault(); }, onChange: event => { if (!props.readOnly && !isDisabled(event.currentTarget))
56
+ onChange?.(event); } }), label] }), hint && _jsx("span", { id: hintId, className: "hw-hint", children: hint }), error && _jsx("span", { id: errorId, className: "hw-error", children: error })] });
57
+ }
58
+ export function Checkbox(props) { return _jsx(Choice, { ...props, kind: "checkbox" }); }
59
+ export function Radio(props) { return _jsx(Choice, { ...props, kind: "radio" }); }
60
+ export function Switch(props) { return _jsx(Choice, { ...props, kind: "switch" }); }
61
+ export function FileUpload({ label, hint, error, onFilesChange, onChange, children, className, ...props }) {
62
+ const id = useId();
63
+ return _jsxs(Field, { label: label, hint: hint, error: error, required: props.required, controlId: props.id ?? id, className: "hw-upload", children: [_jsx(InputControl, { ...props, className: className, type: "file", onChange: event => { onChange?.(event); onFilesChange?.(Array.from(event.currentTarget.files ?? [])); } }), children] });
64
+ }
65
+ //# sourceMappingURL=forms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forms.js","sourceRoot":"","sources":["../src/forms.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /** Native React components. Import @hawiyah/styles/styles.css once in the application. */
2
+ export { Field } from './shared.js';
3
+ export type { FieldProps, FieldDecoration } from './shared.js';
4
+ export * from './forms.js';
5
+ export * from './interactive.js';
6
+ export * from './display.js';
7
+ export type { Direction, Orientation, Option } from '@hawiyah/core';
8
+ export * from './expanded-controls.js';
9
+ export * from './calendar.js';
10
+ export * from './expanded-interactive.js';
11
+ export * from './expanded-content.js';
12
+ export * from './expanded-layout.js';
13
+ export * from './portal.js';
14
+ export type { CalendarSystem } from '@hawiyah/core';
15
+ export * from './mobile-top-bar.js';
16
+ export * from './navigation-drawer.js';
17
+ export * from './site-verification.js';
18
+ export * from './content-navigation.js';
19
+ export * from './file-upload-list.js';
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC/D,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACpE,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC;AAC9B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,aAAa,CAAC;AAC5B,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AAEvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AAExC,cAAc,uBAAuB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ /** Native React components. Import @hawiyah/styles/styles.css once in the application. */
2
+ export { Field } from './shared.js';
3
+ export * from './forms.js';
4
+ export * from './interactive.js';
5
+ export * from './display.js';
6
+ export * from './expanded-controls.js';
7
+ export * from './calendar.js';
8
+ export * from './expanded-interactive.js';
9
+ export * from './expanded-content.js';
10
+ export * from './expanded-layout.js';
11
+ export * from './portal.js';
12
+ export * from './mobile-top-bar.js';
13
+ export * from './navigation-drawer.js';
14
+ export * from './site-verification.js';
15
+ export * from './content-navigation.js';
16
+ export * from './file-upload-list.js';
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAE7B,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC;AAC9B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,aAAa,CAAC;AAE5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AAEvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AAExC,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,60 @@
1
+ import type { ButtonHTMLAttributes, ComponentPropsWithRef, HTMLAttributes, ReactElement, ReactNode } from 'react';
2
+ import type { Option, Orientation } from '@hawiyah/core';
3
+ import type { FieldDecoration } from './shared.js';
4
+ export interface TabItem {
5
+ value: string;
6
+ label: ReactNode;
7
+ content: ReactNode;
8
+ disabled?: boolean;
9
+ }
10
+ export interface TabsProps extends Omit<ComponentPropsWithRef<'div'>, 'defaultValue' | 'onChange'> {
11
+ items: readonly TabItem[];
12
+ value?: string;
13
+ defaultValue?: string;
14
+ onValueChange?: (value: string) => void;
15
+ label: string;
16
+ orientation?: Orientation;
17
+ activationMode?: 'automatic' | 'manual';
18
+ overflow?: 'menu' | 'scroll';
19
+ overflowLabel?: string;
20
+ }
21
+ export declare function Tabs({ items, value, defaultValue, onValueChange, label, orientation, activationMode, overflow, overflowLabel, className, ...props }: TabsProps): import("react").JSX.Element;
22
+ export interface DialogProps extends Omit<ComponentPropsWithRef<'dialog'>, 'open' | 'children' | 'title'> {
23
+ open?: boolean;
24
+ defaultOpen?: boolean;
25
+ onOpenChange?: (open: boolean) => void;
26
+ title: ReactNode;
27
+ description?: ReactNode;
28
+ trigger?: ReactElement<ButtonHTMLAttributes<HTMLButtonElement>>;
29
+ children?: ReactNode | ((context: {
30
+ close: () => void;
31
+ }) => ReactNode);
32
+ footer?: ReactNode | ((context: {
33
+ close: () => void;
34
+ }) => ReactNode);
35
+ closeOnBackdrop?: boolean;
36
+ closeLabel?: string;
37
+ }
38
+ export declare function Dialog({ open: controlled, defaultOpen, onOpenChange, title, description, trigger, children, footer, closeOnBackdrop, closeLabel, ref, className, onCancel, onClose, onClick, onPointerDown, onPointerCancel, ...props }: DialogProps): import("react").JSX.Element;
39
+ export interface ComboboxProps extends Omit<ComponentPropsWithRef<'input'>, 'value' | 'defaultValue' | 'onChange' | 'type' | 'children'>, FieldDecoration {
40
+ options: readonly Option[];
41
+ value?: string;
42
+ defaultValue?: string;
43
+ onValueChange?: (value: string, option?: Option) => void;
44
+ onInputChange?: (query: string) => void;
45
+ open?: boolean;
46
+ defaultOpen?: boolean;
47
+ onOpenChange?: (open: boolean) => void;
48
+ emptyMessage?: string;
49
+ getResultsMessage?: (count: number) => string;
50
+ requiredMessage?: string;
51
+ }
52
+ export declare function Combobox({ label, hint, error, ...props }: ComboboxProps): import("react").JSX.Element;
53
+ export interface TooltipProps extends Omit<ComponentPropsWithRef<'span'>, 'children' | 'content'> {
54
+ children: ReactElement<HTMLAttributes<HTMLElement>>;
55
+ content: string;
56
+ placement?: 'top' | 'bottom' | 'start' | 'end';
57
+ align?: 'start' | 'center' | 'end';
58
+ }
59
+ export declare function Tooltip({ children, content, placement, align, className, ref, onMouseEnter, onMouseLeave, onFocus, onBlur, onKeyDown, ...props }: TooltipProps): import("react").JSX.Element;
60
+ //# sourceMappingURL=interactive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../src/interactive.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElH,OAAO,KAAK,EAAiB,MAAM,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAExE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnD,MAAM,WAAW,OAAO;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE;AACpG,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,cAAc,GAAG,UAAU,CAAC;IAChG,KAAK,EAAE,SAAS,OAAO,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,cAAc,CAAC,EAAE,WAAW,GAAG,QAAQ,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AACD,wBAAgB,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,KAAK,EAAE,WAA0B,EAAE,cAA4B,EAAE,QAAiB,EAAE,aAAqC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,SAAS,+BAsE5N;AAED,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC;IACvG,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,OAAO,CAAC,EAAE,YAAY,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAChE,QAAQ,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,IAAI,CAAA;KAAE,KAAK,SAAS,CAAC,CAAC;IACvE,MAAM,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,IAAI,CAAA;KAAE,KAAK,SAAS,CAAC,CAAC;IACrE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AACD,wBAAgB,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,WAAmB,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAsB,EAAE,UAAoB,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,GAAG,KAAK,EAAE,EAAE,WAAW,+BA+D7Q;AAED,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,cAAc,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC,EAAE,eAAe;IACvJ,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AA2GD,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,+BAIvE;AAED,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAC/F,QAAQ,EAAE,YAAY,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;IAC/C,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;CACpC;AACD,wBAAgB,OAAO,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAiB,EAAE,KAAe,EAAE,SAAS,EAAE,GAAG,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,YAAY,+BAiChL"}
@@ -0,0 +1,321 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { cloneElement, useEffect, useId, useRef, useState } from 'react';
3
+ import { createComboboxState, getComboboxOptions, getDirection, getNextEnabledIndex, isDisabled, observeTabOverflow, transitionCombobox } from '@hawiyah/core';
4
+ import { assignRef, cx, Field, useControllable, useFieldControl, useFieldValueRef } from './shared.js';
5
+ import { Button } from './forms.js';
6
+ export function Tabs({ items, value, defaultValue, onValueChange, label, orientation = 'horizontal', activationMode = 'automatic', overflow = 'menu', overflowLabel = 'المزيد من التبويبات', className, ...props }) {
7
+ const id = useId();
8
+ const [selected, select] = useControllable(value, defaultValue ?? items.find(item => !item.disabled)?.value ?? '', onValueChange);
9
+ const [focused, setFocused] = useState();
10
+ const buttons = useRef(new Map());
11
+ const bar = useRef(null);
12
+ const disclosure = useRef(null);
13
+ const [hidden, setHidden] = useState([]);
14
+ const observer = useRef(undefined);
15
+ const latest = useRef({ items, overflow, orientation });
16
+ latest.current = { items, overflow, orientation };
17
+ const pendingFocus = useRef(undefined);
18
+ const active = items.find(item => item.value === selected && !item.disabled)?.value ?? items.find(item => !item.disabled)?.value;
19
+ const visible = items.filter((item, index) => !hidden.includes(index) && !item.disabled);
20
+ const focusValue = visible.find(item => item.value === focused)?.value ?? visible.find(item => item.value === active)?.value ?? visible[0]?.value;
21
+ const close = () => { if (disclosure.current)
22
+ disclosure.current.open = false; };
23
+ useEffect(() => {
24
+ const node = bar.current;
25
+ if (!node)
26
+ return;
27
+ observer.current = observeTabOverflow({ container: node,
28
+ tabs: () => latest.current.items.map(item => buttons.current.get(item.value)).filter(Boolean),
29
+ enabled: () => latest.current.overflow === 'menu' && latest.current.orientation === 'horizontal',
30
+ onChange: next => {
31
+ const focusedElement = node.ownerDocument.activeElement;
32
+ const menuValue = focusedElement?.dataset.hwOverflowValue;
33
+ const itemIndex = latest.current.items.findIndex(item => item.value === menuValue);
34
+ if (menuValue !== undefined && itemIndex >= 0 && next.includes(itemIndex))
35
+ pendingFocus.current = { kind: 'menu', value: menuValue };
36
+ else {
37
+ if (menuValue !== undefined && itemIndex >= 0)
38
+ pendingFocus.current = { kind: 'tab', value: menuValue };
39
+ else if (next.some(index => buttons.current.get(latest.current.items[index]?.value ?? '') === focusedElement))
40
+ pendingFocus.current = { kind: 'summary' };
41
+ else if (disclosure.current?.contains(focusedElement))
42
+ pendingFocus.current = { kind: next.length ? 'summary' : 'tab' };
43
+ close();
44
+ }
45
+ setHidden(next);
46
+ } });
47
+ const outside = (event) => { if (!node.contains(event.target))
48
+ close(); };
49
+ node.ownerDocument.addEventListener('click', outside);
50
+ return () => { observer.current?.destroy(); node.ownerDocument.removeEventListener('click', outside); };
51
+ }, []);
52
+ useEffect(() => { observer.current?.refresh(); const focusedElement = bar.current?.ownerDocument.activeElement; if (focusedElement?.getAttribute('data-hw-overflow-hidden') === 'true' && bar.current?.contains(focusedElement))
53
+ disclosure.current?.querySelector('summary')?.focus(); }, [items, overflow, orientation]);
54
+ useEffect(() => { close(); }, [active]);
55
+ useEffect(() => {
56
+ const destination = pendingFocus.current;
57
+ pendingFocus.current = undefined;
58
+ if (destination?.kind === 'summary')
59
+ disclosure.current?.querySelector('summary')?.focus();
60
+ else if (destination?.kind === 'tab' && (destination.value ?? focusValue))
61
+ buttons.current.get(destination.value ?? focusValue)?.focus();
62
+ else if (destination?.kind === 'menu')
63
+ [...(disclosure.current?.querySelectorAll('[data-hw-overflow-value]') ?? [])].find(button => button.dataset.hwOverflowValue === destination.value)?.focus();
64
+ }, [hidden, focusValue]);
65
+ return _jsxs("div", { ...props, className: cx('hw-tabs', className), children: [_jsxs("div", { className: "hw-tab-bar", ref: bar, "data-hw-overflow": overflow, "data-orientation": orientation, children: [_jsx("div", { className: "hw-tab-list", "data-hw-overflow": overflow, role: "tablist", "aria-label": label, "aria-orientation": orientation, "aria-hidden": hidden.length === items.length || undefined, children: items.map((item, index) => _jsx("button", { type: "button", role: "tab", className: "hw-tab", id: `${id}-tab-${index}`, "aria-controls": `${id}-panel-${index}`, "aria-selected": active === item.value, "aria-hidden": hidden.includes(index) || undefined, "data-hw-overflow-hidden": hidden.includes(index) || undefined, tabIndex: !hidden.includes(index) && focusValue === item.value ? 0 : -1, disabled: item.disabled, ref: node => { if (node)
66
+ buttons.current.set(item.value, node);
67
+ else
68
+ buttons.current.delete(item.value); }, onFocus: () => setFocused(item.value), onClick: () => { if (!item.disabled)
69
+ select(item.value); }, onKeyDown: event => {
70
+ if (event.altKey || event.ctrlKey || event.metaKey || event.nativeEvent.isComposing)
71
+ return;
72
+ const keys = orientation === 'vertical' ? ['ArrowDown', 'ArrowUp', 'Home', 'End'] : ['ArrowRight', 'ArrowLeft', 'Home', 'End'];
73
+ if (!keys.includes(event.key))
74
+ return;
75
+ event.preventDefault();
76
+ const next = items[getNextEnabledIndex(index, event.key, items.map((candidate, i) => ({ disabled: candidate.disabled || hidden.includes(i) })), getDirection(event.currentTarget), orientation)];
77
+ if (!next)
78
+ return;
79
+ setFocused(next.value);
80
+ buttons.current.get(next.value)?.focus();
81
+ if (activationMode === 'automatic')
82
+ select(next.value);
83
+ }, children: item.label }, item.value)) }), _jsxs("details", { ref: disclosure, className: "hw-tabs-overflow", hidden: !hidden.length, onKeyDown: event => { if (event.key === 'Escape' && event.currentTarget.open) {
84
+ event.preventDefault();
85
+ close();
86
+ event.currentTarget.querySelector('summary')?.focus();
87
+ } }, children: [_jsx("summary", { className: "hw-tab-overflow-trigger", "aria-label": overflowLabel, "data-active": hidden.some(index => items[index]?.value === active) || undefined, children: "\u2026" }), _jsx("div", { className: "hw-tab-overflow-menu", children: hidden.map(index => { const item = items[index]; return item && _jsx("button", { type: "button", className: "hw-tab-overflow-item", "data-hw-overflow-value": item.value, disabled: item.disabled, "aria-pressed": item.value === active, onClick: () => { if (item.disabled)
88
+ return; select(item.value); close(); disclosure.current?.querySelector('summary')?.focus(); }, children: item.label }, item.value); }) })] })] }), items.map((item, index) => _jsx("section", { className: "hw-tab-panel", id: `${id}-panel-${index}`, role: "tabpanel", "aria-labelledby": `${id}-tab-${index}`, tabIndex: 0, hidden: active !== item.value, children: item.content }, item.value))] });
89
+ }
90
+ export function Dialog({ open: controlled, defaultOpen = false, onOpenChange, title, description, trigger, children, footer, closeOnBackdrop = true, closeLabel = 'Close', ref, className, onCancel, onClose, onClick, onPointerDown, onPointerCancel, ...props }) {
91
+ const id = useId();
92
+ const [open, setOpen] = useControllable(controlled, defaultOpen, onOpenChange);
93
+ const [nativeCloseVersion, setNativeCloseVersion] = useState(0);
94
+ const dialog = useRef(null);
95
+ const previousFocus = useRef(null);
96
+ const backdropStarted = useRef(false);
97
+ const close = () => { backdropStarted.current = false; setOpen(false); };
98
+ useEffect(() => {
99
+ const node = dialog.current;
100
+ if (!node)
101
+ return;
102
+ if (open && !node.open) {
103
+ backdropStarted.current = false;
104
+ previousFocus.current = node.ownerDocument.activeElement;
105
+ node.showModal();
106
+ }
107
+ else if (!open && node.open) {
108
+ backdropStarted.current = false;
109
+ node.close();
110
+ if (previousFocus.current?.isConnected)
111
+ previousFocus.current.focus();
112
+ }
113
+ }, [open, nativeCloseVersion]);
114
+ useEffect(() => {
115
+ const node = dialog.current;
116
+ return () => {
117
+ if (node?.open) {
118
+ node.close();
119
+ if (previousFocus.current?.isConnected)
120
+ previousFocus.current.focus();
121
+ }
122
+ };
123
+ }, []);
124
+ const context = { close };
125
+ return _jsxs(_Fragment, { children: [trigger && cloneElement(trigger, { onClick: event => { trigger.props.onClick?.(event); if (!event.defaultPrevented)
126
+ setOpen(true); }, 'aria-haspopup': 'dialog' }), _jsxs("dialog", { ...props, ref: node => { dialog.current = node; assignRef(ref, node); }, className: cx('hw-dialog', className), "aria-labelledby": props['aria-labelledby'] ?? `${id}-title`, "aria-describedby": props['aria-describedby'] ?? (description ? `${id}-description` : undefined), onCancel: event => { backdropStarted.current = false; onCancel?.(event); if (!event.defaultPrevented) {
127
+ event.preventDefault();
128
+ close();
129
+ } }, onClose: event => {
130
+ backdropStarted.current = false;
131
+ onClose?.(event);
132
+ if (open && !event.currentTarget.open) {
133
+ close();
134
+ if (controlled !== undefined)
135
+ setNativeCloseVersion(version => version + 1);
136
+ }
137
+ }, onPointerDown: event => {
138
+ onPointerDown?.(event);
139
+ const bounds = event.currentTarget.getBoundingClientRect();
140
+ backdropStarted.current = !event.defaultPrevented && event.button === 0 && event.target === event.currentTarget &&
141
+ (event.clientX < bounds.left || event.clientX > bounds.right || event.clientY < bounds.top || event.clientY > bounds.bottom);
142
+ }, onPointerCancel: event => { backdropStarted.current = false; onPointerCancel?.(event); }, onClick: event => {
143
+ const started = backdropStarted.current;
144
+ backdropStarted.current = false;
145
+ onClick?.(event);
146
+ if (event.defaultPrevented || !closeOnBackdrop || !started || event.target !== event.currentTarget)
147
+ return;
148
+ const bounds = event.currentTarget.getBoundingClientRect();
149
+ if (event.clientX < bounds.left || event.clientX > bounds.right || event.clientY < bounds.top || event.clientY > bounds.bottom)
150
+ close();
151
+ }, children: [_jsxs("header", { className: "hw-dialog-header", children: [_jsx("h2", { id: `${id}-title`, children: title }), _jsx(Button, { variant: "ghost", "aria-label": closeLabel, onClick: close, children: "\u00D7" })] }), _jsxs("div", { className: "hw-dialog-body", children: [description && _jsx("p", { id: `${id}-description`, children: description }), typeof children === 'function' ? children(context) : children] }), footer && _jsx("footer", { className: "hw-dialog-footer", children: typeof footer === 'function' ? footer(context) : footer })] })] });
152
+ }
153
+ function ComboboxControl({ options, value, defaultValue = '', onValueChange, onInputChange, open: controlledOpen, defaultOpen = false, onOpenChange, emptyMessage = 'No options found', getResultsMessage = count => `${count} results available`, requiredMessage = 'Choose an option from the list.', className, ref, onKeyDown, onFocus, onBlur, onClick, name, form, disabled, ...props }) {
154
+ const field = useFieldControl(props);
155
+ const [state, setState] = useState(() => ({ ...createComboboxState(options, value ?? defaultValue), open: controlledOpen ?? defaultOpen }));
156
+ const input = useRef(null);
157
+ const presenceRef = useFieldValueRef((node) => { input.current = node; assignRef(ref, node); });
158
+ const [ancestorDisabled, setAncestorDisabled] = useState(false);
159
+ const blocked = () => disabled || props.readOnly || !!(input.current && isDisabled(input.current));
160
+ const requestedValue = value ?? state.value;
161
+ const selected = options.some(option => option.value === requestedValue && !option.disabled) ? requestedValue : '';
162
+ const open = !disabled && !ancestorDisabled && !props.readOnly && (controlledOpen ?? state.open);
163
+ const query = value !== undefined && state.value && state.value !== selected ? options.find(option => option.value === selected)?.label ?? '' : state.query;
164
+ const effectiveState = { ...state, value: selected, query, open };
165
+ const results = getComboboxOptions(effectiveState, options);
166
+ const active = results[state.activeIndex];
167
+ const listId = `${field.id}-listbox`;
168
+ const statusId = `${field.id}-status`;
169
+ const lastEmittedValue = useRef(undefined);
170
+ const latestOptions = useRef(options);
171
+ const previousOptions = useRef(options);
172
+ latestOptions.current = options;
173
+ useEffect(() => {
174
+ const element = input.current;
175
+ if (!element)
176
+ return;
177
+ const refresh = () => setAncestorDisabled(isDisabled(element));
178
+ const Observer = element.ownerDocument.defaultView?.MutationObserver;
179
+ const observer = Observer ? new Observer(refresh) : undefined;
180
+ for (let ancestor = element.parentElement; ancestor; ancestor = ancestor.parentElement) {
181
+ if (ancestor.localName === 'fieldset')
182
+ observer?.observe(ancestor, { attributes: true, attributeFilter: ['disabled'] });
183
+ }
184
+ refresh();
185
+ return () => observer?.disconnect();
186
+ }, []);
187
+ useEffect(() => {
188
+ if (disabled || ancestorDisabled || props.readOnly)
189
+ setState(current => ({ ...current, open: false, activeIndex: -1 }));
190
+ }, [disabled, ancestorDisabled, props.readOnly]);
191
+ useEffect(() => {
192
+ if (value === undefined)
193
+ return;
194
+ if (value === lastEmittedValue.current) {
195
+ lastEmittedValue.current = undefined;
196
+ setState(current => ({ ...current, value }));
197
+ }
198
+ else {
199
+ setState(current => ({ ...createComboboxState(latestOptions.current, value), open: current.open }));
200
+ }
201
+ }, [value]);
202
+ useEffect(() => {
203
+ const previousLabel = previousOptions.current.find(option => option.value === selected)?.label;
204
+ const nextLabel = options.find(option => option.value === selected)?.label;
205
+ previousOptions.current = options;
206
+ if (!selected || nextLabel === undefined || previousLabel === nextLabel)
207
+ return;
208
+ setState(current => current.value === selected && current.query === previousLabel
209
+ ? { ...current, query: nextLabel, activeIndex: -1 } : current);
210
+ }, [options, selected]);
211
+ useEffect(() => {
212
+ input.current?.setCustomValidity(field.required && !selected ? requiredMessage : '');
213
+ }, [field.required, selected, requiredMessage]);
214
+ useEffect(() => {
215
+ const formElement = input.current?.form;
216
+ let mounted = true;
217
+ const reset = (event) => {
218
+ queueMicrotask(() => {
219
+ if (mounted && !event.defaultPrevented && value === undefined)
220
+ setState(createComboboxState(latestOptions.current, defaultValue));
221
+ });
222
+ };
223
+ formElement?.addEventListener('reset', reset);
224
+ return () => { mounted = false; formElement?.removeEventListener('reset', reset); };
225
+ }, [value, defaultValue, form]);
226
+ useEffect(() => {
227
+ if (open && state.activeIndex >= 0)
228
+ input.current?.ownerDocument.getElementById(`${field.id}-option-${state.activeIndex}`)?.scrollIntoView?.({ block: 'nearest' });
229
+ }, [open, state.activeIndex, field.id]);
230
+ function dispatch(event) {
231
+ if (blocked() && event.type !== 'close' && event.type !== 'reset')
232
+ return;
233
+ const next = transitionCombobox(effectiveState, event, options);
234
+ setState(next);
235
+ if (next.value !== selected) {
236
+ lastEmittedValue.current = next.value;
237
+ onValueChange?.(next.value, options.find(option => option.value === next.value));
238
+ }
239
+ if (next.open !== open)
240
+ onOpenChange?.(next.open);
241
+ }
242
+ return _jsxs("div", { className: "hw-combobox", onBlur: event => { if (!event.currentTarget.contains(event.relatedTarget))
243
+ dispatch({ type: 'close' }); }, children: [_jsx("input", { ...props, ...field, ref: presenceRef, form: form, disabled: disabled, className: cx('hw-input', className), type: "text", role: "combobox", autoComplete: "off", "aria-autocomplete": "list", "aria-readonly": props.readOnly || undefined, "aria-expanded": open, "aria-controls": listId, "aria-activedescendant": open && active && !active.disabled ? `${field.id}-option-${state.activeIndex}` : undefined, value: query, onChange: event => { if (blocked()) {
244
+ event.currentTarget.value = query;
245
+ return;
246
+ } dispatch({ type: 'input', query: event.currentTarget.value }); onInputChange?.(event.currentTarget.value); }, onFocus: event => { onFocus?.(event); if (!event.defaultPrevented)
247
+ dispatch({ type: 'open' }); }, onBlur: onBlur, onClick: event => { onClick?.(event); if (!event.defaultPrevented && !open)
248
+ dispatch({ type: 'open' }); }, onKeyDown: event => {
249
+ onKeyDown?.(event);
250
+ if (event.defaultPrevented || event.nativeEvent.isComposing || blocked())
251
+ return;
252
+ if (['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) {
253
+ if (!open && (event.key === 'Home' || event.key === 'End'))
254
+ return;
255
+ event.preventDefault();
256
+ dispatch({ type: 'navigate', key: event.key });
257
+ }
258
+ else if (event.key === 'Enter' && open && active && !active.disabled) {
259
+ event.preventDefault();
260
+ dispatch({ type: 'select', value: active.value });
261
+ }
262
+ else if (event.key === 'Escape' && open) {
263
+ event.preventDefault();
264
+ event.stopPropagation();
265
+ dispatch({ type: 'close' });
266
+ }
267
+ else if (event.key === 'Tab')
268
+ dispatch({ type: 'close' });
269
+ } }), _jsx("input", { type: "hidden", name: name, form: form, value: selected, disabled: disabled }), _jsxs("ul", { className: "hw-listbox", id: listId, role: "listbox", "aria-label": props['aria-label'], "aria-labelledby": field['aria-labelledby'], hidden: !open, children: [results.map((option, index) => _jsx("li", { id: `${field.id}-option-${index}`, className: "hw-option", role: "option", "aria-selected": selected === option.value, "aria-disabled": option.disabled || undefined, "data-active": state.activeIndex === index ? 'true' : undefined, "data-state": state.activeIndex === index ? 'active' : undefined, onPointerDown: event => event.preventDefault(), onClick: () => { if (!option.disabled) {
270
+ dispatch({ type: 'select', value: option.value });
271
+ input.current?.focus();
272
+ } }, children: option.label }, option.value)), !results.length && _jsx("li", { role: "presentation", className: "hw-hint", children: emptyMessage })] }), _jsx("span", { id: statusId, className: "hw-sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: open ? getResultsMessage(results.length) : '' })] });
273
+ }
274
+ export function Combobox({ label, hint, error, ...props }) {
275
+ const id = useId();
276
+ if (label == null && hint == null && error == null)
277
+ return _jsx(ComboboxControl, { ...props });
278
+ return _jsx(Field, { label: label, hint: hint, error: error, required: props.required, controlId: props.id ?? id, children: _jsx(ComboboxControl, { ...props }) });
279
+ }
280
+ export function Tooltip({ children, content, placement = 'top', align = 'start', className, ref, onMouseEnter, onMouseLeave, onFocus, onBlur, onKeyDown, ...props }) {
281
+ const id = useId();
282
+ const root = useRef(null);
283
+ const [open, setOpen] = useState(false);
284
+ const hovered = useRef(false);
285
+ const focused = useRef(false);
286
+ const hideTimer = useRef(undefined);
287
+ const show = () => { clearTimeout(hideTimer.current); setOpen(true); };
288
+ const hideAfterPointerTravel = () => {
289
+ clearTimeout(hideTimer.current);
290
+ if (!hovered.current && !focused.current)
291
+ hideTimer.current = setTimeout(() => setOpen(false), 120);
292
+ };
293
+ useEffect(() => () => clearTimeout(hideTimer.current), []);
294
+ useEffect(() => {
295
+ const doc = root.current?.ownerDocument;
296
+ if (!open || !doc)
297
+ return;
298
+ const escape = (event) => {
299
+ if (event.key !== 'Escape' || event.defaultPrevented)
300
+ return;
301
+ event.preventDefault();
302
+ event.stopPropagation();
303
+ clearTimeout(hideTimer.current);
304
+ setOpen(false);
305
+ };
306
+ doc.addEventListener('keydown', escape);
307
+ return () => doc.removeEventListener('keydown', escape);
308
+ }, [open]);
309
+ return _jsxs("span", { ...props, "data-hw": "tooltip", className: className, ref: node => { root.current = node; assignRef(ref, node); }, onMouseEnter: event => { onMouseEnter?.(event); hovered.current = true; if (!event.defaultPrevented)
310
+ show(); }, onMouseLeave: event => { onMouseLeave?.(event); hovered.current = false; hideAfterPointerTravel(); }, onFocus: event => { onFocus?.(event); focused.current = true; if (!event.defaultPrevented)
311
+ show(); }, onBlur: event => { onBlur?.(event); if (!event.currentTarget.contains(event.relatedTarget)) {
312
+ focused.current = false;
313
+ hideAfterPointerTravel();
314
+ } }, onKeyDown: event => { onKeyDown?.(event); if (event.key === 'Escape' && open && !event.defaultPrevented) {
315
+ event.preventDefault();
316
+ event.stopPropagation();
317
+ clearTimeout(hideTimer.current);
318
+ setOpen(false);
319
+ } }, children: [cloneElement(children, { 'aria-describedby': [children.props['aria-describedby'], id].filter(Boolean).join(' ') }), _jsx("span", { className: "hw-tooltip", role: "tooltip", id: id, "data-placement": placement, "data-align": align, hidden: !open, children: content })] });
320
+ }
321
+ //# sourceMappingURL=interactive.js.map