@djangocfg/ui-tools 2.1.381 → 2.1.383

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 (178) hide show
  1. package/README.md +132 -899
  2. package/dist/ChatRoot-6IZFM5HM.mjs +5 -0
  3. package/dist/{ChatRoot-EJC5Y2YM.cjs.map → ChatRoot-6IZFM5HM.mjs.map} +1 -1
  4. package/dist/ChatRoot-LW4XNIKP.cjs +14 -0
  5. package/dist/{ChatRoot-QOSKJPM6.mjs.map → ChatRoot-LW4XNIKP.cjs.map} +1 -1
  6. package/dist/DictationField-U25MEYAL.mjs +4 -0
  7. package/dist/DictationField-U25MEYAL.mjs.map +1 -0
  8. package/dist/DictationField-XWR5VOID.cjs +13 -0
  9. package/dist/DictationField-XWR5VOID.cjs.map +1 -0
  10. package/dist/{DocsLayout-2YKPXZYO.mjs → DocsLayout-2P3ONDWJ.mjs} +3 -3
  11. package/dist/{DocsLayout-2YKPXZYO.mjs.map → DocsLayout-2P3ONDWJ.mjs.map} +1 -1
  12. package/dist/{DocsLayout-Q4KS3QWW.cjs → DocsLayout-2YZNS5VK.cjs} +8 -8
  13. package/dist/{DocsLayout-Q4KS3QWW.cjs.map → DocsLayout-2YZNS5VK.cjs.map} +1 -1
  14. package/dist/chunk-4PFW7MIJ.cjs +837 -0
  15. package/dist/chunk-4PFW7MIJ.cjs.map +1 -0
  16. package/dist/chunk-C2YN6WEO.mjs +833 -0
  17. package/dist/chunk-C2YN6WEO.mjs.map +1 -0
  18. package/dist/{chunk-XACCHZH2.cjs → chunk-FIRK5CEH.cjs} +42 -4
  19. package/dist/chunk-FIRK5CEH.cjs.map +1 -0
  20. package/dist/{chunk-NWUT327A.mjs → chunk-HIK6BPL7.mjs} +38 -5
  21. package/dist/chunk-HIK6BPL7.mjs.map +1 -0
  22. package/dist/chunk-OZAU3QWD.cjs +2493 -0
  23. package/dist/chunk-OZAU3QWD.cjs.map +1 -0
  24. package/dist/chunk-UWVP6LCW.mjs +2447 -0
  25. package/dist/chunk-UWVP6LCW.mjs.map +1 -0
  26. package/dist/index.cjs +1668 -99
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +1215 -107
  29. package/dist/index.d.ts +1215 -107
  30. package/dist/index.mjs +1555 -50
  31. package/dist/index.mjs.map +1 -1
  32. package/package.json +16 -15
  33. package/src/audio-assets.d.ts +8 -0
  34. package/src/components/markdown/MarkdownMessage/CollapseToggle.tsx +3 -1
  35. package/src/components/markdown/MarkdownMessage/components.tsx +2 -5
  36. package/src/tools/Chat/README.md +347 -530
  37. package/src/tools/Chat/components/Attachments.tsx +6 -1
  38. package/src/tools/Chat/components/ChatRoot.tsx +30 -2
  39. package/src/tools/Chat/components/Composer.tsx +20 -3
  40. package/src/tools/Chat/components/ErrorBanner.tsx +7 -3
  41. package/src/tools/Chat/components/MessageActions.tsx +3 -1
  42. package/src/tools/Chat/components/MessageBubble.tsx +6 -5
  43. package/src/tools/Chat/components/MessageList.tsx +87 -1
  44. package/src/tools/Chat/components/ToolCalls.tsx +21 -3
  45. package/src/tools/Chat/context/ChatProvider.tsx +21 -3
  46. package/src/tools/Chat/core/audio/audioBus.ts +10 -163
  47. package/src/tools/Chat/core/audio/defaults.ts +43 -0
  48. package/src/tools/Chat/core/audio/index.ts +1 -0
  49. package/src/tools/Chat/core/audio/preferences.ts +5 -59
  50. package/src/tools/Chat/core/audio/sounds/error.mp3 +0 -0
  51. package/src/tools/Chat/core/audio/sounds/mention.mp3 +0 -0
  52. package/src/tools/Chat/core/audio/sounds/notification.mp3 +0 -0
  53. package/src/tools/Chat/core/audio/sounds/received.mp3 +0 -0
  54. package/src/tools/Chat/core/audio/sounds/sent.mp3 +0 -0
  55. package/src/tools/Chat/core/audio/sounds/start.mp3 +0 -0
  56. package/src/tools/Chat/core/audio/types.ts +28 -0
  57. package/src/tools/Chat/core/reducer.ts +33 -0
  58. package/src/tools/Chat/core/transport/index.ts +13 -0
  59. package/src/tools/Chat/core/transport/mappers/index.ts +6 -0
  60. package/src/tools/Chat/core/transport/mappers/pydantic-ai.ts +142 -0
  61. package/src/tools/Chat/core/transport/pydantic-ai-transport.ts +208 -0
  62. package/src/tools/Chat/core/transport/sse.ts +18 -5
  63. package/src/tools/Chat/hooks/index.ts +25 -0
  64. package/src/tools/Chat/hooks/useAutoFocusOnStreamEnd.ts +5 -3
  65. package/src/tools/Chat/hooks/useChat.ts +28 -0
  66. package/src/tools/Chat/hooks/useChatAudio.ts +59 -180
  67. package/src/tools/Chat/hooks/useChatDockPrefs.ts +74 -0
  68. package/src/tools/Chat/hooks/useChatReset.ts +70 -0
  69. package/src/tools/Chat/hooks/useChatUnread.ts +87 -0
  70. package/src/tools/Chat/hooks/useFocusOnEmptyClick.ts +111 -0
  71. package/src/tools/Chat/hooks/useVisitorFingerprint.ts +48 -0
  72. package/src/tools/Chat/index.ts +84 -1
  73. package/src/tools/Chat/launcher/ChatDock.tsx +263 -0
  74. package/src/tools/Chat/launcher/ChatFAB.tsx +349 -0
  75. package/src/tools/Chat/launcher/ChatGreeting.tsx +200 -0
  76. package/src/tools/Chat/launcher/ChatHeader.tsx +76 -0
  77. package/src/tools/Chat/launcher/ChatHeaderActionButton.tsx +87 -0
  78. package/src/tools/Chat/launcher/ChatHeaderAudioToggle.tsx +47 -0
  79. package/src/tools/Chat/launcher/ChatHeaderLanguageButton.tsx +179 -0
  80. package/src/tools/Chat/launcher/ChatHeaderModeToggle.tsx +57 -0
  81. package/src/tools/Chat/launcher/ChatHeaderResetButton.tsx +93 -0
  82. package/src/tools/Chat/launcher/ChatLauncher.tsx +321 -0
  83. package/src/tools/Chat/launcher/ChatUnreadPreview.tsx +197 -0
  84. package/src/tools/Chat/launcher/index.ts +46 -0
  85. package/src/tools/Chat/launcher/useChatPresence.ts +44 -0
  86. package/src/tools/Chat/styles/bubbleTokens.ts +71 -0
  87. package/src/tools/Chat/styles/index.ts +16 -0
  88. package/src/tools/Chat/styles/useChatStyles.ts +101 -0
  89. package/src/tools/Chat/types/attachment.ts +25 -0
  90. package/src/tools/Chat/types/config.ts +48 -0
  91. package/src/tools/Chat/types/events.ts +35 -0
  92. package/src/tools/Chat/types/index.ts +34 -0
  93. package/src/tools/Chat/types/labels.ts +38 -0
  94. package/src/tools/Chat/types/message.ts +32 -0
  95. package/src/tools/Chat/types/persona.ts +31 -0
  96. package/src/tools/Chat/types/session.ts +43 -0
  97. package/src/tools/Chat/types/tool-call.ts +17 -0
  98. package/src/tools/Chat/types/transport.ts +28 -0
  99. package/src/tools/Chat/types.ts +5 -240
  100. package/src/tools/MarkdownEditor/MarkdownEditor.tsx +50 -14
  101. package/src/tools/MarkdownEditor/index.ts +1 -1
  102. package/src/tools/SpeechRecognition/README.md +336 -0
  103. package/src/tools/SpeechRecognition/__tests__/ids.test.ts +15 -0
  104. package/src/tools/SpeechRecognition/__tests__/language.test.ts +59 -0
  105. package/src/tools/SpeechRecognition/__tests__/reducer.test.ts +71 -0
  106. package/src/tools/SpeechRecognition/__tests__/transcript.test.ts +52 -0
  107. package/src/tools/SpeechRecognition/components/DevicePicker.tsx +49 -0
  108. package/src/tools/SpeechRecognition/components/DictationButton.tsx +93 -0
  109. package/src/tools/SpeechRecognition/components/EngineBadge.tsx +30 -0
  110. package/src/tools/SpeechRecognition/components/ErrorBanner.tsx +52 -0
  111. package/src/tools/SpeechRecognition/components/LanguagePicker.tsx +63 -0
  112. package/src/tools/SpeechRecognition/components/MicMeter.tsx +63 -0
  113. package/src/tools/SpeechRecognition/components/PushToTalkHint.tsx +51 -0
  114. package/src/tools/SpeechRecognition/components/TranscriptView.tsx +55 -0
  115. package/src/tools/SpeechRecognition/components/index.ts +16 -0
  116. package/src/tools/SpeechRecognition/context/SpeechRecognitionProvider.tsx +47 -0
  117. package/src/tools/SpeechRecognition/context/index.ts +6 -0
  118. package/src/tools/SpeechRecognition/core/audio/defaults.ts +24 -0
  119. package/src/tools/SpeechRecognition/core/engine/external.ts +222 -0
  120. package/src/tools/SpeechRecognition/core/engine/http.ts +147 -0
  121. package/src/tools/SpeechRecognition/core/engine/index.ts +52 -0
  122. package/src/tools/SpeechRecognition/core/engine/mediarecorder.ts +105 -0
  123. package/src/tools/SpeechRecognition/core/engine/websocket.ts +211 -0
  124. package/src/tools/SpeechRecognition/core/engine/webspeech.ts +188 -0
  125. package/src/tools/SpeechRecognition/core/ids.ts +11 -0
  126. package/src/tools/SpeechRecognition/core/index.ts +14 -0
  127. package/src/tools/SpeechRecognition/core/language.ts +78 -0
  128. package/src/tools/SpeechRecognition/core/languages-catalog.ts +229 -0
  129. package/src/tools/SpeechRecognition/core/logger.ts +3 -0
  130. package/src/tools/SpeechRecognition/core/reducer.ts +105 -0
  131. package/src/tools/SpeechRecognition/core/transcript.ts +36 -0
  132. package/src/tools/SpeechRecognition/hooks/index.ts +14 -0
  133. package/src/tools/SpeechRecognition/hooks/useDictation.ts +59 -0
  134. package/src/tools/SpeechRecognition/hooks/useEnginePrefs.ts +15 -0
  135. package/src/tools/SpeechRecognition/hooks/useMicDevices.ts +57 -0
  136. package/src/tools/SpeechRecognition/hooks/useMicLevel.ts +52 -0
  137. package/src/tools/SpeechRecognition/hooks/usePushToTalk.ts +85 -0
  138. package/src/tools/SpeechRecognition/hooks/useResolvedLanguage.ts +28 -0
  139. package/src/tools/SpeechRecognition/hooks/useSpeechLanguageInfo.ts +108 -0
  140. package/src/tools/SpeechRecognition/hooks/useSpeechRecognition.ts +188 -0
  141. package/src/tools/SpeechRecognition/hooks/useVoiceSupport.ts +78 -0
  142. package/src/tools/SpeechRecognition/index.ts +82 -0
  143. package/src/tools/SpeechRecognition/lazy.tsx +19 -0
  144. package/src/tools/SpeechRecognition/store/index.ts +2 -0
  145. package/src/tools/SpeechRecognition/store/prefsStore.ts +54 -0
  146. package/src/tools/SpeechRecognition/types.ts +133 -0
  147. package/src/tools/SpeechRecognition/widgets/DictationField.tsx +105 -0
  148. package/src/tools/SpeechRecognition/widgets/VoiceComposerSlot.tsx +305 -0
  149. package/src/tools/SpeechRecognition/widgets/VoiceMessageRecorder.tsx +88 -0
  150. package/src/tools/SpeechRecognition/widgets/index.ts +6 -0
  151. package/dist/ChatRoot-EJC5Y2YM.cjs +0 -14
  152. package/dist/ChatRoot-QOSKJPM6.mjs +0 -5
  153. package/dist/chunk-NWUT327A.mjs.map +0 -1
  154. package/dist/chunk-QLMKCSR6.mjs +0 -2420
  155. package/dist/chunk-QLMKCSR6.mjs.map +0 -1
  156. package/dist/chunk-SI5RD2GD.cjs +0 -2460
  157. package/dist/chunk-SI5RD2GD.cjs.map +0 -1
  158. package/dist/chunk-XACCHZH2.cjs.map +0 -1
  159. package/src/components/markdown/MarkdownMessage/MarkdownMessage.story.tsx +0 -771
  160. package/src/stories/index.ts +0 -33
  161. package/src/tools/AudioPlayer/AudioPlayer.story.tsx +0 -481
  162. package/src/tools/Chat/Chat.story.tsx +0 -1457
  163. package/src/tools/CodeEditor/CodeEditor.story.tsx +0 -202
  164. package/src/tools/CronScheduler/CronScheduler.story.tsx +0 -300
  165. package/src/tools/Gallery/Gallery.story.tsx +0 -237
  166. package/src/tools/ImageViewer/ImageViewer.story.tsx +0 -85
  167. package/src/tools/JsonForm/JsonForm.story.tsx +0 -350
  168. package/src/tools/JsonTree/JsonTree.story.tsx +0 -141
  169. package/src/tools/LottiePlayer/LottiePlayer.story.tsx +0 -95
  170. package/src/tools/Map/Map.story.tsx +0 -458
  171. package/src/tools/MarkdownEditor/MarkdownEditor.story.tsx +0 -225
  172. package/src/tools/Mermaid/Mermaid.story.tsx +0 -251
  173. package/src/tools/OpenapiViewer/OpenapiViewer.story.tsx +0 -230
  174. package/src/tools/PrettyCode/PrettyCode.story.tsx +0 -304
  175. package/src/tools/Tour/Tour.story.tsx +0 -279
  176. package/src/tools/Tree/Tree.story.tsx +0 -620
  177. package/src/tools/Uploader/Uploader.story.tsx +0 -415
  178. package/src/tools/VideoPlayer/VideoPlayer.story.tsx +0 -87
@@ -1,304 +0,0 @@
1
- import { defineStory, useSelect, useBoolean } from '@djangocfg/playground';
2
- import PrettyCode from './index';
3
-
4
- export default defineStory({
5
- title: 'Tools/Pretty Code',
6
- component: PrettyCode,
7
- description: 'Syntax highlighted code block with copy button.',
8
- });
9
-
10
- // ─── Samples ──────────────────────────────────────────────────────────────────
11
-
12
- const LONG_CODE_SAMPLES = {
13
- typescript: `import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
14
-
15
- export type Status = 'idle' | 'loading' | 'success' | 'error';
16
-
17
- export interface PaginationParams {
18
- page: number;
19
- pageSize: number;
20
- sortBy?: string;
21
- sortOrder?: 'asc' | 'desc';
22
- }
23
-
24
- export function useVehicles(initialParams?: Partial<PaginationParams>) {
25
- const [status, setStatus] = useState<Status>('idle');
26
- const [data, setData] = useState<unknown[]>([]);
27
- const [error, setError] = useState<Error | null>(null);
28
- const abortRef = useRef<AbortController | null>(null);
29
-
30
- const [params, setParams] = useState<PaginationParams>({
31
- page: 1,
32
- pageSize: 20,
33
- ...initialParams,
34
- });
35
-
36
- const fetch = useCallback(async (p: PaginationParams) => {
37
- abortRef.current?.abort();
38
- abortRef.current = new AbortController();
39
- setStatus('loading');
40
- try {
41
- const query = new URLSearchParams({
42
- page: String(p.page),
43
- pageSize: String(p.pageSize),
44
- });
45
- const res = await window.fetch(\`/api/vehicles?\${query}\`, {
46
- signal: abortRef.current.signal,
47
- });
48
- if (!res.ok) throw new Error(\`HTTP \${res.status}\`);
49
- const json = await res.json();
50
- setData(json.data);
51
- setStatus('success');
52
- } catch (err) {
53
- if ((err as Error).name === 'AbortError') return;
54
- setError(err as Error);
55
- setStatus('error');
56
- }
57
- }, []);
58
-
59
- useEffect(() => {
60
- fetch(params);
61
- return () => abortRef.current?.abort();
62
- }, [params, fetch]);
63
-
64
- return { status, data, error, params, setParams };
65
- }`,
66
-
67
- python: `from __future__ import annotations
68
-
69
- import asyncio
70
- import logging
71
- from dataclasses import dataclass, field
72
- from datetime import datetime
73
- from enum import StrEnum
74
-
75
- import httpx
76
- from pydantic import BaseModel, ConfigDict, Field, field_validator
77
-
78
- logger = logging.getLogger(__name__)
79
-
80
-
81
- class FuelType(StrEnum):
82
- GASOLINE = "gasoline"
83
- DIESEL = "diesel"
84
- ELECTRIC = "electric"
85
-
86
-
87
- class Vehicle(BaseModel):
88
- model_config = ConfigDict(from_attributes=True)
89
-
90
- id: str
91
- make: str
92
- model: str
93
- year: int
94
- fuel_type: FuelType
95
- created_at: datetime = Field(default_factory=datetime.utcnow)
96
-
97
- @field_validator("year")
98
- @classmethod
99
- def validate_year(cls, v: int) -> int:
100
- if not (1900 <= v <= datetime.utcnow().year + 1):
101
- raise ValueError(f"Invalid year: {v}")
102
- return v
103
-
104
-
105
- @dataclass
106
- class VehicleRepository:
107
- base_url: str
108
-
109
- async def list(self, page: int = 1, page_size: int = 20) -> list[Vehicle]:
110
- async with httpx.AsyncClient(base_url=self.base_url) as client:
111
- response = await client.get(
112
- "/vehicles",
113
- params={"page": page, "page_size": page_size},
114
- )
115
- response.raise_for_status()
116
- payload = response.json()
117
- return [Vehicle.model_validate(v) for v in payload["data"]]`,
118
-
119
- javascript: `class EventBus {
120
- #listeners = new Map();
121
- #wildcards = new Set();
122
-
123
- on(event, handler) {
124
- if (event === '*') {
125
- this.#wildcards.add(handler);
126
- return () => this.#wildcards.delete(handler);
127
- }
128
- if (!this.#listeners.has(event)) {
129
- this.#listeners.set(event, new Set());
130
- }
131
- this.#listeners.get(event).add(handler);
132
- return () => this.#listeners.get(event)?.delete(handler);
133
- }
134
-
135
- once(event, handler) {
136
- const unsub = this.on(event, (...args) => {
137
- unsub();
138
- handler(...args);
139
- });
140
- return unsub;
141
- }
142
-
143
- emit(event, payload) {
144
- this.#listeners.get(event)?.forEach(h => h(payload));
145
- this.#wildcards.forEach(h => h(event, payload));
146
- }
147
- }
148
-
149
- const bus = new EventBus();
150
- bus.on('*', (event, payload) => console.debug(\`[bus] \${event}\`, payload));
151
- bus.emit('vehicle:created', { id: 'v_001', make: 'BMW' });`,
152
-
153
- css: `:root {
154
- --radius-md: 0.5rem;
155
- --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
156
- --transition-base: 200ms cubic-bezier(0.4, 0, 0.2, 1);
157
- }
158
-
159
- .card {
160
- border-radius: var(--radius-md);
161
- border: 1px solid hsl(var(--border));
162
- background: hsl(var(--card));
163
- box-shadow: var(--shadow-sm);
164
- transition: box-shadow var(--transition-base);
165
-
166
- &:hover {
167
- box-shadow: var(--shadow-md);
168
- transform: translateY(-1px);
169
- }
170
- }
171
-
172
- @keyframes shimmer {
173
- from { background-position: -200% 0; }
174
- to { background-position: 200% 0; }
175
- }
176
-
177
- .card--skeleton .card-title {
178
- background: linear-gradient(90deg, hsl(var(--muted)) 25%, hsl(var(--muted-foreground) / 0.1) 50%, hsl(var(--muted)) 75%);
179
- background-size: 200% 100%;
180
- animation: shimmer 1.5s infinite;
181
- }`,
182
-
183
- html: `<!DOCTYPE html>
184
- <html lang="en">
185
- <head>
186
- <meta charset="UTF-8" />
187
- <title>Sample</title>
188
- </head>
189
- <body>
190
- <div id="root"></div>
191
- <script type="module" src="/entry.tsx"></script>
192
- </body>
193
- </html>`,
194
- };
195
-
196
- // ─── Stories ──────────────────────────────────────────────────────────────────
197
-
198
- /** Interactive — language, variant, compact, scrollIsolation all toggleable.
199
- * Replaces the old per-language / per-mode / per-flag stories which were
200
- * just this component with different defaults. */
201
- export const Interactive = () => {
202
- const [language] = useSelect('language', {
203
- options: ['typescript', 'javascript', 'python', 'css', 'html'] as const,
204
- defaultValue: 'typescript',
205
- label: 'Language',
206
- });
207
-
208
- const [variant] = useSelect('variant', {
209
- options: ['card', 'plain'] as const,
210
- defaultValue: 'card',
211
- label: 'Variant',
212
- description: 'card = full chrome, plain = bare highlighted code',
213
- });
214
-
215
- const [isCompact] = useBoolean('isCompact', {
216
- defaultValue: false,
217
- label: 'Compact',
218
- });
219
-
220
- const [scrollIsolation] = useBoolean('scrollIsolation', {
221
- defaultValue: true,
222
- label: 'Scroll isolation',
223
- description: 'Require click before the block captures scroll (card only)',
224
- });
225
-
226
- return (
227
- <PrettyCode
228
- data={LONG_CODE_SAMPLES[language]}
229
- language={language}
230
- variant={variant}
231
- isCompact={isCompact}
232
- scrollIsolation={scrollIsolation}
233
- />
234
- );
235
- };
236
-
237
- /** Variants — ``card`` vs ``plain`` side by side. The card has its own
238
- * border, hover toolbar, and can internally scroll when a ``maxLines``
239
- * cap is hit. The plain variant is chrome-less for embedding inside
240
- * an existing scroll container (e.g. a response panel), where the
241
- * parent surface owns chrome and scroll. */
242
- export const Variants = () => (
243
- <div className="grid grid-cols-1 lg:grid-cols-2 gap-4 max-w-6xl">
244
- <section className="space-y-2">
245
- <h3 className="text-sm font-semibold">variant=&quot;card&quot; (default)</h3>
246
- <p className="text-xs text-muted-foreground">
247
- Border, background, hover toolbar with Copy + language tag.
248
- </p>
249
- <PrettyCode data={LONG_CODE_SAMPLES.typescript} language="typescript" variant="card" />
250
- </section>
251
- <section className="space-y-2">
252
- <h3 className="text-sm font-semibold">variant=&quot;plain&quot;</h3>
253
- <p className="text-xs text-muted-foreground">
254
- Chrome-less. No border, no background, no toolbar. Flows into the
255
- parent scroll container.
256
- </p>
257
- <PrettyCode data={LONG_CODE_SAMPLES.typescript} language="typescript" variant="plain" />
258
- </section>
259
- </div>
260
- );
261
-
262
- /** LongCode — stress test for multi-column layouts and long snippets. */
263
- export const LongCode = () => (
264
- <div className="flex flex-col gap-6 max-w-4xl">
265
- <div className="h-96">
266
- <PrettyCode data={LONG_CODE_SAMPLES.typescript} language="typescript" maxLines={30} />
267
- </div>
268
- <div className="h-96">
269
- <PrettyCode data={LONG_CODE_SAMPLES.python} language="python" maxLines={30} />
270
- </div>
271
- <div className="h-96">
272
- <PrettyCode data={LONG_CODE_SAMPLES.javascript} language="javascript" maxLines={30} />
273
- </div>
274
- </div>
275
- );
276
-
277
- /** ScrollIsolation — both states side-by-side so the difference is
278
- * obvious without toggling. The isolation flag matters most when the
279
- * block is embedded in a scrolling page: without it, wheel-scrolling
280
- * anywhere over the block "captures" the wheel and the page itself
281
- * stops scrolling — click-to-unlock avoids that surprise. */
282
- export const ScrollIsolation = () => (
283
- <div className="flex flex-col gap-8 max-w-4xl">
284
- <section className="space-y-2">
285
- <h3 className="text-sm font-semibold">scrollIsolation=true (default)</h3>
286
- <p className="text-xs text-muted-foreground">
287
- Hover to see the hint; click to unlock wheel-scroll inside the block.
288
- </p>
289
- <div className="h-64">
290
- <PrettyCode data={LONG_CODE_SAMPLES.typescript} language="typescript" maxLines={15} scrollIsolation />
291
- </div>
292
- </section>
293
- <section className="space-y-2">
294
- <h3 className="text-sm font-semibold">scrollIsolation=false</h3>
295
- <p className="text-xs text-muted-foreground">
296
- Wheel-scroll captured immediately — use when the block has no
297
- surrounding page-level scroll to compete with.
298
- </p>
299
- <div className="h-64">
300
- <PrettyCode data={LONG_CODE_SAMPLES.typescript} language="typescript" maxLines={15} scrollIsolation={false} />
301
- </div>
302
- </section>
303
- </div>
304
- );
@@ -1,279 +0,0 @@
1
- 'use client';
2
-
3
- import { useState } from 'react';
4
- import { defineStory, useSelect } from '@djangocfg/playground';
5
- import { Button, Card, CardContent, CardHeader, CardTitle } from '@djangocfg/ui-core/components';
6
- import { Tour, useTour } from './index';
7
- import type { Tour as TourConfig } from './types';
8
-
9
- export default defineStory({
10
- title: 'Tools/Tour',
11
- component: Tour,
12
- description: 'Onboarding tours with spotlight highlighting and keyboard navigation.',
13
- });
14
-
15
- // Demo tours
16
- const demoTours: TourConfig[] = [
17
- {
18
- id: 'basic',
19
- steps: [
20
- {
21
- id: 'welcome',
22
- target: '[data-tour-step-id="header"]',
23
- title: 'Welcome to the Demo',
24
- content: 'This tour will show you the main features of this page.',
25
- position: 'bottom',
26
- },
27
- {
28
- id: 'sidebar',
29
- target: '[data-tour-step-id="sidebar"]',
30
- title: 'Navigation Sidebar',
31
- content: 'Use this sidebar to navigate between different sections.',
32
- position: 'right',
33
- },
34
- {
35
- id: 'content',
36
- target: '[data-tour-step-id="main-content"]',
37
- title: 'Main Content Area',
38
- content: 'This is where all your content will be displayed.',
39
- position: 'top',
40
- },
41
- {
42
- id: 'actions',
43
- target: '[data-tour-step-id="action-button"]',
44
- title: 'Quick Actions',
45
- content: 'Click here to perform quick actions.',
46
- position: 'left',
47
- },
48
- ],
49
- },
50
- {
51
- id: 'cards',
52
- defaults: {
53
- spotlightPadding: 12,
54
- },
55
- steps: [
56
- {
57
- id: 'step-1',
58
- target: '[data-tour-step-id="card-1"]',
59
- title: 'First Card',
60
- content: 'This card shows important information.',
61
- position: 'bottom',
62
- },
63
- {
64
- id: 'step-2',
65
- target: '[data-tour-step-id="card-2"]',
66
- title: 'Second Card',
67
- content: 'Another important card with different data.',
68
- position: 'bottom',
69
- },
70
- {
71
- id: 'step-3',
72
- target: '[data-tour-step-id="card-3"]',
73
- title: 'Third Card',
74
- content: 'The last card in this row.',
75
- position: 'bottom',
76
- nextLabel: 'Complete Tour',
77
- },
78
- ],
79
- },
80
- ];
81
-
82
- // Demo component with tour targets
83
- function DemoLayout() {
84
- const { start, isActive, currentStepIndex, totalSteps } = useTour();
85
-
86
- return (
87
- <div className="min-h-[500px] rounded-lg bg-muted/30 p-4">
88
- {/* Header */}
89
- <header
90
- data-tour-step-id="header"
91
- className="mb-4 rounded-lg bg-primary p-4 text-primary-foreground"
92
- >
93
- <div className="flex items-center justify-between">
94
- <h1 className="text-xl font-bold">Tour Demo</h1>
95
- <div className="flex gap-2">
96
- <Button
97
- variant="secondary"
98
- size="sm"
99
- onClick={() => start('basic')}
100
- disabled={isActive}
101
- >
102
- Basic Tour
103
- </Button>
104
- <Button
105
- variant="secondary"
106
- size="sm"
107
- onClick={() => start('cards')}
108
- disabled={isActive}
109
- >
110
- Cards Tour
111
- </Button>
112
- </div>
113
- </div>
114
- </header>
115
-
116
- <div className="flex gap-4">
117
- {/* Sidebar */}
118
- <aside
119
- data-tour-step-id="sidebar"
120
- className="w-48 rounded-lg bg-card p-4 shadow"
121
- >
122
- <nav className="space-y-2">
123
- <div className="rounded bg-muted p-2 text-sm">Dashboard</div>
124
- <div className="rounded p-2 text-sm hover:bg-muted">Settings</div>
125
- <div className="rounded p-2 text-sm hover:bg-muted">Profile</div>
126
- <div className="rounded p-2 text-sm hover:bg-muted">Help</div>
127
- </nav>
128
- </aside>
129
-
130
- {/* Main content */}
131
- <main className="flex-1 space-y-4">
132
- <div
133
- data-tour-step-id="main-content"
134
- className="rounded-lg bg-card p-6 shadow"
135
- >
136
- <h2 className="mb-4 text-lg font-semibold">Main Content</h2>
137
- <p className="text-muted-foreground">
138
- This is the main content area.
139
- {isActive && (
140
- <span className="ml-2 text-primary">
141
- (Step {currentStepIndex + 1} of {totalSteps})
142
- </span>
143
- )}
144
- </p>
145
-
146
- <Button data-tour-step-id="action-button" className="mt-4">
147
- Quick Action
148
- </Button>
149
- </div>
150
-
151
- {/* Cards row */}
152
- <div className="grid grid-cols-3 gap-4">
153
- <Card data-tour-step-id="card-1">
154
- <CardHeader>
155
- <CardTitle className="text-base">Card 1</CardTitle>
156
- </CardHeader>
157
- <CardContent>
158
- <p className="text-sm text-muted-foreground">First card content.</p>
159
- </CardContent>
160
- </Card>
161
-
162
- <Card data-tour-step-id="card-2">
163
- <CardHeader>
164
- <CardTitle className="text-base">Card 2</CardTitle>
165
- </CardHeader>
166
- <CardContent>
167
- <p className="text-sm text-muted-foreground">Second card content.</p>
168
- </CardContent>
169
- </Card>
170
-
171
- <Card data-tour-step-id="card-3">
172
- <CardHeader>
173
- <CardTitle className="text-base">Card 3</CardTitle>
174
- </CardHeader>
175
- <CardContent>
176
- <p className="text-sm text-muted-foreground">Third card content.</p>
177
- </CardContent>
178
- </Card>
179
- </div>
180
- </main>
181
- </div>
182
- </div>
183
- );
184
- }
185
-
186
- export const Interactive = () => {
187
- const [progressVariant] = useSelect('progressVariant', {
188
- options: ['dots', 'bar', 'fraction', 'none'] as const,
189
- defaultValue: 'dots',
190
- label: 'Progress Variant',
191
- });
192
-
193
- return (
194
- <Tour
195
- tours={demoTours}
196
- progressVariant={progressVariant}
197
- onStart={(id) => console.log('Tour started:', id)}
198
- onComplete={(id) => console.log('Tour completed:', id)}
199
- onSkip={(id, step) => console.log('Tour skipped:', id, 'at step', step)}
200
- onStepChange={(index, step, direction) =>
201
- console.log('Step:', index, step.id, direction)
202
- }
203
- >
204
- <DemoLayout />
205
- </Tour>
206
- );
207
- };
208
-
209
- export const WithProgressBar = () => (
210
- <Tour tours={demoTours} progressVariant="bar">
211
- <DemoLayout />
212
- </Tour>
213
- );
214
-
215
- export const WithFraction = () => (
216
- <Tour tours={demoTours} progressVariant="fraction">
217
- <DemoLayout />
218
- </Tour>
219
- );
220
-
221
- export const NoSpotlight = () => (
222
- <Tour tours={demoTours} spotlight={false}>
223
- <DemoLayout />
224
- </Tour>
225
- );
226
-
227
- export const CloseOnOverlayClick = () => (
228
- <Tour tours={demoTours} closeOnOverlayClick>
229
- <DemoLayout />
230
- </Tour>
231
- );
232
-
233
- export const NoSkipButton = () => (
234
- <Tour tours={demoTours} showSkipButton={false}>
235
- <DemoLayout />
236
- </Tour>
237
- );
238
-
239
- export const WithArrow = () => (
240
- <Tour tours={demoTours} showArrow>
241
- <DemoLayout />
242
- </Tour>
243
- );
244
-
245
- export const WithBlur = () => (
246
- <Tour tours={demoTours} spotlightBlur={4} spotlightOpacity={0.3}>
247
- <DemoLayout />
248
- </Tour>
249
- );
250
-
251
- export const WithArrowAndBlur = () => (
252
- <Tour tours={demoTours} showArrow spotlightBlur={3} spotlightOpacity={0.4}>
253
- <DemoLayout />
254
- </Tour>
255
- );
256
-
257
- export const WithPulseRing = () => (
258
- <Tour tours={demoTours} pulseRing>
259
- <DemoLayout />
260
- </Tour>
261
- );
262
-
263
- export const NoAnimation = () => (
264
- <Tour tours={demoTours} animated={false}>
265
- <DemoLayout />
266
- </Tour>
267
- );
268
-
269
- export const FullFeatured = () => (
270
- <Tour
271
- tours={demoTours}
272
- showArrow
273
- spotlightBlur={2}
274
- spotlightOpacity={0.4}
275
- pulseRing
276
- >
277
- <DemoLayout />
278
- </Tour>
279
- );