@krizaka/ui 3.40.0 → 3.41.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/flock.d.ts +7 -1
- package/dist/flock.js +35 -7
- package/dist/flock.js.map +1 -1
- package/package.json +5 -5
- package/registry/action-bar.json +1 -1
- package/registry/examples/action-bar/icons.js +1 -1
- package/registry/examples/action-bar/icons.tsx +1 -1
- package/registry/examples/progress/bar-sizes.js +1 -1
- package/registry/examples/progress/bar-sizes.tsx +1 -1
- package/registry/flock.json +45 -2
- package/registry/index.json +5 -5
- package/registry/map.json +1 -1
- package/registry/nav-bar.json +1 -1
- package/registry/progress.json +1 -1
- package/registry/rail-nav.json +1 -1
- package/registry/site-footer.json +1 -1
package/dist/flock.d.ts
CHANGED
|
@@ -28,6 +28,12 @@ type FlockProps = Omit<React.ComponentProps<"div">, "children"> & FlockMoments &
|
|
|
28
28
|
tone?: "brand" | "own";
|
|
29
29
|
/** The source of chance (default `Math.random`): give it a fixed one in a test. */
|
|
30
30
|
random?: () => number;
|
|
31
|
+
/** How lively the moments are: 1 is calm (default), 2 doubles how often neighbours glance and the pigeon delivers. */
|
|
32
|
+
tempo?: number;
|
|
33
|
+
/** A bird the page points at (a product the reader is on): it looks up and jumps once each time it changes. */
|
|
34
|
+
active?: MascotBird | null;
|
|
35
|
+
/** The birds greet the reader once as the flock first comes into view: each jumps in turn, left to right. */
|
|
36
|
+
greet?: boolean;
|
|
31
37
|
};
|
|
32
38
|
/**
|
|
33
39
|
* The flock on its stage, alive. Decorative (`aria-hidden`): put the words beside it. Each bird idles in character; every
|
|
@@ -35,6 +41,6 @@ type FlockProps = Omit<React.ComponentProps<"div">, "children"> & FlockMoments &
|
|
|
35
41
|
* returns; a bird looks up when you come near. All of it pauses off screen and in a hidden tab, and the flock is still
|
|
36
42
|
* under `prefers-reduced-motion`.
|
|
37
43
|
*/
|
|
38
|
-
declare function Flock({ birds, ratio, tone, glance, courier, react, random, className, style, ...props }: FlockProps): React.JSX.Element;
|
|
44
|
+
declare function Flock({ birds, ratio, tone, glance, courier, react, random, tempo, active, greet, className, style, ...props }: FlockProps): React.JSX.Element;
|
|
39
45
|
|
|
40
46
|
export { Flock, type FlockBird, type FlockMoments, type FlockProps };
|
package/dist/flock.js
CHANGED
|
@@ -16,7 +16,9 @@ var COURIER_EVERY_MS = [26e3, 38e3];
|
|
|
16
16
|
var FLIGHT_MS = 2600;
|
|
17
17
|
var HAND_OVER_MS = 1300;
|
|
18
18
|
var LOOK_BACK_MS = 1600;
|
|
19
|
-
var between = (random, [lo, hi]) => lo + random() * (hi - lo);
|
|
19
|
+
var between = (random, [lo, hi], tempo = 1) => (lo + random() * (hi - lo)) / tempo;
|
|
20
|
+
var GREET_STEP_MS = 110;
|
|
21
|
+
var POKE_MS = 700;
|
|
20
22
|
function useMayMove() {
|
|
21
23
|
const [may, setMay] = React.useState(false);
|
|
22
24
|
React.useEffect(() => {
|
|
@@ -43,7 +45,7 @@ function useOnScreen(ref) {
|
|
|
43
45
|
}, [ref]);
|
|
44
46
|
return on;
|
|
45
47
|
}
|
|
46
|
-
function Flock({ birds, ratio = 16 / 9, tone = "brand", glance = true, courier = true, react = true, random = Math.random, className, style, ...props }) {
|
|
48
|
+
function Flock({ birds, ratio = 16 / 9, tone = "brand", glance = true, courier = true, react = true, random = Math.random, tempo = 1, active = null, greet = false, className, style, ...props }) {
|
|
47
49
|
const stage = React.useRef(null);
|
|
48
50
|
const bodies = React.useRef([]);
|
|
49
51
|
const may = useMayMove();
|
|
@@ -69,14 +71,14 @@ function Flock({ birds, ratio = 16 / 9, tone = "brand", glance = true, courier =
|
|
|
69
71
|
rest(a, b);
|
|
70
72
|
next();
|
|
71
73
|
}, between(random, GLANCE_HOLD_MS));
|
|
72
|
-
}, between(random, GLANCE_EVERY_MS));
|
|
74
|
+
}, between(random, GLANCE_EVERY_MS, tempo));
|
|
73
75
|
};
|
|
74
76
|
next();
|
|
75
77
|
return () => {
|
|
76
78
|
clearTimeout(timer);
|
|
77
79
|
clearTimeout(back);
|
|
78
80
|
};
|
|
79
|
-
}, [running, glance, order, random]);
|
|
81
|
+
}, [running, glance, order, random, tempo]);
|
|
80
82
|
React.useEffect(() => {
|
|
81
83
|
const pigeon = birds.findIndex((b) => b.bird === "pigeon");
|
|
82
84
|
if (!running || !courier || pigeon < 0 || birds.length < 2) return;
|
|
@@ -122,17 +124,43 @@ function Flock({ birds, ratio = 16 / 9, tone = "brand", glance = true, courier =
|
|
|
122
124
|
const schedule = (ms) => {
|
|
123
125
|
timer = setTimeout(async () => {
|
|
124
126
|
await trip();
|
|
125
|
-
if (alive) schedule(between(random, COURIER_EVERY_MS));
|
|
127
|
+
if (alive) schedule(between(random, COURIER_EVERY_MS, tempo));
|
|
126
128
|
}, ms);
|
|
127
129
|
};
|
|
128
|
-
schedule(COURIER_FIRST_MS);
|
|
130
|
+
schedule(COURIER_FIRST_MS / tempo);
|
|
129
131
|
return () => {
|
|
130
132
|
alive = false;
|
|
131
133
|
clearTimeout(timer);
|
|
132
134
|
animation?.cancel();
|
|
133
135
|
setFlying(null);
|
|
134
136
|
};
|
|
135
|
-
}, [running, courier, birds, random]);
|
|
137
|
+
}, [running, courier, birds, random, tempo]);
|
|
138
|
+
const greeted = React.useRef(false);
|
|
139
|
+
React.useEffect(() => {
|
|
140
|
+
if (!running || !greet || greeted.current) return;
|
|
141
|
+
greeted.current = true;
|
|
142
|
+
const timers = [];
|
|
143
|
+
order.forEach((index, n) => {
|
|
144
|
+
timers.push(setTimeout(() => setPoked((p) => ({ ...p, [index]: true })), n * GREET_STEP_MS));
|
|
145
|
+
timers.push(setTimeout(() => setPoked((p) => ({ ...p, [index]: false })), n * GREET_STEP_MS + POKE_MS));
|
|
146
|
+
});
|
|
147
|
+
return () => timers.forEach(clearTimeout);
|
|
148
|
+
}, [running, greet, order]);
|
|
149
|
+
React.useEffect(() => {
|
|
150
|
+
const index = active ? birds.findIndex((b) => b.bird === active) : -1;
|
|
151
|
+
if (!running || index < 0) return;
|
|
152
|
+
const start = setTimeout(() => {
|
|
153
|
+
look(index, "up");
|
|
154
|
+
setPoked((p) => ({ ...p, [index]: true }));
|
|
155
|
+
}, 0);
|
|
156
|
+
const t = setTimeout(() => setPoked((p) => ({ ...p, [index]: false })), POKE_MS);
|
|
157
|
+
return () => {
|
|
158
|
+
clearTimeout(start);
|
|
159
|
+
clearTimeout(t);
|
|
160
|
+
setPoked((p) => ({ ...p, [index]: false }));
|
|
161
|
+
rest(index);
|
|
162
|
+
};
|
|
163
|
+
}, [active, running, birds]);
|
|
136
164
|
const hover = (index, event) => {
|
|
137
165
|
if (!running || !react || event.pointerType !== "mouse") return;
|
|
138
166
|
look(index, "up");
|
package/dist/flock.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/flock/flock.tsx"],"sourcesContent":["\"use client\";\n\n// The flock, alive: birds standing on a stage, each at ease in character (Mascot `life`), and the moments between\n// them — two birds glance at each other, the pigeon carries its parcel across, a bird looks up when you come near or tap\n// it. Transform and opacity only: CSS for each bird's own life, a timer for the moments (no frame loop), and nothing at\n// all when the flock is off screen, the tab is hidden or the person asked for less motion.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport type { MascotBird, MascotLook } from \"../mascots\";\nimport { Mascot } from \"../mascots\";\n\nexport interface FlockBird {\n /** Which bird. */\n bird: MascotBird;\n /** Where its feet are, across the stage: 0 to 100 (% of the stage's width). */\n x: number;\n /** Where its feet are, down the stage: 0 to 100 (% of the stage's height). */\n y: number;\n /** How wide it is: % of the stage's width. */\n size: number;\n}\n\nexport interface FlockMoments {\n /** Two neighbours look at each other now and then. Default `true`. */\n glance?: boolean;\n /** The pigeon, when the flock has one, carries its parcel across to another bird and comes back. Default `true`. */\n courier?: boolean;\n /** A bird looks up when the pointer comes near, and jumps a little when tapped. Default `true`. */\n react?: boolean;\n}\n\nexport type FlockProps = Omit<React.ComponentProps<\"div\">, \"children\"> &\n FlockMoments & {\n /** The birds on the stage. */\n birds: readonly FlockBird[];\n /** The stage's shape, width over height (default 16 / 9). It keeps its ratio at any width, so the birds keep their places. */\n ratio?: number;\n /** The birds' colours: the host's accent (`brand`, default) or the Krizaka story's (`own`). */\n tone?: \"brand\" | \"own\";\n /** The source of chance (default `Math.random`): give it a fixed one in a test. */\n random?: () => number;\n };\n\n// @fixed: the rhythm of the moments — how long a glance lasts, how often one happens, how long a delivery takes. A\n// choreography's pace, not a business parameter.\nconst GLANCE_HOLD_MS = [1500, 2400] as const;\nconst GLANCE_EVERY_MS = [4200, 9000] as const;\nconst COURIER_FIRST_MS = 9000;\nconst COURIER_EVERY_MS = [26000, 38000] as const;\nconst FLIGHT_MS = 2600;\nconst HAND_OVER_MS = 1300;\nconst LOOK_BACK_MS = 1600;\n\nconst between = (random: () => number, [lo, hi]: readonly [number, number]) => lo + random() * (hi - lo);\n\n/** `true` while the page may move: no reduced-motion request, the tab visible. */\nfunction useMayMove(): boolean {\n const [may, setMay] = React.useState(false);\n React.useEffect(() => {\n const query = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n const update = () => setMay(!query.matches && document.visibilityState !== \"hidden\");\n update();\n query.addEventListener(\"change\", update);\n document.addEventListener(\"visibilitychange\", update);\n return () => {\n query.removeEventListener(\"change\", update);\n document.removeEventListener(\"visibilitychange\", update);\n };\n }, []);\n return may;\n}\n\n/** `true` while the element is on screen. */\nfunction useOnScreen(ref: React.RefObject<Element | null>): boolean {\n const [on, setOn] = React.useState(false);\n React.useEffect(() => {\n const el = ref.current;\n if (!el || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(([entry]) => setOn(Boolean(entry?.isIntersecting)), { threshold: 0.1 });\n observer.observe(el);\n return () => observer.disconnect();\n }, [ref]);\n return on;\n}\n\n/**\n * The flock on its stage, alive. Decorative (`aria-hidden`): put the words beside it. Each bird idles in character; every\n * few seconds two neighbours glance at each other; every half minute the pigeon carries its parcel to another bird and\n * returns; a bird looks up when you come near. All of it pauses off screen and in a hidden tab, and the flock is still\n * under `prefers-reduced-motion`.\n */\nexport function Flock({ birds, ratio = 16 / 9, tone = \"brand\", glance = true, courier = true, react = true, random = Math.random, className, style, ...props }: FlockProps) {\n const stage = React.useRef<HTMLDivElement>(null);\n const bodies = React.useRef<(HTMLDivElement | null)[]>([]);\n const may = useMayMove();\n const onScreen = useOnScreen(stage);\n const running = may && onScreen;\n const [looks, setLooks] = React.useState<Record<number, MascotLook>>({});\n const [flying, setFlying] = React.useState<{ index: number; bank: number } | null>(null);\n const [poked, setPoked] = React.useState<Record<number, boolean>>({});\n\n // Left to right: a bird's neighbours are the birds beside it on the stage.\n const order = React.useMemo(() => birds.map((_, i) => i).sort((a, b) => birds[a]!.x - birds[b]!.x), [birds]);\n const look = (index: number, to: MascotLook) => setLooks((l) => (l[index] === to ? l : { ...l, [index]: to }));\n const rest = (...indexes: number[]) => setLooks((l) => Object.fromEntries(Object.entries(l).filter(([k]) => !indexes.includes(Number(k)))));\n\n // Glances: two neighbours turn towards each other, hold, and look away.\n React.useEffect(() => {\n if (!running || !glance || order.length < 2) return;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let back: ReturnType<typeof setTimeout> | undefined;\n const next = () => {\n timer = setTimeout(() => {\n const at = Math.floor(random() * (order.length - 1));\n const [a, b] = [order[at]!, order[at + 1]!];\n look(a, \"right\");\n look(b, \"left\");\n back = setTimeout(() => {\n rest(a, b);\n next();\n }, between(random, GLANCE_HOLD_MS));\n }, between(random, GLANCE_EVERY_MS));\n };\n next();\n return () => {\n clearTimeout(timer);\n clearTimeout(back);\n };\n }, [running, glance, order, random]);\n\n // The delivery: the pigeon takes its parcel across to the farthest bird, hands it over, and flies back.\n React.useEffect(() => {\n const pigeon = birds.findIndex((b) => b.bird === \"pigeon\");\n if (!running || !courier || pigeon < 0 || birds.length < 2) return;\n let alive = true;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let animation: Animation | undefined;\n const wait = (ms: number) => new Promise<void>((resolve) => (timer = setTimeout(resolve, ms)));\n const trip = async () => {\n const el = bodies.current[pigeon];\n const box = stage.current?.getBoundingClientRect();\n if (!el || !box || typeof el.animate !== \"function\") return;\n // The farthest bird from the pigeon, so the parcel crosses the whole stage.\n const from = birds[pigeon]!;\n const target = birds.reduce((far, b, i) => (i !== pigeon && Math.abs(b.x - from.x) > Math.abs(birds[far]!.x - from.x) ? i : far), pigeon === 0 ? 1 : 0);\n const to = birds[target]!;\n const side = to.x > from.x ? 1 : -1;\n // It lands beside the receiver, not on it.\n const dx = ((to.x - from.x) / 100) * box.width - side * (to.size / 100) * box.width * 0.55;\n const lift = (from.size / 100) * box.width * 0.35;\n const fly = async (fromX: number, toX: number) => {\n setFlying({ index: pigeon, bank: Math.sign(toX - fromX) });\n animation = el.animate(\n [\n { transform: `translate(${fromX}px, 0)`, easing: \"ease-out\" },\n { transform: `translate(${(fromX + toX) / 2}px, ${-lift}px)`, easing: \"ease-in\", offset: 0.5 },\n { transform: `translate(${toX}px, 0)` },\n ],\n { duration: FLIGHT_MS, fill: \"forwards\" },\n );\n await animation.finished.catch(() => undefined);\n setFlying(null);\n };\n look(target, side > 0 ? \"left\" : \"right\");\n await fly(0, dx);\n if (!alive) return;\n // The hand-over: the receiver looks at the pigeon, the pigeon hops on the doorstep.\n setPoked((p) => ({ ...p, [pigeon]: true }));\n await wait(HAND_OVER_MS);\n if (!alive) return;\n setPoked((p) => ({ ...p, [pigeon]: false }));\n look(target, \"up\");\n await fly(dx, 0);\n animation?.cancel();\n rest(target);\n };\n const schedule = (ms: number) => {\n timer = setTimeout(async () => {\n await trip();\n if (alive) schedule(between(random, COURIER_EVERY_MS));\n }, ms);\n };\n schedule(COURIER_FIRST_MS);\n return () => {\n alive = false;\n clearTimeout(timer);\n animation?.cancel();\n setFlying(null);\n };\n }, [running, courier, birds, random]);\n\n // A bird looks up when you come near, and jumps when you tap it.\n const hover = (index: number, event: React.PointerEvent) => {\n if (!running || !react || event.pointerType !== \"mouse\") return;\n look(index, \"up\");\n };\n const leave = (index: number) => {\n if (!react) return;\n rest(index);\n };\n const tap = (index: number) => {\n if (!running || !react) return;\n look(index, \"up\");\n setPoked((p) => ({ ...p, [index]: true }));\n setTimeout(() => {\n setPoked((p) => ({ ...p, [index]: false }));\n rest(index);\n }, LOOK_BACK_MS);\n };\n\n return (\n <div\n ref={stage}\n aria-hidden\n data-flock=\"\"\n data-running={running ? \"\" : undefined}\n className={cn(\"relative w-full\", className)}\n style={{ aspectRatio: String(ratio), containerType: \"inline-size\", ...style }}\n {...props}\n >\n {birds.map((b, i) => (\n <div\n key={`${b.bird}-${i}`}\n ref={(el) => {\n bodies.current[i] = el;\n }}\n data-flock-bird={b.bird}\n className=\"absolute touch-manipulation\"\n style={{ left: `${b.x - b.size / 2}%`, width: `${b.size}%`, top: `calc(${b.y}% - ${b.size * 0.9}cqw)`, aspectRatio: \"1\", pointerEvents: react ? \"auto\" : \"none\" }}\n onPointerEnter={(event) => hover(i, event)}\n onPointerLeave={() => leave(i)}\n onPointerDown={() => tap(i)}\n >\n <Mascot\n bird={b.bird}\n tone={tone}\n life\n look={looks[i]}\n flying={flying?.index === i}\n bank={flying?.index === i ? flying.bank : 0}\n poked={poked[i]}\n size={120}\n style={{ width: \"100%\", height: \"100%\", display: \"block\", overflow: \"visible\" }}\n />\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAMA,YAAY,WAAW;AAoOb;AA5LV,IAAM,iBAAiB,CAAC,MAAM,IAAI;AAClC,IAAM,kBAAkB,CAAC,MAAM,GAAI;AACnC,IAAM,mBAAmB;AACzB,IAAM,mBAAmB,CAAC,MAAO,IAAK;AACtC,IAAM,YAAY;AAClB,IAAM,eAAe;AACrB,IAAM,eAAe;AAErB,IAAM,UAAU,CAAC,QAAsB,CAAC,IAAI,EAAE,MAAiC,KAAK,OAAO,KAAK,KAAK;AAGrG,SAAS,aAAsB;AAC7B,QAAM,CAAC,KAAK,MAAM,IAAU,eAAS,KAAK;AAC1C,EAAM,gBAAU,MAAM;AACpB,UAAM,QAAQ,OAAO,WAAW,kCAAkC;AAClE,UAAM,SAAS,MAAM,OAAO,CAAC,MAAM,WAAW,SAAS,oBAAoB,QAAQ;AACnF,WAAO;AACP,UAAM,iBAAiB,UAAU,MAAM;AACvC,aAAS,iBAAiB,oBAAoB,MAAM;AACpD,WAAO,MAAM;AACX,YAAM,oBAAoB,UAAU,MAAM;AAC1C,eAAS,oBAAoB,oBAAoB,MAAM;AAAA,IACzD;AAAA,EACF,GAAG,CAAC,CAAC;AACL,SAAO;AACT;AAGA,SAAS,YAAY,KAA+C;AAClE,QAAM,CAAC,IAAI,KAAK,IAAU,eAAS,KAAK;AACxC,EAAM,gBAAU,MAAM;AACpB,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,MAAM,OAAO,yBAAyB,YAAa;AACxD,UAAM,WAAW,IAAI,qBAAqB,CAAC,CAAC,KAAK,MAAM,MAAM,QAAQ,OAAO,cAAc,CAAC,GAAG,EAAE,WAAW,IAAI,CAAC;AAChH,aAAS,QAAQ,EAAE;AACnB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,GAAG,CAAC;AACR,SAAO;AACT;AAQO,SAAS,MAAM,EAAE,OAAO,QAAQ,KAAK,GAAG,OAAO,SAAS,SAAS,MAAM,UAAU,MAAM,QAAQ,MAAM,SAAS,KAAK,QAAQ,WAAW,OAAO,GAAG,MAAM,GAAe;AAC1K,QAAM,QAAc,aAAuB,IAAI;AAC/C,QAAM,SAAe,aAAkC,CAAC,CAAC;AACzD,QAAM,MAAM,WAAW;AACvB,QAAM,WAAW,YAAY,KAAK;AAClC,QAAM,UAAU,OAAO;AACvB,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAqC,CAAC,CAAC;AACvE,QAAM,CAAC,QAAQ,SAAS,IAAU,eAAiD,IAAI;AACvF,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAkC,CAAC,CAAC;AAGpE,QAAM,QAAc,cAAQ,MAAM,MAAM,IAAI,CAAC,GAAG,MAAM,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,MAAM,CAAC,EAAG,IAAI,MAAM,CAAC,EAAG,CAAC,GAAG,CAAC,KAAK,CAAC;AAC3G,QAAM,OAAO,CAAC,OAAe,OAAmB,SAAS,CAAC,MAAO,EAAE,KAAK,MAAM,KAAK,IAAI,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,GAAG,CAAE;AAC7G,QAAM,OAAO,IAAI,YAAsB,SAAS,CAAC,MAAM,OAAO,YAAY,OAAO,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,SAAS,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAG1I,EAAM,gBAAU,MAAM;AACpB,QAAI,CAAC,WAAW,CAAC,UAAU,MAAM,SAAS,EAAG;AAC7C,QAAI;AACJ,QAAI;AACJ,UAAM,OAAO,MAAM;AACjB,cAAQ,WAAW,MAAM;AACvB,cAAM,KAAK,KAAK,MAAM,OAAO,KAAK,MAAM,SAAS,EAAE;AACnD,cAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAI,MAAM,KAAK,CAAC,CAAE;AAC1C,aAAK,GAAG,OAAO;AACf,aAAK,GAAG,MAAM;AACd,eAAO,WAAW,MAAM;AACtB,eAAK,GAAG,CAAC;AACT,eAAK;AAAA,QACP,GAAG,QAAQ,QAAQ,cAAc,CAAC;AAAA,MACpC,GAAG,QAAQ,QAAQ,eAAe,CAAC;AAAA,IACrC;AACA,SAAK;AACL,WAAO,MAAM;AACX,mBAAa,KAAK;AAClB,mBAAa,IAAI;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,SAAS,QAAQ,OAAO,MAAM,CAAC;AAGnC,EAAM,gBAAU,MAAM;AACpB,UAAM,SAAS,MAAM,UAAU,CAAC,MAAM,EAAE,SAAS,QAAQ;AACzD,QAAI,CAAC,WAAW,CAAC,WAAW,SAAS,KAAK,MAAM,SAAS,EAAG;AAC5D,QAAI,QAAQ;AACZ,QAAI;AACJ,QAAI;AACJ,UAAM,OAAO,CAAC,OAAe,IAAI,QAAc,CAAC,YAAa,QAAQ,WAAW,SAAS,EAAE,CAAE;AAC7F,UAAM,OAAO,YAAY;AACvB,YAAM,KAAK,OAAO,QAAQ,MAAM;AAChC,YAAM,MAAM,MAAM,SAAS,sBAAsB;AACjD,UAAI,CAAC,MAAM,CAAC,OAAO,OAAO,GAAG,YAAY,WAAY;AAErD,YAAM,OAAO,MAAM,MAAM;AACzB,YAAM,SAAS,MAAM,OAAO,CAAC,KAAK,GAAG,MAAO,MAAM,UAAU,KAAK,IAAI,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,MAAM,GAAG,EAAG,IAAI,KAAK,CAAC,IAAI,IAAI,KAAM,WAAW,IAAI,IAAI,CAAC;AACtJ,YAAM,KAAK,MAAM,MAAM;AACvB,YAAM,OAAO,GAAG,IAAI,KAAK,IAAI,IAAI;AAEjC,YAAM,MAAO,GAAG,IAAI,KAAK,KAAK,MAAO,IAAI,QAAQ,QAAQ,GAAG,OAAO,OAAO,IAAI,QAAQ;AACtF,YAAM,OAAQ,KAAK,OAAO,MAAO,IAAI,QAAQ;AAC7C,YAAM,MAAM,OAAO,OAAe,QAAgB;AAChD,kBAAU,EAAE,OAAO,QAAQ,MAAM,KAAK,KAAK,MAAM,KAAK,EAAE,CAAC;AACzD,oBAAY,GAAG;AAAA,UACb;AAAA,YACE,EAAE,WAAW,aAAa,KAAK,UAAU,QAAQ,WAAW;AAAA,YAC5D,EAAE,WAAW,cAAc,QAAQ,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,QAAQ,WAAW,QAAQ,IAAI;AAAA,YAC7F,EAAE,WAAW,aAAa,GAAG,SAAS;AAAA,UACxC;AAAA,UACA,EAAE,UAAU,WAAW,MAAM,WAAW;AAAA,QAC1C;AACA,cAAM,UAAU,SAAS,MAAM,MAAM,MAAS;AAC9C,kBAAU,IAAI;AAAA,MAChB;AACA,WAAK,QAAQ,OAAO,IAAI,SAAS,OAAO;AACxC,YAAM,IAAI,GAAG,EAAE;AACf,UAAI,CAAC,MAAO;AAEZ,eAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,GAAG,KAAK,EAAE;AAC1C,YAAM,KAAK,YAAY;AACvB,UAAI,CAAC,MAAO;AACZ,eAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,GAAG,MAAM,EAAE;AAC3C,WAAK,QAAQ,IAAI;AACjB,YAAM,IAAI,IAAI,CAAC;AACf,iBAAW,OAAO;AAClB,WAAK,MAAM;AAAA,IACb;AACA,UAAM,WAAW,CAAC,OAAe;AAC/B,cAAQ,WAAW,YAAY;AAC7B,cAAM,KAAK;AACX,YAAI,MAAO,UAAS,QAAQ,QAAQ,gBAAgB,CAAC;AAAA,MACvD,GAAG,EAAE;AAAA,IACP;AACA,aAAS,gBAAgB;AACzB,WAAO,MAAM;AACX,cAAQ;AACR,mBAAa,KAAK;AAClB,iBAAW,OAAO;AAClB,gBAAU,IAAI;AAAA,IAChB;AAAA,EACF,GAAG,CAAC,SAAS,SAAS,OAAO,MAAM,CAAC;AAGpC,QAAM,QAAQ,CAAC,OAAe,UAA8B;AAC1D,QAAI,CAAC,WAAW,CAAC,SAAS,MAAM,gBAAgB,QAAS;AACzD,SAAK,OAAO,IAAI;AAAA,EAClB;AACA,QAAM,QAAQ,CAAC,UAAkB;AAC/B,QAAI,CAAC,MAAO;AACZ,SAAK,KAAK;AAAA,EACZ;AACA,QAAM,MAAM,CAAC,UAAkB;AAC7B,QAAI,CAAC,WAAW,CAAC,MAAO;AACxB,SAAK,OAAO,IAAI;AAChB,aAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,KAAK,EAAE;AACzC,eAAW,MAAM;AACf,eAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,MAAM,EAAE;AAC1C,WAAK,KAAK;AAAA,IACZ,GAAG,YAAY;AAAA,EACjB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,eAAW;AAAA,MACX,cAAW;AAAA,MACX,gBAAc,UAAU,KAAK;AAAA,MAC7B,WAAW,GAAG,mBAAmB,SAAS;AAAA,MAC1C,OAAO,EAAE,aAAa,OAAO,KAAK,GAAG,eAAe,eAAe,GAAG,MAAM;AAAA,MAC3E,GAAG;AAAA,MAEH,gBAAM,IAAI,CAAC,GAAG,MACb;AAAA,QAAC;AAAA;AAAA,UAEC,KAAK,CAAC,OAAO;AACX,mBAAO,QAAQ,CAAC,IAAI;AAAA,UACtB;AAAA,UACA,mBAAiB,EAAE;AAAA,UACnB,WAAU;AAAA,UACV,OAAO,EAAE,MAAM,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,OAAO,GAAG,EAAE,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,aAAa,KAAK,eAAe,QAAQ,SAAS,OAAO;AAAA,UAChK,gBAAgB,CAAC,UAAU,MAAM,GAAG,KAAK;AAAA,UACzC,gBAAgB,MAAM,MAAM,CAAC;AAAA,UAC7B,eAAe,MAAM,IAAI,CAAC;AAAA,UAE1B;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,EAAE;AAAA,cACR;AAAA,cACA,MAAI;AAAA,cACJ,MAAM,MAAM,CAAC;AAAA,cACb,QAAQ,QAAQ,UAAU;AAAA,cAC1B,MAAM,QAAQ,UAAU,IAAI,OAAO,OAAO;AAAA,cAC1C,OAAO,MAAM,CAAC;AAAA,cACd,MAAM;AAAA,cACN,OAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,SAAS,SAAS,UAAU,UAAU;AAAA;AAAA,UAChF;AAAA;AAAA,QArBK,GAAG,EAAE,IAAI,IAAI,CAAC;AAAA,MAsBrB,CACD;AAAA;AAAA,EACH;AAEJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/flock/flock.tsx"],"sourcesContent":["\"use client\";\n\n// The flock, alive: birds standing on a stage, each at ease in character (Mascot `life`), and the moments between\n// them — two birds glance at each other, the pigeon carries its parcel across, a bird looks up when you come near or tap\n// it. Transform and opacity only: CSS for each bird's own life, a timer for the moments (no frame loop), and nothing at\n// all when the flock is off screen, the tab is hidden or the person asked for less motion.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport type { MascotBird, MascotLook } from \"../mascots\";\nimport { Mascot } from \"../mascots\";\n\nexport interface FlockBird {\n /** Which bird. */\n bird: MascotBird;\n /** Where its feet are, across the stage: 0 to 100 (% of the stage's width). */\n x: number;\n /** Where its feet are, down the stage: 0 to 100 (% of the stage's height). */\n y: number;\n /** How wide it is: % of the stage's width. */\n size: number;\n}\n\nexport interface FlockMoments {\n /** Two neighbours look at each other now and then. Default `true`. */\n glance?: boolean;\n /** The pigeon, when the flock has one, carries its parcel across to another bird and comes back. Default `true`. */\n courier?: boolean;\n /** A bird looks up when the pointer comes near, and jumps a little when tapped. Default `true`. */\n react?: boolean;\n}\n\nexport type FlockProps = Omit<React.ComponentProps<\"div\">, \"children\"> &\n FlockMoments & {\n /** The birds on the stage. */\n birds: readonly FlockBird[];\n /** The stage's shape, width over height (default 16 / 9). It keeps its ratio at any width, so the birds keep their places. */\n ratio?: number;\n /** The birds' colours: the host's accent (`brand`, default) or the Krizaka story's (`own`). */\n tone?: \"brand\" | \"own\";\n /** The source of chance (default `Math.random`): give it a fixed one in a test. */\n random?: () => number;\n /** How lively the moments are: 1 is calm (default), 2 doubles how often neighbours glance and the pigeon delivers. */\n tempo?: number;\n /** A bird the page points at (a product the reader is on): it looks up and jumps once each time it changes. */\n active?: MascotBird | null;\n /** The birds greet the reader once as the flock first comes into view: each jumps in turn, left to right. */\n greet?: boolean;\n };\n\n// @fixed: the rhythm of the moments — how long a glance lasts, how often one happens, how long a delivery takes. A\n// choreography's pace, not a business parameter.\nconst GLANCE_HOLD_MS = [1500, 2400] as const;\nconst GLANCE_EVERY_MS = [4200, 9000] as const;\nconst COURIER_FIRST_MS = 9000;\nconst COURIER_EVERY_MS = [26000, 38000] as const;\nconst FLIGHT_MS = 2600;\nconst HAND_OVER_MS = 1300;\nconst LOOK_BACK_MS = 1600;\n\nconst between = (random: () => number, [lo, hi]: readonly [number, number], tempo = 1) => (lo + random() * (hi - lo)) / tempo;\nconst GREET_STEP_MS = 110;\nconst POKE_MS = 700;\n\n/** `true` while the page may move: no reduced-motion request, the tab visible. */\nfunction useMayMove(): boolean {\n const [may, setMay] = React.useState(false);\n React.useEffect(() => {\n const query = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n const update = () => setMay(!query.matches && document.visibilityState !== \"hidden\");\n update();\n query.addEventListener(\"change\", update);\n document.addEventListener(\"visibilitychange\", update);\n return () => {\n query.removeEventListener(\"change\", update);\n document.removeEventListener(\"visibilitychange\", update);\n };\n }, []);\n return may;\n}\n\n/** `true` while the element is on screen. */\nfunction useOnScreen(ref: React.RefObject<Element | null>): boolean {\n const [on, setOn] = React.useState(false);\n React.useEffect(() => {\n const el = ref.current;\n if (!el || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(([entry]) => setOn(Boolean(entry?.isIntersecting)), { threshold: 0.1 });\n observer.observe(el);\n return () => observer.disconnect();\n }, [ref]);\n return on;\n}\n\n/**\n * The flock on its stage, alive. Decorative (`aria-hidden`): put the words beside it. Each bird idles in character; every\n * few seconds two neighbours glance at each other; every half minute the pigeon carries its parcel to another bird and\n * returns; a bird looks up when you come near. All of it pauses off screen and in a hidden tab, and the flock is still\n * under `prefers-reduced-motion`.\n */\nexport function Flock({ birds, ratio = 16 / 9, tone = \"brand\", glance = true, courier = true, react = true, random = Math.random, tempo = 1, active = null, greet = false, className, style, ...props }: FlockProps) {\n const stage = React.useRef<HTMLDivElement>(null);\n const bodies = React.useRef<(HTMLDivElement | null)[]>([]);\n const may = useMayMove();\n const onScreen = useOnScreen(stage);\n const running = may && onScreen;\n const [looks, setLooks] = React.useState<Record<number, MascotLook>>({});\n const [flying, setFlying] = React.useState<{ index: number; bank: number } | null>(null);\n const [poked, setPoked] = React.useState<Record<number, boolean>>({});\n\n // Left to right: a bird's neighbours are the birds beside it on the stage.\n const order = React.useMemo(() => birds.map((_, i) => i).sort((a, b) => birds[a]!.x - birds[b]!.x), [birds]);\n const look = (index: number, to: MascotLook) => setLooks((l) => (l[index] === to ? l : { ...l, [index]: to }));\n const rest = (...indexes: number[]) => setLooks((l) => Object.fromEntries(Object.entries(l).filter(([k]) => !indexes.includes(Number(k)))));\n\n // Glances: two neighbours turn towards each other, hold, and look away.\n React.useEffect(() => {\n if (!running || !glance || order.length < 2) return;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let back: ReturnType<typeof setTimeout> | undefined;\n const next = () => {\n timer = setTimeout(() => {\n const at = Math.floor(random() * (order.length - 1));\n const [a, b] = [order[at]!, order[at + 1]!];\n look(a, \"right\");\n look(b, \"left\");\n back = setTimeout(() => {\n rest(a, b);\n next();\n }, between(random, GLANCE_HOLD_MS));\n }, between(random, GLANCE_EVERY_MS, tempo));\n };\n next();\n return () => {\n clearTimeout(timer);\n clearTimeout(back);\n };\n }, [running, glance, order, random, tempo]);\n\n // The delivery: the pigeon takes its parcel across to the farthest bird, hands it over, and flies back.\n React.useEffect(() => {\n const pigeon = birds.findIndex((b) => b.bird === \"pigeon\");\n if (!running || !courier || pigeon < 0 || birds.length < 2) return;\n let alive = true;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let animation: Animation | undefined;\n const wait = (ms: number) => new Promise<void>((resolve) => (timer = setTimeout(resolve, ms)));\n const trip = async () => {\n const el = bodies.current[pigeon];\n const box = stage.current?.getBoundingClientRect();\n if (!el || !box || typeof el.animate !== \"function\") return;\n // The farthest bird from the pigeon, so the parcel crosses the whole stage.\n const from = birds[pigeon]!;\n const target = birds.reduce((far, b, i) => (i !== pigeon && Math.abs(b.x - from.x) > Math.abs(birds[far]!.x - from.x) ? i : far), pigeon === 0 ? 1 : 0);\n const to = birds[target]!;\n const side = to.x > from.x ? 1 : -1;\n // It lands beside the receiver, not on it.\n const dx = ((to.x - from.x) / 100) * box.width - side * (to.size / 100) * box.width * 0.55;\n const lift = (from.size / 100) * box.width * 0.35;\n const fly = async (fromX: number, toX: number) => {\n setFlying({ index: pigeon, bank: Math.sign(toX - fromX) });\n animation = el.animate(\n [\n { transform: `translate(${fromX}px, 0)`, easing: \"ease-out\" },\n { transform: `translate(${(fromX + toX) / 2}px, ${-lift}px)`, easing: \"ease-in\", offset: 0.5 },\n { transform: `translate(${toX}px, 0)` },\n ],\n { duration: FLIGHT_MS, fill: \"forwards\" },\n );\n await animation.finished.catch(() => undefined);\n setFlying(null);\n };\n look(target, side > 0 ? \"left\" : \"right\");\n await fly(0, dx);\n if (!alive) return;\n // The hand-over: the receiver looks at the pigeon, the pigeon hops on the doorstep.\n setPoked((p) => ({ ...p, [pigeon]: true }));\n await wait(HAND_OVER_MS);\n if (!alive) return;\n setPoked((p) => ({ ...p, [pigeon]: false }));\n look(target, \"up\");\n await fly(dx, 0);\n animation?.cancel();\n rest(target);\n };\n const schedule = (ms: number) => {\n timer = setTimeout(async () => {\n await trip();\n if (alive) schedule(between(random, COURIER_EVERY_MS, tempo));\n }, ms);\n };\n schedule(COURIER_FIRST_MS / tempo);\n return () => {\n alive = false;\n clearTimeout(timer);\n animation?.cancel();\n setFlying(null);\n };\n }, [running, courier, birds, random, tempo]);\n\n // The greeting: the first time the flock is on screen, each bird jumps in turn.\n const greeted = React.useRef(false);\n React.useEffect(() => {\n if (!running || !greet || greeted.current) return;\n greeted.current = true;\n const timers: ReturnType<typeof setTimeout>[] = [];\n order.forEach((index, n) => {\n timers.push(setTimeout(() => setPoked((p) => ({ ...p, [index]: true })), n * GREET_STEP_MS));\n timers.push(setTimeout(() => setPoked((p) => ({ ...p, [index]: false })), n * GREET_STEP_MS + POKE_MS));\n });\n return () => timers.forEach(clearTimeout);\n }, [running, greet, order]);\n\n // The bird the page points at looks up and jumps.\n React.useEffect(() => {\n const index = active ? birds.findIndex((b) => b.bird === active) : -1;\n if (!running || index < 0) return;\n const start = setTimeout(() => {\n look(index, \"up\");\n setPoked((p) => ({ ...p, [index]: true }));\n }, 0);\n const t = setTimeout(() => setPoked((p) => ({ ...p, [index]: false })), POKE_MS);\n return () => {\n clearTimeout(start);\n clearTimeout(t);\n setPoked((p) => ({ ...p, [index]: false }));\n rest(index);\n };\n }, [active, running, birds]);\n\n // A bird looks up when you come near, and jumps when you tap it.\n const hover = (index: number, event: React.PointerEvent) => {\n if (!running || !react || event.pointerType !== \"mouse\") return;\n look(index, \"up\");\n };\n const leave = (index: number) => {\n if (!react) return;\n rest(index);\n };\n const tap = (index: number) => {\n if (!running || !react) return;\n look(index, \"up\");\n setPoked((p) => ({ ...p, [index]: true }));\n setTimeout(() => {\n setPoked((p) => ({ ...p, [index]: false }));\n rest(index);\n }, LOOK_BACK_MS);\n };\n\n return (\n <div\n ref={stage}\n aria-hidden\n data-flock=\"\"\n data-running={running ? \"\" : undefined}\n className={cn(\"relative w-full\", className)}\n style={{ aspectRatio: String(ratio), containerType: \"inline-size\", ...style }}\n {...props}\n >\n {birds.map((b, i) => (\n <div\n key={`${b.bird}-${i}`}\n ref={(el) => {\n bodies.current[i] = el;\n }}\n data-flock-bird={b.bird}\n className=\"absolute touch-manipulation\"\n style={{ left: `${b.x - b.size / 2}%`, width: `${b.size}%`, top: `calc(${b.y}% - ${b.size * 0.9}cqw)`, aspectRatio: \"1\", pointerEvents: react ? \"auto\" : \"none\" }}\n onPointerEnter={(event) => hover(i, event)}\n onPointerLeave={() => leave(i)}\n onPointerDown={() => tap(i)}\n >\n <Mascot\n bird={b.bird}\n tone={tone}\n life\n look={looks[i]}\n flying={flying?.index === i}\n bank={flying?.index === i ? flying.bank : 0}\n poked={poked[i]}\n size={120}\n style={{ width: \"100%\", height: \"100%\", display: \"block\", overflow: \"visible\" }}\n />\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krizaka/ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.41.0",
|
|
4
4
|
"description": "The Krizaka front-end platform's components: the primitives (button, card, dialog, toast, menu, field, tabs, chip, switch, slider, command palette, theme, countdown…) on the semantic --kz-* tokens, the animated marks (Krizaka, Orazaka, Orochia, Yasakia, Yororis) and the motion signature — web and /native.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"krizaka",
|
|
@@ -745,7 +745,7 @@
|
|
|
745
745
|
],
|
|
746
746
|
"peerDependencies": {
|
|
747
747
|
"@krizaka/query": ">=0.2.1",
|
|
748
|
-
"@krizaka/tailwind": ">=3.
|
|
748
|
+
"@krizaka/tailwind": ">=3.41.0",
|
|
749
749
|
"@maplibre/maplibre-react-native": ">=11",
|
|
750
750
|
"@shopify/flash-list": ">=2",
|
|
751
751
|
"expo-camera": ">=16",
|
|
@@ -805,8 +805,8 @@
|
|
|
805
805
|
"typescript": "^5.9.0",
|
|
806
806
|
"vitest": "^4.1.11",
|
|
807
807
|
"zod": "^4.6.5",
|
|
808
|
-
"@krizaka/
|
|
809
|
-
"@krizaka/
|
|
808
|
+
"@krizaka/config": "0.4.1",
|
|
809
|
+
"@krizaka/query": "0.2.1"
|
|
810
810
|
},
|
|
811
811
|
"engines": {
|
|
812
812
|
"node": ">=22"
|
|
@@ -2550,7 +2550,7 @@
|
|
|
2550
2550
|
"tailwind-variants": "^3.3.1",
|
|
2551
2551
|
"@krizaka/intl": "^0.2.1",
|
|
2552
2552
|
"@krizaka/pacer": "^0.1.0",
|
|
2553
|
-
"@krizaka/tokens": "^3.
|
|
2553
|
+
"@krizaka/tokens": "^3.41.0"
|
|
2554
2554
|
},
|
|
2555
2555
|
"scripts": {
|
|
2556
2556
|
"build": "node -e \"const fs=require('node:fs');fs.rmSync('dist',{recursive:true,force:true});for(const f of fs.readdirSync('registry/examples',{recursive:true}))if(/\\.(js|d\\.ts)$/.test(f))fs.rmSync('registry/examples/'+f)\" && tsup && node -e \"require('node:fs').copyFileSync('src/motion.css','dist/motion.css');require('node:fs').copyFileSync('src/blocks.css','dist/blocks.css');require('node:fs').copyFileSync('src/story.css','dist/story.css')\" && node scripts/build-registry.mjs",
|
package/registry/action-bar.json
CHANGED
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"description": "Discuss, propose, report: icon buttons with their words in a tooltip.",
|
|
128
128
|
"path": "examples/action-bar/icons.tsx",
|
|
129
129
|
"module": "@krizaka/ui/registry/examples/action-bar/icons",
|
|
130
|
-
"code": "import { ActionBar } from \"@krizaka/ui/action-bar\";\n\nconst glyph = (d: string) => (\n <svg viewBox=\"0 0 24 24\" width=\"17\" height=\"17\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d={d} />\n </svg>\n);\nconst repo = \"https://github.com/krizaka/krizaka-build\";\n\n// `icons`: where room is short (the foot of a card) — the words are the accessible name and a tooltip on hover and focus.\nexport default function ActionBarIcons() {\n return (\n <ActionBar\n variant=\"icons\"\n label=\"Talk about Krizaka Java\"\n actions={[\n { href:
|
|
130
|
+
"code": "import { ActionBar } from \"@krizaka/ui/action-bar\";\n\nconst glyph = (d: string) => (\n <svg viewBox=\"0 0 24 24\" width=\"17\" height=\"17\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d={d} />\n </svg>\n);\nconst repo = \"https://github.com/krizaka/krizaka-build\";\n\n// `icons`: where room is short (the foot of a card) — the words are the accessible name and a tooltip on hover and focus.\nexport default function ActionBarIcons() {\n return (\n <ActionBar\n variant=\"icons\"\n label=\"Talk about Krizaka Java\"\n actions={[\n { href: `https://github.com/krizaka/krizaka-ui/discussions`, label: \"Discuss\", icon: glyph(\"M4 5h16v11H9l-5 4V5Z\") },\n { href: `${repo}/issues/new?template=idea.yml`, label: \"Propose\", icon: glyph(\"M12 3v4M5 12H3m18 0h-2M8 17h8m-6 4h4M12 7a5 5 0 0 0-3 9h6a5 5 0 0 0-3-9Z\") },\n { href: `${repo}/issues/new?template=bug.yml`, label: \"Report a problem\", icon: glyph(\"M12 4 2.5 20h19L12 4Zm0 6v4m0 3v.1\") },\n ]}\n />\n );\n}\n"
|
|
131
131
|
}
|
|
132
132
|
]
|
|
133
133
|
},
|
|
@@ -12,7 +12,7 @@ function ActionBarIcons() {
|
|
|
12
12
|
variant: "icons",
|
|
13
13
|
label: "Talk about Krizaka Java",
|
|
14
14
|
actions: [
|
|
15
|
-
{ href:
|
|
15
|
+
{ href: `https://github.com/krizaka/krizaka-ui/discussions`, label: "Discuss", icon: glyph("M4 5h16v11H9l-5 4V5Z") },
|
|
16
16
|
{ href: `${repo}/issues/new?template=idea.yml`, label: "Propose", icon: glyph("M12 3v4M5 12H3m18 0h-2M8 17h8m-6 4h4M12 7a5 5 0 0 0-3 9h6a5 5 0 0 0-3-9Z") },
|
|
17
17
|
{ href: `${repo}/issues/new?template=bug.yml`, label: "Report a problem", icon: glyph("M12 4 2.5 20h19L12 4Zm0 6v4m0 3v.1") }
|
|
18
18
|
]
|
|
@@ -14,7 +14,7 @@ export default function ActionBarIcons() {
|
|
|
14
14
|
variant="icons"
|
|
15
15
|
label="Talk about Krizaka Java"
|
|
16
16
|
actions={[
|
|
17
|
-
{ href:
|
|
17
|
+
{ href: `https://github.com/krizaka/krizaka-ui/discussions`, label: "Discuss", icon: glyph("M4 5h16v11H9l-5 4V5Z") },
|
|
18
18
|
{ href: `${repo}/issues/new?template=idea.yml`, label: "Propose", icon: glyph("M12 3v4M5 12H3m18 0h-2M8 17h8m-6 4h4M12 7a5 5 0 0 0-3 9h6a5 5 0 0 0-3-9Z") },
|
|
19
19
|
{ href: `${repo}/issues/new?template=bug.yml`, label: "Report a problem", icon: glyph("M12 4 2.5 20h19L12 4Zm0 6v4m0 3v.1") },
|
|
20
20
|
]}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Progress } from "@krizaka/ui/progress";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
function ProgressBarSizes() {
|
|
7
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
7
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex w-64 max-w-full flex-col gap-4", children: [
|
|
8
8
|
/* @__PURE__ */ jsx(Progress, { label: "Small", size: "sm", value: 30 }),
|
|
9
9
|
/* @__PURE__ */ jsx(Progress, { label: "Medium", size: "md", value: 55 }),
|
|
10
10
|
/* @__PURE__ */ jsx(Progress, { label: "Large", size: "lg", value: 80 })
|
|
@@ -2,7 +2,7 @@ import { Progress } from "@krizaka/ui/progress";
|
|
|
2
2
|
|
|
3
3
|
export default function ProgressBarSizes() {
|
|
4
4
|
return (
|
|
5
|
-
<div className="flex flex-col gap-4">
|
|
5
|
+
<div className="flex w-64 max-w-full flex-col gap-4">
|
|
6
6
|
<Progress label="Small" size="sm" value={30} />
|
|
7
7
|
<Progress label="Medium" size="md" value={55} />
|
|
8
8
|
<Progress label="Large" size="lg" value={80} />
|
package/registry/flock.json
CHANGED
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"One flock per page, under the words and never behind them: the birds are the scene, not the message.",
|
|
44
44
|
"Give the stage the ratio of its backdrop (`ratio`) and the birds' places in percent: they keep their positions at any width.",
|
|
45
45
|
"Keep each bird to its job: the falcon watches, the owl reads, the duck builds, the flamingo answers, the pigeon delivers.",
|
|
46
|
+
"Make it the page's own: `active` is the bird of the product the reader is on (it looks up and jumps), `greet` has every bird jump once as the flock comes into view, `tempo` makes the moments more frequent.",
|
|
46
47
|
"Turn a moment off with `glance={false}`, `courier={false}` or `react={false}` on a page that already moves."
|
|
47
48
|
],
|
|
48
49
|
"accessibility": {
|
|
@@ -74,6 +75,13 @@
|
|
|
74
75
|
"description": "The birds on the stage.",
|
|
75
76
|
"required": true
|
|
76
77
|
},
|
|
78
|
+
{
|
|
79
|
+
"name": "active",
|
|
80
|
+
"type": "\"falcon\" | \"owl\" | \"duck\" | \"flamingo\" | \"pigeon\" | \"lovebird\" | \"crane\" | \"magpie\" | \"beaver\" | null",
|
|
81
|
+
"default": "null",
|
|
82
|
+
"description": "A bird the page points at (a product the reader is on): it looks up and jumps once each time it changes.",
|
|
83
|
+
"required": false
|
|
84
|
+
},
|
|
77
85
|
{
|
|
78
86
|
"name": "courier",
|
|
79
87
|
"type": "boolean",
|
|
@@ -88,6 +96,13 @@
|
|
|
88
96
|
"description": "Two neighbours look at each other now and then. Default `true`.",
|
|
89
97
|
"required": false
|
|
90
98
|
},
|
|
99
|
+
{
|
|
100
|
+
"name": "greet",
|
|
101
|
+
"type": "boolean",
|
|
102
|
+
"default": "false",
|
|
103
|
+
"description": "The birds greet the reader once as the flock first comes into view: each jumps in turn, left to right.",
|
|
104
|
+
"required": false
|
|
105
|
+
},
|
|
91
106
|
{
|
|
92
107
|
"name": "random",
|
|
93
108
|
"type": "(() => number)",
|
|
@@ -109,6 +124,13 @@
|
|
|
109
124
|
"description": "A bird looks up when the pointer comes near, and jumps a little when tapped. Default `true`.",
|
|
110
125
|
"required": false
|
|
111
126
|
},
|
|
127
|
+
{
|
|
128
|
+
"name": "tempo",
|
|
129
|
+
"type": "number",
|
|
130
|
+
"default": "1",
|
|
131
|
+
"description": "How lively the moments are: 1 is calm (default), 2 doubles how often neighbours glance and the pigeon delivers.",
|
|
132
|
+
"required": false
|
|
133
|
+
},
|
|
112
134
|
{
|
|
113
135
|
"name": "tone",
|
|
114
136
|
"type": "\"brand\" | \"own\"",
|
|
@@ -142,7 +164,7 @@
|
|
|
142
164
|
"files": [
|
|
143
165
|
{
|
|
144
166
|
"path": "flock/flock.tsx",
|
|
145
|
-
"content": "\"use client\";\n\n// The flock, alive: birds standing on a stage, each at ease in character (Mascot `life`), and the moments between\n// them — two birds glance at each other, the pigeon carries its parcel across, a bird looks up when you come near or tap\n// it. Transform and opacity only: CSS for each bird's own life, a timer for the moments (no frame loop), and nothing at\n// all when the flock is off screen, the tab is hidden or the person asked for less motion.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport type { MascotBird, MascotLook } from \"../mascots\";\nimport { Mascot } from \"../mascots\";\n\nexport interface FlockBird {\n /** Which bird. */\n bird: MascotBird;\n /** Where its feet are, across the stage: 0 to 100 (% of the stage's width). */\n x: number;\n /** Where its feet are, down the stage: 0 to 100 (% of the stage's height). */\n y: number;\n /** How wide it is: % of the stage's width. */\n size: number;\n}\n\nexport interface FlockMoments {\n /** Two neighbours look at each other now and then. Default `true`. */\n glance?: boolean;\n /** The pigeon, when the flock has one, carries its parcel across to another bird and comes back. Default `true`. */\n courier?: boolean;\n /** A bird looks up when the pointer comes near, and jumps a little when tapped. Default `true`. */\n react?: boolean;\n}\n\nexport type FlockProps = Omit<React.ComponentProps<\"div\">, \"children\"> &\n FlockMoments & {\n /** The birds on the stage. */\n birds: readonly FlockBird[];\n /** The stage's shape, width over height (default 16 / 9). It keeps its ratio at any width, so the birds keep their places. */\n ratio?: number;\n /** The birds' colours: the host's accent (`brand`, default) or the Krizaka story's (`own`). */\n tone?: \"brand\" | \"own\";\n /** The source of chance (default `Math.random`): give it a fixed one in a test. */\n random?: () => number;\n };\n\n// @fixed: the rhythm of the moments — how long a glance lasts, how often one happens, how long a delivery takes. A\n// choreography's pace, not a business parameter.\nconst GLANCE_HOLD_MS = [1500, 2400] as const;\nconst GLANCE_EVERY_MS = [4200, 9000] as const;\nconst COURIER_FIRST_MS = 9000;\nconst COURIER_EVERY_MS = [26000, 38000] as const;\nconst FLIGHT_MS = 2600;\nconst HAND_OVER_MS = 1300;\nconst LOOK_BACK_MS = 1600;\n\nconst between = (random: () => number, [lo, hi]: readonly [number, number]) => lo + random() * (hi - lo);\n\n/** `true` while the page may move: no reduced-motion request, the tab visible. */\nfunction useMayMove(): boolean {\n const [may, setMay] = React.useState(false);\n React.useEffect(() => {\n const query = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n const update = () => setMay(!query.matches && document.visibilityState !== \"hidden\");\n update();\n query.addEventListener(\"change\", update);\n document.addEventListener(\"visibilitychange\", update);\n return () => {\n query.removeEventListener(\"change\", update);\n document.removeEventListener(\"visibilitychange\", update);\n };\n }, []);\n return may;\n}\n\n/** `true` while the element is on screen. */\nfunction useOnScreen(ref: React.RefObject<Element | null>): boolean {\n const [on, setOn] = React.useState(false);\n React.useEffect(() => {\n const el = ref.current;\n if (!el || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(([entry]) => setOn(Boolean(entry?.isIntersecting)), { threshold: 0.1 });\n observer.observe(el);\n return () => observer.disconnect();\n }, [ref]);\n return on;\n}\n\n/**\n * The flock on its stage, alive. Decorative (`aria-hidden`): put the words beside it. Each bird idles in character; every\n * few seconds two neighbours glance at each other; every half minute the pigeon carries its parcel to another bird and\n * returns; a bird looks up when you come near. All of it pauses off screen and in a hidden tab, and the flock is still\n * under `prefers-reduced-motion`.\n */\nexport function Flock({ birds, ratio = 16 / 9, tone = \"brand\", glance = true, courier = true, react = true, random = Math.random, className, style, ...props }: FlockProps) {\n const stage = React.useRef<HTMLDivElement>(null);\n const bodies = React.useRef<(HTMLDivElement | null)[]>([]);\n const may = useMayMove();\n const onScreen = useOnScreen(stage);\n const running = may && onScreen;\n const [looks, setLooks] = React.useState<Record<number, MascotLook>>({});\n const [flying, setFlying] = React.useState<{ index: number; bank: number } | null>(null);\n const [poked, setPoked] = React.useState<Record<number, boolean>>({});\n\n // Left to right: a bird's neighbours are the birds beside it on the stage.\n const order = React.useMemo(() => birds.map((_, i) => i).sort((a, b) => birds[a]!.x - birds[b]!.x), [birds]);\n const look = (index: number, to: MascotLook) => setLooks((l) => (l[index] === to ? l : { ...l, [index]: to }));\n const rest = (...indexes: number[]) => setLooks((l) => Object.fromEntries(Object.entries(l).filter(([k]) => !indexes.includes(Number(k)))));\n\n // Glances: two neighbours turn towards each other, hold, and look away.\n React.useEffect(() => {\n if (!running || !glance || order.length < 2) return;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let back: ReturnType<typeof setTimeout> | undefined;\n const next = () => {\n timer = setTimeout(() => {\n const at = Math.floor(random() * (order.length - 1));\n const [a, b] = [order[at]!, order[at + 1]!];\n look(a, \"right\");\n look(b, \"left\");\n back = setTimeout(() => {\n rest(a, b);\n next();\n }, between(random, GLANCE_HOLD_MS));\n }, between(random, GLANCE_EVERY_MS));\n };\n next();\n return () => {\n clearTimeout(timer);\n clearTimeout(back);\n };\n }, [running, glance, order, random]);\n\n // The delivery: the pigeon takes its parcel across to the farthest bird, hands it over, and flies back.\n React.useEffect(() => {\n const pigeon = birds.findIndex((b) => b.bird === \"pigeon\");\n if (!running || !courier || pigeon < 0 || birds.length < 2) return;\n let alive = true;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let animation: Animation | undefined;\n const wait = (ms: number) => new Promise<void>((resolve) => (timer = setTimeout(resolve, ms)));\n const trip = async () => {\n const el = bodies.current[pigeon];\n const box = stage.current?.getBoundingClientRect();\n if (!el || !box || typeof el.animate !== \"function\") return;\n // The farthest bird from the pigeon, so the parcel crosses the whole stage.\n const from = birds[pigeon]!;\n const target = birds.reduce((far, b, i) => (i !== pigeon && Math.abs(b.x - from.x) > Math.abs(birds[far]!.x - from.x) ? i : far), pigeon === 0 ? 1 : 0);\n const to = birds[target]!;\n const side = to.x > from.x ? 1 : -1;\n // It lands beside the receiver, not on it.\n const dx = ((to.x - from.x) / 100) * box.width - side * (to.size / 100) * box.width * 0.55;\n const lift = (from.size / 100) * box.width * 0.35;\n const fly = async (fromX: number, toX: number) => {\n setFlying({ index: pigeon, bank: Math.sign(toX - fromX) });\n animation = el.animate(\n [\n { transform: `translate(${fromX}px, 0)`, easing: \"ease-out\" },\n { transform: `translate(${(fromX + toX) / 2}px, ${-lift}px)`, easing: \"ease-in\", offset: 0.5 },\n { transform: `translate(${toX}px, 0)` },\n ],\n { duration: FLIGHT_MS, fill: \"forwards\" },\n );\n await animation.finished.catch(() => undefined);\n setFlying(null);\n };\n look(target, side > 0 ? \"left\" : \"right\");\n await fly(0, dx);\n if (!alive) return;\n // The hand-over: the receiver looks at the pigeon, the pigeon hops on the doorstep.\n setPoked((p) => ({ ...p, [pigeon]: true }));\n await wait(HAND_OVER_MS);\n if (!alive) return;\n setPoked((p) => ({ ...p, [pigeon]: false }));\n look(target, \"up\");\n await fly(dx, 0);\n animation?.cancel();\n rest(target);\n };\n const schedule = (ms: number) => {\n timer = setTimeout(async () => {\n await trip();\n if (alive) schedule(between(random, COURIER_EVERY_MS));\n }, ms);\n };\n schedule(COURIER_FIRST_MS);\n return () => {\n alive = false;\n clearTimeout(timer);\n animation?.cancel();\n setFlying(null);\n };\n }, [running, courier, birds, random]);\n\n // A bird looks up when you come near, and jumps when you tap it.\n const hover = (index: number, event: React.PointerEvent) => {\n if (!running || !react || event.pointerType !== \"mouse\") return;\n look(index, \"up\");\n };\n const leave = (index: number) => {\n if (!react) return;\n rest(index);\n };\n const tap = (index: number) => {\n if (!running || !react) return;\n look(index, \"up\");\n setPoked((p) => ({ ...p, [index]: true }));\n setTimeout(() => {\n setPoked((p) => ({ ...p, [index]: false }));\n rest(index);\n }, LOOK_BACK_MS);\n };\n\n return (\n <div\n ref={stage}\n aria-hidden\n data-flock=\"\"\n data-running={running ? \"\" : undefined}\n className={cn(\"relative w-full\", className)}\n style={{ aspectRatio: String(ratio), containerType: \"inline-size\", ...style }}\n {...props}\n >\n {birds.map((b, i) => (\n <div\n key={`${b.bird}-${i}`}\n ref={(el) => {\n bodies.current[i] = el;\n }}\n data-flock-bird={b.bird}\n className=\"absolute touch-manipulation\"\n style={{ left: `${b.x - b.size / 2}%`, width: `${b.size}%`, top: `calc(${b.y}% - ${b.size * 0.9}cqw)`, aspectRatio: \"1\", pointerEvents: react ? \"auto\" : \"none\" }}\n onPointerEnter={(event) => hover(i, event)}\n onPointerLeave={() => leave(i)}\n onPointerDown={() => tap(i)}\n >\n <Mascot\n bird={b.bird}\n tone={tone}\n life\n look={looks[i]}\n flying={flying?.index === i}\n bank={flying?.index === i ? flying.bank : 0}\n poked={poked[i]}\n size={120}\n style={{ width: \"100%\", height: \"100%\", display: \"block\", overflow: \"visible\" }}\n />\n </div>\n ))}\n </div>\n );\n}\n"
|
|
167
|
+
"content": "\"use client\";\n\n// The flock, alive: birds standing on a stage, each at ease in character (Mascot `life`), and the moments between\n// them — two birds glance at each other, the pigeon carries its parcel across, a bird looks up when you come near or tap\n// it. Transform and opacity only: CSS for each bird's own life, a timer for the moments (no frame loop), and nothing at\n// all when the flock is off screen, the tab is hidden or the person asked for less motion.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport type { MascotBird, MascotLook } from \"../mascots\";\nimport { Mascot } from \"../mascots\";\n\nexport interface FlockBird {\n /** Which bird. */\n bird: MascotBird;\n /** Where its feet are, across the stage: 0 to 100 (% of the stage's width). */\n x: number;\n /** Where its feet are, down the stage: 0 to 100 (% of the stage's height). */\n y: number;\n /** How wide it is: % of the stage's width. */\n size: number;\n}\n\nexport interface FlockMoments {\n /** Two neighbours look at each other now and then. Default `true`. */\n glance?: boolean;\n /** The pigeon, when the flock has one, carries its parcel across to another bird and comes back. Default `true`. */\n courier?: boolean;\n /** A bird looks up when the pointer comes near, and jumps a little when tapped. Default `true`. */\n react?: boolean;\n}\n\nexport type FlockProps = Omit<React.ComponentProps<\"div\">, \"children\"> &\n FlockMoments & {\n /** The birds on the stage. */\n birds: readonly FlockBird[];\n /** The stage's shape, width over height (default 16 / 9). It keeps its ratio at any width, so the birds keep their places. */\n ratio?: number;\n /** The birds' colours: the host's accent (`brand`, default) or the Krizaka story's (`own`). */\n tone?: \"brand\" | \"own\";\n /** The source of chance (default `Math.random`): give it a fixed one in a test. */\n random?: () => number;\n /** How lively the moments are: 1 is calm (default), 2 doubles how often neighbours glance and the pigeon delivers. */\n tempo?: number;\n /** A bird the page points at (a product the reader is on): it looks up and jumps once each time it changes. */\n active?: MascotBird | null;\n /** The birds greet the reader once as the flock first comes into view: each jumps in turn, left to right. */\n greet?: boolean;\n };\n\n// @fixed: the rhythm of the moments — how long a glance lasts, how often one happens, how long a delivery takes. A\n// choreography's pace, not a business parameter.\nconst GLANCE_HOLD_MS = [1500, 2400] as const;\nconst GLANCE_EVERY_MS = [4200, 9000] as const;\nconst COURIER_FIRST_MS = 9000;\nconst COURIER_EVERY_MS = [26000, 38000] as const;\nconst FLIGHT_MS = 2600;\nconst HAND_OVER_MS = 1300;\nconst LOOK_BACK_MS = 1600;\n\nconst between = (random: () => number, [lo, hi]: readonly [number, number], tempo = 1) => (lo + random() * (hi - lo)) / tempo;\nconst GREET_STEP_MS = 110;\nconst POKE_MS = 700;\n\n/** `true` while the page may move: no reduced-motion request, the tab visible. */\nfunction useMayMove(): boolean {\n const [may, setMay] = React.useState(false);\n React.useEffect(() => {\n const query = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n const update = () => setMay(!query.matches && document.visibilityState !== \"hidden\");\n update();\n query.addEventListener(\"change\", update);\n document.addEventListener(\"visibilitychange\", update);\n return () => {\n query.removeEventListener(\"change\", update);\n document.removeEventListener(\"visibilitychange\", update);\n };\n }, []);\n return may;\n}\n\n/** `true` while the element is on screen. */\nfunction useOnScreen(ref: React.RefObject<Element | null>): boolean {\n const [on, setOn] = React.useState(false);\n React.useEffect(() => {\n const el = ref.current;\n if (!el || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(([entry]) => setOn(Boolean(entry?.isIntersecting)), { threshold: 0.1 });\n observer.observe(el);\n return () => observer.disconnect();\n }, [ref]);\n return on;\n}\n\n/**\n * The flock on its stage, alive. Decorative (`aria-hidden`): put the words beside it. Each bird idles in character; every\n * few seconds two neighbours glance at each other; every half minute the pigeon carries its parcel to another bird and\n * returns; a bird looks up when you come near. All of it pauses off screen and in a hidden tab, and the flock is still\n * under `prefers-reduced-motion`.\n */\nexport function Flock({ birds, ratio = 16 / 9, tone = \"brand\", glance = true, courier = true, react = true, random = Math.random, tempo = 1, active = null, greet = false, className, style, ...props }: FlockProps) {\n const stage = React.useRef<HTMLDivElement>(null);\n const bodies = React.useRef<(HTMLDivElement | null)[]>([]);\n const may = useMayMove();\n const onScreen = useOnScreen(stage);\n const running = may && onScreen;\n const [looks, setLooks] = React.useState<Record<number, MascotLook>>({});\n const [flying, setFlying] = React.useState<{ index: number; bank: number } | null>(null);\n const [poked, setPoked] = React.useState<Record<number, boolean>>({});\n\n // Left to right: a bird's neighbours are the birds beside it on the stage.\n const order = React.useMemo(() => birds.map((_, i) => i).sort((a, b) => birds[a]!.x - birds[b]!.x), [birds]);\n const look = (index: number, to: MascotLook) => setLooks((l) => (l[index] === to ? l : { ...l, [index]: to }));\n const rest = (...indexes: number[]) => setLooks((l) => Object.fromEntries(Object.entries(l).filter(([k]) => !indexes.includes(Number(k)))));\n\n // Glances: two neighbours turn towards each other, hold, and look away.\n React.useEffect(() => {\n if (!running || !glance || order.length < 2) return;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let back: ReturnType<typeof setTimeout> | undefined;\n const next = () => {\n timer = setTimeout(() => {\n const at = Math.floor(random() * (order.length - 1));\n const [a, b] = [order[at]!, order[at + 1]!];\n look(a, \"right\");\n look(b, \"left\");\n back = setTimeout(() => {\n rest(a, b);\n next();\n }, between(random, GLANCE_HOLD_MS));\n }, between(random, GLANCE_EVERY_MS, tempo));\n };\n next();\n return () => {\n clearTimeout(timer);\n clearTimeout(back);\n };\n }, [running, glance, order, random, tempo]);\n\n // The delivery: the pigeon takes its parcel across to the farthest bird, hands it over, and flies back.\n React.useEffect(() => {\n const pigeon = birds.findIndex((b) => b.bird === \"pigeon\");\n if (!running || !courier || pigeon < 0 || birds.length < 2) return;\n let alive = true;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let animation: Animation | undefined;\n const wait = (ms: number) => new Promise<void>((resolve) => (timer = setTimeout(resolve, ms)));\n const trip = async () => {\n const el = bodies.current[pigeon];\n const box = stage.current?.getBoundingClientRect();\n if (!el || !box || typeof el.animate !== \"function\") return;\n // The farthest bird from the pigeon, so the parcel crosses the whole stage.\n const from = birds[pigeon]!;\n const target = birds.reduce((far, b, i) => (i !== pigeon && Math.abs(b.x - from.x) > Math.abs(birds[far]!.x - from.x) ? i : far), pigeon === 0 ? 1 : 0);\n const to = birds[target]!;\n const side = to.x > from.x ? 1 : -1;\n // It lands beside the receiver, not on it.\n const dx = ((to.x - from.x) / 100) * box.width - side * (to.size / 100) * box.width * 0.55;\n const lift = (from.size / 100) * box.width * 0.35;\n const fly = async (fromX: number, toX: number) => {\n setFlying({ index: pigeon, bank: Math.sign(toX - fromX) });\n animation = el.animate(\n [\n { transform: `translate(${fromX}px, 0)`, easing: \"ease-out\" },\n { transform: `translate(${(fromX + toX) / 2}px, ${-lift}px)`, easing: \"ease-in\", offset: 0.5 },\n { transform: `translate(${toX}px, 0)` },\n ],\n { duration: FLIGHT_MS, fill: \"forwards\" },\n );\n await animation.finished.catch(() => undefined);\n setFlying(null);\n };\n look(target, side > 0 ? \"left\" : \"right\");\n await fly(0, dx);\n if (!alive) return;\n // The hand-over: the receiver looks at the pigeon, the pigeon hops on the doorstep.\n setPoked((p) => ({ ...p, [pigeon]: true }));\n await wait(HAND_OVER_MS);\n if (!alive) return;\n setPoked((p) => ({ ...p, [pigeon]: false }));\n look(target, \"up\");\n await fly(dx, 0);\n animation?.cancel();\n rest(target);\n };\n const schedule = (ms: number) => {\n timer = setTimeout(async () => {\n await trip();\n if (alive) schedule(between(random, COURIER_EVERY_MS, tempo));\n }, ms);\n };\n schedule(COURIER_FIRST_MS / tempo);\n return () => {\n alive = false;\n clearTimeout(timer);\n animation?.cancel();\n setFlying(null);\n };\n }, [running, courier, birds, random, tempo]);\n\n // The greeting: the first time the flock is on screen, each bird jumps in turn.\n const greeted = React.useRef(false);\n React.useEffect(() => {\n if (!running || !greet || greeted.current) return;\n greeted.current = true;\n const timers: ReturnType<typeof setTimeout>[] = [];\n order.forEach((index, n) => {\n timers.push(setTimeout(() => setPoked((p) => ({ ...p, [index]: true })), n * GREET_STEP_MS));\n timers.push(setTimeout(() => setPoked((p) => ({ ...p, [index]: false })), n * GREET_STEP_MS + POKE_MS));\n });\n return () => timers.forEach(clearTimeout);\n }, [running, greet, order]);\n\n // The bird the page points at looks up and jumps.\n React.useEffect(() => {\n const index = active ? birds.findIndex((b) => b.bird === active) : -1;\n if (!running || index < 0) return;\n const start = setTimeout(() => {\n look(index, \"up\");\n setPoked((p) => ({ ...p, [index]: true }));\n }, 0);\n const t = setTimeout(() => setPoked((p) => ({ ...p, [index]: false })), POKE_MS);\n return () => {\n clearTimeout(start);\n clearTimeout(t);\n setPoked((p) => ({ ...p, [index]: false }));\n rest(index);\n };\n }, [active, running, birds]);\n\n // A bird looks up when you come near, and jumps when you tap it.\n const hover = (index: number, event: React.PointerEvent) => {\n if (!running || !react || event.pointerType !== \"mouse\") return;\n look(index, \"up\");\n };\n const leave = (index: number) => {\n if (!react) return;\n rest(index);\n };\n const tap = (index: number) => {\n if (!running || !react) return;\n look(index, \"up\");\n setPoked((p) => ({ ...p, [index]: true }));\n setTimeout(() => {\n setPoked((p) => ({ ...p, [index]: false }));\n rest(index);\n }, LOOK_BACK_MS);\n };\n\n return (\n <div\n ref={stage}\n aria-hidden\n data-flock=\"\"\n data-running={running ? \"\" : undefined}\n className={cn(\"relative w-full\", className)}\n style={{ aspectRatio: String(ratio), containerType: \"inline-size\", ...style }}\n {...props}\n >\n {birds.map((b, i) => (\n <div\n key={`${b.bird}-${i}`}\n ref={(el) => {\n bodies.current[i] = el;\n }}\n data-flock-bird={b.bird}\n className=\"absolute touch-manipulation\"\n style={{ left: `${b.x - b.size / 2}%`, width: `${b.size}%`, top: `calc(${b.y}% - ${b.size * 0.9}cqw)`, aspectRatio: \"1\", pointerEvents: react ? \"auto\" : \"none\" }}\n onPointerEnter={(event) => hover(i, event)}\n onPointerLeave={() => leave(i)}\n onPointerDown={() => tap(i)}\n >\n <Mascot\n bird={b.bird}\n tone={tone}\n life\n look={looks[i]}\n flying={flying?.index === i}\n bank={flying?.index === i ? flying.bank : 0}\n poked={poked[i]}\n size={120}\n style={{ width: \"100%\", height: \"100%\", display: \"block\", overflow: \"visible\" }}\n />\n </div>\n ))}\n </div>\n );\n}\n"
|
|
146
168
|
},
|
|
147
169
|
{
|
|
148
170
|
"path": "flock/index.ts",
|
|
@@ -150,7 +172,7 @@
|
|
|
150
172
|
},
|
|
151
173
|
{
|
|
152
174
|
"path": "flock/meta.ts",
|
|
153
|
-
"content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Flock\",\n summary:\n \"The Krizaka birds standing on a stage, alive: each at ease in character, two neighbours glancing at each other, the pigeon carrying its parcel across, a bird looking up when you come near or tap it.\",\n status: \"beta\",\n category: \"blocks\",\n platforms: \"web\",\n parity: {\n native: { status: \"n/a\", reason: \"A decorative stage for a public page's first screen; an app screen speaks with MascotState.\", use: \"mascot-state\" },\n desktop: \"web\",\n },\n whenToUse: [\n \"On a company's home, where the flock is the scene: five birds on a ridge, under the words.\",\n \"On an about or story page, where the birds are the subject and a still drawing would feel dead.\",\n ],\n whenNotToUse: [\n { when: \"A bird that answers a state (a wait, an error, an empty list).\", use: \"mascot-state\" },\n { when: \"One bird at ease inside a card or beside a rule.\", use: \"mascots\" },\n { when: \"The flock as a still plate with its promises.\", use: \"bird-plate\" },\n ],\n bestPractices: [\n \"One flock per page, under the words and never behind them: the birds are the scene, not the message.\",\n \"Give the stage the ratio of its backdrop (`ratio`) and the birds' places in percent: they keep their positions at any width.\",\n \"Keep each bird to its job: the falcon watches, the owl reads, the duck builds, the flamingo answers, the pigeon delivers.\",\n \"Turn a moment off with `glance={false}`, `courier={false}` or `react={false}` on a page that already moves.\",\n ],\n accessibility: {\n keyboard: [],\n notes: [\n \"Decorative (`aria-hidden`): it never carries information; put the words beside it.\",\n \"Under `prefers-reduced-motion` the birds are still: no idle motion, no glance, no delivery, no reaction.\",\n \"The moments pause while the flock is off screen and while the tab is hidden; there is no frame loop.\",\n ],\n },\n related: [\"mascots\", \"mascot-state\", \"bird-plate\", \"landscape\"],\n web: {\n imports: [\"Flock\"],\n examples: [\n { name: \"ridge\", title: \"The flock on its ridge\", description: \"Five birds across a stage, each at ease; neighbours glance, the pigeon delivers.\" },\n { name: \"pair\", title: \"Two birds\", description: \"The owl and the duck: they look at each other now and then.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
|
|
175
|
+
"content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Flock\",\n summary:\n \"The Krizaka birds standing on a stage, alive: each at ease in character, two neighbours glancing at each other, the pigeon carrying its parcel across, a bird looking up when you come near or tap it.\",\n status: \"beta\",\n category: \"blocks\",\n platforms: \"web\",\n parity: {\n native: { status: \"n/a\", reason: \"A decorative stage for a public page's first screen; an app screen speaks with MascotState.\", use: \"mascot-state\" },\n desktop: \"web\",\n },\n whenToUse: [\n \"On a company's home, where the flock is the scene: five birds on a ridge, under the words.\",\n \"On an about or story page, where the birds are the subject and a still drawing would feel dead.\",\n ],\n whenNotToUse: [\n { when: \"A bird that answers a state (a wait, an error, an empty list).\", use: \"mascot-state\" },\n { when: \"One bird at ease inside a card or beside a rule.\", use: \"mascots\" },\n { when: \"The flock as a still plate with its promises.\", use: \"bird-plate\" },\n ],\n bestPractices: [\n \"One flock per page, under the words and never behind them: the birds are the scene, not the message.\",\n \"Give the stage the ratio of its backdrop (`ratio`) and the birds' places in percent: they keep their positions at any width.\",\n \"Keep each bird to its job: the falcon watches, the owl reads, the duck builds, the flamingo answers, the pigeon delivers.\",\n \"Make it the page's own: `active` is the bird of the product the reader is on (it looks up and jumps), `greet` has every bird jump once as the flock comes into view, `tempo` makes the moments more frequent.\",\n \"Turn a moment off with `glance={false}`, `courier={false}` or `react={false}` on a page that already moves.\",\n ],\n accessibility: {\n keyboard: [],\n notes: [\n \"Decorative (`aria-hidden`): it never carries information; put the words beside it.\",\n \"Under `prefers-reduced-motion` the birds are still: no idle motion, no glance, no delivery, no reaction.\",\n \"The moments pause while the flock is off screen and while the tab is hidden; there is no frame loop.\",\n ],\n },\n related: [\"mascots\", \"mascot-state\", \"bird-plate\", \"landscape\"],\n web: {\n imports: [\"Flock\"],\n examples: [\n { name: \"ridge\", title: \"The flock on its ridge\", description: \"Five birds across a stage, each at ease; neighbours glance, the pigeon delivers.\" },\n { name: \"pair\", title: \"Two birds\", description: \"The owl and the duck: they look at each other now and then.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
|
|
154
176
|
},
|
|
155
177
|
{
|
|
156
178
|
"path": "meta.ts",
|
|
@@ -178,6 +200,13 @@
|
|
|
178
200
|
"description": "The birds on the stage.",
|
|
179
201
|
"required": true
|
|
180
202
|
},
|
|
203
|
+
{
|
|
204
|
+
"name": "active",
|
|
205
|
+
"type": "\"falcon\" | \"owl\" | \"duck\" | \"flamingo\" | \"pigeon\" | \"lovebird\" | \"crane\" | \"magpie\" | \"beaver\" | null",
|
|
206
|
+
"default": "null",
|
|
207
|
+
"description": "A bird the page points at (a product the reader is on): it looks up and jumps once each time it changes.",
|
|
208
|
+
"required": false
|
|
209
|
+
},
|
|
181
210
|
{
|
|
182
211
|
"name": "courier",
|
|
183
212
|
"type": "boolean",
|
|
@@ -192,6 +221,13 @@
|
|
|
192
221
|
"description": "Two neighbours look at each other now and then. Default `true`.",
|
|
193
222
|
"required": false
|
|
194
223
|
},
|
|
224
|
+
{
|
|
225
|
+
"name": "greet",
|
|
226
|
+
"type": "boolean",
|
|
227
|
+
"default": "false",
|
|
228
|
+
"description": "The birds greet the reader once as the flock first comes into view: each jumps in turn, left to right.",
|
|
229
|
+
"required": false
|
|
230
|
+
},
|
|
195
231
|
{
|
|
196
232
|
"name": "random",
|
|
197
233
|
"type": "(() => number)",
|
|
@@ -213,6 +249,13 @@
|
|
|
213
249
|
"description": "A bird looks up when the pointer comes near, and jumps a little when tapped. Default `true`.",
|
|
214
250
|
"required": false
|
|
215
251
|
},
|
|
252
|
+
{
|
|
253
|
+
"name": "tempo",
|
|
254
|
+
"type": "number",
|
|
255
|
+
"default": "1",
|
|
256
|
+
"description": "How lively the moments are: 1 is calm (default), 2 doubles how often neighbours glance and the pigeon delivers.",
|
|
257
|
+
"required": false
|
|
258
|
+
},
|
|
216
259
|
{
|
|
217
260
|
"name": "tone",
|
|
218
261
|
"type": "\"brand\" | \"own\"",
|
package/registry/index.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krizaka/ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.41.0",
|
|
4
4
|
"templatePurposes": {
|
|
5
5
|
"convince": {
|
|
6
6
|
"title": "Convince",
|
|
@@ -3142,7 +3142,7 @@
|
|
|
3142
3142
|
"native": 2
|
|
3143
3143
|
},
|
|
3144
3144
|
"dependencies": [
|
|
3145
|
-
"@krizaka/tokens@^3.
|
|
3145
|
+
"@krizaka/tokens@^3.41.0",
|
|
3146
3146
|
"@protomaps/basemaps@5.7.2",
|
|
3147
3147
|
"tailwind-variants@^3.3.1"
|
|
3148
3148
|
],
|
|
@@ -3581,7 +3581,7 @@
|
|
|
3581
3581
|
"native": 0
|
|
3582
3582
|
},
|
|
3583
3583
|
"dependencies": [
|
|
3584
|
-
"@krizaka/tokens@^3.
|
|
3584
|
+
"@krizaka/tokens@^3.41.0",
|
|
3585
3585
|
"radix-ui@^1.7.0",
|
|
3586
3586
|
"tailwind-variants@^3.3.1"
|
|
3587
3587
|
],
|
|
@@ -4244,7 +4244,7 @@
|
|
|
4244
4244
|
"native": 0
|
|
4245
4245
|
},
|
|
4246
4246
|
"dependencies": [
|
|
4247
|
-
"@krizaka/tokens@^3.
|
|
4247
|
+
"@krizaka/tokens@^3.41.0",
|
|
4248
4248
|
"radix-ui@^1.7.0",
|
|
4249
4249
|
"tailwind-variants@^3.3.1"
|
|
4250
4250
|
],
|
|
@@ -4587,7 +4587,7 @@
|
|
|
4587
4587
|
"native": 0
|
|
4588
4588
|
},
|
|
4589
4589
|
"dependencies": [
|
|
4590
|
-
"@krizaka/tokens@^3.
|
|
4590
|
+
"@krizaka/tokens@^3.41.0",
|
|
4591
4591
|
"radix-ui@^1.7.0",
|
|
4592
4592
|
"tailwind-variants@^3.3.1"
|
|
4593
4593
|
],
|
package/registry/map.json
CHANGED
package/registry/nav-bar.json
CHANGED
package/registry/progress.json
CHANGED
|
@@ -200,7 +200,7 @@
|
|
|
200
200
|
"description": "`sm`, `md`, `lg`.",
|
|
201
201
|
"path": "examples/progress/bar-sizes.tsx",
|
|
202
202
|
"module": "@krizaka/ui/registry/examples/progress/bar-sizes",
|
|
203
|
-
"code": "import { Progress } from \"@krizaka/ui/progress\";\n\nexport default function ProgressBarSizes() {\n return (\n <div className=\"flex flex-col gap-4\">\n <Progress label=\"Small\" size=\"sm\" value={30} />\n <Progress label=\"Medium\" size=\"md\" value={55} />\n <Progress label=\"Large\" size=\"lg\" value={80} />\n </div>\n );\n}\n"
|
|
203
|
+
"code": "import { Progress } from \"@krizaka/ui/progress\";\n\nexport default function ProgressBarSizes() {\n return (\n <div className=\"flex w-64 max-w-full flex-col gap-4\">\n <Progress label=\"Small\" size=\"sm\" value={30} />\n <Progress label=\"Medium\" size=\"md\" value={55} />\n <Progress label=\"Large\" size=\"lg\" value={80} />\n </div>\n );\n}\n"
|
|
204
204
|
},
|
|
205
205
|
{
|
|
206
206
|
"name": "bar-indeterminate",
|
package/registry/rail-nav.json
CHANGED