@foldkit/ui 0.153.0 → 0.154.0-canary.db46ded8d159

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.
@@ -45,13 +45,26 @@ export declare const portalToContainingRoot: (element: Element) => (() => void);
45
45
  * - `focusAfterPosition`: focuses the element once the first position
46
46
  * resolves. Defaults to `false`.
47
47
  * - `focusSelector`: focuses this descendant instead of the element itself.
48
- * Read only when `focusAfterPosition` is true. */
48
+ * Read only when `focusAfterPosition` is true.
49
+ * - `arrowId`: id of an arrow element inside the panel, resolved through the
50
+ * element's own root. An id that resolves to an element outside the panel is
51
+ * ignored, and any element type is accepted, so an `<svg>` arrow works. When
52
+ * it resolves, the arrow's offset along the panel edge is published as
53
+ * `--arrow-x` and `--arrow-y`, with the axis the placement does not use
54
+ * reset to `initial`, and the panel is no longer made a scroll container,
55
+ * since scrolling clips on both axes and an arrow sits half outside the
56
+ * panel's box.
57
+ * - `arrowPadding`: distance in pixels the arrow keeps from the panel's
58
+ * corners. Defaults to `0`. Separate from `anchor.padding`, which is the
59
+ * viewport padding. */
49
60
  export type SetupConfig = Readonly<{
50
61
  buttonId: string;
51
62
  anchor: AnchorConfig;
52
63
  interceptTab?: boolean;
53
64
  focusAfterPosition?: boolean;
54
65
  focusSelector?: string;
66
+ arrowId?: string;
67
+ arrowPadding?: number;
55
68
  }>;
56
69
  /** Positions a floating element relative to its button using Floating UI, then
57
70
  * returns a cleanup function. Designed to be called inside an `OnMount`
@@ -1 +1 @@
1
- {"version":3,"file":"anchor.d.ts","sourceRoot":"","sources":["../../src/anchor/anchor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,MAAM,IAAI,CAAC,EAAgB,MAAM,QAAQ,CAAA;AAYnE;mFACmF;AACnF,eAAO,MAAM,SAAS,qKAapB,CAAA;AAEF,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC,IAAI,CAAA;AAE7C;+DAC+D;AAC/D,eAAO,MAAM,OAAO;;;;;IAQlB,CAAA;AAEF,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAEzC,oGAAoG;AACpG,eAAO,MAAM,YAAY;;;;;;;;;;;;EAOvB,CAAA;AAEF,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC,IAAI,CAAA;AAkCnD;;;;;;0DAM0D;AAC1D,eAAO,MAAM,sBAAsB,GAAI,SAAS,OAAO,KAAG,CAAC,MAAM,IAAI,CAWpE,CAAA;AAKD;;;;;;;;;;qDAUqD;AACrD,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,YAAY,CAAA;IACpB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAC,CAAA;AAEF;;;;;;;;;;;;;;;gFAegF;AAChF,eAAO,MAAM,WAAW,GACtB,SAAS,OAAO,EAChB,QAAQ,WAAW,KAClB,CAAC,MAAM,IAAI,CA4Lb,CAAA"}
1
+ {"version":3,"file":"anchor.d.ts","sourceRoot":"","sources":["../../src/anchor/anchor.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,MAAM,IAAI,CAAC,EAAgB,MAAM,QAAQ,CAAA;AAa3E;mFACmF;AACnF,eAAO,MAAM,SAAS,qKAapB,CAAA;AAEF,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC,IAAI,CAAA;AAE7C;+DAC+D;AAC/D,eAAO,MAAM,OAAO;;;;;IAQlB,CAAA;AAEF,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAEzC,oGAAoG;AACpG,eAAO,MAAM,YAAY;;;;;;;;;;;;EAOvB,CAAA;AAEF,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC,IAAI,CAAA;AAkCnD;;;;;;0DAM0D;AAC1D,eAAO,MAAM,sBAAsB,GAAI,SAAS,OAAO,KAAG,CAAC,MAAM,IAAI,CAWpE,CAAA;AAKD;;;;;;;;;;;;;;;;;;;;;0BAqB0B;AAC1B,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,YAAY,CAAA;IACpB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAC,CAAA;AAsBF;;;;;;;;;;;;;;;gFAegF;AAChF,eAAO,MAAM,WAAW,GACtB,SAAS,OAAO,EAChB,QAAQ,WAAW,KAClB,CAAC,MAAM,IAAI,CAyOb,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { brandViewResult as __foldkitBrandViewResult } from 'foldkit/brand'
2
- import { Array, Function, Schema as S, String, pipe } from 'effect';
3
- import { autoUpdate, computePosition, flip, offset as floatingOffset, shift, size, } from '@floating-ui/dom';
2
+ import { Array, Function, Option, Schema as S, String, pipe } from 'effect';
3
+ import { arrow, autoUpdate, computePosition, flip, offset as floatingOffset, shift, size, } from '@floating-ui/dom';
4
4
  /** Schema mirroring `@floating-ui/dom`'s `Placement` literal union: a side
5
5
  * (`top`/`right`/`bottom`/`left`) optionally suffixed with `-start` or `-end`. */
6
6
  export const Placement = S.Literals([
@@ -84,6 +84,22 @@ export const portalToContainingRoot = (element) => {
84
84
  }), "@foldkit/ui/anchor/anchor.js#portalToContainingRoot");
85
85
  };
86
86
  const toSide = (placement) => __foldkitBrandViewResult((pipe(placement, String.split('-'), Array.headNonEmpty)), "@foldkit/ui/anchor/anchor.js#toSide");
87
+ // NOTE: the unused axis is reset to `initial` rather than removed. Custom
88
+ // properties inherit, so removing it lets an ancestor's `--arrow-x` reach the
89
+ // arrow, and a `left` that resolves beats the side rule's `right` under
90
+ // over-constrained absolute positioning. An ancestor is not far-fetched: a
91
+ // `portal: false` popover nested inside another popover's panel sits under one.
92
+ // `initial` gives the property its guaranteed-invalid initial value, so
93
+ // `var(--arrow-x)` is invalid at computed-value time and `left` falls back to
94
+ // `auto`.
95
+ const setOrResetLength = (element, property, value) => {
96
+ if (value === undefined) {
97
+ element.style.setProperty(property, 'initial');
98
+ }
99
+ else {
100
+ element.style.setProperty(property, `${value}px`);
101
+ }
102
+ };
87
103
  /** Positions a floating element relative to its button using Floating UI, then
88
104
  * returns a cleanup function. Designed to be called inside an `OnMount`
89
105
  * action: the consumer wraps the call in `Effect.sync` and stashes the
@@ -123,6 +139,19 @@ export const anchorSetup = (element, config) => {
123
139
  if (inShadow) {
124
140
  element.style.position = 'fixed';
125
141
  }
142
+ // NOTE: an id lookup searches the whole root, so an unrelated element that
143
+ // happens to carry the arrow id would be positioned as the arrow. The
144
+ // containment check holds the lookup to what `arrowId` documents, an arrow
145
+ // inside the panel. The candidate is not narrowed to `HTMLElement`, since
146
+ // Floating UI's `arrow` takes any `Element` and a triangle arrow is usually
147
+ // an `<svg>`.
148
+ const maybeArrowElement = pipe(Option.fromNullishOr(config.arrowId), Option.flatMapNullishOr(arrowId => __foldkitBrandViewResult((owner.getElementById(arrowId)), "@foldkit/ui/anchor/anchor.js#anonymous~2")), Option.filter(candidate => __foldkitBrandViewResult((element.contains(candidate)), "@foldkit/ui/anchor/anchor.js#anonymous~3")));
149
+ const arrowMiddleware = Option.match(maybeArrowElement, {
150
+ onNone: () => __foldkitBrandViewResult(([]), "@foldkit/ui/anchor/anchor.js#onNone"),
151
+ onSome: arrowElement => __foldkitBrandViewResult(([
152
+ arrow({ element: arrowElement, padding: config.arrowPadding ?? 0 }),
153
+ ]), "@foldkit/ui/anchor/anchor.js#onSome"),
154
+ });
126
155
  const { placement, gap, offset: crossAxis, padding, isPlacementLocked, } = config.anchor;
127
156
  const shouldInterceptTab = config.interceptTab ?? true;
128
157
  const isPlacementLockEnabled = isPlacementLocked ?? false;
@@ -168,15 +197,30 @@ export const anchorSetup = (element, config) => {
168
197
  }
169
198
  element.style.setProperty('--button-width', `${rects.reference.width}px`);
170
199
  element.style.maxHeight = `${Math.max(0, availableHeight)}px`;
171
- element.style.overflowY = 'auto';
172
- element.style.overscrollBehavior = 'none';
200
+ // NOTE: `overflow-y: auto` makes `overflow-x` compute to `auto`
201
+ // too, so a scrolling panel clips on every side. An arrow sits
202
+ // half outside the panel's padding box, so it would be clipped
203
+ // away entirely. A panel with an arrow keeps its scroll container
204
+ // inside itself instead.
205
+ if (Option.isNone(maybeArrowElement)) {
206
+ element.style.overflowY = 'auto';
207
+ element.style.overscrollBehavior = 'none';
208
+ }
173
209
  },
174
210
  }),
211
+ // NOTE: `arrow` runs last. Sitting after `shift` is the constraint: an
212
+ // offset computed before `shift` would ignore the displacement `shift`
213
+ // applied, which is the case this hook exists to handle. Running after
214
+ // `size` is safe for a different reason: when `size.apply` changes the
215
+ // panel's dimensions, `size` returns `reset: { rects: true }` and
216
+ // Floating UI reruns the whole chain, so `arrow` always resolves
217
+ // against the final rects rather than the ones `size` invalidated.
218
+ ...arrowMiddleware,
175
219
  ],
176
220
  });
177
221
  isPositioning = true;
178
222
  tick
179
- .then(({ x, y, placement: resolvedPlacement }) => {
223
+ .then(({ x, y, placement: resolvedPlacement, middlewareData }) => {
180
224
  hasWarnedFailure = false;
181
225
  if (!isActive) {
182
226
  return;
@@ -187,6 +231,11 @@ export const anchorSetup = (element, config) => {
187
231
  lockedPlacement = lockedPlacement ?? resolvedPlacement;
188
232
  }
189
233
  element.setAttribute('data-placement', toSide(lockedPlacement ?? resolvedPlacement));
234
+ if (Option.isSome(maybeArrowElement)) {
235
+ const { x: arrowX, y: arrowY } = middlewareData.arrow ?? {};
236
+ setOrResetLength(element, '--arrow-x', arrowX);
237
+ setOrResetLength(element, '--arrow-y', arrowY);
238
+ }
190
239
  if (isFirstUpdate) {
191
240
  isFirstUpdate = false;
192
241
  element.style.visibility = '';
@@ -242,6 +291,10 @@ export const anchorSetup = (element, config) => {
242
291
  element.removeEventListener('keydown', handleTabKey);
243
292
  }
244
293
  element.removeAttribute('data-placement');
294
+ element.style.removeProperty('overflow-y');
295
+ element.style.removeProperty('overscroll-behavior');
296
+ element.style.removeProperty('--arrow-x');
297
+ element.style.removeProperty('--arrow-y');
245
298
  portalCleanup?.();
246
299
  }), "@foldkit/ui/anchor/anchor.js#anchorSetup");
247
300
  };
@@ -78,6 +78,11 @@ export declare const init: (config: InitConfig) => Model;
78
78
  * trigger via a native `<label for={Popover.buttonId(id)}>` or an
79
79
  * `aria-labelledby` reference. */
80
80
  export declare const buttonId: (id: string) => string;
81
+ /** Returns the bare DOM id of the popover arrow, derived from the popover's
82
+ * base id. The `arrow` bundle already carries this id, so reach for this when
83
+ * you need the id on its own, such as asserting the panel Mount's `arrowId`
84
+ * argument in a Scene test. */
85
+ export declare const arrowId: (id: string) => string;
81
86
  type UpdateReturn = Update.ReturnWithOutMessage<Model, Message, OutMessage>;
82
87
  /** Prevents page scrolling while the popover is open in modal mode. */
83
88
  export declare const LockScroll: Command.CommandDefinitionNoArgs<"LockScroll", Effect.Effect<{
@@ -209,6 +214,8 @@ export declare const AnchorPopover: Mount.MountDefinitionWithArgs<"AnchorPopover
209
214
  readonly isPlacementLocked: S.optional<S.Boolean>;
210
215
  }>;
211
216
  focusSelector: S.optional<S.String>;
217
+ arrowId: S.optional<S.String>;
218
+ arrowPadding: S.optional<S.Number>;
212
219
  }, {
213
220
  readonly _tag: "CompletedAnchorPopover";
214
221
  }>;
@@ -234,6 +241,11 @@ export declare const close: (model: Model) => UpdateReturn;
234
241
  * - `backdrop`: attribute bundle for the modal backdrop. Includes the
235
242
  * portal Mount that moves the backdrop to document.body. The
236
243
  * backdrop's OnClick closes the popover.
244
+ * - `arrow`: attribute bundle for an arrow element inside the panel.
245
+ * Carries the id the anchor Mount resolves and hides the element from
246
+ * assistive technology. Spread it onto your own element and place it
247
+ * with the `--arrow-x` and `--arrow-y` custom properties Anchor
248
+ * publishes on the panel. Nothing renders until you do.
237
249
  * - `isVisible`: derived from `isOpen` and the Animation
238
250
  * `transitionState`. The consumer renders the panel + backdrop only
239
251
  * while this is true. */
@@ -241,6 +253,7 @@ export type RenderInfo = Readonly<{
241
253
  button: ReadonlyArray<ChildAttribute>;
242
254
  panel: ReadonlyArray<ChildAttribute>;
243
255
  backdrop: ReadonlyArray<ChildAttribute>;
256
+ arrow: ReadonlyArray<ChildAttribute>;
244
257
  isVisible: boolean;
245
258
  }>;
246
259
  /** Per-render view inputs passed to `view` via `h.submodel`'s `viewInputs` field. */
@@ -249,6 +262,7 @@ export type ViewInputs = Readonly<{
249
262
  toView: (render: RenderInfo) => Html;
250
263
  isDisabled?: boolean;
251
264
  focusSelector?: string;
265
+ arrowPadding?: number;
252
266
  ariaLabel?: string;
253
267
  ariaLabelledBy?: string;
254
268
  }>;
@@ -311,6 +325,7 @@ export declare const view: import("foldkit/submodel").View<{
311
325
  toView: (render: RenderInfo) => Html;
312
326
  isDisabled?: boolean;
313
327
  focusSelector?: string;
328
+ arrowPadding?: number;
314
329
  ariaLabel?: string;
315
330
  ariaLabelledBy?: string;
316
331
  }>>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/popover/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/popover/index.ts"],"names":[],"mappings":"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"}
@@ -81,6 +81,11 @@ const panelSelector = (id) => __foldkitBrandViewResult((idSelector(`${id}-panel`
81
81
  * trigger via a native `<label for={Popover.buttonId(id)}>` or an
82
82
  * `aria-labelledby` reference. */
83
83
  export const buttonId = (id) => __foldkitBrandViewResult((`${id}-button`), "@foldkit/ui/popover/index.js#buttonId");
84
+ /** Returns the bare DOM id of the popover arrow, derived from the popover's
85
+ * base id. The `arrow` bundle already carries this id, so reach for this when
86
+ * you need the id on its own, such as asserting the panel Mount's `arrowId`
87
+ * argument in a Scene test. */
88
+ export const arrowId = (id) => __foldkitBrandViewResult((`${id}-arrow`), "@foldkit/ui/popover/index.js#arrowId");
84
89
  /** Prevents page scrolling while the popover is open in modal mode. */
85
90
  export const LockScroll = Command.define('LockScroll', {
86
91
  messages: [Message.CompletedLockScroll],
@@ -243,13 +248,17 @@ export const AnchorPopover = Mount.define('AnchorPopover', {
243
248
  buttonId: S.String,
244
249
  anchor: AnchorConfig,
245
250
  focusSelector: S.optional(S.String),
246
- }, Message.CompletedAnchorPopover)(({ buttonId, anchor, focusSelector }) => __foldkitBrandViewResult((element => __foldkitBrandViewResult((Effect.gen(function* () {
251
+ arrowId: S.optional(S.String),
252
+ arrowPadding: S.optional(S.Number),
253
+ }, Message.CompletedAnchorPopover)(({ buttonId, anchor, focusSelector, arrowId, arrowPadding }) => __foldkitBrandViewResult((element => __foldkitBrandViewResult((Effect.gen(function* () {
247
254
  yield* Effect.acquireRelease(Effect.sync(() => __foldkitBrandViewResult((anchorSetup(element, {
248
255
  buttonId,
249
256
  anchor,
250
257
  interceptTab: false,
251
258
  focusAfterPosition: true,
252
259
  ...(focusSelector !== undefined && { focusSelector }),
260
+ ...(arrowId !== undefined && { arrowId }),
261
+ ...(arrowPadding !== undefined && { arrowPadding }),
253
262
  })), "@foldkit/ui/popover/index.js#anonymous~11")), cleanup => __foldkitBrandViewResult((Effect.sync(cleanup)), "@foldkit/ui/popover/index.js#anonymous~12"));
254
263
  return __foldkitBrandViewResult((Message.CompletedAnchorPopover()), "@foldkit/ui/popover/index.js#anonymous~10");
255
264
  })), "@foldkit/ui/popover/index.js#anonymous~9")), "@foldkit/ui/popover/index.js#anonymous~8"));
@@ -270,7 +279,7 @@ export const close = (model) => __foldkitBrandViewResult((update(model, Message.
270
279
  /** Renders a headless popover with a trigger button and a floating panel. */
271
280
  export const view = defineView((model, viewInputs, h) => {
272
281
  const { id, isOpen, contentFocus, animation: { transitionState }, maybeLastButtonPointerType, } = model;
273
- const { anchor, toView, isDisabled, focusSelector, ariaLabel, ariaLabelledBy, } = viewInputs;
282
+ const { anchor, toView, isDisabled, focusSelector, arrowPadding, ariaLabel, ariaLabelledBy, } = viewInputs;
274
283
  const isLeaving = transitionState === 'LeaveStart' || transitionState === 'LeaveAnimating';
275
284
  const isVisible = isOpen || isLeaving;
276
285
  const animationAttributes = M.value(transitionState).pipe(M.when('EnterStart', () => __foldkitBrandViewResult(([
@@ -345,6 +354,8 @@ export const view = defineView((model, viewInputs, h) => {
345
354
  buttonId: `${id}-button`,
346
355
  anchor,
347
356
  ...(focusSelector !== undefined && { focusSelector }),
357
+ arrowId: arrowId(id),
358
+ ...(arrowPadding !== undefined && { arrowPadding }),
348
359
  })),
349
360
  ...animationAttributes,
350
361
  ...(isLeaving
@@ -358,10 +369,12 @@ export const view = defineView((model, viewInputs, h) => {
358
369
  h.OnMount(PortalPopoverBackdrop()),
359
370
  ...(isLeaving ? [] : [h.OnClick(Message.RequestedClose())]),
360
371
  ];
372
+ const arrowAttributes = [h.Id(arrowId(id)), h.AriaHidden(true)];
361
373
  return __foldkitBrandViewResult((toView({
362
374
  button: childAttributes(buttonAttributes),
363
375
  panel: childAttributes(panelAttributes),
364
376
  backdrop: childAttributes(backdropAttributes),
377
+ arrow: childAttributes(arrowAttributes),
365
378
  isVisible,
366
379
  })), "@foldkit/ui/popover/index.js#anonymous~17");
367
380
  });
@@ -1,4 +1,4 @@
1
- export { init, update, open, close, view, buttonId, Model, Message, OutMessage, type Opened, type Closed, type RequestedOpen, type RequestedClose, AnchorPopover, PortalPopoverBackdrop, LockScroll, UnlockScroll, InertOthers, RestoreInert, FocusPanel, FocusButton, DetectMovementOrAnimationEnd, type BlurredPanel, type PressedPointerOnButton, type IgnoredMouseClick, type SuppressedSpaceScroll, } from './index.js';
1
+ export { init, update, open, close, view, buttonId, arrowId, Model, Message, OutMessage, type Opened, type Closed, type RequestedOpen, type RequestedClose, AnchorPopover, PortalPopoverBackdrop, LockScroll, UnlockScroll, InertOthers, RestoreInert, FocusPanel, FocusButton, DetectMovementOrAnimationEnd, type BlurredPanel, type PressedPointerOnButton, type IgnoredMouseClick, type SuppressedSpaceScroll, } from './index.js';
2
2
  export type { InitConfig, ViewInputs, RenderInfo } from './index.js';
3
3
  export type { AnchorConfig } from '../anchor/index.js';
4
4
  //# sourceMappingURL=public.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/popover/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,OAAO,EACP,UAAU,EACV,KAAK,MAAM,EACX,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,aAAa,EACb,qBAAqB,EACrB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,UAAU,EACV,WAAW,EACX,4BAA4B,EAC5B,KAAK,YAAY,EACjB,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,GAC3B,MAAM,YAAY,CAAA;AAEnB,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEpE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/popover/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,KAAK,EACL,OAAO,EACP,UAAU,EACV,KAAK,MAAM,EACX,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,aAAa,EACb,qBAAqB,EACrB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,UAAU,EACV,WAAW,EACX,4BAA4B,EAC5B,KAAK,YAAY,EACjB,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,GAC3B,MAAM,YAAY,CAAA;AAEnB,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEpE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA"}
@@ -1 +1 @@
1
- export { init, update, open, close, view, buttonId, Model, Message, OutMessage, AnchorPopover, PortalPopoverBackdrop, LockScroll, UnlockScroll, InertOthers, RestoreInert, FocusPanel, FocusButton, DetectMovementOrAnimationEnd, } from './index.js';
1
+ export { init, update, open, close, view, buttonId, arrowId, Model, Message, OutMessage, AnchorPopover, PortalPopoverBackdrop, LockScroll, UnlockScroll, InertOthers, RestoreInert, FocusPanel, FocusButton, DetectMovementOrAnimationEnd, } from './index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foldkit/ui",
3
- "version": "0.153.0",
3
+ "version": "0.154.0-canary.db46ded8d159",
4
4
  "description": "Headless, accessible UI components for Foldkit",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -118,7 +118,7 @@
118
118
  ],
119
119
  "peerDependencies": {
120
120
  "effect": "4.0.0-rc.112",
121
- "foldkit": ">=0.153.0"
121
+ "foldkit": "0.154.0-canary.db46ded8d159"
122
122
  },
123
123
  "dependencies": {
124
124
  "@floating-ui/dom": "^1.7.6"
@@ -131,8 +131,8 @@
131
131
  "typedoc": "^0.28.19",
132
132
  "typescript": "^6.0.3",
133
133
  "vitest": "^4.1.9",
134
- "@foldkit/vite-plugin": "0.19.0",
135
- "foldkit": "0.153.0"
134
+ "@foldkit/vite-plugin": "0.19.0-canary.db46ded8d159",
135
+ "foldkit": "0.154.0-canary.db46ded8d159"
136
136
  },
137
137
  "keywords": [
138
138
  "foldkit",