@primitiv-ui/web 0.1.55 → 0.1.57

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 (39) hide show
  1. package/README.md +70 -3
  2. package/dist/cdn/chunks/chunk-P2K4DPA4.js +1 -0
  3. package/dist/cdn/dropdown.js +1 -0
  4. package/dist/cdn/popover.js +1 -1
  5. package/dist/cdn/tooltip.js +1 -0
  6. package/dist/custom-elements.json +1647 -381
  7. package/dist/define/dropdown.d.ts +1 -0
  8. package/dist/define/dropdown.js +12 -0
  9. package/dist/define/index.d.ts +2 -0
  10. package/dist/define/index.js +2 -0
  11. package/dist/define/tooltip.d.ts +1 -0
  12. package/dist/define/tooltip.js +11 -0
  13. package/dist/dropdown/DropdownController.d.ts +63 -0
  14. package/dist/dropdown/DropdownController.js +130 -0
  15. package/dist/dropdown/DropdownSubController.d.ts +34 -0
  16. package/dist/dropdown/DropdownSubController.js +78 -0
  17. package/dist/dropdown/elements.d.ts +174 -0
  18. package/dist/dropdown/elements.js +501 -0
  19. package/dist/dropdown/index.d.ts +3 -0
  20. package/dist/dropdown/index.js +3 -0
  21. package/dist/frameworks/events.d.ts +22 -0
  22. package/dist/frameworks/react.d.ts +62 -2
  23. package/dist/frameworks/svelte.d.ts +59 -1
  24. package/dist/frameworks/vue.d.ts +64 -1
  25. package/dist/index.d.ts +2 -0
  26. package/dist/index.js +2 -0
  27. package/dist/popover/PopoverController.d.ts +6 -0
  28. package/dist/popover/PopoverController.js +41 -1
  29. package/dist/primitiv-web.js +1 -1
  30. package/dist/tags.d.ts +21 -0
  31. package/dist/tooltip/TooltipController.d.ts +74 -0
  32. package/dist/tooltip/TooltipController.js +149 -0
  33. package/dist/tooltip/TooltipGroup.d.ts +23 -0
  34. package/dist/tooltip/TooltipGroup.js +44 -0
  35. package/dist/tooltip/elements.d.ts +83 -0
  36. package/dist/tooltip/elements.js +223 -0
  37. package/dist/tooltip/index.d.ts +3 -0
  38. package/dist/tooltip/index.js +3 -0
  39. package/package.json +18 -2
@@ -0,0 +1,501 @@
1
+ import { defineElements, ElementBase, reportOnce, write } from "../internal/element.js";
2
+ import { PrimitivRootElement } from "../internal/RootElement.js";
3
+ import { DropdownController } from "./DropdownController.js";
4
+ import { DropdownSubController } from "./DropdownSubController.js";
5
+ // Stryker disable StringLiteral: static — evaluated once at import, so runtime mutant switching never reaches them; the names are pinned by the defineDropdown registry test instead.
6
+ const ROOT = "primitiv-dropdown";
7
+ const TRIGGER = "primitiv-dropdown-trigger";
8
+ const CONTENT = "primitiv-dropdown-content";
9
+ const ITEM = "primitiv-dropdown-item";
10
+ const CHECKBOX_ITEM = "primitiv-dropdown-checkbox-item";
11
+ const RADIO_GROUP = "primitiv-dropdown-radio-group";
12
+ const RADIO_ITEM = "primitiv-dropdown-radio-item";
13
+ const INDICATOR = "primitiv-dropdown-item-indicator";
14
+ const GROUP = "primitiv-dropdown-group";
15
+ const LABEL = "primitiv-dropdown-label";
16
+ const SEPARATOR = "primitiv-dropdown-separator";
17
+ const SUB = "primitiv-dropdown-sub";
18
+ const SUB_TRIGGER = "primitiv-dropdown-sub-trigger";
19
+ const SUB_CONTENT = "primitiv-dropdown-sub-content";
20
+ /** Every part that is a chosen item: the plain, checkbox and radio items. */
21
+ const ITEMS = `${ITEM},${CHECKBOX_ITEM},${RADIO_ITEM}`;
22
+ // Stryker restore StringLiteral
23
+ /** The one element child an interactive part decorates, or `null`, reported once, without exactly one. */
24
+ function decoratable(part, what) {
25
+ if (part.childElementCount === 1)
26
+ return part.firstElementChild;
27
+ reportOnce(part, `<${part.localName}> needs exactly one element child (${what}) to decorate.`);
28
+ return null;
29
+ }
30
+ /** Fire a bubbling, cancelable event on `target`; whether no listener vetoed it. */
31
+ function announceOn(target, type, detail) {
32
+ return target.dispatchEvent(new CustomEvent(type, { bubbles: true, cancelable: true, detail }));
33
+ }
34
+ /** A checkbox item's state, from its `checked` attribute: absent, present, or `"indeterminate"`. */
35
+ function checkedState(part) {
36
+ const value = part.getAttribute("checked");
37
+ return value === "indeterminate" ? value : value !== null;
38
+ }
39
+ /** A radio item's state: whether its value is its group's. */
40
+ function radioChecked(part) {
41
+ return part.closest(RADIO_GROUP)?.getAttribute("value") === part.getAttribute("value");
42
+ }
43
+ /**
44
+ * `<primitiv-dropdown>` — the state owner of a dropdown menu (WAI-ARIA APG
45
+ * Menu Button): a trigger that opens a menu of items. The menu is a
46
+ * `popover="auto"` in the top layer, shown with the trigger as its source,
47
+ * which makes the trigger its implicit anchor for CSS anchor positioning.
48
+ * Choosing an item — any item, a submenu's too — closes the menu and hands
49
+ * focus back to the trigger.
50
+ *
51
+ * The root finds its own parts and reconciles their ARIA from its reflected
52
+ * state whenever a part arrives, leaves, or changes an attribute the root
53
+ * reads (the lifecycle is `PrimitivRootElement`'s).
54
+ *
55
+ * @customElement primitiv-dropdown
56
+ * @summary The state owner of a dropdown menu.
57
+ * @attr {boolean} open - Whether the menu is open. Reflected: it changes as the user opens and dismisses it. Set it (or the `open` property) to open or close it from script.
58
+ * @fires {CustomEvent<{ open: boolean }>} primitiv-dropdown-change - Bubbles, cancelable, fired before a user-driven change is applied — the trigger, Escape, a press outside or a chosen item — with the open state afterwards. Call `preventDefault()` to veto it.
59
+ * @fires {CustomEvent<null>} primitiv-dropdown-select - Bubbles, cancelable, fired on the item part when an item is chosen, after a checkbox or radio item has changed. Call `preventDefault()` to keep the menu open.
60
+ * @fires {CustomEvent<{ checked: boolean }>} primitiv-dropdown-checked-change - Bubbles, cancelable, fired on a checkbox item before it changes, with its state afterwards. Call `preventDefault()` to veto it.
61
+ * @fires {CustomEvent<{ value: string }>} primitiv-dropdown-value-change - Bubbles, cancelable, fired on a radio group before its value changes, with the value afterwards. Call `preventDefault()` to veto it.
62
+ */
63
+ export class PrimitivDropdownElement extends PrimitivRootElement {
64
+ /** The menu the root currently decorates, and the controller over it. */
65
+ #content = null;
66
+ #menu = null;
67
+ /** The decorated trigger, as of the last reconcile. */
68
+ #trigger = null;
69
+ /** The root's own listeners, while connected. */
70
+ #listening = null;
71
+ controller = {
72
+ connect: () => this.#connect(),
73
+ disconnect: () => this.#disconnect(),
74
+ };
75
+ reconcileOn = ["open", "disabled", "checked", "value"];
76
+ /** Whether the menu is open, reflected as the boolean `open` attribute. */
77
+ get open() {
78
+ return this.hasAttribute("open");
79
+ }
80
+ set open(next) {
81
+ this.toggleAttribute("open", next);
82
+ }
83
+ /** The controller's view of this element: state lives in the reflected attribute. */
84
+ #host() {
85
+ return {
86
+ state: () => ({ open: this.open }),
87
+ change: (open) => this.#change(open),
88
+ trigger: () => this.#trigger,
89
+ source: () => this.#trigger,
90
+ };
91
+ }
92
+ /** A change the user asked for: announced first, so a listener can veto it. */
93
+ #change(open) {
94
+ if (open === this.open)
95
+ return;
96
+ if (this.announce("primitiv-dropdown-change", { open }))
97
+ this.open = open;
98
+ // A veto can leave the menu out of step — the browser is light-dismissing
99
+ // it — so put it back where the state says, once that dismissal is done.
100
+ else
101
+ queueMicrotask(() => this.#menu?.sync());
102
+ }
103
+ #connect() {
104
+ this.#listening = new AbortController();
105
+ const options = { signal: this.#listening.signal };
106
+ this.addEventListener("click", (event) => this.#click(event), options);
107
+ this.addEventListener("pointerover", (event) => this.#highlight(event, true), options);
108
+ this.addEventListener("pointerout", (event) => this.#highlight(event, false), options);
109
+ this.#menu?.connect();
110
+ }
111
+ #disconnect() {
112
+ // A root disconnects only after it connected, so it is listening.
113
+ this.#listening.abort();
114
+ this.#listening = null;
115
+ this.#menu?.disconnect();
116
+ }
117
+ /** The item part (of this root) an event landed in, or `null`. */
118
+ #itemOf(target) {
119
+ const part = target?.closest(ITEMS) ?? null;
120
+ return part?.closest(ROOT) === this ? part : null;
121
+ }
122
+ /** Mark the item the pointer is over, as React's `data-highlighted`. */
123
+ #highlight(event, over) {
124
+ const part = this.#itemOf(event.target);
125
+ // The relatedTarget test keeps the highlight steady as the pointer crosses
126
+ // the row's own children. user-event sets no relatedTarget, so only a real
127
+ // browser can tell: the interop lab's dropdown spec counts the flips.
128
+ // Stryker disable next-line ConditionalExpression,LogicalOperator: see above — unobservable in jsdom, proven in Chromium.
129
+ if (!part || part === this.#itemOf(event.relatedTarget))
130
+ return;
131
+ const child = part.firstElementChild;
132
+ write(child, "data-highlighted", over ? "" : null);
133
+ }
134
+ #click(event) {
135
+ const target = event.target;
136
+ if (this.#trigger?.contains(target)) {
137
+ this.#change(!this.open);
138
+ return;
139
+ }
140
+ const part = this.#itemOf(target);
141
+ if (!part || part.hasAttribute("disabled"))
142
+ return;
143
+ if (part.localName === CHECKBOX_ITEM) {
144
+ const checked = checkedState(part) !== true;
145
+ // `write`, not `toggleAttribute`: a mixed item's "indeterminate" must become plain checked.
146
+ if (announceOn(part, "primitiv-dropdown-checked-change", { checked }))
147
+ write(part, "checked", checked ? "" : null);
148
+ }
149
+ else if (part.localName === RADIO_ITEM && !radioChecked(part)) {
150
+ const group = part.closest(RADIO_GROUP);
151
+ const value = part.getAttribute("value");
152
+ if (announceOn(group, "primitiv-dropdown-value-change", { value }))
153
+ group.setAttribute("value", value);
154
+ }
155
+ if (!announceOn(part, "primitiv-dropdown-select", null))
156
+ return;
157
+ this.#change(false);
158
+ this.#trigger?.focus();
159
+ }
160
+ /**
161
+ * Decorate `content` from now on: the controller follows it. Only a
162
+ * reconcile calls this, and a root reconciles only while connected, so the
163
+ * new controller listens at once.
164
+ */
165
+ #use(content) {
166
+ // Stryker disable next-line ConditionalExpression: equivalent — rebuilding the controller over the same menu swaps one listener set for an identical one.
167
+ if (content === this.#content)
168
+ return;
169
+ this.#menu?.disconnect();
170
+ this.#content = content;
171
+ this.#menu = content ? new DropdownController(content, this.#host()) : null;
172
+ this.#menu?.connect();
173
+ }
174
+ reconcile() {
175
+ const open = this.open;
176
+ const rootId = this.rootId();
177
+ const [content = null] = this.parts(CONTENT);
178
+ const [triggerPart] = this.parts(TRIGGER);
179
+ this.#trigger = triggerPart ? decoratable(triggerPart, "a <button>") : null;
180
+ if (content) {
181
+ if (!content.id)
182
+ content.id = `${rootId}-content`;
183
+ write(content, "role", "menu");
184
+ if (!content.hasAttribute("popover"))
185
+ content.setAttribute("popover", "auto");
186
+ }
187
+ if (this.#trigger) {
188
+ write(this.#trigger, "aria-haspopup", "menu");
189
+ write(this.#trigger, "aria-expanded", String(open));
190
+ write(this.#trigger, "aria-controls", content ? content.id : null);
191
+ }
192
+ for (const part of this.parts(ITEMS)) {
193
+ const child = decoratable(part, "an <li>");
194
+ if (!child)
195
+ continue;
196
+ const checkbox = part.localName === CHECKBOX_ITEM;
197
+ const radio = part.localName === RADIO_ITEM;
198
+ const checked = checkbox ? checkedState(part) : radio && radioChecked(part);
199
+ write(child, "role", checkbox ? "menuitemcheckbox" : radio ? "menuitemradio" : "menuitem");
200
+ write(child, "tabindex", "-1");
201
+ write(child, "aria-disabled", part.hasAttribute("disabled") ? "true" : null);
202
+ write(child, "aria-checked", checkbox || radio ? (checked === "indeterminate" ? "mixed" : String(checked)) : null);
203
+ }
204
+ for (const indicator of this.parts(INDICATOR)) {
205
+ const item = indicator.closest(`${CHECKBOX_ITEM},${RADIO_ITEM}`);
206
+ const checked = item?.localName === CHECKBOX_ITEM ? checkedState(item) : item ? radioChecked(item) : false;
207
+ write(indicator, "data-state", checked === "indeterminate" ? checked : checked ? "checked" : "unchecked");
208
+ write(indicator, "hidden", checked || indicator.hasAttribute("force-mount") ? null : "");
209
+ }
210
+ let labels = 0;
211
+ for (const label of this.parts(LABEL)) {
212
+ if (!label.id)
213
+ label.id = `${rootId}-label-${++labels}`;
214
+ }
215
+ for (const group of this.parts(GROUP)) {
216
+ const label = [...group.querySelectorAll(LABEL)].find((candidate) => candidate.closest(GROUP) === group);
217
+ write(group, "role", "group");
218
+ write(group, "aria-labelledby", label ? label.id : null);
219
+ }
220
+ for (const group of this.parts(RADIO_GROUP))
221
+ write(group, "role", "group");
222
+ for (const separator of this.parts(SEPARATOR))
223
+ write(separator, "role", "separator");
224
+ this.#use(content);
225
+ this.#menu?.sync();
226
+ }
227
+ }
228
+ /**
229
+ * `<primitiv-dropdown-sub>` — the state owner of a submenu, inside a dropdown's
230
+ * menu: a sub-trigger row that opens a nested menu on a click, a hover, or the
231
+ * arrow pointing away from its parent (ArrowRight, or ArrowLeft right to
232
+ * left), and closes it on the arrow back, or when the pointer moves to another
233
+ * row of the parent menu.
234
+ *
235
+ * @customElement primitiv-dropdown-sub
236
+ * @summary The state owner of a submenu.
237
+ * @attr {boolean} open - Whether the submenu is open. Reflected.
238
+ * @fires {CustomEvent<{ open: boolean }>} primitiv-dropdown-sub-change - Bubbles, cancelable, fired before a user-driven change is applied, with the open state afterwards. Call `preventDefault()` to veto it.
239
+ */
240
+ export class PrimitivDropdownSubElement extends PrimitivRootElement {
241
+ #content = null;
242
+ #menu = null;
243
+ #trigger = null;
244
+ /** The sub-trigger part (whose `disabled` the sub reads), as of the last reconcile. */
245
+ #triggerPart = null;
246
+ /** Whether the pointer is over the sub-trigger. */
247
+ #hovered = false;
248
+ #listening = null;
249
+ controller = {
250
+ connect: () => this.#connect(),
251
+ disconnect: () => this.#disconnect(),
252
+ };
253
+ reconcileOn = ["open", "disabled"];
254
+ /** Whether the submenu is open, reflected as the boolean `open` attribute. */
255
+ get open() {
256
+ return this.hasAttribute("open");
257
+ }
258
+ set open(next) {
259
+ this.toggleAttribute("open", next);
260
+ }
261
+ #host() {
262
+ return {
263
+ state: () => ({ open: this.open }),
264
+ change: (open) => this.#change(open),
265
+ trigger: () => this.#trigger,
266
+ source: () => this.#trigger,
267
+ closeKey: () => (this.direction() === "rtl" ? "ArrowRight" : "ArrowLeft"),
268
+ };
269
+ }
270
+ #change(open) {
271
+ if (open === this.open)
272
+ return;
273
+ if (this.announce("primitiv-dropdown-sub-change", { open }))
274
+ this.open = open;
275
+ else
276
+ queueMicrotask(() => this.#menu?.sync());
277
+ }
278
+ /** Whether the sub-trigger is disabled. Only asked once a decorated sub-trigger exists. */
279
+ #disabled() {
280
+ return this.#triggerPart.hasAttribute("disabled");
281
+ }
282
+ #connect() {
283
+ this.#listening = new AbortController();
284
+ const options = { signal: this.#listening.signal };
285
+ this.addEventListener("click", (event) => this.#onTrigger(event) && this.#openIfEnabled(), options);
286
+ this.addEventListener("keydown", (event) => this.#keydown(event), options);
287
+ this.addEventListener("pointerover", (event) => this.#pointer(event, true), options);
288
+ this.addEventListener("pointerout", (event) => this.#pointer(event, false), options);
289
+ document.addEventListener("pointerover", (event) => this.#sibling(event), options);
290
+ this.#menu?.connect();
291
+ }
292
+ #disconnect() {
293
+ this.#listening.abort();
294
+ this.#listening = null;
295
+ this.#menu?.disconnect();
296
+ }
297
+ /** Whether an event landed on the decorated sub-trigger. */
298
+ #onTrigger(event) {
299
+ return this.#trigger?.contains(event.target) ?? false;
300
+ }
301
+ #openIfEnabled() {
302
+ if (!this.#disabled())
303
+ this.#change(true);
304
+ }
305
+ #keydown(event) {
306
+ if (!this.#onTrigger(event) || this.#disabled())
307
+ return;
308
+ if (event.key !== (this.direction() === "rtl" ? "ArrowLeft" : "ArrowRight"))
309
+ return;
310
+ event.preventDefault();
311
+ event.stopPropagation();
312
+ this.#change(true);
313
+ }
314
+ #pointer(event, over) {
315
+ // Past the first test there is a decorated trigger. The relatedTarget test
316
+ // keeps the hover steady across the row's own children, which only a real
317
+ // browser can show (see #highlight).
318
+ // Stryker disable next-line ConditionalExpression,LogicalOperator: unobservable in jsdom, proven in Chromium by the interop lab.
319
+ if (!this.#onTrigger(event) || this.#trigger.contains(event.relatedTarget))
320
+ return;
321
+ this.#hovered = over;
322
+ this.reconcile();
323
+ if (over)
324
+ this.#openIfEnabled();
325
+ }
326
+ /** The pointer reached another row of the parent menu: close, as moving the keyboard there would. */
327
+ #sibling(event) {
328
+ if (!this.open || this.contains(event.target))
329
+ return;
330
+ const row = event.target.closest('[role^="menuitem"]');
331
+ // A connected sub always has a parent; only a row of its parent's menu closes it.
332
+ if (row && row.closest('[role="menu"]') === this.parentElement.closest('[role="menu"]'))
333
+ this.#change(false);
334
+ }
335
+ #use(content) {
336
+ // Stryker disable next-line ConditionalExpression: equivalent — rebuilding the controller over the same menu swaps one listener set for an identical one.
337
+ if (content === this.#content)
338
+ return;
339
+ this.#menu?.disconnect();
340
+ this.#content = content;
341
+ this.#menu = content ? new DropdownSubController(content, this.#host()) : null;
342
+ this.#menu?.connect();
343
+ }
344
+ reconcile() {
345
+ const open = this.open;
346
+ const [content = null] = this.parts(SUB_CONTENT);
347
+ const [triggerPart = null] = this.parts(SUB_TRIGGER);
348
+ this.#triggerPart = triggerPart;
349
+ this.#trigger = triggerPart ? decoratable(triggerPart, "an <li>") : null;
350
+ if (content) {
351
+ if (!content.id)
352
+ content.id = `${this.rootId()}-content`;
353
+ write(content, "role", "menu");
354
+ if (!content.hasAttribute("popover"))
355
+ content.setAttribute("popover", "auto");
356
+ }
357
+ if (this.#trigger) {
358
+ write(this.#trigger, "role", "menuitem");
359
+ write(this.#trigger, "tabindex", "-1");
360
+ write(this.#trigger, "aria-haspopup", "menu");
361
+ write(this.#trigger, "aria-expanded", String(open));
362
+ write(this.#trigger, "aria-controls", content ? content.id : null);
363
+ write(this.#trigger, "aria-disabled", this.#disabled() ? "true" : null);
364
+ write(this.#trigger, "data-highlighted", this.#hovered || open ? "" : null);
365
+ }
366
+ this.#use(content);
367
+ this.#menu?.sync();
368
+ }
369
+ }
370
+ /**
371
+ * `<primitiv-dropdown-trigger>` — an interactive part. It decorates exactly
372
+ * one child the consumer writes, a `<button>`, which toggles the menu and is
373
+ * its menu button (`aria-haspopup`, `aria-expanded`, `aria-controls`).
374
+ *
375
+ * @customElement primitiv-dropdown-trigger
376
+ */
377
+ export class PrimitivDropdownTriggerElement extends ElementBase {
378
+ }
379
+ /**
380
+ * `<primitiv-dropdown-content>` — the menu itself. Structural: the root gives
381
+ * it an id, `role="menu"` and `popover="auto"` (write it in your markup too, so
382
+ * it is hidden before the script loads).
383
+ *
384
+ * @customElement primitiv-dropdown-content
385
+ */
386
+ export class PrimitivDropdownContentElement extends ElementBase {
387
+ }
388
+ /**
389
+ * `<primitiv-dropdown-item>` — an interactive part: one item. It decorates
390
+ * exactly one child the consumer writes, an `<li>` (or a link), which gets
391
+ * `role="menuitem"`, `tabindex="-1"` and `data-highlighted` while the pointer
392
+ * is over it.
393
+ *
394
+ * @customElement primitiv-dropdown-item
395
+ * @attr {boolean} disabled - Skipped by the keyboard, and does nothing when clicked (`aria-disabled` on the child).
396
+ */
397
+ export class PrimitivDropdownItemElement extends ElementBase {
398
+ }
399
+ /**
400
+ * `<primitiv-dropdown-checkbox-item>` — an item that toggles. Its child gets
401
+ * `role="menuitemcheckbox"` and `aria-checked`.
402
+ *
403
+ * @customElement primitiv-dropdown-checkbox-item
404
+ * @attr {boolean} checked - Whether it is checked; `checked="indeterminate"` for mixed. Reflected.
405
+ * @attr {boolean} disabled - As on an item.
406
+ */
407
+ export class PrimitivDropdownCheckboxItemElement extends ElementBase {
408
+ }
409
+ /**
410
+ * `<primitiv-dropdown-radio-group>` — a group of radio items, one of which is
411
+ * checked. Structural: `role="group"`.
412
+ *
413
+ * @customElement primitiv-dropdown-radio-group
414
+ * @attr {string} value - The checked item's value. Reflected.
415
+ */
416
+ export class PrimitivDropdownRadioGroupElement extends ElementBase {
417
+ }
418
+ /**
419
+ * `<primitiv-dropdown-radio-item>` — an item checked when its value is its
420
+ * group's. Its child gets `role="menuitemradio"` and `aria-checked`.
421
+ *
422
+ * @customElement primitiv-dropdown-radio-item
423
+ * @attr {string} value - The value it sets on its group.
424
+ * @attr {boolean} disabled - As on an item.
425
+ */
426
+ export class PrimitivDropdownRadioItemElement extends ElementBase {
427
+ }
428
+ /**
429
+ * `<primitiv-dropdown-item-indicator>` — the check or dot inside a checkbox or
430
+ * radio item, `hidden` while unchecked. Structural: it gets `data-state`.
431
+ *
432
+ * @customElement primitiv-dropdown-item-indicator
433
+ * @attr {boolean} force-mount - Keep it shown while unchecked, to animate it.
434
+ */
435
+ export class PrimitivDropdownItemIndicatorElement extends ElementBase {
436
+ }
437
+ /**
438
+ * `<primitiv-dropdown-group>` — a labelled group of items. Structural:
439
+ * `role="group"`, named by the label inside it.
440
+ *
441
+ * @customElement primitiv-dropdown-group
442
+ */
443
+ export class PrimitivDropdownGroupElement extends ElementBase {
444
+ }
445
+ /**
446
+ * `<primitiv-dropdown-label>` — a group's label, not an item. Structural: the
447
+ * root gives it an id.
448
+ *
449
+ * @customElement primitiv-dropdown-label
450
+ */
451
+ export class PrimitivDropdownLabelElement extends ElementBase {
452
+ }
453
+ /**
454
+ * `<primitiv-dropdown-separator>` — a rule between items. Structural:
455
+ * `role="separator"`.
456
+ *
457
+ * @customElement primitiv-dropdown-separator
458
+ */
459
+ export class PrimitivDropdownSeparatorElement extends ElementBase {
460
+ }
461
+ /**
462
+ * `<primitiv-dropdown-sub-trigger>` — the row that opens a submenu. It
463
+ * decorates exactly one child, an `<li>`, which gets `role="menuitem"` and the
464
+ * submenu's `aria-haspopup`, `aria-expanded` and `aria-controls`.
465
+ *
466
+ * @customElement primitiv-dropdown-sub-trigger
467
+ * @attr {boolean} disabled - It does not open the submenu.
468
+ */
469
+ export class PrimitivDropdownSubTriggerElement extends ElementBase {
470
+ }
471
+ /**
472
+ * `<primitiv-dropdown-sub-content>` — the submenu itself, inside the parent
473
+ * menu. Structural, as the content: an id, `role="menu"`, `popover="auto"`.
474
+ *
475
+ * @customElement primitiv-dropdown-sub-content
476
+ */
477
+ export class PrimitivDropdownSubContentElement extends ElementBase {
478
+ }
479
+ /**
480
+ * Define the fourteen Dropdown elements on `registry` (the global
481
+ * `customElements` by default). Safe to call more than once, and a no-op where
482
+ * there is no registry (a server render).
483
+ */
484
+ export function defineDropdown(registry = globalThis.customElements) {
485
+ defineElements(registry, [
486
+ [ROOT, PrimitivDropdownElement],
487
+ [TRIGGER, PrimitivDropdownTriggerElement],
488
+ [CONTENT, PrimitivDropdownContentElement],
489
+ [ITEM, PrimitivDropdownItemElement],
490
+ [CHECKBOX_ITEM, PrimitivDropdownCheckboxItemElement],
491
+ [RADIO_GROUP, PrimitivDropdownRadioGroupElement],
492
+ [RADIO_ITEM, PrimitivDropdownRadioItemElement],
493
+ [INDICATOR, PrimitivDropdownItemIndicatorElement],
494
+ [GROUP, PrimitivDropdownGroupElement],
495
+ [LABEL, PrimitivDropdownLabelElement],
496
+ [SEPARATOR, PrimitivDropdownSeparatorElement],
497
+ [SUB, PrimitivDropdownSubElement],
498
+ [SUB_TRIGGER, PrimitivDropdownSubTriggerElement],
499
+ [SUB_CONTENT, PrimitivDropdownSubContentElement],
500
+ ]);
501
+ }
@@ -0,0 +1,3 @@
1
+ export { DropdownController, type DropdownControllerHost } from "./DropdownController.js";
2
+ export { DropdownSubController, type DropdownSubControllerHost } from "./DropdownSubController.js";
3
+ export { defineDropdown, PrimitivDropdownCheckboxItemElement, PrimitivDropdownContentElement, PrimitivDropdownElement, PrimitivDropdownGroupElement, PrimitivDropdownItemElement, PrimitivDropdownItemIndicatorElement, PrimitivDropdownLabelElement, PrimitivDropdownRadioGroupElement, PrimitivDropdownRadioItemElement, PrimitivDropdownSeparatorElement, PrimitivDropdownSubContentElement, PrimitivDropdownSubElement, PrimitivDropdownSubTriggerElement, PrimitivDropdownTriggerElement, } from "./elements.js";
@@ -0,0 +1,3 @@
1
+ export { DropdownController } from "./DropdownController.js";
2
+ export { DropdownSubController } from "./DropdownSubController.js";
3
+ export { defineDropdown, PrimitivDropdownCheckboxItemElement, PrimitivDropdownContentElement, PrimitivDropdownElement, PrimitivDropdownGroupElement, PrimitivDropdownItemElement, PrimitivDropdownItemIndicatorElement, PrimitivDropdownLabelElement, PrimitivDropdownRadioGroupElement, PrimitivDropdownRadioItemElement, PrimitivDropdownSeparatorElement, PrimitivDropdownSubContentElement, PrimitivDropdownSubElement, PrimitivDropdownSubTriggerElement, PrimitivDropdownTriggerElement, } from "./elements.js";
@@ -23,3 +23,25 @@ export type PrimitivModalChangeEvent = CustomEvent<{
23
23
  export type PrimitivPopoverChangeEvent = CustomEvent<{
24
24
  open: boolean;
25
25
  }>;
26
+ /** The `primitiv-tooltip-change` event: bubbles, cancelable, fired before the change applies, with the open state afterwards. */
27
+ export type PrimitivTooltipChangeEvent = CustomEvent<{
28
+ open: boolean;
29
+ }>;
30
+ /** The `primitiv-dropdown-change` event: bubbles, cancelable, fired before the change applies, with the open state afterwards. */
31
+ export type PrimitivDropdownChangeEvent = CustomEvent<{
32
+ open: boolean;
33
+ }>;
34
+ /** The `primitiv-dropdown-sub-change` event: bubbles, cancelable, fired before a submenu's change applies, with its open state afterwards. */
35
+ export type PrimitivDropdownSubChangeEvent = CustomEvent<{
36
+ open: boolean;
37
+ }>;
38
+ /** The `primitiv-dropdown-select` event: bubbles, cancelable, fired on an item as it is chosen; cancel it to keep the menu open. */
39
+ export type PrimitivDropdownSelectEvent = CustomEvent<null>;
40
+ /** The `primitiv-dropdown-checked-change` event: bubbles, cancelable, fired on a checkbox item before it changes, with its state afterwards. */
41
+ export type PrimitivDropdownCheckedChangeEvent = CustomEvent<{
42
+ checked: boolean;
43
+ }>;
44
+ /** The `primitiv-dropdown-value-change` event: bubbles, cancelable, fired on a radio group before its value changes, with the value afterwards. */
45
+ export type PrimitivDropdownValueChangeEvent = CustomEvent<{
46
+ value: string;
47
+ }>;
@@ -20,15 +20,17 @@
20
20
  import type { DetailedHTMLProps, HTMLAttributes } from "react";
21
21
  import type { PrimitivAccordionContentElement, PrimitivAccordionElement, PrimitivAccordionItemElement, PrimitivAccordionTriggerElement, PrimitivAccordionTriggerIconElement } from "../accordion/elements.js";
22
22
  import type { PrimitivCollapsibleContentElement, PrimitivCollapsibleElement, PrimitivCollapsibleTriggerElement, PrimitivCollapsibleTriggerIconElement } from "../collapsible/elements.js";
23
+ import type { PrimitivDropdownCheckboxItemElement, PrimitivDropdownContentElement, PrimitivDropdownElement, PrimitivDropdownGroupElement, PrimitivDropdownItemElement, PrimitivDropdownItemIndicatorElement, PrimitivDropdownLabelElement, PrimitivDropdownRadioGroupElement, PrimitivDropdownRadioItemElement, PrimitivDropdownSeparatorElement, PrimitivDropdownSubContentElement, PrimitivDropdownSubElement, PrimitivDropdownSubTriggerElement, PrimitivDropdownTriggerElement } from "../dropdown/elements.js";
23
24
  import type { PrimitivModalCloseElement, PrimitivModalContentElement, PrimitivModalDescriptionElement, PrimitivModalElement, PrimitivModalTitleElement, PrimitivModalTriggerElement } from "../modal/elements.js";
24
25
  import type { PrimitivPopoverAnchorElement, PrimitivPopoverCloseElement, PrimitivPopoverContentElement, PrimitivPopoverDescriptionElement, PrimitivPopoverElement, PrimitivPopoverTitleElement, PrimitivPopoverTriggerElement } from "../popover/elements.js";
25
26
  import type { PrimitivTabsContentElement, PrimitivTabsElement, PrimitivTabsListElement, PrimitivTabsTriggerElement } from "../tabs/elements.js";
26
- import type { PrimitivAccordionChangeEvent, PrimitivCollapsibleChangeEvent, PrimitivModalChangeEvent, PrimitivPopoverChangeEvent, PrimitivTabsChangeEvent } from "./events.js";
27
+ import type { PrimitivTooltipArrowElement, PrimitivTooltipContentElement, PrimitivTooltipElement, PrimitivTooltipProviderElement, PrimitivTooltipTriggerElement } from "../tooltip/elements.js";
28
+ import type { PrimitivAccordionChangeEvent, PrimitivDropdownChangeEvent, PrimitivDropdownCheckedChangeEvent, PrimitivDropdownSelectEvent, PrimitivDropdownSubChangeEvent, PrimitivDropdownValueChangeEvent, PrimitivCollapsibleChangeEvent, PrimitivModalChangeEvent, PrimitivPopoverChangeEvent, PrimitivTabsChangeEvent, PrimitivTooltipChangeEvent } from "./events.js";
27
29
  /** React's own props for an element, plus `class`, which React 19 passes to custom elements. */
28
30
  type ElementProps<E extends HTMLElement> = DetailedHTMLProps<HTMLAttributes<E>, E> & {
29
31
  class?: string;
30
32
  };
31
- export type { PrimitivAccordionChangeEvent, PrimitivCollapsibleChangeEvent, PrimitivModalChangeEvent, PrimitivPopoverChangeEvent, PrimitivTabsChangeEvent, };
33
+ export type { PrimitivAccordionChangeEvent, PrimitivDropdownChangeEvent, PrimitivDropdownCheckedChangeEvent, PrimitivDropdownSelectEvent, PrimitivDropdownSubChangeEvent, PrimitivDropdownValueChangeEvent, PrimitivCollapsibleChangeEvent, PrimitivModalChangeEvent, PrimitivPopoverChangeEvent, PrimitivTabsChangeEvent, PrimitivTooltipChangeEvent, };
32
34
  declare module "react" {
33
35
  namespace JSX {
34
36
  interface IntrinsicElements {
@@ -61,6 +63,49 @@ declare module "react" {
61
63
  "onprimitiv-collapsible-show"?: (event: CustomEvent) => void;
62
64
  };
63
65
  "primitiv-collapsible-trigger-icon": ElementProps<PrimitivCollapsibleTriggerIconElement>;
66
+ "primitiv-dropdown": ElementProps<PrimitivDropdownElement> & {
67
+ open?: boolean;
68
+ "onprimitiv-dropdown-change"?: (event: PrimitivDropdownChangeEvent) => void;
69
+ };
70
+ "primitiv-dropdown-trigger": ElementProps<PrimitivDropdownTriggerElement>;
71
+ "primitiv-dropdown-content": ElementProps<PrimitivDropdownContentElement> & {
72
+ popover?: "auto" | "manual" | "";
73
+ };
74
+ "primitiv-dropdown-item": ElementProps<PrimitivDropdownItemElement> & {
75
+ disabled?: boolean;
76
+ "onprimitiv-dropdown-select"?: (event: PrimitivDropdownSelectEvent) => void;
77
+ };
78
+ "primitiv-dropdown-checkbox-item": ElementProps<PrimitivDropdownCheckboxItemElement> & {
79
+ checked?: boolean | "indeterminate";
80
+ disabled?: boolean;
81
+ "onprimitiv-dropdown-checked-change"?: (event: PrimitivDropdownCheckedChangeEvent) => void;
82
+ "onprimitiv-dropdown-select"?: (event: PrimitivDropdownSelectEvent) => void;
83
+ };
84
+ "primitiv-dropdown-radio-group": ElementProps<PrimitivDropdownRadioGroupElement> & {
85
+ value?: string;
86
+ "onprimitiv-dropdown-value-change"?: (event: PrimitivDropdownValueChangeEvent) => void;
87
+ };
88
+ "primitiv-dropdown-radio-item": ElementProps<PrimitivDropdownRadioItemElement> & {
89
+ value: string;
90
+ disabled?: boolean;
91
+ "onprimitiv-dropdown-select"?: (event: PrimitivDropdownSelectEvent) => void;
92
+ };
93
+ "primitiv-dropdown-item-indicator": ElementProps<PrimitivDropdownItemIndicatorElement> & {
94
+ "force-mount"?: boolean;
95
+ };
96
+ "primitiv-dropdown-group": ElementProps<PrimitivDropdownGroupElement>;
97
+ "primitiv-dropdown-label": ElementProps<PrimitivDropdownLabelElement>;
98
+ "primitiv-dropdown-separator": ElementProps<PrimitivDropdownSeparatorElement>;
99
+ "primitiv-dropdown-sub": ElementProps<PrimitivDropdownSubElement> & {
100
+ open?: boolean;
101
+ "onprimitiv-dropdown-sub-change"?: (event: PrimitivDropdownSubChangeEvent) => void;
102
+ };
103
+ "primitiv-dropdown-sub-trigger": ElementProps<PrimitivDropdownSubTriggerElement> & {
104
+ disabled?: boolean;
105
+ };
106
+ "primitiv-dropdown-sub-content": ElementProps<PrimitivDropdownSubContentElement> & {
107
+ popover?: "auto" | "manual" | "";
108
+ };
64
109
  "primitiv-modal": ElementProps<PrimitivModalElement> & {
65
110
  open?: boolean;
66
111
  "onprimitiv-modal-change"?: (event: PrimitivModalChangeEvent) => void;
@@ -99,6 +144,21 @@ declare module "react" {
99
144
  value: string;
100
145
  "onprimitiv-tabs-show"?: (event: CustomEvent) => void;
101
146
  };
147
+ "primitiv-tooltip": ElementProps<PrimitivTooltipElement> & {
148
+ open?: boolean;
149
+ "delay-duration"?: number | string;
150
+ "disable-hoverable-content"?: boolean;
151
+ "onprimitiv-tooltip-change"?: (event: PrimitivTooltipChangeEvent) => void;
152
+ };
153
+ "primitiv-tooltip-provider": ElementProps<PrimitivTooltipProviderElement> & {
154
+ "delay-duration"?: number | string;
155
+ "skip-delay-duration"?: number | string;
156
+ };
157
+ "primitiv-tooltip-trigger": ElementProps<PrimitivTooltipTriggerElement>;
158
+ "primitiv-tooltip-content": ElementProps<PrimitivTooltipContentElement> & {
159
+ popover?: "auto" | "manual" | "";
160
+ };
161
+ "primitiv-tooltip-arrow": ElementProps<PrimitivTooltipArrowElement>;
102
162
  }
103
163
  }
104
164
  }