@basic-ui/material 0.1.15 → 0.1.16

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 (55) hide show
  1. package/build/cjs/index.js +58 -43
  2. package/build/cjs/index.js.map +1 -1
  3. package/build/esm/Combobox/Combobox.d.ts +7 -7
  4. package/build/esm/Combobox/Combobox.js +1 -0
  5. package/build/esm/Combobox/Combobox.js.map +1 -1
  6. package/build/esm/FloatingLabel/FloatingLabel.d.ts +0 -1
  7. package/build/esm/FloatingLabel/FloatingLabel.js +1 -3
  8. package/build/esm/FloatingLabel/FloatingLabel.js.map +1 -1
  9. package/build/esm/Menu/Menu.d.ts +4 -4
  10. package/build/esm/Menu/Menu.js +1 -0
  11. package/build/esm/Menu/Menu.js.map +1 -1
  12. package/build/esm/NotchedOutline/styledComponents.js +2 -2
  13. package/build/esm/NotchedOutline/styledComponents.js.map +1 -1
  14. package/build/esm/Select/Select.d.ts +3 -3
  15. package/build/esm/Select/Select.js +3 -12
  16. package/build/esm/Select/Select.js.map +1 -1
  17. package/build/esm/Select/SelectIcon.d.ts +1 -1
  18. package/build/esm/Select/SelectIcon.js +3 -1
  19. package/build/esm/Select/SelectIcon.js.map +1 -1
  20. package/build/esm/Select/styledComponents.js +9 -5
  21. package/build/esm/Select/styledComponents.js.map +1 -1
  22. package/build/esm/SelectItem/SelectItem.d.ts +1 -1
  23. package/build/esm/TextField/FilledContainer.d.ts +1 -1
  24. package/build/esm/TextField/FilledContainer.js +11 -5
  25. package/build/esm/TextField/FilledContainer.js.map +1 -1
  26. package/build/esm/TextField/IconContainer.d.ts +2 -2
  27. package/build/esm/TextField/IconContainer.js +4 -2
  28. package/build/esm/TextField/IconContainer.js.map +1 -1
  29. package/build/esm/TextField/Input.d.ts +1 -1
  30. package/build/esm/TextField/Input.js +17 -9
  31. package/build/esm/TextField/Input.js.map +1 -1
  32. package/build/esm/TextField/OutlinedContainer.d.ts +1 -1
  33. package/build/esm/TextField/OutlinedContainer.js +7 -4
  34. package/build/esm/TextField/OutlinedContainer.js.map +1 -1
  35. package/build/esm/TextField/TextField.d.ts +1 -1
  36. package/build/esm/TextField/consts.d.ts +5 -0
  37. package/build/esm/TextField/consts.js +6 -0
  38. package/build/esm/TextField/consts.js.map +1 -0
  39. package/build/tsconfig.tsbuildinfo +1 -1
  40. package/package.json +2 -2
  41. package/src/Combobox/Combobox.tsx +1 -0
  42. package/src/FloatingLabel/FloatingLabel.tsx +0 -3
  43. package/src/Menu/Menu.tsx +1 -0
  44. package/src/NotchedOutline/styledComponents.ts +1 -1
  45. package/src/Select/PaymentMethodSelect.story.tsx +321 -0
  46. package/src/Select/Select.story.tsx +26 -1
  47. package/src/Select/Select.tsx +12 -34
  48. package/src/Select/SelectIcon.tsx +2 -1
  49. package/src/Select/styledComponents.tsx +8 -4
  50. package/src/TextField/FilledContainer.tsx +22 -5
  51. package/src/TextField/IconContainer.tsx +5 -4
  52. package/src/TextField/Input.tsx +29 -9
  53. package/src/TextField/OutlinedContainer.tsx +18 -4
  54. package/src/TextField/TextField.story.tsx +14 -1
  55. package/src/TextField/consts.ts +7 -0
@@ -0,0 +1,321 @@
1
+ import { Box, Select, SelectItem, SelectProps } from '../';
2
+ import { forwardRef, useState } from 'react';
3
+ import type { ReactElement, ComponentType } from 'react';
4
+
5
+ export default {
6
+ title: 'components/Select/Complex',
7
+ };
8
+
9
+ const ApplePayMark = (props) => (
10
+ <svg
11
+ xmlns="http://www.w3.org/2000/svg"
12
+ width={165.521}
13
+ height={105.965}
14
+ viewBox="0 0 166 106"
15
+ xmlSpace="preserve"
16
+ {...props}
17
+ >
18
+ <path d="M150.698 0H14.823c-.566 0-1.133 0-1.698.003-.477.004-.953.009-1.43.022-1.039.028-2.087.09-3.113.274a10.51 10.51 0 0 0-2.958.975 9.932 9.932 0 0 0-4.35 4.35 10.463 10.463 0 0 0-.975 2.96C.113 9.611.052 10.658.024 11.696c-.013.477-.019.953-.022 1.43C0 13.69 0 14.256 0 14.823v76.318c0 .567 0 1.132.002 1.699.003.476.009.953.022 1.43.028 1.036.09 2.084.275 3.11a10.46 10.46 0 0 0 .974 2.96 9.897 9.897 0 0 0 1.83 2.52 9.874 9.874 0 0 0 2.52 1.83c.947.483 1.917.79 2.96.977 1.025.183 2.073.245 3.112.273.477.011.953.018 1.43.02.565.004 1.132.004 1.698.004h135.875c.565 0 1.132 0 1.697-.004a79.71 79.71 0 0 0 1.431-.02c1.037-.028 2.085-.09 3.113-.273a10.478 10.478 0 0 0 2.958-.977 9.955 9.955 0 0 0 4.35-4.35c.483-.947.789-1.917.974-2.96.186-1.026.246-2.074.274-3.11.013-.477.02-.954.022-1.43.004-.567.004-1.132.004-1.699V14.824c0-.567 0-1.133-.004-1.699a63.067 63.067 0 0 0-.022-1.429c-.028-1.038-.088-2.085-.274-3.112a10.4 10.4 0 0 0-.974-2.96 9.941 9.941 0 0 0-4.35-4.35A10.52 10.52 0 0 0 156.939.3c-1.028-.185-2.076-.246-3.113-.274a71.413 71.413 0 0 0-1.431-.022C151.83 0 151.263 0 150.698 0z" />
19
+ <path
20
+ fill="#FFF"
21
+ d="m150.698 3.532 1.672.003c.452.003.905.008 1.36.02.792.022 1.719.065 2.583.22.75.135 1.38.34 1.984.648a6.392 6.392 0 0 1 2.804 2.807c.306.6.51 1.226.645 1.983.154.854.197 1.783.218 2.58.013.45.019.9.02 1.36.005.557.005 1.113.005 1.671v76.318c0 .558 0 1.114-.004 1.682-.002.45-.008.9-.02 1.35-.022.796-.065 1.725-.221 2.589a6.855 6.855 0 0 1-.645 1.975 6.397 6.397 0 0 1-2.808 2.807c-.6.306-1.228.512-1.971.645-.882.157-1.847.2-2.574.22-.457.01-.912.017-1.379.019-.555.004-1.113.004-1.669.004H14.801c-.55 0-1.1 0-1.66-.004a75.086 75.086 0 0 1-1.35-.018c-.744-.02-1.71-.064-2.584-.22a6.938 6.938 0 0 1-1.986-.65 6.337 6.337 0 0 1-1.622-1.18 6.355 6.355 0 0 1-1.178-1.623 6.935 6.935 0 0 1-.647-1.985c-.155-.863-.198-1.788-.22-2.578a66.017 66.017 0 0 1-.02-1.355l-.002-1.327V14.474l.002-1.325c.003-.453.008-.905.02-1.357.022-.792.065-1.717.222-2.587a6.924 6.924 0 0 1 .646-1.981c.304-.598.7-1.144 1.18-1.622a6.385 6.385 0 0 1 1.624-1.18 6.96 6.96 0 0 1 1.98-.647c.865-.155 1.792-.198 2.586-.22.452-.012.905-.017 1.354-.02l1.677-.003h135.875"
22
+ />
23
+ <path d="M45.186 35.64c1.417-1.772 2.38-4.152 2.126-6.585-2.075.104-4.607 1.37-6.073 3.143-1.316 1.52-2.48 4-2.177 6.33 2.33.202 4.656-1.165 6.124-2.887M47.285 38.983c-3.382-.202-6.258 1.919-7.873 1.919-1.616 0-4.09-1.818-6.764-1.769-3.482.051-6.713 2.02-8.48 5.15-3.634 6.264-.959 15.556 2.575 20.657 1.717 2.524 3.785 5.303 6.51 5.203 2.575-.101 3.584-1.668 6.714-1.668 3.128 0 4.037 1.668 6.763 1.617 2.827-.05 4.594-2.525 6.31-5.051 1.969-2.877 2.775-5.655 2.825-5.808-.05-.05-5.45-2.122-5.5-8.333-.051-5.201 4.24-7.675 4.441-7.828-2.423-3.584-6.209-3.988-7.52-4.09M76.734 31.944c7.35 0 12.47 5.067 12.47 12.444 0 7.404-5.225 12.497-12.654 12.497h-8.14v12.943h-5.88V31.944h14.204zM68.41 51.949h6.747c5.12 0 8.034-2.756 8.034-7.534 0-4.778-2.914-7.509-8.008-7.509h-6.773V51.95zM90.74 61.979c0-4.831 3.702-7.798 10.266-8.166l7.56-.446v-2.126c0-3.072-2.074-4.91-5.539-4.91-3.282 0-5.33 1.575-5.828 4.043h-5.356c.315-4.988 4.568-8.664 11.394-8.664 6.695 0 10.974 3.545 10.974 9.084v19.034h-5.435v-4.542h-.13c-1.602 3.072-5.094 5.015-8.717 5.015-5.408 0-9.189-3.36-9.189-8.322zm17.826-2.494v-2.18l-6.8.42c-3.386.237-5.303 1.733-5.303 4.096 0 2.415 1.996 3.99 5.041 3.99 3.964 0 7.062-2.73 7.062-6.326zM119.342 79.989v-4.595c.419.105 1.364.105 1.837.105 2.625 0 4.043-1.102 4.909-3.938 0-.052.5-1.68.5-1.706l-9.977-27.646h6.143l6.984 22.474h.104l6.985-22.474h5.985l-10.344 29.063c-2.362 6.695-5.093 8.848-10.816 8.848-.473 0-1.891-.053-2.31-.131z" />
24
+ </svg>
25
+ );
26
+
27
+ const MastercardCard = (props) => (
28
+ <svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" {...props}>
29
+ <g fill="none">
30
+ <path d="M0 0h32v32H0z" fill="#000" />
31
+ <path d="M13.02 10.505h5.923v10.857H13.02z" fill="#ff5f00" />
32
+ <path
33
+ d="M13.396 15.935a6.944 6.944 0 0 1 2.585-5.43c-2.775-2.224-6.76-1.9-9.156.745s-2.395 6.723 0 9.368 6.38 2.969 9.156.744a6.944 6.944 0 0 1-2.585-5.427z"
34
+ fill="#eb001b"
35
+ />
36
+ <path
37
+ d="M26.934 15.935c0 2.643-1.48 5.054-3.81 6.21s-5.105.851-7.143-.783a6.955 6.955 0 0 0 2.587-5.428c0-2.118-.954-4.12-2.587-5.429 2.038-1.633 4.81-1.937 7.142-.782s3.811 3.566 3.811 6.21z"
38
+ fill="#f79e1b"
39
+ />
40
+ </g>
41
+ </svg>
42
+ );
43
+
44
+ const IDEALLogo = (props) => (
45
+ <svg
46
+ id="Layer_1"
47
+ xmlns="http://www.w3.org/2000/svg"
48
+ x={0}
49
+ y={0}
50
+ viewBox="0 0 306.1 269.8"
51
+ style={{
52
+ enableBackground: 'new 0 0 306.1 269.8',
53
+ }}
54
+ xmlSpace="preserve"
55
+ {...props}
56
+ >
57
+ <style>{'.st0{fill:#fff}'}</style>
58
+ <path
59
+ className="st0"
60
+ d="M0 20v229.8c0 11 9 20 20 20h137.3c103.8 0 148.8-58.1 148.8-135.2C306.1 57.9 261.1 0 157.3 0H20C9 0 0 9 0 20z"
61
+ />
62
+ <path
63
+ d="M91.9 56.4v169.8h73.9c67.1 0 96.2-37.9 96.2-91.5 0-51.3-29.1-91.1-96.2-91.1h-61.1c-7.1 0-12.8 5.8-12.8 12.8z"
64
+ style={{
65
+ fill: '#c06',
66
+ }}
67
+ />
68
+ <path d="M157.3 251.5H37.9c-10.6 0-19.2-8.6-19.2-19.2V37.6c0-10.6 8.6-19.2 19.2-19.2h119.4c113.3 0 130.2 72.9 130.2 116.3 0 75.3-46.3 116.8-130.2 116.8zM37.9 24.8c-7.1 0-12.8 5.7-12.8 12.8v194.7c0 7.1 5.7 12.8 12.8 12.8h119.4c79.8 0 123.8-39.2 123.8-110.4 0-95.6-77.6-109.9-123.8-109.9H37.9z" />
69
+ <path
70
+ className="st0"
71
+ d="M117.9 111.8c2.6 0 5 .4 7.3 1.2 2.3.8 4.2 2.1 5.9 3.7 1.6 1.7 2.9 3.8 3.9 6.2.9 2.5 1.4 5.4 1.4 8.8 0 3-.4 5.7-1.1 8.2-.8 2.5-1.9 4.7-3.4 6.5-1.5 1.8-3.4 3.2-5.7 4.3-2.3 1-5 1.6-8.1 1.6h-17.5v-40.6h17.3zm-.6 33.1c1.3 0 2.5-.2 3.8-.6 1.2-.4 2.3-1.1 3.2-2.1.9-1 1.7-2.2 2.3-3.8.6-1.6.9-3.4.9-5.7 0-2-.2-3.9-.6-5.5-.4-1.6-1.1-3.1-2-4.2s-2.1-2.1-3.6-2.7c-1.5-.6-3.3-.9-5.5-.9h-6.4V145h7.9zM172.5 111.8v7.5h-21.4v8.7h19.7v6.9h-19.7v9.9H173v7.5h-30.8v-40.6h30.3zM203.1 111.8l15.2 40.6H209l-3.1-9h-15.2l-3.2 9h-9l15.3-40.6h9.3zm.5 24.9-5.1-14.9h-.1l-5.3 14.9h10.5zM232.8 111.8v33.1h19.8v7.5h-28.7v-40.6h8.9z"
72
+ />
73
+ <circle cx={58.5} cy={132.1} r={18.7} />
74
+ <path d="M72.6 226.2c-15.7 0-28.3-12.7-28.3-28.3v-22.1c0-7.8 6.3-14.2 14.2-14.2 7.8 0 14.2 6.3 14.2 14.2v50.4z" />
75
+ </svg>
76
+ );
77
+
78
+ const GooglePayLogo = (props) => (
79
+ <svg
80
+ width={752}
81
+ height={400}
82
+ fill="none"
83
+ viewBox="0 0 752 400"
84
+ xmlns="http://www.w3.org/2000/svg"
85
+ {...props}
86
+ >
87
+ <path
88
+ d="M552 0H200C90 0 0 90 0 200s90 200 200 200h352c110 0 200-90 200-200S662 0 552 0Z"
89
+ fill="#fff"
90
+ />
91
+ <path
92
+ d="M552 16.2c24.7 0 48.7 4.9 71.3 14.5 21.9 9.3 41.5 22.6 58.5 39.5 16.9 16.9 30.2 36.6 39.5 58.5 9.6 22.6 14.5 46.6 14.5 71.3 0 24.7-4.9 48.7-14.5 71.3-9.3 21.9-22.6 41.5-39.5 58.5-16.9 16.9-36.6 30.2-58.5 39.5-22.6 9.6-46.6 14.5-71.3 14.5H200c-24.7 0-48.7-4.9-71.3-14.5-21.9-9.3-41.5-22.6-58.5-39.5-16.9-16.9-30.2-36.6-39.5-58.5-9.6-22.6-14.5-46.6-14.5-71.3 0-24.7 4.9-48.7 14.5-71.3 9.3-21.9 22.6-41.5 39.5-58.5 16.9-16.9 36.6-30.2 58.5-39.5 22.6-9.6 46.6-14.5 71.3-14.5h352ZM552 0H200C90 0 0 90 0 200s90 200 200 200h352c110 0 200-90 200-200S662 0 552 0Z"
93
+ fill="#3C4043"
94
+ />
95
+ <path
96
+ d="M358.298 214.201v60.5h-19.2v-149.4h50.9c12.9 0 23.9 4.3 32.9 12.9 9.2 8.6 13.8 19.1 13.8 31.5 0 12.7-4.6 23.2-13.8 31.7-8.9 8.5-19.9 12.7-32.9 12.7h-31.7v.1Zm0-70.5v52.1h32.1c7.6 0 14-2.6 19-7.7 5.1-5.1 7.7-11.3 7.7-18.3 0-6.9-2.6-13-7.7-18.1-5-5.3-11.3-7.9-19-7.9h-32.1v-.1ZM486.897 169.101c14.2 0 25.4 3.8 33.6 11.4 8.2 7.6 12.3 18 12.3 31.2v63h-18.3v-14.2h-.8c-7.9 11.7-18.5 17.5-31.7 17.5-11.3 0-20.7-3.3-28.3-10-7.6-6.7-11.4-15-11.4-25 0-10.6 4-19 12-25.2 8-6.3 18.7-9.4 32-9.4 11.4 0 20.8 2.1 28.1 6.3v-4.4c0-6.7-2.6-12.3-7.9-17-5.3-4.7-11.5-7-18.6-7-10.7 0-19.2 4.5-25.4 13.6l-16.9-10.6c9.3-13.5 23.1-20.2 41.3-20.2Zm-24.8 74.2c0 5 2.1 9.2 6.4 12.5 4.2 3.3 9.2 5 14.9 5 8.1 0 15.3-3 21.6-9 6.3-6 9.5-13 9.5-21.1-6-4.7-14.3-7.1-25-7.1-7.8 0-14.3 1.9-19.5 5.6-5.3 3.9-7.9 8.6-7.9 14.1ZM637.196 172.4l-64 147.2h-19.8l23.8-51.5-42.2-95.7h20.9l30.4 73.4h.4l29.6-73.4h20.9Z"
97
+ fill="#3C4043"
98
+ />
99
+ <path
100
+ d="M281.928 202c0-6.26-.56-12.25-1.6-18.01h-80.48v33l46.35.01c-1.88 10.98-7.93 20.34-17.2 26.58v21.41h27.59c16.11-14.91 25.34-36.95 25.34-62.99Z"
101
+ fill="#4285F4"
102
+ />
103
+ <path
104
+ d="M229.009 243.581c-7.68 5.18-17.57 8.21-29.14 8.21-22.35 0-41.31-15.06-48.1-35.36h-28.46v22.08c14.1 27.98 43.08 47.18 76.56 47.18 23.14 0 42.58-7.61 56.73-20.71l-27.59-21.4Z"
105
+ fill="#34A853"
106
+ />
107
+ <path
108
+ d="M149.089 200.05c0-5.7.95-11.21 2.68-16.39v-22.08h-28.46c-5.83 11.57-9.11 24.63-9.11 38.47s3.29 26.9 9.11 38.47l28.46-22.08a51.657 51.657 0 0 1-2.68-16.39Z"
109
+ fill="#FABB05"
110
+ />
111
+ <path
112
+ d="M199.869 148.3c12.63 0 23.94 4.35 32.87 12.85l24.45-24.43c-14.85-13.83-34.21-22.32-57.32-22.32-33.47 0-62.46 19.2-76.56 47.18l28.46 22.08c6.79-20.3 25.75-35.36 48.1-35.36Z"
113
+ fill="#E94235"
114
+ />
115
+ </svg>
116
+ );
117
+
118
+ const svgByBrand: Record<string, ComponentType<any>> = {
119
+ amex: MastercardCard,
120
+ diners: MastercardCard,
121
+ discover: MastercardCard,
122
+ jcb: MastercardCard,
123
+ mastercard: MastercardCard,
124
+ unionpay: MastercardCard,
125
+ visa: MastercardCard,
126
+ unknown: MastercardCard,
127
+ };
128
+
129
+ export type PaymentMethodCard = {
130
+ type: 'card';
131
+ id: string;
132
+ last4: string;
133
+ brand: string;
134
+ };
135
+
136
+ export type PaymentMethodSepaDebit = {
137
+ type: 'sepa_debit';
138
+ id: string;
139
+ };
140
+
141
+ export type PaymentMethodIDEAL = {
142
+ type: 'ideal';
143
+ id: 'ideal';
144
+ };
145
+
146
+ export type PaymentMethodApplePay = {
147
+ type: 'apple_pay';
148
+ id: 'apple_pay';
149
+ };
150
+
151
+ export type PaymentMethodGooglePay = {
152
+ type: 'google_pay';
153
+ id: 'google_pay';
154
+ };
155
+
156
+ export type PaymentMethodNewCard = {
157
+ type: 'new_card';
158
+ id: 'new_card';
159
+ };
160
+
161
+ export type PaymentMethod =
162
+ | PaymentMethodCard
163
+ | PaymentMethodIDEAL
164
+ | PaymentMethodSepaDebit
165
+ | PaymentMethodApplePay
166
+ | PaymentMethodGooglePay
167
+ | PaymentMethodNewCard;
168
+
169
+ export interface PaymentMethodSelectProps {
170
+ paymentMethods: PaymentMethod[];
171
+ value: string;
172
+ onChange: SelectProps['onChange'];
173
+ }
174
+
175
+ const logoWidth = 32;
176
+ const logoHeight = 24;
177
+ const LogoWrapper = ({ children }: { children: ReactElement }) => (
178
+ <Box
179
+ sx={{
180
+ mr: 2,
181
+ borderRadius: '2px',
182
+ overflow: 'hidden',
183
+ width: logoWidth,
184
+ height: logoHeight,
185
+ minWidth: logoWidth,
186
+ minHeight: logoHeight,
187
+ '& > svg': { width: logoWidth, height: logoWidth },
188
+ display: 'inline-flex',
189
+ alignItems: 'center',
190
+ justifyContent: 'center',
191
+ }}
192
+ >
193
+ {children}
194
+ </Box>
195
+ );
196
+
197
+ function SelectItemContent(props: PaymentMethod): ReactElement {
198
+ if (props.type === 'card') {
199
+ const CardIcon = svgByBrand[props.brand] || svgByBrand['unknown'];
200
+ return (
201
+ <>
202
+ <LogoWrapper>
203
+ <CardIcon />
204
+ </LogoWrapper>
205
+ {props.id} •••• {props.last4}
206
+ </>
207
+ );
208
+ }
209
+ if (props.type === 'ideal') {
210
+ return (
211
+ <>
212
+ <LogoWrapper>
213
+ <IDEALLogo />
214
+ </LogoWrapper>
215
+ iDEAL
216
+ </>
217
+ );
218
+ }
219
+ if (props.type === 'apple_pay') {
220
+ return (
221
+ <>
222
+ <LogoWrapper>
223
+ <ApplePayMark />
224
+ </LogoWrapper>
225
+ Apple Pay
226
+ </>
227
+ );
228
+ }
229
+ if (props.type === 'google_pay') {
230
+ return (
231
+ <>
232
+ <LogoWrapper>
233
+ <GooglePayLogo />
234
+ </LogoWrapper>
235
+ Google Pay
236
+ </>
237
+ );
238
+ }
239
+ if (props.type === 'sepa_debit') {
240
+ return (
241
+ <>
242
+ <LogoWrapper>
243
+ <IDEALLogo />
244
+ </LogoWrapper>
245
+ Direct debit
246
+ </>
247
+ );
248
+ }
249
+ if (props.type === 'new_card') {
250
+ return <>Add new card...</>;
251
+ }
252
+ return <>Unknown payment method</>;
253
+ }
254
+
255
+ const PaymentMethodSelect = forwardRef<
256
+ HTMLButtonElement | HTMLSelectElement,
257
+ PaymentMethodSelectProps
258
+ >(function PaymentMethodSelect(props, ref) {
259
+ const { onChange, paymentMethods, value } = props;
260
+ return (
261
+ <Select
262
+ ref={ref}
263
+ label="Payment method"
264
+ variant="outlined"
265
+ onChange={onChange}
266
+ value={value}
267
+ >
268
+ {paymentMethods.map((m) => (
269
+ <SelectItem value={m.id} key={m.id}>
270
+ <Box display="inline-flex" alignItems="center">
271
+ <SelectItemContent {...m} />
272
+ </Box>
273
+ </SelectItem>
274
+ ))}
275
+ </Select>
276
+ );
277
+ });
278
+
279
+ export const PaymentMethodSelectControlled = () => {
280
+ const paymentMethods: PaymentMethod[] = [
281
+ {
282
+ type: 'apple_pay',
283
+ id: 'apple_pay',
284
+ },
285
+ {
286
+ type: 'google_pay',
287
+ id: 'google_pay',
288
+ },
289
+ { type: 'ideal', id: 'ideal' },
290
+ ];
291
+ for (const testCard of [
292
+ 'amex',
293
+ 'diners',
294
+ 'discover',
295
+ 'jcb',
296
+ 'mastercard',
297
+ 'unionpay',
298
+ 'visa',
299
+ 'unknown',
300
+ ]) {
301
+ for (let i = 0; i < 10; i++) {
302
+ paymentMethods.push({
303
+ type: 'card',
304
+ id: testCard + `_${i}`,
305
+ brand: testCard,
306
+ last4: '1234',
307
+ });
308
+ }
309
+ }
310
+ const [value, setValue] = useState(paymentMethods[0].id);
311
+
312
+ console.log({ value });
313
+
314
+ return (
315
+ <PaymentMethodSelect
316
+ value={value}
317
+ onChange={(_, v) => setValue(v)}
318
+ paymentMethods={paymentMethods}
319
+ />
320
+ );
321
+ };
@@ -9,6 +9,19 @@ export default {
9
9
  title: 'components/Select',
10
10
  };
11
11
 
12
+ const SearchIcon = (props) => (
13
+ <svg
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ height={24}
16
+ width={24}
17
+ viewBox="0 0 48 48"
18
+ fill="currentColor"
19
+ {...props}
20
+ >
21
+ <path d="M39.8 41.95 26.65 28.8q-1.5 1.3-3.5 2.025-2 .725-4.25.725-5.4 0-9.15-3.75T6 18.75q0-5.3 3.75-9.05 3.75-3.75 9.1-3.75 5.3 0 9.025 3.75 3.725 3.75 3.725 9.05 0 2.15-.7 4.15-.7 2-2.1 3.75L42 39.75Zm-20.95-13.4q4.05 0 6.9-2.875Q28.6 22.8 28.6 18.75t-2.85-6.925Q22.9 8.95 18.85 8.95q-4.1 0-6.975 2.875T9 18.75q0 4.05 2.875 6.925t6.975 2.875Z" />
22
+ </svg>
23
+ );
24
+
12
25
  const Example = ({ variant, native = false }) => {
13
26
  const [error, setError] = useState<boolean | string>(false);
14
27
  const [color, setColor] = useState<'primary' | 'secondary'>('primary');
@@ -186,7 +199,19 @@ const Example = ({ variant, native = false }) => {
186
199
  label=""
187
200
  helperText="Helper text"
188
201
  children={children}
189
- leadingIcon={'O'}
202
+ leadingIcon={<SearchIcon />}
203
+ />
204
+ </Box>
205
+ <Box mb={3} width={230}>
206
+ <Select
207
+ error={error}
208
+ color={color}
209
+ native={native}
210
+ variant={variant}
211
+ label="Standard"
212
+ helperText="Helper text"
213
+ children={children}
214
+ leadingIcon={<SearchIcon />}
190
215
  />
191
216
  </Box>
192
217
  </Box>
@@ -1,12 +1,4 @@
1
- import {
2
- forwardRef,
3
- useState,
4
- useRef,
5
- ForwardRefExoticComponent,
6
- RefAttributes,
7
- useEffect,
8
- useId,
9
- } from 'react';
1
+ import { forwardRef, useState, useRef, useEffect, useId } from 'react';
10
2
 
11
3
  import type * as React from 'react';
12
4
  import {
@@ -15,11 +7,7 @@ import {
15
7
  useControlledState,
16
8
  } from '@basic-ui/core';
17
9
  import { useTheme, Theme } from '../theme';
18
- import {
19
- Select as SelectComp,
20
- SelectButton,
21
- SelectProps as BaseSelectProps,
22
- } from './styledComponents';
10
+ import { Select as SelectComp, SelectButton } from './styledComponents';
23
11
  import { FilledContainer } from '../TextField/FilledContainer';
24
12
  import { HelperText } from '../TextField/HelperText';
25
13
  import { OutlinedContainer } from '../TextField/OutlinedContainer';
@@ -27,7 +15,7 @@ import { SelectProvider } from './context';
27
15
  import { Menu, MenuPopover, MenuList } from '../Menu';
28
16
  import { SelectIcon } from './SelectIcon';
29
17
  import { makeDefaultRender } from './defaultRender';
30
- import { Box } from '../Box';
18
+ import { Box, BoxProps } from '../Box';
31
19
  import { IconContainer } from '../TextField/IconContainer';
32
20
 
33
21
  const componentMap = {
@@ -37,7 +25,7 @@ const componentMap = {
37
25
 
38
26
  export interface SelectProps
39
27
  extends Omit<
40
- React.SelectHTMLAttributes<HTMLSelectElement>,
28
+ BoxProps<HTMLSelectElement, React.SelectHTMLAttributes<HTMLSelectElement>>,
41
29
  'value' | 'defaultValue' | 'onChange'
42
30
  > {
43
31
  variant?: 'outlined' | 'filled';
@@ -108,7 +96,10 @@ export const Select = forwardRef<
108
96
 
109
97
  const handleOnChange = (e: any) => {
110
98
  onChange &&
111
- onChange(e as any, native ? e.target.value : e.target.dataset.value);
99
+ onChange(
100
+ e as any,
101
+ native ? e.target.value : e.currentTarget.dataset.value
102
+ );
112
103
  };
113
104
 
114
105
  const hasError = Boolean(error);
@@ -122,9 +113,9 @@ export const Select = forwardRef<
122
113
 
123
114
  const labelIsFloating = hasFocus || open || renderValue(value) !== undefined;
124
115
 
125
- const Comp: ForwardRefExoticComponent<
126
- BaseSelectProps & RefAttributes<HTMLSelectElement | HTMLButtonElement>
127
- > = native ? SelectComp : (SelectButton as any);
116
+ const Comp: React.ComponentType<any> = native
117
+ ? (SelectComp as any)
118
+ : (SelectButton as any);
128
119
 
129
120
  useEffect(() => {
130
121
  // right after mounting, if the default value in the select element
@@ -170,6 +161,7 @@ export const Select = forwardRef<
170
161
  aria-describedby={helperTextId}
171
162
  hasLabel={!!label}
172
163
  leadingIcon={Boolean(leadingIcon)}
164
+ trailingIcon={true}
173
165
  {...otherProps}
174
166
  >
175
167
  {native ? children : renderValue(value)}
@@ -182,20 +174,6 @@ export const Select = forwardRef<
182
174
  minWidth: buttonRef?.current?.offsetWidth,
183
175
  }}
184
176
  role="listbox"
185
- __css={
186
- variant === 'filled'
187
- ? {
188
- "[data-popper-placement='top'] &": {
189
- borderBottomLeftRadius: 0,
190
- borderBottomRightRadius: 0,
191
- },
192
- "[data-popper-placement='bottom'] &": {
193
- borderTopLeftRadius: 0,
194
- borderTopRightRadius: 0,
195
- },
196
- }
197
- : {}
198
- }
199
177
  >
200
178
  {children}
201
179
  </MenuList>
@@ -3,6 +3,7 @@ import type * as React from 'react';
3
3
  import { Box, BoxProps } from '../Box';
4
4
  import { rem } from 'polished';
5
5
  import { alpha } from '../color';
6
+ import { PADDING_RIGHT_WITH_ICON } from '../TextField/consts';
6
7
 
7
8
  export type SelectIconProps = BoxProps<
8
9
  SVGElement,
@@ -22,7 +23,7 @@ export const SelectIcon = forwardRef<SVGElement, SelectIconProps>(
22
23
  __css={{
23
24
  position: 'absolute',
24
25
  top: `calc(50% - ${rem(12)})`,
25
- right: rem(7),
26
+ right: rem(PADDING_RIGHT_WITH_ICON),
26
27
  width: rem(24),
27
28
  height: rem(24),
28
29
  color: open ? 'primary' : alpha('on.surface', 0.54),
@@ -26,17 +26,21 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(
26
26
  const SelectButtonInner = forwardRef<
27
27
  HTMLButtonElement,
28
28
  SelectProps & { innerAs?: React.ElementType<any> }
29
- >(({ innerAs = 'button', ...props }, forwardedRef) => {
29
+ >(({ innerAs = 'div', ...props }, forwardedRef) => {
30
30
  const InputButton: React.FC<
31
31
  SelectProps & React.RefAttributes<HTMLButtonElement>
32
32
  > = Input as any;
33
33
 
34
- return <InputButton as={innerAs} ref={forwardedRef} {...props} />;
34
+ return (
35
+ <InputButton as={innerAs} ref={forwardedRef} tabIndex={0} {...props} />
36
+ );
35
37
  });
36
38
 
37
39
  export const SelectButton = forwardRef<
38
40
  HTMLButtonElement,
39
41
  SelectProps & MenuButtonProps
40
- >(({ as, ...props }, ref) => (
41
- <BaseMenuButton as={SelectButtonInner} innerAs={as} ref={ref} {...props} />
42
+ >(({ as, children, ...props }, ref) => (
43
+ <BaseMenuButton as={SelectButtonInner} innerAs={as} ref={ref} {...props}>
44
+ {children}
45
+ </BaseMenuButton>
42
46
  ));
@@ -7,6 +7,11 @@ import { LineRipple } from '../LineRipple';
7
7
  import { rem } from 'polished';
8
8
  import { alpha } from '../color';
9
9
  import { Box, BoxProps } from '../Box';
10
+ import {
11
+ ICON_WIDTH,
12
+ PADDING_LEFT_WITHOUT_ICON,
13
+ PADDING_LEFT_WITH_ICON,
14
+ } from './consts';
10
15
 
11
16
  const makeSelector = (
12
17
  state:
@@ -99,7 +104,8 @@ export const FilledContainer = forwardRef<HTMLDivElement, FilledContainerProps>(
99
104
  ...otherProps
100
105
  } = props;
101
106
 
102
- const labelHeight = 18;
107
+ const finalLabelHeight = 16;
108
+ const labelHeight = finalLabelHeight / 0.75;
103
109
  const inputHeight = 56;
104
110
 
105
111
  const color = error ? 'error' : colorProp;
@@ -112,6 +118,7 @@ export const FilledContainer = forwardRef<HTMLDivElement, FilledContainerProps>(
112
118
  active={active || error}
113
119
  __css={{
114
120
  position: 'relative',
121
+ lineHeight: 0,
115
122
  width: '100%',
116
123
  backgroundColor: 'surface',
117
124
  overflow: 'hidden',
@@ -126,11 +133,21 @@ export const FilledContainer = forwardRef<HTMLDivElement, FilledContainerProps>(
126
133
  >
127
134
  {label && (
128
135
  <FloatingLabel
129
- height={labelHeight}
130
- top={(inputHeight - labelHeight) * 0.5}
131
- offsetX={leadingIcon ? 48 : 16}
136
+ height={inputHeight}
137
+ offsetX={
138
+ leadingIcon
139
+ ? PADDING_LEFT_WITH_ICON +
140
+ ICON_WIDTH +
141
+ PADDING_LEFT_WITHOUT_ICON
142
+ : PADDING_LEFT_WITHOUT_ICON
143
+ }
132
144
  translateX={0}
133
- translateY={-labelHeight * 0.75}
145
+ translateY={
146
+ // To debug, delete these lines one by one to see it doing its work
147
+ -(inputHeight * 0.5) + // Put it at the top, crossing middle label
148
+ labelHeight * 0.75 * 0.5 + // Put it at position 0
149
+ 8 // Add a 8px padding to the top
150
+ }
134
151
  active={labelIsFloating}
135
152
  htmlFor={inputId}
136
153
  color={active || error ? color : undefined}
@@ -1,10 +1,11 @@
1
1
  import { rem } from 'polished';
2
- import { VFC } from 'react';
2
+ import { FC } from 'react';
3
3
  import type * as React from 'react';
4
4
  import { Box } from '../Box';
5
5
  import { alpha } from '../color';
6
+ import { ICON_WIDTH, PADDING_LEFT_WITH_ICON } from './consts';
6
7
 
7
- export const IconContainer: VFC<{
8
+ export const IconContainer: FC<{
8
9
  children?: React.ReactNode;
9
10
  position: 'start' | 'end';
10
11
  }> = ({ position, children }) => (
@@ -12,8 +13,8 @@ export const IconContainer: VFC<{
12
13
  position="absolute"
13
14
  __css={{
14
15
  top: 0,
15
- [position === 'start' ? 'left' : 'right']: 3,
16
- minWidth: rem(24),
16
+ [position === 'start' ? 'left' : 'right']: rem(PADDING_LEFT_WITH_ICON),
17
+ minWidth: rem(ICON_WIDTH),
17
18
  display: 'inline-flex',
18
19
  alignItems: 'center',
19
20
  justifyContent: 'center',
@@ -2,6 +2,13 @@ import { rem } from 'polished';
2
2
  import { InputHTMLAttributes, forwardRef } from 'react';
3
3
  import { Box, BoxProps } from '../Box';
4
4
  import { alpha } from '../color';
5
+ import {
6
+ ICON_WIDTH,
7
+ PADDING_LEFT_WITHOUT_ICON,
8
+ PADDING_LEFT_WITH_ICON,
9
+ PADDING_RIGHT_WITHOUT_ICON,
10
+ PADDING_RIGHT_WITH_ICON,
11
+ } from './consts';
5
12
 
6
13
  export interface InputProps
7
14
  extends BoxProps<HTMLInputElement, InputHTMLAttributes<HTMLInputElement>> {
@@ -36,7 +43,6 @@ export const Input = forwardRef<HTMLDivElement, InputProps>(function Input(
36
43
  {...otherProps}
37
44
  __css={{
38
45
  WebkitTapHighlightColor: 'transparent',
39
- boxSizing: 'border-box',
40
46
  appearance: 'none',
41
47
  outline: 'none',
42
48
  width: '100%',
@@ -50,6 +56,14 @@ export const Input = forwardRef<HTMLDivElement, InputProps>(function Input(
50
56
  color: alpha('on.surface', 0.87),
51
57
  letterSpacing: rem(0.5),
52
58
  textAlign: 'left',
59
+ display: 'flex',
60
+ alignItems: 'center',
61
+ ...(!multiline && {
62
+ lineHeight: 1,
63
+ overflow: 'hidden',
64
+ whiteSpace: 'nowrap',
65
+ textOverflow: 'ellipsis',
66
+ }),
53
67
  '::placeholder': {
54
68
  opacity: 0,
55
69
  color: alpha('on.surface', 0.54),
@@ -64,7 +78,7 @@ export const Input = forwardRef<HTMLDivElement, InputProps>(function Input(
64
78
  ':disabled': {
65
79
  cursor: 'default',
66
80
  },
67
- 'select&,button&': {
81
+ 'select&,[role="button"]&': {
68
82
  cursor: 'pointer',
69
83
  },
70
84
  'select&': {
@@ -73,20 +87,26 @@ export const Input = forwardRef<HTMLDivElement, InputProps>(function Input(
73
87
  color: 'on.surface',
74
88
  },
75
89
  },
76
- paddingLeft: leadingIcon ? rem(18 + 30) : rem(16),
77
- paddingRight: trailingIcon ? rem(18 + 30) : rem(16),
90
+ paddingLeft: leadingIcon
91
+ ? rem(PADDING_LEFT_WITH_ICON + ICON_WIDTH + PADDING_LEFT_WITHOUT_ICON)
92
+ : rem(PADDING_LEFT_WITHOUT_ICON),
93
+ paddingRight: trailingIcon
94
+ ? rem(
95
+ PADDING_RIGHT_WITH_ICON + ICON_WIDTH + PADDING_RIGHT_WITHOUT_ICON
96
+ )
97
+ : rem(PADDING_RIGHT_WITHOUT_ICON),
78
98
  ...(variant === 'outlined' && {
79
- paddingTop: hasLabel ? rem(12) : rem(12),
80
- paddingBottom: hasLabel ? rem(14) : rem(12),
99
+ paddingTop: rem(16),
100
+ paddingBottom: rem(16),
81
101
  }),
82
102
  ...(variant === 'filled' && {
83
- paddingTop: hasLabel ? rem(20) : rem(12),
84
- paddingBottom: hasLabel ? rem(6) : rem(12),
103
+ paddingTop: hasLabel ? rem(24) : rem(16),
104
+ paddingBottom: hasLabel ? rem(8) : rem(16),
85
105
  }),
86
106
  ...(multiline && {
87
107
  resize: 'vertical',
88
108
  paddingTop: 0,
89
- marginTop: variant === 'outlined' || !hasLabel ? rem(12) : rem(20),
109
+ marginTop: variant === 'outlined' || !hasLabel ? rem(18) : rem(26),
90
110
  }),
91
111
  ...__css,
92
112
  }}