@helpwave/hightide-native 0.0.7 → 0.1.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 (125) hide show
  1. package/README.md +4 -4
  2. package/package.json +8 -19
  3. package/src/components/card/Card.tsx +41 -0
  4. package/src/components/card/index.ts +1 -0
  5. package/src/components/chat/ChatAttachmentMessageBubble.tsx +246 -0
  6. package/src/components/chat/ChatConversationRow.tsx +21 -16
  7. package/src/components/chat/ChatDateDivider.tsx +2 -2
  8. package/src/components/chat/ChatMessageBubble.tsx +62 -52
  9. package/src/components/chat/ChatMessageComposer.tsx +16 -14
  10. package/src/components/chat/ChatQuickReplyChip.tsx +41 -43
  11. package/src/components/chat/ChatSystemLine.tsx +14 -7
  12. package/src/components/chat/ChatThreadHeader.tsx +60 -6
  13. package/src/components/chat/index.ts +1 -2
  14. package/src/components/index.ts +3 -1
  15. package/src/components/layout/Divider.tsx +60 -0
  16. package/src/components/layout/index.ts +1 -0
  17. package/src/components/list/ListActionItem.tsx +99 -0
  18. package/src/components/list/ListItem.tsx +96 -0
  19. package/src/components/list/ListNavigationItem.tsx +102 -0
  20. package/src/components/list/index.ts +3 -0
  21. package/src/components/user-interaction/Button.tsx +80 -31
  22. package/src/components/user-interaction/Checkbox.tsx +72 -32
  23. package/src/components/user-interaction/IconButton.tsx +68 -30
  24. package/src/components/user-interaction/Input.tsx +61 -14
  25. package/src/components/user-interaction/MultiSelect.tsx +142 -75
  26. package/src/components/user-interaction/SearchBar.tsx +222 -0
  27. package/src/components/user-interaction/Select.tsx +129 -58
  28. package/src/components/user-interaction/Switch.tsx +207 -0
  29. package/src/components/user-interaction/ThemedPressable.tsx +136 -0
  30. package/src/components/user-interaction/index.ts +3 -0
  31. package/src/components/visualization-and-display/Avatar.tsx +112 -93
  32. package/src/components/visualization-and-display/Chip.tsx +22 -22
  33. package/src/components/visualization-and-display/ThemedIcon.tsx +72 -0
  34. package/src/components/visualization-and-display/ThemedText.tsx +38 -0
  35. package/src/components/visualization-and-display/index.ts +2 -1
  36. package/src/global-contexts/HightideProvider.tsx +19 -10
  37. package/src/global-contexts/content-theme/ContentThemeContext.ts +22 -0
  38. package/src/global-contexts/content-theme/ContentThemeProvider.tsx +32 -0
  39. package/src/global-contexts/content-theme/index.ts +2 -0
  40. package/src/global-contexts/debug/forward-exports.ts +10 -0
  41. package/src/global-contexts/debug/index.ts +1 -0
  42. package/src/global-contexts/index.ts +2 -0
  43. package/src/global-contexts/theme/ThemeProvider.tsx +7 -1
  44. package/src/icons/HightideIconRegistry.ts +33 -0
  45. package/src/icons/index.ts +2 -0
  46. package/src/icons/types.ts +11 -0
  47. package/src/theme/adapters/container-adapter.ts +323 -0
  48. package/src/theme/adapters/defined.ts +11 -0
  49. package/src/theme/adapters/icon-style-adapter.ts +10 -0
  50. package/src/theme/adapters/index.ts +4 -0
  51. package/src/theme/adapters/text-style-adapter.ts +16 -0
  52. package/src/theme/index.ts +1 -0
  53. package/src/theme/resolvers/avatar.ts +385 -186
  54. package/src/theme/resolvers/button.ts +50 -82
  55. package/src/theme/resolvers/card.ts +27 -0
  56. package/src/theme/resolvers/chat/attachment-message-bubble.ts +163 -0
  57. package/src/theme/resolvers/chat/chat-theme.ts +69 -0
  58. package/src/theme/resolvers/chat/conversation-list.ts +35 -0
  59. package/src/theme/resolvers/chat/conversation-row.ts +64 -0
  60. package/src/theme/resolvers/chat/date-divider.ts +31 -0
  61. package/src/theme/resolvers/chat/index.ts +11 -0
  62. package/src/theme/resolvers/chat/message-bubble.ts +57 -0
  63. package/src/theme/resolvers/chat/message-composer.ts +38 -0
  64. package/src/theme/resolvers/chat/message-list.ts +27 -0
  65. package/src/theme/resolvers/chat/quick-reply-chip.ts +80 -0
  66. package/src/theme/resolvers/chat/system-line.ts +41 -0
  67. package/src/theme/resolvers/chat/thread-header.ts +85 -0
  68. package/src/theme/resolvers/checkbox.ts +87 -74
  69. package/src/theme/resolvers/chip.ts +30 -67
  70. package/src/theme/resolvers/colorScheme.ts +136 -0
  71. package/src/theme/resolvers/divider.ts +34 -0
  72. package/src/theme/resolvers/icon.ts +21 -0
  73. package/src/theme/resolvers/iconButton.ts +45 -61
  74. package/src/theme/resolvers/index.ts +8 -2
  75. package/src/theme/resolvers/input.ts +64 -42
  76. package/src/theme/resolvers/listItem.ts +115 -0
  77. package/src/theme/resolvers/multiSelect.ts +159 -84
  78. package/src/theme/resolvers/searchBar.ts +121 -0
  79. package/src/theme/resolvers/select.ts +141 -66
  80. package/src/theme/resolvers/switch.ts +71 -0
  81. package/src/theme/resolvers/themedPressable.ts +54 -0
  82. package/src/theme/themes/createHightideTheme.ts +214 -49
  83. package/src/theme/themes/hightideThemes.ts +10 -6
  84. package/src/theme/types/color.ts +2 -59
  85. package/src/theme/types/components/avatar.ts +37 -36
  86. package/src/theme/types/components/button.ts +13 -14
  87. package/src/theme/types/components/card.ts +9 -0
  88. package/src/theme/types/components/chat.ts +124 -103
  89. package/src/theme/types/components/checkbox.ts +11 -12
  90. package/src/theme/types/components/chip.ts +8 -14
  91. package/src/theme/types/components/divider.ts +18 -0
  92. package/src/theme/types/components/hightide.ts +41 -40
  93. package/src/theme/types/components/iconButton.ts +19 -14
  94. package/src/theme/types/components/index.ts +6 -1
  95. package/src/theme/types/components/input.ts +17 -8
  96. package/src/theme/types/components/listItem.ts +86 -0
  97. package/src/theme/types/components/multiSelect.ts +14 -12
  98. package/src/theme/types/components/searchBar.ts +28 -0
  99. package/src/theme/types/components/select.ts +21 -9
  100. package/src/theme/types/components/switch.ts +22 -0
  101. package/src/theme/types/components/themedPressable.ts +31 -0
  102. package/src/theme/types/decoration.ts +1 -11
  103. package/src/theme/types/icongraphy.ts +8 -0
  104. package/src/theme/types/index.ts +2 -0
  105. package/src/theme/types/layout.ts +25 -57
  106. package/src/theme/types/resolver.ts +54 -1
  107. package/src/theme/types/semantics.ts +72 -0
  108. package/src/theme/types/theme.ts +26 -13
  109. package/src/theme/types/typography.ts +16 -17
  110. package/src/utils/hitBoxOverlay.ts +44 -0
  111. package/src/utils/index.ts +2 -0
  112. package/src/utils/minimumTouchTargetHitSlop.ts +68 -0
  113. package/src/components/chat/ChatAttachmentCard.tsx +0 -110
  114. package/src/components/chat/ChatMessageCard.tsx +0 -118
  115. package/src/components/menu/Menu.tsx +0 -61
  116. package/src/components/menu/MenuActionItem.tsx +0 -90
  117. package/src/components/menu/MenuItem.tsx +0 -73
  118. package/src/components/menu/MenuNavigationItem.tsx +0 -90
  119. package/src/components/menu/index.ts +0 -4
  120. package/src/components/visualization-and-display/Icon.tsx +0 -27
  121. package/src/storybook/helper.tsx +0 -36
  122. package/src/theme/resolvers/chat.ts +0 -418
  123. package/src/theme/resolvers/coloring.ts +0 -94
  124. package/src/theme/resolvers/menu.ts +0 -120
  125. package/src/theme/types/components/menu.ts +0 -57
@@ -1,72 +1,56 @@
1
- import type { ViewStyle } from 'react-native'
2
-
3
- import { componentLayouts } from '@helpwave/hightide-design/tokens'
4
- import type { HightideThemeTokens as DesignTokensTheme } from '@helpwave/hightide-design/types'
5
-
6
- import {
7
- isOutlineColoringStyle,
8
- resolveColoringStyles
9
- } from './coloring'
10
- import type { HightideSemanticColors } from '../types/color'
11
- import type { HightideComponentThemes } from '../types/components/hightide'
1
+ import { toContainerStyle } from '../adapters/container-adapter'
2
+ import { toIconStyle } from '../adapters/icon-style-adapter'
3
+ import { toTextStyle } from '../adapters/text-style-adapter'
12
4
  import type {
5
+ IconButtonIconStyle,
13
6
  IconButtonState,
14
- IconButtonTheme
7
+ IconButtonStyle,
8
+ IconButtonTextStyle,
9
+ IconButtonThemeResolvers
15
10
  } from '../types/components/iconButton'
16
11
  import {
17
12
  createStyleResolver,
18
- createValueResolver
13
+ createValueResolver,
14
+ toPressableInteractionState,
15
+ type ComponentThemeResolver
19
16
  } from '../types/resolver'
20
17
 
21
- export type CreateIconButtonThemeOptions = {
22
- semantic: HightideSemanticColors,
23
- coloring: HightideComponentThemes['coloring'],
24
- }
25
-
26
- export const createIconButtonTheme = ({
27
- semantic,
28
- coloring,
29
- }: CreateIconButtonThemeOptions): IconButtonTheme => {
30
- const resolveState = (state: IconButtonState) => {
31
- const size = state.size ?? 'md'
32
- const color = state.color ?? 'neutral'
33
- const coloringStyle = state.coloringStyle ?? 'solid'
34
- const tokens = coloring[color]
35
- const resolved = resolveColoringStyles(tokens, coloringStyle, semantic, state)
36
- const sizing = componentLayouts.element[size]
37
- const outlineWidth = componentLayouts.shared.coloringOutlineWidth
38
- const dimension = sizing.height
39
- const borderWidth = resolved.borderWidth > 0
40
- ? resolved.borderWidth
41
- : (isOutlineColoringStyle(coloringStyle) ? outlineWidth : 0)
42
-
43
- const button: ViewStyle = {
44
- alignItems: 'center',
45
- justifyContent: 'center',
46
- backgroundColor: resolved.backgroundColor,
47
- borderColor: resolved.borderColor,
48
- borderWidth,
49
- width: dimension,
50
- height: dimension,
51
- borderRadius: componentLayouts.button[size].borderRadius,
52
- opacity: state.isDisabled ? 0.6 : 1,
53
- }
54
-
55
- return {
56
- button,
57
- icon: { color: resolved.color },
58
- }
59
- }
18
+ export const toIconButtonThemeResolvers: ComponentThemeResolver<IconButtonThemeResolvers> = ({
19
+ themeTokens,
20
+ semanticTokens,
21
+ componentTokens,
22
+ }) => {
23
+ const resolve = (state: IconButtonState) => componentTokens.iconButton({
24
+ themeTokens,
25
+ semanticResolvers: semanticTokens,
26
+ overrides: {
27
+ size: state.size,
28
+ color: state.color,
29
+ variant: state.variant,
30
+ },
31
+ state: toPressableInteractionState(state),
32
+ })
60
33
 
61
34
  return {
62
- button: createStyleResolver((state) => resolveState(state).button),
63
- icon: createValueResolver((state) => resolveState(state).icon),
35
+ container: createStyleResolver((state: IconButtonState): IconButtonStyle => ({
36
+ ...toContainerStyle(resolve(state).container),
37
+ })),
38
+ stateLayer: createStyleResolver((state: IconButtonState): IconButtonStyle => {
39
+ const tokens = resolve(state)
40
+ return {
41
+ ...toContainerStyle(tokens.stateLayer),
42
+ position: 'absolute',
43
+ top: 0,
44
+ right: 0,
45
+ bottom: 0,
46
+ left: 0,
47
+ }
48
+ }),
49
+ icon: createValueResolver((state: IconButtonState): IconButtonIconStyle => (
50
+ toIconStyle(resolve(state).icon)
51
+ )),
52
+ text: createStyleResolver((state: IconButtonState): IconButtonTextStyle => (
53
+ toTextStyle(resolve(state).text)
54
+ )),
64
55
  }
65
56
  }
66
-
67
- export const createIconButtonThemeFromDesign = (theme: DesignTokensTheme): IconButtonTheme => {
68
- return createIconButtonTheme({
69
- semantic: theme.semanticColors,
70
- coloring: theme.coloring as HightideComponentThemes['coloring'],
71
- })
72
- }
@@ -1,11 +1,17 @@
1
1
  export * from './avatar'
2
2
  export * from './button'
3
+ export * from './card'
3
4
  export * from './chat'
4
5
  export * from './checkbox'
5
6
  export * from './chip'
6
- export * from './coloring'
7
+ export * from './colorScheme'
8
+ export * from './divider'
9
+ export * from './icon'
7
10
  export * from './iconButton'
8
11
  export * from './input'
9
- export * from './menu'
12
+ export * from './listItem'
10
13
  export * from './multiSelect'
14
+ export * from './searchBar'
11
15
  export * from './select'
16
+ export * from './switch'
17
+ export * from './themedPressable'
@@ -1,57 +1,79 @@
1
- import type { TextStyle } from 'react-native'
2
-
3
- import { componentLayouts } from '@helpwave/hightide-design/tokens'
4
1
  import type {
5
- ComponentColorTokens,
6
- HightideThemeTokens as DesignTokensTheme
7
- } from '@helpwave/hightide-design/types'
8
-
9
- import type { HightideSemanticColors } from '../types/color'
2
+ InputState as DesignInputState,
3
+ InputStateValue
4
+ } from '@helpwave/hightide-design/component-token-resolvers'
5
+ import { toContainerStyleWithStateLayer } from '../adapters/container-adapter'
6
+ import { toIconStyle } from '../adapters/icon-style-adapter'
7
+ import { toTextStyle } from '../adapters/text-style-adapter'
10
8
  import type {
9
+ InputContainerStyle,
10
+ InputIconStyle,
11
+ InputPlaceholderStyle,
11
12
  InputState,
12
- InputTheme
13
+ InputTextStyle,
14
+ InputThemeResolvers
13
15
  } from '../types/components/input'
14
16
  import {
15
17
  createStyleResolver,
16
- createValueResolver
18
+ createValueResolver,
19
+ type ComponentThemeResolver
17
20
  } from '../types/resolver'
18
21
 
19
- export type CreateInputThemeOptions = {
20
- semantic: HightideSemanticColors,
21
- component: ComponentColorTokens,
22
- }
23
-
24
- export const createInputTheme = ({
25
- semantic,
26
- component,
27
- }: CreateInputThemeOptions): InputTheme => {
28
- const resolveInput = (state: InputState): TextStyle => {
29
- const sizing = componentLayouts.input.md
30
- const borderColor = state.isInvalid ? semantic.negative : component.border
22
+ const toDesignInputState = (state: InputState = {}): DesignInputState => {
23
+ const active = new Set<InputStateValue>()
31
24
 
32
- return {
33
- minHeight: sizing.height,
34
- paddingHorizontal: sizing.paddingX,
35
- paddingVertical: sizing.paddingY,
36
- borderRadius: sizing.borderRadius,
37
- borderWidth: 1,
38
- borderColor,
39
- backgroundColor: state.isDisabled ? semantic.disabled : component.input.background,
40
- color: state.isDisabled ? semantic.onDisabled : component.input.text,
41
- fontSize: 14,
42
- opacity: state.isDisabled ? 0.6 : 1,
43
- }
25
+ if (state.isDisabled) {
26
+ active.add('disabled')
44
27
  }
45
-
46
- return {
47
- input: createStyleResolver(resolveInput),
48
- placeholderColor: createValueResolver(() => semantic.placeholder),
28
+ if (state.isFocused) {
29
+ active.add('focused')
30
+ }
31
+ if (state.isFocusVisible) {
32
+ active.add('focusVisible')
33
+ }
34
+ if (state.isHovered) {
35
+ active.add('hovered')
49
36
  }
37
+ if (state.isPressed) {
38
+ active.add('pressed')
39
+ }
40
+ if (state.isReadonly) {
41
+ active.add('readonly')
42
+ }
43
+ if (state.isInvalid) {
44
+ active.add('invalid')
45
+ }
46
+
47
+ return active
50
48
  }
51
49
 
52
- export const createInputThemeFromDesign = (theme: DesignTokensTheme): InputTheme => {
53
- return createInputTheme({
54
- semantic: theme.semanticColors,
55
- component: theme.componentColors,
50
+ export const toInputThemeResolvers: ComponentThemeResolver<InputThemeResolvers> = ({
51
+ themeTokens,
52
+ semanticTokens,
53
+ componentTokens,
54
+ }) => {
55
+ const resolve = (state: InputState = {}) => componentTokens.input({
56
+ themeTokens,
57
+ semanticResolvers: semanticTokens,
58
+ overrides: {
59
+ color: state.color,
60
+ },
61
+ state: toDesignInputState(state),
56
62
  })
63
+
64
+ return {
65
+ container: createStyleResolver((state: InputState): InputContainerStyle => {
66
+ const { container, stateLayer } = resolve(state)
67
+ return toContainerStyleWithStateLayer(container, stateLayer)
68
+ }),
69
+ text: createStyleResolver((state: InputState): InputTextStyle => (
70
+ toTextStyle(resolve(state).text)
71
+ )),
72
+ placeholder: createStyleResolver((state: InputState): InputPlaceholderStyle => (
73
+ toTextStyle(resolve(state).placeholder)
74
+ )),
75
+ icon: createValueResolver((state: InputState): InputIconStyle => (
76
+ toIconStyle(resolve(state).icon)
77
+ )),
78
+ }
57
79
  }
@@ -0,0 +1,115 @@
1
+ import { toContainerStyle } from '../adapters/container-adapter'
2
+ import { toIconStyle } from '../adapters/icon-style-adapter'
3
+ import { toTextStyle } from '../adapters/text-style-adapter'
4
+ import type {
5
+ ListActionItemContentStyle,
6
+ ListActionItemIconStyle,
7
+ ListActionItemLeadingItemContainerStyle,
8
+ ListActionItemState,
9
+ ListActionItemStyle,
10
+ ListActionItemThemeResolvers,
11
+ ListActionItemTitleStyle,
12
+ ListActionItemTrailingItemContainerStyle,
13
+ ListItemContentStyle,
14
+ ListItemDefaultThemeResolvers,
15
+ ListItemDescriptionStyle,
16
+ ListItemIconStyle,
17
+ ListItemLeadingItemContainerStyle,
18
+ ListItemState,
19
+ ListItemStyle,
20
+ ListItemThemeResolvers,
21
+ ListItemTitleStyle,
22
+ ListItemTrailingItemContainerStyle
23
+ } from '../types/components/listItem'
24
+ import {
25
+ createStyleResolver,
26
+ createValueResolver,
27
+ toPressableInteractionState,
28
+ type ComponentThemeResolver
29
+ } from '../types/resolver'
30
+
31
+ const toListItemDefaultThemeResolvers: ComponentThemeResolver<ListItemDefaultThemeResolvers> = ({
32
+ themeTokens,
33
+ semanticTokens,
34
+ componentTokens,
35
+ }) => {
36
+ const resolve = (state: ListItemState = {}) => componentTokens.listItem.default({
37
+ themeTokens,
38
+ semanticResolvers: semanticTokens,
39
+ overrides: {
40
+ color: state.color,
41
+ },
42
+ })
43
+
44
+ return {
45
+ container: createStyleResolver((state: ListItemState): ListItemStyle => (
46
+ toContainerStyle(resolve(state).container)
47
+ )),
48
+ leadingItemContainer: createStyleResolver((state: ListItemState): ListItemLeadingItemContainerStyle => (
49
+ toContainerStyle(resolve(state).leadingItemContainer)
50
+ )),
51
+ content: createStyleResolver((state: ListItemState): ListItemContentStyle => ({
52
+ ...toContainerStyle(resolve(state).content),
53
+ flex: 1,
54
+ })),
55
+ trailingItemContainer: createStyleResolver((state: ListItemState): ListItemTrailingItemContainerStyle => (
56
+ toContainerStyle(resolve(state).trailingItemContainer)
57
+ )),
58
+ descriptionText: createStyleResolver((state: ListItemState): ListItemDescriptionStyle => (
59
+ toTextStyle(resolve(state).descriptionText)
60
+ )),
61
+ titleText: createStyleResolver((state: ListItemState): ListItemTitleStyle => (
62
+ toTextStyle(resolve(state).titleText)
63
+ )),
64
+ icon: createValueResolver((state: ListItemState): ListItemIconStyle => (
65
+ toIconStyle(resolve(state).icon)
66
+ )),
67
+ }
68
+ }
69
+
70
+ const toListActionItemThemeResolvers: ComponentThemeResolver<ListActionItemThemeResolvers> = ({
71
+ themeTokens,
72
+ semanticTokens,
73
+ componentTokens,
74
+ }) => {
75
+ const resolve = (state: ListActionItemState = {}) => componentTokens.listItem.action({
76
+ themeTokens,
77
+ semanticResolvers: semanticTokens,
78
+ overrides: {
79
+ color: state.color,
80
+ },
81
+ state: toPressableInteractionState(state),
82
+ })
83
+
84
+ return {
85
+ container: createStyleResolver((state: ListActionItemState): ListActionItemStyle => (
86
+ toContainerStyle(resolve(state).container)
87
+ )),
88
+ leadingItemContainer: createStyleResolver((state: ListActionItemState): ListActionItemLeadingItemContainerStyle => (
89
+ toContainerStyle(resolve(state).leadingItemContainer)
90
+ )),
91
+ content: createStyleResolver((state: ListActionItemState): ListActionItemContentStyle => ({
92
+ ...toContainerStyle(resolve(state).content),
93
+ flex: 1,
94
+ })),
95
+ trailingItemContainer: createStyleResolver((state: ListActionItemState): ListActionItemTrailingItemContainerStyle => (
96
+ toContainerStyle(resolve(state).trailingItemContainer)
97
+ )),
98
+ titleText: createStyleResolver((state: ListActionItemState): ListActionItemTitleStyle => (
99
+ toTextStyle(resolve(state).titleText)
100
+ )),
101
+ icon: createValueResolver((state: ListActionItemState): ListActionItemIconStyle => (
102
+ toIconStyle(resolve(state).icon)
103
+ )),
104
+ }
105
+ }
106
+
107
+ export const toListItemThemeResolvers: ComponentThemeResolver<ListItemThemeResolvers> = (params) => {
108
+ const action = toListActionItemThemeResolvers(params)
109
+
110
+ return {
111
+ default: toListItemDefaultThemeResolvers(params),
112
+ action,
113
+ navigation: action,
114
+ }
115
+ }
@@ -1,100 +1,175 @@
1
- import { fontWeights } from '@helpwave/hightide-design/tokens'
2
1
  import type {
3
- ComponentColorTokens,
4
- HightideThemeTokens as DesignTokensTheme
5
- } from '@helpwave/hightide-design/types'
6
-
7
- import type { HightideSemanticColors } from '../types/color'
2
+ MultiSelectState as DesignMultiSelectState,
3
+ MultiSelectStateValue
4
+ } from '@helpwave/hightide-design/component-token-resolvers'
5
+ import { toContainerStyle, toContainerStyleWithStateLayer } from '../adapters/container-adapter'
6
+ import { toIconStyle } from '../adapters/icon-style-adapter'
7
+ import { toTextStyle } from '../adapters/text-style-adapter'
8
8
  import type {
9
+ MultiSelectCheckboxIconStyle,
10
+ MultiSelectCheckboxStyle,
9
11
  MultiSelectOptionState,
12
+ MultiSelectOptionStyle,
13
+ MultiSelectOptionTextStyle,
10
14
  MultiSelectState,
11
- MultiSelectTheme
15
+ MultiSelectThemeResolvers,
16
+ MultiSelectTriggerStyle
12
17
  } from '../types/components/multiSelect'
18
+ import type {
19
+ SelectEmptyTextStyle,
20
+ SelectHeaderStyle,
21
+ SelectMenuState,
22
+ SelectMenuStyle,
23
+ SelectOverlayStyle,
24
+ SelectTriggerTextStyle
25
+ } from '../types/components/select'
13
26
  import {
27
+ createSimpleStyleResolver,
14
28
  createStyleResolver,
15
- createValueResolver
29
+ createValueResolver,
30
+ type ComponentThemeResolver
16
31
  } from '../types/resolver'
17
32
 
18
- export type CreateMultiSelectThemeOptions = {
19
- semantic: HightideSemanticColors,
20
- component: ComponentColorTokens,
33
+ type MultiSelectResolveState = {
34
+ color?: MultiSelectState['color'],
35
+ isDisabled?: boolean,
36
+ isInvalid?: boolean,
37
+ isHovered?: boolean,
38
+ isFocused?: boolean,
39
+ isFocusVisible?: boolean,
40
+ isPressed?: boolean,
41
+ isReadonly?: boolean,
42
+ isOpen?: boolean,
43
+ hasSelections?: boolean,
44
+ isSelected?: boolean,
45
+ isHighlighted?: boolean,
46
+ hasSearch?: boolean,
21
47
  }
22
48
 
23
- export const createMultiSelectTheme = ({
24
- semantic,
25
- component,
26
- }: CreateMultiSelectThemeOptions): MultiSelectTheme => {
27
- return {
28
- trigger: createStyleResolver((state: MultiSelectState) => ({
29
- minHeight: 44,
30
- paddingHorizontal: 12,
31
- paddingVertical: 8,
32
- borderRadius: 6,
33
- borderWidth: 1,
34
- borderColor: state.isInvalid ? semantic.negative : component.border,
35
- backgroundColor: state.isDisabled ? semantic.disabled : component.input.background,
36
- justifyContent: 'center',
37
- gap: 8,
38
- opacity: state.isDisabled ? 0.6 : 1,
39
- })),
40
- triggerText: createStyleResolver(() => ({
41
- color: semantic.placeholder,
42
- })),
43
- overlay: createStyleResolver(() => ({
44
- flex: 1,
45
- backgroundColor: '#00000059',
46
- justifyContent: 'center',
47
- padding: 24,
48
- })),
49
- menu: createStyleResolver(() => ({
50
- maxHeight: 360,
51
- borderRadius: 12,
52
- backgroundColor: component.menu.background,
53
- borderWidth: 1,
54
- borderColor: component.menu.border,
55
- overflow: 'hidden',
56
- })),
57
- search: createStyleResolver(() => ({
58
- paddingHorizontal: 16,
59
- paddingVertical: 12,
60
- borderBottomWidth: 1,
61
- borderBottomColor: component.menu.border,
62
- color: component.menu.text,
63
- })),
64
- searchPlaceholderColor: createValueResolver(() => semantic.placeholder),
65
- option: createStyleResolver((state: MultiSelectOptionState) => ({
66
- paddingHorizontal: 16,
67
- paddingVertical: 12,
68
- backgroundColor: state.isHighlighted ? component.table.rowHoverBackground : 'transparent',
69
- opacity: state.isDisabled ? 0.5 : 1,
70
- flexDirection: 'row',
71
- alignItems: 'center',
72
- gap: 10,
73
- })),
74
- optionText: createStyleResolver((state: MultiSelectOptionState) => ({
75
- color: state.isSelected ? semantic.primary : component.menu.text,
76
- fontWeight: state.isSelected ? fontWeights.semibold : fontWeights.base,
77
- })),
78
- checkbox: createStyleResolver((state: MultiSelectOptionState) => ({
79
- width: 18,
80
- height: 18,
81
- borderRadius: 4,
82
- borderWidth: 1,
83
- borderColor: state.isSelected ? semantic.primary : component.border,
84
- backgroundColor: state.isSelected ? semantic.primary : 'transparent',
85
- alignItems: 'center',
86
- justifyContent: 'center',
87
- })),
88
- checkboxIcon: createValueResolver((state: MultiSelectOptionState) => ({
89
- color: semantic.onPrimary,
90
- visible: !!state.isSelected,
91
- })),
49
+ const toDesignMultiSelectState = (
50
+ state: MultiSelectResolveState = {}
51
+ ): DesignMultiSelectState => {
52
+ const active = new Set<MultiSelectStateValue>()
53
+
54
+ if (state.isDisabled) {
55
+ active.add('disabled')
56
+ }
57
+ if (state.isFocused) {
58
+ active.add('focused')
59
+ }
60
+ if (state.isFocusVisible) {
61
+ active.add('focusVisible')
92
62
  }
63
+ if (state.isHovered) {
64
+ active.add('hovered')
65
+ }
66
+ if (state.isPressed) {
67
+ active.add('pressed')
68
+ }
69
+ if (state.isReadonly) {
70
+ active.add('readonly')
71
+ }
72
+ if (state.isInvalid) {
73
+ active.add('invalid')
74
+ }
75
+ if (state.isOpen) {
76
+ active.add('open')
77
+ }
78
+ if (state.hasSelections) {
79
+ active.add('hasSelections')
80
+ }
81
+ if (state.isSelected) {
82
+ active.add('selected')
83
+ }
84
+ if (state.isHighlighted) {
85
+ active.add('highlighted')
86
+ }
87
+
88
+ return active
93
89
  }
94
90
 
95
- export const createMultiSelectThemeFromDesign = (theme: DesignTokensTheme): MultiSelectTheme => {
96
- return createMultiSelectTheme({
97
- semantic: theme.semanticColors,
98
- component: theme.componentColors,
91
+ export const toMultiSelectThemeResolvers: ComponentThemeResolver<MultiSelectThemeResolvers> = ({
92
+ themeTokens,
93
+ semanticTokens,
94
+ componentTokens,
95
+ }) => {
96
+ const resolve = (state: MultiSelectResolveState = {}) => componentTokens.multiSelect({
97
+ themeTokens,
98
+ semanticResolvers: semanticTokens,
99
+ config: {
100
+ hasSearch: state.hasSearch,
101
+ },
102
+ overrides: {
103
+ color: state.color,
104
+ },
105
+ state: toDesignMultiSelectState(state),
106
+ })
107
+
108
+ const toTriggerState = (state: MultiSelectState): MultiSelectResolveState => ({
109
+ color: state.color,
110
+ isDisabled: state.isDisabled,
111
+ isInvalid: state.isInvalid,
112
+ isHovered: state.isHovered,
113
+ isFocused: state.isFocused,
114
+ isFocusVisible: state.isFocusVisible,
115
+ isPressed: state.isPressed,
116
+ isReadonly: state.isReadonly,
117
+ isOpen: state.isOpen,
118
+ hasSelections: state.hasSelections,
99
119
  })
120
+
121
+ return {
122
+ trigger: createStyleResolver((state: MultiSelectState): MultiSelectTriggerStyle => {
123
+ const { trigger, stateLayer } = resolve(toTriggerState(state))
124
+ return toContainerStyleWithStateLayer(trigger, stateLayer)
125
+ }),
126
+ triggerText: createStyleResolver((state: MultiSelectState): SelectTriggerTextStyle => (
127
+ toTextStyle(resolve(toTriggerState(state)).triggerText)
128
+ )),
129
+ overlay: createSimpleStyleResolver((): SelectOverlayStyle => ({
130
+ ...toContainerStyle(resolve().overlay),
131
+ flex: 1,
132
+ })),
133
+ menu: createStyleResolver((state: SelectMenuState): SelectMenuStyle => (
134
+ toContainerStyle(resolve({ hasSearch: state.hasSearch }).menu)
135
+ )),
136
+ header: createSimpleStyleResolver((): SelectHeaderStyle => (
137
+ toContainerStyle(resolve().header)
138
+ )),
139
+ option: createStyleResolver((state: MultiSelectOptionState): MultiSelectOptionStyle => (
140
+ toContainerStyle(resolve({
141
+ color: state.color,
142
+ isDisabled: state.isDisabled,
143
+ isSelected: state.isSelected,
144
+ isHighlighted: state.isHighlighted,
145
+ }).option)
146
+ )),
147
+ optionText: createStyleResolver((state: MultiSelectOptionState): MultiSelectOptionTextStyle => (
148
+ toTextStyle(resolve({
149
+ color: state.color,
150
+ isDisabled: state.isDisabled,
151
+ isSelected: state.isSelected,
152
+ isHighlighted: state.isHighlighted,
153
+ }).optionText)
154
+ )),
155
+ emptyText: createSimpleStyleResolver((): SelectEmptyTextStyle => (
156
+ toTextStyle(resolve().emptyText)
157
+ )),
158
+ checkbox: createStyleResolver((state: MultiSelectOptionState): MultiSelectCheckboxStyle => (
159
+ toContainerStyle(resolve({
160
+ color: state.color,
161
+ isDisabled: state.isDisabled,
162
+ isSelected: state.isSelected,
163
+ isHighlighted: state.isHighlighted,
164
+ }).checkbox)
165
+ )),
166
+ checkboxIcon: createValueResolver((state: MultiSelectOptionState): MultiSelectCheckboxIconStyle => (
167
+ toIconStyle(resolve({
168
+ color: state.color,
169
+ isDisabled: state.isDisabled,
170
+ isSelected: state.isSelected,
171
+ isHighlighted: state.isHighlighted,
172
+ }).checkboxIcon)
173
+ )),
174
+ }
100
175
  }