@helpwave/hightide-native 0.6.0 → 0.7.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.
Files changed (86) hide show
  1. package/package.json +3 -3
  2. package/src/components/card/Card.tsx +2 -4
  3. package/src/components/chat/ChatAttachmentMessageBubble.tsx +152 -90
  4. package/src/components/chat/ChatConversationList.tsx +4 -12
  5. package/src/components/chat/ChatConversationRow.tsx +159 -164
  6. package/src/components/chat/ChatDateDivider.tsx +3 -8
  7. package/src/components/chat/ChatMessageBubble.tsx +51 -45
  8. package/src/components/chat/ChatMessageComposer.tsx +4 -12
  9. package/src/components/chat/ChatMessageList.tsx +3 -9
  10. package/src/components/chat/ChatQuickReplyChip.tsx +41 -27
  11. package/src/components/chat/ChatSystemLine.tsx +4 -12
  12. package/src/components/chat/ChatThreadHeader.tsx +136 -101
  13. package/src/components/layout/Divider.tsx +2 -4
  14. package/src/components/list/ListActionItem.tsx +57 -65
  15. package/src/components/list/ListItem.tsx +8 -28
  16. package/src/components/list/ListItemTextContent.tsx +4 -2
  17. package/src/components/list/ListNavigationItem.tsx +55 -63
  18. package/src/components/user-interaction/Button.tsx +43 -49
  19. package/src/components/user-interaction/Checkbox.tsx +47 -48
  20. package/src/components/user-interaction/IconButton.tsx +40 -42
  21. package/src/components/user-interaction/Input.tsx +69 -80
  22. package/src/components/user-interaction/MultiSelect/MultiSelect.tsx +40 -0
  23. package/src/components/user-interaction/MultiSelect/MultiSelectContext.tsx +70 -0
  24. package/src/components/user-interaction/MultiSelect/MultiSelectMenu.tsx +87 -0
  25. package/src/components/user-interaction/MultiSelect/MultiSelectOption.tsx +87 -0
  26. package/src/components/user-interaction/MultiSelect/MultiSelectRoot.tsx +194 -0
  27. package/src/components/user-interaction/MultiSelect/MultiSelectTrigger.tsx +123 -0
  28. package/src/components/user-interaction/MultiSelect/index.ts +6 -0
  29. package/src/components/user-interaction/SearchBar.tsx +11 -17
  30. package/src/components/user-interaction/Select/Select.tsx +40 -0
  31. package/src/components/user-interaction/Select/SelectContext.tsx +69 -0
  32. package/src/components/user-interaction/Select/SelectMenu.tsx +87 -0
  33. package/src/components/user-interaction/Select/SelectOption.tsx +77 -0
  34. package/src/components/user-interaction/Select/SelectRoot.tsx +186 -0
  35. package/src/components/user-interaction/Select/SelectTrigger.tsx +92 -0
  36. package/src/components/user-interaction/Select/index.ts +6 -0
  37. package/src/components/user-interaction/Switch.tsx +46 -98
  38. package/src/components/user-interaction/Textarea.tsx +165 -12
  39. package/src/components/user-interaction/ThemedPressable.tsx +48 -49
  40. package/src/components/visualization-and-display/Avatar.tsx +19 -17
  41. package/src/components/visualization-and-display/Chip.tsx +4 -12
  42. package/src/components/visualization-and-display/ThemedText.tsx +12 -5
  43. package/src/enums/chatMessageDirection.ts +18 -0
  44. package/src/enums/chatMessageStatus.ts +18 -0
  45. package/src/enums/hightideThemeModes.ts +18 -0
  46. package/src/enums/index.ts +7 -0
  47. package/src/enums/listItemContentOrder.ts +18 -0
  48. package/src/enums/themedTextAppearance.ts +18 -0
  49. package/src/enums/useMultiSelectFirstHighlightBehavior.ts +18 -0
  50. package/src/enums/useSelectFirstHighlightBehavior.ts +18 -0
  51. package/src/global-contexts/content-theme/ContentThemeProvider.tsx +9 -5
  52. package/src/global-contexts/theme/ThemeProvider.tsx +4 -1
  53. package/src/hooks/index.ts +2 -0
  54. package/src/hooks/useAnimatedStyleTransition.ts +460 -0
  55. package/src/hooks/useMemoizedTheme.ts +23 -0
  56. package/src/hooks/useMultiSelect.ts +3 -1
  57. package/src/hooks/useSelect.ts +3 -1
  58. package/src/icons/HightideIconRegistry.ts +2 -0
  59. package/src/theme/adapters/style-adapter-utils.ts +121 -27
  60. package/src/theme/resolvers/avatar.ts +48 -39
  61. package/src/theme/resolvers/button.ts +7 -17
  62. package/src/theme/resolvers/chat/attachment-message-bubble.ts +5 -6
  63. package/src/theme/resolvers/chat/conversation-row.ts +4 -6
  64. package/src/theme/resolvers/chat/message-bubble.ts +1 -2
  65. package/src/theme/resolvers/chat/message-composer.ts +1 -2
  66. package/src/theme/resolvers/chat/quick-reply-chip.ts +2 -3
  67. package/src/theme/resolvers/chat/system-line.ts +1 -2
  68. package/src/theme/resolvers/chat/thread-header.ts +3 -4
  69. package/src/theme/resolvers/checkbox.ts +2 -2
  70. package/src/theme/resolvers/chip.ts +1 -2
  71. package/src/theme/resolvers/iconButton.ts +4 -13
  72. package/src/theme/resolvers/input.ts +30 -14
  73. package/src/theme/resolvers/listItem.ts +2 -3
  74. package/src/theme/resolvers/multiSelect.ts +1 -2
  75. package/src/theme/resolvers/searchBar.ts +1 -2
  76. package/src/theme/resolvers/select.ts +1 -2
  77. package/src/theme/resolvers/themedPressable.ts +4 -15
  78. package/src/theme/themes/hightideThemes.ts +3 -1
  79. package/src/theme/types/components/chat.ts +6 -21
  80. package/src/theme/types/components/input.ts +3 -0
  81. package/src/theme/types/components/textarea.ts +1 -0
  82. package/src/theme/types/resolver.ts +4 -27
  83. package/src/utils/index.ts +1 -0
  84. package/src/utils/pressableInteraction.ts +7 -0
  85. package/src/components/user-interaction/MultiSelect.tsx +0 -282
  86. package/src/components/user-interaction/Select.tsx +0 -242
@@ -0,0 +1,460 @@
1
+ import {
2
+ useLayoutEffect,
3
+ useMemo,
4
+ useRef,
5
+ useState
6
+ } from 'react'
7
+ import {
8
+ Animated,
9
+ StyleSheet,
10
+ type ViewStyle
11
+ } from 'react-native'
12
+
13
+ import { HexColorUtils } from '../utils/hex'
14
+
15
+ const TRANSFORM_KEYS = [
16
+ 'translateX',
17
+ 'translateY',
18
+ 'scale',
19
+ 'scaleX',
20
+ 'scaleY',
21
+ 'rotate',
22
+ 'rotateX',
23
+ 'rotateY',
24
+ 'rotateZ',
25
+ 'skewX',
26
+ 'skewY',
27
+ 'perspective',
28
+ ] as const
29
+
30
+ type TransformKey = typeof TRANSFORM_KEYS[number]
31
+ type StyleRecord = Record<string, unknown>
32
+ type TransformEntry = Record<string, unknown>
33
+ type AnimatedViewStyle = Animated.WithAnimatedObject<ViewStyle>
34
+ type AnimatedAttribute = keyof ViewStyle
35
+
36
+ const POSITION_STYLE_KEYS = [
37
+ 'position',
38
+ 'left',
39
+ 'right',
40
+ 'top',
41
+ 'bottom',
42
+ ] as const satisfies readonly AnimatedAttribute[]
43
+
44
+ const toRecord = (style: ViewStyle): StyleRecord => style as StyleRecord
45
+
46
+ const resolveAnimatedAttributes = (
47
+ animatedAttributes: AnimatedAttribute[]
48
+ ): AnimatedAttribute[] => {
49
+ const resolved: AnimatedAttribute[] = []
50
+
51
+ for (const key of animatedAttributes) {
52
+ if (key === 'position') {
53
+ resolved.push(...POSITION_STYLE_KEYS)
54
+ continue
55
+ }
56
+
57
+ resolved.push(key)
58
+ }
59
+
60
+ return [...new Set(resolved)]
61
+ }
62
+
63
+ const parseUnitValue = (value: unknown, unit: string): number | undefined => {
64
+ if (typeof value !== 'string' || !value.endsWith(unit)) {
65
+ return undefined
66
+ }
67
+
68
+ const parsed = Number.parseFloat(value.slice(0, -unit.length))
69
+ return Number.isFinite(parsed) ? parsed : undefined
70
+ }
71
+
72
+ const parseHexChannels = (hex: string): [number, number, number, number] => {
73
+ const normalized = hex.startsWith('#') ? hex.slice(1) : hex
74
+ const rgb = normalized.slice(0, 6)
75
+ const alphaHex = normalized.length >= 8 ? normalized.slice(6, 8) : 'ff'
76
+
77
+ return [
78
+ Number.parseInt(rgb.slice(0, 2), 16),
79
+ Number.parseInt(rgb.slice(2, 4), 16),
80
+ Number.parseInt(rgb.slice(4, 6), 16),
81
+ Number.parseInt(alphaHex, 16) / 255,
82
+ ]
83
+ }
84
+
85
+ const toHexColor = (r: number, g: number, b: number, alpha: number): string => {
86
+ const channel = (value: number) => Math.round(Math.min(255, Math.max(0, value)))
87
+ .toString(16)
88
+ .padStart(2, '0')
89
+ const rgb = `#${channel(r)}${channel(g)}${channel(b)}`
90
+
91
+ if (alpha >= 1) {
92
+ return rgb
93
+ }
94
+
95
+ return `${rgb}${channel(alpha * 255)}`
96
+ }
97
+
98
+ const mixNumber = (from: number, to: number, progress: number): number => (
99
+ from + (to - from) * progress
100
+ )
101
+
102
+ const mixColor = (from: string, to: string, progress: number): string | undefined => {
103
+ const fromHex = HexColorUtils.tryParseColorValue(from)
104
+ const toHex = HexColorUtils.tryParseColorValue(to)
105
+
106
+ if (fromHex === undefined || toHex === undefined) {
107
+ return undefined
108
+ }
109
+
110
+ const [fr, fg, fb, fa] = parseHexChannels(fromHex)
111
+ const [tr, tg, tb, ta] = parseHexChannels(toHex)
112
+
113
+ return toHexColor(
114
+ mixNumber(fr, tr, progress),
115
+ mixNumber(fg, tg, progress),
116
+ mixNumber(fb, tb, progress),
117
+ mixNumber(fa, ta, progress)
118
+ )
119
+ }
120
+
121
+ const mixScalar = (from: unknown, to: unknown, progress: number): unknown => {
122
+ if (from === to) {
123
+ return to
124
+ }
125
+
126
+ if (typeof from === 'number' && typeof to === 'number') {
127
+ return mixNumber(from, to, progress)
128
+ }
129
+
130
+ const fromPercent = parseUnitValue(from, '%')
131
+ const toPercent = parseUnitValue(to, '%')
132
+ if (fromPercent !== undefined && toPercent !== undefined) {
133
+ return `${mixNumber(fromPercent, toPercent, progress)}%`
134
+ }
135
+
136
+ const fromDeg = parseUnitValue(from, 'deg')
137
+ const toDeg = parseUnitValue(to, 'deg')
138
+ if (fromDeg !== undefined && toDeg !== undefined) {
139
+ return `${mixNumber(fromDeg, toDeg, progress)}deg`
140
+ }
141
+
142
+ if (typeof from === 'string' && typeof to === 'string') {
143
+ const mixedColor = mixColor(from, to, progress)
144
+ if (mixedColor !== undefined) {
145
+ return mixedColor
146
+ }
147
+ }
148
+
149
+ return progress < 1 ? from : to
150
+ }
151
+
152
+ const flattenTransform = (transform: ViewStyle['transform']): TransformEntry => {
153
+ if (!Array.isArray(transform)) {
154
+ return {}
155
+ }
156
+
157
+ const flattened: TransformEntry = {}
158
+
159
+ for (const entry of transform) {
160
+ if (entry !== undefined && typeof entry === 'object') {
161
+ Object.assign(flattened, entry)
162
+ }
163
+ }
164
+
165
+ return flattened
166
+ }
167
+
168
+ const transformKeys = (from: TransformEntry, to: TransformEntry): TransformKey[] => {
169
+ const keys = TRANSFORM_KEYS.filter((key) => from[key] !== undefined || to[key] !== undefined)
170
+
171
+ for (const key of Object.keys({ ...from, ...to })) {
172
+ if (!TRANSFORM_KEYS.includes(key as TransformKey)) {
173
+ keys.push(key as TransformKey)
174
+ }
175
+ }
176
+
177
+ return keys
178
+ }
179
+
180
+ const mixTransforms = (
181
+ from: ViewStyle['transform'],
182
+ to: ViewStyle['transform'],
183
+ progress: number
184
+ ): ViewStyle['transform'] => {
185
+ const fromFlat = flattenTransform(from)
186
+ const toFlat = flattenTransform(to)
187
+ const mixed: TransformEntry[] = []
188
+
189
+ for (const key of transformKeys(fromFlat, toFlat)) {
190
+ const fromValue = fromFlat[key]
191
+ const toValue = toFlat[key]
192
+
193
+ if (toValue === undefined) {
194
+ continue
195
+ }
196
+
197
+ mixed.push({
198
+ [key]: fromValue === undefined ? toValue : mixScalar(fromValue, toValue, progress),
199
+ })
200
+ }
201
+
202
+ return mixed.length === 0 ? undefined : mixed as unknown as ViewStyle['transform']
203
+ }
204
+
205
+ const mixStyles = (
206
+ from: ViewStyle,
207
+ to: ViewStyle,
208
+ progress: number,
209
+ animatedAttributes: (keyof ViewStyle)[]
210
+ ): ViewStyle => {
211
+ const fromRecord = toRecord(from)
212
+ const toRecordStyle = toRecord(to)
213
+ const attributes = resolveAnimatedAttributes(animatedAttributes)
214
+ const mixed: StyleRecord = { ...toRecordStyle }
215
+
216
+ for (const key of attributes) {
217
+ if (key === 'transform') {
218
+ mixed.transform = mixTransforms(from.transform, to.transform, progress)
219
+ continue
220
+ }
221
+
222
+ const fromValue = fromRecord[key]
223
+ const toValue = toRecordStyle[key]
224
+
225
+ if (fromValue === undefined || fromValue === toValue) {
226
+ continue
227
+ }
228
+
229
+ mixed[key] = mixScalar(fromValue, toValue, progress)
230
+ }
231
+
232
+ return mixed as ViewStyle
233
+ }
234
+
235
+ const interpolateScalar = (
236
+ from: unknown,
237
+ to: unknown,
238
+ progress: Animated.Value
239
+ ): unknown => {
240
+ if (from === to) {
241
+ return to
242
+ }
243
+
244
+ if (typeof from === 'number' && typeof to === 'number') {
245
+ return progress.interpolate({
246
+ inputRange: [0, 1],
247
+ outputRange: [from, to],
248
+ })
249
+ }
250
+
251
+ const fromPercent = parseUnitValue(from, '%')
252
+ const toPercent = parseUnitValue(to, '%')
253
+ if (fromPercent !== undefined && toPercent !== undefined) {
254
+ return progress.interpolate({
255
+ inputRange: [0, 1],
256
+ outputRange: [`${fromPercent}%`, `${toPercent}%`],
257
+ })
258
+ }
259
+
260
+ const fromDeg = parseUnitValue(from, 'deg')
261
+ const toDeg = parseUnitValue(to, 'deg')
262
+ if (fromDeg !== undefined && toDeg !== undefined) {
263
+ return progress.interpolate({
264
+ inputRange: [0, 1],
265
+ outputRange: [`${fromDeg}deg`, `${toDeg}deg`],
266
+ })
267
+ }
268
+
269
+ if (typeof from === 'string' && typeof to === 'string') {
270
+ const fromColor = HexColorUtils.tryParseColorValue(from)
271
+ const toColor = HexColorUtils.tryParseColorValue(to)
272
+
273
+ if (fromColor !== undefined && toColor !== undefined) {
274
+ return progress.interpolate({
275
+ inputRange: [0, 1],
276
+ outputRange: [fromColor, toColor],
277
+ })
278
+ }
279
+ }
280
+
281
+ return to
282
+ }
283
+
284
+ const interpolateTransforms = (
285
+ from: ViewStyle['transform'],
286
+ to: ViewStyle['transform'],
287
+ progress: Animated.Value
288
+ ): ViewStyle['transform'] | Animated.WithAnimatedValue<ViewStyle['transform']> => {
289
+ const fromFlat = flattenTransform(from)
290
+ const toFlat = flattenTransform(to)
291
+ const interpolated: TransformEntry[] = []
292
+
293
+ for (const key of transformKeys(fromFlat, toFlat)) {
294
+ const fromValue = fromFlat[key]
295
+ const toValue = toFlat[key]
296
+
297
+ if (toValue === undefined) {
298
+ continue
299
+ }
300
+
301
+ interpolated.push({
302
+ [key]: fromValue === undefined ? toValue : interpolateScalar(fromValue, toValue, progress),
303
+ })
304
+ }
305
+
306
+ return interpolated.length === 0 ? undefined : interpolated as unknown as ViewStyle['transform']
307
+ }
308
+
309
+ const createInterpolatedStyle = (
310
+ from: ViewStyle,
311
+ to: ViewStyle,
312
+ progress: Animated.Value,
313
+ animatedAttributes: (keyof ViewStyle)[]
314
+ ): AnimatedViewStyle => {
315
+ const fromRecord = toRecord(from)
316
+ const toRecordStyle = toRecord(to)
317
+ const attributes = resolveAnimatedAttributes(animatedAttributes)
318
+ const interpolated: StyleRecord = { ...toRecordStyle }
319
+
320
+ for (const key of attributes) {
321
+ if (key === 'transform') {
322
+ interpolated.transform = interpolateTransforms(from.transform, to.transform, progress)
323
+ continue
324
+ }
325
+
326
+ const fromValue = fromRecord[key]
327
+ const toValue = toRecordStyle[key]
328
+
329
+ if (fromValue === undefined || fromValue === toValue) {
330
+ continue
331
+ }
332
+
333
+ interpolated[key] = interpolateScalar(fromValue, toValue, progress)
334
+ }
335
+
336
+ return interpolated as AnimatedViewStyle
337
+ }
338
+
339
+ const pickAnimatedAttributes = (
340
+ style: ViewStyle,
341
+ animatedAttributes: (keyof ViewStyle)[]
342
+ ): StyleRecord => {
343
+ const flattened = StyleSheet.flatten(style) ?? {}
344
+ const record = toRecord(flattened)
345
+ const picked: StyleRecord = {}
346
+ const attributes = resolveAnimatedAttributes(animatedAttributes)
347
+
348
+ for (const key of attributes) {
349
+ picked[key] = record[key]
350
+ }
351
+
352
+ return picked
353
+ }
354
+
355
+ const areAnimatedAttributesEqual = (
356
+ left: ViewStyle,
357
+ right: ViewStyle,
358
+ animatedAttributes: (keyof ViewStyle)[]
359
+ ): boolean => (
360
+ JSON.stringify(pickAnimatedAttributes(left, animatedAttributes))
361
+ === JSON.stringify(pickAnimatedAttributes(right, animatedAttributes))
362
+ )
363
+
364
+ const areStylesEqual = (left: ViewStyle, right: ViewStyle): boolean => {
365
+ const normalize = (style: ViewStyle): string => {
366
+ const flattened = StyleSheet.flatten(style) ?? {}
367
+ const record = toRecord(flattened)
368
+ const normalized: StyleRecord = {}
369
+
370
+ for (const key of Object.keys(record).sort()) {
371
+ normalized[key] = record[key]
372
+ }
373
+
374
+ return JSON.stringify(normalized)
375
+ }
376
+
377
+ return normalize(left) === normalize(right)
378
+ }
379
+
380
+ export type AnimatedStyleTransitionProps = {
381
+ style: ViewStyle,
382
+ duration: number,
383
+ animatedAttributes: (keyof ViewStyle)[],
384
+ }
385
+
386
+ // TODO fix transitions
387
+ export const useAnimatedStyleTransition = ({
388
+ style,
389
+ duration,
390
+ animatedAttributes,
391
+ }: AnimatedStyleTransitionProps): AnimatedViewStyle => {
392
+ const progress = useRef(new Animated.Value(1)).current
393
+ const fromRef = useRef(style)
394
+ const toRef = useRef(style)
395
+ const isFirstRender = useRef(true)
396
+ const animationRef = useRef<Animated.CompositeAnimation | null>(null)
397
+ const [transition, setTransition] = useState({
398
+ from: style,
399
+ to: style,
400
+ })
401
+
402
+ useLayoutEffect(() => {
403
+ if (isFirstRender.current) {
404
+ isFirstRender.current = false
405
+ fromRef.current = style
406
+ toRef.current = style
407
+ return
408
+ }
409
+
410
+ if (areAnimatedAttributesEqual(toRef.current, style, animatedAttributes)) {
411
+ if (areStylesEqual(toRef.current, style)) {
412
+ return
413
+ }
414
+
415
+ toRef.current = style
416
+ setTransition((current) => ({
417
+ from: current.from,
418
+ to: style,
419
+ }))
420
+ return
421
+ }
422
+
423
+ progress.stopAnimation((currentProgress) => {
424
+ const captured = mixStyles(
425
+ fromRef.current,
426
+ toRef.current,
427
+ currentProgress,
428
+ animatedAttributes
429
+ )
430
+ fromRef.current = captured
431
+ toRef.current = style
432
+ setTransition({
433
+ from: captured,
434
+ to: style,
435
+ })
436
+ animationRef.current?.stop()
437
+ progress.setValue(0)
438
+ animationRef.current = Animated.timing(progress, {
439
+ toValue: 1,
440
+ duration,
441
+ useNativeDriver: false,
442
+ })
443
+ animationRef.current.start()
444
+ })
445
+ }, [animatedAttributes, duration, progress, style])
446
+
447
+ useLayoutEffect(() => () => {
448
+ animationRef.current?.stop()
449
+ }, [])
450
+
451
+ return useMemo(
452
+ () => createInterpolatedStyle(
453
+ transition.from,
454
+ transition.to,
455
+ progress,
456
+ animatedAttributes
457
+ ),
458
+ [animatedAttributes, progress, transition]
459
+ )
460
+ }
@@ -0,0 +1,23 @@
1
+ import { useMemo } from 'react'
2
+ import { StyleSheet } from 'react-native'
3
+ import type { StyleOverwrite, StyleResolverFunction } from '../theme'
4
+
5
+ export const useMemoizedTheme = <State, Style>(
6
+ resolver: StyleResolverFunction<State, Style>,
7
+ state: State,
8
+ overwrite?: StyleOverwrite<State, Style>
9
+ ): Style => {
10
+ const memoizedTheme = useMemo(() => StyleSheet.flatten(resolver(state, overwrite)), [overwrite, resolver, state])
11
+ return memoizedTheme as Style
12
+ }
13
+
14
+ export const useMemoizedThemeFactory = <State, Factory>(
15
+ resolver: StyleResolverFunction<State, Factory>,
16
+ state: State,
17
+ overwrite?: StyleOverwrite<State, Factory>
18
+ ): Factory => {
19
+ return useMemo(
20
+ () => resolver(state, overwrite) as Factory,
21
+ [overwrite, resolver, state]
22
+ )
23
+ }
@@ -30,7 +30,9 @@ export interface UseMultiSelectOptions {
30
30
  typeAheadResetMs?: number,
31
31
  }
32
32
 
33
- export type UseMultiSelectFirstHighlightBehavior = 'first' | 'last'
33
+ import type { UseMultiSelectFirstHighlightBehavior } from '../enums/useMultiSelectFirstHighlightBehavior'
34
+
35
+ export type { UseMultiSelectFirstHighlightBehavior }
34
36
 
35
37
  export interface UseMultiSelectState {
36
38
  value: string[],
@@ -31,7 +31,9 @@ export interface UseSelectOptions {
31
31
  typeAheadResetMs?: number,
32
32
  }
33
33
 
34
- export type UseSelectFirstHighlightBehavior = 'first' | 'last'
34
+ import type { UseSelectFirstHighlightBehavior } from '../enums/useSelectFirstHighlightBehavior'
35
+
36
+ export type { UseSelectFirstHighlightBehavior }
35
37
 
36
38
  export interface UseSelectState {
37
39
  value: string | null,
@@ -3,6 +3,7 @@ import {
3
3
  CheckCheck,
4
4
  ChevronDown,
5
5
  ChevronRight,
6
+ Clock,
6
7
  Download,
7
8
  FileText,
8
9
  Minus,
@@ -20,6 +21,7 @@ export const HightideIconRegistry = {
20
21
  CheckCheck: CheckCheck,
21
22
  ChevronDown: ChevronDown,
22
23
  ChevronRight: ChevronRight,
24
+ Clock: Clock,
23
25
  Download: Download,
24
26
  FileText: FileText,
25
27
  Minus: Minus,