@rnine/nineos 0.0.0-stage → 0.1.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 +93 -2
- package/dist/_variables.scss +199 -0
- package/dist/components/Chrome/Chrome.d.ts +27 -0
- package/dist/components/Chrome/Chrome.js +45 -0
- package/dist/components/Chrome/Chrome.js.map +1 -0
- package/dist/components/Chrome/Chrome.module.js +14 -0
- package/dist/components/Chrome/Chrome.module.js.map +1 -0
- package/dist/components/CommandLine/ChipBar.d.ts +20 -0
- package/dist/components/CommandLine/ChipBar.js +25 -0
- package/dist/components/CommandLine/ChipBar.js.map +1 -0
- package/dist/components/CommandLine/ChipBar.module.js +9 -0
- package/dist/components/CommandLine/ChipBar.module.js.map +1 -0
- package/dist/components/CommandLine/CommandLine.d.ts +38 -0
- package/dist/components/CommandLine/CommandLine.js +121 -0
- package/dist/components/CommandLine/CommandLine.js.map +1 -0
- package/dist/components/CommandLine/CommandLine.module.js +13 -0
- package/dist/components/CommandLine/CommandLine.module.js.map +1 -0
- package/dist/components/Hint/Hint.d.ts +22 -0
- package/dist/components/Hint/Hint.js +52 -0
- package/dist/components/Hint/Hint.js.map +1 -0
- package/dist/components/Hint/Hint.module.js +11 -0
- package/dist/components/Hint/Hint.module.js.map +1 -0
- package/dist/components/Panel/PanelStage.d.ts +19 -0
- package/dist/components/Panel/PanelStage.js +67 -0
- package/dist/components/Panel/PanelStage.js.map +1 -0
- package/dist/components/Panel/PanelStage.module.js +10 -0
- package/dist/components/Panel/PanelStage.module.js.map +1 -0
- package/dist/components.css +2 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +9 -0
- package/dist/motion/DecryptText.d.ts +9 -0
- package/dist/motion/DecryptText.js +87 -0
- package/dist/motion/DecryptText.js.map +1 -0
- package/dist/motion/DecryptText.module.js +12 -0
- package/dist/motion/DecryptText.module.js.map +1 -0
- package/dist/motion/DotField.d.ts +8 -0
- package/dist/motion/DotField.js +116 -0
- package/dist/motion/DotField.js.map +1 -0
- package/dist/motion/DotField.module.js +8 -0
- package/dist/motion/DotField.module.js.map +1 -0
- package/dist/motion/Monogram.d.ts +7 -0
- package/dist/motion/Monogram.js +40 -0
- package/dist/motion/Monogram.js.map +1 -0
- package/dist/motion/Monogram.module.js +10 -0
- package/dist/motion/Monogram.module.js.map +1 -0
- package/dist/motion/PageEffects.d.ts +2 -0
- package/dist/motion/PageEffects.js +45 -0
- package/dist/motion/PageEffects.js.map +1 -0
- package/dist/motion/PageEffects.module.js +8 -0
- package/dist/motion/PageEffects.module.js.map +1 -0
- package/dist/motion/Terminal.d.ts +12 -0
- package/dist/motion/Terminal.js +57 -0
- package/dist/motion/Terminal.js.map +1 -0
- package/dist/motion/Terminal.module.js +12 -0
- package/dist/motion/Terminal.module.js.map +1 -0
- package/dist/motion/VelocityStrip.d.ts +7 -0
- package/dist/motion/VelocityStrip.js +46 -0
- package/dist/motion/VelocityStrip.js.map +1 -0
- package/dist/motion/VelocityStrip.module.js +9 -0
- package/dist/motion/VelocityStrip.module.js.map +1 -0
- package/dist/motion/signal.d.ts +15 -0
- package/dist/motion/signal.js +78 -0
- package/dist/motion/signal.js.map +1 -0
- package/dist/motion/useDecrypt.d.ts +21 -0
- package/dist/motion/useDecrypt.js +45 -0
- package/dist/motion/useDecrypt.js.map +1 -0
- package/dist/motion.d.ts +10 -0
- package/dist/motion.js +10 -0
- package/dist/nineos-base.css +54 -0
- package/dist/provider/ThemeProvider.d.ts +25 -0
- package/dist/provider/ThemeProvider.js +44 -0
- package/dist/provider/ThemeProvider.js.map +1 -0
- package/dist/styles.css +637 -0
- package/dist/tokens.css +470 -0
- package/dist/tokens.d.ts +2446 -0
- package/dist/tokens.js +2446 -0
- package/fonts.css +9 -0
- package/package.json +114 -4
- package/scss/_glitch.scss +23 -0
- package/scss/_index.scss +6 -0
- package/scss/base.scss +24 -0
- package/theme.json +9 -0
- package/tokens/color.json +46 -0
- package/tokens/layout.json +29 -0
- package/tokens/motion.json +18 -0
- package/tokens/palettes/acid.json +65 -0
- package/tokens/palettes/volt.json +65 -0
- package/tokens/shape.json +26 -0
- package/tokens/space.json +19 -0
- package/tokens/themes/dark.json +81 -0
- package/tokens/themes/light.json +106 -0
- package/tokens/typography.json +58 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
var e = {
|
|
2
|
+
root: "r9-CommandLine-module_root_H9Abl",
|
|
3
|
+
prompt: "r9-CommandLine-module_prompt_7lvgy",
|
|
4
|
+
label: "r9-CommandLine-module_label_Xnb9w",
|
|
5
|
+
input: "r9-CommandLine-module_input_qNwiM",
|
|
6
|
+
run: "r9-CommandLine-module_run_gErnw",
|
|
7
|
+
list: "r9-CommandLine-module_list_FaLjW",
|
|
8
|
+
option: "r9-CommandLine-module_option_reZv-"
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
export { e as default };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=CommandLine.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommandLine.module.js","names":[],"sources":["../../../src/components/CommandLine/CommandLine.module.scss"],"sourcesContent":["@use \"@rnine/nineos/scss\" as r9;\n\n.root {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: r9.$space-2;\n min-width: 0;\n}\n\n.prompt {\n display: flex;\n align-items: center;\n gap: r9.$space-3;\n padding: 0 r9.$space-2 0 r9.$space-4;\n background: r9.$surface-raised;\n @include r9.hairline(r9.$line-strong);\n @include r9.corner(md);\n\n // Clip-path cuts outlines: the focus edge is drawn inside, in magenta\n &:focus-within { box-shadow: inset 0 0 0 2px r9.$accent; }\n}\n\n.label {\n @include r9.type(code);\n color: r9.$accent-text;\n white-space: nowrap;\n}\n\n.input {\n flex: 1;\n min-width: 0;\n height: 48px;\n border: 0;\n outline: 0;\n background: transparent;\n color: r9.$fg;\n caret-color: r9.$accent;\n // 16px keeps iOS from zooming in on focus\n font: r9.$font-weight-regular 1rem r9.$font-mono;\n\n &::placeholder { color: r9.$fg-subtle; }\n}\n\n.run {\n height: 36px;\n padding: 0 r9.$space-4;\n border: 0;\n cursor: pointer;\n background: r9.$accent;\n color: r9.$on-accent;\n font: r9.$font-weight-semibold r9.$font-size-sm r9.$font-body;\n @include r9.corner(sm);\n\n &:hover { background: r9.$accent-hover; }\n &:focus-visible { outline: 0; box-shadow: inset 0 0 0 2px r9.$on-accent, inset 0 0 0 4px r9.$accent; }\n}\n\n.list {\n position: absolute;\n left: 0;\n right: 0;\n bottom: calc(100% + #{r9.$space-2});\n z-index: r9.$z-overlay;\n max-height: 50vh;\n overflow-y: auto;\n margin: 0;\n padding: r9.$space-1;\n list-style: none;\n background: r9.$surface-raised;\n @include r9.hairline(r9.$line-strong);\n}\n\n.option {\n display: grid;\n grid-template-columns: minmax(0, 11rem) minmax(0, 1fr);\n gap: r9.$space-3;\n padding: r9.$space-2 r9.$space-3;\n cursor: pointer;\n @include r9.type(body-sm);\n\n code { font-family: r9.$font-mono; color: r9.$fg; }\n span { color: r9.$fg-muted; }\n\n &[aria-selected=\"true\"],\n &:hover { background: r9.$accent-soft; box-shadow: inset 3px 0 0 r9.$accent; }\n\n @include r9.down(sm) { grid-template-columns: minmax(0, 1fr); gap: 2px; }\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ReactNode, type RefObject } from 'react';
|
|
2
|
+
type Props = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
/** The element the hint points at. */
|
|
5
|
+
anchor: RefObject<HTMLElement | null>;
|
|
6
|
+
/** top: above the anchor, pointing down. bottom: below it, pointing up. */
|
|
7
|
+
placement?: 'top' | 'bottom';
|
|
8
|
+
/** Align the hint with the anchor's start or end edge. Default start. */
|
|
9
|
+
align?: 'start' | 'end';
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
onDismiss: () => void;
|
|
12
|
+
dismissLabel?: string;
|
|
13
|
+
/** Hide on its own after this many ms (calls onDismiss). */
|
|
14
|
+
autoHide?: number;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* A one-time onboarding hint pointing at part of the UI. Show each hint once,
|
|
18
|
+
* only after the person has had a moment to look around (see the brand kit's
|
|
19
|
+
* Patterns). Magenta element: the pointer tick.
|
|
20
|
+
*/
|
|
21
|
+
export declare function Hint({ open, anchor, placement, align, children, onDismiss, dismissLabel, autoHide }: Props): import("react").ReactPortal | null;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import e from "./Hint.module.js";
|
|
2
|
+
import { cx as t } from "@rnine/core/kit";
|
|
3
|
+
import { useEffect as n, useLayoutEffect as r, useRef as i } from "react";
|
|
4
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
5
|
+
import { createPortal as s } from "react-dom";
|
|
6
|
+
//#region src/components/Hint/Hint.tsx
|
|
7
|
+
var c = 14, l = 16;
|
|
8
|
+
function u({ open: u, anchor: d, placement: f = "top", align: p = "start", children: m, onDismiss: h, dismissLabel: g = "Got it", autoHide: _ }) {
|
|
9
|
+
let v = i(null);
|
|
10
|
+
return r(() => {
|
|
11
|
+
if (!u) return;
|
|
12
|
+
let e = () => {
|
|
13
|
+
let e = v.current, t = d.current;
|
|
14
|
+
if (!e || !t) return;
|
|
15
|
+
let n = t.getBoundingClientRect(), r = e.offsetWidth, i = e.offsetHeight, a = Math.max(l, Math.min(window.innerWidth - r - l, p === "start" ? n.left : n.right - r)), o = f === "top" ? n.top - i - c : n.bottom + c, s = p === "start" ? n.left + 32 : n.left + n.width / 2;
|
|
16
|
+
e.style.left = `${a}px`, e.style.top = `${o}px`, e.style.setProperty("--tick-x", `${Math.max(12, Math.min(r - 22, s - a - 5))}px`);
|
|
17
|
+
};
|
|
18
|
+
return e(), window.addEventListener("resize", e), window.addEventListener("scroll", e, { passive: !0 }), () => {
|
|
19
|
+
window.removeEventListener("resize", e), window.removeEventListener("scroll", e);
|
|
20
|
+
};
|
|
21
|
+
}, [
|
|
22
|
+
u,
|
|
23
|
+
d,
|
|
24
|
+
f,
|
|
25
|
+
p
|
|
26
|
+
]), n(() => {
|
|
27
|
+
if (!u || !_) return;
|
|
28
|
+
let e = window.setTimeout(h, _);
|
|
29
|
+
return () => window.clearTimeout(e);
|
|
30
|
+
}, [
|
|
31
|
+
u,
|
|
32
|
+
_,
|
|
33
|
+
h
|
|
34
|
+
]), u ? s(/* @__PURE__ */ o("div", {
|
|
35
|
+
ref: v,
|
|
36
|
+
role: "status",
|
|
37
|
+
className: t(e.hint, f === "top" ? e.top : e.bottom),
|
|
38
|
+
children: [/* @__PURE__ */ a("div", {
|
|
39
|
+
className: e.body,
|
|
40
|
+
children: m
|
|
41
|
+
}), /* @__PURE__ */ a("button", {
|
|
42
|
+
type: "button",
|
|
43
|
+
className: e.dismiss,
|
|
44
|
+
onClick: h,
|
|
45
|
+
children: g
|
|
46
|
+
})]
|
|
47
|
+
}), document.body) : null;
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
export { u as Hint };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=Hint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Hint.js","names":[],"sources":["../../../src/components/Hint/Hint.tsx"],"sourcesContent":["import { useEffect, useLayoutEffect, useRef, type ReactNode, type RefObject } from 'react';\nimport { createPortal } from 'react-dom';\nimport { cx } from '@rnine/core/kit';\nimport styles from './Hint.module.scss';\n\ntype Props = {\n open: boolean;\n /** The element the hint points at. */\n anchor: RefObject<HTMLElement | null>;\n /** top: above the anchor, pointing down. bottom: below it, pointing up. */\n placement?: 'top' | 'bottom';\n /** Align the hint with the anchor's start or end edge. Default start. */\n align?: 'start' | 'end';\n children: ReactNode;\n onDismiss: () => void;\n dismissLabel?: string;\n /** Hide on its own after this many ms (calls onDismiss). */\n autoHide?: number;\n};\n\nconst GAP = 14;\nconst EDGE = 16;\n\n/**\n * A one-time onboarding hint pointing at part of the UI. Show each hint once,\n * only after the person has had a moment to look around (see the brand kit's\n * Patterns). Magenta element: the pointer tick.\n */\nexport function Hint({ open, anchor, placement = 'top', align = 'start', children, onDismiss, dismissLabel = 'Got it', autoHide }: Props) {\n const ref = useRef<HTMLDivElement>(null);\n\n // Position next to the anchor; written straight to the element (no re-render)\n useLayoutEffect(() => {\n if (!open) return;\n const place = () => {\n const el = ref.current;\n const a = anchor.current;\n if (!el || !a) return;\n const r = a.getBoundingClientRect();\n const w = el.offsetWidth;\n const h = el.offsetHeight;\n const left = Math.max(EDGE, Math.min(window.innerWidth - w - EDGE, align === 'start' ? r.left : r.right - w));\n const top = placement === 'top' ? r.top - h - GAP : r.bottom + GAP;\n const target = align === 'start' ? r.left + 32 : r.left + r.width / 2;\n el.style.left = `${left}px`;\n el.style.top = `${top}px`;\n el.style.setProperty('--tick-x', `${Math.max(12, Math.min(w - 22, target - left - 5))}px`);\n };\n place();\n window.addEventListener('resize', place);\n window.addEventListener('scroll', place, { passive: true });\n return () => {\n window.removeEventListener('resize', place);\n window.removeEventListener('scroll', place);\n };\n }, [open, anchor, placement, align]);\n\n useEffect(() => {\n if (!open || !autoHide) return;\n const t = window.setTimeout(onDismiss, autoHide);\n return () => window.clearTimeout(t);\n }, [open, autoHide, onDismiss]);\n\n if (!open) return null;\n return createPortal(\n <div ref={ref} role=\"status\" className={cx(styles.hint, placement === 'top' ? styles.top : styles.bottom)}>\n <div className={styles.body}>{children}</div>\n <button type=\"button\" className={styles.dismiss} onClick={onDismiss}>\n {dismissLabel}\n </button>\n </div>,\n document.body,\n );\n}\n"],"mappings":";;;;;;AAoBA,IAAM,IAAM,IACN,IAAO;AAOb,SAAgB,EAAK,EAAE,SAAM,WAAQ,eAAY,OAAO,WAAQ,SAAS,aAAU,cAAW,kBAAe,UAAU,eAAmB;CACxI,IAAM,IAAM,EAAuB,IAAI;CAmCvC,OAhCA,QAAsB;EACpB,IAAI,CAAC,GAAM;EACX,IAAM,UAAc;GAClB,IAAM,IAAK,EAAI,SACT,IAAI,EAAO;GACjB,IAAI,CAAC,KAAM,CAAC,GAAG;GACf,IAAM,IAAI,EAAE,sBAAsB,GAC5B,IAAI,EAAG,aACP,IAAI,EAAG,cACP,IAAO,KAAK,IAAI,GAAM,KAAK,IAAI,OAAO,aAAa,IAAI,GAAM,MAAU,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,GACtG,IAAM,MAAc,QAAQ,EAAE,MAAM,IAAI,IAAM,EAAE,SAAS,GACzD,IAAS,MAAU,UAAU,EAAE,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ;GAGpE,AAFA,EAAG,MAAM,OAAO,GAAG,EAAK,KACxB,EAAG,MAAM,MAAM,GAAG,EAAI,KACtB,EAAG,MAAM,YAAY,YAAY,GAAG,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,IAAS,IAAO,CAAC,CAAC,EAAE,GAAG;EAC3F;EAIA,OAHA,EAAM,GACN,OAAO,iBAAiB,UAAU,CAAK,GACvC,OAAO,iBAAiB,UAAU,GAAO,EAAE,SAAS,GAAK,CAAC,SAC7C;GAEX,AADA,OAAO,oBAAoB,UAAU,CAAK,GAC1C,OAAO,oBAAoB,UAAU,CAAK;EAC5C;CACF,GAAG;EAAC;EAAM;EAAQ;EAAW;CAAK,CAAC,GAEnC,QAAgB;EACd,IAAI,CAAC,KAAQ,CAAC,GAAU;EACxB,IAAM,IAAI,OAAO,WAAW,GAAW,CAAQ;EAC/C,aAAa,OAAO,aAAa,CAAC;CACpC,GAAG;EAAC;EAAM;EAAU;CAAS,CAAC,GAEzB,IACE,EACL,kBAAC,OAAD;EAAU;EAAK,MAAK;EAAS,WAAW,EAAG,EAAO,MAAM,MAAc,QAAQ,EAAO,MAAM,EAAO,MAAM;YAAxG,CACE,kBAAC,OAAD;GAAK,WAAW,EAAO;GAAO;EAAc,CAAA,GAC5C,kBAAC,UAAD;GAAQ,MAAK;GAAS,WAAW,EAAO;GAAS,SAAS;aACvD;EACK,CAAA,CACL;KACL,SAAS,IACX,IATkB;AAUpB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var e = {
|
|
2
|
+
hint: "r9-Hint-module_hint_n0MWn",
|
|
3
|
+
top: "r9-Hint-module_top_W47pN",
|
|
4
|
+
bottom: "r9-Hint-module_bottom_LXFOs",
|
|
5
|
+
body: "r9-Hint-module_body_kMVt9",
|
|
6
|
+
dismiss: "r9-Hint-module_dismiss_J3Hb5"
|
|
7
|
+
};
|
|
8
|
+
//#endregion
|
|
9
|
+
export { e as default };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=Hint.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Hint.module.js","names":[],"sources":["../../../src/components/Hint/Hint.module.scss"],"sourcesContent":["@use \"@rnine/nineos/scss\" as r9;\n\n.hint {\n position: fixed;\n z-index: r9.$z-toast;\n width: max-content;\n max-width: min(320px, calc(100vw - 32px));\n display: flex;\n align-items: flex-start;\n gap: r9.$space-4;\n padding: r9.$space-3 r9.$space-4;\n background: r9.$inverse-bg;\n color: r9.$inverse-fg;\n @include r9.type(body-sm);\n box-shadow: 0 12px 32px rgb(0 0 0 / 0.28);\n @include r9.motion-safe { animation: r9.keyframes(reveal) r9.$duration-base r9.$easing-out; }\n\n // The pointer tick: the hint's magenta element\n &::after {\n content: \"\";\n position: absolute;\n left: var(--tick-x, 24px);\n width: 10px;\n height: 10px;\n background: r9.$accent;\n }\n}\n\n.top::after { top: 100%; }\n.bottom::after { bottom: 100%; }\n\n.body {\n min-width: 0;\n\n p { margin: 0; }\n strong { font-weight: r9.$font-weight-semibold; }\n code { font-family: r9.$font-mono; }\n}\n\n.dismiss {\n flex: none;\n padding: 2px 0;\n border: 0;\n background: none;\n color: inherit;\n cursor: pointer;\n @include r9.type(caption);\n text-decoration: underline;\n text-underline-offset: 3px;\n opacity: 0.8;\n\n &:hover { opacity: 1; }\n &:focus-visible { outline: 2px solid r9.$accent; outline-offset: 2px; }\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
type Props = {
|
|
3
|
+
/** Identifies the panel to show (e.g. the command that opened it). null shows an empty stage. */
|
|
4
|
+
panelKey: string | null;
|
|
5
|
+
/** The panel for panelKey (usually a <Panel>). Ignored while panelKey is null. */
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Shows one panel at a time and switches with the NineOS "signal swap": the
|
|
11
|
+
* old panel signals out (RGB split, 220ms), then the new one is revealed
|
|
12
|
+
* top-down behind a magenta scan line. panelKey null closes to an empty stage.
|
|
13
|
+
*
|
|
14
|
+
* A panel on its way out always hands over to the newest panelKey, so fast
|
|
15
|
+
* switching ends on the last command and closing mid-swap ends empty.
|
|
16
|
+
* Nothing is left on the panels afterwards; with reduced motion, swaps are instant.
|
|
17
|
+
*/
|
|
18
|
+
export declare function PanelStage({ panelKey, children, className }: Props): import("react").JSX.Element;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { swapIn as e, swapOut as t } from "../../motion/signal.js";
|
|
2
|
+
import n from "./PanelStage.module.js";
|
|
3
|
+
import { PanelStageContext as r, cx as i } from "@rnine/core/kit";
|
|
4
|
+
import { useCallback as a, useLayoutEffect as o, useRef as s, useState as c } from "react";
|
|
5
|
+
import { useReducedMotion as l } from "@rnine/core/motion";
|
|
6
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
7
|
+
//#region src/components/Panel/PanelStage.tsx
|
|
8
|
+
function f({ panelKey: f, children: p, className: m }) {
|
|
9
|
+
let h = l(), g = s(null), _ = s(null), v = s(null), y = s(null), b = s(null), x = s(null), S = s(!1), C = a((e) => {
|
|
10
|
+
e ? y.current = e : (b.current = y.current, y.current = null);
|
|
11
|
+
}, []), [w, T] = c(f), [E, D] = c(0);
|
|
12
|
+
return w !== f && (T(f), D(w !== null && !h ? 340 : 120)), o(() => {
|
|
13
|
+
let n = g.current, r = _.current, i = y.current, a = b.current;
|
|
14
|
+
if (b.current = null, !S.current) {
|
|
15
|
+
S.current = !0, i && (i.dataset.shown = "true");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (!n || !r) return;
|
|
19
|
+
let o = () => {
|
|
20
|
+
let t = y.current;
|
|
21
|
+
n.style.minHeight = "", t && (t.style.visibility = "", t.dataset.shown = "true", h || e(t, v.current));
|
|
22
|
+
};
|
|
23
|
+
if (h) {
|
|
24
|
+
r.replaceChildren(), x.current = null, o();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (x.current) {
|
|
28
|
+
i && (i.style.visibility = "hidden");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (a && a.dataset.shown === "true") {
|
|
32
|
+
r.appendChild(a), a.inert = !0, x.current = a, n.style.minHeight = `${a.offsetHeight}px`, i && (i.style.visibility = "hidden"), t(a).then(() => {
|
|
33
|
+
a.remove(), x.current === a && (x.current = null), o();
|
|
34
|
+
});
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
o();
|
|
38
|
+
}, [f, h]), /* @__PURE__ */ u(r.Provider, {
|
|
39
|
+
value: { enterDelay: E },
|
|
40
|
+
children: /* @__PURE__ */ d("div", {
|
|
41
|
+
ref: g,
|
|
42
|
+
className: i(n.stage, m),
|
|
43
|
+
"aria-live": "polite",
|
|
44
|
+
children: [
|
|
45
|
+
/* @__PURE__ */ u("div", {
|
|
46
|
+
ref: _,
|
|
47
|
+
className: n.ghost,
|
|
48
|
+
"aria-hidden": "true"
|
|
49
|
+
}),
|
|
50
|
+
/* @__PURE__ */ u("div", {
|
|
51
|
+
ref: v,
|
|
52
|
+
className: n.scanbar,
|
|
53
|
+
"aria-hidden": "true"
|
|
54
|
+
}),
|
|
55
|
+
f !== null && /* @__PURE__ */ u("div", {
|
|
56
|
+
ref: C,
|
|
57
|
+
className: n.slot,
|
|
58
|
+
children: p
|
|
59
|
+
}, f)
|
|
60
|
+
]
|
|
61
|
+
})
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
export { f as PanelStage };
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=PanelStage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PanelStage.js","names":[],"sources":["../../../src/components/Panel/PanelStage.tsx"],"sourcesContent":["import { useCallback, useLayoutEffect, useRef, useState, type ReactNode } from 'react';\nimport { useReducedMotion } from '@rnine/core/motion';\nimport { SWAP_OUT_MS, swapIn, swapOut } from '../../motion/signal';\nimport { cx } from '@rnine/core/kit';\nimport { PanelStageContext } from '@rnine/core/kit';\nimport styles from './PanelStage.module.scss';\n\ntype Props = {\n /** Identifies the panel to show (e.g. the command that opened it). null shows an empty stage. */\n panelKey: string | null;\n /** The panel for panelKey (usually a <Panel>). Ignored while panelKey is null. */\n children?: ReactNode;\n className?: string;\n};\n\ntype Slot = HTMLDivElement & { inert: boolean };\n\n/**\n * Shows one panel at a time and switches with the NineOS \"signal swap\": the\n * old panel signals out (RGB split, 220ms), then the new one is revealed\n * top-down behind a magenta scan line. panelKey null closes to an empty stage.\n *\n * A panel on its way out always hands over to the newest panelKey, so fast\n * switching ends on the last command and closing mid-swap ends empty.\n * Nothing is left on the panels afterwards; with reduced motion, swaps are instant.\n */\nexport function PanelStage({ panelKey, children, className }: Props) {\n const reduced = useReducedMotion();\n const stageRef = useRef<HTMLDivElement>(null);\n const ghostRef = useRef<HTMLDivElement>(null);\n const barRef = useRef<HTMLDivElement>(null);\n const slot = useRef<Slot | null>(null);\n const detached = useRef<Slot | null>(null);\n const leaving = useRef<Slot | null>(null);\n const mounted = useRef(false);\n\n // The current panel's wrapper; when it unmounts, keep the element to animate it out\n const setSlot = useCallback((el: HTMLDivElement | null) => {\n if (el) slot.current = el as Slot;\n else {\n detached.current = slot.current;\n slot.current = null;\n }\n }, []);\n\n // Panels entering after an exit wait for it before their own effects start\n const [prevKey, setPrevKey] = useState(panelKey);\n const [enterDelay, setEnterDelay] = useState(0);\n if (prevKey !== panelKey) {\n setPrevKey(panelKey);\n setEnterDelay(prevKey !== null && !reduced ? SWAP_OUT_MS + 120 : 120);\n }\n\n useLayoutEffect(() => {\n const stage = stageRef.current;\n const ghost = ghostRef.current;\n const next = slot.current;\n const old = detached.current;\n detached.current = null;\n\n // First render: no transition, the panel is simply there\n if (!mounted.current) {\n mounted.current = true;\n if (next) next.dataset.shown = 'true';\n return;\n }\n if (!stage || !ghost) return;\n\n const reveal = () => {\n const current = slot.current;\n stage.style.minHeight = '';\n if (!current) return;\n current.style.visibility = '';\n current.dataset.shown = 'true';\n if (!reduced) swapIn(current, barRef.current);\n };\n\n if (reduced) {\n ghost.replaceChildren();\n leaving.current = null;\n reveal();\n return;\n }\n\n // A panel is already leaving: the newest one waits for it (hidden)\n if (leaving.current) {\n if (next) next.style.visibility = 'hidden';\n return;\n }\n\n // The panel that was showing leaves first, then the new one enters\n if (old && old.dataset.shown === 'true') {\n ghost.appendChild(old);\n old.inert = true;\n leaving.current = old;\n stage.style.minHeight = `${old.offsetHeight}px`;\n if (next) next.style.visibility = 'hidden';\n void swapOut(old).then(() => {\n old.remove();\n if (leaving.current === old) leaving.current = null;\n reveal();\n });\n return;\n }\n\n reveal();\n }, [panelKey, reduced]);\n\n return (\n <PanelStageContext.Provider value={{ enterDelay }}>\n <div ref={stageRef} className={cx(styles.stage, className)} aria-live=\"polite\">\n <div ref={ghostRef} className={styles.ghost} aria-hidden=\"true\" />\n <div ref={barRef} className={styles.scanbar} aria-hidden=\"true\" />\n {panelKey !== null && (\n <div key={panelKey} ref={setSlot} className={styles.slot}>\n {children}\n </div>\n )}\n </div>\n </PanelStageContext.Provider>\n );\n}\n"],"mappings":";;;;;;;AA0BA,SAAgB,EAAW,EAAE,aAAU,aAAU,gBAAoB;CACnE,IAAM,IAAU,EAAiB,GAC3B,IAAW,EAAuB,IAAI,GACtC,IAAW,EAAuB,IAAI,GACtC,IAAS,EAAuB,IAAI,GACpC,IAAO,EAAoB,IAAI,GAC/B,IAAW,EAAoB,IAAI,GACnC,IAAU,EAAoB,IAAI,GAClC,IAAU,EAAO,EAAK,GAGtB,IAAU,GAAa,MAA8B;EACzD,AAAI,IAAI,EAAK,UAAU,KAErB,EAAS,UAAU,EAAK,SACxB,EAAK,UAAU;CAEnB,GAAG,CAAC,CAAC,GAGC,CAAC,GAAS,KAAc,EAAS,CAAQ,GACzC,CAAC,GAAY,KAAiB,EAAS,CAAC;CA6D9C,OA5DI,MAAY,MACd,EAAW,CAAQ,GACnB,EAAc,MAAY,QAAQ,CAAC,IAAA,MAA8B,GAAG,IAGtE,QAAsB;EACpB,IAAM,IAAQ,EAAS,SACjB,IAAQ,EAAS,SACjB,IAAO,EAAK,SACZ,IAAM,EAAS;EAIrB,IAHA,EAAS,UAAU,MAGf,CAAC,EAAQ,SAAS;GAEpB,AADA,EAAQ,UAAU,IACd,MAAM,EAAK,QAAQ,QAAQ;GAC/B;EACF;EACA,IAAI,CAAC,KAAS,CAAC,GAAO;EAEtB,IAAM,UAAe;GACnB,IAAM,IAAU,EAAK;GACrB,EAAM,MAAM,YAAY,IACnB,MACL,EAAQ,MAAM,aAAa,IAC3B,EAAQ,QAAQ,QAAQ,QACnB,KAAS,EAAO,GAAS,EAAO,OAAO;EAC9C;EAEA,IAAI,GAAS;GAGX,AAFA,EAAM,gBAAgB,GACtB,EAAQ,UAAU,MAClB,EAAO;GACP;EACF;EAGA,IAAI,EAAQ,SAAS;GACnB,AAAI,MAAM,EAAK,MAAM,aAAa;GAClC;EACF;EAGA,IAAI,KAAO,EAAI,QAAQ,UAAU,QAAQ;GAMvC,AALA,EAAM,YAAY,CAAG,GACrB,EAAI,QAAQ,IACZ,EAAQ,UAAU,GAClB,EAAM,MAAM,YAAY,GAAG,EAAI,aAAa,KACxC,MAAM,EAAK,MAAM,aAAa,WAClC,EAAa,CAAG,EAAE,WAAW;IAG3B,AAFA,EAAI,OAAO,GACP,EAAQ,YAAY,MAAK,EAAQ,UAAU,OAC/C,EAAO;GACT,CAAC;GACD;EACF;EAEA,EAAO;CACT,GAAG,CAAC,GAAU,CAAO,CAAC,GAGpB,kBAAC,EAAkB,UAAnB;EAA4B,OAAO,EAAE,cAAW;YAC9C,kBAAC,OAAD;GAAK,KAAK;GAAU,WAAW,EAAG,EAAO,OAAO,CAAS;GAAG,aAAU;aAAtE;IACE,kBAAC,OAAD;KAAK,KAAK;KAAU,WAAW,EAAO;KAAO,eAAY;IAAQ,CAAA;IACjE,kBAAC,OAAD;KAAK,KAAK;KAAQ,WAAW,EAAO;KAAS,eAAY;IAAQ,CAAA;IAChE,MAAa,QACZ,kBAAC,OAAD;KAAoB,KAAK;KAAS,WAAW,EAAO;KACjD;IACE,GAFK,CAEL;GAEJ;;CACqB,CAAA;AAEhC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
var e = {
|
|
2
|
+
stage: "r9-PanelStage-module_stage_borrY",
|
|
3
|
+
ghost: "r9-PanelStage-module_ghost_JUO1L",
|
|
4
|
+
slot: "r9-PanelStage-module_slot_qdJUb",
|
|
5
|
+
scanbar: "r9-PanelStage-module_scanbar_O7owB"
|
|
6
|
+
};
|
|
7
|
+
//#endregion
|
|
8
|
+
export { e as default };
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=PanelStage.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PanelStage.module.js","names":[],"sources":["../../../src/components/Panel/PanelStage.module.scss"],"sourcesContent":["@use \"@rnine/nineos/scss\" as r9;\n\n.stage {\n position: relative;\n min-width: 0;\n}\n\n// The leaving panel plays its exit here, over the stage, outside React's tree\n.ghost {\n position: absolute;\n inset: 0 0 auto;\n pointer-events: none;\n z-index: 1;\n}\n\n.slot {\n min-width: 0;\n}\n\n// Runs down the new panel as it is revealed (swapIn)\n.scanbar {\n position: absolute;\n left: -6px;\n right: -6px;\n top: 0;\n height: 2px;\n z-index: 3;\n opacity: 0;\n pointer-events: none;\n background: r9.$accent;\n box-shadow: 0 0 14px r9.$accent, 0 0 2px r9.$accent;\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
.r9-Chrome-module_dock_laf9m{z-index:var(--r9-z-sticky);padding-left:max(var(--r9-container-gutter), env(safe-area-inset-left,0px));padding-right:max(var(--r9-container-gutter), env(safe-area-inset-right,0px));padding-top:var(--r9-space-3);padding-bottom:calc(var(--r9-space-3) + env(safe-area-inset-bottom,0px));background:var(--r9-glass);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-top:var(--r9-border-hair) solid var(--r9-line);position:fixed;bottom:0;left:0;right:0}.r9-Chrome-module_dockInner_4aQte{gap:var(--r9-space-2);flex-direction:column;width:100%;max-width:940px;margin-inline:auto;display:flex}.r9-Chrome-module_hud_pprK9{z-index:var(--r9-z-overlay);align-items:center;gap:var(--r9-space-3);display:flex;position:fixed}.r9-Chrome-module_topleft_b36X-{top:calc(14px + env(safe-area-inset-top,0px));left:max(16px, env(safe-area-inset-left,0px))}.r9-Chrome-module_topright_PpRWm{top:calc(14px + env(safe-area-inset-top,0px));right:max(16px, env(safe-area-inset-right,0px))}.r9-Chrome-module_bottomleft_13I-i{bottom:calc(14px + env(safe-area-inset-bottom,0px));left:max(16px, env(safe-area-inset-left,0px))}.r9-Chrome-module_bottomright_FhlYK{bottom:calc(14px + env(safe-area-inset-bottom,0px));right:max(16px, env(safe-area-inset-right,0px))}.r9-Chrome-module_kbd_823sZ{font-family:var(--r9-type-caption-font-family);font-weight:var(--r9-type-caption-font-weight);font-size:var(--r9-type-caption-font-size);line-height:var(--r9-type-caption-line-height);letter-spacing:var(--r9-type-caption-letter-spacing);color:var(--r9-fg-muted);white-space:nowrap;box-shadow:inset 0 0 0 var(--r9-border-hair) var(--r9-line-strong);padding:1px 6px;display:inline-block}.r9-ChipBar-module_bar_92cr1{gap:var(--r9-space-2);scrollbar-width:none;min-width:0;display:flex;overflow-x:auto}.r9-ChipBar-module_bar_92cr1::-webkit-scrollbar{display:none}.r9-ChipBar-module_chip_UQIxe{min-height:36px;padding:var(--r9-space-2) var(--r9-space-3);cursor:pointer;font-family:var(--r9-type-code-font-family);font-weight:var(--r9-type-code-font-weight);font-size:var(--r9-type-code-font-size);line-height:var(--r9-type-code-line-height);letter-spacing:var(--r9-type-code-letter-spacing);font-size:var(--r9-font-size-sm);color:var(--r9-fg);background:var(--r9-surface);box-shadow:inset 0 0 0 var(--r9-border-hair) var(--r9-line);transition:box-shadow var(--r9-duration-fast), color var(--r9-duration-fast);border:0;flex:none}.r9-ChipBar-module_chip_UQIxe:hover{box-shadow:inset 0 0 0 var(--r9-border-hair) var(--r9-accent);color:var(--r9-accent-text)}.r9-ChipBar-module_chip_UQIxe:focus-visible{outline:2px solid var(--r9-accent);outline-offset:2px}.r9-ChipBar-module_prefix_-WFJm{color:var(--r9-accent-text)}.r9-CommandLine-module_root_H9Abl{gap:var(--r9-space-2);flex-direction:column;min-width:0;display:flex;position:relative}.r9-CommandLine-module_prompt_7lvgy{align-items:center;gap:var(--r9-space-3);padding:0 var(--r9-space-2) 0 var(--r9-space-4);background:var(--r9-surface-raised);box-shadow:inset 0 0 0 var(--r9-border-hair) var(--r9-line-strong);clip-path:polygon(calc(var(--r9-corner-md) * var(--r9-shape-cut)) 0, calc(100% - 0px) 0, 100% 0px, 100% calc(100% - calc(var(--r9-corner-md) * var(--r9-shape-cut))), calc(100% - calc(var(--r9-corner-md) * var(--r9-shape-cut))) 100%, 0px 100%, 0 calc(100% - 0px), 0 calc(var(--r9-corner-md) * var(--r9-shape-cut)));border-radius:calc(var(--r9-corner-md) * var(--r9-shape-round));display:flex}.r9-CommandLine-module_prompt_7lvgy:focus-within{box-shadow:inset 0 0 0 2px var(--r9-accent)}.r9-CommandLine-module_label_Xnb9w{font-family:var(--r9-type-code-font-family);font-weight:var(--r9-type-code-font-weight);font-size:var(--r9-type-code-font-size);line-height:var(--r9-type-code-line-height);letter-spacing:var(--r9-type-code-letter-spacing);color:var(--r9-accent-text);white-space:nowrap}.r9-CommandLine-module_input_qNwiM{min-width:0;height:48px;color:var(--r9-fg);caret-color:var(--r9-accent);font:var(--r9-font-weight-regular) 1rem var(--r9-font-mono);background:0 0;border:0;outline:0;flex:1}.r9-CommandLine-module_input_qNwiM::placeholder{color:var(--r9-fg-subtle)}.r9-CommandLine-module_run_gErnw{height:36px;padding:0 var(--r9-space-4);cursor:pointer;background:var(--r9-accent);color:var(--r9-on-accent);font:var(--r9-font-weight-semibold) var(--r9-font-size-sm) var(--r9-font-body);clip-path:polygon(calc(var(--r9-corner-sm) * var(--r9-shape-cut)) 0, calc(100% - 0px) 0, 100% 0px, 100% calc(100% - calc(var(--r9-corner-sm) * var(--r9-shape-cut))), calc(100% - calc(var(--r9-corner-sm) * var(--r9-shape-cut))) 100%, 0px 100%, 0 calc(100% - 0px), 0 calc(var(--r9-corner-sm) * var(--r9-shape-cut)));border-radius:calc(var(--r9-corner-sm) * var(--r9-shape-round));border:0}.r9-CommandLine-module_run_gErnw:hover{background:var(--r9-accent-hover)}.r9-CommandLine-module_run_gErnw:focus-visible{box-shadow:inset 0 0 0 2px var(--r9-on-accent), inset 0 0 0 4px var(--r9-accent);outline:0}.r9-CommandLine-module_list_FaLjW{left:0;right:0;bottom:calc(100% + var(--r9-space-2));z-index:var(--r9-z-overlay);max-height:50vh;padding:var(--r9-space-1);background:var(--r9-surface-raised);box-shadow:inset 0 0 0 var(--r9-border-hair) var(--r9-line-strong);margin:0;list-style:none;position:absolute;overflow-y:auto}.r9-CommandLine-module_option_reZv-{gap:var(--r9-space-3);padding:var(--r9-space-2) var(--r9-space-3);cursor:pointer;font-family:var(--r9-type-body-sm-font-family);font-weight:var(--r9-type-body-sm-font-weight);font-size:var(--r9-type-body-sm-font-size);line-height:var(--r9-type-body-sm-line-height);letter-spacing:var(--r9-type-body-sm-letter-spacing);grid-template-columns:minmax(0,11rem) minmax(0,1fr);display:grid}.r9-CommandLine-module_option_reZv- code{font-family:var(--r9-font-mono);color:var(--r9-fg)}.r9-CommandLine-module_option_reZv- span{color:var(--r9-fg-muted)}.r9-CommandLine-module_option_reZv-[aria-selected=true],.r9-CommandLine-module_option_reZv-:hover{background:var(--r9-accent-soft);box-shadow:inset 3px 0 0 var(--r9-accent)}@media (width<=639.98px){.r9-CommandLine-module_option_reZv-{grid-template-columns:minmax(0,1fr);gap:2px}}.r9-Hint-module_hint_n0MWn{z-index:var(--r9-z-toast);align-items:flex-start;gap:var(--r9-space-4);width:max-content;max-width:min(320px,100vw - 32px);padding:var(--r9-space-3) var(--r9-space-4);background:var(--r9-inverse-bg);color:var(--r9-inverse-fg);font-family:var(--r9-type-body-sm-font-family);font-weight:var(--r9-type-body-sm-font-weight);font-size:var(--r9-type-body-sm-font-size);line-height:var(--r9-type-body-sm-line-height);letter-spacing:var(--r9-type-body-sm-letter-spacing);display:flex;position:fixed;box-shadow:0 12px 32px #00000047}@media (prefers-reduced-motion:no-preference){.r9-Hint-module_hint_n0MWn{animation:r9-reveal var(--r9-duration-base) var(--r9-easing-out)}}.r9-Hint-module_hint_n0MWn:after{content:"";left:var(--tick-x,24px);background:var(--r9-accent);width:10px;height:10px;position:absolute}.r9-Hint-module_top_W47pN:after{top:100%}.r9-Hint-module_bottom_LXFOs:after{bottom:100%}.r9-Hint-module_body_kMVt9{min-width:0}.r9-Hint-module_body_kMVt9 p{margin:0}.r9-Hint-module_body_kMVt9 strong{font-weight:var(--r9-font-weight-semibold)}.r9-Hint-module_body_kMVt9 code{font-family:var(--r9-font-mono)}.r9-Hint-module_dismiss_J3Hb5{color:inherit;cursor:pointer;font-family:var(--r9-type-caption-font-family);font-weight:var(--r9-type-caption-font-weight);font-size:var(--r9-type-caption-font-size);line-height:var(--r9-type-caption-line-height);letter-spacing:var(--r9-type-caption-letter-spacing);text-underline-offset:3px;opacity:.8;background:0 0;border:0;flex:none;padding:2px 0;text-decoration:underline}.r9-Hint-module_dismiss_J3Hb5:hover{opacity:1}.r9-Hint-module_dismiss_J3Hb5:focus-visible{outline:2px solid var(--r9-accent);outline-offset:2px}.r9-PanelStage-module_stage_borrY{min-width:0;position:relative}.r9-PanelStage-module_ghost_JUO1L{pointer-events:none;z-index:1;position:absolute;inset:0 0 auto}.r9-PanelStage-module_slot_qdJUb{min-width:0}.r9-PanelStage-module_scanbar_O7owB{z-index:3;opacity:0;pointer-events:none;background:var(--r9-accent);height:2px;box-shadow:0 0 14px var(--r9-accent), 0 0 2px var(--r9-accent);position:absolute;top:0;left:-6px;right:-6px}.r9-Monogram-module_mono_b1gNw{color:currentColor;flex:none;display:inline-block;position:relative}.r9-Monogram-module_mono_b1gNw svg{width:100%;height:100%;display:block}.r9-Monogram-module_ghosts_1W2MQ{pointer-events:none;position:absolute;inset:0}.r9-Monogram-module_g1_b6O-L,.r9-Monogram-module_g2_RJHLf{opacity:0;position:absolute;inset:0}.r9-Monogram-module_g1_b6O-L{color:var(--r9-signal);animation:r9-slice-a var(--r9-duration-slow) steps(1) 1}.r9-Monogram-module_g2_RJHLf{color:var(--r9-accent);animation:r9-slice-b var(--r9-duration-slow) steps(1) 1}.r9-DecryptText-module_text_P0Frp{position:relative}.r9-DecryptText-module_running_9APA1{color:#0000}.r9-DecryptText-module_sr_cUr05{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;position:absolute!important}.r9-DecryptText-module_overlay_HLlxA{pointer-events:none;position:absolute;inset:0}.r9-DecryptText-module_char_zov52,.r9-DecryptText-module_glyph_hyPrZ{text-align:center;white-space:pre;display:block;position:absolute}.r9-DecryptText-module_char_zov52{color:inherit}.r9-DecryptText-module_glyph_hyPrZ{color:var(--r9-accent-text)}.r9-PageEffects-module_scan_v35jx{z-index:var(--r9-z-fx);pointer-events:none;position:fixed;inset:0;overflow:hidden}.r9-PageEffects-module_scan_v35jx:before{content:"";background-image:repeating-linear-gradient(0deg, var(--r9-scan) 0 1px, transparent 1px 3px);position:absolute;inset:-4px 0 0}@media (prefers-reduced-motion:no-preference){.r9-PageEffects-module_scan_v35jx:before{animation:9s linear infinite r9-drift}}.r9-PageEffects-module_scan_v35jx:after{content:"";background:linear-gradient(180deg, transparent, var(--r9-scan), transparent);height:120px;position:absolute;top:0;left:0;right:0;transform:translateY(-140px)}@media (prefers-reduced-motion:no-preference){.r9-PageEffects-module_scan_v35jx:after{animation:r9-band 7s var(--r9-easing-out) infinite}}.r9-PageEffects-module_sparks_ZqcXT{width:100%;height:100%;z-index:var(--r9-z-fx);pointer-events:none;position:fixed;inset:0}.r9-DotField-module_field_yN0Gh{width:100%;height:100%;display:block;position:absolute;inset:0}.r9-DotField-module_fixed_Jo78C{z-index:0;touch-action:pan-y;position:fixed}.r9-Terminal-module_term_MpeFz{font-family:var(--r9-type-code-font-family);font-weight:var(--r9-type-code-font-weight);font-size:var(--r9-type-code-font-size);line-height:var(--r9-type-code-line-height);letter-spacing:var(--r9-type-code-letter-spacing);clip-path:polygon(calc(var(--r9-corner-md) * var(--r9-shape-cut)) 0, calc(100% - 0px) 0, 100% 0px, 100% calc(100% - calc(var(--r9-corner-md) * var(--r9-shape-cut))), calc(100% - calc(var(--r9-corner-md) * var(--r9-shape-cut))) 100%, 0px 100%, 0 calc(100% - 0px), 0 calc(var(--r9-corner-md) * var(--r9-shape-cut)));border-radius:calc(var(--r9-corner-md) * var(--r9-shape-round));box-shadow:inset 0 0 0 var(--r9-border-hair) var(--r9-line);background:var(--r9-surface);padding:var(--r9-space-5);min-height:12.5rem;overflow-x:auto}.r9-Terminal-module_line_moKRF{white-space:pre}.r9-Terminal-module_cmd_-e6US:before{content:"$ ";color:var(--r9-accent-text)}.r9-Terminal-module_dim_8Upay{color:var(--r9-fg-muted)}.r9-Terminal-module_ok_v-BTE{color:var(--r9-signal-text)}.r9-Terminal-module_caret_yU4Ba{vertical-align:-.15em;background:var(--r9-accent);width:8px;height:1.05em;display:inline-block}@media (prefers-reduced-motion:no-preference){.r9-Terminal-module_caret_yU4Ba{animation:1s step-end infinite r9-blink}}.r9-VelocityStrip-module_strip_wt7wa{white-space:nowrap;padding-block:var(--r9-space-4);border-block:var(--r9-border-hair) solid var(--r9-line);position:relative;overflow:hidden}.r9-VelocityStrip-module_track_DESQR{will-change:transform;display:inline-flex}.r9-VelocityStrip-module_track_DESQR span{font:var(--r9-font-weight-medium) .9375rem/1 var(--r9-font-display);letter-spacing:.3em;text-transform:uppercase;color:var(--r9-fg-muted)}.r9-VelocityStrip-module_track_DESQR i{color:var(--r9-accent-text);padding-inline:1.1em;font-style:normal}.r9-VelocityStrip-module_sr_apB97{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;top:0;left:0;overflow:hidden;position:absolute!important}
|
|
2
|
+
/*$vite$:1*/
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from '@rnine/core';
|
|
2
|
+
export { MOOD_BOOT_SCRIPT, NINEOS_THEME, ThemeProvider, moodBootScript } from './provider/ThemeProvider';
|
|
3
|
+
export { Mark } from './motion/Monogram';
|
|
4
|
+
export { PanelStage } from './components/Panel/PanelStage';
|
|
5
|
+
export { CommandLine } from './components/CommandLine/CommandLine';
|
|
6
|
+
export type { CommandSpec } from './components/CommandLine/CommandLine';
|
|
7
|
+
export { ChipBar } from './components/CommandLine/ChipBar';
|
|
8
|
+
export type { ChipItem } from './components/CommandLine/ChipBar';
|
|
9
|
+
export { Dock, Hud, Kbd } from './components/Chrome/Chrome';
|
|
10
|
+
export { Hint } from './components/Hint/Hint';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { MOOD_BOOT_SCRIPT as e, NINEOS_THEME as t, ThemeProvider as n, moodBootScript as r } from "./provider/ThemeProvider.js";
|
|
2
|
+
import { Mark as i } from "./motion/Monogram.js";
|
|
3
|
+
import { PanelStage as a } from "./components/Panel/PanelStage.js";
|
|
4
|
+
import { ChipBar as o } from "./components/CommandLine/ChipBar.js";
|
|
5
|
+
import { CommandLine as s } from "./components/CommandLine/CommandLine.js";
|
|
6
|
+
import { Dock as c, Hud as l, Kbd as u } from "./components/Chrome/Chrome.js";
|
|
7
|
+
import { Hint as d } from "./components/Hint/Hint.js";
|
|
8
|
+
export * from "@rnine/core";
|
|
9
|
+
export { o as ChipBar, s as CommandLine, c as Dock, d as Hint, l as Hud, u as Kbd, e as MOOD_BOOT_SCRIPT, i as Mark, t as NINEOS_THEME, a as PanelStage, n as ThemeProvider, r as moodBootScript };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TextRevealProps } from '@rnine/core/motion';
|
|
2
|
+
/**
|
|
3
|
+
* Text that decodes from magenta terminal symbols, left to right, in 1s.
|
|
4
|
+
* The real text never moves: it stays as plain text (transparent while the
|
|
5
|
+
* effect runs) and every glyph is pinned to the measured position of its
|
|
6
|
+
* letter, so the effect can't wrap differently or spill into the next
|
|
7
|
+
* content. Screen readers always get the final text.
|
|
8
|
+
*/
|
|
9
|
+
export declare function TextReveal({ text, as: Tag, className, id, trigger, duration, delay }: TextRevealProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { useTextReveal as e } from "./useDecrypt.js";
|
|
2
|
+
import t from "./DecryptText.module.js";
|
|
3
|
+
import { cx as n } from "@rnine/core/kit";
|
|
4
|
+
import { useEffect as r, useLayoutEffect as i, useRef as a, useState as o } from "react";
|
|
5
|
+
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
6
|
+
//#region src/motion/DecryptText.tsx
|
|
7
|
+
function l(e, t, n) {
|
|
8
|
+
let r = document.createRange();
|
|
9
|
+
if (typeof r.getClientRects != "function") return null;
|
|
10
|
+
let i = t.getBoundingClientRect(), a = 0;
|
|
11
|
+
return n.map((t) => {
|
|
12
|
+
r.setStart(e, a), r.setEnd(e, a + t.length), a += t.length;
|
|
13
|
+
let n = r.getClientRects(), o = n[n.length - 1] ?? r.getBoundingClientRect();
|
|
14
|
+
return {
|
|
15
|
+
left: o.left - i.left,
|
|
16
|
+
top: o.top - i.top,
|
|
17
|
+
width: o.width,
|
|
18
|
+
height: o.height
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function u({ text: u, as: d = "span", className: f, id: p, trigger: m = "view", duration: h = 1e3, delay: g = 0 }) {
|
|
23
|
+
let [_, v] = e(u, h), y = a(null), b = a(null), [x, S] = o(null), C = Array.from(u), w = _ !== null;
|
|
24
|
+
r(() => {
|
|
25
|
+
if (m === "mount") {
|
|
26
|
+
let e = window.setTimeout(v, g);
|
|
27
|
+
return () => window.clearTimeout(e);
|
|
28
|
+
}
|
|
29
|
+
if (m === "view" && y.current) {
|
|
30
|
+
let e = new IntersectionObserver(([t]) => {
|
|
31
|
+
t.isIntersecting && (window.setTimeout(v, g), e.disconnect());
|
|
32
|
+
}, { threshold: .4 });
|
|
33
|
+
return e.observe(y.current), () => e.disconnect();
|
|
34
|
+
}
|
|
35
|
+
}, [
|
|
36
|
+
m,
|
|
37
|
+
v,
|
|
38
|
+
g
|
|
39
|
+
]), i(() => {
|
|
40
|
+
if (!w) return;
|
|
41
|
+
let e = b.current?.firstChild;
|
|
42
|
+
e instanceof Text && y.current && S(l(e, y.current, Array.from(u)));
|
|
43
|
+
}, [w, u]);
|
|
44
|
+
let T = w && x;
|
|
45
|
+
return /* @__PURE__ */ c(d, {
|
|
46
|
+
ref: y,
|
|
47
|
+
id: p,
|
|
48
|
+
className: n(t.text, f),
|
|
49
|
+
onMouseEnter: m === "hover" ? v : void 0,
|
|
50
|
+
children: [
|
|
51
|
+
/* @__PURE__ */ s("span", {
|
|
52
|
+
className: t.sr,
|
|
53
|
+
children: u
|
|
54
|
+
}),
|
|
55
|
+
/* @__PURE__ */ s("span", {
|
|
56
|
+
ref: b,
|
|
57
|
+
"aria-hidden": "true",
|
|
58
|
+
className: T ? t.running : void 0,
|
|
59
|
+
children: u
|
|
60
|
+
}),
|
|
61
|
+
T && /* @__PURE__ */ s("span", {
|
|
62
|
+
"aria-hidden": "true",
|
|
63
|
+
className: t.overlay,
|
|
64
|
+
children: C.map((e, n) => {
|
|
65
|
+
let r = x[n];
|
|
66
|
+
if (!r || /\s/.test(e)) return null;
|
|
67
|
+
let i = {
|
|
68
|
+
left: r.left,
|
|
69
|
+
top: r.top,
|
|
70
|
+
width: r.width,
|
|
71
|
+
height: r.height,
|
|
72
|
+
lineHeight: `${r.height}px`
|
|
73
|
+
}, a = _[n];
|
|
74
|
+
return /* @__PURE__ */ s("span", {
|
|
75
|
+
className: a ? t.glyph : t.char,
|
|
76
|
+
style: i,
|
|
77
|
+
children: a ?? e
|
|
78
|
+
}, n);
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
]
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
export { u as TextReveal };
|
|
86
|
+
|
|
87
|
+
//# sourceMappingURL=DecryptText.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DecryptText.js","names":[],"sources":["../../src/motion/DecryptText.tsx"],"sourcesContent":["import { useEffect, useLayoutEffect, useRef, useState, type CSSProperties } from 'react';\nimport { cx } from '@rnine/core/kit';\nimport type { TextRevealProps } from '@rnine/core/motion';\nimport { useTextReveal } from './useDecrypt';\nimport styles from './DecryptText.module.scss';\n\n\ntype Cell = { left: number; top: number; width: number; height: number };\n\n/** Where each character of the text node sits, relative to `box`. */\nfunction measure(node: Text, box: HTMLElement, chars: string[]): Cell[] | null {\n const range = document.createRange();\n if (typeof range.getClientRects !== 'function') return null;\n const origin = box.getBoundingClientRect();\n let offset = 0;\n return chars.map((c) => {\n range.setStart(node, offset);\n range.setEnd(node, offset + c.length);\n offset += c.length;\n const rects = range.getClientRects();\n const r = rects[rects.length - 1] ?? range.getBoundingClientRect();\n return { left: r.left - origin.left, top: r.top - origin.top, width: r.width, height: r.height };\n });\n}\n\n/**\n * Text that decodes from magenta terminal symbols, left to right, in 1s.\n * The real text never moves: it stays as plain text (transparent while the\n * effect runs) and every glyph is pinned to the measured position of its\n * letter, so the effect can't wrap differently or spill into the next\n * content. Screen readers always get the final text.\n */\nexport function TextReveal({ text, as: Tag = 'span', className, id, trigger = 'view', duration = 1000, delay = 0 }: TextRevealProps) {\n const [frame, play] = useTextReveal(text, duration);\n const ref = useRef<HTMLElement>(null);\n const baseRef = useRef<HTMLSpanElement>(null);\n const [cells, setCells] = useState<Cell[] | null>(null);\n const chars = Array.from(text);\n const running = frame !== null;\n\n useEffect(() => {\n if (trigger === 'mount') {\n const t = window.setTimeout(play, delay);\n return () => window.clearTimeout(t);\n }\n if (trigger === 'view' && ref.current) {\n const io = new IntersectionObserver(([e]) => {\n if (e.isIntersecting) {\n window.setTimeout(play, delay);\n io.disconnect();\n }\n }, { threshold: 0.4 });\n io.observe(ref.current);\n return () => io.disconnect();\n }\n }, [trigger, play, delay]);\n\n // Measure when the effect starts, before it paints (cells from an earlier run are only used while running)\n useLayoutEffect(() => {\n if (!running) return;\n const node = baseRef.current?.firstChild;\n if (node instanceof Text && ref.current) setCells(measure(node, ref.current, Array.from(text)));\n }, [running, text]);\n\n const pinned = running && cells;\n\n return (\n <Tag ref={ref} id={id} className={cx(styles.text, className)} onMouseEnter={trigger === 'hover' ? play : undefined}>\n {/* What assistive tech reads, always the final text (works inside buttons and links too) */}\n <span className={styles.sr}>{text}</span>\n <span ref={baseRef} aria-hidden=\"true\" className={pinned ? styles.running : undefined}>\n {text}\n </span>\n {pinned && (\n <span aria-hidden=\"true\" className={styles.overlay}>\n {chars.map((c, i) => {\n const cell = cells[i];\n if (!cell || /\\s/.test(c)) return null;\n const style: CSSProperties = { left: cell.left, top: cell.top, width: cell.width, height: cell.height, lineHeight: `${cell.height}px` };\n const glyph = frame[i];\n return (\n <span key={i} className={glyph ? styles.glyph : styles.char} style={style}>\n {glyph ?? c}\n </span>\n );\n })}\n </span>\n )}\n </Tag>\n );\n}\n"],"mappings":";;;;;;AAUA,SAAS,EAAQ,GAAY,GAAkB,GAAgC;CAC7E,IAAM,IAAQ,SAAS,YAAY;CACnC,IAAI,OAAO,EAAM,kBAAmB,YAAY,OAAO;CACvD,IAAM,IAAS,EAAI,sBAAsB,GACrC,IAAS;CACb,OAAO,EAAM,KAAK,MAAM;EAGtB,AAFA,EAAM,SAAS,GAAM,CAAM,GAC3B,EAAM,OAAO,GAAM,IAAS,EAAE,MAAM,GACpC,KAAU,EAAE;EACZ,IAAM,IAAQ,EAAM,eAAe,GAC7B,IAAI,EAAM,EAAM,SAAS,MAAM,EAAM,sBAAsB;EACjE,OAAO;GAAE,MAAM,EAAE,OAAO,EAAO;GAAM,KAAK,EAAE,MAAM,EAAO;GAAK,OAAO,EAAE;GAAO,QAAQ,EAAE;EAAO;CACjG,CAAC;AACH;AASA,SAAgB,EAAW,EAAE,SAAM,IAAI,IAAM,QAAQ,cAAW,OAAI,aAAU,QAAQ,cAAW,KAAM,WAAQ,KAAsB;CACnI,IAAM,CAAC,GAAO,KAAQ,EAAc,GAAM,CAAQ,GAC5C,IAAM,EAAoB,IAAI,GAC9B,IAAU,EAAwB,IAAI,GACtC,CAAC,GAAO,KAAY,EAAwB,IAAI,GAChD,IAAQ,MAAM,KAAK,CAAI,GACvB,IAAU,MAAU;CAoB1B,AAlBA,QAAgB;EACd,IAAI,MAAY,SAAS;GACvB,IAAM,IAAI,OAAO,WAAW,GAAM,CAAK;GACvC,aAAa,OAAO,aAAa,CAAC;EACpC;EACA,IAAI,MAAY,UAAU,EAAI,SAAS;GACrC,IAAM,IAAK,IAAI,sBAAsB,CAAC,OAAO;IAC3C,AAAI,EAAE,mBACJ,OAAO,WAAW,GAAM,CAAK,GAC7B,EAAG,WAAW;GAElB,GAAG,EAAE,WAAW,GAAI,CAAC;GAErB,OADA,EAAG,QAAQ,EAAI,OAAO,SACT,EAAG,WAAW;EAC7B;CACF,GAAG;EAAC;EAAS;EAAM;CAAK,CAAC,GAGzB,QAAsB;EACpB,IAAI,CAAC,GAAS;EACd,IAAM,IAAO,EAAQ,SAAS;EAC9B,AAAI,aAAgB,QAAQ,EAAI,WAAS,EAAS,EAAQ,GAAM,EAAI,SAAS,MAAM,KAAK,CAAI,CAAC,CAAC;CAChG,GAAG,CAAC,GAAS,CAAI,CAAC;CAElB,IAAM,IAAS,KAAW;CAE1B,OACE,kBAAC,GAAD;EAAU;EAAS;EAAI,WAAW,EAAG,EAAO,MAAM,CAAS;EAAG,cAAc,MAAY,UAAU,IAAO,KAAA;YAAzG;GAEE,kBAAC,QAAD;IAAM,WAAW,EAAO;cAAK;GAAW,CAAA;GACxC,kBAAC,QAAD;IAAM,KAAK;IAAS,eAAY;IAAO,WAAW,IAAS,EAAO,UAAU,KAAA;cACzE;GACG,CAAA;GACL,KACC,kBAAC,QAAD;IAAM,eAAY;IAAO,WAAW,EAAO;cACxC,EAAM,KAAK,GAAG,MAAM;KACnB,IAAM,IAAO,EAAM;KACnB,IAAI,CAAC,KAAQ,KAAK,KAAK,CAAC,GAAG,OAAO;KAClC,IAAM,IAAuB;MAAE,MAAM,EAAK;MAAM,KAAK,EAAK;MAAK,OAAO,EAAK;MAAO,QAAQ,EAAK;MAAQ,YAAY,GAAG,EAAK,OAAO;KAAI,GAChI,IAAQ,EAAM;KACpB,OACE,kBAAC,QAAD;MAAc,WAAW,IAAQ,EAAO,QAAQ,EAAO;MAAa;gBACjE,KAAS;KACN,GAFK,CAEL;IAEV,CAAC;GACG,CAAA;EAEL;;AAET"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
var e = {
|
|
2
|
+
text: "r9-DecryptText-module_text_P0Frp",
|
|
3
|
+
running: "r9-DecryptText-module_running_9APA1",
|
|
4
|
+
sr: "r9-DecryptText-module_sr_cUr05",
|
|
5
|
+
overlay: "r9-DecryptText-module_overlay_HLlxA",
|
|
6
|
+
char: "r9-DecryptText-module_char_zov52",
|
|
7
|
+
glyph: "r9-DecryptText-module_glyph_hyPrZ"
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { e as default };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=DecryptText.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DecryptText.module.js","names":[],"sources":["../../src/motion/DecryptText.module.scss"],"sourcesContent":["@use \"@rnine/nineos/scss\" as r9;\n\n// The real text always does the layout. While the effect runs it turns\n// transparent, and each character is drawn on the overlay at the measured\n// position of its letter. Nothing in the overlay flows or wraps, so it can't\n// differ from the text (balanced headings included) or spill out of it.\n.text {\n position: relative;\n}\n\n.running {\n color: transparent;\n}\n\n.sr {\n @include r9.visually-hidden;\n}\n\n.overlay {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n\n.char,\n.glyph {\n position: absolute;\n display: block;\n text-align: center;\n white-space: pre;\n}\n\n// Resolved letters use the element's own color (muted text stays muted)\n.char {\n color: inherit;\n}\n\n.glyph {\n color: r9.$accent-text;\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { BackdropProps } from '@rnine/core/motion';
|
|
2
|
+
/**
|
|
3
|
+
* A grid of dots that reacts to the pointer: dots near it grow, push away and
|
|
4
|
+
* turn magenta. When the pointer rests, a cyan ripple crosses the field now
|
|
5
|
+
* and then; a click or tap sends one, and so does rippleAt(x, y) from code.
|
|
6
|
+
* On touch, dragging stirs the dots.
|
|
7
|
+
*/
|
|
8
|
+
export declare function Backdrop({ gap, reach, fixed }: BackdropProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import e from "./DotField.module.js";
|
|
2
|
+
import { cx as t } from "@rnine/core/kit";
|
|
3
|
+
import { useEffect as n, useRef as r } from "react";
|
|
4
|
+
import { RIPPLE_EVENT as i, useReducedMotion as a } from "@rnine/core/motion";
|
|
5
|
+
import { jsx as o } from "react/jsx-runtime";
|
|
6
|
+
//#region src/motion/DotField.tsx
|
|
7
|
+
function s({ gap: s = 22, reach: c = 150, fixed: l = !1 }) {
|
|
8
|
+
let u = r(null), d = a();
|
|
9
|
+
return n(() => {
|
|
10
|
+
let e = u.current, t = e?.parentElement;
|
|
11
|
+
if (!e || !t) return;
|
|
12
|
+
let n = e.getContext("2d");
|
|
13
|
+
if (!n) return;
|
|
14
|
+
let r = Math.min(window.devicePixelRatio || 1, 2), a = 0, o = 0, f = {
|
|
15
|
+
dot: "",
|
|
16
|
+
hot: "",
|
|
17
|
+
ripple: ""
|
|
18
|
+
}, p = {
|
|
19
|
+
x: -9999,
|
|
20
|
+
y: -9999
|
|
21
|
+
}, m = [], h = 0, g = !0, _ = 0, v = 0, y = () => {
|
|
22
|
+
let t = getComputedStyle(e);
|
|
23
|
+
f = {
|
|
24
|
+
dot: t.getPropertyValue("--r9-dot").trim(),
|
|
25
|
+
hot: t.getPropertyValue("--r9-accent").trim(),
|
|
26
|
+
ripple: t.getPropertyValue("--r9-signal").trim()
|
|
27
|
+
};
|
|
28
|
+
}, b = () => l ? {
|
|
29
|
+
left: 0,
|
|
30
|
+
top: 0
|
|
31
|
+
} : t.getBoundingClientRect(), x = () => {
|
|
32
|
+
let i = l ? {
|
|
33
|
+
width: window.innerWidth,
|
|
34
|
+
height: window.innerHeight
|
|
35
|
+
} : t.getBoundingClientRect();
|
|
36
|
+
a = i.width, o = i.height, e.width = Math.round(a * r), e.height = Math.round(o * r), n.setTransform(r, 0, 0, r, 0, 0);
|
|
37
|
+
}, S = (e) => {
|
|
38
|
+
v++ % 30 == 0 && y(), n.clearRect(0, 0, a, o), !d && e - h > 5e3 && (!m.length || e - m[m.length - 1].t > 5500) && m.push({
|
|
39
|
+
x: Math.random() * a,
|
|
40
|
+
y: Math.random() * o * .8,
|
|
41
|
+
t: e
|
|
42
|
+
}), m = m.filter((t) => e - t.t < 2600);
|
|
43
|
+
for (let t = s / 2; t < o; t += s) for (let r = s / 2; r < a; r += s) {
|
|
44
|
+
let i = r - p.x, a = t - p.y, o = Math.hypot(i, a), s = o < c ? 1 - o / c : 0, l = r, u = t, d = 1, h = 0;
|
|
45
|
+
s > 0 && (l += i / (o || 1) * s * 6, u += a / (o || 1) * s * 6, d = 1 + s * 1.4);
|
|
46
|
+
for (let n of m) {
|
|
47
|
+
let i = (e - n.t) / 2600, a = Math.abs(Math.hypot(r - n.x, t - n.y) - i * 520);
|
|
48
|
+
if (a < 26) {
|
|
49
|
+
let e = (1 - a / 26) * (1 - i);
|
|
50
|
+
h = Math.max(h, e * .8), d = Math.max(d, 1 + e * 1.2);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
s > .05 ? (n.fillStyle = f.hot, n.globalAlpha = Math.min(1, .2 + s)) : h > .05 ? (n.fillStyle = f.ripple, n.globalAlpha = Math.min(1, .25 + h)) : (n.fillStyle = f.dot, n.globalAlpha = 1), n.fillRect(l - d / 2, u - d / 2, d, d);
|
|
54
|
+
}
|
|
55
|
+
n.globalAlpha = 1;
|
|
56
|
+
}, C = (e) => {
|
|
57
|
+
g && !document.hidden && S(e), _ = requestAnimationFrame(C);
|
|
58
|
+
}, w = (e) => {
|
|
59
|
+
let t = b();
|
|
60
|
+
p.x = e.clientX - t.left, p.y = e.clientY - t.top, h = performance.now();
|
|
61
|
+
}, T = () => {
|
|
62
|
+
p.x = p.y = -9999;
|
|
63
|
+
}, E = (e) => {
|
|
64
|
+
e.pointerType !== "mouse" && window.setTimeout(T, 600);
|
|
65
|
+
}, D = (e) => {
|
|
66
|
+
let t = b();
|
|
67
|
+
m.push({
|
|
68
|
+
x: e.clientX - t.left,
|
|
69
|
+
y: e.clientY - t.top,
|
|
70
|
+
t: performance.now()
|
|
71
|
+
});
|
|
72
|
+
}, O = (e) => {
|
|
73
|
+
let { x: t, y: n } = e.detail, r = b();
|
|
74
|
+
m.push({
|
|
75
|
+
x: t - r.left,
|
|
76
|
+
y: n - r.top,
|
|
77
|
+
t: performance.now()
|
|
78
|
+
});
|
|
79
|
+
}, k = l ? window : t;
|
|
80
|
+
x(), y();
|
|
81
|
+
let A = new ResizeObserver(x);
|
|
82
|
+
A.observe(l ? document.documentElement : t);
|
|
83
|
+
let j = new IntersectionObserver(([e]) => {
|
|
84
|
+
g = l || e.isIntersecting;
|
|
85
|
+
});
|
|
86
|
+
if (j.observe(t), d) {
|
|
87
|
+
S(0);
|
|
88
|
+
let e = new MutationObserver(() => {
|
|
89
|
+
y(), S(0);
|
|
90
|
+
});
|
|
91
|
+
return e.observe(document.documentElement, {
|
|
92
|
+
attributes: !0,
|
|
93
|
+
attributeFilter: ["data-scheme", "data-palette"]
|
|
94
|
+
}), () => {
|
|
95
|
+
A.disconnect(), j.disconnect(), e.disconnect();
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
let M = { passive: !0 };
|
|
99
|
+
return k.addEventListener("pointermove", w, M), k.addEventListener("pointerdown", D, M), k.addEventListener("pointerup", E, M), (l ? document.documentElement : t).addEventListener("pointerleave", T), window.addEventListener(i, O), _ = requestAnimationFrame(C), () => {
|
|
100
|
+
cancelAnimationFrame(_), A.disconnect(), j.disconnect(), k.removeEventListener("pointermove", w), k.removeEventListener("pointerdown", D), k.removeEventListener("pointerup", E), (l ? document.documentElement : t).removeEventListener("pointerleave", T), window.removeEventListener(i, O);
|
|
101
|
+
};
|
|
102
|
+
}, [
|
|
103
|
+
s,
|
|
104
|
+
c,
|
|
105
|
+
d,
|
|
106
|
+
l
|
|
107
|
+
]), /* @__PURE__ */ o("canvas", {
|
|
108
|
+
ref: u,
|
|
109
|
+
className: t(e.field, l && e.fixed),
|
|
110
|
+
"aria-hidden": "true"
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { s as Backdrop };
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=DotField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DotField.js","names":[],"sources":["../../src/motion/DotField.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport type { BackdropProps } from '@rnine/core/motion';\nimport { useReducedMotion } from '@rnine/core/motion';\nimport { cx } from '@rnine/core/kit';\nimport { RIPPLE_EVENT, type RippleDetail } from '@rnine/core/motion';\nimport styles from './DotField.module.scss';\n\n\ntype Ripple = { x: number; y: number; t: number };\n\n/**\n * A grid of dots that reacts to the pointer: dots near it grow, push away and\n * turn magenta. When the pointer rests, a cyan ripple crosses the field now\n * and then; a click or tap sends one, and so does rippleAt(x, y) from code.\n * On touch, dragging stirs the dots.\n */\nexport function Backdrop({ gap = 22, reach = 150, fixed = false }: BackdropProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const reduced = useReducedMotion();\n\n useEffect(() => {\n const canvas = canvasRef.current;\n const host = canvas?.parentElement;\n if (!canvas || !host) return;\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const dpr = Math.min(window.devicePixelRatio || 1, 2);\n let w = 0;\n let h = 0;\n let colors = { dot: '', hot: '', ripple: '' };\n const pointer = { x: -9999, y: -9999 };\n let ripples: Ripple[] = [];\n let lastMove = 0;\n let visible = true;\n let raf = 0;\n let frame = 0;\n\n const readColors = () => {\n const s = getComputedStyle(canvas);\n colors = {\n dot: s.getPropertyValue('--r9-dot').trim(),\n hot: s.getPropertyValue('--r9-accent').trim(),\n ripple: s.getPropertyValue('--r9-signal').trim(),\n };\n };\n // Where the field's top-left corner sits in the viewport\n const origin = () => (fixed ? { left: 0, top: 0 } : host.getBoundingClientRect());\n const resize = () => {\n const r = fixed ? { width: window.innerWidth, height: window.innerHeight } : host.getBoundingClientRect();\n w = r.width;\n h = r.height;\n canvas.width = Math.round(w * dpr);\n canvas.height = Math.round(h * dpr);\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n };\n\n const draw = (now: number) => {\n if (frame++ % 30 === 0) readColors();\n ctx.clearRect(0, 0, w, h);\n if (!reduced && now - lastMove > 5000 && (!ripples.length || now - ripples[ripples.length - 1].t > 5500)) {\n ripples.push({ x: Math.random() * w, y: Math.random() * h * 0.8, t: now });\n }\n ripples = ripples.filter((r) => now - r.t < 2600);\n\n for (let y = gap / 2; y < h; y += gap) {\n for (let x = gap / 2; x < w; x += gap) {\n const dx = x - pointer.x;\n const dy = y - pointer.y;\n const d = Math.hypot(dx, dy);\n const k = d < reach ? 1 - d / reach : 0;\n let px = x;\n let py = y;\n let size = 1;\n let wave = 0;\n if (k > 0) {\n px += (dx / (d || 1)) * k * 6;\n py += (dy / (d || 1)) * k * 6;\n size = 1 + k * 1.4;\n }\n for (const r of ripples) {\n const age = (now - r.t) / 2600;\n const off = Math.abs(Math.hypot(x - r.x, y - r.y) - age * 520);\n if (off < 26) {\n const s = (1 - off / 26) * (1 - age);\n wave = Math.max(wave, s * 0.8);\n size = Math.max(size, 1 + s * 1.2);\n }\n }\n if (k > 0.05) {\n ctx.fillStyle = colors.hot;\n ctx.globalAlpha = Math.min(1, 0.2 + k);\n } else if (wave > 0.05) {\n ctx.fillStyle = colors.ripple;\n ctx.globalAlpha = Math.min(1, 0.25 + wave);\n } else {\n ctx.fillStyle = colors.dot;\n ctx.globalAlpha = 1;\n }\n ctx.fillRect(px - size / 2, py - size / 2, size, size);\n }\n }\n ctx.globalAlpha = 1;\n };\n\n const loop = (now: number) => {\n if (visible && !document.hidden) draw(now);\n raf = requestAnimationFrame(loop);\n };\n\n const onMove = (e: PointerEvent) => {\n const o = origin();\n pointer.x = e.clientX - o.left;\n pointer.y = e.clientY - o.top;\n lastMove = performance.now();\n };\n const onLeave = () => {\n pointer.x = pointer.y = -9999;\n };\n // A lifted finger shouldn't leave the dots pushed aside\n const onUp = (e: PointerEvent) => {\n if (e.pointerType !== 'mouse') window.setTimeout(onLeave, 600);\n };\n const onDown = (e: PointerEvent) => {\n const o = origin();\n ripples.push({ x: e.clientX - o.left, y: e.clientY - o.top, t: performance.now() });\n };\n const onRipple = (e: Event) => {\n const { x, y } = (e as CustomEvent<RippleDetail>).detail;\n const o = origin();\n ripples.push({ x: x - o.left, y: y - o.top, t: performance.now() });\n };\n const target: Window | HTMLElement = fixed ? window : host;\n\n resize();\n readColors();\n const ro = new ResizeObserver(resize);\n ro.observe(fixed ? document.documentElement : host);\n const io = new IntersectionObserver(([e]) => {\n visible = fixed || e.isIntersecting;\n });\n io.observe(host);\n\n if (reduced) {\n draw(0);\n // Still redraw static dots when the mood changes\n const mo = new MutationObserver(() => {\n readColors();\n draw(0);\n });\n mo.observe(document.documentElement, { attributes: true, attributeFilter: ['data-scheme', 'data-palette'] });\n return () => {\n ro.disconnect();\n io.disconnect();\n mo.disconnect();\n };\n }\n\n const opts = { passive: true } as const;\n target.addEventListener('pointermove', onMove as EventListener, opts);\n target.addEventListener('pointerdown', onDown as EventListener, opts);\n target.addEventListener('pointerup', onUp as EventListener, opts);\n (fixed ? document.documentElement : host).addEventListener('pointerleave', onLeave);\n window.addEventListener(RIPPLE_EVENT, onRipple);\n raf = requestAnimationFrame(loop);\n return () => {\n cancelAnimationFrame(raf);\n ro.disconnect();\n io.disconnect();\n target.removeEventListener('pointermove', onMove as EventListener);\n target.removeEventListener('pointerdown', onDown as EventListener);\n target.removeEventListener('pointerup', onUp as EventListener);\n (fixed ? document.documentElement : host).removeEventListener('pointerleave', onLeave);\n window.removeEventListener(RIPPLE_EVENT, onRipple);\n };\n }, [gap, reach, reduced, fixed]);\n\n return <canvas ref={canvasRef} className={cx(styles.field, fixed && styles.fixed)} aria-hidden=\"true\" />;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DotField.module.js","names":[],"sources":["../../src/motion/DotField.module.scss"],"sourcesContent":[".field {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n display: block;\n}\n\n// Page background: behind everything, never blocks scrolling\n.fixed {\n position: fixed;\n z-index: 0;\n touch-action: pan-y;\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MarkProps } from '@rnine/core';
|
|
2
|
+
/**
|
|
3
|
+
* The app's mark (ThemeProvider mark, or MarkProvider), with the NineOS slice
|
|
4
|
+
* glitch in cyan and magenta (animate: on hover, on mount, or never). Until an
|
|
5
|
+
* app provides one it shows a placeholder frame.
|
|
6
|
+
*/
|
|
7
|
+
export declare function Mark({ size, variant, animate, label, className }: MarkProps): import("react").JSX.Element;
|