@zag-js/popover 1.38.1 → 1.39.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.
package/dist/index.d.mts CHANGED
@@ -3,7 +3,7 @@ export { anatomy } from './popover.anatomy.mjs';
3
3
  export { connect } from './popover.connect.mjs';
4
4
  export { machine } from './popover.machine.mjs';
5
5
  export { props, splitProps } from './popover.props.mjs';
6
- export { PopoverApi as Api, ElementIds, IntlTranslations, PopoverMachine as Machine, OpenChangeDetails, PopoverProps as Props, PopoverService as Service } from './popover.types.mjs';
6
+ export { PopoverApi as Api, ElementIds, IntlTranslations, PopoverMachine as Machine, OpenChangeDetails, PopoverProps as Props, PopoverService as Service, TriggerProps, TriggerValueChangeDetails } from './popover.types.mjs';
7
7
  export { Placement, PositioningOptions } from '@zag-js/popper';
8
8
  import '@zag-js/anatomy';
9
9
  import '@zag-js/types';
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { anatomy } from './popover.anatomy.js';
3
3
  export { connect } from './popover.connect.js';
4
4
  export { machine } from './popover.machine.js';
5
5
  export { props, splitProps } from './popover.props.js';
6
- export { PopoverApi as Api, ElementIds, IntlTranslations, PopoverMachine as Machine, OpenChangeDetails, PopoverProps as Props, PopoverService as Service } from './popover.types.js';
6
+ export { PopoverApi as Api, ElementIds, IntlTranslations, PopoverMachine as Machine, OpenChangeDetails, PopoverProps as Props, PopoverService as Service, TriggerProps, TriggerValueChangeDetails } from './popover.types.js';
7
7
  export { Placement, PositioningOptions } from '@zag-js/popper';
8
8
  import '@zag-js/anatomy';
9
9
  import '@zag-js/types';
@@ -44,6 +44,7 @@ function connect(service, normalize) {
44
44
  const currentPlacement = context.get("currentPlacement");
45
45
  const portalled = computed("currentPortalled");
46
46
  const rendered = context.get("renderedElements");
47
+ const triggerValue = context.get("triggerValue");
47
48
  const popperStyles = (0, import_popper.getPlacementStyles)({
48
49
  ...prop("positioning"),
49
50
  placement: currentPlacement
@@ -56,6 +57,10 @@ function connect(service, normalize) {
56
57
  if (open2 === nextOpen) return;
57
58
  send({ type: nextOpen ? "OPEN" : "CLOSE" });
58
59
  },
60
+ triggerValue,
61
+ setTriggerValue(value) {
62
+ send({ type: "TRIGGER_VALUE.SET", value });
63
+ },
59
64
  reposition(options = {}) {
60
65
  send({ type: "POSITIONING.SET", options });
61
66
  },
@@ -81,15 +86,20 @@ function connect(service, normalize) {
81
86
  id: dom.getAnchorId(scope)
82
87
  });
83
88
  },
84
- getTriggerProps() {
89
+ getTriggerProps(props = {}) {
90
+ const { value } = props;
91
+ const current = value == null ? false : triggerValue === value;
85
92
  return normalize.button({
86
93
  ...import_popover.parts.trigger.attrs,
87
94
  dir: prop("dir"),
88
95
  type: "button",
89
96
  "data-placement": currentPlacement,
90
- id: dom.getTriggerId(scope),
97
+ id: dom.getTriggerId(scope, value),
98
+ "data-ownedby": scope.id,
99
+ "data-value": value,
100
+ "data-current": (0, import_dom_query.dataAttr)(current),
91
101
  "aria-haspopup": "dialog",
92
- "aria-expanded": open,
102
+ "aria-expanded": value == null ? open : open && current,
93
103
  "data-state": open ? "open" : "closed",
94
104
  "aria-controls": dom.getContentId(scope),
95
105
  onPointerDown(event) {
@@ -100,7 +110,8 @@ function connect(service, normalize) {
100
110
  },
101
111
  onClick(event) {
102
112
  if (event.defaultPrevented) return;
103
- send({ type: "TOGGLE" });
113
+ const shouldSwitch = open && value != null && !current;
114
+ send({ type: shouldSwitch ? "TRIGGER_VALUE.SET" : "TOGGLE", value });
104
115
  },
105
116
  onBlur(event) {
106
117
  send({ type: "TRIGGER_BLUR", target: event.relatedTarget });
@@ -10,6 +10,7 @@ function connect(service, normalize) {
10
10
  const currentPlacement = context.get("currentPlacement");
11
11
  const portalled = computed("currentPortalled");
12
12
  const rendered = context.get("renderedElements");
13
+ const triggerValue = context.get("triggerValue");
13
14
  const popperStyles = getPlacementStyles({
14
15
  ...prop("positioning"),
15
16
  placement: currentPlacement
@@ -22,6 +23,10 @@ function connect(service, normalize) {
22
23
  if (open2 === nextOpen) return;
23
24
  send({ type: nextOpen ? "OPEN" : "CLOSE" });
24
25
  },
26
+ triggerValue,
27
+ setTriggerValue(value) {
28
+ send({ type: "TRIGGER_VALUE.SET", value });
29
+ },
25
30
  reposition(options = {}) {
26
31
  send({ type: "POSITIONING.SET", options });
27
32
  },
@@ -47,15 +52,20 @@ function connect(service, normalize) {
47
52
  id: dom.getAnchorId(scope)
48
53
  });
49
54
  },
50
- getTriggerProps() {
55
+ getTriggerProps(props = {}) {
56
+ const { value } = props;
57
+ const current = value == null ? false : triggerValue === value;
51
58
  return normalize.button({
52
59
  ...parts.trigger.attrs,
53
60
  dir: prop("dir"),
54
61
  type: "button",
55
62
  "data-placement": currentPlacement,
56
- id: dom.getTriggerId(scope),
63
+ id: dom.getTriggerId(scope, value),
64
+ "data-ownedby": scope.id,
65
+ "data-value": value,
66
+ "data-current": dataAttr(current),
57
67
  "aria-haspopup": "dialog",
58
- "aria-expanded": open,
68
+ "aria-expanded": value == null ? open : open && current,
59
69
  "data-state": open ? "open" : "closed",
60
70
  "aria-controls": dom.getContentId(scope),
61
71
  onPointerDown(event) {
@@ -66,7 +76,8 @@ function connect(service, normalize) {
66
76
  },
67
77
  onClick(event) {
68
78
  if (event.defaultPrevented) return;
69
- send({ type: "TOGGLE" });
79
+ const shouldSwitch = open && value != null && !current;
80
+ send({ type: shouldSwitch ? "TRIGGER_VALUE.SET" : "TOGGLE", value });
70
81
  },
71
82
  onBlur(event) {
72
83
  send({ type: "TRIGGER_BLUR", target: event.relatedTarget });
@@ -1,7 +1,7 @@
1
1
  import { Scope } from '@zag-js/core';
2
2
 
3
3
  declare const getAnchorId: (scope: Scope) => any;
4
- declare const getTriggerId: (scope: Scope) => any;
4
+ declare const getTriggerId: (scope: Scope, value?: string) => any;
5
5
  declare const getContentId: (scope: Scope) => any;
6
6
  declare const getPositionerId: (scope: Scope) => any;
7
7
  declare const getArrowId: (scope: Scope) => any;
@@ -9,7 +9,8 @@ declare const getTitleId: (scope: Scope) => any;
9
9
  declare const getDescriptionId: (scope: Scope) => any;
10
10
  declare const getCloseTriggerId: (scope: Scope) => any;
11
11
  declare const getAnchorEl: (scope: Scope) => HTMLElement | null;
12
- declare const getTriggerEl: (scope: Scope) => HTMLElement | null;
12
+ declare const getTriggerEls: (scope: Scope) => HTMLElement[];
13
+ declare const getActiveTriggerEl: (scope: Scope, value: string | null) => HTMLElement | null;
13
14
  declare const getContentEl: (scope: Scope) => HTMLElement | null;
14
15
  declare const getPositionerEl: (scope: Scope) => HTMLElement | null;
15
16
  declare const getTitleEl: (scope: Scope) => HTMLElement | null;
@@ -17,4 +18,4 @@ declare const getDescriptionEl: (scope: Scope) => HTMLElement | null;
17
18
  declare const getFocusableEls: (scope: Scope) => HTMLElement[];
18
19
  declare const getFirstFocusableEl: (scope: Scope) => HTMLElement;
19
20
 
20
- export { getAnchorEl, getAnchorId, getArrowId, getCloseTriggerId, getContentEl, getContentId, getDescriptionEl, getDescriptionId, getFirstFocusableEl, getFocusableEls, getPositionerEl, getPositionerId, getTitleEl, getTitleId, getTriggerEl, getTriggerId };
21
+ export { getActiveTriggerEl, getAnchorEl, getAnchorId, getArrowId, getCloseTriggerId, getContentEl, getContentId, getDescriptionEl, getDescriptionId, getFirstFocusableEl, getFocusableEls, getPositionerEl, getPositionerId, getTitleEl, getTitleId, getTriggerEls, getTriggerId };
@@ -1,7 +1,7 @@
1
1
  import { Scope } from '@zag-js/core';
2
2
 
3
3
  declare const getAnchorId: (scope: Scope) => any;
4
- declare const getTriggerId: (scope: Scope) => any;
4
+ declare const getTriggerId: (scope: Scope, value?: string) => any;
5
5
  declare const getContentId: (scope: Scope) => any;
6
6
  declare const getPositionerId: (scope: Scope) => any;
7
7
  declare const getArrowId: (scope: Scope) => any;
@@ -9,7 +9,8 @@ declare const getTitleId: (scope: Scope) => any;
9
9
  declare const getDescriptionId: (scope: Scope) => any;
10
10
  declare const getCloseTriggerId: (scope: Scope) => any;
11
11
  declare const getAnchorEl: (scope: Scope) => HTMLElement | null;
12
- declare const getTriggerEl: (scope: Scope) => HTMLElement | null;
12
+ declare const getTriggerEls: (scope: Scope) => HTMLElement[];
13
+ declare const getActiveTriggerEl: (scope: Scope, value: string | null) => HTMLElement | null;
13
14
  declare const getContentEl: (scope: Scope) => HTMLElement | null;
14
15
  declare const getPositionerEl: (scope: Scope) => HTMLElement | null;
15
16
  declare const getTitleEl: (scope: Scope) => HTMLElement | null;
@@ -17,4 +18,4 @@ declare const getDescriptionEl: (scope: Scope) => HTMLElement | null;
17
18
  declare const getFocusableEls: (scope: Scope) => HTMLElement[];
18
19
  declare const getFirstFocusableEl: (scope: Scope) => HTMLElement;
19
20
 
20
- export { getAnchorEl, getAnchorId, getArrowId, getCloseTriggerId, getContentEl, getContentId, getDescriptionEl, getDescriptionId, getFirstFocusableEl, getFocusableEls, getPositionerEl, getPositionerId, getTitleEl, getTitleId, getTriggerEl, getTriggerId };
21
+ export { getActiveTriggerEl, getAnchorEl, getAnchorId, getArrowId, getCloseTriggerId, getContentEl, getContentId, getDescriptionEl, getDescriptionId, getFirstFocusableEl, getFocusableEls, getPositionerEl, getPositionerId, getTitleEl, getTitleId, getTriggerEls, getTriggerId };
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/popover.dom.ts
21
21
  var popover_dom_exports = {};
22
22
  __export(popover_dom_exports, {
23
+ getActiveTriggerEl: () => getActiveTriggerEl,
23
24
  getAnchorEl: () => getAnchorEl,
24
25
  getAnchorId: () => getAnchorId,
25
26
  getArrowId: () => getArrowId,
@@ -34,13 +35,18 @@ __export(popover_dom_exports, {
34
35
  getPositionerId: () => getPositionerId,
35
36
  getTitleEl: () => getTitleEl,
36
37
  getTitleId: () => getTitleId,
37
- getTriggerEl: () => getTriggerEl,
38
+ getTriggerEls: () => getTriggerEls,
38
39
  getTriggerId: () => getTriggerId
39
40
  });
40
41
  module.exports = __toCommonJS(popover_dom_exports);
41
42
  var import_dom_query = require("@zag-js/dom-query");
43
+ var import_utils = require("@zag-js/utils");
42
44
  var getAnchorId = (scope) => scope.ids?.anchor ?? `popover:${scope.id}:anchor`;
43
- var getTriggerId = (scope) => scope.ids?.trigger ?? `popover:${scope.id}:trigger`;
45
+ var getTriggerId = (scope, value) => {
46
+ const customId = scope.ids?.trigger;
47
+ if (customId != null) return (0, import_utils.isFunction)(customId) ? customId(value) : customId;
48
+ return value ? `popover:${scope.id}:trigger:${value}` : `popover:${scope.id}:trigger`;
49
+ };
44
50
  var getContentId = (scope) => scope.ids?.content ?? `popover:${scope.id}:content`;
45
51
  var getPositionerId = (scope) => scope.ids?.positioner ?? `popover:${scope.id}:popper`;
46
52
  var getArrowId = (scope) => scope.ids?.arrow ?? `popover:${scope.id}:arrow`;
@@ -48,7 +54,10 @@ var getTitleId = (scope) => scope.ids?.title ?? `popover:${scope.id}:title`;
48
54
  var getDescriptionId = (scope) => scope.ids?.description ?? `popover:${scope.id}:desc`;
49
55
  var getCloseTriggerId = (scope) => scope.ids?.closeTrigger ?? `popover:${scope.id}:close`;
50
56
  var getAnchorEl = (scope) => scope.getById(getAnchorId(scope));
51
- var getTriggerEl = (scope) => scope.getById(getTriggerId(scope));
57
+ var getTriggerEls = (scope) => (0, import_dom_query.queryAll)(scope.getDoc(), `[data-scope="popover"][data-part="trigger"][data-ownedby="${scope.id}"]`);
58
+ var getActiveTriggerEl = (scope, value) => {
59
+ return value == null ? getTriggerEls(scope)[0] : scope.getById(getTriggerId(scope, value));
60
+ };
52
61
  var getContentEl = (scope) => scope.getById(getContentId(scope));
53
62
  var getPositionerEl = (scope) => scope.getById(getPositionerId(scope));
54
63
  var getTitleEl = (scope) => scope.getById(getTitleId(scope));
@@ -57,6 +66,7 @@ var getFocusableEls = (scope) => (0, import_dom_query.getFocusables)(getContentE
57
66
  var getFirstFocusableEl = (scope) => getFocusableEls(scope)[0];
58
67
  // Annotate the CommonJS export names for ESM import in node:
59
68
  0 && (module.exports = {
69
+ getActiveTriggerEl,
60
70
  getAnchorEl,
61
71
  getAnchorId,
62
72
  getArrowId,
@@ -71,6 +81,6 @@ var getFirstFocusableEl = (scope) => getFocusableEls(scope)[0];
71
81
  getPositionerId,
72
82
  getTitleEl,
73
83
  getTitleId,
74
- getTriggerEl,
84
+ getTriggerEls,
75
85
  getTriggerId
76
86
  });
@@ -1,7 +1,12 @@
1
1
  // src/popover.dom.ts
2
- import { getFocusables } from "@zag-js/dom-query";
2
+ import { getFocusables, queryAll } from "@zag-js/dom-query";
3
+ import { isFunction } from "@zag-js/utils";
3
4
  var getAnchorId = (scope) => scope.ids?.anchor ?? `popover:${scope.id}:anchor`;
4
- var getTriggerId = (scope) => scope.ids?.trigger ?? `popover:${scope.id}:trigger`;
5
+ var getTriggerId = (scope, value) => {
6
+ const customId = scope.ids?.trigger;
7
+ if (customId != null) return isFunction(customId) ? customId(value) : customId;
8
+ return value ? `popover:${scope.id}:trigger:${value}` : `popover:${scope.id}:trigger`;
9
+ };
5
10
  var getContentId = (scope) => scope.ids?.content ?? `popover:${scope.id}:content`;
6
11
  var getPositionerId = (scope) => scope.ids?.positioner ?? `popover:${scope.id}:popper`;
7
12
  var getArrowId = (scope) => scope.ids?.arrow ?? `popover:${scope.id}:arrow`;
@@ -9,7 +14,10 @@ var getTitleId = (scope) => scope.ids?.title ?? `popover:${scope.id}:title`;
9
14
  var getDescriptionId = (scope) => scope.ids?.description ?? `popover:${scope.id}:desc`;
10
15
  var getCloseTriggerId = (scope) => scope.ids?.closeTrigger ?? `popover:${scope.id}:close`;
11
16
  var getAnchorEl = (scope) => scope.getById(getAnchorId(scope));
12
- var getTriggerEl = (scope) => scope.getById(getTriggerId(scope));
17
+ var getTriggerEls = (scope) => queryAll(scope.getDoc(), `[data-scope="popover"][data-part="trigger"][data-ownedby="${scope.id}"]`);
18
+ var getActiveTriggerEl = (scope, value) => {
19
+ return value == null ? getTriggerEls(scope)[0] : scope.getById(getTriggerId(scope, value));
20
+ };
13
21
  var getContentEl = (scope) => scope.getById(getContentId(scope));
14
22
  var getPositionerEl = (scope) => scope.getById(getPositionerId(scope));
15
23
  var getTitleEl = (scope) => scope.getById(getTitleId(scope));
@@ -17,6 +25,7 @@ var getDescriptionEl = (scope) => scope.getById(getDescriptionId(scope));
17
25
  var getFocusableEls = (scope) => getFocusables(getContentEl(scope));
18
26
  var getFirstFocusableEl = (scope) => getFocusableEls(scope)[0];
19
27
  export {
28
+ getActiveTriggerEl,
20
29
  getAnchorEl,
21
30
  getAnchorId,
22
31
  getArrowId,
@@ -31,6 +40,6 @@ export {
31
40
  getPositionerId,
32
41
  getTitleEl,
33
42
  getTitleId,
34
- getTriggerEl,
43
+ getTriggerEls,
35
44
  getTriggerId
36
45
  };
@@ -64,13 +64,23 @@ var machine = (0, import_core.createMachine)({
64
64
  const open = prop("open") || prop("defaultOpen");
65
65
  return open ? "open" : "closed";
66
66
  },
67
- context({ bindable }) {
67
+ context({ bindable, prop, scope }) {
68
68
  return {
69
69
  currentPlacement: bindable(() => ({
70
70
  defaultValue: void 0
71
71
  })),
72
72
  renderedElements: bindable(() => ({
73
73
  defaultValue: { title: true, description: true }
74
+ })),
75
+ triggerValue: bindable(() => ({
76
+ defaultValue: prop("defaultTriggerValue") ?? null,
77
+ value: prop("triggerValue"),
78
+ onChange(value) {
79
+ const onTriggerValueChange = prop("onTriggerValueChange");
80
+ if (!onTriggerValueChange) return;
81
+ const triggerElement = dom.getActiveTriggerEl(scope, value);
82
+ onTriggerValueChange({ value, triggerElement });
83
+ }
74
84
  }))
75
85
  };
76
86
  },
@@ -83,6 +93,11 @@ var machine = (0, import_core.createMachine)({
83
93
  });
84
94
  },
85
95
  entry: ["checkRenderedElements"],
96
+ on: {
97
+ "TRIGGER_VALUE.SET": {
98
+ actions: ["setTriggerValue", "reposition"]
99
+ }
100
+ },
86
101
  states: {
87
102
  closed: {
88
103
  on: {
@@ -93,21 +108,21 @@ var machine = (0, import_core.createMachine)({
93
108
  TOGGLE: [
94
109
  {
95
110
  guard: "isOpenControlled",
96
- actions: ["invokeOnOpen"]
111
+ actions: ["invokeOnOpen", "setTriggerValue"]
97
112
  },
98
113
  {
99
114
  target: "open",
100
- actions: ["invokeOnOpen", "setInitialFocus"]
115
+ actions: ["invokeOnOpen", "setTriggerValue", "setInitialFocus"]
101
116
  }
102
117
  ],
103
118
  OPEN: [
104
119
  {
105
120
  guard: "isOpenControlled",
106
- actions: ["invokeOnOpen"]
121
+ actions: ["invokeOnOpen", "setTriggerValue"]
107
122
  },
108
123
  {
109
124
  target: "open",
110
- actions: ["invokeOnOpen", "setInitialFocus"]
125
+ actions: ["invokeOnOpen", "setTriggerValue", "setInitialFocus"]
111
126
  }
112
127
  ]
113
128
  }
@@ -159,9 +174,10 @@ var machine = (0, import_core.createMachine)({
159
174
  effects: {
160
175
  trackPositioning({ context, prop, scope }) {
161
176
  context.set("currentPlacement", prop("positioning").placement);
162
- const anchorEl = dom.getAnchorEl(scope) ?? dom.getTriggerEl(scope);
177
+ const anchorEl = dom.getAnchorEl(scope);
163
178
  const getPositionerEl2 = () => dom.getPositionerEl(scope);
164
- return (0, import_popper.getPlacement)(anchorEl, getPositionerEl2, {
179
+ const getTriggerEl = () => anchorEl ?? dom.getActiveTriggerEl(scope, context.get("triggerValue"));
180
+ return (0, import_popper.getPlacement)(getTriggerEl, getPositionerEl2, {
165
181
  ...prop("positioning"),
166
182
  defer: true,
167
183
  onComplete(data) {
@@ -175,7 +191,7 @@ var machine = (0, import_core.createMachine)({
175
191
  return (0, import_dismissable.trackDismissableElement)(getContentEl2, {
176
192
  type: "popover",
177
193
  pointerBlocking: prop("modal"),
178
- exclude: dom.getTriggerEl(scope),
194
+ exclude: dom.getTriggerEls(scope),
179
195
  defer: true,
180
196
  onEscapeKeyDown(event) {
181
197
  prop("onEscapeKeyDown")?.(event);
@@ -199,11 +215,11 @@ var machine = (0, import_core.createMachine)({
199
215
  }
200
216
  });
201
217
  },
202
- proxyTabFocus({ prop, scope }) {
218
+ proxyTabFocus({ prop, scope, context }) {
203
219
  if (prop("modal") || !prop("portalled")) return;
204
220
  const getContentEl2 = () => dom.getContentEl(scope);
205
221
  return (0, import_dom_query.proxyTabFocus)(getContentEl2, {
206
- triggerElement: dom.getTriggerEl(scope),
222
+ triggerElement: dom.getActiveTriggerEl(scope, context.get("triggerValue")),
207
223
  defer: true,
208
224
  getShadowRoot: true,
209
225
  onFocus(el) {
@@ -211,9 +227,9 @@ var machine = (0, import_core.createMachine)({
211
227
  }
212
228
  });
213
229
  },
214
- hideContentBelow({ prop, scope }) {
230
+ hideContentBelow({ prop, scope, context }) {
215
231
  if (!prop("modal")) return;
216
- const getElements = () => [dom.getContentEl(scope), dom.getTriggerEl(scope)];
232
+ const getElements = () => [dom.getContentEl(scope), dom.getActiveTriggerEl(scope, context.get("triggerValue"))];
217
233
  return (0, import_aria_hidden.ariaHidden)(getElements, { defer: true });
218
234
  },
219
235
  preventScroll({ prop, scope }) {
@@ -235,9 +251,10 @@ var machine = (0, import_core.createMachine)({
235
251
  },
236
252
  actions: {
237
253
  reposition({ event, prop, scope, context }) {
238
- const anchorEl = dom.getAnchorEl(scope) ?? dom.getTriggerEl(scope);
254
+ const anchorEl = dom.getAnchorEl(scope);
239
255
  const getPositionerEl2 = () => dom.getPositionerEl(scope);
240
- (0, import_popper.getPlacement)(anchorEl, getPositionerEl2, {
256
+ const getTriggerEl = () => anchorEl ?? dom.getActiveTriggerEl(scope, context.get("triggerValue"));
257
+ (0, import_popper.getPlacement)(getTriggerEl, getPositionerEl2, {
241
258
  ...prop("positioning"),
242
259
  ...event.options,
243
260
  defer: true,
@@ -247,6 +264,10 @@ var machine = (0, import_core.createMachine)({
247
264
  }
248
265
  });
249
266
  },
267
+ setTriggerValue({ context, event }) {
268
+ if (event.value === void 0) return;
269
+ context.set("triggerValue", event.value);
270
+ },
250
271
  checkRenderedElements({ context, scope }) {
251
272
  (0, import_dom_query.raf)(() => {
252
273
  Object.assign(context.get("renderedElements"), {
@@ -266,11 +287,11 @@ var machine = (0, import_core.createMachine)({
266
287
  element?.focus({ preventScroll: true });
267
288
  });
268
289
  },
269
- setFinalFocus({ event, scope }) {
290
+ setFinalFocus({ event, scope, context }) {
270
291
  const restoreFocus = event.restoreFocus ?? event.previousEvent?.restoreFocus;
271
292
  if (restoreFocus != null && !restoreFocus) return;
272
293
  (0, import_dom_query.raf)(() => {
273
- const element = dom.getTriggerEl(scope);
294
+ const element = dom.getActiveTriggerEl(scope, context.get("triggerValue"));
274
295
  element?.focus({ preventScroll: true });
275
296
  });
276
297
  },
@@ -30,13 +30,23 @@ var machine = createMachine({
30
30
  const open = prop("open") || prop("defaultOpen");
31
31
  return open ? "open" : "closed";
32
32
  },
33
- context({ bindable }) {
33
+ context({ bindable, prop, scope }) {
34
34
  return {
35
35
  currentPlacement: bindable(() => ({
36
36
  defaultValue: void 0
37
37
  })),
38
38
  renderedElements: bindable(() => ({
39
39
  defaultValue: { title: true, description: true }
40
+ })),
41
+ triggerValue: bindable(() => ({
42
+ defaultValue: prop("defaultTriggerValue") ?? null,
43
+ value: prop("triggerValue"),
44
+ onChange(value) {
45
+ const onTriggerValueChange = prop("onTriggerValueChange");
46
+ if (!onTriggerValueChange) return;
47
+ const triggerElement = dom.getActiveTriggerEl(scope, value);
48
+ onTriggerValueChange({ value, triggerElement });
49
+ }
40
50
  }))
41
51
  };
42
52
  },
@@ -49,6 +59,11 @@ var machine = createMachine({
49
59
  });
50
60
  },
51
61
  entry: ["checkRenderedElements"],
62
+ on: {
63
+ "TRIGGER_VALUE.SET": {
64
+ actions: ["setTriggerValue", "reposition"]
65
+ }
66
+ },
52
67
  states: {
53
68
  closed: {
54
69
  on: {
@@ -59,21 +74,21 @@ var machine = createMachine({
59
74
  TOGGLE: [
60
75
  {
61
76
  guard: "isOpenControlled",
62
- actions: ["invokeOnOpen"]
77
+ actions: ["invokeOnOpen", "setTriggerValue"]
63
78
  },
64
79
  {
65
80
  target: "open",
66
- actions: ["invokeOnOpen", "setInitialFocus"]
81
+ actions: ["invokeOnOpen", "setTriggerValue", "setInitialFocus"]
67
82
  }
68
83
  ],
69
84
  OPEN: [
70
85
  {
71
86
  guard: "isOpenControlled",
72
- actions: ["invokeOnOpen"]
87
+ actions: ["invokeOnOpen", "setTriggerValue"]
73
88
  },
74
89
  {
75
90
  target: "open",
76
- actions: ["invokeOnOpen", "setInitialFocus"]
91
+ actions: ["invokeOnOpen", "setTriggerValue", "setInitialFocus"]
77
92
  }
78
93
  ]
79
94
  }
@@ -125,9 +140,10 @@ var machine = createMachine({
125
140
  effects: {
126
141
  trackPositioning({ context, prop, scope }) {
127
142
  context.set("currentPlacement", prop("positioning").placement);
128
- const anchorEl = dom.getAnchorEl(scope) ?? dom.getTriggerEl(scope);
143
+ const anchorEl = dom.getAnchorEl(scope);
129
144
  const getPositionerEl2 = () => dom.getPositionerEl(scope);
130
- return getPlacement(anchorEl, getPositionerEl2, {
145
+ const getTriggerEl = () => anchorEl ?? dom.getActiveTriggerEl(scope, context.get("triggerValue"));
146
+ return getPlacement(getTriggerEl, getPositionerEl2, {
131
147
  ...prop("positioning"),
132
148
  defer: true,
133
149
  onComplete(data) {
@@ -141,7 +157,7 @@ var machine = createMachine({
141
157
  return trackDismissableElement(getContentEl2, {
142
158
  type: "popover",
143
159
  pointerBlocking: prop("modal"),
144
- exclude: dom.getTriggerEl(scope),
160
+ exclude: dom.getTriggerEls(scope),
145
161
  defer: true,
146
162
  onEscapeKeyDown(event) {
147
163
  prop("onEscapeKeyDown")?.(event);
@@ -165,11 +181,11 @@ var machine = createMachine({
165
181
  }
166
182
  });
167
183
  },
168
- proxyTabFocus({ prop, scope }) {
184
+ proxyTabFocus({ prop, scope, context }) {
169
185
  if (prop("modal") || !prop("portalled")) return;
170
186
  const getContentEl2 = () => dom.getContentEl(scope);
171
187
  return proxyTabFocus(getContentEl2, {
172
- triggerElement: dom.getTriggerEl(scope),
188
+ triggerElement: dom.getActiveTriggerEl(scope, context.get("triggerValue")),
173
189
  defer: true,
174
190
  getShadowRoot: true,
175
191
  onFocus(el) {
@@ -177,9 +193,9 @@ var machine = createMachine({
177
193
  }
178
194
  });
179
195
  },
180
- hideContentBelow({ prop, scope }) {
196
+ hideContentBelow({ prop, scope, context }) {
181
197
  if (!prop("modal")) return;
182
- const getElements = () => [dom.getContentEl(scope), dom.getTriggerEl(scope)];
198
+ const getElements = () => [dom.getContentEl(scope), dom.getActiveTriggerEl(scope, context.get("triggerValue"))];
183
199
  return ariaHidden(getElements, { defer: true });
184
200
  },
185
201
  preventScroll({ prop, scope }) {
@@ -201,9 +217,10 @@ var machine = createMachine({
201
217
  },
202
218
  actions: {
203
219
  reposition({ event, prop, scope, context }) {
204
- const anchorEl = dom.getAnchorEl(scope) ?? dom.getTriggerEl(scope);
220
+ const anchorEl = dom.getAnchorEl(scope);
205
221
  const getPositionerEl2 = () => dom.getPositionerEl(scope);
206
- getPlacement(anchorEl, getPositionerEl2, {
222
+ const getTriggerEl = () => anchorEl ?? dom.getActiveTriggerEl(scope, context.get("triggerValue"));
223
+ getPlacement(getTriggerEl, getPositionerEl2, {
207
224
  ...prop("positioning"),
208
225
  ...event.options,
209
226
  defer: true,
@@ -213,6 +230,10 @@ var machine = createMachine({
213
230
  }
214
231
  });
215
232
  },
233
+ setTriggerValue({ context, event }) {
234
+ if (event.value === void 0) return;
235
+ context.set("triggerValue", event.value);
236
+ },
216
237
  checkRenderedElements({ context, scope }) {
217
238
  raf(() => {
218
239
  Object.assign(context.get("renderedElements"), {
@@ -232,11 +253,11 @@ var machine = createMachine({
232
253
  element?.focus({ preventScroll: true });
233
254
  });
234
255
  },
235
- setFinalFocus({ event, scope }) {
256
+ setFinalFocus({ event, scope, context }) {
236
257
  const restoreFocus = event.restoreFocus ?? event.previousEvent?.restoreFocus;
237
258
  if (restoreFocus != null && !restoreFocus) return;
238
259
  raf(() => {
239
- const element = dom.getTriggerEl(scope);
260
+ const element = dom.getActiveTriggerEl(scope, context.get("triggerValue"));
240
261
  element?.focus({ preventScroll: true });
241
262
  });
242
263
  },
@@ -30,6 +30,8 @@ var props = (0, import_types.createProps)()([
30
30
  "autoFocus",
31
31
  "closeOnEscape",
32
32
  "closeOnInteractOutside",
33
+ "defaultOpen",
34
+ "defaultTriggerValue",
33
35
  "dir",
34
36
  "getRootNode",
35
37
  "id",
@@ -41,13 +43,14 @@ var props = (0, import_types.createProps)()([
41
43
  "onInteractOutside",
42
44
  "onOpenChange",
43
45
  "onPointerDownOutside",
46
+ "onTriggerValueChange",
44
47
  "onRequestDismiss",
45
- "defaultOpen",
46
48
  "open",
47
49
  "persistentElements",
48
50
  "portalled",
49
51
  "positioning",
50
- "translations"
52
+ "translations",
53
+ "triggerValue"
51
54
  ]);
52
55
  var splitProps = (0, import_utils.createSplitProps)(props);
53
56
  // Annotate the CommonJS export names for ESM import in node:
@@ -5,6 +5,8 @@ var props = createProps()([
5
5
  "autoFocus",
6
6
  "closeOnEscape",
7
7
  "closeOnInteractOutside",
8
+ "defaultOpen",
9
+ "defaultTriggerValue",
8
10
  "dir",
9
11
  "getRootNode",
10
12
  "id",
@@ -16,13 +18,14 @@ var props = createProps()([
16
18
  "onInteractOutside",
17
19
  "onOpenChange",
18
20
  "onPointerDownOutside",
21
+ "onTriggerValueChange",
19
22
  "onRequestDismiss",
20
- "defaultOpen",
21
23
  "open",
22
24
  "persistentElements",
23
25
  "portalled",
24
26
  "positioning",
25
- "translations"
27
+ "translations",
28
+ "triggerValue"
26
29
  ]);
27
30
  var splitProps = createSplitProps(props);
28
31
  export {
@@ -7,12 +7,22 @@ import { PropTypes, RequiredBy, CommonProperties, DirectionProperty } from '@zag
7
7
  interface OpenChangeDetails {
8
8
  open: boolean;
9
9
  }
10
+ interface TriggerValueChangeDetails {
11
+ /**
12
+ * The value of the trigger
13
+ */
14
+ value: string | null;
15
+ /**
16
+ * The trigger element
17
+ */
18
+ triggerElement: HTMLElement | null;
19
+ }
10
20
  interface IntlTranslations {
11
21
  closeTriggerLabel?: string | undefined;
12
22
  }
13
23
  type ElementIds = Partial<{
14
24
  anchor: string;
15
- trigger: string;
25
+ trigger: string | ((value?: string) => string);
16
26
  content: string;
17
27
  title: string;
18
28
  description: string;
@@ -84,6 +94,19 @@ interface PopoverProps extends CommonProperties, DirectionProperty, DismissableE
84
94
  * Use when you don't need to control the open state of the popover.
85
95
  */
86
96
  defaultOpen?: boolean | undefined;
97
+ /**
98
+ * The controlled trigger value
99
+ */
100
+ triggerValue?: string | null | undefined;
101
+ /**
102
+ * The initial trigger value when rendered.
103
+ * Use when you don't need to control the trigger value.
104
+ */
105
+ defaultTriggerValue?: string | null | undefined;
106
+ /**
107
+ * Function called when the trigger value changes.
108
+ */
109
+ onTriggerValueChange?: ((details: TriggerValueChangeDetails) => void) | undefined;
87
110
  }
88
111
  type PropsWithDefault = "closeOnInteractOutside" | "closeOnEscape" | "modal" | "portalled" | "autoFocus" | "positioning" | "translations";
89
112
  type ComputedContext = Readonly<{
@@ -104,6 +127,10 @@ interface PrivateContext {
104
127
  * The computed placement (maybe different from initial placement)
105
128
  */
106
129
  currentPlacement?: Placement | undefined;
130
+ /**
131
+ * The trigger value
132
+ */
133
+ triggerValue: string | null;
107
134
  }
108
135
  interface PopoverSchema {
109
136
  props: RequiredBy<PopoverProps, PropsWithDefault>;
@@ -117,6 +144,12 @@ interface PopoverSchema {
117
144
  }
118
145
  type PopoverService = Service<PopoverSchema>;
119
146
  type PopoverMachine = Machine<PopoverSchema>;
147
+ interface TriggerProps {
148
+ /**
149
+ * The value that identifies this specific trigger
150
+ */
151
+ value?: string;
152
+ }
120
153
  interface PopoverApi<T extends PropTypes = PropTypes> {
121
154
  /**
122
155
  * Whether the popover is portalled.
@@ -130,6 +163,14 @@ interface PopoverApi<T extends PropTypes = PropTypes> {
130
163
  * Function to open or close the popover
131
164
  */
132
165
  setOpen: (open: boolean) => void;
166
+ /**
167
+ * The trigger value
168
+ */
169
+ triggerValue: string | null;
170
+ /**
171
+ * Function to set the trigger value
172
+ */
173
+ setTriggerValue: (value: string | null) => void;
133
174
  /**
134
175
  * Function to reposition the popover
135
176
  */
@@ -137,7 +178,7 @@ interface PopoverApi<T extends PropTypes = PropTypes> {
137
178
  getArrowProps: () => T["element"];
138
179
  getArrowTipProps: () => T["element"];
139
180
  getAnchorProps: () => T["element"];
140
- getTriggerProps: () => T["button"];
181
+ getTriggerProps: (props?: TriggerProps) => T["button"];
141
182
  getIndicatorProps: () => T["element"];
142
183
  getPositionerProps: () => T["element"];
143
184
  getContentProps: () => T["element"];
@@ -146,4 +187,4 @@ interface PopoverApi<T extends PropTypes = PropTypes> {
146
187
  getCloseTriggerProps: () => T["button"];
147
188
  }
148
189
 
149
- export type { ElementIds, IntlTranslations, OpenChangeDetails, PopoverApi, PopoverMachine, PopoverProps, PopoverSchema, PopoverService };
190
+ export type { ElementIds, IntlTranslations, OpenChangeDetails, PopoverApi, PopoverMachine, PopoverProps, PopoverSchema, PopoverService, TriggerProps, TriggerValueChangeDetails };
@@ -7,12 +7,22 @@ import { PropTypes, RequiredBy, CommonProperties, DirectionProperty } from '@zag
7
7
  interface OpenChangeDetails {
8
8
  open: boolean;
9
9
  }
10
+ interface TriggerValueChangeDetails {
11
+ /**
12
+ * The value of the trigger
13
+ */
14
+ value: string | null;
15
+ /**
16
+ * The trigger element
17
+ */
18
+ triggerElement: HTMLElement | null;
19
+ }
10
20
  interface IntlTranslations {
11
21
  closeTriggerLabel?: string | undefined;
12
22
  }
13
23
  type ElementIds = Partial<{
14
24
  anchor: string;
15
- trigger: string;
25
+ trigger: string | ((value?: string) => string);
16
26
  content: string;
17
27
  title: string;
18
28
  description: string;
@@ -84,6 +94,19 @@ interface PopoverProps extends CommonProperties, DirectionProperty, DismissableE
84
94
  * Use when you don't need to control the open state of the popover.
85
95
  */
86
96
  defaultOpen?: boolean | undefined;
97
+ /**
98
+ * The controlled trigger value
99
+ */
100
+ triggerValue?: string | null | undefined;
101
+ /**
102
+ * The initial trigger value when rendered.
103
+ * Use when you don't need to control the trigger value.
104
+ */
105
+ defaultTriggerValue?: string | null | undefined;
106
+ /**
107
+ * Function called when the trigger value changes.
108
+ */
109
+ onTriggerValueChange?: ((details: TriggerValueChangeDetails) => void) | undefined;
87
110
  }
88
111
  type PropsWithDefault = "closeOnInteractOutside" | "closeOnEscape" | "modal" | "portalled" | "autoFocus" | "positioning" | "translations";
89
112
  type ComputedContext = Readonly<{
@@ -104,6 +127,10 @@ interface PrivateContext {
104
127
  * The computed placement (maybe different from initial placement)
105
128
  */
106
129
  currentPlacement?: Placement | undefined;
130
+ /**
131
+ * The trigger value
132
+ */
133
+ triggerValue: string | null;
107
134
  }
108
135
  interface PopoverSchema {
109
136
  props: RequiredBy<PopoverProps, PropsWithDefault>;
@@ -117,6 +144,12 @@ interface PopoverSchema {
117
144
  }
118
145
  type PopoverService = Service<PopoverSchema>;
119
146
  type PopoverMachine = Machine<PopoverSchema>;
147
+ interface TriggerProps {
148
+ /**
149
+ * The value that identifies this specific trigger
150
+ */
151
+ value?: string;
152
+ }
120
153
  interface PopoverApi<T extends PropTypes = PropTypes> {
121
154
  /**
122
155
  * Whether the popover is portalled.
@@ -130,6 +163,14 @@ interface PopoverApi<T extends PropTypes = PropTypes> {
130
163
  * Function to open or close the popover
131
164
  */
132
165
  setOpen: (open: boolean) => void;
166
+ /**
167
+ * The trigger value
168
+ */
169
+ triggerValue: string | null;
170
+ /**
171
+ * Function to set the trigger value
172
+ */
173
+ setTriggerValue: (value: string | null) => void;
133
174
  /**
134
175
  * Function to reposition the popover
135
176
  */
@@ -137,7 +178,7 @@ interface PopoverApi<T extends PropTypes = PropTypes> {
137
178
  getArrowProps: () => T["element"];
138
179
  getArrowTipProps: () => T["element"];
139
180
  getAnchorProps: () => T["element"];
140
- getTriggerProps: () => T["button"];
181
+ getTriggerProps: (props?: TriggerProps) => T["button"];
141
182
  getIndicatorProps: () => T["element"];
142
183
  getPositionerProps: () => T["element"];
143
184
  getContentProps: () => T["element"];
@@ -146,4 +187,4 @@ interface PopoverApi<T extends PropTypes = PropTypes> {
146
187
  getCloseTriggerProps: () => T["button"];
147
188
  }
148
189
 
149
- export type { ElementIds, IntlTranslations, OpenChangeDetails, PopoverApi, PopoverMachine, PopoverProps, PopoverSchema, PopoverService };
190
+ export type { ElementIds, IntlTranslations, OpenChangeDetails, PopoverApi, PopoverMachine, PopoverProps, PopoverSchema, PopoverService, TriggerProps, TriggerValueChangeDetails };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/popover",
3
- "version": "1.38.1",
3
+ "version": "1.39.0",
4
4
  "description": "Core logic for the popover widget implemented as a state machine",
5
5
  "keywords": [
6
6
  "js",
@@ -26,16 +26,16 @@
26
26
  "url": "https://github.com/chakra-ui/zag/issues"
27
27
  },
28
28
  "dependencies": {
29
- "@zag-js/anatomy": "1.38.1",
30
- "@zag-js/aria-hidden": "1.38.1",
31
- "@zag-js/core": "1.38.1",
32
- "@zag-js/dom-query": "1.38.1",
33
- "@zag-js/utils": "1.38.1",
34
- "@zag-js/dismissable": "1.38.1",
35
- "@zag-js/popper": "1.38.1",
36
- "@zag-js/remove-scroll": "1.38.1",
37
- "@zag-js/types": "1.38.1",
38
- "@zag-js/focus-trap": "1.38.1"
29
+ "@zag-js/anatomy": "1.39.0",
30
+ "@zag-js/aria-hidden": "1.39.0",
31
+ "@zag-js/core": "1.39.0",
32
+ "@zag-js/dom-query": "1.39.0",
33
+ "@zag-js/utils": "1.39.0",
34
+ "@zag-js/dismissable": "1.39.0",
35
+ "@zag-js/popper": "1.39.0",
36
+ "@zag-js/remove-scroll": "1.39.0",
37
+ "@zag-js/types": "1.39.0",
38
+ "@zag-js/focus-trap": "1.39.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "clean-package": "2.2.0"