@orangecheck/app-design 0.3.2 → 0.4.1

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.
@@ -1,4 +1,4 @@
1
- import { forwardRef, createContext, createElement, useContext, Children, isValidElement, cloneElement, useState, useRef, useEffect, useCallback } from 'react';
1
+ import { forwardRef, createContext, createElement, useRef, useState, useEffect, useContext, Children, isValidElement, cloneElement, useCallback } from 'react';
2
2
  import { clsx } from 'clsx';
3
3
  import { extendTailwindMerge } from 'tailwind-merge';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -61,6 +61,131 @@ function Prose({ className, ...props }) {
61
61
  }
62
62
  );
63
63
  }
64
+ function prefersStill() {
65
+ return typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
66
+ }
67
+ function useInView(rootMargin = "0px 0px -12% 0px") {
68
+ const ref = useRef(null);
69
+ const [seen, setSeen] = useState(false);
70
+ useEffect(() => {
71
+ const el = ref.current;
72
+ if (!el || seen) return;
73
+ if (typeof IntersectionObserver === "undefined") {
74
+ setSeen(true);
75
+ return;
76
+ }
77
+ const io = new IntersectionObserver(
78
+ ([e]) => {
79
+ if (e?.isIntersecting) {
80
+ setSeen(true);
81
+ io.disconnect();
82
+ }
83
+ },
84
+ { rootMargin }
85
+ );
86
+ io.observe(el);
87
+ return () => io.disconnect();
88
+ }, [rootMargin, seen]);
89
+ return [ref, seen];
90
+ }
91
+ function Reveal({
92
+ as = "div",
93
+ delay = 0,
94
+ className,
95
+ style,
96
+ children
97
+ }) {
98
+ const ref = useRef(null);
99
+ useEffect(() => {
100
+ const el = ref.current;
101
+ if (!el || prefersStill() || typeof IntersectionObserver === "undefined") return;
102
+ if (el.getBoundingClientRect().top < window.innerHeight * 0.92) return;
103
+ el.dataset.reveal = "pending";
104
+ const io = new IntersectionObserver(
105
+ ([e]) => {
106
+ if (e?.isIntersecting) {
107
+ el.dataset.reveal = "shown";
108
+ io.disconnect();
109
+ }
110
+ },
111
+ { rootMargin: "0px 0px -10% 0px" }
112
+ );
113
+ io.observe(el);
114
+ return () => io.disconnect();
115
+ }, []);
116
+ return createElement(
117
+ as,
118
+ { ref, className, style: { ...style, "--reveal-delay": `${delay}ms` } },
119
+ children
120
+ );
121
+ }
122
+ function ScrollStory({
123
+ length = 2,
124
+ className,
125
+ innerClassName,
126
+ children
127
+ }) {
128
+ const ref = useRef(null);
129
+ const [pinned, setPinned] = useState(false);
130
+ useEffect(() => {
131
+ const el = ref.current;
132
+ if (!el || prefersStill()) return;
133
+ setPinned(true);
134
+ let raf = 0;
135
+ const update = () => {
136
+ raf = 0;
137
+ const r = el.getBoundingClientRect();
138
+ const travel = r.height - window.innerHeight;
139
+ const p = travel > 0 ? Math.min(1, Math.max(0, -r.top / travel)) : 0;
140
+ el.style.setProperty("--p", p.toFixed(4));
141
+ };
142
+ const on = () => {
143
+ if (!raf) raf = requestAnimationFrame(update);
144
+ };
145
+ update();
146
+ window.addEventListener("scroll", on, { passive: true });
147
+ window.addEventListener("resize", on);
148
+ return () => {
149
+ cancelAnimationFrame(raf);
150
+ window.removeEventListener("scroll", on);
151
+ window.removeEventListener("resize", on);
152
+ };
153
+ }, []);
154
+ return /* @__PURE__ */ jsx("div", { ref, className: cn("relative", className), style: pinned ? { height: `${length * 100}svh` } : void 0, children: /* @__PURE__ */ jsx("div", { className: cn(pinned && "sticky top-0 flex min-h-svh items-center", innerClassName), children }) });
155
+ }
156
+ function Ticker({
157
+ value,
158
+ duration = 1200,
159
+ format = (n) => Math.round(n).toLocaleString("en-US"),
160
+ className
161
+ }) {
162
+ const [ref, seen] = useInView();
163
+ const [shown, setShown] = useState(value);
164
+ const animate = useRef(null);
165
+ useEffect(() => {
166
+ const el = ref.current;
167
+ if (!el || animate.current !== null) return;
168
+ animate.current = !prefersStill() && el.getBoundingClientRect().top > window.innerHeight * 0.9;
169
+ if (animate.current) setShown(0);
170
+ }, [ref]);
171
+ useEffect(() => {
172
+ if (!seen) return;
173
+ if (!animate.current) {
174
+ setShown(value);
175
+ return;
176
+ }
177
+ let raf = 0;
178
+ const t0 = performance.now();
179
+ const step = (now) => {
180
+ const k = Math.min(1, (now - t0) / duration);
181
+ setShown(value * (1 - Math.pow(1 - k, 3)));
182
+ if (k < 1) raf = requestAnimationFrame(step);
183
+ };
184
+ raf = requestAnimationFrame(step);
185
+ return () => cancelAnimationFrame(raf);
186
+ }, [seen, value, duration]);
187
+ return /* @__PURE__ */ jsx("span", { ref, className: cn("oca-tabular", className), children: format(shown) });
188
+ }
64
189
  var sectionVariants = cva("relative w-full overflow-x-clip", {
65
190
  variants: {
66
191
  tone: {
@@ -89,19 +214,12 @@ function Container({ className, ...props }) {
89
214
  function SectionIntro({
90
215
  className,
91
216
  align = "start",
92
- ...props
217
+ reveal = true,
218
+ children,
219
+ style
93
220
  }) {
94
- return /* @__PURE__ */ jsx(
95
- "div",
96
- {
97
- className: cn(
98
- "flex flex-col gap-4 md:gap-5",
99
- align === "center" && "mx-auto items-center text-center",
100
- className
101
- ),
102
- ...props
103
- }
104
- );
221
+ const cls = cn("flex flex-col gap-4 md:gap-5", align === "center" && "mx-auto items-center text-center", className);
222
+ return reveal ? /* @__PURE__ */ jsx(Reveal, { className: cls, style, children }) : /* @__PURE__ */ jsx("div", { className: cls, style, children });
105
223
  }
106
224
  function PageHero({
107
225
  eyebrow,
@@ -120,14 +238,14 @@ function PageHero({
120
238
  }
121
239
  return /* @__PURE__ */ jsx(Section, { space: "none", className: cn("pb-14 pt-12 md:pb-20 md:pt-20", className), children: /* @__PURE__ */ jsxs("div", { className: cn("grid grid-cols-1 gap-10", art && "items-center md:grid-cols-12"), children: [
122
240
  /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col items-start gap-6", art ? "md:col-span-7" : "max-w-[52rem]"), children: [
123
- eyebrow ? /* @__PURE__ */ jsx("p", { className: "oca-eyebrow", children: eyebrow }) : null,
124
- /* @__PURE__ */ jsx("h1", { className: size === "xl" ? "oca-display-1" : "oca-display-2", children: title }),
125
- lead ? /* @__PURE__ */ jsx("p", { className: "oca-lead max-w-[38rem]", children: lead }) : null,
126
- meta ? /* @__PURE__ */ jsx("p", { className: "text-[0.875rem] text-muted-foreground", children: meta }) : null,
127
- actions ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-3", children: actions }) : null,
128
- children
241
+ eyebrow ? /* @__PURE__ */ jsx("p", { className: "oca-eyebrow oca-enter", children: eyebrow }) : null,
242
+ /* @__PURE__ */ jsx("h1", { className: cn(size === "xl" ? "oca-display-1" : "oca-display-2", "oca-enter [--enter-delay:70ms]"), children: title }),
243
+ lead ? /* @__PURE__ */ jsx("p", { className: "oca-lead oca-enter max-w-[38rem] [--enter-delay:150ms]", children: lead }) : null,
244
+ meta ? /* @__PURE__ */ jsx("p", { className: "oca-enter text-[0.875rem] text-muted-foreground [--enter-delay:190ms]", children: meta }) : null,
245
+ actions ? /* @__PURE__ */ jsx("div", { className: "oca-enter flex flex-wrap gap-3 [--enter-delay:230ms]", children: actions }) : null,
246
+ children ? /* @__PURE__ */ jsx("div", { className: "oca-enter w-full [--enter-delay:270ms]", children }) : null
129
247
  ] }),
130
- art ? /* @__PURE__ */ jsx("div", { className: "md:col-span-5", children: art }) : null
248
+ art ? /* @__PURE__ */ jsx("div", { className: "oca-enter md:col-span-5 [--enter-delay:120ms]", children: art }) : null
131
249
  ] }) });
132
250
  }
133
251
  function DocHeader({
@@ -140,8 +258,8 @@ function DocHeader({
140
258
  className
141
259
  }) {
142
260
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-5", className), children: [
143
- eyebrow ? /* @__PURE__ */ jsx("p", { className: "oca-eyebrow", children: eyebrow }) : null,
144
- /* @__PURE__ */ jsx("h1", { className: "oca-display-2", children: title }),
261
+ eyebrow ? /* @__PURE__ */ jsx("p", { className: "oca-eyebrow oca-enter", children: eyebrow }) : null,
262
+ /* @__PURE__ */ jsx("h1", { className: "oca-display-2 oca-enter [--enter-delay:70ms]", children: title }),
145
263
  lead ? /* @__PURE__ */ jsx("p", { className: "oca-lead", children: lead }) : null,
146
264
  meta ? /* @__PURE__ */ jsx("p", { className: "text-[0.875rem] text-muted-foreground", children: meta }) : null,
147
265
  actions ? /* @__PURE__ */ jsx("div", { className: "mt-1 flex flex-wrap gap-3", children: actions }) : null,
@@ -1096,7 +1214,81 @@ function SatsFigure({
1096
1214
  hint ? /* @__PURE__ */ jsx("span", { className: "text-[0.875rem] text-muted-foreground", children: hint }) : null
1097
1215
  ] });
1098
1216
  }
1217
+ function PhoneFrame({ className, style, children }) {
1218
+ return /* @__PURE__ */ jsx(
1219
+ "div",
1220
+ {
1221
+ className: cn(
1222
+ "relative w-[17.5rem] shrink-0 rounded-[2.9rem] bg-[#0b0b0c] p-[0.6rem]",
1223
+ "shadow-[0_50px_100px_-40px_rgb(10_10_10/0.55),0_0_0_1px_rgb(255_255_255/0.06)_inset]",
1224
+ className
1225
+ ),
1226
+ style,
1227
+ children: /* @__PURE__ */ jsxs("div", { className: "relative aspect-[9/19.2] overflow-hidden rounded-[2.35rem] bg-background", children: [
1228
+ /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "absolute left-1/2 top-2.5 z-10 h-[1.35rem] w-[5.6rem] -translate-x-1/2 rounded-full bg-[#0b0b0c]" }),
1229
+ children
1230
+ ] })
1231
+ }
1232
+ );
1233
+ }
1234
+ function BrowserFrame({
1235
+ url = "ochk.app",
1236
+ className,
1237
+ style,
1238
+ children
1239
+ }) {
1240
+ return /* @__PURE__ */ jsxs(
1241
+ "div",
1242
+ {
1243
+ className: cn(
1244
+ "overflow-hidden rounded-[1.1rem] border border-border bg-card",
1245
+ "shadow-[0_40px_90px_-40px_rgb(10_10_10/0.45)]",
1246
+ className
1247
+ ),
1248
+ style,
1249
+ children: [
1250
+ /* @__PURE__ */ jsxs("div", { className: "flex h-10 items-center gap-3 border-b border-border px-4", children: [
1251
+ /* @__PURE__ */ jsxs("div", { "aria-hidden": true, className: "flex gap-1.5", children: [
1252
+ /* @__PURE__ */ jsx("span", { className: "size-2.5 rounded-full bg-[#ff5f57]" }),
1253
+ /* @__PURE__ */ jsx("span", { className: "size-2.5 rounded-full bg-[#febc2e]" }),
1254
+ /* @__PURE__ */ jsx("span", { className: "size-2.5 rounded-full bg-[#28c840]" })
1255
+ ] }),
1256
+ /* @__PURE__ */ jsx("div", { className: "mx-auto flex h-6 min-w-0 max-w-[60%] flex-1 items-center justify-center rounded-md bg-muted px-3 text-[0.75rem] text-muted-foreground", children: /* @__PURE__ */ jsx("span", { className: "truncate", children: url }) }),
1257
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "w-[2.6rem]" })
1258
+ ] }),
1259
+ /* @__PURE__ */ jsx("div", { className: "relative", children })
1260
+ ]
1261
+ }
1262
+ );
1263
+ }
1264
+ function FloatChip({
1265
+ icon,
1266
+ title,
1267
+ detail,
1268
+ delay = 0,
1269
+ className,
1270
+ style
1271
+ }) {
1272
+ return /* @__PURE__ */ jsxs(
1273
+ "div",
1274
+ {
1275
+ className: cn(
1276
+ "oca-animate-drift flex items-center gap-3 rounded-2xl border border-border bg-card/90 py-2.5 pl-2.5 pr-4 backdrop-blur-md",
1277
+ "shadow-[0_24px_50px_-24px_rgb(10_10_10/0.35)]",
1278
+ className
1279
+ ),
1280
+ style: { animationDelay: `${-delay}s`, ...style },
1281
+ children: [
1282
+ icon ? /* @__PURE__ */ jsx("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-xl bg-muted text-foreground [&_svg]:size-4", children: icon }) : null,
1283
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col", children: [
1284
+ /* @__PURE__ */ jsx("span", { className: "text-[0.875rem] font-medium leading-tight", children: title }),
1285
+ detail ? /* @__PURE__ */ jsx("span", { className: "text-[0.8125rem] leading-tight text-muted-foreground", children: detail }) : null
1286
+ ] })
1287
+ ]
1288
+ }
1289
+ );
1290
+ }
1099
1291
 
1100
- export { Accent, ActivityList, CodeBlock, Container, CopyButton, DataTable, Display, DocHeader, DocLayout, EmptyLine, EmptyState, Eyebrow, Footnote, HashChip, KeyValueList, Lead, LoadingBlock, Notice, PackageCard, PageHeader, PageHero, ProgressSteps, Prose, Sats, SatsFigure, Section, SectionIntro, SplitBar, StatCard, StatGrid, StepGrid, TruthTable, UsdApprox, satsText, sectionVariants, useCopy };
1292
+ export { Accent, ActivityList, BrowserFrame, CodeBlock, Container, CopyButton, DataTable, Display, DocHeader, DocLayout, EmptyLine, EmptyState, Eyebrow, FloatChip, Footnote, HashChip, KeyValueList, Lead, LoadingBlock, Notice, PackageCard, PageHeader, PageHero, PhoneFrame, ProgressSteps, Prose, Reveal, Sats, SatsFigure, ScrollStory, Section, SectionIntro, SplitBar, StatCard, StatGrid, StepGrid, Ticker, TruthTable, UsdApprox, satsText, sectionVariants, useCopy, useInView };
1101
1293
  //# sourceMappingURL=components.mjs.map
1102
1294
  //# sourceMappingURL=components.mjs.map