@marimo-team/frontend 0.21.2-dev96 → 0.21.2-dev98
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.html
CHANGED
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
<marimo-server-token data-token="{{ server_token }}" hidden></marimo-server-token>
|
|
67
67
|
<!-- /TODO -->
|
|
68
68
|
<title>{{ title }}</title>
|
|
69
|
-
<script type="module" crossorigin src="./assets/index-
|
|
69
|
+
<script type="module" crossorigin src="./assets/index-DgkDB3V6.js"></script>
|
|
70
70
|
<link rel="modulepreload" crossorigin href="./assets/preload-helper-BOTGA194.js">
|
|
71
71
|
<link rel="modulepreload" crossorigin href="./assets/chunk-LvLJmgfZ.js">
|
|
72
72
|
<link rel="modulepreload" crossorigin href="./assets/react-Bj1aDYRI.js">
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { AtSignIcon, GlobeIcon, LockIcon } from "lucide-react";
|
|
4
|
-
import { type JSX, useState } from "react";
|
|
4
|
+
import { type JSX, useRef, useState } from "react";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import {
|
|
7
7
|
DebouncedInput,
|
|
8
8
|
Input,
|
|
9
9
|
OnBlurredInput,
|
|
10
10
|
} from "../../components/ui/input";
|
|
11
|
+
import { RANDOM_ID_ATTR } from "../../core/dom/ui-element-constants";
|
|
11
12
|
import { cn } from "../../utils/cn";
|
|
12
13
|
import type { IPlugin, IPluginProps, Setter } from "../types";
|
|
13
14
|
import { Labeled } from "./common/labeled";
|
|
@@ -25,8 +26,12 @@ interface Data {
|
|
|
25
26
|
disabled?: boolean;
|
|
26
27
|
debounce?: boolean | number;
|
|
27
28
|
fullWidth: boolean;
|
|
29
|
+
passwordHasValue?: boolean;
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
// Matches the masked dots.
|
|
33
|
+
const MASK_PLACEHOLDER = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
34
|
+
|
|
30
35
|
export class TextInputPlugin implements IPlugin<T, Data> {
|
|
31
36
|
tagName = "marimo-text";
|
|
32
37
|
|
|
@@ -40,11 +45,20 @@ export class TextInputPlugin implements IPlugin<T, Data> {
|
|
|
40
45
|
fullWidth: z.boolean().default(false),
|
|
41
46
|
disabled: z.boolean().optional(),
|
|
42
47
|
debounce: z.optional(z.union([z.boolean(), z.number()])),
|
|
48
|
+
passwordHasValue: z.boolean().optional(),
|
|
43
49
|
});
|
|
44
50
|
|
|
45
51
|
render(props: IPluginProps<T, Data>): JSX.Element {
|
|
52
|
+
// Force remount on cell re-run so masked state resets cleanly
|
|
53
|
+
const remountKey =
|
|
54
|
+
props.data.kind === "password"
|
|
55
|
+
? props.host
|
|
56
|
+
.closest(`[${RANDOM_ID_ATTR}]`)
|
|
57
|
+
?.getAttribute(RANDOM_ID_ATTR)
|
|
58
|
+
: undefined;
|
|
46
59
|
return (
|
|
47
60
|
<TextComponent
|
|
61
|
+
key={remountKey ?? undefined}
|
|
48
62
|
{...props.data}
|
|
49
63
|
value={props.value}
|
|
50
64
|
setValue={props.setValue}
|
|
@@ -59,9 +73,33 @@ interface TextComponentProps extends Data {
|
|
|
59
73
|
}
|
|
60
74
|
|
|
61
75
|
const TextComponent = (props: TextComponentProps) => {
|
|
62
|
-
|
|
76
|
+
// Before first real keystroke: show masked placeholder, suppress setValue.
|
|
77
|
+
// After first keystroke: normal password field.
|
|
78
|
+
const initiallyMasked =
|
|
79
|
+
props.kind === "password" && props.passwordHasValue === true;
|
|
80
|
+
const [masked, setMasked] = useState(initiallyMasked);
|
|
81
|
+
const hasTyped = useRef(false);
|
|
82
|
+
|
|
83
|
+
const value = masked ? "" : props.value;
|
|
84
|
+
const placeholder = masked ? MASK_PLACEHOLDER : props.placeholder;
|
|
85
|
+
const setValue: Setter<T> = masked
|
|
86
|
+
? (v) => {
|
|
87
|
+
if (!hasTyped.current) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
setMasked(false);
|
|
91
|
+
props.setValue(v);
|
|
92
|
+
}
|
|
93
|
+
: props.setValue;
|
|
94
|
+
// Capture-phase handler sets the ref synchronously before child onChange
|
|
95
|
+
const onInputCapture = masked
|
|
96
|
+
? () => {
|
|
97
|
+
hasTyped.current = true;
|
|
98
|
+
}
|
|
99
|
+
: undefined;
|
|
63
100
|
|
|
64
|
-
const
|
|
101
|
+
const [valueOnBlur, setValueOnBlur] = useState(props.value);
|
|
102
|
+
const valueToValidate = valueOnBlur == null ? value : valueOnBlur;
|
|
65
103
|
const isValid = validate(props.kind, valueToValidate);
|
|
66
104
|
|
|
67
105
|
const icon: Record<InputType, JSX.Element | null> = {
|
|
@@ -73,80 +111,79 @@ const TextComponent = (props: TextComponentProps) => {
|
|
|
73
111
|
|
|
74
112
|
const endAdornment = props.maxLength ? (
|
|
75
113
|
<span className="text-muted-foreground text-xs font-medium">
|
|
76
|
-
{
|
|
114
|
+
{value.length}/{props.maxLength}
|
|
77
115
|
</span>
|
|
78
116
|
) : null;
|
|
79
117
|
|
|
118
|
+
const inputClassName = cn({
|
|
119
|
+
"border-destructive": !isValid,
|
|
120
|
+
"w-full": props.fullWidth,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
let input: JSX.Element;
|
|
124
|
+
|
|
80
125
|
if (props.debounce === true) {
|
|
81
|
-
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
value={props.value}
|
|
98
|
-
onValueChange={props.setValue}
|
|
99
|
-
/>
|
|
100
|
-
</Labeled>
|
|
126
|
+
input = (
|
|
127
|
+
<OnBlurredInput
|
|
128
|
+
data-testid="marimo-plugin-text-input"
|
|
129
|
+
type={props.kind}
|
|
130
|
+
icon={icon[props.kind]}
|
|
131
|
+
placeholder={placeholder}
|
|
132
|
+
maxLength={props.maxLength}
|
|
133
|
+
minLength={props.minLength}
|
|
134
|
+
required={props.minLength != null && props.minLength > 0}
|
|
135
|
+
disabled={props.disabled}
|
|
136
|
+
className={inputClassName}
|
|
137
|
+
endAdornment={endAdornment}
|
|
138
|
+
value={value}
|
|
139
|
+
onValueChange={setValue}
|
|
140
|
+
onInputCapture={onInputCapture}
|
|
141
|
+
/>
|
|
101
142
|
);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
value={props.value}
|
|
122
|
-
onValueChange={props.setValue}
|
|
123
|
-
onBlur={(event) => setValueOnBlur(event.currentTarget.value)}
|
|
124
|
-
delay={props.debounce}
|
|
125
|
-
/>
|
|
126
|
-
</Labeled>
|
|
143
|
+
} else if (typeof props.debounce === "number") {
|
|
144
|
+
input = (
|
|
145
|
+
<DebouncedInput
|
|
146
|
+
data-testid="marimo-plugin-text-input"
|
|
147
|
+
type={props.kind}
|
|
148
|
+
icon={icon[props.kind]}
|
|
149
|
+
placeholder={placeholder}
|
|
150
|
+
maxLength={props.maxLength}
|
|
151
|
+
minLength={props.minLength}
|
|
152
|
+
required={props.minLength != null && props.minLength > 0}
|
|
153
|
+
disabled={props.disabled}
|
|
154
|
+
className={inputClassName}
|
|
155
|
+
endAdornment={endAdornment}
|
|
156
|
+
value={value}
|
|
157
|
+
onValueChange={setValue}
|
|
158
|
+
onBlur={(event) => setValueOnBlur(event.currentTarget.value)}
|
|
159
|
+
delay={props.debounce}
|
|
160
|
+
onInputCapture={onInputCapture}
|
|
161
|
+
/>
|
|
127
162
|
);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return (
|
|
131
|
-
<Labeled label={props.label} fullWidth={props.fullWidth}>
|
|
163
|
+
} else {
|
|
164
|
+
input = (
|
|
132
165
|
<Input
|
|
133
166
|
data-testid="marimo-plugin-text-input"
|
|
134
167
|
type={props.kind}
|
|
135
168
|
icon={icon[props.kind]}
|
|
136
|
-
placeholder={
|
|
169
|
+
placeholder={placeholder}
|
|
137
170
|
maxLength={props.maxLength}
|
|
138
171
|
minLength={props.minLength}
|
|
139
172
|
required={props.minLength != null && props.minLength > 0}
|
|
140
173
|
disabled={props.disabled}
|
|
141
|
-
className={
|
|
142
|
-
"border-destructive": !isValid,
|
|
143
|
-
"w-full": props.fullWidth,
|
|
144
|
-
})}
|
|
174
|
+
className={inputClassName}
|
|
145
175
|
endAdornment={endAdornment}
|
|
146
|
-
value={
|
|
147
|
-
onInput={(event) =>
|
|
176
|
+
value={value}
|
|
177
|
+
onInput={(event) => setValue(event.currentTarget.value)}
|
|
148
178
|
onBlur={(event) => setValueOnBlur(event.currentTarget.value)}
|
|
179
|
+
onInputCapture={onInputCapture}
|
|
149
180
|
/>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<Labeled label={props.label} fullWidth={props.fullWidth}>
|
|
186
|
+
{input}
|
|
150
187
|
</Labeled>
|
|
151
188
|
);
|
|
152
189
|
};
|