@seamapi/react 0.26.1 → 0.28.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 (84) hide show
  1. package/index.css +166 -3
  2. package/index.css.map +1 -1
  3. package/index.min.css +1 -1
  4. package/index.min.css.map +1 -1
  5. package/lib/capitalize.d.ts +1 -0
  6. package/lib/capitalize.js +4 -0
  7. package/lib/capitalize.js.map +1 -0
  8. package/lib/icons/ChevronDown.d.ts +2 -0
  9. package/lib/icons/ChevronDown.js +3 -0
  10. package/lib/icons/ChevronDown.js.map +1 -0
  11. package/lib/icons/Close.d.ts +2 -0
  12. package/lib/icons/Close.js +5 -0
  13. package/lib/icons/Close.js.map +1 -0
  14. package/lib/icons/Search.d.ts +2 -0
  15. package/lib/icons/Search.js +5 -0
  16. package/lib/icons/Search.js.map +1 -0
  17. package/lib/ui/Button.d.ts +4 -3
  18. package/lib/ui/Button.js +2 -2
  19. package/lib/ui/Button.js.map +1 -1
  20. package/lib/ui/DeviceTable/DeviceTable.js +9 -1
  21. package/lib/ui/DeviceTable/DeviceTable.js.map +1 -1
  22. package/lib/ui/SupportedDeviceTable/FilterCategoryMenu.d.ts +21 -0
  23. package/lib/ui/SupportedDeviceTable/FilterCategoryMenu.js +20 -0
  24. package/lib/ui/SupportedDeviceTable/FilterCategoryMenu.js.map +1 -0
  25. package/lib/ui/SupportedDeviceTable/SupportedDeviceContent.d.ts +4 -0
  26. package/lib/ui/SupportedDeviceTable/SupportedDeviceContent.js +53 -0
  27. package/lib/ui/SupportedDeviceTable/SupportedDeviceContent.js.map +1 -0
  28. package/lib/ui/SupportedDeviceTable/SupportedDeviceFilterArea.d.ts +11 -0
  29. package/lib/ui/SupportedDeviceTable/SupportedDeviceFilterArea.js +50 -0
  30. package/lib/ui/SupportedDeviceTable/SupportedDeviceFilterArea.js.map +1 -0
  31. package/lib/ui/SupportedDeviceTable/SupportedDeviceHeader.d.ts +1 -0
  32. package/lib/ui/{SupportedDevices/SupportedDevicesHeader.js → SupportedDeviceTable/SupportedDeviceHeader.js} +2 -2
  33. package/lib/ui/SupportedDeviceTable/SupportedDeviceHeader.js.map +1 -0
  34. package/lib/ui/{SupportedDevices → SupportedDeviceTable}/SupportedDeviceRow.d.ts +1 -1
  35. package/lib/ui/SupportedDeviceTable/SupportedDeviceRow.js.map +1 -0
  36. package/lib/ui/SupportedDeviceTable/SupportedDeviceTable.d.ts +3 -0
  37. package/lib/ui/SupportedDeviceTable/SupportedDeviceTable.js +8 -0
  38. package/lib/ui/SupportedDeviceTable/SupportedDeviceTable.js.map +1 -0
  39. package/lib/ui/{SupportedDevices → SupportedDeviceTable}/types.d.ts +5 -0
  40. package/lib/ui/SupportedDeviceTable/types.js.map +1 -0
  41. package/lib/ui/Table/TableHeader.js +1 -1
  42. package/lib/ui/Table/TableHeader.js.map +1 -1
  43. package/lib/ui/TextField/SearchTextField.d.ts +1 -1
  44. package/lib/ui/TextField/SearchTextField.js +52 -2
  45. package/lib/ui/TextField/SearchTextField.js.map +1 -1
  46. package/lib/ui/TextField/TextField.d.ts +11 -4
  47. package/lib/ui/TextField/TextField.js +5 -3
  48. package/lib/ui/TextField/TextField.js.map +1 -1
  49. package/lib/ui/index.d.ts +1 -1
  50. package/lib/ui/index.js +1 -1
  51. package/lib/ui/index.js.map +1 -1
  52. package/package.json +1 -1
  53. package/src/lib/capitalize.ts +3 -0
  54. package/src/lib/icons/ChevronDown.tsx +19 -0
  55. package/src/lib/icons/Close.tsx +30 -0
  56. package/src/lib/icons/Search.tsx +30 -0
  57. package/src/lib/ui/Button.tsx +12 -6
  58. package/src/lib/ui/DeviceTable/DeviceTable.tsx +16 -1
  59. package/src/lib/ui/SupportedDeviceTable/FilterCategoryMenu.tsx +65 -0
  60. package/src/lib/ui/SupportedDeviceTable/SupportedDeviceContent.tsx +147 -0
  61. package/src/lib/ui/SupportedDeviceTable/SupportedDeviceFilterArea.tsx +131 -0
  62. package/src/lib/ui/{SupportedDevices/SupportedDevicesHeader.tsx → SupportedDeviceTable/SupportedDeviceHeader.tsx} +1 -1
  63. package/src/lib/ui/{SupportedDevices → SupportedDeviceTable}/SupportedDeviceRow.tsx +1 -1
  64. package/src/lib/ui/SupportedDeviceTable/SupportedDeviceTable.tsx +16 -0
  65. package/src/lib/ui/{SupportedDevices → SupportedDeviceTable}/types.ts +6 -0
  66. package/src/lib/ui/Table/TableHeader.tsx +5 -1
  67. package/src/lib/ui/TextField/SearchTextField.tsx +79 -0
  68. package/src/lib/ui/TextField/TextField.tsx +43 -12
  69. package/src/lib/ui/index.ts +1 -1
  70. package/src/styles/_colors.scss +1 -0
  71. package/src/styles/_device-table.scss +4 -0
  72. package/src/styles/_inputs.scss +74 -2
  73. package/src/styles/_supported-devices.scss +114 -0
  74. package/src/styles/_tables.scss +8 -1
  75. package/lib/ui/SupportedDevices/SupportedDeviceRow.js.map +0 -1
  76. package/lib/ui/SupportedDevices/SupportedDevices.d.ts +0 -1
  77. package/lib/ui/SupportedDevices/SupportedDevices.js +0 -21
  78. package/lib/ui/SupportedDevices/SupportedDevices.js.map +0 -1
  79. package/lib/ui/SupportedDevices/SupportedDevicesHeader.d.ts +0 -1
  80. package/lib/ui/SupportedDevices/SupportedDevicesHeader.js.map +0 -1
  81. package/lib/ui/SupportedDevices/types.js.map +0 -1
  82. package/src/lib/ui/SupportedDevices/SupportedDevices.tsx +0 -65
  83. /package/lib/ui/{SupportedDevices → SupportedDeviceTable}/SupportedDeviceRow.js +0 -0
  84. /package/lib/ui/{SupportedDevices → SupportedDeviceTable}/types.js +0 -0
@@ -0,0 +1,147 @@
1
+ import { useQuery } from '@tanstack/react-query'
2
+ import axios from 'axios'
3
+ import { useCallback, useEffect, useState } from 'react'
4
+
5
+ import { Button } from 'lib/ui/Button.js'
6
+ import { SupportedDeviceFilterArea } from 'lib/ui/SupportedDeviceTable/SupportedDeviceFilterArea.js'
7
+ import type { DeviceModel, Filters } from 'lib/ui/SupportedDeviceTable/types.js'
8
+
9
+ import { SupportedDeviceHeader } from './SupportedDeviceHeader.js'
10
+ import { SupportedDeviceRow } from './SupportedDeviceRow.js'
11
+
12
+ const BASE_URL = 'https://devicedb.seam.co/api/device_models/list'
13
+
14
+ export interface SupportedDeviceContentProps {
15
+ cannotFilter?: boolean
16
+ }
17
+
18
+ export function SupportedDeviceContent({
19
+ cannotFilter = false,
20
+ }: SupportedDeviceContentProps) {
21
+ const [allDeviceModels, setAllDeviceModels] = useState<DeviceModel[]>([])
22
+ const [filterValue, setFilterValue] = useState('')
23
+ const [filters, setFilters] = useState<Filters>({
24
+ supportedOnly: false,
25
+ category: null,
26
+ brand: null,
27
+ })
28
+
29
+ const fetchDeviceModels = useCallback(async () => {
30
+ const queries = []
31
+
32
+ if (filterValue.trim() !== '') {
33
+ queries.push(`text_search=${encodeURIComponent(filterValue.trim())}`)
34
+ }
35
+
36
+ if (filters.supportedOnly) {
37
+ queries.push('support_level=live')
38
+ }
39
+
40
+ if (filters.category !== null) {
41
+ queries.push(`main_category=${encodeURIComponent(filters.category)}`)
42
+ }
43
+
44
+ if (filters.brand !== null) {
45
+ queries.push(`brand=${encodeURIComponent(filters.brand)}`)
46
+ }
47
+
48
+ const url = `${BASE_URL}?${queries.join('&')}`
49
+ return await axios.get(url)
50
+ }, [filterValue, filters])
51
+
52
+ const { data, isLoading, isError, refetch } = useQuery<{
53
+ data: {
54
+ device_models?: DeviceModel[]
55
+ }
56
+ }>({
57
+ queryKey: ['supported_devices', filterValue, filters],
58
+ queryFn: fetchDeviceModels,
59
+ })
60
+
61
+ useEffect(() => {
62
+ if (
63
+ data?.data?.device_models !== undefined &&
64
+ allDeviceModels.length === 0
65
+ ) {
66
+ setAllDeviceModels(data.data.device_models)
67
+ }
68
+ }, [data, allDeviceModels.length])
69
+
70
+ const deviceModels = data?.data?.device_models ?? []
71
+
72
+ return (
73
+ <div className='seam-supported-devices-table-wrap'>
74
+ {!cannotFilter && (
75
+ <SupportedDeviceFilterArea
76
+ deviceModels={allDeviceModels}
77
+ filterValue={filterValue}
78
+ setFilterValue={setFilterValue}
79
+ filters={filters}
80
+ setFilters={setFilters}
81
+ />
82
+ )}
83
+
84
+ {isLoading && (
85
+ <div className='seam-supported-devices-table-state-block'>
86
+ <p>Loading device models...</p>
87
+ </div>
88
+ )}
89
+
90
+ {isError && (
91
+ <div className='seam-supported-devices-table-state-block'>
92
+ <p>There was an error fetching device models.</p>
93
+ <Button
94
+ variant='solid'
95
+ size='small'
96
+ onClick={() => {
97
+ void refetch()
98
+ }}
99
+ >
100
+ Retry
101
+ </Button>
102
+ </div>
103
+ )}
104
+
105
+ {!isLoading && !isError && data?.data?.device_models !== null && (
106
+ <table className='seam-supported-devices-table'>
107
+ <SupportedDeviceHeader />
108
+ <tbody>
109
+ {deviceModels.length !== 0 &&
110
+ deviceModels.map((deviceModel, index) => (
111
+ <SupportedDeviceRow
112
+ key={`${index}:${deviceModel.manufacturer_model_id}`}
113
+ deviceModel={deviceModel}
114
+ />
115
+ ))}
116
+
117
+ {deviceModels.length === 0 && (
118
+ <tr className='seam-supported-devices-table-message-row'>
119
+ <td colSpan={6}>
120
+ <div className='seam-supported-devices-table-message'>
121
+ {filterValue.length === 0 ? (
122
+ <p>No device models found.</p>
123
+ ) : (
124
+ <>
125
+ <p>No device models matched your search.</p>
126
+ <Button
127
+ variant='outline'
128
+ size='small'
129
+ onClick={() => {
130
+ setFilterValue('')
131
+ }}
132
+ className='seam-supported-devices-table-message-clear-search'
133
+ >
134
+ Clear search terms
135
+ </Button>
136
+ </>
137
+ )}
138
+ </div>
139
+ </td>
140
+ </tr>
141
+ )}
142
+ </tbody>
143
+ </table>
144
+ )}
145
+ </div>
146
+ )
147
+ }
@@ -0,0 +1,131 @@
1
+ import type { Dispatch, SetStateAction } from 'react'
2
+
3
+ import { capitalize } from 'lib/capitalize.js'
4
+ import { Button } from 'lib/ui/Button.js'
5
+ import { Menu } from 'lib/ui/Menu/Menu.js'
6
+ import { FilterCategoryMenu } from 'lib/ui/SupportedDeviceTable/FilterCategoryMenu.js'
7
+ import type { DeviceModel, Filters } from 'lib/ui/SupportedDeviceTable/types.js'
8
+ import { SearchTextField } from 'lib/ui/TextField/SearchTextField.js'
9
+
10
+ interface SupportedDeviceFilterAreaProps {
11
+ deviceModels: DeviceModel[]
12
+ filterValue: string
13
+ setFilterValue: (filter: string) => void
14
+ filters: Filters
15
+ setFilters: Dispatch<SetStateAction<Filters>>
16
+ }
17
+
18
+ export function SupportedDeviceFilterArea({
19
+ deviceModels,
20
+ filterValue,
21
+ setFilterValue,
22
+ filters,
23
+ setFilters,
24
+ }: SupportedDeviceFilterAreaProps) {
25
+ const appliedFilters = Object.keys(filters).filter((key) => {
26
+ const filterProperty = filters[key as keyof Filters]
27
+ return (
28
+ filterProperty !== undefined &&
29
+ filterProperty !== null &&
30
+ filterProperty !== false
31
+ )
32
+ })
33
+ const appliedFiltersCount = appliedFilters.length
34
+
35
+ const getAvailablePropertiesFromDeviceModels = (
36
+ property: keyof DeviceModel
37
+ ) => {
38
+ const properties = new Set<string>()
39
+ deviceModels.forEach((deviceModel) => {
40
+ properties.add(capitalize(deviceModel[property]))
41
+ })
42
+ return Array.from(properties)
43
+ }
44
+
45
+ const resetFilter = (filterType: keyof Filters) => {
46
+ setFilters((filters) => ({
47
+ ...filters,
48
+ [filterType]: null,
49
+ }))
50
+ }
51
+
52
+ const isPlural = appliedFiltersCount > 1
53
+ const filterButtonLabel = isPlural
54
+ ? `Filters (${appliedFiltersCount})`
55
+ : 'Filter'
56
+
57
+ return (
58
+ <div className='seam-supported-devices-filter-area'>
59
+ <div className='seam-deliberate-block' />
60
+ <div className='seam-filters-wrap'>
61
+ <Menu
62
+ renderButton={({ onOpen }) => (
63
+ <Button
64
+ variant='outline'
65
+ className='seam-filters-button'
66
+ onClick={onOpen}
67
+ >
68
+ {filterButtonLabel}
69
+ </Button>
70
+ )}
71
+ >
72
+ <div
73
+ className='seam-supported-devices-filter-menu'
74
+ onClick={(event) => {
75
+ event.stopPropagation()
76
+ }}
77
+ >
78
+ <div className='seam-filter-menu-row'>
79
+ <FilterCategoryMenu
80
+ label='Brand'
81
+ options={getAvailablePropertiesFromDeviceModels('brand')}
82
+ onSelect={(brand: string) => {
83
+ setFilters((filters) => ({
84
+ ...filters,
85
+ brand,
86
+ }))
87
+ }}
88
+ buttonLabel={filters.brand ?? 'All'}
89
+ onAllOptionSelect={() => {
90
+ resetFilter('brand')
91
+ }}
92
+ />
93
+ </div>
94
+
95
+ <div className='seam-filter-menu-row'>
96
+ <label
97
+ htmlFor='supportedOnly'
98
+ className='seam-filter-checkbox-label'
99
+ >
100
+ <p>Supported</p>
101
+ <input
102
+ id='supportedOnly'
103
+ name='supportedOnly'
104
+ type='checkbox'
105
+ className='seam-filter-checkbox'
106
+ checked={filters.supportedOnly}
107
+ onChange={(event) => {
108
+ setFilters((filters) => ({
109
+ ...filters,
110
+ supportedOnly: event.target.checked,
111
+ }))
112
+ }}
113
+ />
114
+ </label>
115
+ </div>
116
+ </div>
117
+ </Menu>
118
+
119
+ <div className='seam-supported-devices-filter-area-search-bar-wrap'>
120
+ <SearchTextField
121
+ value={filterValue}
122
+ onChange={(value) => {
123
+ setFilterValue(value)
124
+ }}
125
+ className='seam-supported-devices-filter-area-search-bar'
126
+ />
127
+ </div>
128
+ </div>
129
+ </div>
130
+ )
131
+ }
@@ -1,4 +1,4 @@
1
- export function SupportedDevicesHeader() {
1
+ export function SupportedDeviceHeader() {
2
2
  return (
3
3
  <thead>
4
4
  <tr>
@@ -1,6 +1,6 @@
1
1
  import classNames from 'classnames'
2
2
 
3
- import type { DeviceModel } from 'lib/ui/SupportedDevices/types.js'
3
+ import type { DeviceModel } from 'lib/ui/SupportedDeviceTable/types.js'
4
4
 
5
5
  export interface SupportedDeviceRowProps {
6
6
  deviceModel: DeviceModel
@@ -0,0 +1,16 @@
1
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
2
+
3
+ import type { SupportedDeviceContentProps } from 'lib/ui/SupportedDeviceTable/SupportedDeviceContent.js'
4
+ import { SupportedDeviceContent } from 'lib/ui/SupportedDeviceTable/SupportedDeviceContent.js'
5
+
6
+ const client = new QueryClient()
7
+
8
+ export type SupportedDeviceTableProps = SupportedDeviceContentProps
9
+
10
+ export function SupportedDeviceTable(props: SupportedDeviceContentProps) {
11
+ return (
12
+ <QueryClientProvider client={client}>
13
+ <SupportedDeviceContent {...props} />
14
+ </QueryClientProvider>
15
+ )
16
+ }
@@ -8,3 +8,9 @@ export interface DeviceModel {
8
8
  icon_url: string
9
9
  seam_device_model_page_url: string
10
10
  }
11
+
12
+ export interface Filters {
13
+ supportedOnly: boolean
14
+ category: string | null
15
+ brand: string | null
16
+ }
@@ -1,5 +1,9 @@
1
1
  import type { PropsWithChildren } from 'react'
2
2
 
3
3
  export function TableHeader({ children }: PropsWithChildren): JSX.Element {
4
- return <div className='seam-table-header'>{children}</div>
4
+ return (
5
+ <div className='seam-table-header'>
6
+ <div className='seam-body'>{children}</div>
7
+ </div>
8
+ )
5
9
  }
@@ -1,15 +1,94 @@
1
1
  import classNames from 'classnames'
2
+ import { useEffect, useState } from 'react'
2
3
 
4
+ import { CloseIcon } from 'lib/icons/Close.js'
5
+ import { SearchIcon } from 'lib/icons/Search.js'
3
6
  import { TextField, type TextFieldProps } from 'lib/ui/TextField/TextField.js'
4
7
 
5
8
  export function SearchTextField({
6
9
  className,
10
+ value,
11
+ onChange,
7
12
  ...props
8
13
  }: TextFieldProps): JSX.Element {
14
+ const [inputEl, setInputEl] = useState<HTMLInputElement | null>(null)
15
+
16
+ const valueIsEmpty = useValueIsEmpty(value, inputEl)
17
+ const clearInput = () => {
18
+ if (onChange != null) {
19
+ onChange('')
20
+ return
21
+ }
22
+
23
+ if (inputEl != null) {
24
+ inputEl.value = ''
25
+ }
26
+ }
27
+
9
28
  return (
10
29
  <TextField
30
+ value={value}
31
+ onChange={onChange}
11
32
  {...props}
12
33
  className={classNames(className, 'seam-search-text-field')}
34
+ startAdornment={<SearchIcon />}
35
+ inputProps={{
36
+ ref: setInputEl,
37
+ placeholder: 'Search',
38
+ }}
39
+ endAdornment={
40
+ <button
41
+ className={classNames({
42
+ 'seam-hidden': valueIsEmpty,
43
+ })}
44
+ onClick={clearInput}
45
+ >
46
+ <CloseIcon />
47
+ </button>
48
+ }
13
49
  />
14
50
  )
15
51
  }
52
+
53
+ function useValueIsEmpty(
54
+ value: string | undefined,
55
+ inputEl: HTMLInputElement | null
56
+ ) {
57
+ const [valueIsEmpty, setValueIsEmpty] = useState(true)
58
+
59
+ // If this is a controlled element, we'll just look at `value`
60
+ useEffect(() => {
61
+ setValueIsEmpty(value == null || value === '')
62
+ }, [value])
63
+
64
+ // If this is not a controlled element, we'll need to listen to `input`
65
+ // events.
66
+ useEffect(() => {
67
+ if (inputEl == null) {
68
+ return
69
+ }
70
+
71
+ const handler = (event: Event) => {
72
+ if (value !== undefined) {
73
+ return
74
+ }
75
+
76
+ if (event.target == null || !('value' in event.target)) {
77
+ return
78
+ }
79
+
80
+ const inputValue = event.target.value
81
+ if (value === undefined) {
82
+ setValueIsEmpty(inputValue === '')
83
+ }
84
+ }
85
+
86
+ inputEl.addEventListener('input', handler)
87
+
88
+ return () => {
89
+ inputEl.removeEventListener('input', handler)
90
+ }
91
+ }, [inputEl, value])
92
+
93
+ return valueIsEmpty
94
+ }
@@ -1,21 +1,52 @@
1
1
  import classNames from 'classnames'
2
- import type { ChangeEvent, InputHTMLAttributes } from 'react'
2
+ import type { ChangeEvent, InputHTMLAttributes, MutableRefObject } from 'react'
3
3
 
4
- export type TextFieldProps = {
4
+ export interface TextFieldProps {
5
5
  value?: string
6
- onChange: (value: string) => void
7
- } & Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange'>
6
+ onChange?: (value: string) => void
7
+ startAdornment?: JSX.Element
8
+ endAdornment?: JSX.Element
9
+ disabled?: boolean
10
+ className?: string
11
+ inputProps?: {
12
+ ref?:
13
+ | MutableRefObject<HTMLInputElement | null>
14
+ | ((inputEl: HTMLInputElement) => void)
15
+ } & Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange'>
16
+ }
8
17
 
9
18
  export function TextField(props: TextFieldProps): JSX.Element {
10
- const { value, onChange, className, ...inputProps } = props
19
+ const {
20
+ value,
21
+ onChange,
22
+ className,
23
+ startAdornment,
24
+ endAdornment,
25
+ inputProps,
26
+ disabled,
27
+ } = props
28
+
11
29
  return (
12
- <input
13
- className={classNames('seam-text-field', className)}
14
- value={value}
15
- onChange={handleString(onChange)}
16
- type='text'
17
- {...inputProps}
18
- />
30
+ <div
31
+ className={classNames('seam-text-field', className, {
32
+ 'seam-disabled': disabled,
33
+ })}
34
+ >
35
+ {startAdornment != null && (
36
+ <div className='seam-adornment seam-start'>{startAdornment}</div>
37
+ )}
38
+ <input
39
+ className='seam-text-field-input'
40
+ value={value}
41
+ onChange={onChange == null ? undefined : handleString(onChange)}
42
+ type='text'
43
+ disabled={disabled}
44
+ {...inputProps}
45
+ />
46
+ {endAdornment != null && (
47
+ <div className='seam-adornment seam-end'>{endAdornment}</div>
48
+ )}
49
+ </div>
19
50
  )
20
51
  }
21
52
 
@@ -2,4 +2,4 @@ export * from './AccessCodeDetails/AccessCodeDetails.js'
2
2
  export * from './AccessCodeTable/AccessCodeTable.js'
3
3
  export * from './DeviceDetails/DeviceDetails.js'
4
4
  export * from './DeviceTable/DeviceTable.js'
5
- export * from './SupportedDevices/SupportedDevices.js'
5
+ export * from './SupportedDeviceTable/SupportedDeviceTable.js'
@@ -18,6 +18,7 @@ $bg-d: #cbd0d3;
18
18
  $bg-c: #dde0e2;
19
19
  $bg-b: #e9edef;
20
20
  $bg-a: #f1f3f4;
21
+ $bg-gray: #ececec;
21
22
  $status-red: #e36857;
22
23
  $status-orange: #f3980f;
23
24
  $status-green: #27ae60;
@@ -2,6 +2,10 @@
2
2
 
3
3
  @mixin all {
4
4
  .seam-device-table {
5
+ .seam-search-text-field {
6
+ width: 170px;
7
+ }
8
+
5
9
  .seam-table-row {
6
10
  cursor: pointer;
7
11
 
@@ -2,10 +2,82 @@
2
2
 
3
3
  @mixin text-field {
4
4
  .seam-text-field {
5
- padding: 0 8px;
6
- height: 40px;
7
5
  border: 1px solid colors.$text-gray-3;
8
6
  border-radius: 8px;
7
+ padding: 0 8px;
8
+ display: flex;
9
+ align-items: center;
10
+
11
+ &:hover {
12
+ border-color: colors.$text-default;
13
+ }
14
+
15
+ &:focus-within {
16
+ border-color: colors.$primary;
17
+ }
18
+
19
+ &.seam-disabled {
20
+ border-color: colors.$text-gray-3;
21
+ background: colors.$bg-gray;
22
+ cursor: not-allowed;
23
+
24
+ .seam-text-field-input {
25
+ cursor: not-allowed;
26
+ }
27
+ }
28
+
29
+ .seam-text-field-input {
30
+ height: 40px;
31
+ border: 0;
32
+
33
+ &:focus {
34
+ outline: none;
35
+ }
36
+ }
37
+
38
+ .seam-adornment {
39
+ display: flex;
40
+ align-items: center;
41
+
42
+ svg {
43
+ scale: 0.8333;
44
+ }
45
+
46
+ &.seam-start {
47
+ margin-right: 4px;
48
+ }
49
+
50
+ button {
51
+ width: 24px;
52
+ height: 24px;
53
+ display: flex;
54
+ justify-content: center;
55
+ align-items: center;
56
+ appearance: none;
57
+ background-color: transparent;
58
+ padding: 0;
59
+ margin: 0;
60
+ border: 0;
61
+ box-shadow: none;
62
+ border-radius: 4px;
63
+ cursor: pointer;
64
+
65
+ &:hover {
66
+ background-color: colors.$bg-b;
67
+ }
68
+
69
+ &.seam-hidden {
70
+ opacity: 0;
71
+ visibility: hidden;
72
+ }
73
+ }
74
+ }
75
+
76
+ &.seam-search-text-field {
77
+ .seam-text-field-input {
78
+ width: 100%;
79
+ }
80
+ }
9
81
  }
10
82
  }
11
83