@titan-design/react-ui 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +399 -4
- package/dist/index.d.ts +399 -4
- package/dist/index.js +2295 -895
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2279 -905
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-DyEx-lBf.d.ts → pages-B1hqnGIC.d.ts} +149 -15
- package/dist/{pages-D7hOD4Vj.d.mts → pages-CvsFqNtx.d.mts} +149 -15
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1974 -1740
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +1975 -1741
- package/dist/pages.mjs.map +1 -1
- package/package.json +8 -3
- package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +258 -0
- package/src/components/custom/Fatigue/DualGhostSpark.test.tsx +138 -0
- package/src/components/custom/Fatigue/DualGhostSpark.tsx +208 -0
- package/src/components/custom/Fatigue/FatigueLights.stories.tsx +74 -0
- package/src/components/custom/Fatigue/FatigueLights.test.tsx +33 -0
- package/src/components/custom/Fatigue/FatigueLights.tsx +83 -0
- package/src/components/custom/Fatigue/GhostBand.test.tsx +235 -0
- package/src/components/custom/Fatigue/GhostBand.tsx +246 -0
- package/src/components/custom/Fatigue/GhostBloom.tsx +132 -0
- package/src/components/custom/Fatigue/GhostSpark.stories.tsx +175 -0
- package/src/components/custom/Fatigue/GhostSpark.test.tsx +45 -0
- package/src/components/custom/Fatigue/GhostSpark.tsx +126 -0
- package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +126 -0
- package/src/components/custom/Fatigue/LiveFatigueCard.test.tsx +28 -0
- package/src/components/custom/Fatigue/LiveFatigueCard.tsx +86 -0
- package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +95 -0
- package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +151 -0
- package/src/components/custom/Fatigue/README.md +93 -0
- package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +94 -0
- package/src/components/custom/Fatigue/RomProgressionChart.test.tsx +31 -0
- package/src/components/custom/Fatigue/RomProgressionChart.tsx +132 -0
- package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +226 -0
- package/src/components/custom/Fatigue/VelocityHero.stories.tsx +70 -0
- package/src/components/custom/Fatigue/VelocityHero.test.tsx +26 -0
- package/src/components/custom/Fatigue/VelocityHero.tsx +46 -0
- package/src/components/custom/Fatigue/VerdictHero.stories.tsx +59 -0
- package/src/components/custom/Fatigue/VerdictHero.test.tsx +49 -0
- package/src/components/custom/Fatigue/VerdictHero.tsx +79 -0
- package/src/components/custom/Fatigue/fatigue-mock.test.ts +59 -0
- package/src/components/custom/Fatigue/fatigue-mock.ts +268 -0
- package/src/components/custom/Fatigue/fatigue-model.ts +129 -0
- package/src/components/custom/Fatigue/fatigue-tokens.test.ts +70 -0
- package/src/components/custom/Fatigue/fatigue-tokens.ts +180 -0
- package/src/components/custom/Fatigue/index.ts +41 -0
- package/src/components/custom/Fatigue/tempo-pacing.test.ts +176 -0
- package/src/components/custom/Fatigue/tempo-pacing.ts +112 -0
- package/src/components/custom/Workout/DualVelocityStrip.test.tsx +685 -0
- package/src/components/custom/Workout/ExerciseCard.test.tsx +4 -4
- package/src/components/custom/Workout/ExerciseCard.tsx +3 -1
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +7 -1
- package/src/components/custom/Workout/README.md +62 -16
- package/src/components/custom/Workout/SegmentedBar.stories.tsx +3 -3
- package/src/components/custom/Workout/SessionRail.test.tsx +43 -0
- package/src/components/custom/Workout/SessionRail.tsx +50 -13
- package/src/components/custom/Workout/SetRow.test.tsx +6 -6
- package/src/components/custom/Workout/SetRow.tsx +9 -2
- package/src/components/custom/Workout/VelocityStrip.compact.stories.tsx +191 -0
- package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +186 -0
- package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +296 -0
- package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +259 -0
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +217 -250
- package/src/components/custom/Workout/VelocityStrip.test.tsx +196 -79
- package/src/components/custom/Workout/VelocityStrip.tsx +953 -609
- package/src/components/custom/Workout/index.ts +3 -0
- package/src/components/custom/Workout/velocity-story-kit.tsx +438 -0
- package/src/components/custom/charts/SetBarChart.test.tsx +275 -0
- package/src/components/custom/charts/SetBarChart.tsx +651 -0
- package/src/components/custom/charts/live-rep-growth.ts +91 -0
- package/src/components/custom/index.ts +1 -0
- package/src/test/raw-color-patterns.test.ts +71 -0
- package/src/theme/ColorPalettes.stories.tsx +871 -0
- package/src/theme/ColorPrimitives.stories.tsx +300 -0
- package/src/theme/bar-paper.ts +59 -0
- package/src/theme/color-stories.coverage.test.ts +130 -0
- package/src/theme/color-story-kit.tsx +170 -0
- package/src/components/custom/Workout/VelocityStrip.modalities.stories.tsx +0 -254
- package/src/components/custom/Workout/VelocityStrip.responsive.stories.tsx +0 -145
- package/src/theme/ColorSystem.stories.tsx +0 -508
- package/src/theme/Colors.stories.tsx +0 -323
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// The newest-rep grow-from-bottom entrance, shared by SetBarChart (hero / dual / ROM)
|
|
2
|
+
// and VelocityStrip's framed + bare expanded charts. Promoted out of VelocityStrip so
|
|
3
|
+
// every value-height bar family animates the live rep identically instead of re-rolling
|
|
4
|
+
// the hook.
|
|
5
|
+
import { useEffect, useState } from 'react'
|
|
6
|
+
import { Animated, Easing } from 'react-native'
|
|
7
|
+
|
|
8
|
+
const ANIMATION_EASING = Easing.bezier(0.22, 1, 0.36, 1)
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Growth-factor overshoot for a new-peak bar: it grows past its full height then
|
|
12
|
+
* settles back to 1 (a small bounce), rather than the plain 0→1 grow every other
|
|
13
|
+
* bar gets. Modest (12%) since it stretches actual bar HEIGHT, not a uniform scale
|
|
14
|
+
* — a large overshoot here reads as a much bigger jump than the same factor did
|
|
15
|
+
* against the old whole-bar scale pop.
|
|
16
|
+
*/
|
|
17
|
+
const PEAK_OVERSHOOT = 1.12
|
|
18
|
+
|
|
19
|
+
function getReducedMotionPreference(): boolean {
|
|
20
|
+
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false
|
|
21
|
+
return window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Track the OS "reduce motion" preference; falls back to `false` (jsdom/SSR). */
|
|
25
|
+
export function usePrefersReducedMotion(): boolean {
|
|
26
|
+
const [reduced, setReduced] = useState(getReducedMotionPreference)
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return
|
|
29
|
+
const mq = window.matchMedia('(prefers-reduced-motion: reduce)')
|
|
30
|
+
const handler = () => setReduced(mq.matches)
|
|
31
|
+
handler()
|
|
32
|
+
mq.addEventListener?.('change', handler)
|
|
33
|
+
return () => mq.removeEventListener?.('change', handler)
|
|
34
|
+
}, [])
|
|
35
|
+
return reduced
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The newest-rep entrance shared by SetBarChart and the framed / bare `expanded` charts:
|
|
40
|
+
* the bar GROWS UP FROM THE BASELINE (0 → full height), reading as though it's tracking
|
|
41
|
+
* the rep as it lands. A new set peak overshoots past full height then settles (a small
|
|
42
|
+
* bounce) instead of the plain grow. Honors reduced motion (jumps straight to full
|
|
43
|
+
* height, no animation). Returns a 0→1(+overshoot) growth-factor `Animated.Value`;
|
|
44
|
+
* callers interpolate it against their own target height. Inert while `active` is
|
|
45
|
+
* false or no live rep is present.
|
|
46
|
+
*/
|
|
47
|
+
export function useLiveRepGrowth(
|
|
48
|
+
active: boolean,
|
|
49
|
+
liveRepIndex: number | undefined,
|
|
50
|
+
liveVelocity: number | undefined,
|
|
51
|
+
isNewPeak: boolean
|
|
52
|
+
): Animated.Value {
|
|
53
|
+
const prefersReducedMotion = usePrefersReducedMotion()
|
|
54
|
+
const [liveGrowth] = useState(() => new Animated.Value(1))
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (!active || liveRepIndex == null || liveVelocity == null) return
|
|
57
|
+
if (prefersReducedMotion) {
|
|
58
|
+
liveGrowth.setValue(1)
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
liveGrowth.setValue(0)
|
|
62
|
+
if (isNewPeak) {
|
|
63
|
+
Animated.sequence([
|
|
64
|
+
Animated.timing(liveGrowth, {
|
|
65
|
+
toValue: PEAK_OVERSHOOT,
|
|
66
|
+
duration: 220,
|
|
67
|
+
easing: Easing.out(Easing.quad),
|
|
68
|
+
useNativeDriver: false,
|
|
69
|
+
}),
|
|
70
|
+
Animated.spring(liveGrowth, {
|
|
71
|
+
toValue: 1,
|
|
72
|
+
friction: 5,
|
|
73
|
+
tension: 140,
|
|
74
|
+
useNativeDriver: false,
|
|
75
|
+
}),
|
|
76
|
+
]).start()
|
|
77
|
+
} else {
|
|
78
|
+
// Plain entrance: a clean ease-out grow with no overshoot (ANIMATION_EASING has
|
|
79
|
+
// no control point above y=1) — it reads as tracking the rep, not bouncing.
|
|
80
|
+
Animated.timing(liveGrowth, {
|
|
81
|
+
toValue: 1,
|
|
82
|
+
duration: 300,
|
|
83
|
+
easing: ANIMATION_EASING,
|
|
84
|
+
useNativeDriver: false,
|
|
85
|
+
}).start()
|
|
86
|
+
}
|
|
87
|
+
}, [active, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveGrowth])
|
|
88
|
+
return liveGrowth
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { ANIMATION_EASING }
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { createRequire } from 'node:module'
|
|
3
|
+
|
|
4
|
+
const { extractRawColors } = createRequire(import.meta.url)(
|
|
5
|
+
'../../eslint-rules/raw-color-patterns.js'
|
|
6
|
+
) as { extractRawColors: (text: string) => Array<{ value: string; id: string }> }
|
|
7
|
+
|
|
8
|
+
const ids = (text: string) => extractRawColors(text).map((c) => c.id)
|
|
9
|
+
const values = (text: string) => extractRawColors(text).map((c) => c.value)
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Detection for the `no-raw-color` ratchet. The rule and its baseline generator
|
|
13
|
+
* share this module, so a gap here silently widens the allowance rather than
|
|
14
|
+
* failing loudly — worth pinning the notations directly.
|
|
15
|
+
*/
|
|
16
|
+
describe('extractRawColors', () => {
|
|
17
|
+
it('detects every hex length', () => {
|
|
18
|
+
expect(ids('#fff')).toEqual(['hex'])
|
|
19
|
+
expect(ids('#ffff')).toEqual(['hex'])
|
|
20
|
+
expect(ids('#1C1916')).toEqual(['hex'])
|
|
21
|
+
expect(ids('#1C1916FF')).toEqual(['hex'])
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('detects functional notations, including ones with no occurrences today', () => {
|
|
25
|
+
for (const text of [
|
|
26
|
+
'rgb(1,2,3)',
|
|
27
|
+
'rgba(1,2,3,0.5)',
|
|
28
|
+
'hsl(1 2% 3%)',
|
|
29
|
+
'hsla(1,2%,3%,0.5)',
|
|
30
|
+
'oklch(0.5 0.2 30)',
|
|
31
|
+
'lab(50% 40 59)',
|
|
32
|
+
'color-mix(in oklch, red, blue)',
|
|
33
|
+
]) {
|
|
34
|
+
expect(ids(text), text).toContain('functional')
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('detects Tailwind colour utilities but not layout classes', () => {
|
|
39
|
+
expect(ids('bg-red-500')).toEqual(['twPalette'])
|
|
40
|
+
expect(ids('text-slate-200')).toEqual(['twPalette'])
|
|
41
|
+
expect(ids('bg-white')).toEqual(['twAchromatic'])
|
|
42
|
+
expect(ids('bg-[#1C1C1C]')).toEqual(['twArbitrary'])
|
|
43
|
+
expect(ids('flex flex-row gap-2 p-4 rounded-lg')).toEqual([])
|
|
44
|
+
// Semantic utilities are the desired output, not violations.
|
|
45
|
+
expect(ids('bg-surface-raised text-text-primary')).toEqual([])
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('treats a bare keyword as a colour only when it is the whole value', () => {
|
|
49
|
+
expect(ids('white')).toEqual(['named'])
|
|
50
|
+
expect(ids(' black ')).toEqual(['named'])
|
|
51
|
+
// Prose and identifiers must not match, or every comment becomes a violation.
|
|
52
|
+
expect(ids('white text on a black plane')).toEqual([])
|
|
53
|
+
expect(ids('whiteboard')).toEqual([])
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('does not flag absence or inheritance', () => {
|
|
57
|
+
// `transparent` encodes absence and `currentColor` defers to the cascade;
|
|
58
|
+
// neither has a token equivalent.
|
|
59
|
+
expect(ids('transparent')).toEqual([])
|
|
60
|
+
expect(ids('currentColor')).toEqual([])
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('normalises case so #FFF and #fff are the same debt', () => {
|
|
64
|
+
expect(values('#ABCDEF')).toEqual(['#abcdef'])
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('extracts every colour in a multi-colour literal', () => {
|
|
68
|
+
// The baseline funds each one, so both have to come back.
|
|
69
|
+
expect(values('linear-gradient(#111111, #222222)')).toEqual(['#111111', '#222222'])
|
|
70
|
+
})
|
|
71
|
+
})
|