@thewhateverapp/tile-sdk 0.13.31 → 0.13.33

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 (102) hide show
  1. package/dist/react/index.d.ts.map +1 -1
  2. package/dist/react/index.js +1 -0
  3. package/dist/spec/host/OverlayHost.d.ts +68 -0
  4. package/dist/spec/host/OverlayHost.d.ts.map +1 -0
  5. package/dist/spec/host/OverlayHost.js +143 -0
  6. package/dist/spec/host/index.d.ts +7 -0
  7. package/dist/spec/host/index.d.ts.map +1 -0
  8. package/dist/spec/host/index.js +6 -0
  9. package/dist/spec/index.d.ts +29 -0
  10. package/dist/spec/index.d.ts.map +1 -0
  11. package/dist/spec/index.js +81 -0
  12. package/dist/spec/registry/ComponentRegistry.d.ts +208 -0
  13. package/dist/spec/registry/ComponentRegistry.d.ts.map +1 -0
  14. package/dist/spec/registry/ComponentRegistry.js +227 -0
  15. package/dist/spec/registry/composites/BottomSheet.d.ts +33 -0
  16. package/dist/spec/registry/composites/BottomSheet.d.ts.map +1 -0
  17. package/dist/spec/registry/composites/BottomSheet.js +98 -0
  18. package/dist/spec/registry/composites/CountdownCTA.d.ts +35 -0
  19. package/dist/spec/registry/composites/CountdownCTA.d.ts.map +1 -0
  20. package/dist/spec/registry/composites/CountdownCTA.js +91 -0
  21. package/dist/spec/registry/composites/Poll.d.ts +39 -0
  22. package/dist/spec/registry/composites/Poll.d.ts.map +1 -0
  23. package/dist/spec/registry/composites/Poll.js +76 -0
  24. package/dist/spec/registry/composites/Prediction.d.ts +37 -0
  25. package/dist/spec/registry/composites/Prediction.d.ts.map +1 -0
  26. package/dist/spec/registry/composites/Prediction.js +116 -0
  27. package/dist/spec/registry/composites/index.d.ts +33 -0
  28. package/dist/spec/registry/composites/index.d.ts.map +1 -0
  29. package/dist/spec/registry/composites/index.js +36 -0
  30. package/dist/spec/registry/index.d.ts +15 -0
  31. package/dist/spec/registry/index.d.ts.map +1 -0
  32. package/dist/spec/registry/index.js +24 -0
  33. package/dist/spec/registry/primitives/Button.d.ts +30 -0
  34. package/dist/spec/registry/primitives/Button.d.ts.map +1 -0
  35. package/dist/spec/registry/primitives/Button.js +62 -0
  36. package/dist/spec/registry/primitives/Divider.d.ts +22 -0
  37. package/dist/spec/registry/primitives/Divider.d.ts.map +1 -0
  38. package/dist/spec/registry/primitives/Divider.js +56 -0
  39. package/dist/spec/registry/primitives/Image.d.ts +27 -0
  40. package/dist/spec/registry/primitives/Image.d.ts.map +1 -0
  41. package/dist/spec/registry/primitives/Image.js +36 -0
  42. package/dist/spec/registry/primitives/ProgressBar.d.ts +28 -0
  43. package/dist/spec/registry/primitives/ProgressBar.d.ts.map +1 -0
  44. package/dist/spec/registry/primitives/ProgressBar.js +50 -0
  45. package/dist/spec/registry/primitives/Row.d.ts +26 -0
  46. package/dist/spec/registry/primitives/Row.d.ts.map +1 -0
  47. package/dist/spec/registry/primitives/Row.js +50 -0
  48. package/dist/spec/registry/primitives/Spacer.d.ts +18 -0
  49. package/dist/spec/registry/primitives/Spacer.d.ts.map +1 -0
  50. package/dist/spec/registry/primitives/Spacer.js +25 -0
  51. package/dist/spec/registry/primitives/Stack.d.ts +22 -0
  52. package/dist/spec/registry/primitives/Stack.d.ts.map +1 -0
  53. package/dist/spec/registry/primitives/Stack.js +41 -0
  54. package/dist/spec/registry/primitives/Text.d.ts +26 -0
  55. package/dist/spec/registry/primitives/Text.d.ts.map +1 -0
  56. package/dist/spec/registry/primitives/Text.js +33 -0
  57. package/dist/spec/registry/primitives/index.d.ts +45 -0
  58. package/dist/spec/registry/primitives/index.d.ts.map +1 -0
  59. package/dist/spec/registry/primitives/index.js +55 -0
  60. package/dist/spec/renderer/BindingResolver.d.ts +35 -0
  61. package/dist/spec/renderer/BindingResolver.d.ts.map +1 -0
  62. package/dist/spec/renderer/BindingResolver.js +131 -0
  63. package/dist/spec/renderer/CaptionTrack.d.ts +22 -0
  64. package/dist/spec/renderer/CaptionTrack.d.ts.map +1 -0
  65. package/dist/spec/renderer/CaptionTrack.js +83 -0
  66. package/dist/spec/renderer/LayoutRenderer.d.ts +24 -0
  67. package/dist/spec/renderer/LayoutRenderer.d.ts.map +1 -0
  68. package/dist/spec/renderer/LayoutRenderer.js +66 -0
  69. package/dist/spec/renderer/OverlayCue.d.ts +20 -0
  70. package/dist/spec/renderer/OverlayCue.d.ts.map +1 -0
  71. package/dist/spec/renderer/OverlayCue.js +161 -0
  72. package/dist/spec/renderer/index.d.ts +10 -0
  73. package/dist/spec/renderer/index.d.ts.map +1 -0
  74. package/dist/spec/renderer/index.js +13 -0
  75. package/dist/spec/runtime/ActionRouter.d.ts +33 -0
  76. package/dist/spec/runtime/ActionRouter.d.ts.map +1 -0
  77. package/dist/spec/runtime/ActionRouter.js +84 -0
  78. package/dist/spec/runtime/OverlayRuntime.d.ts +84 -0
  79. package/dist/spec/runtime/OverlayRuntime.d.ts.map +1 -0
  80. package/dist/spec/runtime/OverlayRuntime.js +216 -0
  81. package/dist/spec/runtime/StateManager.d.ts +31 -0
  82. package/dist/spec/runtime/StateManager.d.ts.map +1 -0
  83. package/dist/spec/runtime/StateManager.js +60 -0
  84. package/dist/spec/runtime/TimeSync.d.ts +47 -0
  85. package/dist/spec/runtime/TimeSync.d.ts.map +1 -0
  86. package/dist/spec/runtime/TimeSync.js +140 -0
  87. package/dist/spec/runtime/index.d.ts +10 -0
  88. package/dist/spec/runtime/index.d.ts.map +1 -0
  89. package/dist/spec/runtime/index.js +13 -0
  90. package/dist/spec/schema.d.ts +889 -0
  91. package/dist/spec/schema.d.ts.map +1 -0
  92. package/dist/spec/schema.js +284 -0
  93. package/dist/spec/theme/ThemeProvider.d.ts +151 -0
  94. package/dist/spec/theme/ThemeProvider.d.ts.map +1 -0
  95. package/dist/spec/theme/ThemeProvider.js +227 -0
  96. package/dist/spec/theme/index.d.ts +7 -0
  97. package/dist/spec/theme/index.d.ts.map +1 -0
  98. package/dist/spec/theme/index.js +12 -0
  99. package/dist/spec/types.d.ts +322 -0
  100. package/dist/spec/types.d.ts.map +1 -0
  101. package/dist/spec/types.js +36 -0
  102. package/package.json +8 -1
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Component Registry
3
+ *
4
+ * Maps component type IDs (e.g., "ui.text", "ui.button") to React components.
5
+ * The spec uses string IDs; the registry resolves them to actual components.
6
+ */
7
+ // =============================================================================
8
+ // Global Registry
9
+ // =============================================================================
10
+ const registry = new Map();
11
+ /**
12
+ * Register a component with the registry.
13
+ * Components should extend RegistryComponentProps but can have additional required props.
14
+ */
15
+ export function registerComponent(type,
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ component, options) {
18
+ registry.set(type, {
19
+ component,
20
+ defaults: options?.defaults,
21
+ transformProps: options?.transformProps,
22
+ });
23
+ }
24
+ /**
25
+ * Get a component from the registry
26
+ */
27
+ export function getComponent(type) {
28
+ return registry.get(type);
29
+ }
30
+ /**
31
+ * Check if a component type is registered
32
+ */
33
+ export function hasComponent(type) {
34
+ return registry.has(type);
35
+ }
36
+ /**
37
+ * Get all registered component types
38
+ */
39
+ export function getRegisteredTypes() {
40
+ return Array.from(registry.keys());
41
+ }
42
+ /** Alias for getRegisteredTypes */
43
+ export const getAllComponentIds = getRegisteredTypes;
44
+ /**
45
+ * Clear the registry (useful for testing)
46
+ */
47
+ export function clearRegistry() {
48
+ registry.clear();
49
+ }
50
+ // =============================================================================
51
+ // Component Registry Class (for scoped registries)
52
+ // =============================================================================
53
+ export class ComponentRegistry {
54
+ constructor() {
55
+ this.components = new Map();
56
+ }
57
+ /**
58
+ * Register a component
59
+ */
60
+ register(type, component, options) {
61
+ this.components.set(type, {
62
+ component,
63
+ defaults: options?.defaults,
64
+ transformProps: options?.transformProps,
65
+ });
66
+ return this;
67
+ }
68
+ /**
69
+ * Get a component definition
70
+ */
71
+ get(type) {
72
+ return this.components.get(type);
73
+ }
74
+ /**
75
+ * Check if type is registered
76
+ */
77
+ has(type) {
78
+ return this.components.has(type);
79
+ }
80
+ /**
81
+ * Get all registered types
82
+ */
83
+ types() {
84
+ return Array.from(this.components.keys());
85
+ }
86
+ /**
87
+ * Merge another registry into this one
88
+ */
89
+ merge(other) {
90
+ for (const [type, def] of other.components) {
91
+ this.components.set(type, def);
92
+ }
93
+ return this;
94
+ }
95
+ /**
96
+ * Create a child registry that inherits from this one
97
+ */
98
+ extend() {
99
+ const child = new ComponentRegistry();
100
+ child.merge(this);
101
+ return child;
102
+ }
103
+ }
104
+ // =============================================================================
105
+ // Default Registry Instance
106
+ // =============================================================================
107
+ export const defaultRegistry = new ComponentRegistry();
108
+ /** Alias for defaultRegistry (for compatibility) */
109
+ export const globalRegistry = defaultRegistry;
110
+ // =============================================================================
111
+ // Fallback Component
112
+ // =============================================================================
113
+ /**
114
+ * Fallback component for unknown types.
115
+ * This is defined in the React component files that use it.
116
+ */
117
+ export function createUnknownComponentMessage(type) {
118
+ return `Unknown component: ${type}`;
119
+ }
120
+ // =============================================================================
121
+ // Design Token Utilities
122
+ // =============================================================================
123
+ /**
124
+ * Size token to Tailwind class mapping
125
+ */
126
+ export const sizeClasses = {
127
+ xs: 'text-xs',
128
+ sm: 'text-sm',
129
+ md: 'text-base',
130
+ lg: 'text-lg',
131
+ xl: 'text-xl',
132
+ '2xl': 'text-2xl',
133
+ };
134
+ /**
135
+ * Weight token to Tailwind class mapping
136
+ */
137
+ export const weightClasses = {
138
+ normal: 'font-normal',
139
+ medium: 'font-medium',
140
+ semibold: 'font-semibold',
141
+ bold: 'font-bold',
142
+ };
143
+ /**
144
+ * Color token to Tailwind class mapping
145
+ */
146
+ export const colorClasses = {
147
+ primary: 'text-purple-500',
148
+ secondary: 'text-gray-500',
149
+ success: 'text-green-500',
150
+ warning: 'text-yellow-500',
151
+ error: 'text-red-500',
152
+ muted: 'text-gray-400',
153
+ white: 'text-white',
154
+ black: 'text-black',
155
+ };
156
+ /**
157
+ * Background color token to Tailwind class mapping
158
+ */
159
+ export const bgColorClasses = {
160
+ primary: 'bg-purple-500',
161
+ secondary: 'bg-gray-500',
162
+ success: 'bg-green-500',
163
+ warning: 'bg-yellow-500',
164
+ error: 'bg-red-500',
165
+ muted: 'bg-gray-400',
166
+ white: 'bg-white',
167
+ black: 'bg-black',
168
+ };
169
+ /**
170
+ * Variant token to button style mapping
171
+ */
172
+ export const variantStyles = {
173
+ solid: {
174
+ base: 'bg-purple-500 text-white hover:bg-purple-600',
175
+ disabled: 'bg-gray-300 text-gray-500',
176
+ },
177
+ outline: {
178
+ base: 'border-2 border-purple-500 text-purple-500 hover:bg-purple-50',
179
+ disabled: 'border-gray-300 text-gray-400',
180
+ },
181
+ ghost: {
182
+ base: 'text-purple-500 hover:bg-purple-50',
183
+ disabled: 'text-gray-400',
184
+ },
185
+ link: {
186
+ base: 'text-purple-500 underline hover:text-purple-600',
187
+ disabled: 'text-gray-400',
188
+ },
189
+ };
190
+ /**
191
+ * Gap token to Tailwind class mapping
192
+ */
193
+ export const gapClasses = {
194
+ xs: 'gap-1',
195
+ sm: 'gap-2',
196
+ md: 'gap-4',
197
+ lg: 'gap-6',
198
+ xl: 'gap-8',
199
+ };
200
+ /**
201
+ * Padding token to Tailwind class mapping
202
+ */
203
+ export const paddingClasses = {
204
+ xs: 'p-1',
205
+ sm: 'p-2',
206
+ md: 'p-4',
207
+ lg: 'p-6',
208
+ xl: 'p-8',
209
+ };
210
+ /**
211
+ * Align token to Tailwind class mapping
212
+ */
213
+ export const alignClasses = {
214
+ left: 'items-start',
215
+ center: 'items-center',
216
+ right: 'items-end',
217
+ };
218
+ /**
219
+ * Justify token to Tailwind class mapping
220
+ */
221
+ export const justifyClasses = {
222
+ start: 'justify-start',
223
+ center: 'justify-center',
224
+ end: 'justify-end',
225
+ between: 'justify-between',
226
+ around: 'justify-around',
227
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * BottomSheet Component
3
+ *
4
+ * Slide-up panel with drag-to-dismiss support.
5
+ */
6
+ import type { RegistryComponentProps } from '../ComponentRegistry';
7
+ export interface BottomSheetProps extends RegistryComponentProps {
8
+ /** Whether the sheet is open */
9
+ open?: boolean;
10
+ /** Callback when sheet should close */
11
+ onClose?: () => void;
12
+ /** Header title */
13
+ title?: string;
14
+ /** Show close button */
15
+ showCloseButton?: boolean;
16
+ /** Show drag handle */
17
+ showDragHandle?: boolean;
18
+ /** Enable drag to dismiss */
19
+ dragToDismiss?: boolean;
20
+ /** Snap points (percentage of screen height) */
21
+ snapPoints?: number[];
22
+ /** Initial snap point index */
23
+ initialSnap?: number;
24
+ /** Backdrop opacity */
25
+ backdropOpacity?: number;
26
+ /** Click backdrop to close */
27
+ closeOnBackdropClick?: boolean;
28
+ /** Additional CSS classes */
29
+ className?: string;
30
+ }
31
+ export declare function BottomSheet({ open, onClose, title, showCloseButton, showDragHandle, dragToDismiss, snapPoints, initialSnap, backdropOpacity, closeOnBackdropClick, className, children, }: BottomSheetProps): JSX.Element | null;
32
+ export default BottomSheet;
33
+ //# sourceMappingURL=BottomSheet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BottomSheet.d.ts","sourceRoot":"","sources":["../../../../src/spec/registry/composites/BottomSheet.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEnE,MAAM,WAAW,gBAAiB,SAAQ,sBAAsB;IAC9D,gCAAgC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,wBAAwB;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,uBAAuB;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB,6BAA6B;IAC7B,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,uBAAuB;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,8BAA8B;IAC9B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAE/B,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAKD,wBAAgB,WAAW,CAAC,EAC1B,IAAW,EACX,OAAO,EACP,KAAK,EACL,eAAsB,EACtB,cAAqB,EACrB,aAAoB,EACpB,UAAkB,EAClB,WAAe,EACf,eAAqB,EACrB,oBAA2B,EAC3B,SAAc,EACd,QAAQ,GACT,EAAE,gBAAgB,GAAG,GAAG,CAAC,OAAO,GAAG,IAAI,CA6JvC;AAED,eAAe,WAAW,CAAC"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * BottomSheet Component
3
+ *
4
+ * Slide-up panel with drag-to-dismiss support.
5
+ */
6
+ import React, { useState, useCallback, useRef, useEffect } from 'react';
7
+ const DRAG_THRESHOLD = 100; // px to trigger dismiss
8
+ const VELOCITY_THRESHOLD = 500; // px/s to trigger dismiss
9
+ export function BottomSheet({ open = true, onClose, title, showCloseButton = true, showDragHandle = true, dragToDismiss = true, snapPoints = [0.5], initialSnap = 0, backdropOpacity = 0.5, closeOnBackdropClick = true, className = '', children, }) {
10
+ const [isVisible, setIsVisible] = useState(open);
11
+ const [isAnimating, setIsAnimating] = useState(false);
12
+ const [dragOffset, setDragOffset] = useState(0);
13
+ const [currentSnap, setCurrentSnap] = useState(initialSnap);
14
+ const sheetRef = useRef(null);
15
+ const dragStartRef = useRef(null);
16
+ // Handle open/close
17
+ useEffect(() => {
18
+ if (open) {
19
+ setIsVisible(true);
20
+ setIsAnimating(true);
21
+ setTimeout(() => setIsAnimating(false), 300);
22
+ }
23
+ else {
24
+ setIsAnimating(true);
25
+ setTimeout(() => {
26
+ setIsVisible(false);
27
+ setIsAnimating(false);
28
+ }, 300);
29
+ }
30
+ }, [open]);
31
+ // Get height for current snap point
32
+ const snapHeight = snapPoints[currentSnap] ?? 0.5;
33
+ // Handle backdrop click
34
+ const handleBackdropClick = useCallback(() => {
35
+ if (closeOnBackdropClick && onClose) {
36
+ onClose();
37
+ }
38
+ }, [closeOnBackdropClick, onClose]);
39
+ // Handle close button
40
+ const handleClose = useCallback(() => {
41
+ onClose?.();
42
+ }, [onClose]);
43
+ // Drag handlers
44
+ const handleDragStart = useCallback((e) => {
45
+ if (!dragToDismiss)
46
+ return;
47
+ const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
48
+ dragStartRef.current = { y: clientY, time: Date.now() };
49
+ }, [dragToDismiss]);
50
+ const handleDragMove = useCallback((e) => {
51
+ if (!dragStartRef.current || !dragToDismiss)
52
+ return;
53
+ const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
54
+ const delta = clientY - dragStartRef.current.y;
55
+ // Only allow dragging down
56
+ if (delta > 0) {
57
+ setDragOffset(delta);
58
+ }
59
+ }, [dragToDismiss]);
60
+ const handleDragEnd = useCallback(() => {
61
+ if (!dragStartRef.current || !dragToDismiss)
62
+ return;
63
+ const elapsed = Date.now() - dragStartRef.current.time;
64
+ const velocity = (dragOffset / elapsed) * 1000;
65
+ // Dismiss if dragged far enough or fast enough
66
+ if (dragOffset > DRAG_THRESHOLD || velocity > VELOCITY_THRESHOLD) {
67
+ onClose?.();
68
+ }
69
+ setDragOffset(0);
70
+ dragStartRef.current = null;
71
+ }, [dragToDismiss, dragOffset, onClose]);
72
+ if (!isVisible) {
73
+ return null;
74
+ }
75
+ return (React.createElement("div", { className: "bottom-sheet-container fixed inset-0 z-50" },
76
+ React.createElement("div", { className: `
77
+ absolute inset-0 bg-black transition-opacity duration-300
78
+ ${open && !isAnimating ? '' : 'opacity-0'}
79
+ `, style: { opacity: open && !isAnimating ? backdropOpacity : 0 }, onClick: handleBackdropClick }),
80
+ React.createElement("div", { ref: sheetRef, className: `
81
+ absolute bottom-0 left-0 right-0
82
+ bg-gray-800 rounded-t-2xl
83
+ transition-transform duration-300 ease-out
84
+ ${className}
85
+ `, style: {
86
+ height: `${snapHeight * 100}%`,
87
+ transform: `translateY(${open && !isAnimating ? dragOffset : '100%'}px)`,
88
+ }, onTouchStart: handleDragStart, onTouchMove: handleDragMove, onTouchEnd: handleDragEnd, onMouseDown: handleDragStart, onMouseMove: handleDragMove, onMouseUp: handleDragEnd, onMouseLeave: handleDragEnd },
89
+ showDragHandle && (React.createElement("div", { className: "flex justify-center pt-3 pb-2" },
90
+ React.createElement("div", { className: "w-10 h-1 bg-gray-600 rounded-full" }))),
91
+ (title || showCloseButton) && (React.createElement("div", { className: "flex items-center justify-between px-4 py-2 border-b border-gray-700" },
92
+ React.createElement("h3", { className: "text-white font-semibold text-lg" }, title),
93
+ showCloseButton && (React.createElement("button", { onClick: handleClose, className: "p-2 text-gray-400 hover:text-white transition-colors", "aria-label": "Close" },
94
+ React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "2" },
95
+ React.createElement("path", { d: "M15 5L5 15M5 5l10 10" })))))),
96
+ React.createElement("div", { className: "overflow-auto", style: { maxHeight: 'calc(100% - 60px)' } }, children))));
97
+ }
98
+ export default BottomSheet;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * CountdownCTA Component
3
+ *
4
+ * Urgency countdown with action button.
5
+ */
6
+ import type { RegistryComponentProps } from '../ComponentRegistry';
7
+ import type { Color, Variant } from '../../types';
8
+ export interface CountdownCTAProps extends RegistryComponentProps {
9
+ /** Main headline text */
10
+ headline?: string;
11
+ /** Subtext / description */
12
+ subtext?: string;
13
+ /** Countdown end time (ISO string or ms timestamp) */
14
+ endsAt: string | number;
15
+ /** Button label */
16
+ buttonLabel: string;
17
+ /** Button action callback */
18
+ onPress?: () => void;
19
+ /** Expired state label */
20
+ expiredLabel?: string;
21
+ /** Show urgency styling when time is low */
22
+ urgentThresholdMs?: number;
23
+ /** Color theme */
24
+ color?: Color;
25
+ /** Button variant */
26
+ variant?: Variant;
27
+ /** Compact mode */
28
+ compact?: boolean;
29
+ /** Additional CSS classes */
30
+ className?: string;
31
+ }
32
+ export declare function CountdownCTA({ headline, subtext, endsAt, buttonLabel, onPress, expiredLabel, urgentThresholdMs, // 1 minute
33
+ color, variant, compact, className, }: CountdownCTAProps): JSX.Element;
34
+ export default CountdownCTA;
35
+ //# sourceMappingURL=CountdownCTA.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CountdownCTA.d.ts","sourceRoot":"","sources":["../../../../src/spec/registry/composites/CountdownCTA.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,iBAAkB,SAAQ,sBAAsB;IAC/D,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,sDAAsD;IACtD,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IAExB,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;IAEpB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB,0BAA0B;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,4CAA4C;IAC5C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B,kBAAkB;IAClB,KAAK,CAAC,EAAE,KAAK,CAAC;IAEd,qBAAqB;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,mBAAmB;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA6CD,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,OAAO,EACP,MAAM,EACN,WAAW,EACX,OAAO,EACP,YAAwB,EACxB,iBAAyB,EAAE,WAAW;AACtC,KAAiB,EACjB,OAAiB,EACjB,OAAe,EACf,SAAc,GACf,EAAE,iBAAiB,GAAG,GAAG,CAAC,OAAO,CAiHjC;AAED,eAAe,YAAY,CAAC"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * CountdownCTA Component
3
+ *
4
+ * Urgency countdown with action button.
5
+ */
6
+ import React, { useState, useEffect } from 'react';
7
+ const colorClasses = {
8
+ primary: 'bg-purple-500 hover:bg-purple-600',
9
+ secondary: 'bg-gray-500 hover:bg-gray-600',
10
+ success: 'bg-green-500 hover:bg-green-600',
11
+ warning: 'bg-yellow-500 hover:bg-yellow-600',
12
+ error: 'bg-red-500 hover:bg-red-600',
13
+ muted: 'bg-gray-400 hover:bg-gray-500',
14
+ white: 'bg-white hover:bg-gray-100 text-black',
15
+ black: 'bg-black hover:bg-gray-900',
16
+ };
17
+ /**
18
+ * Breaks down milliseconds into time units.
19
+ */
20
+ function getTimeUnits(ms) {
21
+ if (ms <= 0)
22
+ return [];
23
+ const seconds = Math.floor(ms / 1000);
24
+ const minutes = Math.floor(seconds / 60);
25
+ const hours = Math.floor(minutes / 60);
26
+ const days = Math.floor(hours / 24);
27
+ const units = [];
28
+ if (days > 0) {
29
+ units.push({ value: days, label: 'd' });
30
+ }
31
+ if (hours % 24 > 0 || days > 0) {
32
+ units.push({ value: hours % 24, label: 'h' });
33
+ }
34
+ if (minutes % 60 > 0 || hours > 0) {
35
+ units.push({ value: minutes % 60, label: 'm' });
36
+ }
37
+ units.push({ value: seconds % 60, label: 's' });
38
+ return units.slice(0, 4); // Max 4 units
39
+ }
40
+ export function CountdownCTA({ headline, subtext, endsAt, buttonLabel, onPress, expiredLabel = 'Expired', urgentThresholdMs = 60000, // 1 minute
41
+ color = 'primary', variant = 'solid', compact = false, className = '', }) {
42
+ const [timeRemaining, setTimeRemaining] = useState(0);
43
+ // Countdown timer
44
+ useEffect(() => {
45
+ const endTime = typeof endsAt === 'string' ? new Date(endsAt).getTime() : endsAt;
46
+ const update = () => {
47
+ const remaining = endTime - Date.now();
48
+ setTimeRemaining(remaining > 0 ? remaining : 0);
49
+ };
50
+ update();
51
+ const interval = setInterval(update, 1000);
52
+ return () => clearInterval(interval);
53
+ }, [endsAt]);
54
+ const isExpired = timeRemaining <= 0;
55
+ const isUrgent = timeRemaining > 0 && timeRemaining <= urgentThresholdMs;
56
+ const timeUnits = getTimeUnits(timeRemaining);
57
+ if (compact) {
58
+ return (React.createElement("div", { className: `
59
+ countdown-cta-compact flex items-center gap-3 p-3
60
+ bg-gray-800/90 backdrop-blur rounded-lg
61
+ ${isUrgent ? 'animate-pulse' : ''}
62
+ ${className}
63
+ ` },
64
+ React.createElement("div", { className: `text-lg font-mono font-bold ${isUrgent ? 'text-red-400' : 'text-white'}` }, isExpired ? '--:--' : timeUnits.map((u) => String(u.value).padStart(2, '0')).join(':')),
65
+ React.createElement("button", { onClick: onPress, disabled: isExpired, className: `
66
+ px-4 py-2 rounded-lg font-semibold text-white
67
+ transition-all duration-200
68
+ ${isExpired ? 'bg-gray-600 cursor-not-allowed' : colorClasses[color]}
69
+ ` }, isExpired ? expiredLabel : buttonLabel)));
70
+ }
71
+ return (React.createElement("div", { className: `
72
+ countdown-cta bg-gray-800/90 backdrop-blur rounded-xl p-5
73
+ ${isUrgent ? 'ring-2 ring-red-500/50' : ''}
74
+ ${className}
75
+ ` },
76
+ headline && (React.createElement("h3", { className: "text-white font-bold text-lg mb-1" }, headline)),
77
+ subtext && (React.createElement("p", { className: "text-gray-400 text-sm mb-4" }, subtext)),
78
+ React.createElement("div", { className: "flex justify-center gap-2 mb-4" }, isExpired ? (React.createElement("div", { className: "text-gray-500 text-2xl font-mono" }, "--:--:--")) : (timeUnits.map((unit, index) => (React.createElement("div", { key: unit.label, className: "text-center" },
79
+ React.createElement("div", { className: `
80
+ text-2xl font-mono font-bold
81
+ ${isUrgent ? 'text-red-400' : 'text-white'}
82
+ ` }, String(unit.value).padStart(2, '0')),
83
+ React.createElement("div", { className: "text-gray-500 text-xs uppercase" }, unit.label)))))),
84
+ isUrgent && !isExpired && (React.createElement("div", { className: "text-center text-red-400 text-sm mb-3 animate-pulse" }, "Hurry! Time is running out")),
85
+ React.createElement("button", { onClick: onPress, disabled: isExpired, className: `
86
+ w-full py-3 rounded-lg font-semibold text-white
87
+ transition-all duration-200
88
+ ${isExpired ? 'bg-gray-600 cursor-not-allowed' : colorClasses[color]}
89
+ ` }, isExpired ? expiredLabel : buttonLabel)));
90
+ }
91
+ export default CountdownCTA;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Poll Component
3
+ *
4
+ * Multi-choice voting with animated live results.
5
+ */
6
+ import type { RegistryComponentProps } from '../ComponentRegistry';
7
+ import type { Color } from '../../types';
8
+ export interface PollOption {
9
+ id: string;
10
+ label: string;
11
+ votes?: number;
12
+ }
13
+ export interface PollProps extends RegistryComponentProps {
14
+ /** Poll question */
15
+ question: string;
16
+ /** Poll options */
17
+ options: PollOption[] | string[];
18
+ /** Current results (votes per option) */
19
+ results?: number[];
20
+ /** Total votes */
21
+ totalVotes?: number;
22
+ /** Show results before voting */
23
+ showResultsBeforeVote?: boolean;
24
+ /** Whether the user has already voted */
25
+ hasVoted?: boolean;
26
+ /** The option ID the user voted for */
27
+ votedOptionId?: string;
28
+ /** Callback when user votes */
29
+ onVote?: (optionId: string) => void;
30
+ /** Accent color */
31
+ color?: Color;
32
+ /** Disabled state */
33
+ disabled?: boolean;
34
+ /** Additional CSS classes */
35
+ className?: string;
36
+ }
37
+ export declare function Poll({ question, options: rawOptions, results, totalVotes: providedTotal, showResultsBeforeVote, hasVoted, votedOptionId, onVote, color, disabled, className, }: PollProps): JSX.Element;
38
+ export default Poll;
39
+ //# sourceMappingURL=Poll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Poll.d.ts","sourceRoot":"","sources":["../../../../src/spec/registry/composites/Poll.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,KAAK,EAAU,KAAK,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAU,SAAQ,sBAAsB;IACvD,oBAAoB;IACpB,QAAQ,EAAE,MAAM,CAAC;IAEjB,mBAAmB;IACnB,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,EAAE,CAAC;IAEjC,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,kBAAkB;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,iCAAiC;IACjC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC,yCAAyC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,uCAAuC;IACvC,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,+BAA+B;IAC/B,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAEpC,mBAAmB;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC;IAEd,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAyBD,wBAAgB,IAAI,CAAC,EACnB,QAAQ,EACR,OAAO,EAAE,UAAU,EACnB,OAAY,EACZ,UAAU,EAAE,aAAa,EACzB,qBAA6B,EAC7B,QAAgB,EAChB,aAAa,EACb,MAAM,EACN,KAAiB,EACjB,QAAgB,EAChB,SAAc,GACf,EAAE,SAAS,GAAG,GAAG,CAAC,OAAO,CAgGzB;AAED,eAAe,IAAI,CAAC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Poll Component
3
+ *
4
+ * Multi-choice voting with animated live results.
5
+ */
6
+ import React, { useState, useCallback } from 'react';
7
+ const colorClasses = {
8
+ primary: 'bg-purple-500',
9
+ secondary: 'bg-gray-500',
10
+ success: 'bg-green-500',
11
+ warning: 'bg-yellow-500',
12
+ error: 'bg-red-500',
13
+ muted: 'bg-gray-400',
14
+ white: 'bg-white',
15
+ black: 'bg-black',
16
+ };
17
+ /**
18
+ * Normalizes options to a consistent format.
19
+ */
20
+ function normalizeOptions(options) {
21
+ return options.map((opt, index) => {
22
+ if (typeof opt === 'string') {
23
+ return { id: String(index), label: opt };
24
+ }
25
+ return opt;
26
+ });
27
+ }
28
+ export function Poll({ question, options: rawOptions, results = [], totalVotes: providedTotal, showResultsBeforeVote = false, hasVoted = false, votedOptionId, onVote, color = 'primary', disabled = false, className = '', }) {
29
+ const [selectedId, setSelectedId] = useState(null);
30
+ const [isSubmitting, setIsSubmitting] = useState(false);
31
+ const options = normalizeOptions(rawOptions);
32
+ // Calculate total votes
33
+ const totalVotes = providedTotal ?? results.reduce((sum, v) => sum + v, 0);
34
+ // Should we show results?
35
+ const showResults = hasVoted || showResultsBeforeVote;
36
+ // Handle option click
37
+ const handleOptionClick = useCallback((optionId) => {
38
+ if (disabled || hasVoted || isSubmitting)
39
+ return;
40
+ if (onVote) {
41
+ setIsSubmitting(true);
42
+ setSelectedId(optionId);
43
+ onVote(optionId);
44
+ }
45
+ }, [disabled, hasVoted, isSubmitting, onVote]);
46
+ return (React.createElement("div", { className: `poll-component bg-gray-800/90 backdrop-blur rounded-xl p-4 ${className}` },
47
+ React.createElement("h3", { className: "text-white font-semibold text-lg mb-4" }, question),
48
+ React.createElement("div", { className: "space-y-2" }, options.map((option, index) => {
49
+ const votes = results[index] ?? 0;
50
+ const percentage = totalVotes > 0 ? (votes / totalVotes) * 100 : 0;
51
+ const isSelected = option.id === selectedId;
52
+ const isVotedFor = option.id === votedOptionId;
53
+ const canClick = !disabled && !hasVoted && !isSubmitting;
54
+ return (React.createElement("button", { key: option.id, onClick: () => handleOptionClick(option.id), disabled: !canClick, className: `
55
+ w-full relative overflow-hidden rounded-lg p-3 text-left
56
+ transition-all duration-200
57
+ ${canClick ? 'hover:bg-gray-700/50 cursor-pointer' : 'cursor-default'}
58
+ ${isSelected ? 'ring-2 ring-purple-500' : ''}
59
+ ${isVotedFor ? 'ring-2 ring-green-500' : ''}
60
+ bg-gray-700/30
61
+ ` },
62
+ showResults && (React.createElement("div", { className: `absolute inset-y-0 left-0 ${colorClasses[color]} opacity-30 transition-all duration-500`, style: { width: `${percentage}%` } })),
63
+ React.createElement("div", { className: "relative flex items-center justify-between" },
64
+ React.createElement("span", { className: "text-white font-medium" }, option.label),
65
+ showResults && (React.createElement("span", { className: "text-white/70 text-sm" },
66
+ Math.round(percentage),
67
+ "%")),
68
+ isVotedFor && (React.createElement("span", { className: "ml-2 text-green-400" }, "\u2713")))));
69
+ })),
70
+ showResults && totalVotes > 0 && (React.createElement("div", { className: "mt-3 text-center text-gray-400 text-sm" },
71
+ totalVotes.toLocaleString(),
72
+ " ",
73
+ totalVotes === 1 ? 'vote' : 'votes')),
74
+ isSubmitting && !hasVoted && (React.createElement("div", { className: "mt-2 text-center text-gray-400 text-sm" }, "Submitting..."))));
75
+ }
76
+ export default Poll;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Prediction Component
3
+ *
4
+ * Binary choice prediction with countdown and outcome reveal.
5
+ */
6
+ import type { RegistryComponentProps } from '../ComponentRegistry';
7
+ import type { Color } from '../../types';
8
+ export interface PredictionOption {
9
+ id: string;
10
+ label: string;
11
+ odds?: number;
12
+ percentage?: number;
13
+ }
14
+ export type PredictionStatus = 'open' | 'locked' | 'resolved';
15
+ export interface PredictionProps extends RegistryComponentProps {
16
+ /** Prediction question */
17
+ question: string;
18
+ /** Two options */
19
+ options: [PredictionOption, PredictionOption] | [string, string];
20
+ /** Current status */
21
+ status?: PredictionStatus;
22
+ /** Winning option ID (when resolved) */
23
+ winnerId?: string;
24
+ /** User's prediction option ID */
25
+ userPredictionId?: string;
26
+ /** Countdown end time (ISO string or ms timestamp) */
27
+ locksAt?: string | number;
28
+ /** Callback when user predicts */
29
+ onPredict?: (optionId: string) => void;
30
+ /** Color scheme */
31
+ colors?: [Color, Color];
32
+ /** Additional CSS classes */
33
+ className?: string;
34
+ }
35
+ export declare function Prediction({ question, options: rawOptions, status, winnerId, userPredictionId, locksAt, onPredict, colors, className, }: PredictionProps): JSX.Element;
36
+ export default Prediction;
37
+ //# sourceMappingURL=Prediction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Prediction.d.ts","sourceRoot":"","sources":["../../../../src/spec/registry/composites/Prediction.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE9D,MAAM,WAAW,eAAgB,SAAQ,sBAAsB;IAC7D,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;IAEjB,kBAAkB;IAClB,OAAO,EAAE,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjE,qBAAqB;IACrB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAE1B,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,kCAAkC;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAE1B,kCAAkC;IAClC,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAEvC,mBAAmB;IACnB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAExB,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2CD,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,EAAE,UAAU,EACnB,MAAe,EACf,QAAQ,EACR,gBAAgB,EAChB,OAAO,EACP,SAAS,EACT,MAA6B,EAC7B,SAAc,GACf,EAAE,eAAe,GAAG,GAAG,CAAC,OAAO,CAuI/B;AAED,eAAe,UAAU,CAAC"}