@almadar/ui 5.62.0 → 5.63.2

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.
Files changed (35) hide show
  1. package/dist/avl/index.cjs +526 -161
  2. package/dist/avl/index.js +526 -161
  3. package/dist/components/core/atoms/Box.d.ts +2 -0
  4. package/dist/components/core/molecules/CalendarGrid.d.ts +9 -1
  5. package/dist/components/core/molecules/PositionedCanvas.d.ts +14 -1
  6. package/dist/components/core/molecules/ReplyTree.d.ts +13 -1
  7. package/dist/components/game/molecules/three/index.cjs +64 -0
  8. package/dist/components/game/molecules/three/index.js +64 -0
  9. package/dist/components/game/organisms/hooks/useCamera.d.ts +16 -0
  10. package/dist/components/index.cjs +526 -162
  11. package/dist/components/index.js +526 -162
  12. package/dist/components/marketing/organisms/CaseStudyOrganism.d.ts +10 -1
  13. package/dist/components/marketing/organisms/FeatureGridOrganism.d.ts +10 -1
  14. package/dist/components/marketing/organisms/HeroOrganism.d.ts +24 -1
  15. package/dist/components/marketing/organisms/PricingOrganism.d.ts +14 -1
  16. package/dist/components/marketing/organisms/ShowcaseOrganism.d.ts +14 -1
  17. package/dist/components/marketing/organisms/StatsOrganism.d.ts +6 -1
  18. package/dist/components/marketing/organisms/StepFlowOrganism.d.ts +9 -1
  19. package/dist/components/marketing/organisms/TeamOrganism.d.ts +10 -1
  20. package/dist/docs/index.cjs +64 -0
  21. package/dist/docs/index.d.cts +2 -0
  22. package/dist/docs/index.js +64 -0
  23. package/dist/hooks/index.cjs +148 -0
  24. package/dist/hooks/index.d.ts +2 -0
  25. package/dist/hooks/index.js +147 -1
  26. package/dist/hooks/useCanvasGestures.d.ts +44 -0
  27. package/dist/hooks/useTapReveal.d.ts +32 -0
  28. package/dist/marketing/index.cjs +59 -0
  29. package/dist/marketing/index.d.cts +2 -0
  30. package/dist/marketing/index.js +59 -0
  31. package/dist/providers/index.cjs +526 -161
  32. package/dist/providers/index.js +526 -161
  33. package/dist/runtime/index.cjs +526 -161
  34. package/dist/runtime/index.js +526 -161
  35. package/package.json +2 -2
@@ -52,6 +52,8 @@ export interface BoxProps extends React.HTMLAttributes<HTMLDivElement> {
52
52
  actionPayload?: EventPayload;
53
53
  /** Declarative hover event — emits UI:{hoverEvent} with { hovered: true/false } on mouseEnter/mouseLeave */
54
54
  hoverEvent?: EventKey;
55
+ /** When true (default), a touch/pen tap also fires `hoverEvent` (toggling hovered) so hover-only reveals work on touch. */
56
+ tapReveal?: boolean;
55
57
  /** Maximum width (CSS value, e.g., "550px", "80rem") */
56
58
  maxWidth?: string;
57
59
  /** Children elements */
@@ -13,13 +13,21 @@ import type { EventEmit, EventPayload, EntityRow, EntityWith } from "@almadar/co
13
13
  * 7 on laptop+ (≥1025).
14
14
  */
15
15
  export type CalendarDayWindow = 1 | 3 | 7;
16
+ /** The per-event entity fields this grid reads. `startTime`/`endTime` are ISO
17
+ * strings; `color` is a Tailwind class string applied to the event chip. */
18
+ export interface CalendarEventRow {
19
+ title: string;
20
+ startTime: string;
21
+ endTime?: string;
22
+ color?: string;
23
+ }
16
24
  export interface CalendarGridProps {
17
25
  /** Start of the week (defaults to current week's Monday) */
18
26
  weekStart?: Date;
19
27
  /** Time slot labels (defaults to 09:00-17:00) */
20
28
  timeSlots?: string[];
21
29
  /** Events to display on the grid */
22
- events?: readonly EntityWith<'id' | 'title' | 'startTime'>[];
30
+ events?: readonly EntityWith<CalendarEventRow>[];
23
31
  /** Called when a time slot is clicked */
24
32
  onSlotClick?: (day: Date, time: string) => void;
25
33
  /** Called when a day header is clicked */
@@ -2,12 +2,25 @@ import React from "react";
2
2
  import type { EventEmit, EntityWith } from "@almadar/core";
3
3
  export type CanvasItemStatus = 'empty' | 'seated' | 'ordered' | 'awaiting-bill' | 'cleaning';
4
4
  export type CanvasItemShape = 'round' | 'rectangle' | 'square';
5
+ /** The per-item entity fields this canvas reads. `x`/`y` are pixel coordinates
6
+ * in the canvas plane; `status`/`shape` are string literal unions widened to
7
+ * `string` at the row level. */
8
+ export interface CanvasItemRow {
9
+ label: string;
10
+ x: number;
11
+ y: number;
12
+ capacity: number;
13
+ partySize?: number;
14
+ serverName?: string;
15
+ status?: CanvasItemStatus;
16
+ shape?: CanvasItemShape;
17
+ }
5
18
  export interface PositionedCanvasProps {
6
19
  /**
7
20
  * Items to render. The molecule narrows non-array values to `[]` and reads
8
21
  * each row's `id` / `x` / `y` / `label` fields at render time.
9
22
  */
10
- items: readonly EntityWith<'id' | 'label' | 'x' | 'y'>[];
23
+ items: readonly EntityWith<CanvasItemRow>[];
11
24
  width?: number;
12
25
  height?: number;
13
26
  selectedId?: string | null;
@@ -7,8 +7,20 @@
7
7
  import React from "react";
8
8
  import type { EventEmit, EntityWith } from "@almadar/core";
9
9
  import { type VoteValue } from "./VoteStack";
10
+ /** The per-node entity fields this tree reads. `replies` is the same row shape
11
+ * nested recursively; `userVote` carries the `VoteValue` literal union. */
12
+ export interface ReplyNodeRow {
13
+ authorName: string;
14
+ content: string;
15
+ authorAvatarUrl?: string;
16
+ postedAt?: string;
17
+ voteCount?: number;
18
+ userVote?: VoteValue;
19
+ collapsed?: boolean;
20
+ replies?: readonly EntityWith<ReplyNodeRow>[];
21
+ }
10
22
  export interface ReplyTreeProps {
11
- nodes: readonly EntityWith<'id' | 'authorName' | 'content'>[];
23
+ nodes: readonly EntityWith<ReplyNodeRow>[];
12
24
  maxDepth?: number;
13
25
  onVote?: (nodeId: string, vote: VoteValue) => void;
14
26
  onReply?: (parentNodeId: string) => void;
@@ -3101,6 +3101,9 @@ var GameCanvas3D = React11.forwardRef(
3101
3101
  target: cameraTarget,
3102
3102
  enableDamping: true,
3103
3103
  dampingFactor: 0.05,
3104
+ enableZoom: true,
3105
+ enablePan: true,
3106
+ touches: { ONE: THREE6__namespace.TOUCH.ROTATE, TWO: THREE6__namespace.TOUCH.DOLLY_PAN },
3104
3107
  minDistance: 2,
3105
3108
  maxDistance: 100,
3106
3109
  maxPolarAngle: Math.PI / 2 - 0.1
@@ -4438,6 +4441,51 @@ function GameBoard3D({
4438
4441
  ] });
4439
4442
  }
4440
4443
  GameBoard3D.displayName = "GameBoard3D";
4444
+ function useTapReveal(options = {}) {
4445
+ const { onReveal, onDismiss, refs, enabled = true } = options;
4446
+ const [revealed, setRevealed] = React11.useState(false);
4447
+ const onRevealRef = React11.useRef(onReveal);
4448
+ const onDismissRef = React11.useRef(onDismiss);
4449
+ onRevealRef.current = onReveal;
4450
+ onDismissRef.current = onDismiss;
4451
+ const reveal = React11.useCallback(() => {
4452
+ setRevealed((wasRevealed) => {
4453
+ if (!wasRevealed) onRevealRef.current?.();
4454
+ return true;
4455
+ });
4456
+ }, []);
4457
+ const dismiss = React11.useCallback(() => {
4458
+ setRevealed((wasRevealed) => {
4459
+ if (wasRevealed) onDismissRef.current?.();
4460
+ return false;
4461
+ });
4462
+ }, []);
4463
+ const onPointerDown = React11.useCallback(
4464
+ (e) => {
4465
+ if (!enabled || e.pointerType === "mouse") return;
4466
+ if (revealed) dismiss();
4467
+ else reveal();
4468
+ },
4469
+ [enabled, revealed, reveal, dismiss]
4470
+ );
4471
+ React11.useEffect(() => {
4472
+ if (!revealed || typeof document === "undefined") return;
4473
+ const onDocDown = (ev) => {
4474
+ const target = ev.target;
4475
+ if (!(target instanceof Node)) return;
4476
+ const inside = (refs ?? []).some((r) => r.current?.contains(target));
4477
+ if (!inside) dismiss();
4478
+ };
4479
+ const id = setTimeout(() => document.addEventListener("pointerdown", onDocDown), 0);
4480
+ return () => {
4481
+ clearTimeout(id);
4482
+ document.removeEventListener("pointerdown", onDocDown);
4483
+ };
4484
+ }, [revealed, refs, dismiss]);
4485
+ return { revealed, triggerProps: { onPointerDown }, reveal, dismiss };
4486
+ }
4487
+
4488
+ // components/core/atoms/Box.tsx
4441
4489
  var paddingStyles = {
4442
4490
  none: "p-0",
4443
4491
  xs: "p-1",
@@ -4563,10 +4611,12 @@ var Box = React11__default.default.forwardRef(
4563
4611
  action,
4564
4612
  actionPayload,
4565
4613
  hoverEvent,
4614
+ tapReveal = true,
4566
4615
  maxWidth,
4567
4616
  onClick,
4568
4617
  onMouseEnter,
4569
4618
  onMouseLeave,
4619
+ onPointerDown,
4570
4620
  ...rest
4571
4621
  }, ref) => {
4572
4622
  const eventBus = useEventBus();
@@ -4589,6 +4639,19 @@ var Box = React11__default.default.forwardRef(
4589
4639
  }
4590
4640
  onMouseLeave?.(e);
4591
4641
  }, [hoverEvent, eventBus, onMouseLeave]);
4642
+ const { triggerProps } = useTapReveal({
4643
+ enabled: tapReveal && !!hoverEvent,
4644
+ onReveal: React11.useCallback(() => {
4645
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
4646
+ }, [hoverEvent, eventBus]),
4647
+ onDismiss: React11.useCallback(() => {
4648
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
4649
+ }, [hoverEvent, eventBus])
4650
+ });
4651
+ const handlePointerDown = React11.useCallback((e) => {
4652
+ if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
4653
+ onPointerDown?.(e);
4654
+ }, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
4592
4655
  const isClickable = action || onClick;
4593
4656
  return React11__default.default.createElement(
4594
4657
  Component2,
@@ -4616,6 +4679,7 @@ var Box = React11__default.default.forwardRef(
4616
4679
  onClick: isClickable ? handleClick : void 0,
4617
4680
  onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
4618
4681
  onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
4682
+ onPointerDown: hoverEvent && tapReveal || onPointerDown ? handlePointerDown : void 0,
4619
4683
  style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
4620
4684
  ...rest
4621
4685
  },
@@ -3077,6 +3077,9 @@ var GameCanvas3D = forwardRef(
3077
3077
  target: cameraTarget,
3078
3078
  enableDamping: true,
3079
3079
  dampingFactor: 0.05,
3080
+ enableZoom: true,
3081
+ enablePan: true,
3082
+ touches: { ONE: THREE6.TOUCH.ROTATE, TWO: THREE6.TOUCH.DOLLY_PAN },
3080
3083
  minDistance: 2,
3081
3084
  maxDistance: 100,
3082
3085
  maxPolarAngle: Math.PI / 2 - 0.1
@@ -4414,6 +4417,51 @@ function GameBoard3D({
4414
4417
  ] });
4415
4418
  }
4416
4419
  GameBoard3D.displayName = "GameBoard3D";
4420
+ function useTapReveal(options = {}) {
4421
+ const { onReveal, onDismiss, refs, enabled = true } = options;
4422
+ const [revealed, setRevealed] = useState(false);
4423
+ const onRevealRef = useRef(onReveal);
4424
+ const onDismissRef = useRef(onDismiss);
4425
+ onRevealRef.current = onReveal;
4426
+ onDismissRef.current = onDismiss;
4427
+ const reveal = useCallback(() => {
4428
+ setRevealed((wasRevealed) => {
4429
+ if (!wasRevealed) onRevealRef.current?.();
4430
+ return true;
4431
+ });
4432
+ }, []);
4433
+ const dismiss = useCallback(() => {
4434
+ setRevealed((wasRevealed) => {
4435
+ if (wasRevealed) onDismissRef.current?.();
4436
+ return false;
4437
+ });
4438
+ }, []);
4439
+ const onPointerDown = useCallback(
4440
+ (e) => {
4441
+ if (!enabled || e.pointerType === "mouse") return;
4442
+ if (revealed) dismiss();
4443
+ else reveal();
4444
+ },
4445
+ [enabled, revealed, reveal, dismiss]
4446
+ );
4447
+ useEffect(() => {
4448
+ if (!revealed || typeof document === "undefined") return;
4449
+ const onDocDown = (ev) => {
4450
+ const target = ev.target;
4451
+ if (!(target instanceof Node)) return;
4452
+ const inside = (refs ?? []).some((r) => r.current?.contains(target));
4453
+ if (!inside) dismiss();
4454
+ };
4455
+ const id = setTimeout(() => document.addEventListener("pointerdown", onDocDown), 0);
4456
+ return () => {
4457
+ clearTimeout(id);
4458
+ document.removeEventListener("pointerdown", onDocDown);
4459
+ };
4460
+ }, [revealed, refs, dismiss]);
4461
+ return { revealed, triggerProps: { onPointerDown }, reveal, dismiss };
4462
+ }
4463
+
4464
+ // components/core/atoms/Box.tsx
4417
4465
  var paddingStyles = {
4418
4466
  none: "p-0",
4419
4467
  xs: "p-1",
@@ -4539,10 +4587,12 @@ var Box = React11.forwardRef(
4539
4587
  action,
4540
4588
  actionPayload,
4541
4589
  hoverEvent,
4590
+ tapReveal = true,
4542
4591
  maxWidth,
4543
4592
  onClick,
4544
4593
  onMouseEnter,
4545
4594
  onMouseLeave,
4595
+ onPointerDown,
4546
4596
  ...rest
4547
4597
  }, ref) => {
4548
4598
  const eventBus = useEventBus();
@@ -4565,6 +4615,19 @@ var Box = React11.forwardRef(
4565
4615
  }
4566
4616
  onMouseLeave?.(e);
4567
4617
  }, [hoverEvent, eventBus, onMouseLeave]);
4618
+ const { triggerProps } = useTapReveal({
4619
+ enabled: tapReveal && !!hoverEvent,
4620
+ onReveal: useCallback(() => {
4621
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
4622
+ }, [hoverEvent, eventBus]),
4623
+ onDismiss: useCallback(() => {
4624
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
4625
+ }, [hoverEvent, eventBus])
4626
+ });
4627
+ const handlePointerDown = useCallback((e) => {
4628
+ if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
4629
+ onPointerDown?.(e);
4630
+ }, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
4568
4631
  const isClickable = action || onClick;
4569
4632
  return React11.createElement(
4570
4633
  Component2,
@@ -4592,6 +4655,7 @@ var Box = React11.forwardRef(
4592
4655
  onClick: isClickable ? handleClick : void 0,
4593
4656
  onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
4594
4657
  onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
4658
+ onPointerDown: hoverEvent && tapReveal || onPointerDown ? handlePointerDown : void 0,
4595
4659
  style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
4596
4660
  ...rest
4597
4661
  },
@@ -21,6 +21,22 @@ interface CameraResult {
21
21
  handleMouseLeave: () => void;
22
22
  /** Wheel handler — zoom in/out */
23
23
  handleWheel: (e: React.WheelEvent, drawFn?: () => void) => void;
24
+ /** Pointer down — starts panning (mouse/touch/pen via Pointer Events). */
25
+ handlePointerDown: (e: React.PointerEvent) => void;
26
+ /** Pointer up — stops panning. */
27
+ handlePointerUp: () => void;
28
+ /** Pointer move — pans camera if dragging, returns true if panning. */
29
+ handlePointerMove: (e: React.PointerEvent, drawFn?: () => void) => boolean;
30
+ /** Apply a two-finger pan delta (screen px) directly to the camera. */
31
+ panBy: (dx: number, dy: number, drawFn?: () => void) => void;
32
+ /**
33
+ * Multiplicative zoom by `factor` keeping the world point under (centerX,centerY)
34
+ * — screen px relative to the canvas — fixed. Used by wheel + pinch.
35
+ */
36
+ zoomAtPoint: (factor: number, centerX: number, centerY: number, viewportSize: {
37
+ width: number;
38
+ height: number;
39
+ }, drawFn?: () => void) => void;
24
40
  /** Convert screen coordinates to world coordinates (inverse camera transform) */
25
41
  screenToWorld: (clientX: number, clientY: number, canvas: HTMLCanvasElement, viewportSize: {
26
42
  width: number;