@zag-js/popover 2.0.0-next.0 → 2.0.0-next.1

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, TriggerProps, TriggerValueChangeDetails } from './popover.types.mjs';
6
+ export { PopoverApi as Api, ContentState, ElementIds, IntlTranslations, PopoverMachine as Machine, OpenChangeDetails, PositionerState, PopoverProps as Props, PopoverService as Service, TriggerProps, TriggerState, 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, TriggerProps, TriggerValueChangeDetails } from './popover.types.js';
6
+ export { PopoverApi as Api, ContentState, ElementIds, IntlTranslations, PopoverMachine as Machine, OpenChangeDetails, PositionerState, PopoverProps as Props, PopoverService as Service, TriggerProps, TriggerState, 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';
@@ -33,26 +33,43 @@ __export(popover_connect_exports, {
33
33
  connect: () => connect
34
34
  });
35
35
  module.exports = __toCommonJS(popover_connect_exports);
36
+ var import_dismissable = require("@zag-js/dismissable");
36
37
  var import_dom_query = require("@zag-js/dom-query");
37
38
  var import_popper = require("@zag-js/popper");
38
39
  var import_popover = require("./popover.anatomy.js");
39
40
  var dom = __toESM(require("./popover.dom.js"));
40
41
  function connect(service, normalize) {
41
- const { state, context, send, computed, prop, scope } = service;
42
+ const { state, context, send, prop, scope } = service;
43
+ const layer = context.get("layer");
42
44
  const translations = prop("translations");
43
45
  const open = state.matches("open");
44
46
  const currentPlacement = context.get("currentPlacement");
45
47
  const currentPlacementSide = currentPlacement ? (0, import_popper.getPlacementSide)(currentPlacement) : void 0;
46
- const portalled = computed("currentPortalled");
47
48
  const rendered = context.get("renderedElements");
48
49
  const triggerValue = context.get("triggerValue");
49
50
  const popperStyles = (0, import_popper.getPlacementStyles)({
50
51
  ...prop("positioning"),
51
- placement: currentPlacement,
52
- positioned: context.get("positioned")
52
+ placement: currentPlacement
53
53
  });
54
+ function getTriggerState(props = {}) {
55
+ const { value } = props;
56
+ const current = value == null ? false : triggerValue === value;
57
+ return { value, current, open: value == null ? open : open && current };
58
+ }
59
+ function getPositionerState() {
60
+ return { nested: !!layer?.nested, hasNested: !!layer?.hasNested };
61
+ }
62
+ function getContentState() {
63
+ return {
64
+ open,
65
+ modal: !!prop("modal"),
66
+ nested: !!layer?.nested,
67
+ hasNested: !!layer?.hasNested,
68
+ placement: currentPlacement,
69
+ side: currentPlacementSide
70
+ };
71
+ }
54
72
  return {
55
- portalled,
56
73
  open,
57
74
  setOpen(nextOpen) {
58
75
  const open2 = state.matches("open");
@@ -86,9 +103,11 @@ function connect(service, normalize) {
86
103
  dir: prop("dir")
87
104
  });
88
105
  },
106
+ getTriggerState,
89
107
  getTriggerProps(props = {}) {
90
108
  const { value } = props;
91
- const current = value == null ? false : triggerValue === value;
109
+ const triggerState = getTriggerState(props);
110
+ const { current } = triggerState;
92
111
  return normalize.button({
93
112
  ...import_popover.parts.trigger.attrs(scope.id),
94
113
  dir: prop("dir"),
@@ -98,7 +117,7 @@ function connect(service, normalize) {
98
117
  "data-value": value,
99
118
  "data-current": (0, import_dom_query.dataAttr)(current),
100
119
  "aria-haspopup": "dialog",
101
- "aria-expanded": value == null ? open : open && current,
120
+ "aria-expanded": triggerState.open,
102
121
  "data-state": open ? "open" : "closed",
103
122
  "aria-controls": dom.getContentId(scope),
104
123
  onPointerDown(event) {
@@ -124,28 +143,37 @@ function connect(service, normalize) {
124
143
  "data-state": open ? "open" : "closed"
125
144
  });
126
145
  },
146
+ getPositionerState,
127
147
  getPositionerProps() {
128
148
  return normalize.element({
129
149
  ...import_popover.parts.positioner.attrs(scope.id),
130
150
  dir: prop("dir"),
131
- style: popperStyles.floating
151
+ ...(0, import_dismissable.getDismissableLayerAttrs)(layer),
152
+ style: {
153
+ ...popperStyles.floating,
154
+ ...(0, import_dismissable.getDismissableLayerStyle)(layer, { zIndex: true })
155
+ }
132
156
  });
133
157
  },
158
+ getContentState,
134
159
  getContentProps() {
160
+ const contentState = getContentState();
135
161
  return normalize.element({
136
162
  ...import_popover.parts.content.attrs(scope.id),
137
163
  dir: prop("dir"),
138
164
  id: dom.getContentId(scope),
139
165
  tabIndex: -1,
140
166
  role: "dialog",
141
- "aria-modal": (0, import_dom_query.ariaAttr)(prop("modal")),
142
- hidden: !open,
143
- "data-state": open ? "open" : "closed",
144
- "data-expanded": (0, import_dom_query.dataAttr)(open),
167
+ "aria-modal": (0, import_dom_query.ariaAttr)(contentState.modal),
168
+ hidden: !contentState.open,
169
+ "data-state": contentState.open ? "open" : "closed",
170
+ "data-expanded": (0, import_dom_query.dataAttr)(contentState.open),
145
171
  "aria-labelledby": rendered.title ? dom.getTitleId(scope) : void 0,
146
172
  "aria-describedby": rendered.description ? dom.getDescriptionId(scope) : void 0,
147
- "data-placement": currentPlacement,
148
- "data-side": currentPlacementSide
173
+ "data-placement": contentState.placement,
174
+ "data-side": contentState.side,
175
+ ...(0, import_dismissable.getDismissableLayerAttrs)(layer),
176
+ style: (0, import_dismissable.getDismissableLayerStyle)(layer, { pointerEvents: true })
149
177
  });
150
178
  },
151
179
  getTitleProps() {
@@ -1,24 +1,41 @@
1
1
  // src/popover.connect.ts
2
+ import { getDismissableLayerAttrs, getDismissableLayerStyle } from "@zag-js/dismissable";
2
3
  import { ariaAttr, dataAttr, isLeftClick, isSafari } from "@zag-js/dom-query";
3
4
  import { getPlacementSide, getPlacementStyles } from "@zag-js/popper";
4
5
  import { parts } from "./popover.anatomy.mjs";
5
6
  import * as dom from "./popover.dom.mjs";
6
7
  function connect(service, normalize) {
7
- const { state, context, send, computed, prop, scope } = service;
8
+ const { state, context, send, prop, scope } = service;
9
+ const layer = context.get("layer");
8
10
  const translations = prop("translations");
9
11
  const open = state.matches("open");
10
12
  const currentPlacement = context.get("currentPlacement");
11
13
  const currentPlacementSide = currentPlacement ? getPlacementSide(currentPlacement) : void 0;
12
- const portalled = computed("currentPortalled");
13
14
  const rendered = context.get("renderedElements");
14
15
  const triggerValue = context.get("triggerValue");
15
16
  const popperStyles = getPlacementStyles({
16
17
  ...prop("positioning"),
17
- placement: currentPlacement,
18
- positioned: context.get("positioned")
18
+ placement: currentPlacement
19
19
  });
20
+ function getTriggerState(props = {}) {
21
+ const { value } = props;
22
+ const current = value == null ? false : triggerValue === value;
23
+ return { value, current, open: value == null ? open : open && current };
24
+ }
25
+ function getPositionerState() {
26
+ return { nested: !!layer?.nested, hasNested: !!layer?.hasNested };
27
+ }
28
+ function getContentState() {
29
+ return {
30
+ open,
31
+ modal: !!prop("modal"),
32
+ nested: !!layer?.nested,
33
+ hasNested: !!layer?.hasNested,
34
+ placement: currentPlacement,
35
+ side: currentPlacementSide
36
+ };
37
+ }
20
38
  return {
21
- portalled,
22
39
  open,
23
40
  setOpen(nextOpen) {
24
41
  const open2 = state.matches("open");
@@ -52,9 +69,11 @@ function connect(service, normalize) {
52
69
  dir: prop("dir")
53
70
  });
54
71
  },
72
+ getTriggerState,
55
73
  getTriggerProps(props = {}) {
56
74
  const { value } = props;
57
- const current = value == null ? false : triggerValue === value;
75
+ const triggerState = getTriggerState(props);
76
+ const { current } = triggerState;
58
77
  return normalize.button({
59
78
  ...parts.trigger.attrs(scope.id),
60
79
  dir: prop("dir"),
@@ -64,7 +83,7 @@ function connect(service, normalize) {
64
83
  "data-value": value,
65
84
  "data-current": dataAttr(current),
66
85
  "aria-haspopup": "dialog",
67
- "aria-expanded": value == null ? open : open && current,
86
+ "aria-expanded": triggerState.open,
68
87
  "data-state": open ? "open" : "closed",
69
88
  "aria-controls": dom.getContentId(scope),
70
89
  onPointerDown(event) {
@@ -90,28 +109,37 @@ function connect(service, normalize) {
90
109
  "data-state": open ? "open" : "closed"
91
110
  });
92
111
  },
112
+ getPositionerState,
93
113
  getPositionerProps() {
94
114
  return normalize.element({
95
115
  ...parts.positioner.attrs(scope.id),
96
116
  dir: prop("dir"),
97
- style: popperStyles.floating
117
+ ...getDismissableLayerAttrs(layer),
118
+ style: {
119
+ ...popperStyles.floating,
120
+ ...getDismissableLayerStyle(layer, { zIndex: true })
121
+ }
98
122
  });
99
123
  },
124
+ getContentState,
100
125
  getContentProps() {
126
+ const contentState = getContentState();
101
127
  return normalize.element({
102
128
  ...parts.content.attrs(scope.id),
103
129
  dir: prop("dir"),
104
130
  id: dom.getContentId(scope),
105
131
  tabIndex: -1,
106
132
  role: "dialog",
107
- "aria-modal": ariaAttr(prop("modal")),
108
- hidden: !open,
109
- "data-state": open ? "open" : "closed",
110
- "data-expanded": dataAttr(open),
133
+ "aria-modal": ariaAttr(contentState.modal),
134
+ hidden: !contentState.open,
135
+ "data-state": contentState.open ? "open" : "closed",
136
+ "data-expanded": dataAttr(contentState.open),
111
137
  "aria-labelledby": rendered.title ? dom.getTitleId(scope) : void 0,
112
138
  "aria-describedby": rendered.description ? dom.getDescriptionId(scope) : void 0,
113
- "data-placement": currentPlacement,
114
- "data-side": currentPlacementSide
139
+ "data-placement": contentState.placement,
140
+ "data-side": contentState.side,
141
+ ...getDismissableLayerAttrs(layer),
142
+ style: getDismissableLayerStyle(layer, { pointerEvents: true })
115
143
  });
116
144
  },
117
145
  getTitleProps() {
@@ -48,7 +48,6 @@ var machine = (0, import_core.createMachine)({
48
48
  closeOnEscape: true,
49
49
  autoFocus: true,
50
50
  modal: false,
51
- portalled: true,
52
51
  restoreFocus: true,
53
52
  ...props,
54
53
  translations: {
@@ -67,6 +66,9 @@ var machine = (0, import_core.createMachine)({
67
66
  },
68
67
  context({ bindable, prop, scope }) {
69
68
  return {
69
+ layer: bindable(() => ({
70
+ defaultValue: null
71
+ })),
70
72
  currentPlacement: bindable(() => ({
71
73
  defaultValue: void 0
72
74
  })),
@@ -82,13 +84,9 @@ var machine = (0, import_core.createMachine)({
82
84
  const triggerElement = dom.getActiveTriggerEl(scope, value);
83
85
  onTriggerValueChange({ value, triggerElement });
84
86
  }
85
- })),
86
- positioned: bindable(() => ({ defaultValue: false }))
87
+ }))
87
88
  };
88
89
  },
89
- computed: {
90
- currentPortalled: ({ prop }) => !!prop("modal") || !!prop("portalled")
91
- },
92
90
  watch({ track, prop, action }) {
93
91
  track([() => prop("open")], () => {
94
92
  action(["toggleVisibility"]);
@@ -184,16 +182,18 @@ var machine = (0, import_core.createMachine)({
184
182
  defer: true,
185
183
  onComplete(data) {
186
184
  context.set("currentPlacement", data.placement);
187
- context.set("positioned", true);
188
185
  }
189
186
  });
190
187
  },
191
- trackDismissableElement({ send, prop, scope }) {
188
+ trackDismissableElement({ send, prop, scope, context }) {
192
189
  const getContentEl2 = () => dom.getContentEl(scope);
193
190
  let restoreFocus = true;
194
191
  return (0, import_dismissable.trackDismissableElement)(getContentEl2, {
195
192
  type: "popover",
196
193
  pointerBlocking: prop("modal"),
194
+ onLayerChange(layer) {
195
+ context.set("layer", layer);
196
+ },
197
197
  exclude: dom.getTriggerEls(scope),
198
198
  defer: true,
199
199
  onEscapeKeyDown(event) {
@@ -219,16 +219,25 @@ var machine = (0, import_core.createMachine)({
219
219
  });
220
220
  },
221
221
  proxyTabFocus({ prop, scope, context }) {
222
- if (prop("modal") || !prop("portalled")) return;
222
+ if (prop("modal")) return;
223
223
  const getContentEl2 = () => dom.getContentEl(scope);
224
- return (0, import_dom_query.proxyTabFocus)(getContentEl2, {
225
- triggerElement: dom.getActiveTriggerEl(scope, context.get("triggerValue")),
226
- defer: true,
227
- getShadowRoot: true,
228
- onFocus(el) {
229
- el.focus({ preventScroll: true });
230
- }
224
+ let cleanup;
225
+ const rafCleanup = (0, import_dom_query.raf)(() => {
226
+ const triggerEl = dom.getActiveTriggerEl(scope, context.get("triggerValue"));
227
+ const contentEl = getContentEl2();
228
+ if (!triggerEl || !contentEl || (0, import_dom_query.contains)(triggerEl.parentElement, contentEl)) return;
229
+ cleanup = (0, import_dom_query.proxyTabFocus)(getContentEl2, {
230
+ triggerElement: triggerEl,
231
+ getShadowRoot: true,
232
+ onFocus(el) {
233
+ el.focus({ preventScroll: true });
234
+ }
235
+ });
231
236
  });
237
+ return () => {
238
+ rafCleanup();
239
+ cleanup?.();
240
+ };
232
241
  },
233
242
  hideContentBelow({ prop, scope, context }) {
234
243
  if (!prop("modal")) return;
@@ -2,7 +2,7 @@
2
2
  import { ariaHidden } from "@zag-js/aria-hidden";
3
3
  import { createMachine } from "@zag-js/core";
4
4
  import { trackDismissableElement } from "@zag-js/dismissable";
5
- import { getInitialFocus, proxyTabFocus, raf } from "@zag-js/dom-query";
5
+ import { contains, getInitialFocus, proxyTabFocus, raf } from "@zag-js/dom-query";
6
6
  import { trapFocus } from "@zag-js/focus-trap";
7
7
  import { getPlacement } from "@zag-js/popper";
8
8
  import { preventBodyScroll } from "@zag-js/remove-scroll";
@@ -14,7 +14,6 @@ var machine = createMachine({
14
14
  closeOnEscape: true,
15
15
  autoFocus: true,
16
16
  modal: false,
17
- portalled: true,
18
17
  restoreFocus: true,
19
18
  ...props,
20
19
  translations: {
@@ -33,6 +32,9 @@ var machine = createMachine({
33
32
  },
34
33
  context({ bindable, prop, scope }) {
35
34
  return {
35
+ layer: bindable(() => ({
36
+ defaultValue: null
37
+ })),
36
38
  currentPlacement: bindable(() => ({
37
39
  defaultValue: void 0
38
40
  })),
@@ -48,13 +50,9 @@ var machine = createMachine({
48
50
  const triggerElement = dom.getActiveTriggerEl(scope, value);
49
51
  onTriggerValueChange({ value, triggerElement });
50
52
  }
51
- })),
52
- positioned: bindable(() => ({ defaultValue: false }))
53
+ }))
53
54
  };
54
55
  },
55
- computed: {
56
- currentPortalled: ({ prop }) => !!prop("modal") || !!prop("portalled")
57
- },
58
56
  watch({ track, prop, action }) {
59
57
  track([() => prop("open")], () => {
60
58
  action(["toggleVisibility"]);
@@ -150,16 +148,18 @@ var machine = createMachine({
150
148
  defer: true,
151
149
  onComplete(data) {
152
150
  context.set("currentPlacement", data.placement);
153
- context.set("positioned", true);
154
151
  }
155
152
  });
156
153
  },
157
- trackDismissableElement({ send, prop, scope }) {
154
+ trackDismissableElement({ send, prop, scope, context }) {
158
155
  const getContentEl2 = () => dom.getContentEl(scope);
159
156
  let restoreFocus = true;
160
157
  return trackDismissableElement(getContentEl2, {
161
158
  type: "popover",
162
159
  pointerBlocking: prop("modal"),
160
+ onLayerChange(layer) {
161
+ context.set("layer", layer);
162
+ },
163
163
  exclude: dom.getTriggerEls(scope),
164
164
  defer: true,
165
165
  onEscapeKeyDown(event) {
@@ -185,16 +185,25 @@ var machine = createMachine({
185
185
  });
186
186
  },
187
187
  proxyTabFocus({ prop, scope, context }) {
188
- if (prop("modal") || !prop("portalled")) return;
188
+ if (prop("modal")) return;
189
189
  const getContentEl2 = () => dom.getContentEl(scope);
190
- return proxyTabFocus(getContentEl2, {
191
- triggerElement: dom.getActiveTriggerEl(scope, context.get("triggerValue")),
192
- defer: true,
193
- getShadowRoot: true,
194
- onFocus(el) {
195
- el.focus({ preventScroll: true });
196
- }
190
+ let cleanup;
191
+ const rafCleanup = raf(() => {
192
+ const triggerEl = dom.getActiveTriggerEl(scope, context.get("triggerValue"));
193
+ const contentEl = getContentEl2();
194
+ if (!triggerEl || !contentEl || contains(triggerEl.parentElement, contentEl)) return;
195
+ cleanup = proxyTabFocus(getContentEl2, {
196
+ triggerElement: triggerEl,
197
+ getShadowRoot: true,
198
+ onFocus(el) {
199
+ el.focus({ preventScroll: true });
200
+ }
201
+ });
197
202
  });
203
+ return () => {
204
+ rafCleanup();
205
+ cleanup?.();
206
+ };
198
207
  },
199
208
  hideContentBelow({ prop, scope, context }) {
200
209
  if (!prop("modal")) return;
@@ -48,7 +48,6 @@ var props = (0, import_types.createProps)()([
48
48
  "onRequestDismiss",
49
49
  "open",
50
50
  "persistentElements",
51
- "portalled",
52
51
  "restoreFocus",
53
52
  "positioning",
54
53
  "translations",
@@ -23,7 +23,6 @@ var props = createProps()([
23
23
  "onRequestDismiss",
24
24
  "open",
25
25
  "persistentElements",
26
- "portalled",
27
26
  "restoreFocus",
28
27
  "positioning",
29
28
  "translations",
@@ -1,6 +1,6 @@
1
1
  import { Machine, EventObject, Service } from '@zag-js/core';
2
- import { DismissableElementHandlers, PersistentElementOptions } from '@zag-js/dismissable';
3
- import { PositioningOptions, Placement } from '@zag-js/popper';
2
+ import { DismissableElementHandlers, PersistentElementOptions, LayerSnapshot } from '@zag-js/dismissable';
3
+ import { PositioningOptions, Placement, PlacementSide } from '@zag-js/popper';
4
4
  export { Placement, PositioningOptions } from '@zag-js/popper';
5
5
  import { PropTypes, RequiredBy, CommonProperties, DirectionProperty, MaybeElement } from '@zag-js/types';
6
6
 
@@ -49,13 +49,6 @@ interface PopoverProps extends CommonProperties, DirectionProperty, DismissableE
49
49
  * @default false
50
50
  */
51
51
  modal?: boolean | undefined;
52
- /**
53
- * Whether the popover is portalled. This will proxy the tabbing behavior regardless of the DOM position
54
- * of the popover content.
55
- *
56
- * @default true
57
- */
58
- portalled?: boolean | undefined;
59
52
  /**
60
53
  * Whether to automatically set focus on the first focusable
61
54
  * content within the popover when opened.
@@ -118,14 +111,12 @@ interface PopoverProps extends CommonProperties, DirectionProperty, DismissableE
118
111
  */
119
112
  onTriggerValueChange?: ((details: TriggerValueChangeDetails) => void) | undefined;
120
113
  }
121
- type PropsWithDefault = "closeOnInteractOutside" | "closeOnEscape" | "modal" | "portalled" | "autoFocus" | "restoreFocus" | "positioning" | "translations";
122
- type ComputedContext = Readonly<{
114
+ type PropsWithDefault = "closeOnInteractOutside" | "closeOnEscape" | "modal" | "autoFocus" | "restoreFocus" | "positioning" | "translations";
115
+ interface PrivateContext {
123
116
  /**
124
- * The computed value of `portalled`
117
+ * The computed layer stack state used for declarative styles and attributes.
125
118
  */
126
- currentPortalled: boolean;
127
- }>;
128
- interface PrivateContext {
119
+ layer: LayerSnapshot | null;
129
120
  /**
130
121
  * The elements that are rendered on mount
131
122
  */
@@ -141,16 +132,12 @@ interface PrivateContext {
141
132
  * The trigger value
142
133
  */
143
134
  triggerValue: string | null;
144
- /**
145
- * Whether the popover has been positioned
146
- */
147
- positioned: boolean;
148
135
  }
149
136
  interface PopoverSchema {
150
137
  props: RequiredBy<PopoverProps, PropsWithDefault>;
151
138
  state: "open" | "closed";
152
139
  context: PrivateContext;
153
- computed: ComputedContext;
140
+ computed: {};
154
141
  event: EventObject;
155
142
  action: string;
156
143
  effect: string;
@@ -164,11 +151,57 @@ interface TriggerProps {
164
151
  */
165
152
  value?: string;
166
153
  }
167
- interface PopoverApi<T extends PropTypes = PropTypes> {
154
+ interface TriggerState {
168
155
  /**
169
- * Whether the popover is portalled.
156
+ * The value that identifies this specific trigger
170
157
  */
171
- portalled: boolean;
158
+ value: string | undefined;
159
+ /**
160
+ * Whether this trigger is the one that opened the popover
161
+ */
162
+ current: boolean;
163
+ /**
164
+ * Whether the popover is open
165
+ */
166
+ open: boolean;
167
+ }
168
+ interface PositionerState {
169
+ /**
170
+ * Whether the popover is nested within another layered element
171
+ */
172
+ nested: boolean;
173
+ /**
174
+ * Whether the popover has nested layered elements within it
175
+ */
176
+ hasNested: boolean;
177
+ }
178
+ interface ContentState {
179
+ /**
180
+ * Whether the popover is open
181
+ */
182
+ open: boolean;
183
+ /**
184
+ * Whether the popover is modal
185
+ */
186
+ modal: boolean;
187
+ /**
188
+ * Whether the popover is nested within another layered element
189
+ */
190
+ nested: boolean;
191
+ /**
192
+ * Whether the popover has nested layered elements within it
193
+ */
194
+ hasNested: boolean;
195
+ /**
196
+ * The current placement of the content relative to the trigger
197
+ */
198
+ placement: Placement | undefined;
199
+ /**
200
+ * The side of the trigger the content is placed on
201
+ */
202
+ side: PlacementSide | undefined;
203
+ }
204
+ interface PopoverApi<T extends PropTypes = PropTypes> {
172
205
  /**
173
206
  * Whether the popover is open
174
207
  */
@@ -192,13 +225,25 @@ interface PopoverApi<T extends PropTypes = PropTypes> {
192
225
  getArrowProps: () => T["element"];
193
226
  getArrowTipProps: () => T["element"];
194
227
  getAnchorProps: () => T["element"];
228
+ /**
229
+ * Returns the state of a specific trigger, including whether it's the currently active one
230
+ */
231
+ getTriggerState: (props?: TriggerProps) => TriggerState;
195
232
  getTriggerProps: (props?: TriggerProps) => T["button"];
196
233
  getIndicatorProps: () => T["element"];
234
+ /**
235
+ * Returns the state of the positioner
236
+ */
237
+ getPositionerState: () => PositionerState;
197
238
  getPositionerProps: () => T["element"];
239
+ /**
240
+ * Returns the state of the content
241
+ */
242
+ getContentState: () => ContentState;
198
243
  getContentProps: () => T["element"];
199
244
  getTitleProps: () => T["element"];
200
245
  getDescriptionProps: () => T["element"];
201
246
  getCloseTriggerProps: () => T["button"];
202
247
  }
203
248
 
204
- export type { ElementIds, IntlTranslations, OpenChangeDetails, PopoverApi, PopoverMachine, PopoverProps, PopoverSchema, PopoverService, TriggerProps, TriggerValueChangeDetails };
249
+ export type { ContentState, ElementIds, IntlTranslations, OpenChangeDetails, PopoverApi, PopoverMachine, PopoverProps, PopoverSchema, PopoverService, PositionerState, TriggerProps, TriggerState, TriggerValueChangeDetails };
@@ -1,6 +1,6 @@
1
1
  import { Machine, EventObject, Service } from '@zag-js/core';
2
- import { DismissableElementHandlers, PersistentElementOptions } from '@zag-js/dismissable';
3
- import { PositioningOptions, Placement } from '@zag-js/popper';
2
+ import { DismissableElementHandlers, PersistentElementOptions, LayerSnapshot } from '@zag-js/dismissable';
3
+ import { PositioningOptions, Placement, PlacementSide } from '@zag-js/popper';
4
4
  export { Placement, PositioningOptions } from '@zag-js/popper';
5
5
  import { PropTypes, RequiredBy, CommonProperties, DirectionProperty, MaybeElement } from '@zag-js/types';
6
6
 
@@ -49,13 +49,6 @@ interface PopoverProps extends CommonProperties, DirectionProperty, DismissableE
49
49
  * @default false
50
50
  */
51
51
  modal?: boolean | undefined;
52
- /**
53
- * Whether the popover is portalled. This will proxy the tabbing behavior regardless of the DOM position
54
- * of the popover content.
55
- *
56
- * @default true
57
- */
58
- portalled?: boolean | undefined;
59
52
  /**
60
53
  * Whether to automatically set focus on the first focusable
61
54
  * content within the popover when opened.
@@ -118,14 +111,12 @@ interface PopoverProps extends CommonProperties, DirectionProperty, DismissableE
118
111
  */
119
112
  onTriggerValueChange?: ((details: TriggerValueChangeDetails) => void) | undefined;
120
113
  }
121
- type PropsWithDefault = "closeOnInteractOutside" | "closeOnEscape" | "modal" | "portalled" | "autoFocus" | "restoreFocus" | "positioning" | "translations";
122
- type ComputedContext = Readonly<{
114
+ type PropsWithDefault = "closeOnInteractOutside" | "closeOnEscape" | "modal" | "autoFocus" | "restoreFocus" | "positioning" | "translations";
115
+ interface PrivateContext {
123
116
  /**
124
- * The computed value of `portalled`
117
+ * The computed layer stack state used for declarative styles and attributes.
125
118
  */
126
- currentPortalled: boolean;
127
- }>;
128
- interface PrivateContext {
119
+ layer: LayerSnapshot | null;
129
120
  /**
130
121
  * The elements that are rendered on mount
131
122
  */
@@ -141,16 +132,12 @@ interface PrivateContext {
141
132
  * The trigger value
142
133
  */
143
134
  triggerValue: string | null;
144
- /**
145
- * Whether the popover has been positioned
146
- */
147
- positioned: boolean;
148
135
  }
149
136
  interface PopoverSchema {
150
137
  props: RequiredBy<PopoverProps, PropsWithDefault>;
151
138
  state: "open" | "closed";
152
139
  context: PrivateContext;
153
- computed: ComputedContext;
140
+ computed: {};
154
141
  event: EventObject;
155
142
  action: string;
156
143
  effect: string;
@@ -164,11 +151,57 @@ interface TriggerProps {
164
151
  */
165
152
  value?: string;
166
153
  }
167
- interface PopoverApi<T extends PropTypes = PropTypes> {
154
+ interface TriggerState {
168
155
  /**
169
- * Whether the popover is portalled.
156
+ * The value that identifies this specific trigger
170
157
  */
171
- portalled: boolean;
158
+ value: string | undefined;
159
+ /**
160
+ * Whether this trigger is the one that opened the popover
161
+ */
162
+ current: boolean;
163
+ /**
164
+ * Whether the popover is open
165
+ */
166
+ open: boolean;
167
+ }
168
+ interface PositionerState {
169
+ /**
170
+ * Whether the popover is nested within another layered element
171
+ */
172
+ nested: boolean;
173
+ /**
174
+ * Whether the popover has nested layered elements within it
175
+ */
176
+ hasNested: boolean;
177
+ }
178
+ interface ContentState {
179
+ /**
180
+ * Whether the popover is open
181
+ */
182
+ open: boolean;
183
+ /**
184
+ * Whether the popover is modal
185
+ */
186
+ modal: boolean;
187
+ /**
188
+ * Whether the popover is nested within another layered element
189
+ */
190
+ nested: boolean;
191
+ /**
192
+ * Whether the popover has nested layered elements within it
193
+ */
194
+ hasNested: boolean;
195
+ /**
196
+ * The current placement of the content relative to the trigger
197
+ */
198
+ placement: Placement | undefined;
199
+ /**
200
+ * The side of the trigger the content is placed on
201
+ */
202
+ side: PlacementSide | undefined;
203
+ }
204
+ interface PopoverApi<T extends PropTypes = PropTypes> {
172
205
  /**
173
206
  * Whether the popover is open
174
207
  */
@@ -192,13 +225,25 @@ interface PopoverApi<T extends PropTypes = PropTypes> {
192
225
  getArrowProps: () => T["element"];
193
226
  getArrowTipProps: () => T["element"];
194
227
  getAnchorProps: () => T["element"];
228
+ /**
229
+ * Returns the state of a specific trigger, including whether it's the currently active one
230
+ */
231
+ getTriggerState: (props?: TriggerProps) => TriggerState;
195
232
  getTriggerProps: (props?: TriggerProps) => T["button"];
196
233
  getIndicatorProps: () => T["element"];
234
+ /**
235
+ * Returns the state of the positioner
236
+ */
237
+ getPositionerState: () => PositionerState;
197
238
  getPositionerProps: () => T["element"];
239
+ /**
240
+ * Returns the state of the content
241
+ */
242
+ getContentState: () => ContentState;
198
243
  getContentProps: () => T["element"];
199
244
  getTitleProps: () => T["element"];
200
245
  getDescriptionProps: () => T["element"];
201
246
  getCloseTriggerProps: () => T["button"];
202
247
  }
203
248
 
204
- export type { ElementIds, IntlTranslations, OpenChangeDetails, PopoverApi, PopoverMachine, PopoverProps, PopoverSchema, PopoverService, TriggerProps, TriggerValueChangeDetails };
249
+ export type { ContentState, ElementIds, IntlTranslations, OpenChangeDetails, PopoverApi, PopoverMachine, PopoverProps, PopoverSchema, PopoverService, PositionerState, TriggerProps, TriggerState, TriggerValueChangeDetails };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/popover",
3
- "version": "2.0.0-next.0",
3
+ "version": "2.0.0-next.1",
4
4
  "description": "Core logic for the popover widget implemented as a state machine",
5
5
  "keywords": [
6
6
  "js",
@@ -14,7 +14,10 @@
14
14
  "author": "Segun Adebayo <sage@adebayosegun.com>",
15
15
  "homepage": "https://github.com/chakra-ui/zag#readme",
16
16
  "license": "MIT",
17
- "repository": "https://github.com/chakra-ui/zag/tree/main/packages/popover",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/chakra-ui/zag/tree/main/packages/popover"
20
+ },
18
21
  "sideEffects": false,
19
22
  "files": [
20
23
  "dist"
@@ -26,16 +29,16 @@
26
29
  "url": "https://github.com/chakra-ui/zag/issues"
27
30
  },
28
31
  "dependencies": {
29
- "@zag-js/anatomy": "2.0.0-next.0",
30
- "@zag-js/aria-hidden": "2.0.0-next.0",
31
- "@zag-js/core": "2.0.0-next.0",
32
- "@zag-js/dom-query": "2.0.0-next.0",
33
- "@zag-js/utils": "2.0.0-next.0",
34
- "@zag-js/dismissable": "2.0.0-next.0",
35
- "@zag-js/popper": "2.0.0-next.0",
36
- "@zag-js/remove-scroll": "2.0.0-next.0",
37
- "@zag-js/types": "2.0.0-next.0",
38
- "@zag-js/focus-trap": "2.0.0-next.0"
32
+ "@zag-js/anatomy": "2.0.0-next.1",
33
+ "@zag-js/aria-hidden": "2.0.0-next.1",
34
+ "@zag-js/core": "2.0.0-next.1",
35
+ "@zag-js/dom-query": "2.0.0-next.1",
36
+ "@zag-js/utils": "2.0.0-next.1",
37
+ "@zag-js/dismissable": "2.0.0-next.1",
38
+ "@zag-js/popper": "2.0.0-next.1",
39
+ "@zag-js/remove-scroll": "2.0.0-next.1",
40
+ "@zag-js/types": "2.0.0-next.1",
41
+ "@zag-js/focus-trap": "2.0.0-next.1"
39
42
  },
40
43
  "devDependencies": {
41
44
  "clean-package": "2.2.0"