@northstar-js/react 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions.d.ts +71 -4
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +50 -4
- package/dist/actions.js.map +1 -1
- package/dist/backdrops/StarBackdrop.d.ts +31 -0
- package/dist/backdrops/StarBackdrop.d.ts.map +1 -0
- package/dist/backdrops/StarBackdrop.js +84 -0
- package/dist/backdrops/StarBackdrop.js.map +1 -0
- package/dist/backdrops/index.d.ts +7 -0
- package/dist/backdrops/index.d.ts.map +1 -0
- package/dist/backdrops/index.js +8 -0
- package/dist/backdrops/index.js.map +1 -0
- package/dist/backdrops/math.d.ts +39 -0
- package/dist/backdrops/math.d.ts.map +1 -0
- package/dist/backdrops/math.js +44 -0
- package/dist/backdrops/math.js.map +1 -0
- package/dist/backdrops/scenes.d.ts +20 -0
- package/dist/backdrops/scenes.d.ts.map +1 -0
- package/dist/backdrops/scenes.js +417 -0
- package/dist/backdrops/scenes.js.map +1 -0
- package/dist/backdrops.css +141 -0
- package/dist/data.d.ts +14 -3
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +16 -7
- package/dist/data.js.map +1 -1
- package/dist/effects/actions.d.ts +48 -0
- package/dist/effects/actions.d.ts.map +1 -0
- package/dist/effects/actions.js +80 -0
- package/dist/effects/actions.js.map +1 -0
- package/dist/effects/controls.d.ts +84 -0
- package/dist/effects/controls.d.ts.map +1 -0
- package/dist/effects/controls.js +121 -0
- package/dist/effects/controls.js.map +1 -0
- package/dist/effects/display.d.ts +92 -0
- package/dist/effects/display.d.ts.map +1 -0
- package/dist/effects/display.js +138 -0
- package/dist/effects/display.js.map +1 -0
- package/dist/effects/hooks.d.ts +13 -0
- package/dist/effects/hooks.d.ts.map +1 -0
- package/dist/effects/hooks.js +68 -0
- package/dist/effects/hooks.js.map +1 -0
- package/dist/effects/index.d.ts +14 -0
- package/dist/effects/index.d.ts.map +1 -0
- package/dist/effects/index.js +12 -0
- package/dist/effects/index.js.map +1 -0
- package/dist/effects/loaders.d.ts +67 -0
- package/dist/effects/loaders.d.ts.map +1 -0
- package/dist/effects/loaders.js +73 -0
- package/dist/effects/loaders.js.map +1 -0
- package/dist/effects/math.d.ts +23 -0
- package/dist/effects/math.d.ts.map +1 -0
- package/dist/effects/math.js +56 -0
- package/dist/effects/math.js.map +1 -0
- package/dist/effects/navigation.d.ts +55 -0
- package/dist/effects/navigation.d.ts.map +1 -0
- package/dist/effects/navigation.js +103 -0
- package/dist/effects/navigation.js.map +1 -0
- package/dist/effects/overlays.d.ts +34 -0
- package/dist/effects/overlays.d.ts.map +1 -0
- package/dist/effects/overlays.js +56 -0
- package/dist/effects/overlays.js.map +1 -0
- package/dist/effects.css +1659 -0
- package/dist/feedback.d.ts +6 -1
- package/dist/feedback.d.ts.map +1 -1
- package/dist/feedback.js +3 -3
- package/dist/feedback.js.map +1 -1
- package/dist/flow.d.ts +70 -0
- package/dist/flow.d.ts.map +1 -0
- package/dist/flow.js +118 -0
- package/dist/flow.js.map +1 -0
- package/dist/glass.d.ts +74 -0
- package/dist/glass.d.ts.map +1 -0
- package/dist/glass.js +139 -0
- package/dist/glass.js.map +1 -0
- package/dist/index.d.ts +14 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/expand.d.ts +17 -0
- package/dist/internal/expand.d.ts.map +1 -0
- package/dist/internal/expand.js +23 -0
- package/dist/internal/expand.js.map +1 -0
- package/dist/internal/interaction.d.ts +21 -0
- package/dist/internal/interaction.d.ts.map +1 -0
- package/dist/internal/interaction.js +129 -0
- package/dist/internal/interaction.js.map +1 -0
- package/dist/motion.d.ts +36 -0
- package/dist/motion.d.ts.map +1 -0
- package/dist/motion.js +80 -0
- package/dist/motion.js.map +1 -0
- package/dist/navigation.d.ts +43 -6
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +86 -19
- package/dist/navigation.js.map +1 -1
- package/dist/northstar/ExportMenu.d.ts +3 -1
- package/dist/northstar/ExportMenu.d.ts.map +1 -1
- package/dist/northstar/ExportMenu.js +2 -2
- package/dist/northstar/ExportMenu.js.map +1 -1
- package/dist/northstar/ResearchBuilder.d.ts.map +1 -1
- package/dist/northstar/ResearchBuilder.js +3 -3
- package/dist/northstar/ResearchBuilder.js.map +1 -1
- package/dist/northstar/Run.d.ts +3 -1
- package/dist/northstar/Run.d.ts.map +1 -1
- package/dist/northstar/Run.js +4 -3
- package/dist/northstar/Run.js.map +1 -1
- package/dist/northstar/StudyShell.d.ts +23 -1
- package/dist/northstar/StudyShell.d.ts.map +1 -1
- package/dist/northstar/StudyShell.js +6 -2
- package/dist/northstar/StudyShell.js.map +1 -1
- package/dist/northstar/StudyStatus.d.ts +3 -1
- package/dist/northstar/StudyStatus.d.ts.map +1 -1
- package/dist/northstar/StudyStatus.js +4 -2
- package/dist/northstar/StudyStatus.js.map +1 -1
- package/dist/overlays.d.ts +29 -4
- package/dist/overlays.d.ts.map +1 -1
- package/dist/overlays.js +38 -12
- package/dist/overlays.js.map +1 -1
- package/dist/provider.d.ts +31 -2
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +20 -6
- package/dist/provider.js.map +1 -1
- package/dist/styles.css +1069 -7
- package/dist/surface.d.ts +58 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +39 -0
- package/dist/surface.js.map +1 -0
- package/package.json +18 -8
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type Expand = boolean | 'always';
|
|
2
|
+
export interface ExpandLabelProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
3
|
+
/** The label sits after the icon (`start`, default) or before it (`end`): the gap opens on that side. */
|
|
4
|
+
side?: 'start' | 'end';
|
|
5
|
+
}
|
|
6
|
+
export declare function ExpandLabel({ side, className, children, ...rest }: ExpandLabelProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
/** Props a control spreads to become an expand host. Nothing when `expand` is false. */
|
|
8
|
+
export declare function expandHost(expand: Expand | undefined): {
|
|
9
|
+
className?: string;
|
|
10
|
+
'data-expand'?: 'hover' | 'always';
|
|
11
|
+
};
|
|
12
|
+
/** The icon slot of an expand host. */
|
|
13
|
+
export declare function ExpandIcon({ children, className }: {
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
className?: string;
|
|
16
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
//# sourceMappingURL=expand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expand.d.ts","sourceRoot":"","sources":["../../src/internal/expand.tsx"],"names":[],"mappings":"AASA,MAAM,MAAM,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAExC,MAAM,WAAW,gBAAiB,SAAQ,KAAK,CAAC,cAAc,CAAC,eAAe,CAAC;IAC7E,yGAAyG;IACzG,IAAI,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;CACxB;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,gBAAgB,2CAM7F;AAED,wFAAwF;AACxF,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;CAAE,CAGjH;AAED,uCAAuC;AACvC,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,2CAMpG"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// The one icon-first expand primitive. A control that carries `ns-expand-host`
|
|
3
|
+
// shows its icon; its ExpandLabel is collapsed to zero width and eases open
|
|
4
|
+
// (grid-template-columns 0fr -> 1fr, so the text is measured by layout, never
|
|
5
|
+
// clipped with an ellipsis) on hover, on keyboard focus (:focus-visible, or a
|
|
6
|
+
// focused child), when selected or expanded, and always on touch screens.
|
|
7
|
+
// The label stays in the accessibility tree the whole time, so it names the
|
|
8
|
+
// control. `data-expand="always"` keeps it open.
|
|
9
|
+
import { cx } from './util.js';
|
|
10
|
+
export function ExpandLabel({ side = 'start', className, children, ...rest }) {
|
|
11
|
+
return (_jsx("span", { ...rest, className: cx('ns-reveal-label', 'ns-expand', side === 'end' && 'ns-expand--end', className), children: _jsx("span", { className: "ns-reveal-label__inner ns-expand__inner", children: children }) }));
|
|
12
|
+
}
|
|
13
|
+
/** Props a control spreads to become an expand host. Nothing when `expand` is false. */
|
|
14
|
+
export function expandHost(expand) {
|
|
15
|
+
if (!expand)
|
|
16
|
+
return {};
|
|
17
|
+
return { className: 'ns-expand-host', 'data-expand': expand === 'always' ? 'always' : 'hover' };
|
|
18
|
+
}
|
|
19
|
+
/** The icon slot of an expand host. */
|
|
20
|
+
export function ExpandIcon({ children, className }) {
|
|
21
|
+
return (_jsx("span", { className: cx('ns-expand-icon', className), "aria-hidden": "true", children: children }));
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=expand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expand.js","sourceRoot":"","sources":["../../src/internal/expand.tsx"],"names":[],"mappings":";AAAA,+EAA+E;AAC/E,4EAA4E;AAC5E,8EAA8E;AAC9E,8EAA8E;AAC9E,0EAA0E;AAC1E,4EAA4E;AAC5E,iDAAiD;AACjD,OAAO,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAS/B,MAAM,UAAU,WAAW,CAAC,EAAE,IAAI,GAAG,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAoB;IAC5F,OAAO,CACL,kBAAU,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,iBAAiB,EAAE,WAAW,EAAE,IAAI,KAAK,KAAK,IAAI,gBAAgB,EAAE,SAAS,CAAC,YAC1G,eAAM,SAAS,EAAC,yCAAyC,YAAE,QAAQ,GAAQ,GACtE,CACR,CAAC;AACJ,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,UAAU,CAAC,MAA0B;IACnD,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;AAClG,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,UAAU,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAqD;IACnG,OAAO,CACL,eAAM,SAAS,EAAE,EAAE,CAAC,gBAAgB,EAAE,SAAS,CAAC,iBAAc,MAAM,YACjE,QAAQ,GACJ,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface PointerOptions {
|
|
2
|
+
/** Pull the element toward the pointer by up to this many px (magnetic). 0 disables. */
|
|
3
|
+
magnetic?: number;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Tracks the pointer over an element, rAF-throttled, and writes
|
|
7
|
+
* `--ns-mx` / `--ns-my` (px from the top-left, for spotlights and specular
|
|
8
|
+
* highlights), `--ns-pointer-x` / `--ns-pointer-y` (unitless, -1 at the
|
|
9
|
+
* left/top edge to 1 at the right/bottom, for the spin finish's turning
|
|
10
|
+
* sweep) and, when magnetic, `--ns-mag-x` / `--ns-mag-y`. Touch is
|
|
11
|
+
* ignored; the pointer effects are a hover affordance and keyboard focus gets
|
|
12
|
+
* an equivalent centred highlight from CSS.
|
|
13
|
+
*/
|
|
14
|
+
export declare function usePointerTracking<T extends HTMLElement>(enabled: boolean | ((el: T) => boolean), options?: PointerOptions): React.RefCallback<T>;
|
|
15
|
+
/**
|
|
16
|
+
* Keeps a layer mounted after `open` turns false until its exit animation
|
|
17
|
+
* (`[data-state='closed']` in CSS) has played. With no animation — reduced
|
|
18
|
+
* motion, forced zero durations, or no stylesheet — it unmounts at once.
|
|
19
|
+
*/
|
|
20
|
+
export declare function usePresence(open: boolean, ref: React.RefObject<HTMLElement | null>): boolean;
|
|
21
|
+
//# sourceMappingURL=interaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interaction.d.ts","sourceRoot":"","sources":["../../src/internal/interaction.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,cAAc;IAC7B,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,WAAW,EAAE,OAAO,EAAE,OAAO,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,EAAE,OAAO,GAAE,cAAmB,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CA2DrJ;AAWD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,OAAO,CAuB5F"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// The interaction layer's only JavaScript: pointer tracking for spotlights and
|
|
3
|
+
// magnetic controls, and a presence hook that lets overlays play an exit
|
|
4
|
+
// animation. Everything writes CSS custom properties or attributes straight to
|
|
5
|
+
// the element, so pointer movement never re-renders React.
|
|
6
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
7
|
+
const useIsoLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
|
|
8
|
+
const raf = typeof window !== 'undefined' && window.requestAnimationFrame
|
|
9
|
+
? (cb) => window.requestAnimationFrame(cb)
|
|
10
|
+
: (cb) => setTimeout(cb, 16);
|
|
11
|
+
const caf = typeof window !== 'undefined' && window.cancelAnimationFrame ? (id) => window.cancelAnimationFrame(id) : (id) => clearTimeout(id);
|
|
12
|
+
function prefersReducedMotion() {
|
|
13
|
+
return typeof window !== 'undefined' && !!window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Tracks the pointer over an element, rAF-throttled, and writes
|
|
17
|
+
* `--ns-mx` / `--ns-my` (px from the top-left, for spotlights and specular
|
|
18
|
+
* highlights), `--ns-pointer-x` / `--ns-pointer-y` (unitless, -1 at the
|
|
19
|
+
* left/top edge to 1 at the right/bottom, for the spin finish's turning
|
|
20
|
+
* sweep) and, when magnetic, `--ns-mag-x` / `--ns-mag-y`. Touch is
|
|
21
|
+
* ignored; the pointer effects are a hover affordance and keyboard focus gets
|
|
22
|
+
* an equivalent centred highlight from CSS.
|
|
23
|
+
*/
|
|
24
|
+
export function usePointerTracking(enabled, options = {}) {
|
|
25
|
+
const magnetic = options.magnetic ?? 0;
|
|
26
|
+
const cleanup = useRef(null);
|
|
27
|
+
// A predicate is decided once, when the element mounts (e.g. "am I on glass?").
|
|
28
|
+
const test = useRef(enabled);
|
|
29
|
+
test.current = enabled;
|
|
30
|
+
const key = typeof enabled === 'function' ? 'test' : enabled;
|
|
31
|
+
return useCallback((el) => {
|
|
32
|
+
cleanup.current?.();
|
|
33
|
+
cleanup.current = null;
|
|
34
|
+
const on = typeof test.current === 'function' ? !!el && test.current(el) : test.current;
|
|
35
|
+
if (!el || !on)
|
|
36
|
+
return;
|
|
37
|
+
let frame = 0;
|
|
38
|
+
let last = null;
|
|
39
|
+
const flush = () => {
|
|
40
|
+
frame = 0;
|
|
41
|
+
if (!last)
|
|
42
|
+
return;
|
|
43
|
+
const r = el.getBoundingClientRect();
|
|
44
|
+
const x = last.clientX - r.left;
|
|
45
|
+
const y = last.clientY - r.top;
|
|
46
|
+
el.style.setProperty('--ns-mx', `${x.toFixed(1)}px`);
|
|
47
|
+
el.style.setProperty('--ns-my', `${y.toFixed(1)}px`);
|
|
48
|
+
if (r.width <= 0 || r.height <= 0)
|
|
49
|
+
return;
|
|
50
|
+
const dx = Math.max(-1, Math.min(1, (x / r.width - 0.5) * 2));
|
|
51
|
+
const dy = Math.max(-1, Math.min(1, (y / r.height - 0.5) * 2));
|
|
52
|
+
el.style.setProperty('--ns-pointer-x', dx.toFixed(3));
|
|
53
|
+
el.style.setProperty('--ns-pointer-y', dy.toFixed(3));
|
|
54
|
+
if (magnetic > 0 && !prefersReducedMotion()) {
|
|
55
|
+
el.style.setProperty('--ns-mag-x', `${(dx * magnetic).toFixed(2)}px`);
|
|
56
|
+
el.style.setProperty('--ns-mag-y', `${(dy * magnetic).toFixed(2)}px`);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const onMove = (e) => {
|
|
60
|
+
if (e.pointerType === 'touch')
|
|
61
|
+
return;
|
|
62
|
+
last = e;
|
|
63
|
+
el.setAttribute('data-pointer', '');
|
|
64
|
+
if (!frame)
|
|
65
|
+
frame = raf(flush);
|
|
66
|
+
};
|
|
67
|
+
const onLeave = () => {
|
|
68
|
+
last = null;
|
|
69
|
+
if (frame)
|
|
70
|
+
caf(frame);
|
|
71
|
+
frame = 0;
|
|
72
|
+
el.removeAttribute('data-pointer');
|
|
73
|
+
el.style.removeProperty('--ns-pointer-x');
|
|
74
|
+
el.style.removeProperty('--ns-pointer-y');
|
|
75
|
+
el.style.removeProperty('--ns-mag-x');
|
|
76
|
+
el.style.removeProperty('--ns-mag-y');
|
|
77
|
+
};
|
|
78
|
+
el.addEventListener('pointermove', onMove, { passive: true });
|
|
79
|
+
el.addEventListener('pointerleave', onLeave);
|
|
80
|
+
cleanup.current = () => {
|
|
81
|
+
el.removeEventListener('pointermove', onMove);
|
|
82
|
+
el.removeEventListener('pointerleave', onLeave);
|
|
83
|
+
if (frame)
|
|
84
|
+
caf(frame);
|
|
85
|
+
};
|
|
86
|
+
}, [key, magnetic]);
|
|
87
|
+
}
|
|
88
|
+
function longestAnimation(el) {
|
|
89
|
+
const cs = getComputedStyle(el);
|
|
90
|
+
if (!cs.animationName || cs.animationName === 'none')
|
|
91
|
+
return 0;
|
|
92
|
+
const parse = (s) => (s.trim().endsWith('ms') ? parseFloat(s) : parseFloat(s) * 1000) || 0;
|
|
93
|
+
const durations = (cs.animationDuration || '0s').split(',').map(parse);
|
|
94
|
+
const delays = (cs.animationDelay || '0s').split(',').map(parse);
|
|
95
|
+
return Math.max(0, ...durations.map((d, i) => d + (delays[i] ?? 0)));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Keeps a layer mounted after `open` turns false until its exit animation
|
|
99
|
+
* (`[data-state='closed']` in CSS) has played. With no animation — reduced
|
|
100
|
+
* motion, forced zero durations, or no stylesheet — it unmounts at once.
|
|
101
|
+
*/
|
|
102
|
+
export function usePresence(open, ref) {
|
|
103
|
+
const [mounted, setMounted] = useState(open);
|
|
104
|
+
if (open && !mounted)
|
|
105
|
+
setMounted(true);
|
|
106
|
+
useIsoLayoutEffect(() => {
|
|
107
|
+
if (open || !mounted)
|
|
108
|
+
return;
|
|
109
|
+
const el = ref.current;
|
|
110
|
+
const ms = el ? longestAnimation(el) : 0;
|
|
111
|
+
if (!el || ms <= 0) {
|
|
112
|
+
setMounted(false);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const done = () => setMounted(false);
|
|
116
|
+
const onEnd = (e) => {
|
|
117
|
+
if (e.target === el)
|
|
118
|
+
done();
|
|
119
|
+
};
|
|
120
|
+
el.addEventListener('animationend', onEnd);
|
|
121
|
+
const t = setTimeout(done, ms + 60);
|
|
122
|
+
return () => {
|
|
123
|
+
el.removeEventListener('animationend', onEnd);
|
|
124
|
+
clearTimeout(t);
|
|
125
|
+
};
|
|
126
|
+
}, [open, mounted]);
|
|
127
|
+
return open || mounted;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=interaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interaction.js","sourceRoot":"","sources":["../../src/internal/interaction.ts"],"names":[],"mappings":"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"}
|
package/dist/motion.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare function prefersReducedMotion(): boolean;
|
|
2
|
+
/** True where the View Transitions API is available and motion is allowed. */
|
|
3
|
+
export declare function canViewTransition(): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Runs a React state update inside `document.startViewTransition` where it is
|
|
6
|
+
* supported (flushing synchronously so the new DOM is captured), and plainly
|
|
7
|
+
* everywhere else. With `scope`, only that element is named for the
|
|
8
|
+
* transition (as `name`, default `ns-step`) and only while it runs, so several
|
|
9
|
+
* instances on one page never clash. Returns true when a view transition ran.
|
|
10
|
+
*/
|
|
11
|
+
export declare function withViewTransition(update: () => void, scope?: HTMLElement | null, name?: string): boolean;
|
|
12
|
+
/** A stable callback version of `withViewTransition`. */
|
|
13
|
+
export declare function useViewTransition(): (update: () => void, scope?: HTMLElement | null, name?: string) => boolean;
|
|
14
|
+
type RevealTag = 'div' | 'section' | 'article' | 'li' | 'ul' | 'ol' | 'span' | 'header' | 'footer';
|
|
15
|
+
export interface RevealProps extends React.HTMLAttributes<HTMLElement> {
|
|
16
|
+
/** Stagger the direct children in sequence instead of revealing as one block. */
|
|
17
|
+
stagger?: boolean;
|
|
18
|
+
/** Extra delay before the reveal, in ms. */
|
|
19
|
+
delay?: number;
|
|
20
|
+
/** How the content arrives. Default `rise`. */
|
|
21
|
+
effect?: 'rise' | 'fade' | 'scale';
|
|
22
|
+
/** Reveal again each time it re-enters the viewport. Default false. */
|
|
23
|
+
repeat?: boolean;
|
|
24
|
+
/** Fraction of the element visible before it reveals. Default 0.15. */
|
|
25
|
+
threshold?: number;
|
|
26
|
+
as?: RevealTag;
|
|
27
|
+
ref?: React.Ref<HTMLElement>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Fades and rises its content in when it scrolls into view. The server HTML
|
|
31
|
+
* and no-JS render are fully visible: the element is only held back once the
|
|
32
|
+
* observer is attached. Under reduced motion it shows at once.
|
|
33
|
+
*/
|
|
34
|
+
export declare function Reveal({ stagger, delay, effect, repeat, threshold, as: Tag, className, style, ref, ...rest }: RevealProps): import("react/jsx-runtime").JSX.Element;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=motion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"motion.d.ts","sourceRoot":"","sources":["../src/motion.tsx"],"names":[],"mappings":"AAQA,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAID,8EAA8E;AAC9E,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,EAAE,IAAI,SAAY,GAAG,OAAO,CAgB5G;AAED,yDAAyD;AACzD,wBAAgB,iBAAiB,IAAI,CAAC,MAAM,EAAE,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAE9G;AAED,KAAK,SAAS,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEnG,MAAM,WAAW,WAAY,SAAQ,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC;IACpE,iFAAiF;IACjF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACnC,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,SAAS,CAAC;IACf,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,EAAE,OAAe,EAAE,KAAS,EAAE,MAAe,EAAE,MAAc,EAAE,SAAgB,EAAE,EAAE,EAAE,GAAW,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,EAAE,WAAW,2CAkCrK"}
|
package/dist/motion.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
// Motion utilities without a motion library: a scroll-linked Reveal on
|
|
4
|
+
// IntersectionObserver, and a View Transitions wrapper for step and tab
|
|
5
|
+
// changes. Both do nothing visible under prefers-reduced-motion.
|
|
6
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
7
|
+
import { flushSync } from 'react-dom';
|
|
8
|
+
import { cx, mergeRefs } from './internal/util.js';
|
|
9
|
+
export function prefersReducedMotion() {
|
|
10
|
+
return typeof window !== 'undefined' && !!window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
11
|
+
}
|
|
12
|
+
/** True where the View Transitions API is available and motion is allowed. */
|
|
13
|
+
export function canViewTransition() {
|
|
14
|
+
return typeof document !== 'undefined' && typeof document.startViewTransition === 'function' && !prefersReducedMotion();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Runs a React state update inside `document.startViewTransition` where it is
|
|
18
|
+
* supported (flushing synchronously so the new DOM is captured), and plainly
|
|
19
|
+
* everywhere else. With `scope`, only that element is named for the
|
|
20
|
+
* transition (as `name`, default `ns-step`) and only while it runs, so several
|
|
21
|
+
* instances on one page never clash. Returns true when a view transition ran.
|
|
22
|
+
*/
|
|
23
|
+
export function withViewTransition(update, scope, name = 'ns-step') {
|
|
24
|
+
if (!canViewTransition()) {
|
|
25
|
+
update();
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (scope)
|
|
29
|
+
scope.style.setProperty('view-transition-name', name);
|
|
30
|
+
try {
|
|
31
|
+
const t = document.startViewTransition(() => flushSync(update));
|
|
32
|
+
const clear = () => scope?.style.removeProperty('view-transition-name');
|
|
33
|
+
t.finished.then(clear, clear);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
scope?.style.removeProperty('view-transition-name');
|
|
37
|
+
update();
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
/** A stable callback version of `withViewTransition`. */
|
|
43
|
+
export function useViewTransition() {
|
|
44
|
+
return useCallback((update, scope, name) => withViewTransition(update, scope, name), []);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Fades and rises its content in when it scrolls into view. The server HTML
|
|
48
|
+
* and no-JS render are fully visible: the element is only held back once the
|
|
49
|
+
* observer is attached. Under reduced motion it shows at once.
|
|
50
|
+
*/
|
|
51
|
+
export function Reveal({ stagger = false, delay = 0, effect = 'rise', repeat = false, threshold = 0.15, as: Tag = 'div', className, style, ref, ...rest }) {
|
|
52
|
+
const node = useRef(null);
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
const el = node.current;
|
|
55
|
+
if (!el)
|
|
56
|
+
return;
|
|
57
|
+
if (typeof IntersectionObserver === 'undefined' || prefersReducedMotion()) {
|
|
58
|
+
el.setAttribute('data-reveal', 'in');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
el.setAttribute('data-reveal', 'pending');
|
|
62
|
+
const io = new IntersectionObserver((entries) => {
|
|
63
|
+
for (const e of entries) {
|
|
64
|
+
if (e.isIntersecting) {
|
|
65
|
+
el.setAttribute('data-reveal', 'in');
|
|
66
|
+
if (!repeat)
|
|
67
|
+
io.disconnect();
|
|
68
|
+
}
|
|
69
|
+
else if (repeat)
|
|
70
|
+
el.setAttribute('data-reveal', 'pending');
|
|
71
|
+
}
|
|
72
|
+
}, { threshold, rootMargin: '0px 0px -8% 0px' });
|
|
73
|
+
io.observe(el);
|
|
74
|
+
return () => io.disconnect();
|
|
75
|
+
}, [repeat, threshold]);
|
|
76
|
+
return (_jsx(Tag, { ...rest, ref: mergeRefs(ref, (el) => {
|
|
77
|
+
node.current = el;
|
|
78
|
+
}), className: cx('ns-reveal', `ns-reveal--${effect}`, stagger && 'ns-reveal--stagger', className), style: delay ? { '--ns-reveal-delay': `${delay}ms`, ...style } : style }));
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=motion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"motion.js","sourceRoot":"","sources":["../src/motion.tsx"],"names":[],"mappings":"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"}
|
package/dist/navigation.d.ts
CHANGED
|
@@ -1,17 +1,35 @@
|
|
|
1
|
+
import type { Material } from './provider.js';
|
|
2
|
+
import { type Expand } from './internal/expand.js';
|
|
3
|
+
/** `line` (default): a glacier underline that slides between tabs. `glass` / `solid`: a sliding pill behind the label. */
|
|
4
|
+
export type TabsIndicator = 'line' | Material;
|
|
1
5
|
export interface TabsRootProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'defaultValue' | 'onChange'> {
|
|
2
6
|
value?: string;
|
|
3
7
|
defaultValue?: string;
|
|
4
8
|
onValueChange?: (value: string) => void;
|
|
5
9
|
orientation?: 'horizontal' | 'vertical';
|
|
10
|
+
/** How the selected tab is marked. Default `line`. */
|
|
11
|
+
indicator?: TabsIndicator;
|
|
12
|
+
/** Cross-fade panels with the View Transitions API where supported. Default false. */
|
|
13
|
+
transition?: boolean;
|
|
14
|
+
/** Sliding-pill tabs (`glass`, `solid`) are pills by default; `rounded` opts out to tight corners. */
|
|
15
|
+
shape?: 'pill' | 'rounded';
|
|
6
16
|
}
|
|
7
|
-
declare function TabsRoot({ value, defaultValue, onValueChange, orientation, className, ...rest }: TabsRootProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
-
declare function TabsList({ className, onKeyDown, ...rest }: React.HTMLAttributes<HTMLDivElement> & {
|
|
17
|
+
declare function TabsRoot({ value, defaultValue, onValueChange, orientation, indicator, transition, shape, className, ...rest }: TabsRootProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
declare function TabsList({ className, onKeyDown, children, ...rest }: React.HTMLAttributes<HTMLDivElement> & {
|
|
9
19
|
label?: string;
|
|
10
20
|
}): import("react/jsx-runtime").JSX.Element;
|
|
11
21
|
export interface TabsTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
12
22
|
value: string;
|
|
23
|
+
/** Leading icon (decorative). */
|
|
24
|
+
icon?: React.ReactNode;
|
|
25
|
+
/**
|
|
26
|
+
* Icon tabs: with `icon`, `true` shows the icon and eases the label open on
|
|
27
|
+
* hover, keyboard focus and while selected (always on touch). `always`
|
|
28
|
+
* keeps it open. Default false.
|
|
29
|
+
*/
|
|
30
|
+
expand?: Expand;
|
|
13
31
|
}
|
|
14
|
-
declare function TabsTrigger({ value, className, onClick, ...rest }: TabsTriggerProps): import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
declare function TabsTrigger({ value, icon, expand, className, onClick, children, ...rest }: TabsTriggerProps): import("react/jsx-runtime").JSX.Element;
|
|
15
33
|
export interface TabsContentProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
16
34
|
value: string;
|
|
17
35
|
/** Keep the panel mounted when hidden. */
|
|
@@ -28,11 +46,20 @@ export declare const Tabs: {
|
|
|
28
46
|
export interface BreadcrumbItem {
|
|
29
47
|
label: React.ReactNode;
|
|
30
48
|
href?: string;
|
|
49
|
+
/** Shown in place of the label while the crumb is collapsed. */
|
|
50
|
+
icon?: React.ReactNode;
|
|
31
51
|
}
|
|
32
52
|
export interface BreadcrumbsProps extends React.HTMLAttributes<HTMLElement> {
|
|
33
53
|
items: BreadcrumbItem[];
|
|
54
|
+
/**
|
|
55
|
+
* Collapse the crumbs between the first and the current one to their icon
|
|
56
|
+
* (or an ellipsis); each eases open to its full label on hover and keyboard
|
|
57
|
+
* focus, and every crumb opens while focus is in the trail. The full labels
|
|
58
|
+
* stay in the accessibility tree. Default false.
|
|
59
|
+
*/
|
|
60
|
+
collapse?: boolean;
|
|
34
61
|
}
|
|
35
|
-
export declare function Breadcrumbs({ items, className, ...rest }: BreadcrumbsProps): import("react/jsx-runtime").JSX.Element;
|
|
62
|
+
export declare function Breadcrumbs({ items, collapse, className, ...rest }: BreadcrumbsProps): import("react/jsx-runtime").JSX.Element;
|
|
36
63
|
export interface PaginationProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange'> {
|
|
37
64
|
pageCount: number;
|
|
38
65
|
page?: number;
|
|
@@ -40,14 +67,24 @@ export interface PaginationProps extends Omit<React.HTMLAttributes<HTMLElement>,
|
|
|
40
67
|
onPageChange?: (page: number) => void;
|
|
41
68
|
/** Pages shown either side of the current one. */
|
|
42
69
|
siblings?: number;
|
|
70
|
+
/**
|
|
71
|
+
* Previous and next are icon-first: the arrow shows and "Previous page" /
|
|
72
|
+
* "Next page" ease open on hover and focus. `always` keeps the words, false
|
|
73
|
+
* keeps arrows only (named by aria-label). Default true.
|
|
74
|
+
*/
|
|
75
|
+
expand?: Expand;
|
|
76
|
+
/** Page and step buttons are pills (single pages are circles); `rounded` opts out. */
|
|
77
|
+
shape?: 'pill' | 'rounded';
|
|
43
78
|
}
|
|
44
|
-
export declare function Pagination({ pageCount, page, defaultPage, onPageChange, siblings, className, ...rest }: PaginationProps): import("react/jsx-runtime").JSX.Element;
|
|
79
|
+
export declare function Pagination({ pageCount, page, defaultPage, onPageChange, siblings, expand, shape, className, ...rest }: PaginationProps): import("react/jsx-runtime").JSX.Element;
|
|
45
80
|
export interface SidebarProps extends React.HTMLAttributes<HTMLElement> {
|
|
46
81
|
label?: string;
|
|
82
|
+
/** Icon rail: items show their icon and the rail eases open to full labels on hover and focus. */
|
|
83
|
+
collapsed?: boolean;
|
|
47
84
|
header?: React.ReactNode;
|
|
48
85
|
footer?: React.ReactNode;
|
|
49
86
|
}
|
|
50
|
-
export declare function Sidebar({ label, header, footer, className, children, ...rest }: SidebarProps): import("react/jsx-runtime").JSX.Element;
|
|
87
|
+
export declare function Sidebar({ label, collapsed, header, footer, className, children, ...rest }: SidebarProps): import("react/jsx-runtime").JSX.Element;
|
|
51
88
|
export interface SidebarSectionProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
52
89
|
title?: React.ReactNode;
|
|
53
90
|
}
|
package/dist/navigation.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../src/navigation.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../src/navigation.tsx"],"names":[],"mappings":"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"}
|
package/dist/navigation.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { createContext, useContext, useId } from 'react';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useEffect, useId, useLayoutEffect, useRef } from 'react';
|
|
4
4
|
import { useNorthstar } from './provider.js';
|
|
5
5
|
import { rovingKeyDown } from './internal/layers.js';
|
|
6
|
+
import { withViewTransition } from './motion.js';
|
|
6
7
|
import { cx, useControllable } from './internal/util.js';
|
|
7
8
|
import { ChevronLeft, ChevronRight } from './internal/icons.js';
|
|
9
|
+
import { ExpandIcon, ExpandLabel, expandHost } from './internal/expand.js';
|
|
10
|
+
const useIsoLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
|
|
8
11
|
const TabsContext = createContext(null);
|
|
9
12
|
function useTabs(name) {
|
|
10
13
|
const v = useContext(TabsContext);
|
|
@@ -12,29 +15,81 @@ function useTabs(name) {
|
|
|
12
15
|
throw new Error(`<${name}> must be used inside <Tabs.Root>`);
|
|
13
16
|
return v;
|
|
14
17
|
}
|
|
15
|
-
function TabsRoot({ value, defaultValue, onValueChange, orientation = 'horizontal', className, ...rest }) {
|
|
16
|
-
const [v,
|
|
18
|
+
function TabsRoot({ value, defaultValue, onValueChange, orientation = 'horizontal', indicator = 'line', transition = false, shape = 'pill', className, ...rest }) {
|
|
19
|
+
const [v, setRaw] = useControllable(value, defaultValue ?? '', onValueChange);
|
|
20
|
+
const root = useRef(null);
|
|
21
|
+
const setV = (next) => {
|
|
22
|
+
if (next === v)
|
|
23
|
+
return;
|
|
24
|
+
if (transition)
|
|
25
|
+
withViewTransition(() => setRaw(next), root.current?.querySelector(':scope > .ns-tabs__content, :scope .ns-tabs__content'));
|
|
26
|
+
else
|
|
27
|
+
setRaw(next);
|
|
28
|
+
};
|
|
17
29
|
const baseId = `ns-tabs-${useId().replace(/:/g, '')}`;
|
|
18
|
-
return (_jsx(TabsContext.Provider, { value: { value: v, setValue: setV, baseId, orientation }, children: _jsx("div", { ...rest, className: cx('ns-tabs', `ns-tabs--${orientation}`, className) }) }));
|
|
30
|
+
return (_jsx(TabsContext.Provider, { value: { value: v, setValue: setV, baseId, orientation, indicator }, children: _jsx("div", { ...rest, ref: root, className: cx('ns-tabs', `ns-tabs--${orientation}`, transition && 'ns-tabs--vt', indicator !== 'line' && 'ns-tabs--pill', indicator === 'glass' && 'ns-tabs--glass', shape === 'rounded' && 'ns-tabs--rounded', className) }) }));
|
|
19
31
|
}
|
|
20
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Measures the selected tab and moves the indicator with a transform written
|
|
34
|
+
* straight to the element: no re-render, and a spring transition in CSS. Until
|
|
35
|
+
* it has measured (server HTML, no layout) each selected tab draws its own mark.
|
|
36
|
+
*/
|
|
37
|
+
function useIndicator(list, mark, value) {
|
|
38
|
+
useIsoLayoutEffect(() => {
|
|
39
|
+
const el = list.current;
|
|
40
|
+
const ind = mark.current;
|
|
41
|
+
if (!el || !ind)
|
|
42
|
+
return;
|
|
43
|
+
let frame = 0;
|
|
44
|
+
const place = () => {
|
|
45
|
+
const tab = el.querySelector('[role="tab"][aria-selected="true"]');
|
|
46
|
+
if (!tab || tab.offsetWidth === 0) {
|
|
47
|
+
el.removeAttribute('data-indicator');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
ind.style.width = `${tab.offsetWidth}px`;
|
|
51
|
+
ind.style.height = `${tab.offsetHeight}px`;
|
|
52
|
+
ind.style.transform = `translate(${tab.offsetLeft}px, ${tab.offsetTop}px)`;
|
|
53
|
+
// The first placement jumps into position; later ones slide.
|
|
54
|
+
if (!el.hasAttribute('data-indicator')) {
|
|
55
|
+
el.setAttribute('data-indicator', 'placed');
|
|
56
|
+
frame = requestAnimationFrame(() => el.setAttribute('data-indicator', 'ready'));
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
place();
|
|
60
|
+
const ro = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(place);
|
|
61
|
+
ro?.observe(el);
|
|
62
|
+
// Icon-first tabs change width as their labels ease open; follow them.
|
|
63
|
+
el.querySelectorAll('[role="tab"]').forEach((t) => ro?.observe(t));
|
|
64
|
+
return () => {
|
|
65
|
+
ro?.disconnect();
|
|
66
|
+
cancelAnimationFrame(frame);
|
|
67
|
+
};
|
|
68
|
+
}, [value]);
|
|
69
|
+
}
|
|
70
|
+
function TabsList({ className, onKeyDown, children, ...rest }) {
|
|
21
71
|
const ctx = useTabs('Tabs.List');
|
|
22
72
|
const { label, ...props } = rest;
|
|
23
|
-
|
|
73
|
+
const list = useRef(null);
|
|
74
|
+
const mark = useRef(null);
|
|
75
|
+
useIndicator(list, mark, ctx.value);
|
|
76
|
+
return (_jsxs("div", { role: "tablist", "aria-label": label, "aria-orientation": ctx.orientation, ...props, ref: list, className: cx('ns-tabs__list', className), onKeyDown: (e) => {
|
|
24
77
|
onKeyDown?.(e);
|
|
25
78
|
const t = rovingKeyDown(e, '[role="tab"]', ctx.orientation);
|
|
26
79
|
const v = t?.getAttribute('data-value');
|
|
27
80
|
if (v)
|
|
28
81
|
ctx.setValue(v);
|
|
29
|
-
} }));
|
|
82
|
+
}, children: [_jsx("span", { ref: mark, className: "ns-tabs__indicator", "aria-hidden": "true" }), children] }));
|
|
30
83
|
}
|
|
31
|
-
function TabsTrigger({ value, className, onClick, ...rest }) {
|
|
84
|
+
function TabsTrigger({ value, icon, expand = false, className, onClick, children, ...rest }) {
|
|
32
85
|
const ctx = useTabs('Tabs.Trigger');
|
|
33
86
|
const selected = ctx.value === value;
|
|
34
|
-
|
|
87
|
+
const hasIcon = icon !== undefined && icon !== null && icon !== false;
|
|
88
|
+
const host = hasIcon ? expandHost(expand) : {};
|
|
89
|
+
return (_jsxs("button", { type: "button", role: "tab", id: `${ctx.baseId}-tab-${value}`, "aria-selected": selected, "aria-controls": `${ctx.baseId}-panel-${value}`, tabIndex: selected ? 0 : -1, "data-value": value, "data-expand": host['data-expand'], ...rest, className: cx('ns-tabs__trigger', hasIcon && 'ns-tabs__trigger--icon', host.className, className), onClick: (e) => {
|
|
35
90
|
onClick?.(e);
|
|
36
91
|
ctx.setValue(value);
|
|
37
|
-
} }));
|
|
92
|
+
}, children: [hasIcon && _jsx(ExpandIcon, { className: "ns-tabs__icon", children: icon }), host.className ? _jsx(ExpandLabel, { className: "ns-tabs__label", children: children }) : children] }));
|
|
38
93
|
}
|
|
39
94
|
function TabsContent({ value, forceMount = false, className, ...rest }) {
|
|
40
95
|
const ctx = useTabs('Tabs.Content');
|
|
@@ -45,11 +100,14 @@ function TabsContent({ value, forceMount = false, className, ...rest }) {
|
|
|
45
100
|
}
|
|
46
101
|
/** Tabs with automatic activation: arrow keys move and select, Home/End jump. */
|
|
47
102
|
export const Tabs = { Root: TabsRoot, List: TabsList, Trigger: TabsTrigger, Content: TabsContent };
|
|
48
|
-
export function Breadcrumbs({ items, className, ...rest }) {
|
|
103
|
+
export function Breadcrumbs({ items, collapse = false, className, ...rest }) {
|
|
49
104
|
const { messages } = useNorthstar();
|
|
50
|
-
return (_jsx("nav", { "aria-label": messages.breadcrumbs, ...rest, className: cx('ns-breadcrumbs', className), children: _jsx("ol", { children: items.map((item, i) => {
|
|
105
|
+
return (_jsx("nav", { "aria-label": messages.breadcrumbs, ...rest, className: cx('ns-breadcrumbs', collapse && 'ns-breadcrumbs--collapse', className), children: _jsx("ol", { children: items.map((item, i) => {
|
|
51
106
|
const last = i === items.length - 1;
|
|
52
|
-
|
|
107
|
+
const folded = collapse && i > 0 && !last;
|
|
108
|
+
const body = folded ? (_jsxs(_Fragment, { children: [_jsx(ExpandIcon, { className: "ns-breadcrumbs__icon", children: item.icon ?? _jsx("span", { className: "ns-breadcrumbs__more", children: "\u2026" }) }), _jsx(ExpandLabel, { children: item.label })] })) : (item.label);
|
|
109
|
+
const host = folded ? expandHost(true) : {};
|
|
110
|
+
return (_jsxs("li", { className: host.className, "data-expand": host['data-expand'], children: [last || !item.href ? (_jsx("span", { "aria-current": last ? 'page' : undefined, children: body })) : (_jsx("a", { href: item.href, children: body })), !last && (_jsx("span", { className: "ns-breadcrumbs__sep", "aria-hidden": "true", children: "/" }))] }, i));
|
|
53
111
|
}) }) }));
|
|
54
112
|
}
|
|
55
113
|
function range(page, count, siblings) {
|
|
@@ -67,21 +125,30 @@ function range(page, count, siblings) {
|
|
|
67
125
|
out.push(count);
|
|
68
126
|
return out;
|
|
69
127
|
}
|
|
70
|
-
export function Pagination({ pageCount, page, defaultPage, onPageChange, siblings = 1, className, ...rest }) {
|
|
128
|
+
export function Pagination({ pageCount, page, defaultPage, onPageChange, siblings = 1, expand = true, shape = 'pill', className, ...rest }) {
|
|
71
129
|
const { messages } = useNorthstar();
|
|
72
130
|
const [p, setP] = useControllable(page, defaultPage ?? 1, onPageChange);
|
|
73
131
|
const go = (n) => setP(Math.min(pageCount, Math.max(1, n)));
|
|
74
|
-
return (_jsxs("nav", { "aria-label": messages.pagination, ...rest, className: cx('ns-pagination', className), children: [_jsx(
|
|
132
|
+
return (_jsxs("nav", { "aria-label": messages.pagination, ...rest, className: cx('ns-pagination', shape === 'rounded' && 'ns-pagination--rounded', className), children: [_jsx(PageStep, { expand: expand, label: messages.previousPage, disabled: p <= 1, onClick: () => go(p - 1), children: _jsx(ChevronLeft, {}) }), _jsx("ul", { children: range(p, pageCount, siblings).map((n, i) => n === 'gap' ? (_jsx("li", { className: "ns-pagination__gap", "aria-hidden": "true", children: "\u2026" }, `gap-${i}`)) : (_jsx("li", { children: _jsx("button", { type: "button", className: "ns-button ns-button--ghost ns-size-sm ns-pagination__page", "aria-label": messages.page(n), "aria-current": n === p ? 'page' : undefined, onClick: () => go(n), children: n }) }, n))) }), _jsx(PageStep, { expand: expand, end: true, label: messages.nextPage, disabled: p >= pageCount, onClick: () => go(p + 1), children: _jsx(ChevronRight, {}) })] }));
|
|
133
|
+
}
|
|
134
|
+
function PageStep({ expand, end = false, label, disabled, onClick, children }) {
|
|
135
|
+
if (!expand) {
|
|
136
|
+
return (_jsx("button", { type: "button", className: "ns-button ns-button--ghost ns-size-sm ns-icon-button", "aria-label": label, disabled: disabled, onClick: onClick, children: children }));
|
|
137
|
+
}
|
|
138
|
+
const host = expandHost(expand);
|
|
139
|
+
const icon = _jsx(ExpandIcon, { children: children });
|
|
140
|
+
const text = _jsx(ExpandLabel, { side: end ? 'end' : 'start', children: label });
|
|
141
|
+
return (_jsxs("button", { type: "button", className: cx('ns-button ns-button--ghost ns-size-sm ns-icon-button ns-icon-button--expand ns-button--expand ns-pagination__step', host.className), "data-expand": host['data-expand'], disabled: disabled, onClick: onClick, children: [end ? text : icon, end ? icon : text] }));
|
|
75
142
|
}
|
|
76
|
-
export function Sidebar({ label, header, footer, className, children, ...rest }) {
|
|
143
|
+
export function Sidebar({ label, collapsed = false, header, footer, className, children, ...rest }) {
|
|
77
144
|
const { messages } = useNorthstar();
|
|
78
|
-
return (_jsxs("nav", { "aria-label": label ?? messages.sidebar, ...rest, className: cx('ns-sidebar', className), children: [header && _jsx("div", { className: "ns-sidebar__header", children: header }), _jsx("div", { className: "ns-sidebar__body", children: children }), footer && _jsx("div", { className: "ns-sidebar__footer", children: footer })] }));
|
|
145
|
+
return (_jsxs("nav", { "aria-label": label ?? messages.sidebar, ...rest, className: cx('ns-sidebar', collapsed && 'ns-sidebar--collapsed', className), children: [header && _jsx("div", { className: "ns-sidebar__header", children: header }), _jsx("div", { className: "ns-sidebar__body", children: children }), footer && _jsx("div", { className: "ns-sidebar__footer", children: footer })] }));
|
|
79
146
|
}
|
|
80
147
|
export function SidebarSection({ title, className, children, ...rest }) {
|
|
81
148
|
const id = useId();
|
|
82
149
|
return (_jsxs("div", { role: "group", "aria-labelledby": title ? id : undefined, ...rest, className: cx('ns-sidebar__section', className), children: [title && (_jsx("div", { id: id, className: "ns-sidebar__title", children: title })), _jsx("ul", { children: children })] }));
|
|
83
150
|
}
|
|
84
151
|
export function SidebarItem({ icon, active = false, meta, className, children, ...rest }) {
|
|
85
|
-
return (_jsx("li", { children: _jsxs("a", { "aria-current": active ? 'page' : undefined, ...rest, className: cx('ns-sidebar__item', className), children: [icon && (_jsx("span", { className: "ns-sidebar__icon", "aria-hidden": "true", children: icon })), _jsx(
|
|
152
|
+
return (_jsx("li", { children: _jsxs("a", { "aria-current": active ? 'page' : undefined, ...rest, className: cx('ns-sidebar__item', className), children: [icon && (_jsx("span", { className: "ns-sidebar__icon", "aria-hidden": "true", children: icon })), _jsx(ExpandLabel, { className: "ns-sidebar__text", children: children }), meta !== undefined && _jsx("span", { className: "ns-sidebar__meta", children: meta })] }) }));
|
|
86
153
|
}
|
|
87
154
|
//# sourceMappingURL=navigation.js.map
|