@hawiyah/vue 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 (81) hide show
  1. package/LICENSE +73 -0
  2. package/NOTICE +19 -0
  3. package/PORTAL.md +70 -0
  4. package/README.md +388 -2
  5. package/dist/calendar.d.ts +183 -0
  6. package/dist/calendar.d.ts.map +1 -0
  7. package/dist/calendar.js +448 -0
  8. package/dist/calendar.js.map +1 -0
  9. package/dist/content-navigation.d.ts +61 -0
  10. package/dist/content-navigation.d.ts.map +1 -0
  11. package/dist/content-navigation.js +31 -0
  12. package/dist/content-navigation.js.map +1 -0
  13. package/dist/content.d.ts +392 -0
  14. package/dist/content.d.ts.map +1 -0
  15. package/dist/content.js +305 -0
  16. package/dist/content.js.map +1 -0
  17. package/dist/controls.d.ts +657 -0
  18. package/dist/controls.d.ts.map +1 -0
  19. package/dist/controls.js +679 -0
  20. package/dist/controls.js.map +1 -0
  21. package/dist/display.d.ts +387 -0
  22. package/dist/display.d.ts.map +1 -0
  23. package/dist/display.js +293 -0
  24. package/dist/display.js.map +1 -0
  25. package/dist/expansion-interactive.d.ts +476 -0
  26. package/dist/expansion-interactive.d.ts.map +1 -0
  27. package/dist/expansion-interactive.js +613 -0
  28. package/dist/expansion-interactive.js.map +1 -0
  29. package/dist/file-upload-list.d.ts +54 -0
  30. package/dist/file-upload-list.d.ts.map +1 -0
  31. package/dist/file-upload-list.js +21 -0
  32. package/dist/file-upload-list.js.map +1 -0
  33. package/dist/forms.d.ts +388 -0
  34. package/dist/forms.d.ts.map +1 -0
  35. package/dist/forms.js +156 -0
  36. package/dist/forms.js.map +1 -0
  37. package/dist/index.d.ts +26 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +16 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/interactive.d.ts +240 -0
  42. package/dist/interactive.d.ts.map +1 -0
  43. package/dist/interactive.js +341 -0
  44. package/dist/interactive.js.map +1 -0
  45. package/dist/layout.d.ts +755 -0
  46. package/dist/layout.d.ts.map +1 -0
  47. package/dist/layout.js +101 -0
  48. package/dist/layout.js.map +1 -0
  49. package/dist/mobile-top-bar.d.ts +47 -0
  50. package/dist/mobile-top-bar.d.ts.map +1 -0
  51. package/dist/mobile-top-bar.js +18 -0
  52. package/dist/mobile-top-bar.js.map +1 -0
  53. package/dist/native-attributes.d.ts +38 -0
  54. package/dist/native-attributes.d.ts.map +1 -0
  55. package/dist/native-attributes.js +27 -0
  56. package/dist/native-attributes.js.map +1 -0
  57. package/dist/navigation-drawer.d.ts +87 -0
  58. package/dist/navigation-drawer.d.ts.map +1 -0
  59. package/dist/navigation-drawer.js +56 -0
  60. package/dist/navigation-drawer.js.map +1 -0
  61. package/dist/patterns.d.ts +455 -0
  62. package/dist/patterns.d.ts.map +1 -0
  63. package/dist/patterns.js +355 -0
  64. package/dist/patterns.js.map +1 -0
  65. package/dist/portal.d.ts +174 -0
  66. package/dist/portal.d.ts.map +1 -0
  67. package/dist/portal.js +36 -0
  68. package/dist/portal.js.map +1 -0
  69. package/dist/search-field.d.ts +87 -0
  70. package/dist/search-field.d.ts.map +1 -0
  71. package/dist/search-field.js +123 -0
  72. package/dist/search-field.js.map +1 -0
  73. package/dist/shared.d.ts +46 -0
  74. package/dist/shared.d.ts.map +1 -0
  75. package/dist/shared.js +40 -0
  76. package/dist/shared.js.map +1 -0
  77. package/dist/site-verification.d.ts +63 -0
  78. package/dist/site-verification.d.ts.map +1 -0
  79. package/dist/site-verification.js +34 -0
  80. package/dist/site-verification.js.map +1 -0
  81. package/package.json +53 -3
@@ -0,0 +1,26 @@
1
+ export { Button, Checkbox, Radio, Switch, TextField, Textarea, Select, FileUpload, IconButton, PasswordField, SearchField, NumberField, Slider, PinInput, TagInput, MultiSelect, FileDropzone, Combobox, DatePicker, NavigationDrawer } from './native-attributes.js';
2
+ export { Field } from './forms.js';
3
+ export { Tabs, Dialog, Tooltip } from './interactive.js';
4
+ export type { TabItem } from './interactive.js';
5
+ export { Accordion, Card, Badge, Alert, Table, Pagination, Breadcrumb, Progress, Skeleton, Spinner, Avatar, Divider, Stepper, EmptyState } from './display.js';
6
+ export type { AccordionItem, TableColumn, BreadcrumbItem, StepItem, Tone } from './display.js';
7
+ export type { FieldDecoration, RenderContent } from './shared.js';
8
+ export { InputGroup, RangeSlider, Rating, RatingDisplay } from './controls.js';
9
+ export { Calendar } from './calendar.js';
10
+ export { ToggleGroup, Popover, Menu, Drawer, ToastRegion, Collapsible, TreeView, Carousel } from './expansion-interactive.js';
11
+ export type { MenuItem, ToastMessage, TreeNode, CarouselSlide } from './expansion-interactive.js';
12
+ export { Link, ButtonGroup, Chip, DescriptionList, Stat, Timeline, Meter, AvatarGroup, DataTable } from './content.js';
13
+ export type { DescriptionItem, TimelineItem, AvatarItem, DataColumn } from './content.js';
14
+ export { ServiceCard, ServiceDetails, PortalHeader, PortalFooter, Feedback, CookieConsent, NotificationList } from './patterns.js';
15
+ export type { PortalLink, PortalFooterLink, PortalFooterGroup, FeedbackValue, NotificationItem } from './patterns.js';
16
+ export { Container, Stack, Inline, Grid, Columns, Sidebar, Center, Spacer, AspectRatio, Section, AppShell, PageHeader } from './layout.js';
17
+ export { SecondaryNav, PortalLayout } from './portal.js';
18
+ export type { LayoutProps, Responsive } from '@hawiyah/core';
19
+ export type { SecondaryNavItem } from './portal.js';
20
+ export { MobileTopBar } from './mobile-top-bar.js';
21
+ export type { NavigationDrawerItem } from './navigation-drawer.js';
22
+ export { SiteVerification } from './site-verification.js';
23
+ export { ContentNavigation } from './content-navigation.js';
24
+ export type { ContentNavigationItem } from './content-navigation.js';
25
+ export { FileUploadList } from './file-upload-list.js';
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACtQ,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACzD,YAAY,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/J,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAC/F,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAC9H,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAClG,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACvH,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1F,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACnI,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtH,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC3I,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzD,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE7D,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAEnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,YAAY,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAErE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ export { Button, Checkbox, Radio, Switch, TextField, Textarea, Select, FileUpload, IconButton, PasswordField, SearchField, NumberField, Slider, PinInput, TagInput, MultiSelect, FileDropzone, Combobox, DatePicker, NavigationDrawer } from './native-attributes.js';
2
+ export { Field } from './forms.js';
3
+ export { Tabs, Dialog, Tooltip } from './interactive.js';
4
+ export { Accordion, Card, Badge, Alert, Table, Pagination, Breadcrumb, Progress, Skeleton, Spinner, Avatar, Divider, Stepper, EmptyState } from './display.js';
5
+ export { InputGroup, RangeSlider, Rating, RatingDisplay } from './controls.js';
6
+ export { Calendar } from './calendar.js';
7
+ export { ToggleGroup, Popover, Menu, Drawer, ToastRegion, Collapsible, TreeView, Carousel } from './expansion-interactive.js';
8
+ export { Link, ButtonGroup, Chip, DescriptionList, Stat, Timeline, Meter, AvatarGroup, DataTable } from './content.js';
9
+ export { ServiceCard, ServiceDetails, PortalHeader, PortalFooter, Feedback, CookieConsent, NotificationList } from './patterns.js';
10
+ export { Container, Stack, Inline, Grid, Columns, Sidebar, Center, Spacer, AspectRatio, Section, AppShell, PageHeader } from './layout.js';
11
+ export { SecondaryNav, PortalLayout } from './portal.js';
12
+ export { MobileTopBar } from './mobile-top-bar.js';
13
+ export { SiteVerification } from './site-verification.js';
14
+ export { ContentNavigation } from './content-navigation.js';
15
+ export { FileUploadList } from './file-upload-list.js';
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACtQ,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/J,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAE9H,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEvH,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEnI,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC3I,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAInD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,240 @@
1
+ import type { PropType, VNodeChild } from 'vue';
2
+ import type { Option, Orientation } from '@hawiyah/core';
3
+ export interface TabItem {
4
+ value: string;
5
+ label: string;
6
+ content?: VNodeChild;
7
+ disabled?: boolean;
8
+ }
9
+ export declare const Tabs: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
10
+ items: {
11
+ type: PropType<readonly TabItem[]>;
12
+ required: true;
13
+ };
14
+ label: {
15
+ type: StringConstructor;
16
+ required: true;
17
+ };
18
+ modelValue: StringConstructor;
19
+ defaultValue: StringConstructor;
20
+ orientation: {
21
+ type: PropType<Orientation>;
22
+ default: string;
23
+ };
24
+ activationMode: {
25
+ type: PropType<"automatic" | "manual">;
26
+ default: string;
27
+ };
28
+ overflow: {
29
+ type: PropType<"menu" | "scroll">;
30
+ default: string;
31
+ };
32
+ overflowLabel: {
33
+ type: StringConstructor;
34
+ default: string;
35
+ };
36
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
37
+ [key: string]: any;
38
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
39
+ 'update:modelValue': (_value: string) => true;
40
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
41
+ items: {
42
+ type: PropType<readonly TabItem[]>;
43
+ required: true;
44
+ };
45
+ label: {
46
+ type: StringConstructor;
47
+ required: true;
48
+ };
49
+ modelValue: StringConstructor;
50
+ defaultValue: StringConstructor;
51
+ orientation: {
52
+ type: PropType<Orientation>;
53
+ default: string;
54
+ };
55
+ activationMode: {
56
+ type: PropType<"automatic" | "manual">;
57
+ default: string;
58
+ };
59
+ overflow: {
60
+ type: PropType<"menu" | "scroll">;
61
+ default: string;
62
+ };
63
+ overflowLabel: {
64
+ type: StringConstructor;
65
+ default: string;
66
+ };
67
+ }>> & Readonly<{
68
+ "onUpdate:modelValue"?: ((_value: string) => any) | undefined;
69
+ }>, {
70
+ orientation: Orientation;
71
+ activationMode: "automatic" | "manual";
72
+ overflow: "scroll" | "menu";
73
+ overflowLabel: string;
74
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
75
+ export declare const Dialog: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
76
+ open: {
77
+ type: BooleanConstructor;
78
+ default: undefined;
79
+ };
80
+ defaultOpen: BooleanConstructor;
81
+ title: {
82
+ type: StringConstructor;
83
+ required: true;
84
+ };
85
+ description: StringConstructor;
86
+ closeLabel: {
87
+ type: StringConstructor;
88
+ default: string;
89
+ };
90
+ closeOnBackdrop: {
91
+ type: BooleanConstructor;
92
+ default: boolean;
93
+ };
94
+ }>, () => (import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
95
+ [key: string]: any;
96
+ }> | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
97
+ [key: string]: any;
98
+ }>[] | undefined)[], {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
99
+ 'update:open': (_open: boolean) => true;
100
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
101
+ open: {
102
+ type: BooleanConstructor;
103
+ default: undefined;
104
+ };
105
+ defaultOpen: BooleanConstructor;
106
+ title: {
107
+ type: StringConstructor;
108
+ required: true;
109
+ };
110
+ description: StringConstructor;
111
+ closeLabel: {
112
+ type: StringConstructor;
113
+ default: string;
114
+ };
115
+ closeOnBackdrop: {
116
+ type: BooleanConstructor;
117
+ default: boolean;
118
+ };
119
+ }>> & Readonly<{
120
+ "onUpdate:open"?: ((_open: boolean) => any) | undefined;
121
+ }>, {
122
+ defaultOpen: boolean;
123
+ open: boolean;
124
+ closeLabel: string;
125
+ closeOnBackdrop: boolean;
126
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
127
+ export declare const Combobox: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
128
+ options: {
129
+ type: PropType<readonly Option[]>;
130
+ required: true;
131
+ };
132
+ modelValue: StringConstructor;
133
+ defaultValue: {
134
+ type: StringConstructor;
135
+ default: string;
136
+ };
137
+ disabled: BooleanConstructor;
138
+ readonly: BooleanConstructor;
139
+ emptyMessage: {
140
+ type: StringConstructor;
141
+ default: string;
142
+ };
143
+ resultsMessage: {
144
+ type: PropType<(count: number) => string>;
145
+ default: (count: number) => string;
146
+ };
147
+ requiredMessage: {
148
+ type: StringConstructor;
149
+ default: string;
150
+ };
151
+ label: StringConstructor;
152
+ hint: StringConstructor;
153
+ error: StringConstructor;
154
+ required: {
155
+ readonly type: BooleanConstructor;
156
+ readonly default: undefined;
157
+ };
158
+ }>, () => string | number | boolean | void | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
159
+ [key: string]: any;
160
+ }> | import("vue").VNodeArrayChildren | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
161
+ 'update:modelValue': (_value: string) => true;
162
+ 'update:open': (_open: boolean) => true;
163
+ 'update:query': (_query: string) => true;
164
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
165
+ options: {
166
+ type: PropType<readonly Option[]>;
167
+ required: true;
168
+ };
169
+ modelValue: StringConstructor;
170
+ defaultValue: {
171
+ type: StringConstructor;
172
+ default: string;
173
+ };
174
+ disabled: BooleanConstructor;
175
+ readonly: BooleanConstructor;
176
+ emptyMessage: {
177
+ type: StringConstructor;
178
+ default: string;
179
+ };
180
+ resultsMessage: {
181
+ type: PropType<(count: number) => string>;
182
+ default: (count: number) => string;
183
+ };
184
+ requiredMessage: {
185
+ type: StringConstructor;
186
+ default: string;
187
+ };
188
+ label: StringConstructor;
189
+ hint: StringConstructor;
190
+ error: StringConstructor;
191
+ required: {
192
+ readonly type: BooleanConstructor;
193
+ readonly default: undefined;
194
+ };
195
+ }>> & Readonly<{
196
+ "onUpdate:modelValue"?: ((_value: string) => any) | undefined;
197
+ "onUpdate:open"?: ((_open: boolean) => any) | undefined;
198
+ "onUpdate:query"?: ((_query: string) => any) | undefined;
199
+ }>, {
200
+ disabled: boolean;
201
+ required: boolean;
202
+ defaultValue: string;
203
+ readonly: boolean;
204
+ emptyMessage: string;
205
+ resultsMessage: (count: number) => string;
206
+ requiredMessage: string;
207
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
208
+ export declare const Tooltip: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
209
+ content: {
210
+ type: StringConstructor;
211
+ required: true;
212
+ };
213
+ placement: {
214
+ type: PropType<"top" | "bottom" | "start" | "end">;
215
+ default: string;
216
+ };
217
+ align: {
218
+ type: PropType<"start" | "center" | "end">;
219
+ default: string;
220
+ };
221
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
222
+ [key: string]: any;
223
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
224
+ content: {
225
+ type: StringConstructor;
226
+ required: true;
227
+ };
228
+ placement: {
229
+ type: PropType<"top" | "bottom" | "start" | "end">;
230
+ default: string;
231
+ };
232
+ align: {
233
+ type: PropType<"start" | "center" | "end">;
234
+ default: string;
235
+ };
236
+ }>> & Readonly<{}>, {
237
+ placement: "top" | "bottom" | "start" | "end";
238
+ align: "start" | "end" | "center";
239
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
240
+ //# sourceMappingURL=interactive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../src/interactive.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAEhD,OAAO,KAAK,EAAiB,MAAM,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAIxE,MAAM,WAAW,OAAO;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE;AACnG,eAAO,MAAM,IAAI;;cAEkB,QAAQ,CAAC,SAAS,OAAO,EAAE,CAAC;;;;;;;;;;cAC5B,QAAQ,CAAC,WAAW,CAAC;;;;cAA6D,QAAQ,CAAC,WAAW,GAAG,QAAQ,CAAC;;;;cACtH,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC;;;;;;;;;;kCACjB,MAAM;;;cAHZ,QAAQ,CAAC,SAAS,OAAO,EAAE,CAAC;;;;;;;;;;cAC5B,QAAQ,CAAC,WAAW,CAAC;;;;cAA6D,QAAQ,CAAC,WAAW,GAAG,QAAQ,CAAC;;;;cACtH,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC;;;;;;;;;;;;;;4EA8DxD,CAAC;AAEH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;2BAGe,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;4EA0CvC,CAAC;AAEH,eAAO,MAAM,QAAQ;;cAEoC,QAAQ,CAAC,SAAS,MAAM,EAAE,CAAC;;;;;;;;;;;;;;;cACiB,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;yBAAmB,MAAM;;;;;;;;;;;;;;;;kCACxH,MAAM;2BAAkC,OAAO;6BAAoC,MAAM;;;cAFzE,QAAQ,CAAC,SAAS,MAAM,EAAE,CAAC;;;;;;;;;;;;;;;cACiB,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;yBAAmB,MAAM;;;;;;;;;;;;;;;;;;;;;;;4BAA3C,MAAM,KAAK,MAAM;;4EAqFrI,CAAC;AAEH,eAAO,MAAM,OAAO;;;;;;cACsG,QAAQ,CAAC,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;;;;cAA2C,QAAQ,CAAC,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;;;;;;;;;;;cAA3H,QAAQ,CAAC,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;;;;cAA2C,QAAQ,CAAC,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;;;;;;4EA6CnP,CAAC"}
@@ -0,0 +1,341 @@
1
+ import { cloneVNode, computed, defineComponent, h, isVNode, mergeProps, nextTick, onBeforeUnmount, onMounted, ref, useId, watch, watchEffect } from 'vue';
2
+ import { createComboboxState, getComboboxOptions, getDirection, getNextEnabledIndex, isDisabled, observeTabOverflow, restoreFocus, transitionCombobox } from '@hawiyah/core';
3
+ import { Button, decorate } from './forms.js';
4
+ import { decorationProps, joinIds, useField, useFormReset, useValue } from './shared.js';
5
+ export const Tabs = defineComponent({
6
+ name: 'HwTabs', inheritAttrs: false,
7
+ props: { items: { type: Array, required: true }, label: { type: String, required: true }, modelValue: String, defaultValue: String,
8
+ orientation: { type: String, default: 'horizontal' }, activationMode: { type: String, default: 'automatic' },
9
+ overflow: { type: String, default: 'menu' }, overflowLabel: { type: String, default: 'المزيد من التبويبات' } },
10
+ emits: { 'update:modelValue': (_value) => true },
11
+ setup(props, { attrs, slots, emit }) {
12
+ const id = `hw-${useId()}`;
13
+ const model = useValue(() => props.modelValue, props.defaultValue ?? props.items.find(item => !item.disabled)?.value ?? '', value => emit('update:modelValue', value));
14
+ const focused = ref();
15
+ const active = computed(() => props.items.find(item => item.value === model.value.value && !item.disabled)?.value ?? props.items.find(item => !item.disabled)?.value);
16
+ const buttons = new Map();
17
+ const bar = ref();
18
+ const disclosure = ref();
19
+ const hidden = ref([]);
20
+ let observer;
21
+ let alive = true;
22
+ const visible = computed(() => props.items.filter((item, index) => !hidden.value.includes(index) && !item.disabled));
23
+ const focusValue = computed(() => visible.value.find(item => item.value === focused.value)?.value ?? visible.value.find(item => item.value === active.value)?.value ?? visible.value[0]?.value);
24
+ const close = () => { if (disclosure.value)
25
+ disclosure.value.open = false; };
26
+ const outside = (event) => { if (!bar.value?.contains(event.target))
27
+ close(); };
28
+ onMounted(() => {
29
+ const node = bar.value;
30
+ observer = observeTabOverflow({ container: node, tabs: () => props.items.map(item => buttons.get(item.value)).filter(Boolean), enabled: () => props.overflow === 'menu' && props.orientation === 'horizontal', onChange: next => {
31
+ const focusedElement = node.ownerDocument.activeElement;
32
+ const menuValue = focusedElement?.dataset.hwOverflowValue;
33
+ const itemIndex = props.items.findIndex(item => item.value === menuValue);
34
+ const toMenu = menuValue !== undefined && itemIndex >= 0 && next.includes(itemIndex);
35
+ const toOriginal = menuValue !== undefined && itemIndex >= 0 && !toMenu;
36
+ const toSummary = next.some(index => buttons.get(props.items[index]?.value ?? '') === focusedElement) || (!toMenu && !toOriginal && !!next.length && !!disclosure.value?.contains(focusedElement));
37
+ const toTab = !next.length && disclosure.value?.contains(focusedElement);
38
+ if (!toMenu)
39
+ close();
40
+ hidden.value = next;
41
+ void nextTick(() => { if (!alive)
42
+ return; if (toMenu)
43
+ [...(disclosure.value?.querySelectorAll('[data-hw-overflow-value]') ?? [])].find(button => button.dataset.hwOverflowValue === menuValue)?.focus();
44
+ else if (toOriginal)
45
+ buttons.get(menuValue)?.focus();
46
+ else if (toSummary)
47
+ disclosure.value?.querySelector('summary')?.focus();
48
+ else if (toTab && focusValue.value)
49
+ buttons.get(focusValue.value)?.focus(); });
50
+ } });
51
+ node.ownerDocument.addEventListener('click', outside);
52
+ });
53
+ watch(() => [props.items, props.overflow, props.orientation], () => { observer?.refresh(); const focusedElement = bar.value?.ownerDocument.activeElement; if (focusedElement?.getAttribute('data-hw-overflow-hidden') === 'true' && bar.value?.contains(focusedElement))
54
+ disclosure.value?.querySelector('summary')?.focus(); }, { deep: true, flush: 'post' });
55
+ watch(active, close);
56
+ onBeforeUnmount(() => { alive = false; observer?.destroy(); bar.value?.ownerDocument.removeEventListener('click', outside); });
57
+ return () => h('div', mergeProps(attrs, { class: 'hw-tabs' }), [
58
+ h('div', { class: 'hw-tab-bar', ref: bar, 'data-hw-overflow': props.overflow, 'data-orientation': props.orientation }, [
59
+ h('div', { class: 'hw-tab-list', 'data-hw-overflow': props.overflow, role: 'tablist', 'aria-label': props.label, 'aria-orientation': props.orientation, 'aria-hidden': hidden.value.length === props.items.length || undefined }, props.items.map((item, index) => h('button', {
60
+ key: item.value, ref: (element) => { if (element)
61
+ buttons.set(item.value, element);
62
+ else
63
+ buttons.delete(item.value); },
64
+ class: 'hw-tab', type: 'button', role: 'tab', id: `${id}-tab-${index}`, 'aria-controls': `${id}-panel-${index}`, 'aria-selected': active.value === item.value,
65
+ 'data-hw-overflow-hidden': hidden.value.includes(index) ? 'true' : undefined, 'aria-hidden': hidden.value.includes(index) || undefined,
66
+ tabindex: !hidden.value.includes(index) && focusValue.value === item.value ? 0 : -1, disabled: item.disabled,
67
+ onFocus: () => { focused.value = item.value; }, onClick: () => { if (!item.disabled)
68
+ model.set(item.value); },
69
+ onKeydown: (event) => {
70
+ if (event.altKey || event.ctrlKey || event.metaKey || event.isComposing)
71
+ return;
72
+ const keys = props.orientation === 'vertical' ? ['ArrowDown', 'ArrowUp', 'Home', 'End'] : ['ArrowLeft', 'ArrowRight', 'Home', 'End'];
73
+ if (!keys.includes(event.key))
74
+ return;
75
+ event.preventDefault();
76
+ const next = props.items[getNextEnabledIndex(index, event.key, props.items.map((candidate, i) => ({ disabled: candidate.disabled || hidden.value.includes(i) })), getDirection(event.currentTarget), props.orientation)];
77
+ if (!next)
78
+ return;
79
+ focused.value = next.value;
80
+ buttons.get(next.value)?.focus();
81
+ if (props.activationMode === 'automatic')
82
+ model.set(next.value);
83
+ },
84
+ }, slots[`tab:${item.value}`]?.({ item }) ?? item.label))),
85
+ h('details', { class: 'hw-tabs-overflow', ref: disclosure, hidden: !hidden.value.length, onKeydown: (event) => { if (event.key === 'Escape' && disclosure.value?.open) {
86
+ event.preventDefault();
87
+ close();
88
+ disclosure.value.querySelector('summary')?.focus();
89
+ } } }, [
90
+ h('summary', { class: 'hw-tab-overflow-trigger', 'aria-label': props.overflowLabel, 'data-active': hidden.value.some(index => props.items[index]?.value === active.value) ? 'true' : undefined }, '…'),
91
+ h('div', { class: 'hw-tab-overflow-menu' }, hidden.value.map(index => { const item = props.items[index]; return item && h('button', { key: item.value, type: 'button', class: 'hw-tab-overflow-item', 'data-hw-overflow-value': item.value, disabled: item.disabled, 'aria-pressed': item.value === active.value, onClick: () => { if (item.disabled)
92
+ return; model.set(item.value); close(); disclosure.value?.querySelector('summary')?.focus(); } }, slots[`tab:${item.value}`]?.({ item }) ?? item.label); })),
93
+ ]),
94
+ ]),
95
+ ...props.items.map((item, index) => h('section', { key: item.value, class: 'hw-tab-panel', id: `${id}-panel-${index}`, role: 'tabpanel', 'aria-labelledby': `${id}-tab-${index}`, tabindex: 0, hidden: active.value !== item.value }, [slots[item.value]?.() ?? item.content])),
96
+ ]);
97
+ },
98
+ });
99
+ export const Dialog = defineComponent({
100
+ name: 'HwDialog', inheritAttrs: false,
101
+ props: { open: { type: Boolean, default: undefined }, defaultOpen: Boolean, title: { type: String, required: true }, description: String, closeLabel: { type: String, default: 'Close' }, closeOnBackdrop: { type: Boolean, default: true } },
102
+ emits: { 'update:open': (_open) => true },
103
+ setup(props, { attrs, slots, emit, expose }) {
104
+ const id = `hw-${useId()}`;
105
+ const element = ref();
106
+ const model = useValue(() => props.open, props.defaultOpen, value => emit('update:open', value));
107
+ let previousFocus = null;
108
+ let backdropStarted = false;
109
+ const close = () => { backdropStarted = false; model.set(false); };
110
+ const show = () => { backdropStarted = false; model.set(true); };
111
+ watchEffect(() => {
112
+ const dialog = element.value;
113
+ if (!dialog)
114
+ return;
115
+ if (model.value.value && !dialog.open) {
116
+ backdropStarted = false;
117
+ previousFocus = dialog.ownerDocument.activeElement;
118
+ dialog.showModal();
119
+ }
120
+ else if (!model.value.value && dialog.open) {
121
+ backdropStarted = false;
122
+ dialog.close();
123
+ restoreFocus(previousFocus);
124
+ }
125
+ }, { flush: 'post' });
126
+ onBeforeUnmount(() => { if (element.value?.open) {
127
+ element.value.close();
128
+ restoreFocus(previousFocus);
129
+ } });
130
+ expose({ element, show, close });
131
+ return () => [slots.trigger?.({ open: show }), h('dialog', mergeProps(attrs, {
132
+ ref: element, class: 'hw-dialog', 'aria-labelledby': attrs['aria-labelledby'] ?? `${id}-title`, 'aria-describedby': attrs['aria-describedby'] ?? (props.description ? `${id}-description` : undefined),
133
+ onCancel: (event) => { backdropStarted = false; if (!event.defaultPrevented) {
134
+ event.preventDefault();
135
+ close();
136
+ } },
137
+ onClose: () => { backdropStarted = false; if (model.value.value)
138
+ close(); restoreFocus(previousFocus); },
139
+ onPointerdown: (event) => {
140
+ const bounds = element.value.getBoundingClientRect();
141
+ backdropStarted = !event.defaultPrevented && event.button === 0 && event.target === event.currentTarget &&
142
+ (event.clientX < bounds.left || event.clientX > bounds.right || event.clientY < bounds.top || event.clientY > bounds.bottom);
143
+ },
144
+ onPointercancel: () => { backdropStarted = false; },
145
+ onClick: (event) => {
146
+ const started = backdropStarted;
147
+ backdropStarted = false;
148
+ if (event.defaultPrevented || !props.closeOnBackdrop || !started || event.target !== event.currentTarget)
149
+ return;
150
+ const bounds = element.value.getBoundingClientRect();
151
+ if (event.clientX < bounds.left || event.clientX > bounds.right || event.clientY < bounds.top || event.clientY > bounds.bottom)
152
+ close();
153
+ },
154
+ }), [
155
+ h('header', { class: 'hw-dialog-header' }, [h('h2', { id: `${id}-title` }, props.title), h(Button, { variant: 'ghost', 'aria-label': props.closeLabel, onClick: close }, () => '×')]),
156
+ h('div', { class: 'hw-dialog-body' }, [props.description && h('p', { id: `${id}-description` }, props.description), slots.default?.({ close })]),
157
+ slots.footer && h('footer', { class: 'hw-dialog-footer' }, slots.footer({ close })),
158
+ ])];
159
+ },
160
+ });
161
+ export const Combobox = defineComponent({
162
+ name: 'HwCombobox', inheritAttrs: false,
163
+ props: { ...decorationProps, options: { type: Array, required: true }, modelValue: String, defaultValue: { type: String, default: '' }, disabled: Boolean, readonly: Boolean,
164
+ emptyMessage: { type: String, default: 'No options found' }, resultsMessage: { type: Function, default: (count) => `${count} results available` }, requiredMessage: { type: String, default: 'Choose an option from the list.' } },
165
+ emits: { 'update:modelValue': (_value) => true, 'update:open': (_open) => true, 'update:query': (_query) => true },
166
+ setup(props, { attrs, emit, expose }) {
167
+ const field = useField(props, attrs);
168
+ const element = ref();
169
+ const root = ref();
170
+ const state = ref(createComboboxState(props.options, props.modelValue ?? props.defaultValue));
171
+ const ancestorDisabled = ref(false);
172
+ const blocked = () => props.disabled || props.readonly || !!(element.value && isDisabled(element.value));
173
+ let disabledObserver;
174
+ onMounted(() => {
175
+ const input = element.value;
176
+ if (!input)
177
+ return;
178
+ const refresh = () => { ancestorDisabled.value = isDisabled(input); };
179
+ const Observer = input.ownerDocument.defaultView?.MutationObserver;
180
+ if (Observer)
181
+ disabledObserver = new Observer(refresh);
182
+ for (let ancestor = input.parentElement; ancestor; ancestor = ancestor.parentElement) {
183
+ if (ancestor.localName === 'fieldset')
184
+ disabledObserver?.observe(ancestor, { attributes: true, attributeFilter: ['disabled'] });
185
+ }
186
+ refresh();
187
+ });
188
+ onBeforeUnmount(() => disabledObserver?.disconnect());
189
+ watch(() => props.disabled || props.readonly || ancestorDisabled.value, unavailable => {
190
+ if (unavailable)
191
+ state.value = { ...state.value, open: false, activeIndex: -1 };
192
+ });
193
+ const effective = computed(() => ({ ...state.value, value: props.modelValue ?? state.value.value,
194
+ query: props.modelValue !== undefined && props.modelValue !== state.value.value ? createComboboxState(props.options, props.modelValue).query : state.value.query,
195
+ open: !props.disabled && !ancestorDisabled.value && !props.readonly && state.value.open }));
196
+ const results = computed(() => getComboboxOptions(effective.value, props.options));
197
+ let lastEmitted;
198
+ function dispatch(event) {
199
+ if (blocked() && event.type !== 'close' && event.type !== 'reset')
200
+ return;
201
+ const previous = effective.value;
202
+ const next = transitionCombobox(previous, event, props.options);
203
+ state.value = next;
204
+ if (next.value !== previous.value) {
205
+ lastEmitted = next.value;
206
+ emit('update:modelValue', next.value);
207
+ }
208
+ if (next.open !== previous.open)
209
+ emit('update:open', next.open);
210
+ }
211
+ watch(() => props.modelValue, value => {
212
+ if (value === undefined)
213
+ return;
214
+ if (value === lastEmitted) {
215
+ lastEmitted = undefined;
216
+ state.value = { ...state.value, value };
217
+ }
218
+ else
219
+ state.value = { ...createComboboxState(props.options, value), open: state.value.open };
220
+ });
221
+ watch(() => props.options, options => {
222
+ if (effective.value.value)
223
+ state.value = { ...createComboboxState(options, effective.value.value), open: state.value.open };
224
+ });
225
+ watchEffect(() => element.value?.setCustomValidity(field.controlAttrs.value.required && !effective.value.value ? props.requiredMessage : ''));
226
+ useFormReset(element, () => dispatch({ type: 'reset', value: props.defaultValue }));
227
+ const outside = (event) => { if (!root.value?.contains(event.target))
228
+ dispatch({ type: 'close' }); };
229
+ onMounted(() => element.value?.ownerDocument.addEventListener('pointerdown', outside));
230
+ onBeforeUnmount(() => element.value?.ownerDocument.removeEventListener('pointerdown', outside));
231
+ expose({ element, focus: () => element.value?.focus(), close: () => dispatch({ type: 'close' }) });
232
+ return () => {
233
+ const { name, form, ...inputAttrs } = attrs;
234
+ const listId = `${field.id.value}-listbox`;
235
+ const active = results.value[state.value.activeIndex];
236
+ const open = effective.value.open;
237
+ const query = effective.value.query;
238
+ return decorate(props, field.id.value, h('div', { ref: root, class: 'hw-combobox', onFocusout: (event) => { if (!root.value?.contains(event.relatedTarget))
239
+ dispatch({ type: 'close' }); } }, [
240
+ h('input', mergeProps(inputAttrs, field.controlAttrs.value, { ref: element, form, disabled: props.disabled, readonly: props.readonly, class: 'hw-input', type: 'text', role: 'combobox', autocomplete: 'off', 'aria-autocomplete': 'list', 'aria-readonly': props.readonly || undefined, 'aria-expanded': open, 'aria-controls': listId,
241
+ 'aria-activedescendant': open && active && !active.disabled ? `${field.id.value}-option-${state.value.activeIndex}` : undefined, value: query,
242
+ onInput: (event) => { const input = event.target; if (blocked()) {
243
+ input.value = query;
244
+ return;
245
+ } const nextQuery = input.value; dispatch({ type: 'input', query: nextQuery }); emit('update:query', nextQuery); },
246
+ onFocus: (event) => { if (!event.defaultPrevented)
247
+ dispatch({ type: 'open' }); },
248
+ onClick: (event) => { if (!event.defaultPrevented && !open)
249
+ dispatch({ type: 'open' }); },
250
+ onKeydown: (event) => {
251
+ if (event.defaultPrevented || event.isComposing || blocked())
252
+ return;
253
+ if (['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) {
254
+ if (!open && (event.key === 'Home' || event.key === 'End'))
255
+ return;
256
+ event.preventDefault();
257
+ dispatch({ type: 'navigate', key: event.key });
258
+ }
259
+ else if (event.key === 'Enter' && open && active && !active.disabled) {
260
+ event.preventDefault();
261
+ dispatch({ type: 'select', value: active.value });
262
+ }
263
+ else if (event.key === 'Escape' && open) {
264
+ event.preventDefault();
265
+ event.stopPropagation();
266
+ dispatch({ type: 'close' });
267
+ }
268
+ else if (event.key === 'Tab')
269
+ dispatch({ type: 'close' });
270
+ },
271
+ })),
272
+ h('input', { type: 'hidden', name, form, disabled: props.disabled, value: effective.value.value }),
273
+ h('ul', { id: listId, role: 'listbox', class: 'hw-listbox', 'aria-label': attrs['aria-label'], 'aria-labelledby': field.controlAttrs.value['aria-labelledby'], hidden: !open }, [
274
+ ...results.value.map((option, index) => h('li', { key: option.value, id: `${field.id.value}-option-${index}`, role: 'option', class: 'hw-option', 'aria-selected': effective.value.value === option.value, 'aria-disabled': option.disabled || undefined,
275
+ 'data-active': state.value.activeIndex === index ? 'true' : undefined, 'data-state': state.value.activeIndex === index ? 'active' : undefined,
276
+ onPointerdown: (event) => event.preventDefault(), onClick: () => { if (!option.disabled) {
277
+ dispatch({ type: 'select', value: option.value });
278
+ element.value?.focus();
279
+ } }, }, option.label)), !results.value.length && h('li', { role: 'presentation', class: 'hw-hint' }, props.emptyMessage),
280
+ ]),
281
+ h('span', { class: 'hw-sr-only', role: 'status', 'aria-live': 'polite', 'aria-atomic': 'true' }, open ? props.resultsMessage(results.value.length) : ''),
282
+ ]));
283
+ };
284
+ },
285
+ });
286
+ export const Tooltip = defineComponent({
287
+ name: 'HwTooltip', inheritAttrs: false, props: { content: { type: String, required: true }, placement: { type: String, default: 'top' }, align: { type: String, default: 'start' } },
288
+ setup(props, { attrs, slots }) {
289
+ const id = `hw-${useId()}`;
290
+ const open = ref(false);
291
+ const root = ref();
292
+ let hovered = false, focused = false;
293
+ let hideTimer;
294
+ const show = () => { clearTimeout(hideTimer); open.value = true; };
295
+ const hide = () => { clearTimeout(hideTimer); open.value = false; };
296
+ const leave = () => {
297
+ hovered = false;
298
+ if (!focused)
299
+ hideTimer = setTimeout(hide, 120);
300
+ };
301
+ const escape = (event) => {
302
+ if (!open.value || event.key !== 'Escape' || event.defaultPrevented)
303
+ return;
304
+ event.preventDefault();
305
+ event.stopPropagation();
306
+ hide();
307
+ };
308
+ watch(open, (visible, _, cleanup) => {
309
+ const element = root.value;
310
+ if (!visible || !element)
311
+ return;
312
+ const doc = element.ownerDocument;
313
+ const outside = (event) => { if (!element.contains(event.target))
314
+ hide(); };
315
+ doc.addEventListener('keydown', escape);
316
+ doc.addEventListener('pointerdown', outside);
317
+ cleanup(() => { doc.removeEventListener('keydown', escape); doc.removeEventListener('pointerdown', outside); });
318
+ }, { flush: 'sync' });
319
+ onBeforeUnmount(() => clearTimeout(hideTimer));
320
+ return () => {
321
+ const children = slots.default?.() ?? [];
322
+ return h('span', mergeProps(attrs, {
323
+ ref: root, 'data-hw': 'tooltip',
324
+ onMouseenter: () => { hovered = true; show(); }, onMouseleave: leave,
325
+ onFocusin: () => { focused = true; show(); },
326
+ onFocusout: (event) => {
327
+ if (root.value?.contains(event.relatedTarget))
328
+ return;
329
+ focused = false;
330
+ if (!hovered)
331
+ hide();
332
+ },
333
+ onKeydown: escape,
334
+ }), [
335
+ ...children.map(child => isVNode(child) ? cloneVNode(child, { 'aria-describedby': joinIds(child.props?.['aria-describedby'], id) }) : child),
336
+ h('span', { id, role: 'tooltip', class: 'hw-tooltip', 'data-placement': props.placement, 'data-align': props.align, hidden: !open.value }, props.content),
337
+ ]);
338
+ };
339
+ },
340
+ });
341
+ //# sourceMappingURL=interactive.js.map