rei-kit 2.23.0 → 2.24.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 +126 -18
- package/dist/{BaseListbox-BrIZxUmx.js → BaseListbox-BQoSwYdJ.js} +104 -37
- package/dist/BaseListbox-BQoSwYdJ.js.map +1 -0
- package/dist/components/BarChart.vue.d.ts +41 -0
- package/dist/components/BaseMenu.vue.d.ts +5 -0
- package/dist/components/DonutChart.vue.d.ts +33 -0
- package/dist/composables/use-anchored-panel.d.ts +48 -0
- package/dist/composables/use-dialog-shell.d.ts +17 -0
- package/dist/{format-BEvBK1SJ.js → environment-Cz5KiC1t.js} +45 -2
- package/dist/environment-Cz5KiC1t.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +375 -199
- package/dist/index.js.map +1 -1
- package/dist/motion.js +1 -44
- package/dist/motion.js.map +1 -1
- package/dist/styles.css +198 -80
- package/dist/utils/focusable.d.ts +25 -0
- package/dist/web/BaseHoverCard.vue.d.ts +1 -0
- package/dist/web.js +19 -24
- package/dist/web.js.map +1 -1
- package/package.json +2 -2
- package/dist/BaseListbox-BrIZxUmx.js.map +0 -1
- package/dist/format-BEvBK1SJ.js.map +0 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
declare const __VLS_export: <T extends {
|
|
2
|
+
key: string;
|
|
3
|
+
label: string;
|
|
4
|
+
value: number;
|
|
5
|
+
}>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
6
|
+
props: import('vue').PublicProps & __VLS_PrettifyLocal<{
|
|
7
|
+
/** The parts, in the order they should read around the ring. */
|
|
8
|
+
slices: readonly T[];
|
|
9
|
+
/** Names the chart, e.g. "What the stylesheet is made of". */
|
|
10
|
+
label: string;
|
|
11
|
+
/** How a value reads, e.g. `(kb) => `${kb} KB``. The kit has no units. */
|
|
12
|
+
valueLabel: (value: number, item: T) => string;
|
|
13
|
+
/** A class for one slice, e.g. `text-primary` — it paints the stroke. */
|
|
14
|
+
fill: (item: T, index: number) => string;
|
|
15
|
+
/** How thick the ring is, as a percentage of its radius. */
|
|
16
|
+
thickness?: number | undefined;
|
|
17
|
+
}> & (typeof globalThis extends {
|
|
18
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
19
|
+
} ? P : {});
|
|
20
|
+
expose: (exposed: {}) => void;
|
|
21
|
+
attrs: any;
|
|
22
|
+
slots: {};
|
|
23
|
+
emit: {};
|
|
24
|
+
}>) => import('vue').VNode & {
|
|
25
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
26
|
+
};
|
|
27
|
+
declare const _default: typeof __VLS_export;
|
|
28
|
+
export default _default;
|
|
29
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
30
|
+
[K in keyof T]: T[K];
|
|
31
|
+
} : {
|
|
32
|
+
[K in keyof T as K]: T[K];
|
|
33
|
+
}) & {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
/** Above the anchor or below it. */
|
|
3
|
+
export type PanelSide = 'top' | 'bottom';
|
|
4
|
+
export interface AnchoredPanelOptions {
|
|
5
|
+
/** The element the panel is positioned against. */
|
|
6
|
+
root: Ref<HTMLElement | null>;
|
|
7
|
+
/** The panel itself, measured once it is on screen. */
|
|
8
|
+
panel: Ref<HTMLElement | null>;
|
|
9
|
+
/** Whether it is showing. */
|
|
10
|
+
open: Ref<boolean>;
|
|
11
|
+
/** Which way it opens before anything is measured. */
|
|
12
|
+
side?: PanelSide;
|
|
13
|
+
/** How close to the window edge it may come, in pixels. */
|
|
14
|
+
margin?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Keeping a panel on screen.
|
|
18
|
+
*
|
|
19
|
+
* Two corrections, in this order, because they are not equal. **Flip**: a
|
|
20
|
+
* panel that does not fit below its anchor opens above it when there is
|
|
21
|
+
* more room there. **Shift**: whatever is left of it past the side of the
|
|
22
|
+
* window is slid back in.
|
|
23
|
+
*
|
|
24
|
+
* A panel taller than the window fits neither way, and then the rule is
|
|
25
|
+
* still "more room wins" — which shows more of it, and leaves the rest
|
|
26
|
+
* scrolled to rather than gone. `BasePopover` carried a comment saying it
|
|
27
|
+
* kept the side it asked for in that case; it never did, and nothing
|
|
28
|
+
* checked, because the arithmetic had never been given a layout to work
|
|
29
|
+
* on. The comment is gone and the behaviour is the one under test.
|
|
30
|
+
*
|
|
31
|
+
* This was written inside `BasePopover`, which is how the kit came to ship
|
|
32
|
+
* a popover that flips and a menu that does not. `BaseMenu`'s panel was
|
|
33
|
+
* pinned under its trigger with `top: 100%`, so a menu button near the
|
|
34
|
+
* bottom of a window — a row action, the `…` on a card, anything on a
|
|
35
|
+
* phone — opened a list that ran off the bottom of the screen. Nothing said
|
|
36
|
+
* so: it rendered, it passed axe, and the items were all there.
|
|
37
|
+
*
|
|
38
|
+
* Re-measured while open, because the page can scroll or the window change
|
|
39
|
+
* size underneath, and a panel that stays where it opened ends up attached
|
|
40
|
+
* to nothing. The scroll listener captures, so a scroll in any ancestor
|
|
41
|
+
* counts, and it is passive-by-omission: it only reads.
|
|
42
|
+
*/
|
|
43
|
+
export declare function useAnchoredPanel({ root, panel, open, side, margin, }: AnchoredPanelOptions): {
|
|
44
|
+
placed: Ref<PanelSide, PanelSide>;
|
|
45
|
+
shift: Ref<number, number>;
|
|
46
|
+
place: () => void;
|
|
47
|
+
reset: () => void;
|
|
48
|
+
};
|
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
import { Ref } from 'vue';
|
|
2
|
+
/**
|
|
3
|
+
* What every modal surface has to do, in one place.
|
|
4
|
+
*
|
|
5
|
+
* A dialog, a drawer and anything else that takes the whole page share a
|
|
6
|
+
* list that is easy to get nearly right: trap Tab, stop Escape, make the
|
|
7
|
+
* rest of the page inert, stop it scrolling underneath, and hand focus back
|
|
8
|
+
* to whatever opened it. Each of those has an order that matters, and the
|
|
9
|
+
* orders are not obvious — releasing `inert` *after* restoring focus drops
|
|
10
|
+
* it on the body, because an inert element cannot take focus. That was
|
|
11
|
+
* written once, correctly, in `BaseModal`; a drawer that copied it would be
|
|
12
|
+
* a second copy to keep correct.
|
|
13
|
+
*
|
|
14
|
+
* The Tab wrap has to be done by hand: the browser's own Tab order is the
|
|
15
|
+
* whole document, and a dialog is only part of it. `inert` is what also
|
|
16
|
+
* stops a screen reader's virtual cursor, which walks straight past a
|
|
17
|
+
* keydown handler.
|
|
18
|
+
*/
|
|
2
19
|
export interface DialogShellOptions {
|
|
3
20
|
/** Whether Escape and the scrim close it. */
|
|
4
21
|
dismissible?: () => boolean;
|
|
@@ -75,6 +75,49 @@ function formatNumber(value, options = {}, tag = locale.value) {
|
|
|
75
75
|
return formatter.format(value);
|
|
76
76
|
}
|
|
77
77
|
//#endregion
|
|
78
|
-
|
|
78
|
+
//#region src/motion/environment.ts
|
|
79
|
+
/**
|
|
80
|
+
* What every moving part asks before it moves.
|
|
81
|
+
*
|
|
82
|
+
* Kept in one place so the answers cannot drift apart: a counter that honoured
|
|
83
|
+
* reduced motion beside a marquee that did not would be worse than neither.
|
|
84
|
+
* Each is safe to call on a server, where the answer is "do not move" — the
|
|
85
|
+
* server renders the final state, and that is what a reader without
|
|
86
|
+
* JavaScript should see.
|
|
87
|
+
*/
|
|
88
|
+
/** Whether the reader has asked the operating system for less motion. */
|
|
89
|
+
function prefersReducedMotion() {
|
|
90
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return true;
|
|
91
|
+
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
92
|
+
}
|
|
93
|
+
/** One frame from now, or the nearest thing to it where frames do not exist. */
|
|
94
|
+
function nextFrame(callback) {
|
|
95
|
+
if (typeof requestAnimationFrame === "function") {
|
|
96
|
+
const id = requestAnimationFrame(callback);
|
|
97
|
+
return () => cancelAnimationFrame(id);
|
|
98
|
+
}
|
|
99
|
+
const id = setTimeout(callback, 16);
|
|
100
|
+
return () => clearTimeout(id);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Calls `callback` with whether `element` is on screen, each time that
|
|
104
|
+
* changes. Returns the function that stops watching.
|
|
105
|
+
*
|
|
106
|
+
* Without `IntersectionObserver` the element counts as visible straight away:
|
|
107
|
+
* an animation that never starts is a missing number, not a missing flourish.
|
|
108
|
+
*/
|
|
109
|
+
function watchVisibility(element, callback, threshold = .25) {
|
|
110
|
+
if (typeof IntersectionObserver === "undefined") {
|
|
111
|
+
callback(true);
|
|
112
|
+
return () => {};
|
|
113
|
+
}
|
|
114
|
+
const observer = new IntersectionObserver((entries) => {
|
|
115
|
+
for (const entry of entries) callback(entry.isIntersecting);
|
|
116
|
+
}, { threshold });
|
|
117
|
+
observer.observe(element);
|
|
118
|
+
return () => observer.disconnect();
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
export { formatNumber as a, formatDate as i, prefersReducedMotion as n, setFormatLocale as o, watchVisibility as r, nextFrame as t };
|
|
79
122
|
|
|
80
|
-
//# sourceMappingURL=
|
|
123
|
+
//# sourceMappingURL=environment-Cz5KiC1t.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment-Cz5KiC1t.js","names":[],"sources":["../src/utils/format.ts","../src/motion/environment.ts"],"sourcesContent":["import { ref } from 'vue'\n\n/**\n * The locale `Intl` formatting uses.\n *\n * Held here rather than imported from an i18n runtime so the utilities have no\n * i18n dependency at all: an app that never installs vue-i18n still gets dates\n * in the right language. `createI18nRuntime` sets this when it is used.\n */\nconst locale = ref<string>(typeof navigator === 'undefined' ? 'en' : (navigator.language ?? 'en'))\n\n/**\n * Points every formatter at a new locale.\n *\n * @example\n * ```ts\n * setFormatLocale('tr-TR')\n * ```\n */\nexport function setFormatLocale(next: string): void {\n locale.value = next\n}\n\n/**\n * `Intl.DateTimeFormat` is expensive to construct, so instances are cached per\n * locale and option set. The key includes the locale, which is what lets the\n * cache survive a language change instead of returning stale formatters.\n */\nconst cache = new Map<string, Intl.DateTimeFormat>()\n\n/**\n * Formats a date in the active locale.\n *\n * Reading the locale ref here is deliberate: called from a `computed`, the\n * result re-evaluates when the language changes.\n *\n * @param date - Date to format.\n * @param options - Passed straight to `Intl.DateTimeFormat`.\n *\n * @example\n * ```ts\n * formatDate(new Date(), { weekday: 'narrow' }) // 'T'\n * ```\n */\nexport function formatDate(date: Date, options: Intl.DateTimeFormatOptions): string {\n const tag = locale.value\n const key = `${tag}:${JSON.stringify(options)}`\n\n let formatter = cache.get(key)\n if (!formatter) {\n formatter = new Intl.DateTimeFormat(tag, options)\n cache.set(key, formatter)\n }\n\n return formatter.format(date)\n}\n\nconst numberCache = new Map<string, Intl.NumberFormat>()\n\n/**\n * Formats a number in the active locale, or in `tag` when one is given.\n *\n * The same cache and the same locale as `formatDate`, so a counter and the\n * date beside it change language together.\n *\n * @param value - Number to format.\n * @param options - Passed straight to `Intl.NumberFormat`: currency, percent, decimals.\n * @param tag - A BCP 47 tag that overrides the active locale for this call.\n *\n * @example\n * ```ts\n * formatNumber(48200, { style: 'currency', currency: 'JPY' }) // '¥48,200'\n * ```\n */\nexport function formatNumber(\n value: number,\n options: Intl.NumberFormatOptions = {},\n tag: string = locale.value,\n): string {\n const key = `${tag}:${JSON.stringify(options)}`\n\n let formatter = numberCache.get(key)\n if (!formatter) {\n formatter = new Intl.NumberFormat(tag, options)\n numberCache.set(key, formatter)\n }\n\n return formatter.format(value)\n}\n","/**\n * What every moving part asks before it moves.\n *\n * Kept in one place so the answers cannot drift apart: a counter that honoured\n * reduced motion beside a marquee that did not would be worse than neither.\n * Each is safe to call on a server, where the answer is \"do not move\" — the\n * server renders the final state, and that is what a reader without\n * JavaScript should see.\n */\n\n/** Whether the reader has asked the operating system for less motion. */\nexport function prefersReducedMotion(): boolean {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return true\n\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches\n}\n\n/** One frame from now, or the nearest thing to it where frames do not exist. */\nexport function nextFrame(callback: () => void): () => void {\n if (typeof requestAnimationFrame === 'function') {\n const id = requestAnimationFrame(callback)\n\n return () => cancelAnimationFrame(id)\n }\n\n const id = setTimeout(callback, 16)\n\n return () => clearTimeout(id)\n}\n\n/**\n * Calls `callback` with whether `element` is on screen, each time that\n * changes. Returns the function that stops watching.\n *\n * Without `IntersectionObserver` the element counts as visible straight away:\n * an animation that never starts is a missing number, not a missing flourish.\n */\nexport function watchVisibility(\n element: Element,\n callback: (visible: boolean) => void,\n threshold = 0.25,\n): () => void {\n if (typeof IntersectionObserver === 'undefined') {\n callback(true)\n\n return () => {}\n }\n\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) callback(entry.isIntersecting)\n },\n { threshold },\n )\n observer.observe(element)\n\n return () => observer.disconnect()\n}\n"],"mappings":";;;;;;;;;AASA,IAAM,SAAS,IAAY,OAAO,cAAc,cAAc,OAAQ,UAAU,YAAY,IAAK;;;;;;;;;AAUjG,SAAgB,gBAAgB,MAAoB;CAClD,OAAO,QAAQ;AACjB;;;;;;AAOA,IAAM,wBAAQ,IAAI,IAAiC;;;;;;;;;;;;;;;AAgBnD,SAAgB,WAAW,MAAY,SAA6C;CAClF,MAAM,MAAM,OAAO;CACnB,MAAM,MAAM,GAAG,IAAI,GAAG,KAAK,UAAU,OAAO;CAE5C,IAAI,YAAY,MAAM,IAAI,GAAG;CAC7B,IAAI,CAAC,WAAW;EACd,YAAY,IAAI,KAAK,eAAe,KAAK,OAAO;EAChD,MAAM,IAAI,KAAK,SAAS;CAC1B;CAEA,OAAO,UAAU,OAAO,IAAI;AAC9B;AAEA,IAAM,8BAAc,IAAI,IAA+B;;;;;;;;;;;;;;;;AAiBvD,SAAgB,aACd,OACA,UAAoC,CAAC,GACrC,MAAc,OAAO,OACb;CACR,MAAM,MAAM,GAAG,IAAI,GAAG,KAAK,UAAU,OAAO;CAE5C,IAAI,YAAY,YAAY,IAAI,GAAG;CACnC,IAAI,CAAC,WAAW;EACd,YAAY,IAAI,KAAK,aAAa,KAAK,OAAO;EAC9C,YAAY,IAAI,KAAK,SAAS;CAChC;CAEA,OAAO,UAAU,OAAO,KAAK;AAC/B;;;;;;;;;;;;;AC7EA,SAAgB,uBAAgC;CAC9C,IAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY,OAAO;CAErF,OAAO,OAAO,WAAW,kCAAkC,CAAC,CAAC;AAC/D;;AAGA,SAAgB,UAAU,UAAkC;CAC1D,IAAI,OAAO,0BAA0B,YAAY;EAC/C,MAAM,KAAK,sBAAsB,QAAQ;EAEzC,aAAa,qBAAqB,EAAE;CACtC;CAEA,MAAM,KAAK,WAAW,UAAU,EAAE;CAElC,aAAa,aAAa,EAAE;AAC9B;;;;;;;;AASA,SAAgB,gBACd,SACA,UACA,YAAY,KACA;CACZ,IAAI,OAAO,yBAAyB,aAAa;EAC/C,SAAS,IAAI;EAEb,aAAa,CAAC;CAChB;CAEA,MAAM,WAAW,IAAI,sBAClB,YAAY;EACX,KAAK,MAAM,SAAS,SAAS,SAAS,MAAM,cAAc;CAC5D,GACA,EAAE,UAAU,CACd;CACA,SAAS,QAAQ,OAAO;CAExB,aAAa,SAAS,WAAW;AACnC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -95,6 +95,14 @@ export type { TabItem } from './components/TabBar.vue';
|
|
|
95
95
|
* the picture's order, and one tab stop rather than three hundred odd.
|
|
96
96
|
*/
|
|
97
97
|
export { default as ActivityGrid } from './components/ActivityGrid.vue';
|
|
98
|
+
/**
|
|
99
|
+
* `BarChart` and `DonutChart`: the two shapes the kit's own dashboard
|
|
100
|
+
* vocabulary — `StatCard`, `ProgressBar`, `ActivityGrid` — creates a need
|
|
101
|
+
* for and did not answer. Both are the data as text with a picture beside
|
|
102
|
+
* it, rather than a picture with a sentence describing it.
|
|
103
|
+
*/
|
|
104
|
+
export { default as BarChart } from './components/BarChart.vue';
|
|
105
|
+
export { default as DonutChart } from './components/DonutChart.vue';
|
|
98
106
|
/**
|
|
99
107
|
* `ScrollArea`, a scrolling box with the two things a hand-written one
|
|
100
108
|
* leaves out: a fade at whichever edge has more content past it, and a
|