@situaction/traquiste-mobile 1.0.0-next.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 (168) hide show
  1. package/.eslintrc.js +5 -0
  2. package/.releaserc +12 -0
  3. package/CLAUDE.md +25 -0
  4. package/README.md +124 -0
  5. package/app.json +5 -0
  6. package/bitbucket-pipelines.yml +81 -0
  7. package/build/components/Button/Button.d.ts +31 -0
  8. package/build/components/Button/Button.d.ts.map +1 -0
  9. package/build/components/Button/Button.js +67 -0
  10. package/build/components/Button/Button.js.map +1 -0
  11. package/build/components/Button/Button.stories.d.ts +19 -0
  12. package/build/components/Button/Button.stories.d.ts.map +1 -0
  13. package/build/components/Button/Button.stories.js +95 -0
  14. package/build/components/Button/Button.stories.js.map +1 -0
  15. package/build/components/Button/index.d.ts +3 -0
  16. package/build/components/Button/index.d.ts.map +1 -0
  17. package/build/components/Button/index.js +2 -0
  18. package/build/components/Button/index.js.map +1 -0
  19. package/build/components/ButtonAction/ButtonAction.d.ts +21 -0
  20. package/build/components/ButtonAction/ButtonAction.d.ts.map +1 -0
  21. package/build/components/ButtonAction/ButtonAction.js +35 -0
  22. package/build/components/ButtonAction/ButtonAction.js.map +1 -0
  23. package/build/components/ButtonAction/ButtonAction.stories.d.ts +13 -0
  24. package/build/components/ButtonAction/ButtonAction.stories.d.ts.map +1 -0
  25. package/build/components/ButtonAction/ButtonAction.stories.js +51 -0
  26. package/build/components/ButtonAction/ButtonAction.stories.js.map +1 -0
  27. package/build/components/ButtonAction/index.d.ts +3 -0
  28. package/build/components/ButtonAction/index.d.ts.map +1 -0
  29. package/build/components/ButtonAction/index.js +2 -0
  30. package/build/components/ButtonAction/index.js.map +1 -0
  31. package/build/components/ButtonMap/ButtonMap.d.ts +18 -0
  32. package/build/components/ButtonMap/ButtonMap.d.ts.map +1 -0
  33. package/build/components/ButtonMap/ButtonMap.js +17 -0
  34. package/build/components/ButtonMap/ButtonMap.js.map +1 -0
  35. package/build/components/ButtonMap/ButtonMap.stories.d.ts +12 -0
  36. package/build/components/ButtonMap/ButtonMap.stories.d.ts.map +1 -0
  37. package/build/components/ButtonMap/ButtonMap.stories.js +36 -0
  38. package/build/components/ButtonMap/ButtonMap.stories.js.map +1 -0
  39. package/build/components/ButtonMap/index.d.ts +3 -0
  40. package/build/components/ButtonMap/index.d.ts.map +1 -0
  41. package/build/components/ButtonMap/index.js +2 -0
  42. package/build/components/ButtonMap/index.js.map +1 -0
  43. package/build/components/ButtonMenu/ButtonMenu.d.ts +21 -0
  44. package/build/components/ButtonMenu/ButtonMenu.d.ts.map +1 -0
  45. package/build/components/ButtonMenu/ButtonMenu.js +56 -0
  46. package/build/components/ButtonMenu/ButtonMenu.js.map +1 -0
  47. package/build/components/ButtonMenu/ButtonMenu.stories.d.ts +15 -0
  48. package/build/components/ButtonMenu/ButtonMenu.stories.d.ts.map +1 -0
  49. package/build/components/ButtonMenu/ButtonMenu.stories.js +52 -0
  50. package/build/components/ButtonMenu/ButtonMenu.stories.js.map +1 -0
  51. package/build/components/ButtonMenu/index.d.ts +3 -0
  52. package/build/components/ButtonMenu/index.d.ts.map +1 -0
  53. package/build/components/ButtonMenu/index.js +2 -0
  54. package/build/components/ButtonMenu/index.js.map +1 -0
  55. package/build/components/FilterChip/FilterChip.d.ts +28 -0
  56. package/build/components/FilterChip/FilterChip.d.ts.map +1 -0
  57. package/build/components/FilterChip/FilterChip.js +66 -0
  58. package/build/components/FilterChip/FilterChip.js.map +1 -0
  59. package/build/components/FilterChip/FilterChip.stories.d.ts +15 -0
  60. package/build/components/FilterChip/FilterChip.stories.d.ts.map +1 -0
  61. package/build/components/FilterChip/FilterChip.stories.js +55 -0
  62. package/build/components/FilterChip/FilterChip.stories.js.map +1 -0
  63. package/build/components/FilterChip/index.d.ts +3 -0
  64. package/build/components/FilterChip/index.d.ts.map +1 -0
  65. package/build/components/FilterChip/index.js +2 -0
  66. package/build/components/FilterChip/index.js.map +1 -0
  67. package/build/constants/colors.d.ts +2 -0
  68. package/build/constants/colors.d.ts.map +1 -0
  69. package/build/constants/colors.js +3 -0
  70. package/build/constants/colors.js.map +1 -0
  71. package/build/context/ThemeContext.d.ts +30 -0
  72. package/build/context/ThemeContext.d.ts.map +1 -0
  73. package/build/context/ThemeContext.js +34 -0
  74. package/build/context/ThemeContext.js.map +1 -0
  75. package/build/index.d.ts +7 -0
  76. package/build/index.d.ts.map +1 -0
  77. package/build/index.js +8 -0
  78. package/build/index.js.map +1 -0
  79. package/build/theme/index.d.ts +5 -0
  80. package/build/theme/index.d.ts.map +1 -0
  81. package/build/theme/index.js +6 -0
  82. package/build/theme/index.js.map +1 -0
  83. package/build/theme/tokens/dark.d.ts +8 -0
  84. package/build/theme/tokens/dark.d.ts.map +1 -0
  85. package/build/theme/tokens/dark.js +146 -0
  86. package/build/theme/tokens/dark.js.map +1 -0
  87. package/build/theme/tokens/light.d.ts +8 -0
  88. package/build/theme/tokens/light.d.ts.map +1 -0
  89. package/build/theme/tokens/light.js +152 -0
  90. package/build/theme/tokens/light.js.map +1 -0
  91. package/build/theme/type.d.ts +75 -0
  92. package/build/theme/type.d.ts.map +1 -0
  93. package/build/theme/type.js +7 -0
  94. package/build/theme/type.js.map +1 -0
  95. package/docs/README.md +73 -0
  96. package/docs/eslint.config.js +22 -0
  97. package/docs/index.html +16 -0
  98. package/docs/package-lock.json +5578 -0
  99. package/docs/package.json +37 -0
  100. package/docs/public/favicon.svg +1 -0
  101. package/docs/public/icons.svg +24 -0
  102. package/docs/src/App.css +184 -0
  103. package/docs/src/App.tsx +38 -0
  104. package/docs/src/assets/hero.png +0 -0
  105. package/docs/src/assets/react.svg +1 -0
  106. package/docs/src/assets/vite.svg +1 -0
  107. package/docs/src/components/Layout.tsx +108 -0
  108. package/docs/src/components/LiveEditor.tsx +294 -0
  109. package/docs/src/components/PropsTable.tsx +101 -0
  110. package/docs/src/components/Sidebar.tsx +103 -0
  111. package/docs/src/components/TableOfContents.tsx +75 -0
  112. package/docs/src/context/ColorModeContext.tsx +34 -0
  113. package/docs/src/index.css +76 -0
  114. package/docs/src/lib/createComponentPage.tsx +270 -0
  115. package/docs/src/main.tsx +13 -0
  116. package/docs/src/pages/Examples.tsx +273 -0
  117. package/docs/src/pages/Home.tsx +70 -0
  118. package/docs/src/pages/components/button-action.ts +88 -0
  119. package/docs/src/pages/components/button-map.ts +67 -0
  120. package/docs/src/pages/components/button-menu.ts +90 -0
  121. package/docs/src/pages/components/button.ts +158 -0
  122. package/docs/src/pages/components/filter-chip.ts +109 -0
  123. package/docs/tsconfig.app.json +32 -0
  124. package/docs/tsconfig.json +7 -0
  125. package/docs/tsconfig.node.json +24 -0
  126. package/docs/vite.config.ts +18 -0
  127. package/ios/.xcode.env +11 -0
  128. package/ios/Podfile +63 -0
  129. package/ios/Podfile.properties.json +4 -0
  130. package/ios/situactiontraquistemobile/AppDelegate.swift +69 -0
  131. package/ios/situactiontraquistemobile/Images.xcassets/AppIcon.appiconset/Contents.json +13 -0
  132. package/ios/situactiontraquistemobile/Images.xcassets/Contents.json +6 -0
  133. package/ios/situactiontraquistemobile/Images.xcassets/SplashScreenLegacy.imageset/Contents.json +21 -0
  134. package/ios/situactiontraquistemobile/Images.xcassets/SplashScreenLegacy.imageset/SplashScreenLegacy.png +0 -0
  135. package/ios/situactiontraquistemobile/Info.plist +53 -0
  136. package/ios/situactiontraquistemobile/SplashScreen.storyboard +47 -0
  137. package/ios/situactiontraquistemobile/Supporting/Expo.plist +6 -0
  138. package/ios/situactiontraquistemobile/situactiontraquistemobile-Bridging-Header.h +3 -0
  139. package/ios/situactiontraquistemobile.xcodeproj/project.pbxproj +432 -0
  140. package/ios/situactiontraquistemobile.xcodeproj/xcshareddata/xcschemes/situactiontraquistemobile.xcscheme +88 -0
  141. package/jest.config.js +197 -0
  142. package/package.json +90 -0
  143. package/src/components/.gitkeep +0 -0
  144. package/src/components/Button/Button.stories.tsx +123 -0
  145. package/src/components/Button/Button.tsx +128 -0
  146. package/src/components/Button/index.ts +2 -0
  147. package/src/components/ButtonAction/ButtonAction.stories.tsx +67 -0
  148. package/src/components/ButtonAction/ButtonAction.tsx +67 -0
  149. package/src/components/ButtonAction/index.ts +2 -0
  150. package/src/components/ButtonMap/ButtonMap.stories.tsx +49 -0
  151. package/src/components/ButtonMap/ButtonMap.tsx +40 -0
  152. package/src/components/ButtonMap/index.ts +2 -0
  153. package/src/components/ButtonMenu/ButtonMenu.stories.tsx +68 -0
  154. package/src/components/ButtonMenu/ButtonMenu.tsx +97 -0
  155. package/src/components/ButtonMenu/index.ts +2 -0
  156. package/src/components/FilterChip/FilterChip.stories.tsx +71 -0
  157. package/src/components/FilterChip/FilterChip.tsx +124 -0
  158. package/src/components/FilterChip/index.ts +2 -0
  159. package/src/constants/colors.ts +2 -0
  160. package/src/context/ThemeContext.tsx +84 -0
  161. package/src/index.ts +23 -0
  162. package/src/theme/index.ts +20 -0
  163. package/src/theme/tokens/dark.ts +160 -0
  164. package/src/theme/tokens/light.ts +166 -0
  165. package/src/theme/type.ts +122 -0
  166. package/src/types/.gitkeep +0 -0
  167. package/src/utils/.gitkeep +0 -0
  168. package/tsconfig.json +9 -0
@@ -0,0 +1,294 @@
1
+ /** Demo card: live-editable preview powered by @babel/standalone */
2
+ import { useState, useEffect, useRef, useCallback } from 'react'
3
+ import type { ReactNode } from 'react'
4
+ import ReactForScope from 'react'
5
+ import Editor from '@monaco-editor/react'
6
+ import { ThemeProvider } from '@situaction/traquiste-mobile'
7
+ import { useColorMode } from '../context/ColorModeContext'
8
+
9
+ type BabelModule = typeof import('@babel/standalone')
10
+
11
+ let babelCache: BabelModule | null = null
12
+ let babelLoading: Promise<BabelModule> | null = null
13
+
14
+ function loadBabel(): Promise<BabelModule> {
15
+ if (babelCache) return Promise.resolve(babelCache)
16
+ if (!babelLoading) {
17
+ babelLoading = import('@babel/standalone').then((m) => {
18
+ babelCache = m
19
+ return m
20
+ })
21
+ }
22
+ return babelLoading
23
+ }
24
+
25
+ interface LiveEditorProps {
26
+ title: string
27
+ initialCode: string
28
+ scope: Record<string, unknown>
29
+ }
30
+
31
+ const S = {
32
+ card: {
33
+ border: '1px solid var(--docs-border)',
34
+ borderRadius: 12,
35
+ overflow: 'hidden',
36
+ marginBottom: 20,
37
+ boxShadow: 'var(--docs-shadow-sm)',
38
+ },
39
+ header: {
40
+ padding: '10px 14px',
41
+ borderBottom: '1px solid var(--docs-border)',
42
+ backgroundColor: 'var(--docs-surface)',
43
+ display: 'flex',
44
+ justifyContent: 'space-between' as const,
45
+ alignItems: 'center' as const,
46
+ gap: 8,
47
+ },
48
+ title: {
49
+ fontSize: 12,
50
+ fontWeight: 600,
51
+ color: 'var(--docs-text-muted)',
52
+ letterSpacing: '0.03em',
53
+ textTransform: 'uppercase' as const,
54
+ flex: 1,
55
+ },
56
+ actions: { display: 'flex', gap: 6, alignItems: 'center' as const },
57
+ btn: {
58
+ fontSize: 12,
59
+ fontWeight: 500,
60
+ padding: '4px 10px',
61
+ border: '1px solid var(--docs-border)',
62
+ borderRadius: 6,
63
+ cursor: 'pointer',
64
+ backgroundColor: 'var(--docs-bg)',
65
+ color: 'var(--docs-text-secondary)',
66
+ fontFamily: 'inherit',
67
+ whiteSpace: 'nowrap' as const,
68
+ },
69
+ btnCopied: {
70
+ color: 'var(--docs-badge-default-color)',
71
+ borderColor: 'var(--docs-badge-default-bg)',
72
+ backgroundColor: 'var(--docs-badge-default-bg)',
73
+ },
74
+ loading: { fontSize: 13, color: 'var(--docs-text-disabled)', fontStyle: 'italic' },
75
+ error: {
76
+ color: 'var(--docs-badge-name-color)',
77
+ fontSize: 12,
78
+ fontFamily: 'ui-monospace, Consolas, monospace',
79
+ whiteSpace: 'pre-wrap' as const,
80
+ margin: 0,
81
+ maxWidth: '100%',
82
+ overflowX: 'auto' as const,
83
+ lineHeight: 1.6,
84
+ },
85
+ }
86
+
87
+ /** 6-dot drag indicator (2 cols × 3 rows) */
88
+ function DragDots({ active }: { active: boolean }) {
89
+ const color = active ? 'var(--docs-accent)' : 'var(--docs-text-muted)'
90
+ const dot = { width: 2, height: 2, borderRadius: '50%', backgroundColor: color } as const
91
+ return (
92
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
93
+ {[0, 1, 2].map((i) => (
94
+ <div key={i} style={{ display: 'flex', gap: 3 }}>
95
+ <div style={dot} /><div style={dot} />
96
+ </div>
97
+ ))}
98
+ </div>
99
+ )
100
+ }
101
+
102
+ export default function LiveEditor({ title, initialCode, scope }: LiveEditorProps) {
103
+ const [code, setCode] = useState(initialCode)
104
+ const [showCode, setShowCode] = useState(false)
105
+ const [preview, setPreview] = useState<ReactNode>(null)
106
+ const [error, setError] = useState<string | null>(null)
107
+ const [copied, setCopied] = useState(false)
108
+ const [previewWidth, setPreviewWidth] = useState<number | null>(null)
109
+ const [isDragging, setIsDragging] = useState(false)
110
+ const { mode } = useColorMode()
111
+
112
+ const wrapperRef = useRef<HTMLDivElement>(null)
113
+ const scopeRef = useRef(scope)
114
+ scopeRef.current = scope
115
+ const debounceRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
116
+
117
+ function handleMouseDown(e: React.MouseEvent) {
118
+ e.preventDefault()
119
+ const startX = e.clientX
120
+ const startWidth = wrapperRef.current?.offsetWidth ?? 600
121
+ setIsDragging(true)
122
+
123
+ function onMouseMove(ev: MouseEvent) {
124
+ const next = Math.max(280, Math.min(1000, startWidth + (ev.clientX - startX)))
125
+ setPreviewWidth(next)
126
+ }
127
+ function onMouseUp() {
128
+ setIsDragging(false)
129
+ window.removeEventListener('mousemove', onMouseMove)
130
+ window.removeEventListener('mouseup', onMouseUp)
131
+ }
132
+ window.addEventListener('mousemove', onMouseMove)
133
+ window.addEventListener('mouseup', onMouseUp)
134
+ }
135
+
136
+ const evalCode = useCallback((src: string) => {
137
+ loadBabel()
138
+ .then((Babel) => {
139
+ const fullScope = { React: ReactForScope, ...scopeRef.current }
140
+ const keys = Object.keys(fullScope)
141
+ const wrapped = `const { ${keys.join(', ')} } = __scope__;\nreturn (${src});`
142
+ const result = Babel.transform(wrapped, {
143
+ presets: ['react'],
144
+ filename: 'preview.jsx',
145
+ parserOpts: { allowReturnOutsideFunction: true },
146
+ })
147
+ if (!result.code) throw new Error('Transpilation failed')
148
+ // eslint-disable-next-line no-new-func
149
+ const fn = new Function('__scope__', result.code)
150
+ setPreview(fn(fullScope) as ReactNode)
151
+ setError(null)
152
+ })
153
+ .catch((e: unknown) => {
154
+ setError(e instanceof Error ? e.message : String(e))
155
+ })
156
+ }, [])
157
+
158
+ // eslint-disable-next-line react-hooks/exhaustive-deps
159
+ useEffect(() => { evalCode(initialCode) }, [])
160
+
161
+ function handleChange(value: string | undefined) {
162
+ const src = value ?? ''
163
+ setCode(src)
164
+ clearTimeout(debounceRef.current)
165
+ debounceRef.current = setTimeout(() => evalCode(src), 300)
166
+ }
167
+
168
+ function handleCopy() {
169
+ navigator.clipboard.writeText(code).then(() => {
170
+ setCopied(true)
171
+ setTimeout(() => setCopied(false), 2000)
172
+ })
173
+ }
174
+
175
+ function handleReset() {
176
+ setCode(initialCode)
177
+ evalCode(initialCode)
178
+ }
179
+
180
+ const editorHeight = Math.max(80, code.split('\n').length * 19 + 24)
181
+
182
+ return (
183
+ <div style={{ ...S.card, userSelect: isDragging ? 'none' : undefined }}>
184
+ <div style={S.header}>
185
+ <span style={S.title}>{title}</span>
186
+ <div style={S.actions}>
187
+ {code !== initialCode && (
188
+ <button style={S.btn} onClick={handleReset}>↺ Reset</button>
189
+ )}
190
+ <button style={{ ...S.btn, ...(copied ? S.btnCopied : {}) }} onClick={handleCopy}>
191
+ {copied ? '✓ Copié' : 'Copier'}
192
+ </button>
193
+ <button style={S.btn} onClick={() => setShowCode((v) => !v)}>
194
+ {showCode ? 'Masquer' : '<> Code'}
195
+ </button>
196
+ </div>
197
+ </div>
198
+
199
+ {/* Preview zone */}
200
+ <div style={{
201
+ backgroundColor: 'var(--docs-bg-subtle)',
202
+ display: 'flex',
203
+ justifyContent: 'center',
204
+ minHeight: 140,
205
+ position: 'relative',
206
+ transition: 'background 0.2s',
207
+ }}>
208
+ {/* Resizable wrapper: content + handle side by side */}
209
+ <div
210
+ ref={wrapperRef}
211
+ style={{
212
+ display: 'flex',
213
+ alignItems: 'stretch',
214
+ width: previewWidth ? `${previewWidth}px` : '100%',
215
+ maxWidth: '100%',
216
+ }}
217
+ >
218
+ {/* Content */}
219
+ <div style={{
220
+ flex: 1,
221
+ padding: '48px 32px',
222
+ display: 'flex',
223
+ alignItems: 'center',
224
+ justifyContent: 'center',
225
+ }}>
226
+ <ThemeProvider colorScheme={mode}>
227
+ {error
228
+ ? <pre style={S.error}>{error}</pre>
229
+ : preview ?? <span style={S.loading}>Chargement…</span>}
230
+ </ThemeProvider>
231
+ </div>
232
+
233
+ {/* Drag handle */}
234
+ <div
235
+ onMouseDown={handleMouseDown}
236
+ onDoubleClick={() => setPreviewWidth(null)}
237
+ title="Glisser pour redimensionner · Double-clic pour réinitialiser"
238
+ style={{
239
+ width: 20,
240
+ flexShrink: 0,
241
+ cursor: 'ew-resize',
242
+ display: 'flex',
243
+ alignItems: 'center',
244
+ justifyContent: 'center',
245
+ borderLeft: '1px solid var(--docs-border)',
246
+ backgroundColor: isDragging ? 'var(--docs-accent-subtle)' : 'transparent',
247
+ transition: 'background 0.15s',
248
+ }}
249
+ >
250
+ <DragDots active={isDragging} />
251
+ </div>
252
+ </div>
253
+
254
+ {/* Width badge */}
255
+ {previewWidth && (
256
+ <span style={{
257
+ position: 'absolute',
258
+ bottom: 6,
259
+ left: '50%',
260
+ transform: 'translateX(-50%)',
261
+ fontSize: 11,
262
+ fontFamily: 'ui-monospace, Consolas, monospace',
263
+ color: 'var(--docs-text-muted)',
264
+ backgroundColor: 'var(--docs-surface)',
265
+ border: '1px solid var(--docs-border)',
266
+ borderRadius: 4,
267
+ padding: '2px 6px',
268
+ pointerEvents: 'none' as const,
269
+ }}>
270
+ {previewWidth}px
271
+ </span>
272
+ )}
273
+ </div>
274
+
275
+ {showCode && (
276
+ <Editor
277
+ height={editorHeight}
278
+ language="jsx"
279
+ theme="vs-dark"
280
+ value={code}
281
+ onChange={handleChange}
282
+ options={{
283
+ minimap: { enabled: false },
284
+ scrollBeyondLastLine: false,
285
+ fontSize: 13,
286
+ lineNumbers: 'off',
287
+ padding: { top: 14, bottom: 14 },
288
+ fontFamily: 'ui-monospace, Consolas, monospace',
289
+ }}
290
+ />
291
+ )}
292
+ </div>
293
+ )
294
+ }
@@ -0,0 +1,101 @@
1
+ /** Typed props API table — one row per prop */
2
+ export interface PropDef {
3
+ name: string
4
+ type: string
5
+ required: boolean
6
+ default?: string
7
+ description: string
8
+ }
9
+
10
+ interface PropsTableProps {
11
+ props: PropDef[]
12
+ }
13
+
14
+ const S = {
15
+ table: {
16
+ width: '100%',
17
+ borderCollapse: 'collapse' as const,
18
+ fontSize: 14,
19
+ marginTop: 16,
20
+ },
21
+ th: {
22
+ textAlign: 'left' as const,
23
+ padding: '10px 12px',
24
+ backgroundColor: 'var(--docs-surface)',
25
+ borderBottom: '2px solid var(--docs-border)',
26
+ fontWeight: 600,
27
+ color: 'var(--docs-text-primary)',
28
+ fontSize: 13,
29
+ },
30
+ td: {
31
+ padding: '10px 12px',
32
+ borderBottom: '1px solid var(--docs-border)',
33
+ verticalAlign: 'top' as const,
34
+ color: 'var(--docs-text-secondary)',
35
+ lineHeight: 1.5,
36
+ },
37
+ nameMono: {
38
+ fontFamily: 'ui-monospace, Consolas, monospace',
39
+ fontSize: 13,
40
+ color: 'var(--docs-badge-name-color)',
41
+ backgroundColor: 'var(--docs-badge-name-bg)',
42
+ padding: '2px 5px',
43
+ borderRadius: 3,
44
+ },
45
+ typeMono: {
46
+ fontFamily: 'ui-monospace, Consolas, monospace',
47
+ fontSize: 13,
48
+ color: 'var(--docs-badge-type-color)',
49
+ backgroundColor: 'var(--docs-badge-type-bg)',
50
+ padding: '2px 5px',
51
+ borderRadius: 3,
52
+ },
53
+ defaultMono: {
54
+ fontFamily: 'ui-monospace, Consolas, monospace',
55
+ fontSize: 13,
56
+ color: 'var(--docs-badge-default-color)',
57
+ backgroundColor: 'var(--docs-badge-default-bg)',
58
+ padding: '2px 5px',
59
+ borderRadius: 3,
60
+ },
61
+ required: { color: 'var(--docs-badge-name-color)', fontSize: 12, marginLeft: 4 },
62
+ empty: { color: 'var(--docs-text-disabled)' },
63
+ }
64
+
65
+ const HEADERS = ['Nom', 'Type', 'Défaut', 'Description']
66
+
67
+ export default function PropsTable({ props }: PropsTableProps) {
68
+ return (
69
+ <table style={S.table}>
70
+ <thead>
71
+ <tr>
72
+ {HEADERS.map((h) => (
73
+ <th key={h} style={S.th}>{h}</th>
74
+ ))}
75
+ </tr>
76
+ </thead>
77
+ <tbody>
78
+ {props.map((prop, i) => (
79
+ <tr
80
+ key={prop.name}
81
+ style={{ backgroundColor: i % 2 === 0 ? 'var(--docs-bg)' : 'var(--docs-bg-subtle)' }}
82
+ >
83
+ <td style={S.td}>
84
+ <code style={S.nameMono}>{prop.name}</code>
85
+ {prop.required && <span style={S.required}>*</span>}
86
+ </td>
87
+ <td style={S.td}>
88
+ <code style={S.typeMono}>{prop.type}</code>
89
+ </td>
90
+ <td style={S.td}>
91
+ {prop.default
92
+ ? <code style={S.defaultMono}>{prop.default}</code>
93
+ : <span style={S.empty}>—</span>}
94
+ </td>
95
+ <td style={S.td}>{prop.description}</td>
96
+ </tr>
97
+ ))}
98
+ </tbody>
99
+ </table>
100
+ )
101
+ }
@@ -0,0 +1,103 @@
1
+ /** Navigation sidebar — always dark, pill-style active states */
2
+ import { useState } from 'react'
3
+ import { NavLink } from 'react-router-dom'
4
+
5
+ const NAV = [
6
+ {
7
+ group: 'Général',
8
+ items: [{ label: 'Exemples', path: '/examples' }],
9
+ },
10
+ {
11
+ group: 'Composants',
12
+ items: [
13
+ { label: 'Button', path: '/components/button' },
14
+ { label: 'ButtonMenu', path: '/components/button-menu' },
15
+ { label: 'ButtonAction', path: '/components/button-action' },
16
+ { label: 'ButtonMap', path: '/components/button-map' },
17
+ { label: 'FilterChip', path: '/components/filter-chip' },
18
+ ],
19
+ },
20
+ ]
21
+
22
+ const S = {
23
+ container: {
24
+ padding: '20px 0 24px',
25
+ height: '100%',
26
+ display: 'flex',
27
+ flexDirection: 'column' as const,
28
+ },
29
+ brand: {
30
+ display: 'block',
31
+ color: '#fff',
32
+ fontSize: 15,
33
+ fontWeight: 700,
34
+ padding: '0 16px 16px',
35
+ marginBottom: 8,
36
+ textDecoration: 'none',
37
+ letterSpacing: '-0.01em',
38
+ borderBottom: '1px solid rgba(255,255,255,0.08)',
39
+ },
40
+ brandSub: {
41
+ display: 'block',
42
+ fontSize: 11,
43
+ color: 'rgba(255,255,255,0.4)',
44
+ fontWeight: 400,
45
+ marginTop: 2,
46
+ },
47
+ group: { marginTop: 16, paddingBottom: 8 },
48
+ groupLabel: {
49
+ display: 'block',
50
+ color: 'rgba(255,255,255,0.3)',
51
+ fontSize: 10,
52
+ fontWeight: 600,
53
+ letterSpacing: '0.1em',
54
+ textTransform: 'uppercase' as const,
55
+ padding: '0 16px 6px',
56
+ },
57
+ link: (active: boolean, hovered: boolean): React.CSSProperties => ({
58
+ display: 'block',
59
+ padding: '6px 12px',
60
+ margin: '1px 8px',
61
+ color: active ? '#fff' : hovered ? 'rgba(255,255,255,0.85)' : 'rgba(255,255,255,0.55)',
62
+ textDecoration: 'none',
63
+ fontSize: 14,
64
+ fontWeight: active ? 500 : 400,
65
+ borderRadius: 6,
66
+ backgroundColor: active
67
+ ? 'rgba(255,255,255,0.1)'
68
+ : hovered
69
+ ? 'rgba(255,255,255,0.05)'
70
+ : 'transparent',
71
+ transition: 'background 0.15s, color 0.15s',
72
+ }),
73
+ }
74
+
75
+ export default function Sidebar() {
76
+ const [hovered, setHovered] = useState<string | null>(null)
77
+
78
+ return (
79
+ <div style={S.container}>
80
+ <NavLink to="/" style={S.brand}>
81
+ traquiste-mobile
82
+ <span style={S.brandSub}>by Situaction</span>
83
+ </NavLink>
84
+
85
+ {NAV.map(({ group, items }) => (
86
+ <div key={group} style={S.group}>
87
+ <span style={S.groupLabel}>{group}</span>
88
+ {items.map(({ label, path }) => (
89
+ <NavLink
90
+ key={path}
91
+ to={path}
92
+ style={({ isActive }) => S.link(isActive, hovered === path)}
93
+ onMouseEnter={() => setHovered(path)}
94
+ onMouseLeave={() => setHovered(null)}
95
+ >
96
+ {label}
97
+ </NavLink>
98
+ ))}
99
+ </div>
100
+ ))}
101
+ </div>
102
+ )
103
+ }
@@ -0,0 +1,75 @@
1
+ /** Sticky right-side table of contents with active section tracking */
2
+ import { useState, useEffect } from 'react'
3
+
4
+ export interface TocItem {
5
+ id: string
6
+ label: string
7
+ }
8
+
9
+ interface TableOfContentsProps {
10
+ items: TocItem[]
11
+ }
12
+
13
+ const S = {
14
+ container: {
15
+ width: 200,
16
+ flexShrink: 0,
17
+ position: 'sticky' as const,
18
+ top: 80,
19
+ alignSelf: 'flex-start' as const,
20
+ },
21
+ heading: {
22
+ fontSize: 11,
23
+ fontWeight: 700,
24
+ letterSpacing: '0.08em',
25
+ textTransform: 'uppercase' as const,
26
+ color: 'var(--docs-text-muted)',
27
+ marginBottom: 12,
28
+ },
29
+ link: (active: boolean): React.CSSProperties => ({
30
+ display: 'block',
31
+ fontSize: 13,
32
+ padding: '4px 0 4px 12px',
33
+ color: active ? 'var(--docs-accent)' : 'var(--docs-text-muted)',
34
+ textDecoration: 'none',
35
+ borderLeft: `2px solid ${active ? 'var(--docs-accent)' : 'var(--docs-border)'}`,
36
+ marginBottom: 2,
37
+ fontWeight: active ? 600 : 400,
38
+ transition: 'color 0.15s, border-color 0.15s',
39
+ }),
40
+ }
41
+
42
+ export default function TableOfContents({ items }: TableOfContentsProps) {
43
+ const [activeId, setActiveId] = useState(items[0]?.id ?? '')
44
+
45
+ useEffect(() => {
46
+ const observer = new IntersectionObserver(
47
+ (entries) => {
48
+ const visible = entries.filter((e) => e.isIntersecting)
49
+ if (visible.length > 0) setActiveId(visible[0].target.id)
50
+ },
51
+ { rootMargin: '-10% 0% -70% 0%', threshold: 0 },
52
+ )
53
+ items.forEach(({ id }) => {
54
+ const el = document.getElementById(id)
55
+ if (el) observer.observe(el)
56
+ })
57
+ return () => observer.disconnect()
58
+ }, [items])
59
+
60
+ return (
61
+ <nav style={S.container}>
62
+ <p style={S.heading}>Sur cette page</p>
63
+ {items.map(({ id, label }) => (
64
+ <a
65
+ key={id}
66
+ href={`#${id}`}
67
+ style={S.link(activeId === id)}
68
+ onClick={() => setActiveId(id)}
69
+ >
70
+ {label}
71
+ </a>
72
+ ))}
73
+ </nav>
74
+ )
75
+ }
@@ -0,0 +1,34 @@
1
+ /** Docs-level global color mode state — syncs with data-theme on <html> */
2
+ import { createContext, useContext, useState, useEffect } from 'react'
3
+ import type { ReactNode } from 'react'
4
+ import type { ColorMode } from '@situaction/traquiste-mobile'
5
+
6
+ interface ColorModeContextValue {
7
+ mode: ColorMode
8
+ toggle: () => void
9
+ }
10
+
11
+ const ColorModeContext = createContext<ColorModeContextValue>({
12
+ mode: 'light',
13
+ toggle: () => {},
14
+ })
15
+
16
+ export function ColorModeProvider({ children }: { children: ReactNode }) {
17
+ const [mode, setMode] = useState<ColorMode>('light')
18
+
19
+ useEffect(() => {
20
+ document.documentElement.setAttribute('data-theme', mode)
21
+ }, [mode])
22
+
23
+ const toggle = () => setMode((m) => (m === 'light' ? 'dark' : 'light'))
24
+
25
+ return (
26
+ <ColorModeContext.Provider value={{ mode, toggle }}>
27
+ {children}
28
+ </ColorModeContext.Provider>
29
+ )
30
+ }
31
+
32
+ export function useColorMode(): ColorModeContextValue {
33
+ return useContext(ColorModeContext)
34
+ }
@@ -0,0 +1,76 @@
1
+ *, *::before, *::after {
2
+ box-sizing: border-box;
3
+ margin: 0;
4
+ padding: 0;
5
+ }
6
+
7
+ /* ===== Light theme (default) ===== */
8
+ :root {
9
+ --docs-bg: #ffffff;
10
+ --docs-bg-subtle: #f9fafb;
11
+ --docs-surface: #f4f4f5;
12
+ --docs-border: #e4e4e7;
13
+
14
+ --docs-text-primary: #18181b;
15
+ --docs-text-secondary: #52525b;
16
+ --docs-text-muted: #a1a1aa;
17
+ --docs-text-disabled: #d4d4d8;
18
+
19
+ --docs-accent: #0072E5;
20
+ --docs-accent-subtle: rgba(0, 114, 229, 0.08);
21
+
22
+ --docs-shadow-sm: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.04);
23
+ --docs-shadow-md: 0 4px 12px rgba(0,0,0,0.08), 0 2px 4px rgba(0,0,0,0.04);
24
+
25
+ --docs-badge-name-color: #dc2626;
26
+ --docs-badge-name-bg: #fef2f2;
27
+ --docs-badge-type-color: #2563eb;
28
+ --docs-badge-type-bg: #eff6ff;
29
+ --docs-badge-default-color: #16a34a;
30
+ --docs-badge-default-bg: #f0fdf4;
31
+ }
32
+
33
+ /* ===== Dark theme ===== */
34
+ [data-theme="dark"] {
35
+ --docs-bg: #09090b;
36
+ --docs-bg-subtle: #18181b;
37
+ --docs-surface: #27272a;
38
+ --docs-border: #3f3f46;
39
+
40
+ --docs-text-primary: #fafafa;
41
+ --docs-text-secondary: #a1a1aa;
42
+ --docs-text-muted: #71717a;
43
+ --docs-text-disabled: #52525b;
44
+
45
+ --docs-accent: #60a5fa;
46
+ --docs-accent-subtle: rgba(96, 165, 250, 0.1);
47
+
48
+ --docs-shadow-sm: 0 1px 3px rgba(0,0,0,0.4), 0 1px 2px rgba(0,0,0,0.3);
49
+ --docs-shadow-md: 0 4px 12px rgba(0,0,0,0.4), 0 2px 4px rgba(0,0,0,0.3);
50
+
51
+ --docs-badge-name-color: #fca5a5;
52
+ --docs-badge-name-bg: #450a0a;
53
+ --docs-badge-type-color: #93c5fd;
54
+ --docs-badge-type-bg: #0c1a2e;
55
+ --docs-badge-default-color: #86efac;
56
+ --docs-badge-default-bg: #052e16;
57
+ }
58
+
59
+ html {
60
+ scroll-behavior: smooth;
61
+ }
62
+
63
+ body {
64
+ font-family: 'Urbanist', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
65
+ font-size: 15px;
66
+ line-height: 1.6;
67
+ -webkit-font-smoothing: antialiased;
68
+ -moz-osx-font-smoothing: grayscale;
69
+ background: var(--docs-bg);
70
+ color: var(--docs-text-primary);
71
+ transition: background 0.2s, color 0.2s;
72
+ }
73
+
74
+ #root {
75
+ display: contents;
76
+ }