@seamapi/react 0.26.0 → 0.27.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 (100) hide show
  1. package/README.md +5 -4
  2. package/index.css +69 -3
  3. package/index.css.map +1 -1
  4. package/index.min.css +1 -1
  5. package/index.min.css.map +1 -1
  6. package/lib/copy-to-clipboard.d.ts +1 -0
  7. package/lib/copy-to-clipboard.js +4 -0
  8. package/lib/copy-to-clipboard.js.map +1 -0
  9. package/lib/icons/Close.d.ts +2 -0
  10. package/lib/icons/Close.js +5 -0
  11. package/lib/icons/Close.js.map +1 -0
  12. package/lib/icons/Search.d.ts +2 -0
  13. package/lib/icons/Search.js +5 -0
  14. package/lib/icons/Search.js.map +1 -0
  15. package/lib/ui/AccessCodeTable/AccessCodeTable.js +4 -4
  16. package/lib/ui/AccessCodeTable/AccessCodeTable.js.map +1 -1
  17. package/lib/ui/Alert/Alert.d.ts +6 -4
  18. package/lib/ui/Alert/Alert.js +7 -4
  19. package/lib/ui/Alert/Alert.js.map +1 -1
  20. package/lib/ui/Alert/Alerts.d.ts +4 -4
  21. package/lib/ui/Alert/Alerts.js +4 -2
  22. package/lib/ui/Alert/Alerts.js.map +1 -1
  23. package/lib/ui/DeviceDetails/DeviceDetails.js +2 -3
  24. package/lib/ui/DeviceDetails/DeviceDetails.js.map +1 -1
  25. package/lib/ui/DeviceTable/DeviceRow.d.ts +2 -2
  26. package/lib/ui/DeviceTable/DeviceRow.js +0 -1
  27. package/lib/ui/DeviceTable/DeviceRow.js.map +1 -1
  28. package/lib/ui/DeviceTable/DeviceTable.js +9 -1
  29. package/lib/ui/DeviceTable/DeviceTable.js.map +1 -1
  30. package/lib/ui/Menu/Menu.d.ts +9 -10
  31. package/lib/ui/Menu/Menu.js +23 -21
  32. package/lib/ui/Menu/Menu.js.map +1 -1
  33. package/lib/ui/Menu/MenuItem.d.ts +4 -4
  34. package/lib/ui/Menu/MenuItem.js +1 -1
  35. package/lib/ui/Menu/MenuItem.js.map +1 -1
  36. package/lib/ui/Menu/MoreActionsMenu.d.ts +5 -4
  37. package/lib/ui/Menu/MoreActionsMenu.js +6 -6
  38. package/lib/ui/Menu/MoreActionsMenu.js.map +1 -1
  39. package/lib/ui/SupportedDevices/SupportedDeviceRow.d.ts +1 -1
  40. package/lib/ui/SupportedDevices/SupportedDeviceRow.js +8 -14
  41. package/lib/ui/SupportedDevices/SupportedDeviceRow.js.map +1 -1
  42. package/lib/ui/SupportedDevices/SupportedDevices.js +2 -2
  43. package/lib/ui/SupportedDevices/SupportedDevices.js.map +1 -1
  44. package/lib/ui/SupportedDevices/SupportedDevicesHeader.d.ts +1 -1
  45. package/lib/ui/SupportedDevices/SupportedDevicesHeader.js +1 -1
  46. package/lib/ui/SupportedDevices/SupportedDevicesHeader.js.map +1 -1
  47. package/lib/ui/Table/TableFilterBar/TableFilterBar.d.ts +3 -2
  48. package/lib/ui/Table/TableFilterBar/TableFilterBar.js +1 -1
  49. package/lib/ui/Table/TableFilterBar/TableFilterBar.js.map +1 -1
  50. package/lib/ui/Table/TableFilterBar/TableFilterItem.d.ts +2 -1
  51. package/lib/ui/Table/TableFilterBar/TableFilterItem.js +1 -1
  52. package/lib/ui/Table/TableFilterBar/TableFilterItem.js.map +1 -1
  53. package/lib/ui/Table/TableHeader.js +1 -1
  54. package/lib/ui/Table/TableHeader.js.map +1 -1
  55. package/lib/ui/TextField/SearchTextField.d.ts +1 -1
  56. package/lib/ui/TextField/SearchTextField.js +52 -2
  57. package/lib/ui/TextField/SearchTextField.js.map +1 -1
  58. package/lib/ui/TextField/TextField.d.ts +12 -5
  59. package/lib/ui/TextField/TextField.js +7 -5
  60. package/lib/ui/TextField/TextField.js.map +1 -1
  61. package/lib/ui/device/BatteryStatus.d.ts +2 -1
  62. package/lib/ui/device/LockStatus.d.ts +2 -1
  63. package/lib/ui/device/OnlineStatus.d.ts +2 -1
  64. package/lib/use-toggle.d.ts +1 -2
  65. package/lib/use-toggle.js +1 -2
  66. package/lib/use-toggle.js.map +1 -1
  67. package/package.json +1 -1
  68. package/src/lib/copy-to-clipboard.ts +3 -0
  69. package/src/lib/icons/Close.tsx +30 -0
  70. package/src/lib/icons/Search.tsx +30 -0
  71. package/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx +4 -4
  72. package/src/lib/ui/Alert/Alert.tsx +21 -18
  73. package/src/lib/ui/Alert/Alerts.tsx +7 -5
  74. package/src/lib/ui/DeviceDetails/DeviceDetails.tsx +3 -6
  75. package/src/lib/ui/DeviceTable/DeviceRow.tsx +4 -4
  76. package/src/lib/ui/DeviceTable/DeviceTable.tsx +16 -1
  77. package/src/lib/ui/Menu/Menu.tsx +34 -34
  78. package/src/lib/ui/Menu/MenuItem.tsx +6 -4
  79. package/src/lib/ui/Menu/MoreActionsMenu.tsx +15 -10
  80. package/src/lib/ui/SupportedDevices/SupportedDeviceRow.tsx +9 -20
  81. package/src/lib/ui/SupportedDevices/SupportedDevices.tsx +2 -2
  82. package/src/lib/ui/SupportedDevices/SupportedDevicesHeader.tsx +1 -1
  83. package/src/lib/ui/Table/TableFilterBar/TableFilterBar.tsx +5 -3
  84. package/src/lib/ui/Table/TableFilterBar/TableFilterItem.tsx +2 -2
  85. package/src/lib/ui/Table/TableHeader.tsx +5 -1
  86. package/src/lib/ui/TextField/SearchTextField.tsx +79 -0
  87. package/src/lib/ui/TextField/TextField.tsx +45 -14
  88. package/src/lib/ui/device/BatteryStatus.tsx +1 -1
  89. package/src/lib/ui/device/LockStatus.tsx +1 -1
  90. package/src/lib/ui/device/OnlineStatus.tsx +1 -1
  91. package/src/lib/use-toggle.ts +3 -3
  92. package/src/styles/_colors.scss +1 -0
  93. package/src/styles/_device-table.scss +4 -0
  94. package/src/styles/_inputs.scss +74 -2
  95. package/src/styles/_supported-devices.scss +0 -1
  96. package/src/styles/_tables.scss +8 -1
  97. package/lib/copy.d.ts +0 -1
  98. package/lib/copy.js +0 -4
  99. package/lib/copy.js.map +0 -1
  100. package/src/lib/copy.ts +0 -3
@@ -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,26 +1,57 @@
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
 
22
53
  export const handleString =
23
54
  (setter: (v: string) => void) =>
24
- (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>): void => {
25
- setter(e.currentTarget.value)
55
+ (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>): void => {
56
+ setter(event.currentTarget.value)
26
57
  }
@@ -5,7 +5,7 @@ import { BatteryLevelFullIcon } from 'lib/icons/BatteryLevelFull.js'
5
5
  import { BatteryLevelHighIcon } from 'lib/icons/BatteryLevelHigh.js'
6
6
  import { BatteryLevelLowIcon } from 'lib/icons/BatteryLevelLow.js'
7
7
 
8
- export interface BatteryStatusProps {
8
+ interface BatteryStatusProps {
9
9
  device: LockDevice
10
10
  }
11
11
 
@@ -3,7 +3,7 @@ import type { LockDevice } from 'seamapi'
3
3
  import { LockLockedIcon } from 'lib/icons/LockLocked.js'
4
4
  import { LockUnlockedIcon } from 'lib/icons/LockUnlocked.js'
5
5
 
6
- export interface LockStatusProps {
6
+ interface LockStatusProps {
7
7
  device: LockDevice
8
8
  }
9
9
 
@@ -3,7 +3,7 @@ import type { LockDevice } from 'seamapi'
3
3
  import { OnlineStatusDeviceOfflineIcon } from 'lib/icons/OnlineStatusDeviceOffline.js'
4
4
  import { OnlineStatusOnlineIcon } from 'lib/icons/OnlineStatusOnline.js'
5
5
 
6
- export interface OnlineStatusProps {
6
+ interface OnlineStatusProps {
7
7
  device: LockDevice
8
8
  }
9
9
 
@@ -3,7 +3,9 @@ import { useCallback, useState } from 'react'
3
3
  /**
4
4
  * Simple util to quickly handle on/off states.
5
5
  */
6
- const useToggle = (initialState: boolean = false): [boolean, () => void] => {
6
+ export const useToggle = (
7
+ initialState: boolean = false
8
+ ): [boolean, () => void] => {
7
9
  const [isTrue, setIsTrue] = useState(initialState)
8
10
 
9
11
  // use callback otherwise it would yield unexpected results in promises
@@ -14,5 +16,3 @@ const useToggle = (initialState: boolean = false): [boolean, () => void] => {
14
16
 
15
17
  return [isTrue, toggle]
16
18
  }
17
-
18
- export default useToggle
@@ -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
 
@@ -55,7 +55,6 @@ $row-padding: 8px;
55
55
  }
56
56
  }
57
57
 
58
- // stylelint-disable-next-line no-descending-specificity
59
58
  tr {
60
59
  th:first-of-type,
61
60
  td:first-of-type {
@@ -4,9 +4,16 @@
4
4
  .seam-table-header {
5
5
  display: flex;
6
6
  justify-content: space-between;
7
- align-items: center;
7
+ align-items: flex-start;
8
8
  height: 56px;
9
9
  padding: 0 16px;
10
+
11
+ .seam-body {
12
+ display: flex;
13
+ justify-content: space-between;
14
+ align-items: center;
15
+ width: 100%;
16
+ }
10
17
  }
11
18
 
12
19
  .seam-table-title {
package/lib/copy.d.ts DELETED
@@ -1 +0,0 @@
1
- export declare function copy(value: string): Promise<void>;
package/lib/copy.js DELETED
@@ -1,4 +0,0 @@
1
- export async function copy(value) {
2
- await window.navigator.clipboard.writeText(value);
3
- }
4
- //# sourceMappingURL=copy.js.map
package/lib/copy.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"copy.js","sourceRoot":"","sources":["../src/lib/copy.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,KAAa;IACtC,MAAM,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;AACnD,CAAC"}
package/src/lib/copy.ts DELETED
@@ -1,3 +0,0 @@
1
- export async function copy(value: string) {
2
- await window.navigator.clipboard.writeText(value)
3
- }