@seamapi/react 1.28.0 → 1.29.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 (81) hide show
  1. package/README.md +1 -0
  2. package/dist/elements.js +5755 -5574
  3. package/dist/elements.js.map +1 -1
  4. package/dist/index.css +105 -17
  5. package/dist/index.css.map +1 -1
  6. package/dist/index.min.css +1 -1
  7. package/dist/index.min.css.map +1 -1
  8. package/lib/icons/Add.d.ts +2 -0
  9. package/lib/icons/Add.js +7 -0
  10. package/lib/icons/Add.js.map +1 -0
  11. package/lib/icons/DotsEllipsisMore.js +2 -2
  12. package/lib/icons/DotsEllipsisMore.js.map +1 -1
  13. package/lib/seam/SeamProvider.d.ts +2 -2
  14. package/lib/seam/SeamProvider.js.map +1 -1
  15. package/lib/seam/access-codes/use-create-access-code.d.ts +6 -0
  16. package/lib/seam/access-codes/use-create-access-code.js +18 -0
  17. package/lib/seam/access-codes/use-create-access-code.js.map +1 -0
  18. package/lib/seam/components/AccessCodeForm/AccessCodeForm.d.ts +7 -0
  19. package/lib/seam/components/AccessCodeForm/AccessCodeForm.js +52 -0
  20. package/lib/seam/components/AccessCodeForm/AccessCodeForm.js.map +1 -0
  21. package/lib/seam/components/AccessCodeTable/AccessCodeTable.d.ts +2 -1
  22. package/lib/seam/components/AccessCodeTable/AccessCodeTable.js +10 -2
  23. package/lib/seam/components/AccessCodeTable/AccessCodeTable.js.map +1 -1
  24. package/lib/ui/Alert/Alerts.d.ts +2 -2
  25. package/lib/ui/Alert/Alerts.js.map +1 -1
  26. package/lib/ui/Button.d.ts +3 -2
  27. package/lib/ui/Button.js +2 -2
  28. package/lib/ui/Button.js.map +1 -1
  29. package/lib/ui/FormField.d.ts +7 -0
  30. package/lib/ui/FormField.js +37 -0
  31. package/lib/ui/FormField.js.map +1 -0
  32. package/lib/ui/InputLabel.d.ts +7 -0
  33. package/lib/ui/InputLabel.js +7 -0
  34. package/lib/ui/InputLabel.js.map +1 -0
  35. package/lib/ui/Menu/Menu.d.ts +2 -2
  36. package/lib/ui/Menu/Menu.js.map +1 -1
  37. package/lib/ui/Menu/MenuItem.d.ts +2 -2
  38. package/lib/ui/Menu/MenuItem.js +1 -1
  39. package/lib/ui/Menu/MenuItem.js.map +1 -1
  40. package/lib/ui/Menu/MoreActionsMenu.d.ts +2 -2
  41. package/lib/ui/Menu/MoreActionsMenu.js.map +1 -1
  42. package/lib/ui/TextField/SearchTextField.js +1 -48
  43. package/lib/ui/TextField/SearchTextField.js.map +1 -1
  44. package/lib/ui/TextField/TextField.d.ts +8 -2
  45. package/lib/ui/TextField/TextField.js +56 -5
  46. package/lib/ui/TextField/TextField.js.map +1 -1
  47. package/lib/ui/layout/ContentHeader.d.ts +5 -3
  48. package/lib/ui/layout/ContentHeader.js +2 -2
  49. package/lib/ui/layout/ContentHeader.js.map +1 -1
  50. package/lib/version.d.ts +1 -1
  51. package/lib/version.js +1 -1
  52. package/package.json +1 -1
  53. package/src/lib/icons/Add.tsx +33 -0
  54. package/src/lib/icons/DotsEllipsisMore.tsx +7 -7
  55. package/src/lib/seam/SeamProvider.tsx +3 -2
  56. package/src/lib/seam/access-codes/use-create-access-code.ts +34 -0
  57. package/src/lib/seam/components/AccessCodeForm/AccessCodeForm.element.ts +12 -0
  58. package/src/lib/seam/components/AccessCodeForm/AccessCodeForm.tsx +113 -0
  59. package/src/lib/seam/components/AccessCodeTable/AccessCodeTable.element.ts +1 -0
  60. package/src/lib/seam/components/AccessCodeTable/AccessCodeTable.tsx +30 -3
  61. package/src/lib/ui/Alert/Alerts.tsx +2 -4
  62. package/src/lib/ui/Button.tsx +5 -2
  63. package/src/lib/ui/FormField.tsx +56 -0
  64. package/src/lib/ui/InputLabel.tsx +21 -0
  65. package/src/lib/ui/Menu/Menu.tsx +2 -2
  66. package/src/lib/ui/Menu/MenuItem.tsx +2 -5
  67. package/src/lib/ui/Menu/MoreActionsMenu.tsx +2 -2
  68. package/src/lib/ui/TextField/SearchTextField.tsx +1 -70
  69. package/src/lib/ui/TextField/TextField.tsx +132 -19
  70. package/src/lib/ui/layout/ContentHeader.tsx +13 -5
  71. package/src/lib/version.ts +1 -1
  72. package/src/styles/_access-code-form.scss +55 -0
  73. package/src/styles/_access-code-table.scss +11 -0
  74. package/src/styles/_buttons.scss +11 -1
  75. package/src/styles/_colors.scss +1 -0
  76. package/src/styles/_form-field.scss +15 -0
  77. package/src/styles/_input-label.scss +11 -0
  78. package/src/styles/_inputs.scss +47 -22
  79. package/src/styles/_layout.scss +9 -1
  80. package/src/styles/_main.scss +6 -0
  81. package/src/styles/_tables.scss +1 -1
@@ -1,5 +1,16 @@
1
1
  import classNames from 'classnames'
2
- import type { ChangeEvent, InputHTMLAttributes, MutableRefObject } from 'react'
2
+ import {
3
+ type ChangeEvent,
4
+ type FocusEventHandler,
5
+ forwardRef,
6
+ type InputHTMLAttributes,
7
+ type MutableRefObject,
8
+ useEffect,
9
+ useImperativeHandle,
10
+ useState,
11
+ } from 'react'
12
+
13
+ import { CloseIcon } from 'lib/icons/Close.js'
3
14
 
4
15
  export interface TextFieldProps {
5
16
  value?: string
@@ -8,15 +19,24 @@ export interface TextFieldProps {
8
19
  endAdornment?: JSX.Element
9
20
  disabled?: boolean
10
21
  className?: string
22
+ size?: 'small' | 'large'
11
23
  inputProps?: {
12
24
  ref?:
13
25
  | MutableRefObject<HTMLInputElement | null>
14
26
  | ((inputEl: HTMLInputElement) => void)
15
27
  } & Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange'>
28
+ clearable?: boolean
29
+ onFocus?: FocusEventHandler<HTMLInputElement>
30
+ onBlur?: FocusEventHandler<HTMLInputElement>
31
+ helperText?: string
32
+ hasError?: boolean
16
33
  }
17
34
 
18
- export function TextField(props: TextFieldProps): JSX.Element {
19
- const {
35
+ export const TextField = forwardRef<
36
+ HTMLInputElement | undefined,
37
+ TextFieldProps
38
+ >(function TextField(
39
+ {
20
40
  value,
21
41
  onChange,
22
42
  className,
@@ -24,34 +44,127 @@ export function TextField(props: TextFieldProps): JSX.Element {
24
44
  endAdornment,
25
45
  inputProps,
26
46
  disabled = false,
27
- } = props
47
+ size = 'small',
48
+ clearable = false,
49
+ onFocus,
50
+ onBlur,
51
+ hasError = false,
52
+ helperText,
53
+ },
54
+ ref
55
+ ): JSX.Element {
56
+ const [inputEl, setInputEl] = useState<HTMLInputElement | null>(null)
57
+
58
+ const valueIsEmpty = useValueIsEmpty(value, inputEl)
59
+
60
+ const clearInput = (): void => {
61
+ if (onChange != null) {
62
+ onChange('')
63
+ return
64
+ }
65
+
66
+ if (inputEl != null) {
67
+ inputEl.value = ''
68
+ }
69
+ }
70
+
71
+ // Maintain a local ref, and still forward it along
72
+ useImperativeHandle(ref, () => (inputEl != null ? inputEl : undefined), [
73
+ inputEl,
74
+ ])
75
+
76
+ const endAdornmentVisible = endAdornment != null || clearable
28
77
 
29
78
  return (
30
79
  <div
31
- className={classNames('seam-text-field', className, {
80
+ className={classNames('seam-text-field', className, `seam-${size}`, {
32
81
  'seam-disabled': disabled,
82
+ 'seam-error': hasError,
33
83
  })}
34
84
  >
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>
85
+ <div className='seam-main'>
86
+ {startAdornment != null && (
87
+ <div className='seam-adornment seam-start'>{startAdornment}</div>
88
+ )}
89
+ <input
90
+ className='seam-text-field-input'
91
+ value={value}
92
+ onChange={onChange == null ? undefined : handleString(onChange)}
93
+ type='text'
94
+ disabled={disabled}
95
+ ref={setInputEl}
96
+ onFocus={onFocus}
97
+ onBlur={onBlur}
98
+ {...inputProps}
99
+ />
100
+ {endAdornmentVisible && (
101
+ <div className='seam-adornment seam-end'>
102
+ {clearable && (
103
+ <button
104
+ className={classNames({
105
+ 'seam-hidden': valueIsEmpty,
106
+ })}
107
+ onClick={clearInput}
108
+ >
109
+ <CloseIcon />
110
+ </button>
111
+ )}
112
+ {endAdornment}
113
+ </div>
114
+ )}
115
+ </div>
116
+ {helperText != null && (
117
+ <div className='seam-helper-text'>{helperText}</div>
48
118
  )}
49
119
  </div>
50
120
  )
51
- }
121
+ })
52
122
 
53
123
  export const handleString =
54
124
  (setter: (v: string) => void) =>
55
125
  (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>): void => {
56
126
  setter(event.currentTarget.value)
57
127
  }
128
+
129
+ function useValueIsEmpty(
130
+ value: string | undefined,
131
+ inputEl: HTMLInputElement | null
132
+ ): boolean {
133
+ const [valueIsEmpty, setValueIsEmpty] = useState(true)
134
+
135
+ // If this is a controlled element, we'll just look at `value`
136
+ useEffect(() => {
137
+ setValueIsEmpty(value == null || value === '')
138
+ }, [value])
139
+
140
+ // If this is not a controlled element, we'll need to listen to `input`
141
+ // events.
142
+ useEffect(() => {
143
+ if (inputEl == null) {
144
+ return
145
+ }
146
+
147
+ const handler = (event: Event): void => {
148
+ if (value !== undefined) {
149
+ return
150
+ }
151
+
152
+ if (event.target == null || !('value' in event.target)) {
153
+ return
154
+ }
155
+
156
+ const inputValue = event.target.value
157
+ if (value === undefined) {
158
+ setValueIsEmpty(inputValue === '')
159
+ }
160
+ }
161
+
162
+ inputEl.addEventListener('input', handler)
163
+
164
+ return () => {
165
+ inputEl.removeEventListener('input', handler)
166
+ }
167
+ }, [inputEl, value])
168
+
169
+ return valueIsEmpty
170
+ }
@@ -1,10 +1,13 @@
1
1
  import { ArrowBackIcon } from 'lib/icons/ArrowBack.js'
2
2
 
3
- export function ContentHeader(props: {
4
- title?: string
3
+ export interface ContentHeaderProps {
5
4
  onBack?: () => void
6
- }): JSX.Element | null {
7
- const { title, onBack } = props
5
+ title?: string
6
+ subheading?: string
7
+ }
8
+
9
+ export function ContentHeader(props: ContentHeaderProps): JSX.Element | null {
10
+ const { title, onBack, subheading } = props
8
11
  if (title == null && onBack == null) {
9
12
  return null
10
13
  }
@@ -12,7 +15,12 @@ export function ContentHeader(props: {
12
15
  return (
13
16
  <div className='seam-content-header'>
14
17
  <BackIcon onClick={onBack} />
15
- <span className='seam-title'>{title}</span>
18
+ <div>
19
+ <span className='seam-title'>{title}</span>
20
+ {subheading != null && (
21
+ <span className='seam-subheading'>{subheading}</span>
22
+ )}
23
+ </div>
16
24
  </div>
17
25
  )
18
26
  }
@@ -1,3 +1,3 @@
1
- const seamapiReactVersion = '1.28.0'
1
+ const seamapiReactVersion = '1.29.0'
2
2
 
3
3
  export default seamapiReactVersion
@@ -0,0 +1,55 @@
1
+ @use './colors';
2
+
3
+ @mixin all {
4
+ .seam-access-code-form {
5
+ @include main;
6
+ }
7
+ }
8
+
9
+ @mixin main {
10
+ > .seam-main {
11
+ padding: 24px 24px 0;
12
+
13
+ .seam-code-text-field {
14
+ margin-bottom: 4px;
15
+ }
16
+
17
+ .seam-code-field {
18
+ > .seam-text-field {
19
+ margin-bottom: 4px;
20
+ }
21
+
22
+ > .seam-bottom {
23
+ align-items: center;
24
+ justify-content: space-between;
25
+ display: none;
26
+
27
+ .seam-requirements {
28
+ color: colors.$text-gray-2;
29
+ font-size: 14px;
30
+ line-height: 134%;
31
+ list-style: disc;
32
+ padding-left: 20px;
33
+
34
+ &.seam-error {
35
+ color: colors.$status-red;
36
+ }
37
+ }
38
+
39
+ &.seam-visible {
40
+ display: flex;
41
+ }
42
+ }
43
+ }
44
+
45
+ .seam-actions {
46
+ display: flex;
47
+ gap: 8px;
48
+ justify-content: center;
49
+
50
+ .seam-btn {
51
+ width: 96px;
52
+ }
53
+ }
54
+ }
55
+ }
@@ -6,6 +6,17 @@
6
6
  width: 170px;
7
7
  }
8
8
 
9
+ .seam-table-header {
10
+ .seam-left {
11
+ display: flex;
12
+ align-items: center;
13
+
14
+ .seam-add-access-code-button {
15
+ margin-left: 8px;
16
+ }
17
+ }
18
+ }
19
+
9
20
  .seam-table-row {
10
21
  cursor: pointer;
11
22
 
@@ -7,12 +7,22 @@
7
7
  border: 1px solid rgb(0 122 252 / 50%);
8
8
  cursor: pointer;
9
9
  background: colors.$white;
10
- padding: 6px;
10
+ padding: 2px;
11
11
  line-height: 0;
12
+ width: 30px;
13
+ height: 30px;
12
14
 
13
15
  &:hover {
14
16
  background: rgb(0 122 252 / 8%);
15
17
  }
18
+
19
+ svg {
20
+ scale: 0.75;
21
+
22
+ * {
23
+ fill: colors.$primary;
24
+ }
25
+ }
16
26
  }
17
27
  }
18
28
 
@@ -22,6 +22,7 @@ $bg-aa: #fafafa;
22
22
  $bg-gray: #ececec;
23
23
  $status-red: #e36857;
24
24
  $white: #fff;
25
+ $black: #000;
25
26
  $alert-error-dark: #cb7d7d;
26
27
  $alert-error-line: #f1d4d4;
27
28
  $alert-error-fill: #ffefef;
@@ -0,0 +1,15 @@
1
+ @use './colors';
2
+
3
+ @mixin all {
4
+ .seam-form-field {
5
+ margin-bottom: 24px;
6
+
7
+ > .seam-input-label {
8
+ user-select: none;
9
+ }
10
+
11
+ > .seam-radio-field {
12
+ margin-top: 4px;
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,11 @@
1
+ @use './colors';
2
+
3
+ @mixin all {
4
+ .seam-input-label {
5
+ cursor: default;
6
+ margin-bottom: 4px;
7
+ font-size: 16px;
8
+ font-weight: 600;
9
+ line-height: 134%;
10
+ }
11
+ }
@@ -2,33 +2,27 @@
2
2
 
3
3
  @mixin text-field {
4
4
  .seam-text-field {
5
- border: 1px solid colors.$text-gray-3;
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
- }
5
+ > .seam-main {
6
+ border: 1px solid colors.$text-gray-3;
7
+ border-radius: 8px;
8
+ display: flex;
9
+ padding: 0 8px;
10
+ align-items: center;
18
11
 
19
- &.seam-disabled {
20
- border-color: colors.$text-gray-3;
21
- background: colors.$bg-gray;
22
- cursor: not-allowed;
12
+ &:hover {
13
+ border-color: colors.$text-default;
14
+ }
23
15
 
24
- .seam-text-field-input {
25
- cursor: not-allowed;
16
+ &:focus-within {
17
+ border-color: colors.$primary;
26
18
  }
27
19
  }
28
20
 
29
21
  .seam-text-field-input {
30
22
  height: 40px;
31
23
  border: 0;
24
+ width: 100%;
25
+ border-radius: 8px;
32
26
 
33
27
  &:focus {
34
28
  outline: none;
@@ -73,9 +67,40 @@
73
67
  }
74
68
  }
75
69
 
76
- &.seam-search-text-field {
77
- .seam-text-field-input {
78
- width: 100%;
70
+ > .seam-helper-text {
71
+ margin-top: 4px;
72
+ }
73
+
74
+ &.seam-disabled {
75
+ > .seam-main {
76
+ border-color: colors.$text-gray-3;
77
+ background: colors.$bg-gray;
78
+ cursor: not-allowed;
79
+
80
+ > .seam-text-field-input {
81
+ cursor: not-allowed;
82
+ }
83
+ }
84
+ }
85
+
86
+ &.seam-large {
87
+ > .seam-main {
88
+ padding: 0 12px;
89
+
90
+ > .seam-text-field-input {
91
+ height: 48px;
92
+ font-size: 16px;
93
+ }
94
+ }
95
+ }
96
+
97
+ &.seam-error {
98
+ > .seam-main {
99
+ border: 1px solid colors.$status-red;
100
+ }
101
+
102
+ > .seam-helper-text {
103
+ color: colors.$status-red;
79
104
  }
80
105
  }
81
106
  }
@@ -12,7 +12,7 @@
12
12
  .seam-back-icon {
13
13
  position: absolute;
14
14
  top: 50%;
15
- transform: translate(4px, -25%);
15
+ transform: translate(4px, -50%);
16
16
  left: 0;
17
17
  cursor: pointer;
18
18
  }
@@ -22,6 +22,14 @@
22
22
  line-height: 134%;
23
23
  color: colors.$text-gray-1;
24
24
  }
25
+
26
+ .seam-subheading {
27
+ display: block;
28
+ font-size: 12px;
29
+ line-height: 140%;
30
+ text-align: center;
31
+ color: colors.$text-gray-1;
32
+ }
25
33
  }
26
34
  }
27
35
 
@@ -13,6 +13,9 @@
13
13
  @use './device-table';
14
14
  @use './access-code-details';
15
15
  @use './supported-device-table';
16
+ @use './access-code-form';
17
+ @use './input-label';
18
+ @use './form-field';
16
19
 
17
20
  .seam-components {
18
21
  // Reset
@@ -22,6 +25,8 @@
22
25
  @include typography.all;
23
26
  @include tables.all;
24
27
  @include inputs.all;
28
+ @include input-label.all;
29
+ @include form-field.all;
25
30
  @include buttons.all;
26
31
  @include layout.all;
27
32
  @include icons.all;
@@ -32,6 +37,7 @@
32
37
  @include device-table.all;
33
38
  @include access-code-table.all;
34
39
  @include access-code-details.all;
40
+ @include access-code-form.all;
35
41
  @include alert.all;
36
42
  @include supported-device-table.all;
37
43
  }
@@ -4,7 +4,7 @@
4
4
  .seam-table-header {
5
5
  display: flex;
6
6
  justify-content: space-between;
7
- align-items: flex-start;
7
+ align-items: center;
8
8
  height: 56px;
9
9
  padding: 0 16px;
10
10