@seamapi/react 4.2.0 → 4.3.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/README.md +2 -2
- package/dist/elements.js +4652 -4471
- package/dist/elements.js.map +1 -1
- package/dist/index.css +49 -0
- package/dist/index.css.map +1 -1
- package/dist/index.min.css +1 -1
- package/dist/index.min.css.map +1 -1
- package/lib/icons/Edit.js +2 -4
- package/lib/icons/Edit.js.map +1 -1
- package/lib/seam/components/DeviceDetails/DeviceDetails.js +15 -3
- package/lib/seam/components/DeviceDetails/DeviceDetails.js.map +1 -1
- package/lib/seam/components/DeviceDetails/LockDeviceDetails.d.ts +2 -1
- package/lib/seam/components/DeviceDetails/LockDeviceDetails.js +3 -2
- package/lib/seam/components/DeviceDetails/LockDeviceDetails.js.map +1 -1
- package/lib/seam/components/DeviceDetails/NoiseSensorDeviceDetails.d.ts +2 -1
- package/lib/seam/components/DeviceDetails/NoiseSensorDeviceDetails.js +3 -2
- package/lib/seam/components/DeviceDetails/NoiseSensorDeviceDetails.js.map +1 -1
- package/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.d.ts +2 -1
- package/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.js +2 -2
- package/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.js.map +1 -1
- package/lib/seam/components/SeamEditableDeviceName/SeamEditableDeviceName.d.ts +8 -0
- package/lib/seam/components/SeamEditableDeviceName/SeamEditableDeviceName.js +85 -0
- package/lib/seam/components/SeamEditableDeviceName/SeamEditableDeviceName.js.map +1 -0
- package/lib/seam/devices/use-update-device-name.d.ts +8 -0
- package/lib/seam/devices/use-update-device-name.js +43 -0
- package/lib/seam/devices/use-update-device-name.js.map +1 -0
- package/lib/ui/thermostat/ThermostatCard.d.ts +2 -1
- package/lib/ui/thermostat/ThermostatCard.js +4 -3
- package/lib/ui/thermostat/ThermostatCard.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
- package/src/lib/icons/Edit.tsx +6 -19
- package/src/lib/seam/components/DeviceDetails/DeviceDetails.tsx +35 -4
- package/src/lib/seam/components/DeviceDetails/LockDeviceDetails.tsx +9 -1
- package/src/lib/seam/components/DeviceDetails/NoiseSensorDeviceDetails.tsx +8 -1
- package/src/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.tsx +3 -1
- package/src/lib/seam/components/SeamEditableDeviceName/SeamEditableDeviceName.tsx +208 -0
- package/src/lib/seam/devices/use-update-device-name.ts +93 -0
- package/src/lib/ui/thermostat/ThermostatCard.tsx +11 -6
- package/src/lib/version.ts +1 -1
- package/src/styles/_main.scss +2 -0
- package/src/styles/_seam-editable-device-name.scss +62 -0
|
@@ -4,6 +4,7 @@ import { useState } from 'react'
|
|
|
4
4
|
import type { NestedSpecificDeviceDetailsProps } from 'lib/seam/components/DeviceDetails/DeviceDetails.js'
|
|
5
5
|
import { DeviceInfo } from 'lib/seam/components/DeviceDetails/DeviceInfo.js'
|
|
6
6
|
import { DeviceModel } from 'lib/seam/components/DeviceDetails/DeviceModel.js'
|
|
7
|
+
import { SeamEditableDeviceName } from 'lib/seam/components/SeamEditableDeviceName/SeamEditableDeviceName.js'
|
|
7
8
|
import type { NoiseSensorDevice } from 'lib/seam/noise-sensors/noise-sensor-device.js'
|
|
8
9
|
import { DeviceImage } from 'lib/ui/device/DeviceImage.js'
|
|
9
10
|
import { NoiseLevelStatus } from 'lib/ui/device/NoiseLevelStatus.js'
|
|
@@ -18,6 +19,7 @@ type TabType = 'details' | 'activity'
|
|
|
18
19
|
interface NoiseSensorDeviceDetailsProps
|
|
19
20
|
extends NestedSpecificDeviceDetailsProps {
|
|
20
21
|
device: NoiseSensorDevice
|
|
22
|
+
onEditName?: (newName: string) => void | Promise<void>
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export function NoiseSensorDeviceDetails({
|
|
@@ -26,6 +28,7 @@ export function NoiseSensorDeviceDetails({
|
|
|
26
28
|
disableResourceIds,
|
|
27
29
|
onBack,
|
|
28
30
|
className,
|
|
31
|
+
onEditName,
|
|
29
32
|
}: NoiseSensorDeviceDetailsProps): JSX.Element | null {
|
|
30
33
|
const [tab, setTab] = useState<TabType>('details')
|
|
31
34
|
|
|
@@ -45,7 +48,11 @@ export function NoiseSensorDeviceDetails({
|
|
|
45
48
|
</div>
|
|
46
49
|
<div className='seam-info'>
|
|
47
50
|
<span className='seam-label'>{t.noiseSensor}</span>
|
|
48
|
-
<
|
|
51
|
+
<SeamEditableDeviceName
|
|
52
|
+
onEdit={onEditName}
|
|
53
|
+
tagName='h4'
|
|
54
|
+
value={device.properties.name}
|
|
55
|
+
/>
|
|
49
56
|
<div className='seam-properties'>
|
|
50
57
|
<span className='seam-label'>{t.status}:</span>{' '}
|
|
51
58
|
<OnlineStatus device={device} />
|
|
@@ -29,6 +29,7 @@ import { ThermostatCard } from 'lib/ui/thermostat/ThermostatCard.js'
|
|
|
29
29
|
interface ThermostatDeviceDetailsProps
|
|
30
30
|
extends NestedSpecificDeviceDetailsProps {
|
|
31
31
|
device: ThermostatDevice
|
|
32
|
+
onEditName?: (newName: string) => void | Promise<void>
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
export function ThermostatDeviceDetails({
|
|
@@ -37,6 +38,7 @@ export function ThermostatDeviceDetails({
|
|
|
37
38
|
disableConnectedAccountInformation,
|
|
38
39
|
onBack,
|
|
39
40
|
className,
|
|
41
|
+
onEditName,
|
|
40
42
|
}: ThermostatDeviceDetailsProps): JSX.Element | null {
|
|
41
43
|
if (device == null) {
|
|
42
44
|
return null
|
|
@@ -47,7 +49,7 @@ export function ThermostatDeviceDetails({
|
|
|
47
49
|
<ContentHeader title={t.thermostat} onBack={onBack} />
|
|
48
50
|
|
|
49
51
|
<div className='seam-body'>
|
|
50
|
-
<ThermostatCard device={device} />
|
|
52
|
+
<ThermostatCard device={device} onEditName={onEditName} />
|
|
51
53
|
|
|
52
54
|
<div className='seam-thermostat-device-details'>
|
|
53
55
|
<DetailSectionGroup>
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import classNames from 'classnames'
|
|
2
|
+
import {
|
|
3
|
+
type ChangeEvent,
|
|
4
|
+
type HTMLAttributes,
|
|
5
|
+
type KeyboardEvent,
|
|
6
|
+
type PropsWithChildren,
|
|
7
|
+
useCallback,
|
|
8
|
+
useState,
|
|
9
|
+
} from 'react'
|
|
10
|
+
|
|
11
|
+
import { CheckIcon } from 'lib/icons/Check.js'
|
|
12
|
+
import { CloseIcon } from 'lib/icons/Close.js'
|
|
13
|
+
import { EditIcon } from 'lib/icons/Edit.js'
|
|
14
|
+
|
|
15
|
+
export type SeamDeviceNameProps = {
|
|
16
|
+
onEdit?: (newName: string) => void
|
|
17
|
+
editable?: boolean
|
|
18
|
+
tagName?: string
|
|
19
|
+
value: string
|
|
20
|
+
} & HTMLAttributes<HTMLElement>
|
|
21
|
+
|
|
22
|
+
function IconButton(
|
|
23
|
+
props: PropsWithChildren<HTMLAttributes<HTMLButtonElement>>
|
|
24
|
+
): JSX.Element {
|
|
25
|
+
return (
|
|
26
|
+
<button
|
|
27
|
+
{...props}
|
|
28
|
+
className={classNames(
|
|
29
|
+
'seam-editable-device-name-icon-button',
|
|
30
|
+
props.className
|
|
31
|
+
)}
|
|
32
|
+
>
|
|
33
|
+
{props.children}
|
|
34
|
+
</button>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const fixName = (name: string): string => {
|
|
39
|
+
return name.replace(/\s+/g, ' ').trim()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type Result = { type: 'success' } | { type: 'error'; message: string }
|
|
43
|
+
|
|
44
|
+
const isValidName = (name: string): Result => {
|
|
45
|
+
if (name.length < 2) {
|
|
46
|
+
return {
|
|
47
|
+
type: 'error',
|
|
48
|
+
message: 'Name must be at least 2 characters long',
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (name.length > 64) {
|
|
53
|
+
return {
|
|
54
|
+
type: 'error',
|
|
55
|
+
message: 'Name must be at most 64 characters long',
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
type: 'success',
|
|
61
|
+
} as const
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function SeamEditableDeviceName({
|
|
65
|
+
onEdit,
|
|
66
|
+
editable = true,
|
|
67
|
+
tagName,
|
|
68
|
+
value,
|
|
69
|
+
...props
|
|
70
|
+
}: SeamDeviceNameProps): JSX.Element {
|
|
71
|
+
const [editing, setEditing] = useState(false)
|
|
72
|
+
const [errorText, setErrorText] = useState<null | string>(null)
|
|
73
|
+
const [currentValue, setCurrentValue] = useState(value)
|
|
74
|
+
const Tag = (tagName ?? 'span') as 'div'
|
|
75
|
+
|
|
76
|
+
const handleCheck = useCallback(() => {
|
|
77
|
+
const fixedName = fixName(currentValue)
|
|
78
|
+
const valid = isValidName(fixedName)
|
|
79
|
+
|
|
80
|
+
if (valid.type === 'error') {
|
|
81
|
+
setErrorText(valid.message)
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
setEditing(false)
|
|
86
|
+
setCurrentValue(fixedName)
|
|
87
|
+
onEdit?.(fixedName)
|
|
88
|
+
}, [currentValue, onEdit])
|
|
89
|
+
|
|
90
|
+
const handleChange = useCallback(
|
|
91
|
+
(event: ChangeEvent<HTMLInputElement>): void => {
|
|
92
|
+
setCurrentValue(event.target.value)
|
|
93
|
+
setErrorText(null)
|
|
94
|
+
},
|
|
95
|
+
[]
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
const handleCancel = useCallback(() => {
|
|
99
|
+
setEditing(false)
|
|
100
|
+
setCurrentValue(value)
|
|
101
|
+
setErrorText(null)
|
|
102
|
+
}, [value])
|
|
103
|
+
|
|
104
|
+
const handleInputKeydown = useCallback(
|
|
105
|
+
(e: KeyboardEvent<HTMLInputElement>): void => {
|
|
106
|
+
if (e.repeat) return
|
|
107
|
+
|
|
108
|
+
if (e.key === 'Enter') {
|
|
109
|
+
handleCheck()
|
|
110
|
+
} else if (e.key === 'Escape') {
|
|
111
|
+
handleCancel()
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
[handleCheck, handleCancel]
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<Tag
|
|
119
|
+
{...props}
|
|
120
|
+
className={classNames('seam-editable-device-name', props.className)}
|
|
121
|
+
>
|
|
122
|
+
<NameView
|
|
123
|
+
editing={editing}
|
|
124
|
+
value={currentValue}
|
|
125
|
+
onChange={handleChange}
|
|
126
|
+
onKeyDown={handleInputKeydown}
|
|
127
|
+
errorText={errorText}
|
|
128
|
+
/>
|
|
129
|
+
|
|
130
|
+
{editable && (
|
|
131
|
+
<span className='seam-editable-device-name-icon-wrapper'>
|
|
132
|
+
<ActionButtons
|
|
133
|
+
editing={editing}
|
|
134
|
+
onEdit={() => {
|
|
135
|
+
setEditing(true)
|
|
136
|
+
}}
|
|
137
|
+
onCancel={handleCancel}
|
|
138
|
+
onCheck={handleCheck}
|
|
139
|
+
/>
|
|
140
|
+
</span>
|
|
141
|
+
)}
|
|
142
|
+
</Tag>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
interface NameViewProps {
|
|
147
|
+
editing: boolean
|
|
148
|
+
value: string
|
|
149
|
+
onChange: (event: ChangeEvent<HTMLInputElement>) => void
|
|
150
|
+
onKeyDown: (event: KeyboardEvent<HTMLInputElement>) => void
|
|
151
|
+
errorText?: string | null
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function NameView(props: NameViewProps): JSX.Element {
|
|
155
|
+
if (!props.editing) {
|
|
156
|
+
return <span>{props.value}</span>
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<span className='seam-editable-device-name-input-wrapper'>
|
|
161
|
+
<input
|
|
162
|
+
type='text'
|
|
163
|
+
defaultValue={props.value}
|
|
164
|
+
onChange={props.onChange}
|
|
165
|
+
onKeyDown={props.onKeyDown}
|
|
166
|
+
ref={(el) => {
|
|
167
|
+
setTimeout(() => {
|
|
168
|
+
el?.focus()
|
|
169
|
+
}, 0)
|
|
170
|
+
}}
|
|
171
|
+
/>
|
|
172
|
+
|
|
173
|
+
{props.errorText != null && (
|
|
174
|
+
<span className='seam-editable-device-name-input-error'>
|
|
175
|
+
{props.errorText}
|
|
176
|
+
</span>
|
|
177
|
+
)}
|
|
178
|
+
</span>
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
interface ActionButtonsProps {
|
|
183
|
+
onEdit: () => void
|
|
184
|
+
onCancel: () => void
|
|
185
|
+
onCheck: () => void
|
|
186
|
+
editing: boolean
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function ActionButtons(props: ActionButtonsProps): JSX.Element {
|
|
190
|
+
if (props.editing) {
|
|
191
|
+
return (
|
|
192
|
+
<>
|
|
193
|
+
<IconButton onClick={props.onCheck}>
|
|
194
|
+
<CheckIcon width='1em' height='1em' viewBox='0 0 24 24' />
|
|
195
|
+
</IconButton>
|
|
196
|
+
<IconButton onClick={props.onCancel}>
|
|
197
|
+
<CloseIcon width='1em' height='1em' viewBox='0 0 24 24' />
|
|
198
|
+
</IconButton>
|
|
199
|
+
</>
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<IconButton onClick={props.onEdit}>
|
|
205
|
+
<EditIcon width='1em' height='1em' viewBox='0 0 24 24' />
|
|
206
|
+
</IconButton>
|
|
207
|
+
)
|
|
208
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DevicesGetParams,
|
|
3
|
+
DevicesUpdateBody,
|
|
4
|
+
SeamHttpApiError,
|
|
5
|
+
} from '@seamapi/http/connect'
|
|
6
|
+
import type { Device } from '@seamapi/types/connect'
|
|
7
|
+
import {
|
|
8
|
+
useMutation,
|
|
9
|
+
type UseMutationResult,
|
|
10
|
+
useQueryClient,
|
|
11
|
+
} from '@tanstack/react-query'
|
|
12
|
+
|
|
13
|
+
import { NullSeamClientError, useSeamClient } from 'lib/seam/use-seam-client.js'
|
|
14
|
+
|
|
15
|
+
export type UseUpdateDeviceNameParams = never
|
|
16
|
+
|
|
17
|
+
export type UseUpdateDeviceNameData = undefined
|
|
18
|
+
|
|
19
|
+
export type UseUpdateDeviceNameMutationVariables = Pick<
|
|
20
|
+
DevicesUpdateBody,
|
|
21
|
+
'device_id' | 'name'
|
|
22
|
+
>
|
|
23
|
+
|
|
24
|
+
type MutationError = SeamHttpApiError
|
|
25
|
+
|
|
26
|
+
export function useUpdateDeviceName(
|
|
27
|
+
params: DevicesGetParams
|
|
28
|
+
): UseMutationResult<
|
|
29
|
+
UseUpdateDeviceNameData,
|
|
30
|
+
MutationError,
|
|
31
|
+
UseUpdateDeviceNameMutationVariables
|
|
32
|
+
> {
|
|
33
|
+
const { client } = useSeamClient()
|
|
34
|
+
const queryClient = useQueryClient()
|
|
35
|
+
|
|
36
|
+
return useMutation<
|
|
37
|
+
UseUpdateDeviceNameData,
|
|
38
|
+
MutationError,
|
|
39
|
+
UseUpdateDeviceNameMutationVariables
|
|
40
|
+
>({
|
|
41
|
+
mutationFn: async (variables) => {
|
|
42
|
+
if (client === null) throw new NullSeamClientError()
|
|
43
|
+
await client.devices.update(variables)
|
|
44
|
+
},
|
|
45
|
+
onSuccess: (_data, variables) => {
|
|
46
|
+
queryClient.setQueryData<Device | null>(
|
|
47
|
+
['devices', 'get', params],
|
|
48
|
+
(device) => {
|
|
49
|
+
if (device == null) {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return getUpdatedDevice(
|
|
54
|
+
device,
|
|
55
|
+
variables.name ?? device.properties.name
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
queryClient.setQueryData<Device[]>(
|
|
61
|
+
['devices', 'list', { device_id: variables.device_id }],
|
|
62
|
+
(devices): Device[] => {
|
|
63
|
+
if (devices == null) {
|
|
64
|
+
return []
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return devices.map((device) => {
|
|
68
|
+
if (device.device_id === variables.device_id) {
|
|
69
|
+
return getUpdatedDevice(
|
|
70
|
+
device,
|
|
71
|
+
variables.name ?? device.properties.name
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return device
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const getUpdatedDevice = (device: Device, name: string): Device => {
|
|
84
|
+
const { properties } = device
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
...device,
|
|
88
|
+
properties: {
|
|
89
|
+
...properties,
|
|
90
|
+
name,
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -3,6 +3,7 @@ import { useState } from 'react'
|
|
|
3
3
|
|
|
4
4
|
import { FanIcon } from 'lib/icons/Fan.js'
|
|
5
5
|
import { OffIcon } from 'lib/icons/Off.js'
|
|
6
|
+
import { SeamEditableDeviceName } from 'lib/seam/components/SeamEditableDeviceName/SeamEditableDeviceName.js'
|
|
6
7
|
import type { ThermostatDevice } from 'lib/seam/thermostats/thermostat-device.js'
|
|
7
8
|
import { DeviceImage } from 'lib/ui/device/DeviceImage.js'
|
|
8
9
|
import { ClimateSettingStatus } from 'lib/ui/thermostat/ClimateSettingStatus.js'
|
|
@@ -10,17 +11,18 @@ import { Temperature } from 'lib/ui/thermostat/Temperature.js'
|
|
|
10
11
|
|
|
11
12
|
interface ThermostatCardProps {
|
|
12
13
|
device: ThermostatDevice
|
|
14
|
+
onEditName?: (newName: string) => void
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
export function ThermostatCard(
|
|
17
|
+
export function ThermostatCard(props: ThermostatCardProps): JSX.Element {
|
|
16
18
|
return (
|
|
17
19
|
<div className='seam-thermostat-card'>
|
|
18
|
-
<Content device={device} />
|
|
20
|
+
<Content device={props.device} onEditName={props.onEditName} />
|
|
19
21
|
</div>
|
|
20
22
|
)
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
function Content(props:
|
|
25
|
+
function Content(props: ThermostatCardProps): JSX.Element | null {
|
|
24
26
|
const { device } = props
|
|
25
27
|
|
|
26
28
|
const [temperatureUnit, setTemperatureUnit] = useState<
|
|
@@ -50,9 +52,12 @@ function Content(props: { device: ThermostatDevice }): JSX.Element | null {
|
|
|
50
52
|
</div>
|
|
51
53
|
<div className='seam-thermostat-card-details'>
|
|
52
54
|
<div className='seam-thermostat-heading-wrap'>
|
|
53
|
-
<
|
|
54
|
-
{device.properties.name}
|
|
55
|
-
|
|
55
|
+
<SeamEditableDeviceName
|
|
56
|
+
value={device.properties.name}
|
|
57
|
+
tagName='h4'
|
|
58
|
+
className='seam-thermostat-card-heading'
|
|
59
|
+
onEdit={props.onEditName}
|
|
60
|
+
/>
|
|
56
61
|
<button
|
|
57
62
|
onClick={toggleTemperatureUnit}
|
|
58
63
|
className='seam-thermostat-temperature-toggle'
|
package/src/lib/version.ts
CHANGED
package/src/styles/_main.scss
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
@use './time-zone-picker';
|
|
30
30
|
@use './tab-set';
|
|
31
31
|
@use './noise-sensor';
|
|
32
|
+
@use './seam-editable-device-name';
|
|
32
33
|
|
|
33
34
|
.seam-components {
|
|
34
35
|
// Reset
|
|
@@ -54,6 +55,7 @@
|
|
|
54
55
|
@include switch.all;
|
|
55
56
|
@include time-zone-picker.all;
|
|
56
57
|
@include tab-set.all;
|
|
58
|
+
@include seam-editable-device-name.all;
|
|
57
59
|
|
|
58
60
|
// Components
|
|
59
61
|
@include device-details.all;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
@use './colors';
|
|
2
|
+
|
|
3
|
+
@mixin all {
|
|
4
|
+
.seam-editable-device-name {
|
|
5
|
+
input {
|
|
6
|
+
border: none;
|
|
7
|
+
background-color: transparent;
|
|
8
|
+
font-size: inherit;
|
|
9
|
+
font-weight: inherit;
|
|
10
|
+
letter-spacing: inherit;
|
|
11
|
+
line-height: inherit;
|
|
12
|
+
font-family: inherit;
|
|
13
|
+
margin: 0;
|
|
14
|
+
padding: 0;
|
|
15
|
+
border-bottom: 1px dashed currentcolor;
|
|
16
|
+
outline: none;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
&-input-wrapper {
|
|
20
|
+
display: inline-flex;
|
|
21
|
+
flex-flow: column nowrap;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&-input-error {
|
|
25
|
+
color: colors.$status-red;
|
|
26
|
+
font-size: 0.8em;
|
|
27
|
+
margin-top: 5px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
&-icon-wrapper {
|
|
31
|
+
margin-left: 10px;
|
|
32
|
+
display: inline-flex;
|
|
33
|
+
flex-flow: row nowrap;
|
|
34
|
+
gap: 5px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&-icon-button {
|
|
38
|
+
display: inline-flex;
|
|
39
|
+
padding: 0;
|
|
40
|
+
margin: 0;
|
|
41
|
+
background: none;
|
|
42
|
+
border: none;
|
|
43
|
+
width: fit-content;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
transform-origin: center;
|
|
46
|
+
border-radius: 2px;
|
|
47
|
+
|
|
48
|
+
path {
|
|
49
|
+
fill: currentcolor;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&:hover {
|
|
53
|
+
background-color: currentcolor;
|
|
54
|
+
box-shadow: 0 0 0 2px currentcolor;
|
|
55
|
+
|
|
56
|
+
path {
|
|
57
|
+
fill: white;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|