rei-kit 2.21.0 → 2.22.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/README.md +3 -3
- package/dist/{BaseListbox-Btibgnaf.js → BaseListbox-DMR-Zn1u.js} +232 -4
- package/dist/BaseListbox-DMR-Zn1u.js.map +1 -0
- package/dist/app/guards.d.ts +43 -1
- package/dist/app/index.d.ts +1 -1
- package/dist/app.js +51 -5
- package/dist/app.js.map +1 -1
- package/dist/components/AnnounceHost.vue.d.ts +3 -0
- package/dist/components/BaseCheckbox.vue.d.ts +19 -0
- package/dist/components/ErrorSummary.vue.d.ts +60 -0
- package/dist/components/FormField.vue.d.ts +6 -0
- package/dist/composables/use-announce.d.ts +65 -0
- package/dist/{BaseSheet-B7401rS7.js → focus-target-D0WsXwKh.js} +109 -9
- package/dist/focus-target-D0WsXwKh.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +323 -422
- package/dist/index.js.map +1 -1
- package/dist/motion.js +1 -1
- package/dist/motion.js.map +1 -1
- package/dist/styles.css +456 -389
- package/dist/{GoogleButton-mCqGbrXm.js → use-announce-B1LsfsZx.js} +59 -86
- package/dist/use-announce-B1LsfsZx.js.map +1 -0
- package/dist/utils/direction.d.ts +20 -0
- package/dist/utils/focus-target.d.ts +18 -0
- package/dist/web/SkipLink.vue.d.ts +43 -0
- package/dist/web/index.d.ts +6 -0
- package/dist/web.js +154 -133
- package/dist/web.js.map +1 -1
- package/package.json +2 -2
- package/dist/BaseListbox-Btibgnaf.js.map +0 -1
- package/dist/BaseSheet-B7401rS7.js.map +0 -1
- package/dist/GoogleButton-mCqGbrXm.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as _plugin_vue_export_helper_default } from "./_plugin-vue_export-helper-BOaGB7Aw.js";
|
|
2
2
|
import { t as BaseButton_default } from "./BaseButton-RN2an975.js";
|
|
3
|
-
import { Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, mergeModels, nextTick, normalizeStyle, onScopeDispose, onUnmounted, openBlock, readonly, ref, renderSlot, toDisplayString, unref, useModel, watch, withCtx } from "vue";
|
|
3
|
+
import { Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, mergeModels, nextTick, normalizeClass, normalizeStyle, onScopeDispose, onUnmounted, openBlock, readonly, ref, renderSlot, toDisplayString, unref, useId, useModel, vModelCheckbox, watch, withCtx, withDirectives } from "vue";
|
|
4
4
|
import { X } from "lucide-vue-next";
|
|
5
5
|
//#region src/utils/sheet-root.ts
|
|
6
6
|
/** The node every sheet teleports into. */
|
|
@@ -157,9 +157,9 @@ function inertOutside(keep) {
|
|
|
157
157
|
}
|
|
158
158
|
//#endregion
|
|
159
159
|
//#region src/components/BaseSheet.vue?vue&type=script&setup=true&lang.ts
|
|
160
|
-
var _hoisted_1 = { class: "shell-frame md:rounded-shell relative flex max-h-full flex-col justify-end overflow-hidden" };
|
|
161
|
-
var _hoisted_2 = ["aria-label"];
|
|
162
|
-
var _hoisted_3 = { class: "flex shrink-0 items-start gap-3 px-6 pt-4 pb-5" };
|
|
160
|
+
var _hoisted_1$1 = { class: "shell-frame md:rounded-shell relative flex max-h-full flex-col justify-end overflow-hidden" };
|
|
161
|
+
var _hoisted_2$1 = ["aria-label"];
|
|
162
|
+
var _hoisted_3$1 = { class: "flex shrink-0 items-start gap-3 px-6 pt-4 pb-5" };
|
|
163
163
|
var _hoisted_4 = { class: "min-w-0 flex-1" };
|
|
164
164
|
var _hoisted_5 = { class: "text-ink text-xl leading-tight font-semibold" };
|
|
165
165
|
var _hoisted_6 = {
|
|
@@ -237,7 +237,7 @@ var BaseSheet_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PU
|
|
|
237
237
|
key: 0,
|
|
238
238
|
class: "fixed inset-x-0 top-0 bottom-0 z-50 flex items-center justify-center",
|
|
239
239
|
style: normalizeStyle(viewportStyle.value)
|
|
240
|
-
}, [createElementVNode("div", _hoisted_1, [createElementVNode("div", {
|
|
240
|
+
}, [createElementVNode("div", _hoisted_1$1, [createElementVNode("div", {
|
|
241
241
|
class: "bg-ink/45 absolute inset-0 backdrop-blur-[2px]",
|
|
242
242
|
onClick: close
|
|
243
243
|
}), createElementVNode("section", {
|
|
@@ -253,7 +253,7 @@ var BaseSheet_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PU
|
|
|
253
253
|
class: "flex shrink-0 justify-center pt-3",
|
|
254
254
|
"aria-hidden": "true"
|
|
255
255
|
}, [createElementVNode("span", { class: "bg-hair h-1.5 w-10 rounded-full" })], -1)),
|
|
256
|
-
createElementVNode("header", _hoisted_3, [createElementVNode("div", _hoisted_4, [createElementVNode("h2", _hoisted_5, toDisplayString(__props.title), 1), __props.subtitle ? (openBlock(), createElementBlock("p", _hoisted_6, toDisplayString(__props.subtitle), 1)) : createCommentVNode("", true)]), createVNode(BaseButton_default, {
|
|
256
|
+
createElementVNode("header", _hoisted_3$1, [createElementVNode("div", _hoisted_4, [createElementVNode("h2", _hoisted_5, toDisplayString(__props.title), 1), __props.subtitle ? (openBlock(), createElementBlock("p", _hoisted_6, toDisplayString(__props.subtitle), 1)) : createCommentVNode("", true)]), createVNode(BaseButton_default, {
|
|
257
257
|
variant: "unstyled",
|
|
258
258
|
class: "text-ink-soft hover:bg-muted hover:text-ink -mt-1 flex size-10 shrink-0 items-center justify-center rounded-full transition-colors active:scale-90",
|
|
259
259
|
"aria-label": __props.closeLabel,
|
|
@@ -263,13 +263,113 @@ var BaseSheet_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PU
|
|
|
263
263
|
_: 1
|
|
264
264
|
}, 8, ["aria-label"])]),
|
|
265
265
|
createElementVNode("div", _hoisted_7, [renderSlot(_ctx.$slots, "default", {}, void 0, true)])
|
|
266
|
-
], 8, _hoisted_2)])], 4)) : createCommentVNode("", true)]),
|
|
266
|
+
], 8, _hoisted_2$1)])], 4)) : createCommentVNode("", true)]),
|
|
267
267
|
_: 3
|
|
268
268
|
})], 8, ["to"]);
|
|
269
269
|
};
|
|
270
270
|
}
|
|
271
271
|
}), [["__scopeId", "data-v-2855eea3"]]);
|
|
272
272
|
//#endregion
|
|
273
|
-
|
|
273
|
+
//#region src/components/BaseCheckbox.vue?vue&type=script&setup=true&lang.ts
|
|
274
|
+
var _hoisted_1 = { class: "flex flex-col gap-1.5" };
|
|
275
|
+
var _hoisted_2 = ["for"];
|
|
276
|
+
var _hoisted_3 = [
|
|
277
|
+
"id",
|
|
278
|
+
"disabled",
|
|
279
|
+
"indeterminate",
|
|
280
|
+
"aria-invalid",
|
|
281
|
+
"aria-describedby"
|
|
282
|
+
];
|
|
283
|
+
//#endregion
|
|
284
|
+
//#region src/components/BaseCheckbox.vue
|
|
285
|
+
var BaseCheckbox_default = /* @__PURE__ */ defineComponent({
|
|
286
|
+
__name: "BaseCheckbox",
|
|
287
|
+
props: /*@__PURE__*/ mergeModels({
|
|
288
|
+
label: {},
|
|
289
|
+
error: { default: "" },
|
|
290
|
+
hint: { default: "" },
|
|
291
|
+
disabled: {
|
|
292
|
+
type: Boolean,
|
|
293
|
+
default: false
|
|
294
|
+
},
|
|
295
|
+
size: { default: "md" },
|
|
296
|
+
indeterminate: {
|
|
297
|
+
type: Boolean,
|
|
298
|
+
default: false
|
|
299
|
+
},
|
|
300
|
+
labelHidden: {
|
|
301
|
+
type: Boolean,
|
|
302
|
+
default: false
|
|
303
|
+
}
|
|
304
|
+
}, {
|
|
305
|
+
"modelValue": {
|
|
306
|
+
type: Boolean,
|
|
307
|
+
default: false
|
|
308
|
+
},
|
|
309
|
+
"modelModifiers": {}
|
|
310
|
+
}),
|
|
311
|
+
emits: ["update:modelValue"],
|
|
312
|
+
setup(__props) {
|
|
313
|
+
const model = useModel(__props, "modelValue");
|
|
314
|
+
const id = useId();
|
|
315
|
+
const errorId = `${id}-error`;
|
|
316
|
+
const hintId = `${id}-hint`;
|
|
317
|
+
const describedBy = computed(() => {
|
|
318
|
+
if (__props.error) return errorId;
|
|
319
|
+
if (__props.hint) return hintId;
|
|
320
|
+
});
|
|
321
|
+
return (_ctx, _cache) => {
|
|
322
|
+
return openBlock(), createElementBlock("div", _hoisted_1, [createElementVNode("label", {
|
|
323
|
+
for: unref(id),
|
|
324
|
+
class: normalizeClass(["flex items-center", [__props.labelHidden ? "" : __props.size === "sm" ? "gap-2" : "gap-3", __props.disabled ? "opacity-50" : "cursor-pointer"]])
|
|
325
|
+
}, [withDirectives(createElementVNode("input", {
|
|
326
|
+
id: unref(id),
|
|
327
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => model.value = $event),
|
|
328
|
+
type: "checkbox",
|
|
329
|
+
disabled: __props.disabled,
|
|
330
|
+
indeterminate: __props.indeterminate,
|
|
331
|
+
"aria-invalid": Boolean(__props.error),
|
|
332
|
+
"aria-describedby": describedBy.value,
|
|
333
|
+
class: "accent-primary focus-visible:outline-primary size-4 shrink-0 focus-visible:outline-2 focus-visible:outline-offset-2"
|
|
334
|
+
}, null, 8, _hoisted_3), [[vModelCheckbox, model.value]]), createElementVNode("span", { class: normalizeClass(["text-sm", [__props.labelHidden ? "sr-only" : "", __props.size === "sm" ? "text-ink-soft" : "text-ink"]]) }, toDisplayString(__props.label), 3)], 10, _hoisted_2), __props.error ? (openBlock(), createElementBlock("p", {
|
|
335
|
+
key: 0,
|
|
336
|
+
id: errorId,
|
|
337
|
+
class: "text-negative text-xs"
|
|
338
|
+
}, toDisplayString(__props.error), 1)) : __props.hint ? (openBlock(), createElementBlock("p", {
|
|
339
|
+
key: 1,
|
|
340
|
+
id: hintId,
|
|
341
|
+
class: "text-ink-soft text-xs"
|
|
342
|
+
}, toDisplayString(__props.hint), 1)) : createCommentVNode("", true)]);
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
//#endregion
|
|
347
|
+
//#region src/utils/focus-target.ts
|
|
348
|
+
/**
|
|
349
|
+
* Moves focus to an element that was never meant to take it.
|
|
350
|
+
*
|
|
351
|
+
* A `<main>`, a heading, the top of a view: none of them are focusable, and
|
|
352
|
+
* both places the kit needs this — `SkipLink` and `createRouteAnnouncer` —
|
|
353
|
+
* fail in the same invisible way without it. A skip link scrolls the page
|
|
354
|
+
* and leaves focus in the header; a route change swaps the content and
|
|
355
|
+
* leaves focus on the link that is no longer there.
|
|
356
|
+
*
|
|
357
|
+
* `tabindex="-1"` makes the element focusable by script and never by Tab,
|
|
358
|
+
* which is the whole trick. It is left behind rather than cleaned up: a
|
|
359
|
+
* reader that moves out and comes back would otherwise find an unfocusable
|
|
360
|
+
* target the second time.
|
|
361
|
+
*
|
|
362
|
+
* @param target - The element, or the `id` of one. A missing one is a no-op.
|
|
363
|
+
* @returns Whether focus moved, so a caller can fall back.
|
|
364
|
+
*/
|
|
365
|
+
function focusTarget(target) {
|
|
366
|
+
const element = typeof target === "string" ? document.getElementById(target) : target;
|
|
367
|
+
if (!(element instanceof HTMLElement)) return false;
|
|
368
|
+
if (!element.hasAttribute("tabindex")) element.setAttribute("tabindex", "-1");
|
|
369
|
+
element.focus();
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
//#endregion
|
|
373
|
+
export { useBoundValue as a, ensureSheetRoot as c, inertOutside as i, BaseCheckbox_default as n, useVisualViewport as o, BaseSheet_default as r, SHEET_ROOT_ID as s, focusTarget as t };
|
|
274
374
|
|
|
275
|
-
//# sourceMappingURL=
|
|
375
|
+
//# sourceMappingURL=focus-target-D0WsXwKh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focus-target-D0WsXwKh.js","names":[],"sources":["../src/utils/sheet-root.ts","../src/composables/use-visual-viewport.ts","../src/composables/use-bound-value.ts","../src/utils/inert.ts","../src/components/BaseSheet.vue","../src/components/BaseSheet.vue","../src/components/BaseCheckbox.vue","../src/components/BaseCheckbox.vue","../src/utils/focus-target.ts"],"sourcesContent":["/** The node every sheet teleports into. */\nexport const SHEET_ROOT_ID = 'sheet-root'\n\n/**\n * Returns the sheet root, creating it if the page has none.\n *\n * `BaseSheet` teleports out of the app's tree so the app behind it can be\n * marked `inert` — a sheet inside the element it is disabling would disable\n * itself. That means it needs a mount point outside `#app`.\n *\n * It used to need the consuming app to put `<div id=\"sheet-root\">` in its\n * `index.html`, and nothing said so. A page without it got a sheet that opened,\n * blocked the page, and rendered nothing: Vue warns to the console about a\n * missing teleport target and carries on, so the build is green, the types are\n * fine, and the screen is wrong. The kit's own showcase had exactly that bug,\n * which is how it was found.\n *\n * So the node is made on demand. An app that already declares one keeps it —\n * this only fills a gap, it never replaces.\n */\nexport function ensureSheetRoot(): string {\n const selector = `#${SHEET_ROOT_ID}`\n\n // Server-rendered: there is no document, and Teleport is skipped anyway.\n if (typeof document === 'undefined') return selector\n if (document.getElementById(SHEET_ROOT_ID)) return selector\n\n const root = document.createElement('div')\n root.id = SHEET_ROOT_ID\n document.body.appendChild(root)\n\n return selector\n}\n","import { onScopeDispose, readonly, ref } from 'vue'\n\n/** The visible area, once the on-screen keyboard has taken its share. */\nexport interface VisualViewportRect {\n height: number\n offsetTop: number\n}\n\n/**\n * Tracks the visual viewport.\n *\n * Chrome and Android browsers honour `interactive-widget=resizes-content`, so\n * the layout viewport already shrinks for the keyboard there. Safari on iOS\n * does not implement it: it shrinks only the *visual* viewport, leaving a sheet\n * sized in `dvh` sitting partly underneath the keyboard.\n *\n * `null` means the API is unavailable, which callers should read as \"trust the\n * layout viewport\" rather than as zero. A server has no viewport at all, so it\n * gets that same `null` — this runs during `setup`, and a component using it\n * has to survive being rendered there.\n *\n * @example\n * ```ts\n * const viewport = useVisualViewport()\n * // :style=\"viewport ? { height: `${viewport.height}px` } : undefined\"\n * ```\n */\nexport function useVisualViewport() {\n const rect = ref<VisualViewportRect | null>(null)\n\n const viewport = typeof window === 'undefined' ? undefined : window.visualViewport\n if (!viewport) return readonly(rect)\n\n function read() {\n if (!viewport) return\n\n rect.value = { height: viewport.height, offsetTop: viewport.offsetTop }\n }\n\n read()\n\n // `scroll` matters as much as `resize`: iOS shifts the visual viewport up to\n // keep the focused field visible, without changing its height.\n viewport.addEventListener('resize', read)\n viewport.addEventListener('scroll', read)\n\n onScopeDispose(() => {\n viewport.removeEventListener('resize', read)\n viewport.removeEventListener('scroll', read)\n })\n\n return readonly(rect)\n}\n","import { computed, ref, watch } from 'vue'\nimport type { Ref, WritableComputedRef } from 'vue'\n\n/**\n * A `v-model` that accepts `undefined` and emits only what a field produces.\n *\n * `defineModel` could not be typed both ways. Without a default it declares\n * that it may emit `undefined`, so under vue-tsc's strictTemplates a\n * `ref('')` cannot be bound to it. With a default it drops `undefined` from\n * what it accepts, so under `exactOptionalPropertyTypes` a form library's\n * `string | undefined` cannot be. Hibi binds exactly that, and 2.4.0 as first\n * written broke it — the consumer check caught it before the tag.\n *\n * So the prop and the event are declared by hand — the prop as\n * `T | undefined`, the event as `T` — and this is the rest of what\n * `defineModel` did: follow the prop, and keep a value of its own when\n * nothing is bound. Vue applies `.trim` and `.number` to the event itself, so\n * modifiers behave as they did.\n *\n * Internal: not exported from the package.\n */\nexport function useBoundValue<T>(\n read: () => T | undefined,\n write: (value: T) => void,\n): WritableComputedRef<T | undefined> {\n const local = ref(read()) as Ref<T | undefined>\n\n watch(read, (next) => {\n local.value = next\n })\n\n return computed({\n get: () => local.value,\n set: (next) => {\n local.value = next\n write(next as T)\n },\n })\n}\n","/**\n * How many open layers hold each element inert. An element set inert by the\n * app itself is never in here, so releasing never undoes the app's own choice.\n */\nconst holds = new Map<Element, number>()\n\n/**\n * Takes everything on the page except the branch holding `keep` out of tab\n * order and pointer events, and returns the function that gives it back.\n *\n * `inert` is a real focus trap without keydown bookkeeping, and the only one\n * that also stops a screen reader's virtual cursor. It used to be applied to\n * `document.getElementById('app')` — the id Vite's template happens to use —\n * so an app mounted on `#root` got a dialog whose background was still\n * reachable with Tab, and nothing said so. Working from `<body>`'s children\n * instead holds whatever the app is mounted on, and anything else teleported\n * there too.\n *\n * Counted per element, so a sheet opened over a sheet, or a tour over either,\n * does not hand the page back when the inner one closes.\n */\nexport function inertOutside(keep: Element): () => void {\n if (typeof document === 'undefined') return () => {}\n\n const taken: Element[] = []\n\n for (const element of Array.from(document.body.children)) {\n if (element.contains(keep)) continue\n\n const count = holds.get(element)\n if (count === undefined) {\n if (element.hasAttribute('inert')) continue\n element.setAttribute('inert', '')\n }\n holds.set(element, (count ?? 0) + 1)\n taken.push(element)\n }\n\n let released = false\n\n return () => {\n if (released) return\n released = true\n\n for (const element of taken) {\n const count = (holds.get(element) ?? 1) - 1\n if (count > 0) {\n holds.set(element, count)\n continue\n }\n holds.delete(element)\n element.removeAttribute('inert')\n }\n }\n}\n","<script setup lang=\"ts\">\nimport BaseButton from './BaseButton.vue'\nimport { computed, nextTick, onUnmounted, ref, watch } from 'vue'\nimport { X } from 'lucide-vue-next'\n\nimport { useVisualViewport } from '../composables/use-visual-viewport'\nimport { inertOutside } from '../utils/inert'\nimport { ensureSheetRoot } from '../utils/sheet-root'\n\nconst open = defineModel<boolean>({ required: true })\nconst {\n title,\n subtitle = '',\n closeLabel = 'Close',\n} = defineProps<{\n title: string\n subtitle?: string | undefined\n /**\n * Accessible name for the close button.\n *\n * A prop rather than a translation: a component that calls t() forces every\n * consumer onto one i18n setup, and this is the package's only visible string.\n */\n closeLabel?: string | undefined\n}>()\n\n/* Resolved in setup, before the first render, so the Teleport always has a\n target to find. */\nconst sheetRoot = ensureSheetRoot()\n\nconst viewport = useVisualViewport()\n\n/**\n * Pins the sheet to the area the keyboard has left visible.\n *\n * Only needed where the layout viewport does not shrink on its own — iOS. On\n * Android the numbers already agree, so this is a no-op there rather than a\n * second, competing adjustment.\n */\nconst viewportStyle = computed(() =>\n viewport.value\n ? { height: `${viewport.value.height}px`, top: `${viewport.value.offsetTop}px` }\n : undefined,\n)\n\nconst panel = ref<HTMLElement | null>(null)\nlet lastFocused: HTMLElement | null = null\n\nfunction close() {\n open.value = false\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape') close()\n}\n\nwatch(open, async (isOpen) => {\n if (isOpen) {\n setBackgroundInert(true)\n lastFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null\n window.addEventListener('keydown', onKeydown)\n await nextTick()\n panel.value?.focus()\n } else {\n window.removeEventListener('keydown', onKeydown)\n // Released before focus is handed back: an inert element cannot take\n // focus, so the other order quietly dropped it on the body.\n setBackgroundInert(false)\n lastFocused?.focus()\n lastFocused = null\n }\n})\n\n/* The whole page but the sheet root goes inert while this is open. Held per\n sheet, so the unmount below can only release what this sheet took. */\nlet releaseInert: (() => void) | null = null\n\nfunction setBackgroundInert(isInert: boolean) {\n releaseInert?.()\n releaseInert = null\n\n const root = isInert ? document.querySelector(sheetRoot) : null\n if (root) releaseInert = inertOutside(root)\n}\n\nonUnmounted(() => {\n window.removeEventListener('keydown', onKeydown)\n // Unmounting while open would otherwise leave the whole app inert forever.\n setBackgroundInert(false)\n})\n</script>\n\n<template>\n <Teleport :to=\"sheetRoot\">\n <Transition name=\"sheet\">\n <div\n v-if=\"open\"\n class=\"fixed inset-x-0 top-0 bottom-0 z-50 flex items-center justify-center\"\n :style=\"viewportStyle\"\n >\n <div\n class=\"shell-frame md:rounded-shell relative flex max-h-full flex-col justify-end overflow-hidden\"\n >\n <div class=\"bg-ink/45 absolute inset-0 backdrop-blur-[2px]\" @click=\"close\" />\n\n <!-- Header and footer stay put; only the slot scrolls. Sized in dvh so\n the on-screen keyboard shrinks the sheet instead of pushing its\n content out of reach. -->\n <section\n ref=\"panel\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"title\"\n tabindex=\"-1\"\n class=\"sheet-panel surface-overlay relative flex max-h-[94%] min-h-[56dvh] flex-col rounded-t-[28px] outline-none\"\n >\n <div class=\"flex shrink-0 justify-center pt-3\" aria-hidden=\"true\">\n <span class=\"bg-hair h-1.5 w-10 rounded-full\" />\n </div>\n\n <header class=\"flex shrink-0 items-start gap-3 px-6 pt-4 pb-5\">\n <div class=\"min-w-0 flex-1\">\n <h2 class=\"text-ink text-xl leading-tight font-semibold\">{{ title }}</h2>\n <p v-if=\"subtitle\" class=\"text-ink-soft mt-1 text-sm leading-snug\">\n {{ subtitle }}\n </p>\n </div>\n\n <!-- `unstyled`, so the sheet keeps the exact button it had. What it\n gains is the focus ring it never had: this was a raw\n `<button>` with no `focus-visible` rule, so closing a sheet\n from the keyboard was invisible. -->\n <BaseButton\n variant=\"unstyled\"\n class=\"text-ink-soft hover:bg-muted hover:text-ink -mt-1 flex size-10 shrink-0 items-center justify-center rounded-full transition-colors active:scale-90\"\n :aria-label=\"closeLabel\"\n @click=\"close\"\n >\n <X class=\"size-5\" />\n </BaseButton>\n </header>\n\n <div\n class=\"min-h-0 flex-1 overflow-y-auto px-6 pb-[calc(2rem+env(safe-area-inset-bottom,0px))]\"\n >\n <slot />\n </div>\n </section>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.sheet-enter-active,\n.sheet-leave-active {\n transition: opacity var(--duration-base) ease;\n}\n.sheet-enter-from,\n.sheet-leave-to {\n opacity: 0;\n}\n\n/* The panel travels further than the scrim fades, which is what makes the\n sheet read as rising rather than appearing. */\n.sheet-enter-active .sheet-panel,\n.sheet-leave-active .sheet-panel {\n transition: transform var(--duration-slow) var(--ease-sheet);\n}\n.sheet-enter-from .sheet-panel,\n.sheet-leave-to .sheet-panel {\n transform: translateY(6%);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sheet-enter-from .sheet-panel,\n .sheet-leave-to .sheet-panel {\n transform: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport BaseButton from './BaseButton.vue'\nimport { computed, nextTick, onUnmounted, ref, watch } from 'vue'\nimport { X } from 'lucide-vue-next'\n\nimport { useVisualViewport } from '../composables/use-visual-viewport'\nimport { inertOutside } from '../utils/inert'\nimport { ensureSheetRoot } from '../utils/sheet-root'\n\nconst open = defineModel<boolean>({ required: true })\nconst {\n title,\n subtitle = '',\n closeLabel = 'Close',\n} = defineProps<{\n title: string\n subtitle?: string | undefined\n /**\n * Accessible name for the close button.\n *\n * A prop rather than a translation: a component that calls t() forces every\n * consumer onto one i18n setup, and this is the package's only visible string.\n */\n closeLabel?: string | undefined\n}>()\n\n/* Resolved in setup, before the first render, so the Teleport always has a\n target to find. */\nconst sheetRoot = ensureSheetRoot()\n\nconst viewport = useVisualViewport()\n\n/**\n * Pins the sheet to the area the keyboard has left visible.\n *\n * Only needed where the layout viewport does not shrink on its own — iOS. On\n * Android the numbers already agree, so this is a no-op there rather than a\n * second, competing adjustment.\n */\nconst viewportStyle = computed(() =>\n viewport.value\n ? { height: `${viewport.value.height}px`, top: `${viewport.value.offsetTop}px` }\n : undefined,\n)\n\nconst panel = ref<HTMLElement | null>(null)\nlet lastFocused: HTMLElement | null = null\n\nfunction close() {\n open.value = false\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape') close()\n}\n\nwatch(open, async (isOpen) => {\n if (isOpen) {\n setBackgroundInert(true)\n lastFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null\n window.addEventListener('keydown', onKeydown)\n await nextTick()\n panel.value?.focus()\n } else {\n window.removeEventListener('keydown', onKeydown)\n // Released before focus is handed back: an inert element cannot take\n // focus, so the other order quietly dropped it on the body.\n setBackgroundInert(false)\n lastFocused?.focus()\n lastFocused = null\n }\n})\n\n/* The whole page but the sheet root goes inert while this is open. Held per\n sheet, so the unmount below can only release what this sheet took. */\nlet releaseInert: (() => void) | null = null\n\nfunction setBackgroundInert(isInert: boolean) {\n releaseInert?.()\n releaseInert = null\n\n const root = isInert ? document.querySelector(sheetRoot) : null\n if (root) releaseInert = inertOutside(root)\n}\n\nonUnmounted(() => {\n window.removeEventListener('keydown', onKeydown)\n // Unmounting while open would otherwise leave the whole app inert forever.\n setBackgroundInert(false)\n})\n</script>\n\n<template>\n <Teleport :to=\"sheetRoot\">\n <Transition name=\"sheet\">\n <div\n v-if=\"open\"\n class=\"fixed inset-x-0 top-0 bottom-0 z-50 flex items-center justify-center\"\n :style=\"viewportStyle\"\n >\n <div\n class=\"shell-frame md:rounded-shell relative flex max-h-full flex-col justify-end overflow-hidden\"\n >\n <div class=\"bg-ink/45 absolute inset-0 backdrop-blur-[2px]\" @click=\"close\" />\n\n <!-- Header and footer stay put; only the slot scrolls. Sized in dvh so\n the on-screen keyboard shrinks the sheet instead of pushing its\n content out of reach. -->\n <section\n ref=\"panel\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"title\"\n tabindex=\"-1\"\n class=\"sheet-panel surface-overlay relative flex max-h-[94%] min-h-[56dvh] flex-col rounded-t-[28px] outline-none\"\n >\n <div class=\"flex shrink-0 justify-center pt-3\" aria-hidden=\"true\">\n <span class=\"bg-hair h-1.5 w-10 rounded-full\" />\n </div>\n\n <header class=\"flex shrink-0 items-start gap-3 px-6 pt-4 pb-5\">\n <div class=\"min-w-0 flex-1\">\n <h2 class=\"text-ink text-xl leading-tight font-semibold\">{{ title }}</h2>\n <p v-if=\"subtitle\" class=\"text-ink-soft mt-1 text-sm leading-snug\">\n {{ subtitle }}\n </p>\n </div>\n\n <!-- `unstyled`, so the sheet keeps the exact button it had. What it\n gains is the focus ring it never had: this was a raw\n `<button>` with no `focus-visible` rule, so closing a sheet\n from the keyboard was invisible. -->\n <BaseButton\n variant=\"unstyled\"\n class=\"text-ink-soft hover:bg-muted hover:text-ink -mt-1 flex size-10 shrink-0 items-center justify-center rounded-full transition-colors active:scale-90\"\n :aria-label=\"closeLabel\"\n @click=\"close\"\n >\n <X class=\"size-5\" />\n </BaseButton>\n </header>\n\n <div\n class=\"min-h-0 flex-1 overflow-y-auto px-6 pb-[calc(2rem+env(safe-area-inset-bottom,0px))]\"\n >\n <slot />\n </div>\n </section>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.sheet-enter-active,\n.sheet-leave-active {\n transition: opacity var(--duration-base) ease;\n}\n.sheet-enter-from,\n.sheet-leave-to {\n opacity: 0;\n}\n\n/* The panel travels further than the scrim fades, which is what makes the\n sheet read as rising rather than appearing. */\n.sheet-enter-active .sheet-panel,\n.sheet-leave-active .sheet-panel {\n transition: transform var(--duration-slow) var(--ease-sheet);\n}\n.sheet-enter-from .sheet-panel,\n.sheet-leave-to .sheet-panel {\n transform: translateY(6%);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sheet-enter-from .sheet-panel,\n .sheet-leave-to .sheet-panel {\n transform: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useId } from 'vue'\n\n/**\n * A single checkbox, with its label beside it.\n *\n * Deliberately not built on `FormField`. That component stacks a label above\n * its control, which is right for every field where the control is a box you\n * type into and wrong here: a checkbox is read as one sentence with a mark in\n * front of it, and putting the words above the box breaks the association a\n * sighted reader makes before they get to the accessible name.\n *\n * The whole row is the label, so the words are part of the hit target. On a\n * phone that is the difference between a control and a coin toss.\n */\nconst {\n label,\n error = '',\n hint = '',\n disabled = false,\n size = 'md',\n indeterminate = false,\n labelHidden = false,\n} = defineProps<{\n label: string\n error?: string | undefined\n hint?: string | undefined\n disabled?: boolean | undefined\n /**\n * `md` is a setting: a line the reader came here to change, in ink.\n * `sm` is an aside — \"remember me\" under a sign-in form, \"show the ones I\n * have learned\" above a list — quieter and tighter.\n *\n * The two are not a guess. Of the five hand-written checkboxes across the\n * three consuming apps, four were the aside and one was the setting, and\n * they differed in exactly these two ways.\n */\n size?: 'sm' | 'md' | undefined\n /**\n * Neither checked nor unchecked: some of what this box stands for is.\n *\n * It is a DOM property and not an attribute, so it cannot be written in\n * markup and there is no `indeterminate=\"\"` to set — which is why a\n * \"select all\" box on top of a list is the one checkbox every app ends up\n * writing by hand. The kit's own `DataTable` did, for exactly this\n * reason.\n *\n * It is presentation only: the value underneath is still `true` or\n * `false`, and pressing the box resolves it.\n */\n indeterminate?: boolean | undefined\n /**\n * Hides the words but keeps them as the accessible name. For a box in a\n * table header or a list row whose surroundings already say what it is —\n * dropping the label would leave the control named nothing at all.\n */\n labelHidden?: boolean | undefined\n}>()\n\nconst model = defineModel<boolean>({ default: false })\n\nconst id = useId()\nconst errorId = `${id}-error`\nconst hintId = `${id}-hint`\n\nconst describedBy = computed(() => {\n if (error) return errorId\n if (hint) return hintId\n return undefined\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1.5\">\n <label\n :for=\"id\"\n class=\"flex items-center\"\n :class=\"[\n labelHidden ? '' : size === 'sm' ? 'gap-2' : 'gap-3',\n disabled ? 'opacity-50' : 'cursor-pointer',\n ]\"\n >\n <input\n :id=\"id\"\n v-model=\"model\"\n type=\"checkbox\"\n :disabled=\"disabled\"\n :indeterminate=\"indeterminate\"\n :aria-invalid=\"Boolean(error)\"\n :aria-describedby=\"describedBy\"\n class=\"accent-primary focus-visible:outline-primary size-4 shrink-0 focus-visible:outline-2 focus-visible:outline-offset-2\"\n />\n <span\n class=\"text-sm\"\n :class=\"[labelHidden ? 'sr-only' : '', size === 'sm' ? 'text-ink-soft' : 'text-ink']\"\n >\n {{ label }}\n </span>\n </label>\n\n <p v-if=\"error\" :id=\"errorId\" class=\"text-negative text-xs\">{{ error }}</p>\n <p v-else-if=\"hint\" :id=\"hintId\" class=\"text-ink-soft text-xs\">{{ hint }}</p>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, useId } from 'vue'\n\n/**\n * A single checkbox, with its label beside it.\n *\n * Deliberately not built on `FormField`. That component stacks a label above\n * its control, which is right for every field where the control is a box you\n * type into and wrong here: a checkbox is read as one sentence with a mark in\n * front of it, and putting the words above the box breaks the association a\n * sighted reader makes before they get to the accessible name.\n *\n * The whole row is the label, so the words are part of the hit target. On a\n * phone that is the difference between a control and a coin toss.\n */\nconst {\n label,\n error = '',\n hint = '',\n disabled = false,\n size = 'md',\n indeterminate = false,\n labelHidden = false,\n} = defineProps<{\n label: string\n error?: string | undefined\n hint?: string | undefined\n disabled?: boolean | undefined\n /**\n * `md` is a setting: a line the reader came here to change, in ink.\n * `sm` is an aside — \"remember me\" under a sign-in form, \"show the ones I\n * have learned\" above a list — quieter and tighter.\n *\n * The two are not a guess. Of the five hand-written checkboxes across the\n * three consuming apps, four were the aside and one was the setting, and\n * they differed in exactly these two ways.\n */\n size?: 'sm' | 'md' | undefined\n /**\n * Neither checked nor unchecked: some of what this box stands for is.\n *\n * It is a DOM property and not an attribute, so it cannot be written in\n * markup and there is no `indeterminate=\"\"` to set — which is why a\n * \"select all\" box on top of a list is the one checkbox every app ends up\n * writing by hand. The kit's own `DataTable` did, for exactly this\n * reason.\n *\n * It is presentation only: the value underneath is still `true` or\n * `false`, and pressing the box resolves it.\n */\n indeterminate?: boolean | undefined\n /**\n * Hides the words but keeps them as the accessible name. For a box in a\n * table header or a list row whose surroundings already say what it is —\n * dropping the label would leave the control named nothing at all.\n */\n labelHidden?: boolean | undefined\n}>()\n\nconst model = defineModel<boolean>({ default: false })\n\nconst id = useId()\nconst errorId = `${id}-error`\nconst hintId = `${id}-hint`\n\nconst describedBy = computed(() => {\n if (error) return errorId\n if (hint) return hintId\n return undefined\n})\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1.5\">\n <label\n :for=\"id\"\n class=\"flex items-center\"\n :class=\"[\n labelHidden ? '' : size === 'sm' ? 'gap-2' : 'gap-3',\n disabled ? 'opacity-50' : 'cursor-pointer',\n ]\"\n >\n <input\n :id=\"id\"\n v-model=\"model\"\n type=\"checkbox\"\n :disabled=\"disabled\"\n :indeterminate=\"indeterminate\"\n :aria-invalid=\"Boolean(error)\"\n :aria-describedby=\"describedBy\"\n class=\"accent-primary focus-visible:outline-primary size-4 shrink-0 focus-visible:outline-2 focus-visible:outline-offset-2\"\n />\n <span\n class=\"text-sm\"\n :class=\"[labelHidden ? 'sr-only' : '', size === 'sm' ? 'text-ink-soft' : 'text-ink']\"\n >\n {{ label }}\n </span>\n </label>\n\n <p v-if=\"error\" :id=\"errorId\" class=\"text-negative text-xs\">{{ error }}</p>\n <p v-else-if=\"hint\" :id=\"hintId\" class=\"text-ink-soft text-xs\">{{ hint }}</p>\n </div>\n</template>\n","/**\n * Moves focus to an element that was never meant to take it.\n *\n * A `<main>`, a heading, the top of a view: none of them are focusable, and\n * both places the kit needs this — `SkipLink` and `createRouteAnnouncer` —\n * fail in the same invisible way without it. A skip link scrolls the page\n * and leaves focus in the header; a route change swaps the content and\n * leaves focus on the link that is no longer there.\n *\n * `tabindex=\"-1\"` makes the element focusable by script and never by Tab,\n * which is the whole trick. It is left behind rather than cleaned up: a\n * reader that moves out and comes back would otherwise find an unfocusable\n * target the second time.\n *\n * @param target - The element, or the `id` of one. A missing one is a no-op.\n * @returns Whether focus moved, so a caller can fall back.\n */\nexport function focusTarget(target: Element | string | null): boolean {\n const element = typeof target === 'string' ? document.getElementById(target) : target\n if (!(element instanceof HTMLElement)) return false\n\n if (!element.hasAttribute('tabindex')) element.setAttribute('tabindex', '-1')\n\n // `focus()` scrolls it into view on its own, which is why there is no\n // `scrollIntoView` beside it.\n element.focus()\n\n return true\n}\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -107,6 +107,27 @@ export { default as ScrollArea } from './components/ScrollArea.vue';
|
|
|
107
107
|
* that is usually dropped.
|
|
108
108
|
*/
|
|
109
109
|
export { default as VirtualList } from './components/VirtualList.vue';
|
|
110
|
+
/**
|
|
111
|
+
* `AnnounceHost` and `useAnnounce`, for saying something to a reader when
|
|
112
|
+
* nothing on screen has changed enough to say it: a filter that narrowed a
|
|
113
|
+
* list, a route that changed, a draft that saved itself.
|
|
114
|
+
*/
|
|
115
|
+
export { default as AnnounceHost } from './components/AnnounceHost.vue';
|
|
116
|
+
/**
|
|
117
|
+
* `ErrorSummary`, the answer to "did that work?" at the top of a rejected
|
|
118
|
+
* form — and a way from there into each field in one press.
|
|
119
|
+
*/
|
|
120
|
+
export { default as ErrorSummary } from './components/ErrorSummary.vue';
|
|
121
|
+
export { useAnnounce, announce } from './composables/use-announce';
|
|
122
|
+
/**
|
|
123
|
+
* `textDirection`, because every logical property and every `start`/`end`
|
|
124
|
+
* prop in the kit is inert until the document says which way the language
|
|
125
|
+
* runs. The i18n runtime sets `dir` from it; this is for an app that has
|
|
126
|
+
* no i18n runtime and still has more than one direction.
|
|
127
|
+
*/
|
|
128
|
+
export { textDirection } from './utils/direction';
|
|
129
|
+
export type { TextDirection } from './utils/direction';
|
|
130
|
+
export type { AnnounceOptions } from './composables/use-announce';
|
|
110
131
|
export { default as AvatarStack } from './components/AvatarStack.vue';
|
|
111
132
|
export { default as BaseCalendar } from './components/BaseCalendar.vue';
|
|
112
133
|
export { default as BaseChip } from './components/BaseChip.vue';
|