@moises.ai/design-system 4.19.7 → 4.19.8
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/dist/index.js +2372 -2287
- package/package.json +1 -1
- package/src/components/Select/Select.jsx +113 -36
- package/src/components/Select/Select.module.css +55 -5
- package/src/components/Select/Select.stories.jsx +70 -0
- package/src/components/TextField/TextField.jsx +58 -32
- package/src/components/TextField/TextField.module.css +21 -0
- package/src/components/TextField/TextField.stories.jsx +35 -0
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { useRef, useState } from 'react'
|
|
2
2
|
import {
|
|
3
3
|
Box,
|
|
4
4
|
Flex,
|
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
Tooltip,
|
|
8
8
|
Separator,
|
|
9
9
|
} from '@radix-ui/themes'
|
|
10
|
-
import {
|
|
10
|
+
import { Select as SelectPrimitive } from 'radix-ui'
|
|
11
|
+
import { CheckIcon, InfoIcon } from '../../icons'
|
|
11
12
|
import styles from './Select.module.css'
|
|
12
13
|
import classNames from 'classnames'
|
|
13
14
|
|
|
@@ -22,17 +23,56 @@ export const Select = ({
|
|
|
22
23
|
size = '2',
|
|
23
24
|
variant = 'soft',
|
|
24
25
|
disabled = false,
|
|
26
|
+
allowClickWhenDisabled = false,
|
|
25
27
|
readOnly = false,
|
|
26
28
|
maxHeight = 'auto',
|
|
27
29
|
showSelectIndicator = true,
|
|
28
30
|
maxWidth = '100%',
|
|
29
31
|
placeholder = null,
|
|
32
|
+
onFocus,
|
|
33
|
+
onOpenChange,
|
|
30
34
|
...props
|
|
31
35
|
}) => {
|
|
32
36
|
const resolvedValue = value === null ? '' : value
|
|
33
37
|
const resolvedDefaultValue =
|
|
34
38
|
defaultValue === null ? undefined : defaultValue
|
|
35
39
|
|
|
40
|
+
const isDisabled = Boolean(disabled)
|
|
41
|
+
const isDisabledClickable = isDisabled && Boolean(allowClickWhenDisabled)
|
|
42
|
+
const isRadixDisabled = (isDisabled && !allowClickWhenDisabled) || readOnly
|
|
43
|
+
|
|
44
|
+
const [open, setOpen] = useState(false)
|
|
45
|
+
const suppressCloseRef = useRef(false)
|
|
46
|
+
|
|
47
|
+
const handleOpenChange = (nextOpen) => {
|
|
48
|
+
if (!isDisabledClickable) {
|
|
49
|
+
onOpenChange?.(nextOpen)
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (nextOpen) {
|
|
54
|
+
setOpen(true)
|
|
55
|
+
onOpenChange?.(true)
|
|
56
|
+
// Keep menu open while onFocus runs (e.g. alert/paywall steals focus).
|
|
57
|
+
suppressCloseRef.current = true
|
|
58
|
+
setTimeout(() => {
|
|
59
|
+
onFocus?.()
|
|
60
|
+
setTimeout(() => {
|
|
61
|
+
suppressCloseRef.current = false
|
|
62
|
+
}, 50)
|
|
63
|
+
}, 0)
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (suppressCloseRef.current) {
|
|
68
|
+
setOpen(true)
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
setOpen(false)
|
|
73
|
+
onOpenChange?.(false)
|
|
74
|
+
}
|
|
75
|
+
|
|
36
76
|
return (
|
|
37
77
|
<Box width="100%">
|
|
38
78
|
{title && (
|
|
@@ -53,8 +93,10 @@ export const Select = ({
|
|
|
53
93
|
defaultValue={resolvedDefaultValue}
|
|
54
94
|
value={resolvedValue}
|
|
55
95
|
className={classNames(className)}
|
|
56
|
-
disabled={disabled || readOnly}
|
|
57
96
|
{...props}
|
|
97
|
+
disabled={isRadixDisabled}
|
|
98
|
+
open={isDisabledClickable ? open : props.open}
|
|
99
|
+
onOpenChange={handleOpenChange}
|
|
58
100
|
>
|
|
59
101
|
<SelectRadix.Trigger
|
|
60
102
|
variant={variant}
|
|
@@ -64,6 +106,7 @@ export const Select = ({
|
|
|
64
106
|
className={classNames(styles.selectTrigger, {
|
|
65
107
|
[styles.ghost]: variant === 'ghost',
|
|
66
108
|
[styles.soft]: variant === 'soft',
|
|
109
|
+
[styles.triggerDisabled]: isDisabledClickable,
|
|
67
110
|
})}
|
|
68
111
|
/>
|
|
69
112
|
<SelectRadix.Content
|
|
@@ -72,52 +115,86 @@ export const Select = ({
|
|
|
72
115
|
style={{ height: maxHeight }}
|
|
73
116
|
>
|
|
74
117
|
<SelectRadix.Group>
|
|
75
|
-
{items.map((item) =>
|
|
76
|
-
item.type === 'separator'
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
118
|
+
{items.map((item) => {
|
|
119
|
+
if (item.type === 'separator') {
|
|
120
|
+
return (
|
|
121
|
+
<Separator
|
|
122
|
+
key={item.value}
|
|
123
|
+
my="2"
|
|
124
|
+
style={{
|
|
125
|
+
width: '100%',
|
|
126
|
+
background: 'var(--neutral-alpha-4)',
|
|
127
|
+
}}
|
|
128
|
+
/>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const itemDisabled =
|
|
133
|
+
isDisabledClickable || Boolean(item.disabled) || readOnly
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<SelectPrimitive.Item
|
|
87
137
|
key={item.value}
|
|
88
138
|
value={item.value}
|
|
89
|
-
className={classNames(styles.selectItem, {
|
|
139
|
+
className={classNames('rt-SelectItem', styles.selectItem, {
|
|
90
140
|
[styles.hideIndicator]: !showSelectIndicator,
|
|
91
141
|
[styles.size1]: size === '1',
|
|
92
142
|
[styles.size2]: size === '2',
|
|
93
143
|
})}
|
|
94
|
-
disabled={
|
|
144
|
+
disabled={itemDisabled}
|
|
95
145
|
>
|
|
96
|
-
<
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
{item.extra && (
|
|
146
|
+
<SelectPrimitive.ItemIndicator className="rt-SelectItemIndicator">
|
|
147
|
+
<CheckIcon
|
|
148
|
+
className="rt-SelectItemIndicatorIcon"
|
|
149
|
+
width={16}
|
|
150
|
+
height={16}
|
|
151
|
+
/>
|
|
152
|
+
</SelectPrimitive.ItemIndicator>
|
|
153
|
+
<SelectPrimitive.ItemText className={styles.itemText}>
|
|
154
|
+
<span className={styles.itemLabel}>
|
|
106
155
|
<Text
|
|
107
156
|
size={size}
|
|
108
|
-
className={styles.extra}
|
|
109
157
|
style={{
|
|
110
|
-
color:
|
|
158
|
+
color: itemDisabled
|
|
159
|
+
? 'var(--neutral-alpha-9)'
|
|
160
|
+
: 'var(--neutral-12)',
|
|
111
161
|
}}
|
|
112
162
|
>
|
|
113
|
-
{
|
|
114
|
-
{item.extra}
|
|
163
|
+
{item.label}
|
|
115
164
|
</Text>
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
165
|
+
{item.aiDetected && (
|
|
166
|
+
<span className={styles.aiDetected} aria-hidden>
|
|
167
|
+
✦
|
|
168
|
+
</span>
|
|
169
|
+
)}
|
|
170
|
+
{item.extra && (
|
|
171
|
+
<Text
|
|
172
|
+
size={size}
|
|
173
|
+
className={styles.extra}
|
|
174
|
+
style={{
|
|
175
|
+
color: 'var(--neutral-alpha-9)',
|
|
176
|
+
}}
|
|
177
|
+
>
|
|
178
|
+
{' '}
|
|
179
|
+
{item.extra}
|
|
180
|
+
</Text>
|
|
181
|
+
)}
|
|
182
|
+
</span>
|
|
183
|
+
</SelectPrimitive.ItemText>
|
|
184
|
+
{item.rightIcon && (
|
|
185
|
+
<span className={styles.rightSlot}>
|
|
186
|
+
<span
|
|
187
|
+
className={classNames(styles.rightIcon, {
|
|
188
|
+
[styles.rightIconDisabled]: itemDisabled,
|
|
189
|
+
})}
|
|
190
|
+
>
|
|
191
|
+
{item.rightIcon}
|
|
192
|
+
</span>
|
|
193
|
+
</span>
|
|
194
|
+
)}
|
|
195
|
+
</SelectPrimitive.Item>
|
|
196
|
+
)
|
|
197
|
+
})}
|
|
121
198
|
</SelectRadix.Group>
|
|
122
199
|
</SelectRadix.Content>
|
|
123
200
|
</SelectRadix.Root>
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
.selectTrigger.soft {
|
|
76
76
|
background-color: var(--neutral-alpha-2) ;
|
|
77
77
|
|
|
78
|
-
&:hover:not([disabled]) {
|
|
78
|
+
&:hover:not([disabled]):not(.triggerDisabled) {
|
|
79
79
|
background-color: var(--neutral-alpha-3);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
.selectTrigger.ghost {
|
|
93
93
|
background-color: transparent;
|
|
94
94
|
|
|
95
|
-
&:hover:not([disabled]) {
|
|
95
|
+
&:hover:not([disabled]):not(.triggerDisabled) {
|
|
96
96
|
background-color: var(--neutral-alpha-2);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
@@ -102,6 +102,8 @@
|
|
|
102
102
|
|
|
103
103
|
/* List items */
|
|
104
104
|
.selectItem {
|
|
105
|
+
width: 100%;
|
|
106
|
+
padding-right: 12px;
|
|
105
107
|
&:hover {
|
|
106
108
|
background-color: var(--neutral-alpha-2);
|
|
107
109
|
}
|
|
@@ -115,19 +117,31 @@
|
|
|
115
117
|
/* Disabled styles */
|
|
116
118
|
.selectTrigger.soft {
|
|
117
119
|
color: var(--white);
|
|
118
|
-
&:disabled
|
|
120
|
+
&:disabled,
|
|
121
|
+
&.triggerDisabled {
|
|
119
122
|
color: var(--neutral-alpha-9);
|
|
120
123
|
background-color: var(--neutral-alpha-2);
|
|
121
|
-
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
122
126
|
|
|
123
127
|
.selectTrigger.ghost {
|
|
124
128
|
color: var(--white);
|
|
125
|
-
&:disabled
|
|
129
|
+
&:disabled,
|
|
130
|
+
&.triggerDisabled {
|
|
126
131
|
color: var(--neutral-alpha-9);
|
|
127
132
|
background-color: transparent;
|
|
128
133
|
}
|
|
129
134
|
}
|
|
130
135
|
|
|
136
|
+
.selectTrigger.soft.triggerDisabled[aria-expanded='true'],
|
|
137
|
+
.selectTrigger.ghost.triggerDisabled[aria-expanded='true'] {
|
|
138
|
+
background-color: var(--neutral-alpha-2);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.selectTrigger.ghost.triggerDisabled[aria-expanded='true'] {
|
|
142
|
+
background-color: transparent;
|
|
143
|
+
}
|
|
144
|
+
|
|
131
145
|
.hideIndicator :global(.rt-SelectItemIndicator) {
|
|
132
146
|
display: none;
|
|
133
147
|
}
|
|
@@ -147,8 +161,44 @@
|
|
|
147
161
|
padding: 12px 15px;
|
|
148
162
|
}
|
|
149
163
|
|
|
164
|
+
.itemText {
|
|
165
|
+
flex: 1;
|
|
166
|
+
min-width: 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
150
169
|
.itemLabel {
|
|
151
170
|
line-height: 1;
|
|
171
|
+
min-width: 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.rightSlot {
|
|
175
|
+
margin-left: auto;
|
|
176
|
+
display: flex;
|
|
177
|
+
align-items: center;
|
|
178
|
+
gap: 8px;
|
|
179
|
+
flex-shrink: 0;
|
|
180
|
+
height: 18px;
|
|
181
|
+
color: var(--neutral-alpha-11);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.rightIcon {
|
|
185
|
+
display: flex;
|
|
186
|
+
align-items: center;
|
|
187
|
+
justify-content: center;
|
|
188
|
+
color: var(--neutral-alpha-11);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.rightIcon > svg {
|
|
192
|
+
width: 14px;
|
|
193
|
+
height: 14px;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.rightIconDisabled {
|
|
197
|
+
color: var(--neutral-alpha-7);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.rightIconDisabled > svg {
|
|
201
|
+
color: var(--neutral-alpha-7);
|
|
152
202
|
}
|
|
153
203
|
|
|
154
204
|
.aiDetected {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Flex } from '@radix-ui/themes'
|
|
2
2
|
import { Select } from './Select'
|
|
3
3
|
import { useState } from 'react'
|
|
4
|
+
import { LockIcon, StarIcon } from '../../icons'
|
|
5
|
+
import { GearIcon } from '@radix-ui/react-icons'
|
|
4
6
|
|
|
5
7
|
export default {
|
|
6
8
|
title: 'Components/Select',
|
|
@@ -41,6 +43,8 @@ import { Select } from '@moises.ai/design-system'
|
|
|
41
43
|
items: {
|
|
42
44
|
value: string, // Value of the option (required)
|
|
43
45
|
label: string, // Label of the option (required)
|
|
46
|
+
disabled?: boolean, // Disables this option (still visible in the menu)
|
|
47
|
+
rightIcon?: ReactNode, // Optional icon displayed on the right side
|
|
44
48
|
aiDetected?: boolean, // Shows ✦ icon before label if true (optional)
|
|
45
49
|
extra?: string | ReactNode, // Extra text or element after label (optional)
|
|
46
50
|
}[], // List of items to display in the select
|
|
@@ -52,11 +56,13 @@ import { Select } from '@moises.ai/design-system'
|
|
|
52
56
|
size?: '1' | '2' | '3', // Size variant of the select (default: '2')
|
|
53
57
|
variant?: 'soft' | 'ghost', // Visual style variant (default: 'soft')
|
|
54
58
|
disabled?: boolean, // Whether the select is disabled (default: false)
|
|
59
|
+
allowClickWhenDisabled?: boolean, // Keeps disabled look, opens menu with disabled options, fires onFocus on open (e.g. paywall)
|
|
55
60
|
className?: string, // Additional CSS class name
|
|
56
61
|
readOnly?: boolean, // Whether the select is read-only (default: false)
|
|
57
62
|
maxHeight?: string, // Max height of the dropdown (e.g. '200px')
|
|
58
63
|
showSelectIndicator?: boolean, // Show the default Radix select indicator (default: false)
|
|
59
64
|
maxWidth?: string, // Max width of the select (e.g. '300px')
|
|
65
|
+
onFocus?: () => void, // Called when opening while disabled + allowClickWhenDisabled
|
|
60
66
|
}
|
|
61
67
|
\`\`\`
|
|
62
68
|
`,
|
|
@@ -81,6 +87,12 @@ import { Select } from '@moises.ai/design-system'
|
|
|
81
87
|
description: 'Whether the select is disabled',
|
|
82
88
|
defaultValue: false,
|
|
83
89
|
},
|
|
90
|
+
allowClickWhenDisabled: {
|
|
91
|
+
control: 'boolean',
|
|
92
|
+
description:
|
|
93
|
+
'When true with disabled, keeps disabled look, opens menu with disabled options, and fires onFocus on open',
|
|
94
|
+
defaultValue: false,
|
|
95
|
+
},
|
|
84
96
|
showSelectIndicator: {
|
|
85
97
|
control: 'boolean',
|
|
86
98
|
description: 'Whether to show the default Radix select indicator',
|
|
@@ -191,6 +203,64 @@ export const Disabled = {
|
|
|
191
203
|
decorators: [withFlexContainer],
|
|
192
204
|
}
|
|
193
205
|
|
|
206
|
+
export const AllowClickWhenDisabled = {
|
|
207
|
+
render: () => {
|
|
208
|
+
const [lastAction, setLastAction] = useState('none yet')
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<Flex direction="column" gap="2" width="300px">
|
|
212
|
+
<Select
|
|
213
|
+
title="Instrument"
|
|
214
|
+
info="Looks disabled, opens menu and fires onFocus"
|
|
215
|
+
defaultValue="bass"
|
|
216
|
+
size="2"
|
|
217
|
+
disabled
|
|
218
|
+
allowClickWhenDisabled
|
|
219
|
+
items={[
|
|
220
|
+
{ value: 'bass', label: 'Bass' },
|
|
221
|
+
{ value: 'guitar', label: 'Guitar' },
|
|
222
|
+
{ value: 'piano', label: 'Piano' },
|
|
223
|
+
]}
|
|
224
|
+
onFocus={() => {
|
|
225
|
+
setLastAction('activated (focus)')
|
|
226
|
+
alert('paywall opened')
|
|
227
|
+
}}
|
|
228
|
+
/>
|
|
229
|
+
<div style={{ fontSize: 14, color: '#4b5563' }}>
|
|
230
|
+
Last action: {lastAction}
|
|
231
|
+
</div>
|
|
232
|
+
</Flex>
|
|
233
|
+
)
|
|
234
|
+
},
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export const WithRightIcon = {
|
|
238
|
+
args: {
|
|
239
|
+
title: 'Instrument',
|
|
240
|
+
defaultValue: 'bass',
|
|
241
|
+
size: '2',
|
|
242
|
+
items: [
|
|
243
|
+
{
|
|
244
|
+
value: 'bass',
|
|
245
|
+
label: 'Bass',
|
|
246
|
+
rightIcon: <LockIcon width={14} height={14} />,
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
value: 'guitar',
|
|
250
|
+
label: 'Guitar',
|
|
251
|
+
rightIcon: <StarIcon width={14} height={14} />,
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
value: 'piano',
|
|
255
|
+
label: 'Piano',
|
|
256
|
+
rightIcon: <GearIcon width={14} height={14} />,
|
|
257
|
+
},
|
|
258
|
+
{ value: 'drums', label: 'Drums' },
|
|
259
|
+
],
|
|
260
|
+
},
|
|
261
|
+
decorators: [withFlexContainer],
|
|
262
|
+
}
|
|
263
|
+
|
|
194
264
|
export const DisabledGhost = {
|
|
195
265
|
args: {
|
|
196
266
|
title: 'Instrument',
|
|
@@ -12,6 +12,7 @@ export const TextField = ({
|
|
|
12
12
|
className,
|
|
13
13
|
disabled,
|
|
14
14
|
readOnly,
|
|
15
|
+
allowClickWhenDisabled = false,
|
|
15
16
|
size = '2',
|
|
16
17
|
Icon,
|
|
17
18
|
focusRing = false,
|
|
@@ -23,6 +24,7 @@ export const TextField = ({
|
|
|
23
24
|
aiDetected = false,
|
|
24
25
|
textAlign = 'left',
|
|
25
26
|
style,
|
|
27
|
+
onFocus,
|
|
26
28
|
...props
|
|
27
29
|
}) => {
|
|
28
30
|
// legacy Icon prop is kept for backwards compatibility.
|
|
@@ -31,6 +33,8 @@ export const TextField = ({
|
|
|
31
33
|
(Icon ? <Icon width={16} height={16} className={styles.icon} /> : null)
|
|
32
34
|
|
|
33
35
|
const chars = String(value ?? '').length
|
|
36
|
+
const isDisabled = Boolean(disabled)
|
|
37
|
+
const isDisabledClickable = isDisabled && Boolean(allowClickWhenDisabled)
|
|
34
38
|
|
|
35
39
|
return (
|
|
36
40
|
<Flex direction="column" width="100%" height="100%" gap="1">
|
|
@@ -42,39 +46,61 @@ export const TextField = ({
|
|
|
42
46
|
{info && <TooltipWithInfoIcon content={info} />}
|
|
43
47
|
</Flex>
|
|
44
48
|
)}
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
49
|
+
<div className={styles.fieldWrapper}>
|
|
50
|
+
<TextFieldRadix.Root
|
|
51
|
+
size={size}
|
|
52
|
+
value={value}
|
|
53
|
+
onChange={isDisabledClickable ? undefined : onChange}
|
|
54
|
+
placeholder={placeholder}
|
|
55
|
+
className={classNames(styles.TextField, className, {
|
|
56
|
+
[styles.Disabled]: isDisabled,
|
|
57
|
+
[styles.ReadOnly]: readOnly && !isDisabled,
|
|
58
|
+
[styles.focusRing]: focusRing || error,
|
|
59
|
+
[styles.errorText]: error,
|
|
60
|
+
[styles[`size${size}`]]: size,
|
|
61
|
+
[styles.alignCenter]: textAlign === 'center',
|
|
62
|
+
[styles.alignLeft]: textAlign === 'left',
|
|
63
|
+
})}
|
|
64
|
+
style={{
|
|
65
|
+
...(aiDetected && { '--ai-chars': chars }),
|
|
66
|
+
...style,
|
|
67
|
+
}}
|
|
68
|
+
autoFocus={autoFocus}
|
|
69
|
+
{...props}
|
|
70
|
+
onFocus={isDisabledClickable ? undefined : onFocus}
|
|
71
|
+
readOnly={readOnly}
|
|
72
|
+
disabled={isDisabled}
|
|
73
|
+
>
|
|
74
|
+
{resolvedStartSlot && (
|
|
75
|
+
<TextFieldRadix.Slot>{resolvedStartSlot}</TextFieldRadix.Slot>
|
|
76
|
+
)}
|
|
77
|
+
{aiDetected && (
|
|
78
|
+
<span className={styles.aiDetected} aria-hidden>
|
|
79
|
+
<span className={styles.aiDetectedMark}>✦</span>
|
|
80
|
+
</span>
|
|
81
|
+
)}
|
|
82
|
+
{endSlot && <div className={styles.endSlot}>{endSlot}</div>}
|
|
83
|
+
</TextFieldRadix.Root>
|
|
84
|
+
{isDisabledClickable && (
|
|
85
|
+
<button
|
|
86
|
+
type="button"
|
|
87
|
+
className={styles.disabledClickOverlay}
|
|
88
|
+
aria-label={title || placeholder || 'Activate'}
|
|
89
|
+
tabIndex={0}
|
|
90
|
+
onPointerDown={(event) => {
|
|
91
|
+
// Prevent focus so dialogs/alerts don't restore focus and re-trigger.
|
|
92
|
+
event.preventDefault()
|
|
93
|
+
onFocus?.(event)
|
|
94
|
+
}}
|
|
95
|
+
onKeyDown={(event) => {
|
|
96
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
97
|
+
event.preventDefault()
|
|
98
|
+
onFocus?.(event)
|
|
99
|
+
}
|
|
100
|
+
}}
|
|
101
|
+
/>
|
|
75
102
|
)}
|
|
76
|
-
|
|
77
|
-
</TextFieldRadix.Root>
|
|
103
|
+
</div>
|
|
78
104
|
{secondaryText && (
|
|
79
105
|
<Text
|
|
80
106
|
size="1"
|
|
@@ -77,6 +77,27 @@
|
|
|
77
77
|
cursor: default;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
.fieldWrapper {
|
|
81
|
+
position: relative;
|
|
82
|
+
width: 100%;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.disabledClickOverlay {
|
|
86
|
+
position: absolute;
|
|
87
|
+
inset: 0;
|
|
88
|
+
z-index: 1;
|
|
89
|
+
margin: 0;
|
|
90
|
+
padding: 0;
|
|
91
|
+
border: none;
|
|
92
|
+
border-radius: inherit;
|
|
93
|
+
background: transparent;
|
|
94
|
+
cursor: default;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.disabledClickOverlay:focus {
|
|
98
|
+
outline: none;
|
|
99
|
+
}
|
|
100
|
+
|
|
80
101
|
.ReadOnly {
|
|
81
102
|
background-color: var(--neutral-alpha-2) !important;
|
|
82
103
|
color: var(--neutral-12);
|
|
@@ -66,6 +66,15 @@ const MyComponent = () => (
|
|
|
66
66
|
defaultValue: { summary: 'false' },
|
|
67
67
|
},
|
|
68
68
|
},
|
|
69
|
+
allowClickWhenDisabled: {
|
|
70
|
+
control: 'boolean',
|
|
71
|
+
description:
|
|
72
|
+
'When true with disabled, keeps the disabled look and blocks typing, but still allows focus (e.g. paywall via onFocus)',
|
|
73
|
+
table: {
|
|
74
|
+
type: { summary: 'boolean' },
|
|
75
|
+
defaultValue: { summary: 'false' },
|
|
76
|
+
},
|
|
77
|
+
},
|
|
69
78
|
readOnly: {
|
|
70
79
|
control: 'boolean',
|
|
71
80
|
description: 'Whether the input is read-only',
|
|
@@ -238,6 +247,32 @@ export const Disabled = {
|
|
|
238
247
|
},
|
|
239
248
|
}
|
|
240
249
|
|
|
250
|
+
export const AllowClickWhenDisabled = {
|
|
251
|
+
render: (args) => {
|
|
252
|
+
const [lastAction, setLastAction] = React.useState('none yet')
|
|
253
|
+
|
|
254
|
+
return (
|
|
255
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
256
|
+
<TextField
|
|
257
|
+
{...args}
|
|
258
|
+
disabled
|
|
259
|
+
allowClickWhenDisabled
|
|
260
|
+
value="Looks disabled, click to activate"
|
|
261
|
+
onFocus={() => {
|
|
262
|
+
setLastAction('activated (focus)')
|
|
263
|
+
action('activated (focus)')()
|
|
264
|
+
alert('paywall opened')
|
|
265
|
+
}}
|
|
266
|
+
/>
|
|
267
|
+
<div style={{ fontSize: 14, color: '#4b5563' }}>Last action: {lastAction}</div>
|
|
268
|
+
</div>
|
|
269
|
+
)
|
|
270
|
+
},
|
|
271
|
+
args: {
|
|
272
|
+
...Default.args,
|
|
273
|
+
},
|
|
274
|
+
}
|
|
275
|
+
|
|
241
276
|
export const ReadOnly = {
|
|
242
277
|
args: {
|
|
243
278
|
...Default.args,
|