@acusti/css-value-input 2.1.2 → 2.2.1
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/README.md +916 -17
- package/dist/CSSValueInput.d.ts +3 -3
- package/dist/CSSValueInput.js +34 -46
- package/dist/CSSValueInput.js.map +1 -1
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -5,29 +5,75 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/@acusti/css-value-input)
|
|
6
6
|
[](https://bundlejs.com/?q=%40acusti%2Fcss-value-input)
|
|
7
7
|
|
|
8
|
-
`CSSValueInput` is a React component that renders a text input
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Illustrator
|
|
8
|
+
`CSSValueInput` is a React component that renders a specialized text input
|
|
9
|
+
for CSS values with intelligent unit handling, increment/decrement
|
|
10
|
+
controls, validation, and normalization. Designed with the user experience
|
|
11
|
+
of professional design tools like Adobe Illustrator, it automatically
|
|
12
|
+
manages units, enforces constraints, and provides intuitive keyboard
|
|
13
|
+
interactions.
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
## Key Features
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
- **Smart Unit Management** - Automatically applies appropriate units based
|
|
18
|
+
on CSS value type
|
|
19
|
+
- **Arrow Key Increment/Decrement** - Use ↑/↓ keys to adjust values (Shift
|
|
20
|
+
for 10x multiplier)
|
|
21
|
+
- **Automatic Validation** - Enforces min/max bounds and CSS value type
|
|
22
|
+
constraints
|
|
23
|
+
- **Value Normalization** - Converts inputs to valid CSS values with proper
|
|
24
|
+
units
|
|
25
|
+
- **Escape to Revert** - Press Escape to restore the last valid value
|
|
26
|
+
- **Custom Validators** - Support for regex or function-based validation of
|
|
27
|
+
non-numeric values
|
|
28
|
+
- **Flexible Input Types** - Supports length, angle, time, percentage, and
|
|
29
|
+
integer CSS values
|
|
30
|
+
- **Design Tool UX** - Text selection on focus, enter to confirm, intuitive
|
|
31
|
+
interactions
|
|
17
32
|
|
|
18
|
-
##
|
|
33
|
+
## Installation
|
|
19
34
|
|
|
20
|
-
```
|
|
35
|
+
```bash
|
|
21
36
|
npm install @acusti/css-value-input
|
|
22
37
|
# or
|
|
23
38
|
yarn add @acusti/css-value-input
|
|
24
39
|
```
|
|
25
40
|
|
|
26
|
-
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import CSSValueInput from '@acusti/css-value-input';
|
|
45
|
+
import { useState } from 'react';
|
|
46
|
+
|
|
47
|
+
function StyleEditor() {
|
|
48
|
+
const [width, setWidth] = useState('100px');
|
|
49
|
+
const [rotation, setRotation] = useState('0deg');
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div>
|
|
53
|
+
<CSSValueInput
|
|
54
|
+
label="Width"
|
|
55
|
+
cssValueType="length"
|
|
56
|
+
value={width}
|
|
57
|
+
onSubmitValue={setWidth}
|
|
58
|
+
min={0}
|
|
59
|
+
max={1000}
|
|
60
|
+
/>
|
|
27
61
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
62
|
+
<CSSValueInput
|
|
63
|
+
label="Rotation"
|
|
64
|
+
cssValueType="angle"
|
|
65
|
+
value={rotation}
|
|
66
|
+
onSubmitValue={setRotation}
|
|
67
|
+
step={15}
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## API Reference
|
|
75
|
+
|
|
76
|
+
### Props
|
|
31
77
|
|
|
32
78
|
```ts
|
|
33
79
|
type Props = {
|
|
@@ -36,37 +82,890 @@ type Props = {
|
|
|
36
82
|
* the value). Defaults to true.
|
|
37
83
|
*/
|
|
38
84
|
allowEmpty?: boolean;
|
|
85
|
+
|
|
86
|
+
/** Additional CSS class name for styling */
|
|
39
87
|
className?: string;
|
|
88
|
+
|
|
89
|
+
/** Type of CSS value: 'length', 'angle', 'time', 'percent', or 'integer' */
|
|
40
90
|
cssValueType?: CSSValueType;
|
|
91
|
+
|
|
92
|
+
/** Disable the input */
|
|
41
93
|
disabled?: boolean;
|
|
94
|
+
|
|
42
95
|
/**
|
|
43
96
|
* Function that receives a value and converts it to its numerical equivalent
|
|
44
97
|
* (i.e. '12px' → 12). Defaults to parseFloat().
|
|
45
98
|
*/
|
|
46
99
|
getValueAsNumber?: (value: string | number) => number;
|
|
100
|
+
|
|
101
|
+
/** Icon element to display before the input */
|
|
47
102
|
icon?: React.ReactNode;
|
|
103
|
+
|
|
104
|
+
/** Label text displayed above the input */
|
|
48
105
|
label?: string;
|
|
106
|
+
|
|
107
|
+
/** Maximum allowed numeric value */
|
|
49
108
|
max?: number;
|
|
109
|
+
|
|
110
|
+
/** Minimum allowed numeric value */
|
|
50
111
|
min?: number;
|
|
112
|
+
|
|
113
|
+
/** HTML name attribute for forms */
|
|
51
114
|
name?: string;
|
|
115
|
+
|
|
116
|
+
/** Called when input loses focus */
|
|
52
117
|
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => unknown;
|
|
118
|
+
|
|
119
|
+
/** Called on each keystroke (before validation) */
|
|
53
120
|
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => unknown;
|
|
121
|
+
|
|
122
|
+
/** Called when input gains focus */
|
|
54
123
|
onFocus?: (event: React.FocusEvent<HTMLInputElement>) => unknown;
|
|
124
|
+
|
|
125
|
+
/** Called on key press (before built-in key handling) */
|
|
55
126
|
onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => unknown;
|
|
127
|
+
|
|
128
|
+
/** Called on key release */
|
|
56
129
|
onKeyUp?: (event: React.KeyboardEvent<HTMLInputElement>) => unknown;
|
|
130
|
+
|
|
57
131
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* the previous submitted value or original value.
|
|
132
|
+
* Called when the user submits a value (Enter key or blur after change).
|
|
133
|
+
* This is your main callback for getting the validated, normalized CSS value.
|
|
61
134
|
*/
|
|
62
135
|
onSubmitValue: (value: string) => unknown;
|
|
136
|
+
|
|
137
|
+
/** Placeholder text when input is empty */
|
|
63
138
|
placeholder?: string;
|
|
139
|
+
|
|
140
|
+
/** Step size for arrow key increments (default: 1) */
|
|
64
141
|
step?: number;
|
|
142
|
+
|
|
143
|
+
/** HTML tabindex for focus order */
|
|
65
144
|
tabIndex?: number;
|
|
145
|
+
|
|
146
|
+
/** Tooltip text */
|
|
66
147
|
title?: string;
|
|
148
|
+
|
|
149
|
+
/** Default unit to apply (auto-detected from cssValueType if not provided) */
|
|
67
150
|
unit?: string;
|
|
68
|
-
|
|
151
|
+
|
|
152
|
+
/** Custom validator for non-numeric values (RegExp or function) */
|
|
69
153
|
validator?: RegExp | ((value: string) => boolean);
|
|
154
|
+
|
|
155
|
+
/** Current value of the input */
|
|
70
156
|
value?: string;
|
|
71
157
|
};
|
|
72
158
|
```
|
|
159
|
+
|
|
160
|
+
### CSS Value Types
|
|
161
|
+
|
|
162
|
+
The component supports all CSS value types from `@acusti/css-values`:
|
|
163
|
+
|
|
164
|
+
- **`length`** - px, em, rem, %, vh, vw, etc. (default: px)
|
|
165
|
+
- **`angle`** - deg, rad, grad, turn (default: deg)
|
|
166
|
+
- **`time`** - s, ms (default: s)
|
|
167
|
+
- **`percent`** - % (default: %)
|
|
168
|
+
- **`integer`** - whole numbers only (no unit)
|
|
169
|
+
|
|
170
|
+
## Usage Examples
|
|
171
|
+
|
|
172
|
+
### Design Tool Property Panel
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
import CSSValueInput from '@acusti/css-value-input';
|
|
176
|
+
import { useState } from 'react';
|
|
177
|
+
|
|
178
|
+
function PropertyPanel({ selectedElement }) {
|
|
179
|
+
const [styles, setStyles] = useState({
|
|
180
|
+
width: '100px',
|
|
181
|
+
height: '100px',
|
|
182
|
+
borderRadius: '0px',
|
|
183
|
+
rotation: '0deg',
|
|
184
|
+
opacity: '100%',
|
|
185
|
+
animationDuration: '0.3s',
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const updateStyle = (property: string) => (value: string) => {
|
|
189
|
+
setStyles((prev) => ({ ...prev, [property]: value }));
|
|
190
|
+
// Apply to selected element
|
|
191
|
+
if (selectedElement) {
|
|
192
|
+
selectedElement.style[property] = value;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<div className="property-panel">
|
|
198
|
+
<h3>Transform</h3>
|
|
199
|
+
<div className="input-group">
|
|
200
|
+
<CSSValueInput
|
|
201
|
+
label="Width"
|
|
202
|
+
cssValueType="length"
|
|
203
|
+
value={styles.width}
|
|
204
|
+
onSubmitValue={updateStyle('width')}
|
|
205
|
+
min={0}
|
|
206
|
+
icon="📏"
|
|
207
|
+
/>
|
|
208
|
+
|
|
209
|
+
<CSSValueInput
|
|
210
|
+
label="Height"
|
|
211
|
+
cssValueType="length"
|
|
212
|
+
value={styles.height}
|
|
213
|
+
onSubmitValue={updateStyle('height')}
|
|
214
|
+
min={0}
|
|
215
|
+
icon="📐"
|
|
216
|
+
/>
|
|
217
|
+
</div>
|
|
218
|
+
|
|
219
|
+
<CSSValueInput
|
|
220
|
+
label="Border Radius"
|
|
221
|
+
cssValueType="length"
|
|
222
|
+
value={styles.borderRadius}
|
|
223
|
+
onSubmitValue={updateStyle('borderRadius')}
|
|
224
|
+
min={0}
|
|
225
|
+
step={5}
|
|
226
|
+
icon="⭕"
|
|
227
|
+
/>
|
|
228
|
+
|
|
229
|
+
<CSSValueInput
|
|
230
|
+
label="Rotation"
|
|
231
|
+
cssValueType="angle"
|
|
232
|
+
value={styles.rotation}
|
|
233
|
+
onSubmitValue={updateStyle('rotation')}
|
|
234
|
+
step={15}
|
|
235
|
+
icon="🔄"
|
|
236
|
+
/>
|
|
237
|
+
|
|
238
|
+
<h3>Appearance</h3>
|
|
239
|
+
<CSSValueInput
|
|
240
|
+
label="Opacity"
|
|
241
|
+
cssValueType="percent"
|
|
242
|
+
value={styles.opacity}
|
|
243
|
+
onSubmitValue={updateStyle('opacity')}
|
|
244
|
+
min={0}
|
|
245
|
+
max={100}
|
|
246
|
+
step={5}
|
|
247
|
+
icon="👁️"
|
|
248
|
+
/>
|
|
249
|
+
|
|
250
|
+
<CSSValueInput
|
|
251
|
+
label="Animation Duration"
|
|
252
|
+
cssValueType="time"
|
|
253
|
+
value={styles.animationDuration}
|
|
254
|
+
onSubmitValue={updateStyle('animationDuration')}
|
|
255
|
+
min={0}
|
|
256
|
+
step={0.1}
|
|
257
|
+
icon="⏱️"
|
|
258
|
+
/>
|
|
259
|
+
</div>
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Responsive Design Controls
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
import CSSValueInput from '@acusti/css-value-input';
|
|
268
|
+
import { useState } from 'react';
|
|
269
|
+
|
|
270
|
+
function ResponsiveControls() {
|
|
271
|
+
const [breakpoints, setBreakpoints] = useState({
|
|
272
|
+
mobile: '480px',
|
|
273
|
+
tablet: '768px',
|
|
274
|
+
desktop: '1024px',
|
|
275
|
+
wide: '1440px',
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const [spacing, setSpacing] = useState({
|
|
279
|
+
xs: '4px',
|
|
280
|
+
sm: '8px',
|
|
281
|
+
md: '16px',
|
|
282
|
+
lg: '24px',
|
|
283
|
+
xl: '32px',
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const updateBreakpoint = (key: string) => (value: string) => {
|
|
287
|
+
setBreakpoints((prev) => ({ ...prev, [key]: value }));
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const updateSpacing = (key: string) => (value: string) => {
|
|
291
|
+
setSpacing((prev) => ({ ...prev, [key]: value }));
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
return (
|
|
295
|
+
<div className="responsive-controls">
|
|
296
|
+
<section>
|
|
297
|
+
<h3>Breakpoints</h3>
|
|
298
|
+
{Object.entries(breakpoints).map(([key, value]) => (
|
|
299
|
+
<CSSValueInput
|
|
300
|
+
key={key}
|
|
301
|
+
label={key.charAt(0).toUpperCase() + key.slice(1)}
|
|
302
|
+
cssValueType="length"
|
|
303
|
+
value={value}
|
|
304
|
+
onSubmitValue={updateBreakpoint(key)}
|
|
305
|
+
min={200}
|
|
306
|
+
max={2560}
|
|
307
|
+
step={10}
|
|
308
|
+
unit="px"
|
|
309
|
+
/>
|
|
310
|
+
))}
|
|
311
|
+
</section>
|
|
312
|
+
|
|
313
|
+
<section>
|
|
314
|
+
<h3>Spacing Scale</h3>
|
|
315
|
+
{Object.entries(spacing).map(([key, value]) => (
|
|
316
|
+
<CSSValueInput
|
|
317
|
+
key={key}
|
|
318
|
+
label={key.toUpperCase()}
|
|
319
|
+
cssValueType="length"
|
|
320
|
+
value={value}
|
|
321
|
+
onSubmitValue={updateSpacing(key)}
|
|
322
|
+
min={0}
|
|
323
|
+
max={100}
|
|
324
|
+
step={2}
|
|
325
|
+
/>
|
|
326
|
+
))}
|
|
327
|
+
</section>
|
|
328
|
+
</div>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Animation Keyframe Editor
|
|
334
|
+
|
|
335
|
+
```tsx
|
|
336
|
+
import CSSValueInput from '@acusti/css-value-input';
|
|
337
|
+
import { useState } from 'react';
|
|
338
|
+
|
|
339
|
+
function KeyframeEditor() {
|
|
340
|
+
const [keyframes, setKeyframes] = useState([
|
|
341
|
+
{
|
|
342
|
+
offset: '0%',
|
|
343
|
+
transform: 'translateX(0px) rotate(0deg)',
|
|
344
|
+
opacity: '100%',
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
offset: '50%',
|
|
348
|
+
transform: 'translateX(100px) rotate(180deg)',
|
|
349
|
+
opacity: '50%',
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
offset: '100%',
|
|
353
|
+
transform: 'translateX(0px) rotate(360deg)',
|
|
354
|
+
opacity: '100%',
|
|
355
|
+
},
|
|
356
|
+
]);
|
|
357
|
+
|
|
358
|
+
const [animationSettings, setAnimationSettings] = useState({
|
|
359
|
+
duration: '2s',
|
|
360
|
+
delay: '0s',
|
|
361
|
+
timingFunction: 'ease-in-out',
|
|
362
|
+
iterations: '1',
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
const updateKeyframe = (
|
|
366
|
+
index: number,
|
|
367
|
+
property: string,
|
|
368
|
+
value: string,
|
|
369
|
+
) => {
|
|
370
|
+
setKeyframes((prev) =>
|
|
371
|
+
prev.map((kf, i) =>
|
|
372
|
+
i === index ? { ...kf, [property]: value } : kf,
|
|
373
|
+
),
|
|
374
|
+
);
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
return (
|
|
378
|
+
<div className="keyframe-editor">
|
|
379
|
+
<h3>Animation Settings</h3>
|
|
380
|
+
<div className="animation-controls">
|
|
381
|
+
<CSSValueInput
|
|
382
|
+
label="Duration"
|
|
383
|
+
cssValueType="time"
|
|
384
|
+
value={animationSettings.duration}
|
|
385
|
+
onSubmitValue={(value) =>
|
|
386
|
+
setAnimationSettings((prev) => ({
|
|
387
|
+
...prev,
|
|
388
|
+
duration: value,
|
|
389
|
+
}))
|
|
390
|
+
}
|
|
391
|
+
min={0}
|
|
392
|
+
step={0.1}
|
|
393
|
+
/>
|
|
394
|
+
|
|
395
|
+
<CSSValueInput
|
|
396
|
+
label="Delay"
|
|
397
|
+
cssValueType="time"
|
|
398
|
+
value={animationSettings.delay}
|
|
399
|
+
onSubmitValue={(value) =>
|
|
400
|
+
setAnimationSettings((prev) => ({
|
|
401
|
+
...prev,
|
|
402
|
+
delay: value,
|
|
403
|
+
}))
|
|
404
|
+
}
|
|
405
|
+
min={0}
|
|
406
|
+
step={0.1}
|
|
407
|
+
/>
|
|
408
|
+
|
|
409
|
+
<CSSValueInput
|
|
410
|
+
label="Iterations"
|
|
411
|
+
cssValueType="integer"
|
|
412
|
+
value={animationSettings.iterations}
|
|
413
|
+
onSubmitValue={(value) =>
|
|
414
|
+
setAnimationSettings((prev) => ({
|
|
415
|
+
...prev,
|
|
416
|
+
iterations: value,
|
|
417
|
+
}))
|
|
418
|
+
}
|
|
419
|
+
min={1}
|
|
420
|
+
validator={(value) =>
|
|
421
|
+
value === 'infinite' || !isNaN(Number(value))
|
|
422
|
+
}
|
|
423
|
+
/>
|
|
424
|
+
</div>
|
|
425
|
+
|
|
426
|
+
<h3>Keyframes</h3>
|
|
427
|
+
{keyframes.map((keyframe, index) => (
|
|
428
|
+
<div key={index} className="keyframe">
|
|
429
|
+
<h4>Keyframe {index + 1}</h4>
|
|
430
|
+
<div className="keyframe-controls">
|
|
431
|
+
<CSSValueInput
|
|
432
|
+
label="Offset"
|
|
433
|
+
cssValueType="percent"
|
|
434
|
+
value={keyframe.offset}
|
|
435
|
+
onSubmitValue={(value) =>
|
|
436
|
+
updateKeyframe(index, 'offset', value)
|
|
437
|
+
}
|
|
438
|
+
min={0}
|
|
439
|
+
max={100}
|
|
440
|
+
step={5}
|
|
441
|
+
/>
|
|
442
|
+
|
|
443
|
+
<CSSValueInput
|
|
444
|
+
label="Opacity"
|
|
445
|
+
cssValueType="percent"
|
|
446
|
+
value={keyframe.opacity}
|
|
447
|
+
onSubmitValue={(value) =>
|
|
448
|
+
updateKeyframe(index, 'opacity', value)
|
|
449
|
+
}
|
|
450
|
+
min={0}
|
|
451
|
+
max={100}
|
|
452
|
+
step={10}
|
|
453
|
+
/>
|
|
454
|
+
</div>
|
|
455
|
+
</div>
|
|
456
|
+
))}
|
|
457
|
+
</div>
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### CSS Grid Layout Builder
|
|
463
|
+
|
|
464
|
+
```tsx
|
|
465
|
+
import CSSValueInput from '@acusti/css-value-input';
|
|
466
|
+
import { useState } from 'react';
|
|
467
|
+
|
|
468
|
+
function GridLayoutBuilder() {
|
|
469
|
+
const [gridSettings, setGridSettings] = useState({
|
|
470
|
+
columns: '1fr 1fr 1fr',
|
|
471
|
+
rows: 'auto auto',
|
|
472
|
+
columnGap: '16px',
|
|
473
|
+
rowGap: '16px',
|
|
474
|
+
padding: '20px',
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
const [itemSettings, setItemSettings] = useState({
|
|
478
|
+
columnStart: '1',
|
|
479
|
+
columnEnd: '2',
|
|
480
|
+
rowStart: '1',
|
|
481
|
+
rowEnd: '2',
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
return (
|
|
485
|
+
<div className="grid-builder">
|
|
486
|
+
<h3>Grid Container</h3>
|
|
487
|
+
<div className="grid-controls">
|
|
488
|
+
<CSSValueInput
|
|
489
|
+
label="Column Gap"
|
|
490
|
+
cssValueType="length"
|
|
491
|
+
value={gridSettings.columnGap}
|
|
492
|
+
onSubmitValue={(value) =>
|
|
493
|
+
setGridSettings((prev) => ({
|
|
494
|
+
...prev,
|
|
495
|
+
columnGap: value,
|
|
496
|
+
}))
|
|
497
|
+
}
|
|
498
|
+
min={0}
|
|
499
|
+
step={4}
|
|
500
|
+
/>
|
|
501
|
+
|
|
502
|
+
<CSSValueInput
|
|
503
|
+
label="Row Gap"
|
|
504
|
+
cssValueType="length"
|
|
505
|
+
value={gridSettings.rowGap}
|
|
506
|
+
onSubmitValue={(value) =>
|
|
507
|
+
setGridSettings((prev) => ({
|
|
508
|
+
...prev,
|
|
509
|
+
rowGap: value,
|
|
510
|
+
}))
|
|
511
|
+
}
|
|
512
|
+
min={0}
|
|
513
|
+
step={4}
|
|
514
|
+
/>
|
|
515
|
+
|
|
516
|
+
<CSSValueInput
|
|
517
|
+
label="Padding"
|
|
518
|
+
cssValueType="length"
|
|
519
|
+
value={gridSettings.padding}
|
|
520
|
+
onSubmitValue={(value) =>
|
|
521
|
+
setGridSettings((prev) => ({
|
|
522
|
+
...prev,
|
|
523
|
+
padding: value,
|
|
524
|
+
}))
|
|
525
|
+
}
|
|
526
|
+
min={0}
|
|
527
|
+
step={4}
|
|
528
|
+
/>
|
|
529
|
+
</div>
|
|
530
|
+
|
|
531
|
+
<h3>Grid Item Position</h3>
|
|
532
|
+
<div className="item-controls">
|
|
533
|
+
<CSSValueInput
|
|
534
|
+
label="Column Start"
|
|
535
|
+
cssValueType="integer"
|
|
536
|
+
value={itemSettings.columnStart}
|
|
537
|
+
onSubmitValue={(value) =>
|
|
538
|
+
setItemSettings((prev) => ({
|
|
539
|
+
...prev,
|
|
540
|
+
columnStart: value,
|
|
541
|
+
}))
|
|
542
|
+
}
|
|
543
|
+
min={1}
|
|
544
|
+
/>
|
|
545
|
+
|
|
546
|
+
<CSSValueInput
|
|
547
|
+
label="Column End"
|
|
548
|
+
cssValueType="integer"
|
|
549
|
+
value={itemSettings.columnEnd}
|
|
550
|
+
onSubmitValue={(value) =>
|
|
551
|
+
setItemSettings((prev) => ({
|
|
552
|
+
...prev,
|
|
553
|
+
columnEnd: value,
|
|
554
|
+
}))
|
|
555
|
+
}
|
|
556
|
+
min={1}
|
|
557
|
+
/>
|
|
558
|
+
|
|
559
|
+
<CSSValueInput
|
|
560
|
+
label="Row Start"
|
|
561
|
+
cssValueType="integer"
|
|
562
|
+
value={itemSettings.rowStart}
|
|
563
|
+
onSubmitValue={(value) =>
|
|
564
|
+
setItemSettings((prev) => ({
|
|
565
|
+
...prev,
|
|
566
|
+
rowStart: value,
|
|
567
|
+
}))
|
|
568
|
+
}
|
|
569
|
+
min={1}
|
|
570
|
+
/>
|
|
571
|
+
|
|
572
|
+
<CSSValueInput
|
|
573
|
+
label="Row End"
|
|
574
|
+
cssValueType="integer"
|
|
575
|
+
value={itemSettings.rowEnd}
|
|
576
|
+
onSubmitValue={(value) =>
|
|
577
|
+
setItemSettings((prev) => ({
|
|
578
|
+
...prev,
|
|
579
|
+
rowEnd: value,
|
|
580
|
+
}))
|
|
581
|
+
}
|
|
582
|
+
min={1}
|
|
583
|
+
/>
|
|
584
|
+
</div>
|
|
585
|
+
|
|
586
|
+
<div className="preview">
|
|
587
|
+
<div
|
|
588
|
+
style={{
|
|
589
|
+
display: 'grid',
|
|
590
|
+
gridTemplateColumns: gridSettings.columns,
|
|
591
|
+
gridTemplateRows: gridSettings.rows,
|
|
592
|
+
columnGap: gridSettings.columnGap,
|
|
593
|
+
rowGap: gridSettings.rowGap,
|
|
594
|
+
padding: gridSettings.padding,
|
|
595
|
+
border: '1px dashed #ccc',
|
|
596
|
+
minHeight: '200px',
|
|
597
|
+
}}
|
|
598
|
+
>
|
|
599
|
+
<div
|
|
600
|
+
style={{
|
|
601
|
+
gridColumnStart: itemSettings.columnStart,
|
|
602
|
+
gridColumnEnd: itemSettings.columnEnd,
|
|
603
|
+
gridRowStart: itemSettings.rowStart,
|
|
604
|
+
gridRowEnd: itemSettings.rowEnd,
|
|
605
|
+
backgroundColor: '#e3f2fd',
|
|
606
|
+
padding: '8px',
|
|
607
|
+
border: '1px solid #2196f3',
|
|
608
|
+
}}
|
|
609
|
+
>
|
|
610
|
+
Grid Item
|
|
611
|
+
</div>
|
|
612
|
+
</div>
|
|
613
|
+
</div>
|
|
614
|
+
</div>
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
### Typography Controls
|
|
620
|
+
|
|
621
|
+
```tsx
|
|
622
|
+
import CSSValueInput from '@acusti/css-value-input';
|
|
623
|
+
import { useState } from 'react';
|
|
624
|
+
|
|
625
|
+
function TypographyControls() {
|
|
626
|
+
const [typography, setTypography] = useState({
|
|
627
|
+
fontSize: '16px',
|
|
628
|
+
lineHeight: '1.5',
|
|
629
|
+
letterSpacing: '0px',
|
|
630
|
+
wordSpacing: '0px',
|
|
631
|
+
textIndent: '0px',
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
const updateTypography = (property: string) => (value: string) => {
|
|
635
|
+
setTypography((prev) => ({ ...prev, [property]: value }));
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
return (
|
|
639
|
+
<div className="typography-controls">
|
|
640
|
+
<h3>Typography</h3>
|
|
641
|
+
|
|
642
|
+
<CSSValueInput
|
|
643
|
+
label="Font Size"
|
|
644
|
+
cssValueType="length"
|
|
645
|
+
value={typography.fontSize}
|
|
646
|
+
onSubmitValue={updateTypography('fontSize')}
|
|
647
|
+
min={8}
|
|
648
|
+
max={72}
|
|
649
|
+
step={1}
|
|
650
|
+
icon="🔤"
|
|
651
|
+
/>
|
|
652
|
+
|
|
653
|
+
<CSSValueInput
|
|
654
|
+
label="Line Height"
|
|
655
|
+
cssValueType="length"
|
|
656
|
+
value={typography.lineHeight}
|
|
657
|
+
onSubmitValue={updateTypography('lineHeight')}
|
|
658
|
+
min={0.5}
|
|
659
|
+
max={3}
|
|
660
|
+
step={0.1}
|
|
661
|
+
unit="" // Line height can be unitless
|
|
662
|
+
validator={(value) => {
|
|
663
|
+
// Allow unitless numbers or length values
|
|
664
|
+
return /^(\d*\.?\d+)(px|em|rem|%)?$/.test(value);
|
|
665
|
+
}}
|
|
666
|
+
icon="📏"
|
|
667
|
+
/>
|
|
668
|
+
|
|
669
|
+
<CSSValueInput
|
|
670
|
+
label="Letter Spacing"
|
|
671
|
+
cssValueType="length"
|
|
672
|
+
value={typography.letterSpacing}
|
|
673
|
+
onSubmitValue={updateTypography('letterSpacing')}
|
|
674
|
+
min={-5}
|
|
675
|
+
max={10}
|
|
676
|
+
step={0.5}
|
|
677
|
+
icon="🔤"
|
|
678
|
+
/>
|
|
679
|
+
|
|
680
|
+
<CSSValueInput
|
|
681
|
+
label="Word Spacing"
|
|
682
|
+
cssValueType="length"
|
|
683
|
+
value={typography.wordSpacing}
|
|
684
|
+
onSubmitValue={updateTypography('wordSpacing')}
|
|
685
|
+
min={-10}
|
|
686
|
+
max={20}
|
|
687
|
+
step={1}
|
|
688
|
+
icon="📝"
|
|
689
|
+
/>
|
|
690
|
+
|
|
691
|
+
<CSSValueInput
|
|
692
|
+
label="Text Indent"
|
|
693
|
+
cssValueType="length"
|
|
694
|
+
value={typography.textIndent}
|
|
695
|
+
onSubmitValue={updateTypography('textIndent')}
|
|
696
|
+
min={0}
|
|
697
|
+
max={100}
|
|
698
|
+
step={5}
|
|
699
|
+
icon="⬅️"
|
|
700
|
+
/>
|
|
701
|
+
|
|
702
|
+
<div className="preview-text" style={typography}>
|
|
703
|
+
<p>
|
|
704
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing
|
|
705
|
+
elit. Sed do eiusmod tempor incididunt ut labore et
|
|
706
|
+
dolore magna aliqua. Ut enim ad minim veniam, quis
|
|
707
|
+
nostrud exercitation.
|
|
708
|
+
</p>
|
|
709
|
+
</div>
|
|
710
|
+
</div>
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
### Custom Validator Examples
|
|
716
|
+
|
|
717
|
+
```tsx
|
|
718
|
+
import CSSValueInput from '@acusti/css-value-input';
|
|
719
|
+
|
|
720
|
+
function CustomValidators() {
|
|
721
|
+
// CSS function validator (e.g., calc(), var(), etc.)
|
|
722
|
+
const cssFunctionValidator = (value: string) => {
|
|
723
|
+
return (
|
|
724
|
+
/^(calc|var|min|max|clamp)\(.*\)$/.test(value) ||
|
|
725
|
+
!isNaN(parseFloat(value))
|
|
726
|
+
);
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
// Color hex validator
|
|
730
|
+
const hexColorValidator = /^#([0-9A-Fa-f]{3}){1,2}$/;
|
|
731
|
+
|
|
732
|
+
// CSS keyword validator for display property
|
|
733
|
+
const displayKeywordValidator = (value: string) => {
|
|
734
|
+
const validKeywords = [
|
|
735
|
+
'block',
|
|
736
|
+
'inline',
|
|
737
|
+
'flex',
|
|
738
|
+
'grid',
|
|
739
|
+
'none',
|
|
740
|
+
'inline-block',
|
|
741
|
+
];
|
|
742
|
+
return validKeywords.includes(value) || !isNaN(parseFloat(value));
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
return (
|
|
746
|
+
<div>
|
|
747
|
+
<CSSValueInput
|
|
748
|
+
label="Width (supports calc)"
|
|
749
|
+
cssValueType="length"
|
|
750
|
+
onSubmitValue={(value) => console.log('Width:', value)}
|
|
751
|
+
validator={cssFunctionValidator}
|
|
752
|
+
placeholder="100px or calc(50% - 10px)"
|
|
753
|
+
/>
|
|
754
|
+
|
|
755
|
+
<CSSValueInput
|
|
756
|
+
label="Border Color"
|
|
757
|
+
cssValueType="length" // We'll override the unit behavior
|
|
758
|
+
onSubmitValue={(value) => console.log('Color:', value)}
|
|
759
|
+
validator={hexColorValidator}
|
|
760
|
+
unit="" // No default unit
|
|
761
|
+
placeholder="#ff0000"
|
|
762
|
+
/>
|
|
763
|
+
|
|
764
|
+
<CSSValueInput
|
|
765
|
+
label="Z-Index"
|
|
766
|
+
cssValueType="integer"
|
|
767
|
+
onSubmitValue={(value) => console.log('Z-Index:', value)}
|
|
768
|
+
min={-999}
|
|
769
|
+
max={999}
|
|
770
|
+
step={1}
|
|
771
|
+
validator={(value) =>
|
|
772
|
+
value === 'auto' || !isNaN(parseInt(value))
|
|
773
|
+
}
|
|
774
|
+
/>
|
|
775
|
+
</div>
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
## Keyboard Interactions
|
|
781
|
+
|
|
782
|
+
### Arrow Keys
|
|
783
|
+
|
|
784
|
+
- **↑/↓** - Increment/decrement by step amount
|
|
785
|
+
- **Shift + ↑/↓** - Increment/decrement by step × 10
|
|
786
|
+
- Works with all numeric CSS value types
|
|
787
|
+
|
|
788
|
+
### Special Keys
|
|
789
|
+
|
|
790
|
+
- **Enter** - Submit value and blur input
|
|
791
|
+
- **Escape** - Revert to last submitted value and blur
|
|
792
|
+
- **Tab** - Submit value and move to next input
|
|
793
|
+
|
|
794
|
+
### Value Handling
|
|
795
|
+
|
|
796
|
+
- **Auto-complete units** - Typing "100" becomes "100px" for length inputs
|
|
797
|
+
- **Unit preservation** - Keeps the unit from the previous value when
|
|
798
|
+
possible
|
|
799
|
+
- **Range enforcement** - Automatically clamps values to min/max bounds
|
|
800
|
+
- **Type coercion** - Converts integers when cssValueType="integer"
|
|
801
|
+
|
|
802
|
+
## Styling
|
|
803
|
+
|
|
804
|
+
The component uses CSS classes with the prefix `cssvalueinput`:
|
|
805
|
+
|
|
806
|
+
```css
|
|
807
|
+
.cssvalueinput {
|
|
808
|
+
/* Main container styles */
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
.cssvalueinput-icon {
|
|
812
|
+
/* Icon container styles */
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
.cssvalueinput-label {
|
|
816
|
+
/* Label container styles */
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
.cssvalueinput-label-text {
|
|
820
|
+
/* Label text styles */
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
.cssvalueinput-value {
|
|
824
|
+
/* Input wrapper styles */
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
.cssvalueinput.disabled {
|
|
828
|
+
/* Disabled state styles */
|
|
829
|
+
}
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
### Example Styling
|
|
833
|
+
|
|
834
|
+
```css
|
|
835
|
+
.cssvalueinput {
|
|
836
|
+
display: flex;
|
|
837
|
+
flex-direction: column;
|
|
838
|
+
gap: 4px;
|
|
839
|
+
margin-bottom: 12px;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
.cssvalueinput-label-text {
|
|
843
|
+
font-size: 12px;
|
|
844
|
+
font-weight: 600;
|
|
845
|
+
color: #333;
|
|
846
|
+
margin: 0;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
.cssvalueinput-icon {
|
|
850
|
+
font-size: 16px;
|
|
851
|
+
margin-right: 8px;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
.cssvalueinput input {
|
|
855
|
+
padding: 6px 8px;
|
|
856
|
+
border: 1px solid #ccc;
|
|
857
|
+
border-radius: 4px;
|
|
858
|
+
font-family: monospace;
|
|
859
|
+
text-align: center;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
.cssvalueinput input:focus {
|
|
863
|
+
outline: 2px solid #007bff;
|
|
864
|
+
border-color: transparent;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
.cssvalueinput.disabled {
|
|
868
|
+
opacity: 0.6;
|
|
869
|
+
pointer-events: none;
|
|
870
|
+
}
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
## Integration with CSS-in-JS
|
|
874
|
+
|
|
875
|
+
```tsx
|
|
876
|
+
import CSSValueInput from '@acusti/css-value-input';
|
|
877
|
+
import styled from 'styled-components';
|
|
878
|
+
|
|
879
|
+
const StyledBox = styled.div<{
|
|
880
|
+
width: string;
|
|
881
|
+
height: string;
|
|
882
|
+
rotation: string;
|
|
883
|
+
}>`
|
|
884
|
+
width: ${(props) => props.width};
|
|
885
|
+
height: ${(props) => props.height};
|
|
886
|
+
transform: rotate(${(props) => props.rotation});
|
|
887
|
+
background: linear-gradient(45deg, #007bff, #28a745);
|
|
888
|
+
transition: all 0.3s ease;
|
|
889
|
+
`;
|
|
890
|
+
|
|
891
|
+
function StyledComponentEditor() {
|
|
892
|
+
const [boxStyles, setBoxStyles] = useState({
|
|
893
|
+
width: '200px',
|
|
894
|
+
height: '200px',
|
|
895
|
+
rotation: '0deg',
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
return (
|
|
899
|
+
<div>
|
|
900
|
+
<div className="controls">
|
|
901
|
+
<CSSValueInput
|
|
902
|
+
label="Width"
|
|
903
|
+
cssValueType="length"
|
|
904
|
+
value={boxStyles.width}
|
|
905
|
+
onSubmitValue={(value) =>
|
|
906
|
+
setBoxStyles((prev) => ({ ...prev, width: value }))
|
|
907
|
+
}
|
|
908
|
+
/>
|
|
909
|
+
|
|
910
|
+
<CSSValueInput
|
|
911
|
+
label="Height"
|
|
912
|
+
cssValueType="length"
|
|
913
|
+
value={boxStyles.height}
|
|
914
|
+
onSubmitValue={(value) =>
|
|
915
|
+
setBoxStyles((prev) => ({
|
|
916
|
+
...prev,
|
|
917
|
+
height: value,
|
|
918
|
+
}))
|
|
919
|
+
}
|
|
920
|
+
/>
|
|
921
|
+
|
|
922
|
+
<CSSValueInput
|
|
923
|
+
label="Rotation"
|
|
924
|
+
cssValueType="angle"
|
|
925
|
+
value={boxStyles.rotation}
|
|
926
|
+
onSubmitValue={(value) =>
|
|
927
|
+
setBoxStyles((prev) => ({
|
|
928
|
+
...prev,
|
|
929
|
+
rotation: value,
|
|
930
|
+
}))
|
|
931
|
+
}
|
|
932
|
+
step={15}
|
|
933
|
+
/>
|
|
934
|
+
</div>
|
|
935
|
+
|
|
936
|
+
<StyledBox {...boxStyles}>Styled Component</StyledBox>
|
|
937
|
+
</div>
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
## Accessibility
|
|
943
|
+
|
|
944
|
+
- **Label Association** - Proper label/input relationships for screen
|
|
945
|
+
readers
|
|
946
|
+
- **Keyboard Navigation** - Full keyboard control without mouse dependency
|
|
947
|
+
- **Focus Management** - Clear focus indicators and logical tab order
|
|
948
|
+
- **Value Announcements** - Screen readers announce value changes
|
|
949
|
+
- **Error Handling** - Invalid values are reverted with visual feedback
|
|
950
|
+
|
|
951
|
+
## Browser Compatibility
|
|
952
|
+
|
|
953
|
+
- **Modern Browsers** - Chrome, Firefox, Safari, Edge (latest)
|
|
954
|
+
- **Mobile Support** - Touch-friendly with virtual keyboard support
|
|
955
|
+
- **SSR Compatible** - Works with Next.js, React Router, etc.
|
|
956
|
+
|
|
957
|
+
## Common Use Cases
|
|
958
|
+
|
|
959
|
+
- **Design Tools** - Property panels, style editors, layout builders
|
|
960
|
+
- **CSS Generators** - Live CSS property editors
|
|
961
|
+
- **Animation Tools** - Keyframe editors, timing controls
|
|
962
|
+
- **Theme Builders** - Design system value editors
|
|
963
|
+
- **Form Builders** - CSS-aware form inputs
|
|
964
|
+
- **Component Libraries** - Styleable component property editors
|
|
965
|
+
|
|
966
|
+
## Demo
|
|
967
|
+
|
|
968
|
+
See the
|
|
969
|
+
[Storybook documentation and examples](https://uikit.acusti.ca/?path=/docs/uikit-controls-CSSValueInput--docs)
|
|
970
|
+
for interactive demonstrations of all CSS value input features and
|
|
971
|
+
configurations.
|
package/dist/CSSValueInput.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CSSValueType } from '../../css-values/src';
|
|
2
|
-
import { ChangeEvent, FocusEvent, KeyboardEvent, ReactNode } from 'react';
|
|
2
|
+
import { ChangeEvent, FocusEvent, KeyboardEvent, ReactNode, Ref } from 'react';
|
|
3
3
|
export type Props = {
|
|
4
4
|
/**
|
|
5
5
|
* Boolean indicating if the user can submit an empty value (i.e. clear
|
|
@@ -31,6 +31,7 @@ export type Props = {
|
|
|
31
31
|
*/
|
|
32
32
|
onSubmitValue: (value: string) => unknown;
|
|
33
33
|
placeholder?: string;
|
|
34
|
+
ref?: Ref<HTMLInputElement>;
|
|
34
35
|
step?: number;
|
|
35
36
|
tabIndex?: number;
|
|
36
37
|
title?: string;
|
|
@@ -39,5 +40,4 @@ export type Props = {
|
|
|
39
40
|
validator?: ((value: string) => boolean) | RegExp;
|
|
40
41
|
value?: string;
|
|
41
42
|
};
|
|
42
|
-
|
|
43
|
-
export default _default;
|
|
43
|
+
export default function CSSValueInput({ allowEmpty, className, cssValueType, disabled, getValueAsNumber, icon, label, max, min, name, onBlur, onChange, onFocus, onKeyDown, onKeyUp, onSubmitValue, placeholder, ref, step, tabIndex, title, unit, validator, value: valueFromProps, }: Props): import("react/jsx-runtime").JSX.Element;
|
package/dist/CSSValueInput.js
CHANGED
|
@@ -3,9 +3,9 @@ import { c } from "react/compiler-runtime";
|
|
|
3
3
|
import { DEFAULT_UNIT_BY_CSS_VALUE_TYPE, DEFAULT_CSS_VALUE_TYPE, getCSSValueAsNumber, getUnitFromCSSValue, getCSSValueWithUnit, roundToPrecision } from "@acusti/css-values";
|
|
4
4
|
import InputText from "@acusti/input-text";
|
|
5
5
|
import clsx from "clsx";
|
|
6
|
-
import {
|
|
6
|
+
import { useRef, useImperativeHandle, useEffect } from "react";
|
|
7
7
|
const ROOT_CLASS_NAME = "cssvalueinput";
|
|
8
|
-
|
|
8
|
+
function CSSValueInput(t0) {
|
|
9
9
|
const $ = c(56);
|
|
10
10
|
const {
|
|
11
11
|
allowEmpty: t1,
|
|
@@ -25,6 +25,7 @@ const CSSValueInput = forwardRef(function CSSValueInput2(t0, ref) {
|
|
|
25
25
|
onKeyUp,
|
|
26
26
|
onSubmitValue,
|
|
27
27
|
placeholder,
|
|
28
|
+
ref,
|
|
28
29
|
step: t4,
|
|
29
30
|
tabIndex,
|
|
30
31
|
title,
|
|
@@ -46,7 +47,7 @@ const CSSValueInput = forwardRef(function CSSValueInput2(t0, ref) {
|
|
|
46
47
|
t6 = $[0];
|
|
47
48
|
}
|
|
48
49
|
useImperativeHandle(ref, t6);
|
|
49
|
-
const value = typeof valueFromProps === "number" &&
|
|
50
|
+
const value = typeof valueFromProps === "number" && Number.isFinite(valueFromProps) ? `${valueFromProps}` : valueFromProps;
|
|
50
51
|
const submittedValueRef = useRef(value ?? "");
|
|
51
52
|
let t7;
|
|
52
53
|
let t8;
|
|
@@ -66,9 +67,8 @@ const CSSValueInput = forwardRef(function CSSValueInput2(t0, ref) {
|
|
|
66
67
|
let t9;
|
|
67
68
|
if ($[4] !== onSubmitValue) {
|
|
68
69
|
t9 = (event) => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
onSubmitValue(currentValue);
|
|
70
|
+
submittedValueRef.current = event.currentTarget.value;
|
|
71
|
+
onSubmitValue(event.currentTarget.value);
|
|
72
72
|
};
|
|
73
73
|
$[4] = onSubmitValue;
|
|
74
74
|
$[5] = t9;
|
|
@@ -84,12 +84,12 @@ const CSSValueInput = forwardRef(function CSSValueInput2(t0, ref) {
|
|
|
84
84
|
if (onBlur) {
|
|
85
85
|
onBlur(event_0);
|
|
86
86
|
}
|
|
87
|
-
const
|
|
88
|
-
if (allowEmpty && !
|
|
87
|
+
const currentValue = input.value.trim();
|
|
88
|
+
if (allowEmpty && !currentValue) {
|
|
89
89
|
handleSubmitValue(event_0);
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
|
-
const currentValueAsNumber = getValueAsNumber(
|
|
92
|
+
const currentValueAsNumber = getValueAsNumber(currentValue);
|
|
93
93
|
const isCurrentValueFinite = Number.isFinite(currentValueAsNumber);
|
|
94
94
|
const defaultUnit = unit ? getUnitFromCSSValue({
|
|
95
95
|
cssValueType,
|
|
@@ -99,10 +99,10 @@ const CSSValueInput = forwardRef(function CSSValueInput2(t0, ref) {
|
|
|
99
99
|
if (!isCurrentValueFinite) {
|
|
100
100
|
let isValid = false;
|
|
101
101
|
if (validator instanceof RegExp) {
|
|
102
|
-
isValid = validator.test(
|
|
102
|
+
isValid = validator.test(currentValue);
|
|
103
103
|
} else {
|
|
104
104
|
if (validator) {
|
|
105
|
-
isValid = validator(
|
|
105
|
+
isValid = validator(currentValue);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
if (isValid) {
|
|
@@ -130,14 +130,14 @@ const CSSValueInput = forwardRef(function CSSValueInput2(t0, ref) {
|
|
|
130
130
|
const currentUnit = getUnitFromCSSValue({
|
|
131
131
|
cssValueType,
|
|
132
132
|
defaultUnit,
|
|
133
|
-
value:
|
|
133
|
+
value: currentValue
|
|
134
134
|
});
|
|
135
135
|
input.value = normalizedValueAsNumber + currentUnit;
|
|
136
136
|
} else {
|
|
137
137
|
input.value = getCSSValueWithUnit({
|
|
138
138
|
cssValueType,
|
|
139
139
|
defaultUnit,
|
|
140
|
-
value:
|
|
140
|
+
value: currentValue
|
|
141
141
|
});
|
|
142
142
|
}
|
|
143
143
|
handleSubmitValue(event_0);
|
|
@@ -160,16 +160,16 @@ const CSSValueInput = forwardRef(function CSSValueInput2(t0, ref) {
|
|
|
160
160
|
if ($[16] !== cssValueType || $[17] !== getValueAsNumber || $[18] !== max || $[19] !== min || $[20] !== step || $[21] !== unit) {
|
|
161
161
|
t11 = (t122) => {
|
|
162
162
|
const {
|
|
163
|
-
currentValue:
|
|
163
|
+
currentValue: currentValue_0,
|
|
164
164
|
multiplier: t132,
|
|
165
165
|
signum: t142
|
|
166
166
|
} = t122;
|
|
167
167
|
const multiplier = t132 === void 0 ? 1 : t132;
|
|
168
168
|
const signum = t142 === void 0 ? 1 : t142;
|
|
169
169
|
const modifier = multiplier * step * signum;
|
|
170
|
-
const currentValueAsNumber_0 = getValueAsNumber(
|
|
171
|
-
if (typeof
|
|
172
|
-
return
|
|
170
|
+
const currentValueAsNumber_0 = getValueAsNumber(currentValue_0);
|
|
171
|
+
if (typeof currentValue_0 === "string" && Number.isNaN(currentValueAsNumber_0)) {
|
|
172
|
+
return currentValue_0;
|
|
173
173
|
}
|
|
174
174
|
let nextValue = currentValueAsNumber_0 + modifier;
|
|
175
175
|
if (cssValueType === "integer") {
|
|
@@ -186,7 +186,7 @@ const CSSValueInput = forwardRef(function CSSValueInput2(t0, ref) {
|
|
|
186
186
|
const nextUnit = getUnitFromCSSValue({
|
|
187
187
|
cssValueType,
|
|
188
188
|
defaultUnit: unit,
|
|
189
|
-
value:
|
|
189
|
+
value: currentValue_0
|
|
190
190
|
});
|
|
191
191
|
return `${nextValue}${nextUnit}`;
|
|
192
192
|
};
|
|
@@ -209,33 +209,21 @@ const CSSValueInput = forwardRef(function CSSValueInput2(t0, ref) {
|
|
|
209
209
|
if (onKeyDown) {
|
|
210
210
|
onKeyDown(event_1);
|
|
211
211
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
event_1.stopPropagation();
|
|
226
|
-
event_1.preventDefault();
|
|
227
|
-
input_0.value = nextValue_0;
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
case "Enter":
|
|
231
|
-
case "Escape": {
|
|
232
|
-
if (event_1.key === "Escape") {
|
|
233
|
-
input_0.value = submittedValueRef.current;
|
|
234
|
-
}
|
|
235
|
-
input_0.blur();
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
212
|
+
if (event_1.key !== "ArrowDown" && event_1.key !== "ArrowUp") {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const currentValue_1 = input_0.value ?? placeholder ?? `0${unit}`;
|
|
216
|
+
const nextValue_0 = getNextValue({
|
|
217
|
+
currentValue: currentValue_1,
|
|
218
|
+
multiplier: event_1.shiftKey ? 10 : 1,
|
|
219
|
+
signum: event_1.key === "ArrowUp" ? 1 : -1
|
|
220
|
+
});
|
|
221
|
+
if (nextValue_0 === currentValue_1) {
|
|
222
|
+
return;
|
|
238
223
|
}
|
|
224
|
+
event_1.stopPropagation();
|
|
225
|
+
event_1.preventDefault();
|
|
226
|
+
input_0.value = nextValue_0;
|
|
239
227
|
};
|
|
240
228
|
$[23] = getNextValue;
|
|
241
229
|
$[24] = onKeyDown;
|
|
@@ -293,7 +281,7 @@ const CSSValueInput = forwardRef(function CSSValueInput2(t0, ref) {
|
|
|
293
281
|
}
|
|
294
282
|
let t18;
|
|
295
283
|
if ($[38] !== disabled || $[39] !== handleBlur || $[40] !== handleKeyDown || $[41] !== handleKeyUp || $[42] !== name || $[43] !== onChange || $[44] !== onFocus || $[45] !== placeholder || $[46] !== tabIndex || $[47] !== value) {
|
|
296
|
-
t18 = /* @__PURE__ */ jsx("div", { className: `${ROOT_CLASS_NAME}-value`, children: /* @__PURE__ */ jsx(InputText, { disabled, initialValue: value, name, onBlur: handleBlur, onChange, onFocus, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp, placeholder, ref: inputRef, selectTextOnFocus: true, tabIndex }) });
|
|
284
|
+
t18 = /* @__PURE__ */ jsx("div", { className: `${ROOT_CLASS_NAME}-value`, children: /* @__PURE__ */ jsx(InputText, { disabled, discardOnEscape: true, initialValue: value, name, onBlur: handleBlur, onChange, onFocus, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp, placeholder, ref: inputRef, selectTextOnFocus: true, tabIndex }) });
|
|
297
285
|
$[38] = disabled;
|
|
298
286
|
$[39] = handleBlur;
|
|
299
287
|
$[40] = handleKeyDown;
|
|
@@ -326,7 +314,7 @@ const CSSValueInput = forwardRef(function CSSValueInput2(t0, ref) {
|
|
|
326
314
|
t19 = $[55];
|
|
327
315
|
}
|
|
328
316
|
return t19;
|
|
329
|
-
}
|
|
317
|
+
}
|
|
330
318
|
export {
|
|
331
319
|
CSSValueInput as default
|
|
332
320
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CSSValueInput.js","sources":["../src/CSSValueInput.tsx"],"sourcesContent":["import {\n type CSSValueType,\n DEFAULT_CSS_VALUE_TYPE,\n DEFAULT_UNIT_BY_CSS_VALUE_TYPE,\n getCSSValueAsNumber,\n getCSSValueWithUnit,\n getUnitFromCSSValue,\n roundToPrecision,\n} from '@acusti/css-values';\nimport InputText from '@acusti/input-text';\nimport clsx from 'clsx';\nimport {\n type ChangeEvent,\n type FocusEvent,\n forwardRef,\n type KeyboardEvent,\n type ReactNode,\n type SyntheticEvent,\n useEffect,\n useImperativeHandle,\n useRef,\n} from 'react';\n\nexport type Props = {\n /**\n * Boolean indicating if the user can submit an empty value (i.e. clear\n * the value). Defaults to true.\n */\n allowEmpty?: boolean;\n className?: string;\n cssValueType?: CSSValueType;\n disabled?: boolean;\n /**\n * Function that receives a value and converts it to its numerical equivalent\n * (i.e. '12px' → 12). Defaults to parseFloat().\n */\n getValueAsNumber?: (value: number | string) => number;\n icon?: ReactNode;\n label?: string;\n max?: number;\n min?: number;\n name?: string;\n onBlur?: (event: FocusEvent<HTMLInputElement>) => unknown;\n onChange?: (event: ChangeEvent<HTMLInputElement>) => unknown;\n onFocus?: (event: FocusEvent<HTMLInputElement>) => unknown;\n onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => unknown;\n onKeyUp?: (event: KeyboardEvent<HTMLInputElement>) => unknown;\n /**\n * Custom event handler triggered when the user presses enter/return\n * or blurs the input after making a change. Hitting esc will restore\n * the previous submitted value or original value.\n */\n onSubmitValue: (value: string) => unknown;\n placeholder?: string;\n step?: number;\n tabIndex?: number;\n title?: string;\n unit?: string;\n /** Regex or validator function to validate non-numeric values */\n validator?: ((value: string) => boolean) | RegExp;\n value?: string;\n};\n\ntype InputRef = HTMLInputElement | null;\n\nconst ROOT_CLASS_NAME = 'cssvalueinput';\n\nexport default forwardRef<HTMLInputElement, Props>(function CSSValueInput(\n {\n allowEmpty = true,\n className,\n cssValueType = DEFAULT_CSS_VALUE_TYPE,\n disabled,\n getValueAsNumber = getCSSValueAsNumber,\n icon,\n label,\n max,\n min,\n name,\n onBlur,\n onChange,\n onFocus,\n onKeyDown,\n onKeyUp,\n onSubmitValue,\n placeholder,\n step = 1,\n tabIndex,\n title,\n unit = DEFAULT_UNIT_BY_CSS_VALUE_TYPE[cssValueType],\n validator,\n value: valueFromProps,\n }: Props,\n ref,\n) {\n const inputRef = useRef<InputRef>(null);\n useImperativeHandle<InputRef, InputRef>(ref, () => inputRef.current);\n // props.value should be a string; if it’s a number, convert it here\n const value =\n typeof valueFromProps === 'number' && !Number.isNaN(valueFromProps)\n ? `${valueFromProps}`\n : valueFromProps;\n const submittedValueRef = useRef<string>(value ?? '');\n\n useEffect(() => {\n submittedValueRef.current = value ?? '';\n }, [value]);\n\n const handleSubmitValue = (event: SyntheticEvent<HTMLInputElement>) => {\n const currentValue = event.currentTarget.value;\n // Store last submittedValue (used to reset value on invalid input)\n submittedValueRef.current = currentValue;\n onSubmitValue(currentValue);\n };\n\n const handleBlur = (event: FocusEvent<HTMLInputElement>) => {\n const input = event.currentTarget;\n inputRef.current = input;\n if (onBlur) onBlur(event);\n\n const currentValue = input.value.trim();\n\n // If allowEmpty and value is empty, skip all validation + normalization\n if (allowEmpty && !currentValue) {\n handleSubmitValue(event);\n return;\n }\n\n const currentValueAsNumber = getValueAsNumber(currentValue);\n const isCurrentValueFinite = Number.isFinite(currentValueAsNumber);\n // Inherit unit from last submitted value unless default is unitless;\n // ensures that submitting a new value with no unit doesn’t add a unit\n const defaultUnit = unit\n ? getUnitFromCSSValue({\n cssValueType,\n defaultUnit: unit,\n value: submittedValueRef.current,\n })\n : '';\n\n if (!isCurrentValueFinite) {\n let isValid = false;\n if (validator instanceof RegExp) {\n isValid = validator.test(currentValue);\n } else if (validator) {\n isValid = validator(currentValue);\n }\n\n if (isValid) {\n handleSubmitValue(event);\n } else {\n // If current value isn’t valid, revert to last submitted value\n input.value = submittedValueRef.current;\n }\n\n return;\n }\n\n // Normalize value by applying min/max and integer constraints\n let normalizedValueAsNumber = currentValueAsNumber;\n\n if (isCurrentValueFinite) {\n if (min != null && currentValueAsNumber < min) {\n normalizedValueAsNumber = min;\n } else if (max != null && currentValueAsNumber > max) {\n normalizedValueAsNumber = max;\n } else if (cssValueType === 'integer') {\n normalizedValueAsNumber = Math.floor(currentValueAsNumber);\n }\n }\n\n if (normalizedValueAsNumber !== currentValueAsNumber) {\n const currentUnit = getUnitFromCSSValue({\n cssValueType,\n defaultUnit,\n value: currentValue,\n });\n input.value = normalizedValueAsNumber + currentUnit;\n } else {\n input.value = getCSSValueWithUnit({\n cssValueType,\n defaultUnit,\n value: currentValue,\n });\n }\n\n handleSubmitValue(event);\n };\n\n const getNextValue = ({\n currentValue,\n multiplier = 1,\n signum = 1,\n }: {\n currentValue: number | string;\n multiplier?: number;\n signum?: number;\n }) => {\n const modifier = multiplier * step * signum;\n const currentValueAsNumber = getValueAsNumber(currentValue);\n // If currentValue isn’t numeric, don’t try to increment/decrement it\n if (typeof currentValue === 'string' && Number.isNaN(currentValueAsNumber)) {\n return currentValue;\n }\n\n let nextValue = currentValueAsNumber + modifier;\n if (cssValueType === 'integer') {\n nextValue = Math.floor(nextValue);\n } else {\n nextValue = roundToPrecision(nextValue, 5);\n }\n\n if (typeof max === 'number' && Number.isFinite(max)) {\n nextValue = Math.min(max, nextValue);\n }\n\n if (typeof min === 'number' && Number.isFinite(min)) {\n nextValue = Math.max(min, nextValue);\n }\n\n const nextUnit = getUnitFromCSSValue({\n cssValueType,\n defaultUnit: unit,\n value: currentValue,\n });\n return `${nextValue}${nextUnit}`;\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n const input = event.currentTarget;\n inputRef.current = input;\n if (onKeyDown) onKeyDown(event);\n\n const currentValue = input.value ?? placeholder ?? `0${unit}`;\n let nextValue = '';\n\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowUp':\n nextValue = getNextValue({\n currentValue,\n multiplier: event.shiftKey ? 10 : 1,\n signum: event.key === 'ArrowUp' ? 1 : -1,\n });\n\n if (nextValue === currentValue) return;\n\n event.stopPropagation();\n event.preventDefault();\n\n input.value = nextValue;\n return;\n case 'Enter':\n case 'Escape':\n if (event.key === 'Escape') {\n input.value = submittedValueRef.current;\n }\n input.blur();\n return;\n default:\n // No default key handling\n }\n };\n\n const handleKeyUp = (event: KeyboardEvent<HTMLInputElement>) => {\n if (onKeyUp) onKeyUp(event);\n // If this is the key up from ↑ or ↓ keys, time to handleSubmitValue\n if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {\n handleSubmitValue(event);\n }\n };\n\n return (\n <label\n aria-label={label ? undefined : title}\n className={clsx(ROOT_CLASS_NAME, className, { disabled })}\n title={title}\n >\n {icon == null ? null : (\n <div className={`${ROOT_CLASS_NAME}-icon`}>{icon}</div>\n )}\n {label ? (\n <div className={`${ROOT_CLASS_NAME}-label`}>\n <p className={`${ROOT_CLASS_NAME}-label-text`}>{label}</p>\n </div>\n ) : null}\n <div className={`${ROOT_CLASS_NAME}-value`}>\n <InputText\n disabled={disabled}\n initialValue={value}\n name={name}\n onBlur={handleBlur}\n onChange={onChange}\n onFocus={onFocus}\n onKeyDown={handleKeyDown}\n onKeyUp={handleKeyUp}\n placeholder={placeholder}\n ref={inputRef}\n selectTextOnFocus\n tabIndex={tabIndex}\n />\n </div>\n </label>\n );\n});\n"],"names":["ROOT_CLASS_NAME","forwardRef","CSSValueInput","t0","ref","$","_c","allowEmpty","t1","className","cssValueType","t2","disabled","getValueAsNumber","t3","icon","label","max","min","name","onBlur","onChange","onFocus","onKeyDown","onKeyUp","onSubmitValue","placeholder","step","t4","tabIndex","title","unit","t5","validator","value","valueFromProps","undefined","DEFAULT_CSS_VALUE_TYPE","getCSSValueAsNumber","DEFAULT_UNIT_BY_CSS_VALUE_TYPE","inputRef","useRef","t6","Symbol","for","current","useImperativeHandle","Number","isNaN","submittedValueRef","t7","t8","useEffect","t9","event","currentValue","currentTarget","handleSubmitValue","t10","event_0","input","currentValue_0","trim","currentValueAsNumber","isCurrentValueFinite","isFinite","defaultUnit","getUnitFromCSSValue","isValid","RegExp","test","normalizedValueAsNumber","currentUnit","getCSSValueWithUnit","handleBlur","t11","t12","currentValue_1","multiplier","t13","signum","t14","modifier","currentValueAsNumber_0","nextValue","nextUnit","getNextValue","event_1","input_0","currentValue_2","nextValue_0","key","shiftKey","stopPropagation","preventDefault","blur","handleKeyDown","event_2","handleKeyUp","t15","clsx","t16","t17","t18","t19"],"mappings":";;;;;;AAiEA,MAAMA,kBAAkB;AAExB,MAAA,gBAAeC,WAAoC,SAAAC,eAAAC,IAAAC,KAAA;AAAA,QAAAC,IAAAC,EAAA,EAAA;AAC/C,QAAA;AAAA,IAAAC,YAAAC;AAAAA,IAAAC;AAAAA,IAAAC,cAAAC;AAAAA,IAAAC;AAAAA,IAAAC,kBAAAC;AAAAA,IAAAC;AAAAA,IAAAC;AAAAA,IAAAC;AAAAA,IAAAC;AAAAA,IAAAC;AAAAA,IAAAC;AAAAA,IAAAC;AAAAA,IAAAC;AAAAA,IAAAC;AAAAA,IAAAC;AAAAA,IAAAC;AAAAA,IAAAC;AAAAA,IAAAC,MAAAC;AAAAA,IAAAC;AAAAA,IAAAC;AAAAA,IAAAC,MAAAC;AAAAA,IAAAC;AAAAA,IAAAC,OAAAC;AAAAA,EAAAA,IAAAhC;AACI,QAAAI,aAAAC,OAAiB4B,gBAAjB5B;AAEA,QAAAE,eAAAC,OAAqCyB,SAAAC,yBAArC1B;AAEA,QAAAE,mBAAAC,OAAsCsB,SAAAE,sBAAtCxB;AAaA,QAAAa,OAAAC,OAAQQ,aAARR;AAGA,QAAAG,OAAAC,OAAmDI,SAAAG,+BAAb7B,YAAY,IAAlDsB;AAMJ,QAAAQ,WAAiBC,OAAA,IAAqB;AAAE,MAAAC;AAAA,MAAArC,EAAA,CAAA,MAAAsC,OAAAC,IAAA,2BAAA,GAAA;AACKF,SAAAA,MAAMF,SAAQK;AAAQxC,WAAAqC;AAAAA,EAAAA,OAAA;AAAAA,SAAArC,EAAA,CAAA;AAAA,EAAA;AAAnEyC,sBAAwC1C,KAAKsC,EAAsB;AAEnE,QAAAR,QACI,OAAOC,mBAAmB,aAAaY,OAAAC,MAAab,cAAc,IAC5D,GAAGA,cAAc,KACjBA;AACV,QAAAc,oBAA0BR,OAAeP,SAAS,EAAE;AAAE,MAAAgB;AAAA,MAAAC;AAAA,MAAA9C,SAAA6B,OAAA;AAE5CgB,SAAAA,MAAA;AACND,wBAAiBJ,UAAWX,SAAS;AAAA,IAAA;AACtCiB,UAACjB,KAAK;AAAC7B,WAAA6B;AAAA7B,WAAA6C;AAAA7C,WAAA8C;AAAAA,EAAAA,OAAA;AAAAD,SAAA7C,EAAA,CAAA;AAAA8C,SAAA9C,EAAA,CAAA;AAAA,EAAA;AAFV+C,YAAUF,IAEPC,EAAO;AAAC,MAAAE;AAAA,MAAAhD,SAAAoB,eAAA;AAEe4B,SAAAC,CAAAA,UAAA;AACtB,YAAAC,eAAqBD,MAAKE,cAAAtB;AAE1Be,wBAAiBJ,UAAWU;AAC5B9B,oBAAc8B,YAAY;AAAA,IAAA;AAC7BlD,WAAAoB;AAAApB,WAAAgD;AAAAA,EAAAA,OAAA;AAAAA,SAAAhD,EAAA,CAAA;AAAA,EAAA;AALD,QAAAoD,oBAA0BJ;AAKxB,MAAAK;AAAA,MAAArD,EAAA,CAAA,MAAAE,cAAAF,EAAA,CAAA,MAAAK,gBAAAL,SAAAQ,oBAAAR,EAAA,CAAA,MAAAoD,qBAAApD,EAAA,EAAA,MAAAY,OAAAZ,EAAA,EAAA,MAAAa,OAAAb,EAAA,EAAA,MAAAe,UAAAf,EAAA,EAAA,MAAA0B,QAAA1B,UAAA4B,WAAA;AAEiByB,UAAAC,CAAAA,YAAA;AACf,YAAAC,QAAcN,QAAKE;AACnBhB,eAAQK,UAAWe;AAAK,UACpBxC,QAAM;AAAEA,eAAOkC,OAAK;AAAA,MAAA;AAExB,YAAAO,iBAAqBD,MAAK1B,MAAA4B,KAAAA;AAAc,UAGpCvD,eAAegD,gBAAY;AAC3BE,0BAAkBH,OAAK;AAAC;AAAA,MAAA;AAI5B,YAAAS,uBAA6BlD,iBAAiB0C,cAAY;AAC1D,YAAAS,uBAA6BjB,OAAAkB,SAAgBF,oBAAoB;AAGjE,YAAAG,cAAoBnC,OACdoC,oBAAA;AAAA,QAAAzD;AAAAA,QAAAwD,aAEiBnC;AAAAA,QAAIG,OACVe,kBAAiBJ;AAAAA,MAAAA,CAC3B,IACD;AAAG,UAAA,CAEJmB,sBAAoB;AACrB,YAAAI,UAAA;AAAoB,YAChBnC,qBAASoC,QAAkB;AAC3BD,oBAAUnC,UAASqC,KAAMf,cAAY;AAAA,QAAA,OAA9B;AAAA,cACAtB,WAAS;AAChBmC,sBAAUnC,UAAUsB,cAAY;AAAA,UAAA;AAAA,QAAzB;AAAA,YAGPa,SAAO;AACPX,4BAAkBH,OAAK;AAAA,QAAA,OAAC;AAGxBM,gBAAK1B,QAASe,kBAAiBJ;AAAAA,QAAAA;AAAA;AAAA,MAAA;AAOvC,UAAA0B,0BAA8BR;AAAqB,UAE/CC,sBAAoB;AAAA,YAChB9C,OAAG,QAAY6C,uBAAuB7C,KAAG;AACzCqD,oCAA0BrD;AAAAA,QAAAA,OAAH;AAAA,cAChBD,OAAG,QAAY8C,uBAAuB9C,KAAG;AAChDsD,sCAA0BtD;AAAAA,UAAAA,OAAH;AAAA,gBAChBP,iBAAiB,WAAS;AACjC6D,wCAA0BA,KAAAA,MAAWR,oBAAoB;AAAA,YAAA;AAAA,UAAlC;AAAA,QAAA;AAAA,MAAA;AAAA,UAI3BQ,4BAA4BR,sBAAoB;AAChD,cAAAS,cAAoBL,oBAAA;AAAA,UAAAzD;AAAAA,UAAAwD;AAAAA,UAAAhC,OAGTqB;AAAAA,QAAAA,CACV;AACDK,cAAK1B,QAASqC,0BAA0BC;AAAAA,MAAAA,OAAW;AAEnDZ,cAAK1B,QAASuC,oBAAA;AAAA,UAAA/D;AAAAA,UAAAwD;AAAAA,UAAAhC,OAGHqB;AAAAA,QAAAA,CACV;AAAA,MAAA;AAGLE,wBAAkBH,OAAK;AAAA,IAAA;AAC1BjD,WAAAE;AAAAF,WAAAK;AAAAL,WAAAQ;AAAAR,WAAAoD;AAAApD,YAAAY;AAAAZ,YAAAa;AAAAb,YAAAe;AAAAf,YAAA0B;AAAA1B,YAAA4B;AAAA5B,YAAAqD;AAAAA,EAAAA,OAAA;AAAAA,UAAArD,EAAA,EAAA;AAAA,EAAA;AAxED,QAAAqE,aAAmBhB;AAwEjB,MAAAiB;AAAA,MAAAtE,UAAAK,gBAAAL,EAAA,EAAA,MAAAQ,oBAAAR,EAAA,EAAA,MAAAY,OAAAZ,EAAA,EAAA,MAAAa,OAAAb,UAAAsB,QAAAtB,EAAA,EAAA,MAAA0B,MAAA;AAEmB4C,UAAAC,CAAAA,SAAA;AAAC,YAAA;AAAA,QAAArB,cAAAsB;AAAAA,QAAAC,YAAAC;AAAAA,QAAAC,QAAAC;AAAAA,MAAAA,IAAAL;AAElB,YAAAE,aAAAC,SAAc3C,aAAd2C;AACA,YAAAC,SAAAC,SAAU7C,aAAV6C;AAMA,YAAAC,WAAiBJ,aAAanD,OAAOqD;AACrC,YAAAG,yBAA6BtE,iBAAiB0C,cAAY;AAAE,UAExD,OAAOA,mBAAiB,YAAYR,OAAAC,MAAae,sBAAoB,GAAC;AAAA,eAC/DR;AAAAA,MAAAA;AAGX,UAAA6B,YAAgBrB,yBAAuBmB;AAAS,UAC5CxE,iBAAiB,WAAS;AAC1B0E,oBAAYA,KAAAA,MAAWA,SAAS;AAAA,MAAA,OAAvB;AAETA,oBAAYA,iBAAiBA,YAAY;AAAA,MAAA;AAAhC,UAGT,OAAOnE,QAAQ,YAAY8B,OAAAkB,SAAgBhD,GAAG,GAAC;AAC/CmE,oBAAYA,SAASnE,KAAKmE,SAAS;AAAA,MAAA;AAA1B,UAGT,OAAOlE,QAAQ,YAAY6B,OAAAkB,SAAgB/C,GAAG,GAAC;AAC/CkE,oBAAYA,SAASlE,KAAKkE,SAAS;AAAA,MAAA;AAGvC,YAAAC,WAAiBlB,oBAAA;AAAA,QAAAzD;AAAAA,QAAAwD,aAEAnC;AAAAA,QAAIG,OACVqB;AAAAA,MAAAA,CACV;AAAE,aACI,GAAG6B,SAAS,GAAGC,QAAQ;AAAA,IAAA;AACjChF,YAAAK;AAAAL,YAAAQ;AAAAR,YAAAY;AAAAZ,YAAAa;AAAAb,YAAAsB;AAAAtB,YAAA0B;AAAA1B,YAAAsE;AAAAA,EAAAA,OAAA;AAAAA,UAAAtE,EAAA,EAAA;AAAA,EAAA;AArCD,QAAAiF,eAAqBX;AAqCnB,MAAAC;AAAA,MAAAvE,EAAA,EAAA,MAAAiF,gBAAAjF,EAAA,EAAA,MAAAkB,aAAAlB,EAAA,EAAA,MAAAqB,eAAArB,UAAA0B,MAAA;AAEoB6C,UAAAW,CAAAA,YAAA;AAClB,YAAAC,UAAclC,QAAKE;AACnBhB,eAAQK,UAAWe;AAAK,UACpBrC,WAAS;AAAEA,kBAAU+B,OAAK;AAAA,MAAA;AAE9B,YAAAmC,iBAAqB7B,QAAK1B,SAAUR,eAAe,IAAIK,IAAI;AAC3D,UAAA2D;AAAmB,cAEXpC,QAAKqC,KAAAA;AAAAA,QAAA,KACJ;AAAA,QAAW,KACX,WAAS;AACVP,wBAAYE,aAAY;AAAA,YAAA/B,cACpBA;AAAAA,YAAYuB,YACAxB,QAAKsC,WAAA,KAAA;AAAA,YAAkBZ,QAC3B1B,QAAKqC,QAAS,YAAS,IAAA;AAAA,UAAA,CAClC;AAJQ,cAMLP,gBAAc7B,gBAAY;AAAA;AAAA,UAAA;AAE9BD,kBAAKuC,gBAAAA;AACLvC,kBAAKwC,eAAAA;AAELlC,kBAAK1B,QAASkD;AAAS;AAAA,QAAA;AAAA,QAAA,KAEtB;AAAA,QAAO,KACP,UAAQ;AAAA,cACL9B,QAAKqC,QAAS,UAAQ;AACtB/B,oBAAK1B,QAASe,kBAAiBJ;AAAAA,UAAAA;AAEnCe,kBAAKmC,KAAAA;AAAO;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAKvB1F,YAAAiF;AAAAjF,YAAAkB;AAAAlB,YAAAqB;AAAArB,YAAA0B;AAAA1B,YAAAuE;AAAAA,EAAAA,OAAA;AAAAA,UAAAvE,EAAA,EAAA;AAAA,EAAA;AAlCD,QAAA2F,gBAAsBpB;AAkCpB,MAAAG;AAAA,MAAA1E,EAAA,EAAA,MAAAoD,qBAAApD,UAAAmB,SAAA;AAEkBuD,UAAAkB,CAAAA,YAAA;AAAA,UACZzE,SAAO;AAAEA,gBAAQ8B,OAAK;AAAA,MAAA;AAAC,UAEvBA,QAAKqC,QAAS,aAAarC,QAAKqC,QAAS,aAAW;AACpDlC,0BAAkBH,OAAK;AAAA,MAAA;AAAA,IAAC;AAE/BjD,YAAAoD;AAAApD,YAAAmB;AAAAnB,YAAA0E;AAAAA,EAAAA,OAAA;AAAAA,UAAA1E,EAAA,EAAA;AAAA,EAAA;AAND,QAAA6F,cAAoBnB;AAUA,QAAAE,MAAAjE,QAAKoB,SAAeN;AAAK,MAAAqE;AAAA,MAAA9F,EAAA,EAAA,MAAAI,aAAAJ,UAAAO,UAAA;AAC1BuF,UAAAC,KAAApG,iBAAsBS,WAAS;AAAA,MAAAG;AAAAA,IAAAA,CAAc;AAACP,YAAAI;AAAAJ,YAAAO;AAAAP,YAAA8F;AAAAA,EAAAA,OAAA;AAAAA,UAAA9F,EAAA,EAAA;AAAA,EAAA;AAAA,MAAAgG;AAAA,MAAAhG,UAAAU,MAAA;AAGxDsF,UAAAtF,QAAI,OAAQ,OACT,oBAAA,SAAgB,WAAA,GAAAf,eAAA,yBAAiC;AACpDK,YAAAU;AAAAV,YAAAgG;AAAAA,EAAAA,OAAA;AAAAA,UAAAhG,EAAA,EAAA;AAAA,EAAA;AAAA,MAAAiG;AAAA,MAAAjG,UAAAW,OAAA;AACAsF,UAAAtF,QACG,oBAAA,OAAA,EAAgB,WAAA,GAAAhB,eAAA,UACZ,UAAA,oBAAA,KAAA,EAAc,WAAA,GAAAA,eAAA,eAAkCgB,UAAAA,MAAAA,CAAM,GAC1D,IAAM;AACFX,YAAAW;AAAAX,YAAAiG;AAAAA,EAAAA,OAAA;AAAAA,UAAAjG,EAAA,EAAA;AAAA,EAAA;AAAA,MAAAkG;AAAA,MAAAlG,EAAA,EAAA,MAAAO,YAAAP,UAAAqE,cAAArE,EAAA,EAAA,MAAA2F,iBAAA3F,UAAA6F,eAAA7F,EAAA,EAAA,MAAAc,QAAAd,UAAAgB,YAAAhB,EAAA,EAAA,MAAAiB,WAAAjB,EAAA,EAAA,MAAAqB,eAAArB,EAAA,EAAA,MAAAwB,YAAAxB,UAAA6B,OAAA;AACRqE,UAAA,oBAAA,OAAA,EAAgB,cAAAvG,eAAA,UACZ,UAAA,oBAAC,WAAA,EACaY,UACIsB,cAAAA,OACRf,MACEuD,QAAAA,YACErD,UACDC,SACE0E,WAAAA,eACFE,SAAAA,aACIxE,aACRc,KAAAA,UACL,mBAAA,MACUX,SAAAA,CAAQ,GAE1B;AAAMxB,YAAAO;AAAAP,YAAAqE;AAAArE,YAAA2F;AAAA3F,YAAA6F;AAAA7F,YAAAc;AAAAd,YAAAgB;AAAAhB,YAAAiB;AAAAjB,YAAAqB;AAAArB,YAAAwB;AAAAxB,YAAA6B;AAAA7B,YAAAkG;AAAAA,EAAAA,OAAA;AAAAA,UAAAlG,EAAA,EAAA;AAAA,EAAA;AAAA,MAAAmG;AAAA,MAAAnG,UAAA4E,OAAA5E,EAAA,EAAA,MAAA8F,OAAA9F,EAAA,EAAA,MAAAgG,OAAAhG,EAAA,EAAA,MAAAiG,OAAAjG,UAAAkG,OAAAlG,EAAA,EAAA,MAAAyB,OAAA;AA5BV0E,+BAAA,SAAA,EACgB,cAAAvB,KACD,WAAAkB,KACJrE,OAENuE,UAAAA;AAAAA,MAAAA;AAAAA,MAGAC;AAAAA,MAKDC;AAAAA,IAAAA,GAgBJ;AAAQlG,YAAA4E;AAAA5E,YAAA8F;AAAA9F,YAAAgG;AAAAhG,YAAAiG;AAAAjG,YAAAkG;AAAAlG,YAAAyB;AAAAzB,YAAAmG;AAAAA,EAAAA,OAAA;AAAAA,UAAAnG,EAAA,EAAA;AAAA,EAAA;AAAA,SA7BRmG;AA6BQ,CAEf;"}
|
|
1
|
+
{"version":3,"file":"CSSValueInput.js","sources":["../src/CSSValueInput.tsx"],"sourcesContent":["import {\n type CSSValueType,\n DEFAULT_CSS_VALUE_TYPE,\n DEFAULT_UNIT_BY_CSS_VALUE_TYPE,\n getCSSValueAsNumber,\n getCSSValueWithUnit,\n getUnitFromCSSValue,\n roundToPrecision,\n} from '@acusti/css-values';\nimport InputText from '@acusti/input-text';\nimport clsx from 'clsx';\nimport {\n type ChangeEvent,\n type FocusEvent,\n type KeyboardEvent,\n type ReactNode,\n type Ref,\n type SyntheticEvent,\n useEffect,\n useImperativeHandle,\n useRef,\n} from 'react';\n\nexport type Props = {\n /**\n * Boolean indicating if the user can submit an empty value (i.e. clear\n * the value). Defaults to true.\n */\n allowEmpty?: boolean;\n className?: string;\n cssValueType?: CSSValueType;\n disabled?: boolean;\n /**\n * Function that receives a value and converts it to its numerical equivalent\n * (i.e. '12px' → 12). Defaults to parseFloat().\n */\n getValueAsNumber?: (value: number | string) => number;\n icon?: ReactNode;\n label?: string;\n max?: number;\n min?: number;\n name?: string;\n onBlur?: (event: FocusEvent<HTMLInputElement>) => unknown;\n onChange?: (event: ChangeEvent<HTMLInputElement>) => unknown;\n onFocus?: (event: FocusEvent<HTMLInputElement>) => unknown;\n onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => unknown;\n onKeyUp?: (event: KeyboardEvent<HTMLInputElement>) => unknown;\n /**\n * Custom event handler triggered when the user presses enter/return\n * or blurs the input after making a change. Hitting esc will restore\n * the previous submitted value or original value.\n */\n onSubmitValue: (value: string) => unknown;\n placeholder?: string;\n ref?: Ref<HTMLInputElement>;\n step?: number;\n tabIndex?: number;\n title?: string;\n unit?: string;\n /** Regex or validator function to validate non-numeric values */\n validator?: ((value: string) => boolean) | RegExp;\n value?: string;\n};\n\ntype InputRef = HTMLInputElement | null;\n\nconst ROOT_CLASS_NAME = 'cssvalueinput';\n\nexport default function CSSValueInput({\n allowEmpty = true,\n className,\n cssValueType = DEFAULT_CSS_VALUE_TYPE,\n disabled,\n getValueAsNumber = getCSSValueAsNumber,\n icon,\n label,\n max,\n min,\n name,\n onBlur,\n onChange,\n onFocus,\n onKeyDown,\n onKeyUp,\n onSubmitValue,\n placeholder,\n ref,\n step = 1,\n tabIndex,\n title,\n unit = DEFAULT_UNIT_BY_CSS_VALUE_TYPE[cssValueType],\n validator,\n value: valueFromProps,\n}: Props) {\n const inputRef = useRef<InputRef>(null);\n\n useImperativeHandle<InputRef, InputRef>(ref, () => inputRef.current);\n\n // props.value should be a string; if it’s a number, convert it here\n const value =\n typeof valueFromProps === 'number' && Number.isFinite(valueFromProps)\n ? `${valueFromProps}`\n : valueFromProps;\n const submittedValueRef = useRef(value ?? '');\n\n useEffect(() => {\n submittedValueRef.current = value ?? '';\n }, [value]);\n\n const handleSubmitValue = (event: SyntheticEvent<HTMLInputElement>) => {\n // Store last submittedValue (used to reset value on invalid input)\n submittedValueRef.current = event.currentTarget.value;\n onSubmitValue(event.currentTarget.value);\n };\n\n const handleBlur = (event: FocusEvent<HTMLInputElement>) => {\n const input = event.currentTarget;\n inputRef.current = input;\n if (onBlur) onBlur(event);\n\n const currentValue = input.value.trim();\n\n // If allowEmpty and value is empty, skip all validation + normalization\n if (allowEmpty && !currentValue) {\n handleSubmitValue(event);\n return;\n }\n\n const currentValueAsNumber = getValueAsNumber(currentValue);\n const isCurrentValueFinite = Number.isFinite(currentValueAsNumber);\n // Inherit unit from last submitted value unless default is unitless;\n // ensures that submitting a new value with no unit doesn’t add a unit\n const defaultUnit = unit\n ? getUnitFromCSSValue({\n cssValueType,\n defaultUnit: unit,\n value: submittedValueRef.current,\n })\n : '';\n\n if (!isCurrentValueFinite) {\n let isValid = false;\n if (validator instanceof RegExp) {\n isValid = validator.test(currentValue);\n } else if (validator) {\n isValid = validator(currentValue);\n }\n\n if (isValid) {\n handleSubmitValue(event);\n } else {\n // If current value isn’t valid, revert to last submitted value\n input.value = submittedValueRef.current;\n }\n\n return;\n }\n\n // Normalize value by applying min/max and integer constraints\n let normalizedValueAsNumber = currentValueAsNumber;\n\n if (isCurrentValueFinite) {\n if (min != null && currentValueAsNumber < min) {\n normalizedValueAsNumber = min;\n } else if (max != null && currentValueAsNumber > max) {\n normalizedValueAsNumber = max;\n } else if (cssValueType === 'integer') {\n normalizedValueAsNumber = Math.floor(currentValueAsNumber);\n }\n }\n\n if (normalizedValueAsNumber !== currentValueAsNumber) {\n const currentUnit = getUnitFromCSSValue({\n cssValueType,\n defaultUnit,\n value: currentValue,\n });\n input.value = normalizedValueAsNumber + currentUnit;\n } else {\n input.value = getCSSValueWithUnit({\n cssValueType,\n defaultUnit,\n value: currentValue,\n });\n }\n\n handleSubmitValue(event);\n };\n\n const getNextValue = ({\n currentValue,\n multiplier = 1,\n signum = 1,\n }: {\n currentValue: number | string;\n multiplier?: number;\n signum?: number;\n }) => {\n const modifier = multiplier * step * signum;\n const currentValueAsNumber = getValueAsNumber(currentValue);\n // If currentValue isn’t numeric, don’t try to increment/decrement it\n if (typeof currentValue === 'string' && Number.isNaN(currentValueAsNumber)) {\n return currentValue;\n }\n\n let nextValue = currentValueAsNumber + modifier;\n if (cssValueType === 'integer') {\n nextValue = Math.floor(nextValue);\n } else {\n nextValue = roundToPrecision(nextValue, 5);\n }\n\n if (typeof max === 'number' && Number.isFinite(max)) {\n nextValue = Math.min(max, nextValue);\n }\n\n if (typeof min === 'number' && Number.isFinite(min)) {\n nextValue = Math.max(min, nextValue);\n }\n\n const nextUnit = getUnitFromCSSValue({\n cssValueType,\n defaultUnit: unit,\n value: currentValue,\n });\n return `${nextValue}${nextUnit}`;\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n const input = event.currentTarget;\n inputRef.current = input;\n if (onKeyDown) onKeyDown(event);\n if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return;\n\n const currentValue = input.value ?? placeholder ?? `0${unit}`;\n const nextValue = getNextValue({\n currentValue,\n multiplier: event.shiftKey ? 10 : 1,\n signum: event.key === 'ArrowUp' ? 1 : -1,\n });\n\n if (nextValue === currentValue) return;\n\n event.stopPropagation();\n event.preventDefault();\n\n input.value = nextValue;\n };\n\n const handleKeyUp = (event: KeyboardEvent<HTMLInputElement>) => {\n if (onKeyUp) onKeyUp(event);\n // If this is the key up from ↑ or ↓ keys, time to handleSubmitValue\n if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {\n handleSubmitValue(event);\n }\n };\n\n return (\n <label\n aria-label={label ? undefined : title}\n className={clsx(ROOT_CLASS_NAME, className, { disabled })}\n title={title}\n >\n {icon == null ? null : (\n <div className={`${ROOT_CLASS_NAME}-icon`}>{icon}</div>\n )}\n {label ? (\n <div className={`${ROOT_CLASS_NAME}-label`}>\n <p className={`${ROOT_CLASS_NAME}-label-text`}>{label}</p>\n </div>\n ) : null}\n <div className={`${ROOT_CLASS_NAME}-value`}>\n <InputText\n disabled={disabled}\n discardOnEscape\n initialValue={value}\n name={name}\n onBlur={handleBlur}\n onChange={onChange}\n onFocus={onFocus}\n onKeyDown={handleKeyDown}\n onKeyUp={handleKeyUp}\n placeholder={placeholder}\n ref={inputRef}\n selectTextOnFocus\n tabIndex={tabIndex}\n />\n </div>\n </label>\n );\n}\n"],"names":["ROOT_CLASS_NAME","CSSValueInput","t0","$","_c","allowEmpty","t1","className","cssValueType","t2","disabled","getValueAsNumber","t3","icon","label","max","min","name","onBlur","onChange","onFocus","onKeyDown","onKeyUp","onSubmitValue","placeholder","ref","step","t4","tabIndex","title","unit","t5","validator","value","valueFromProps","undefined","DEFAULT_CSS_VALUE_TYPE","getCSSValueAsNumber","DEFAULT_UNIT_BY_CSS_VALUE_TYPE","inputRef","useRef","t6","Symbol","for","current","useImperativeHandle","Number","isFinite","submittedValueRef","t7","t8","useEffect","t9","event","currentTarget","handleSubmitValue","t10","event_0","input","currentValue","trim","currentValueAsNumber","isCurrentValueFinite","defaultUnit","getUnitFromCSSValue","isValid","RegExp","test","normalizedValueAsNumber","Math","floor","currentUnit","getCSSValueWithUnit","handleBlur","t11","t12","currentValue_0","multiplier","t13","signum","t14","modifier","currentValueAsNumber_0","isNaN","nextValue","roundToPrecision","nextUnit","getNextValue","event_1","input_0","key","currentValue_1","nextValue_0","shiftKey","stopPropagation","preventDefault","handleKeyDown","event_2","handleKeyUp","t15","clsx","t16","t17","t18","t19"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@acusti/css-value-input",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"exports": "./dist/CSSValueInput.js",
|
|
@@ -38,23 +38,23 @@
|
|
|
38
38
|
},
|
|
39
39
|
"homepage": "https://github.com/acusti/uikit/tree/main/packages/css-value-input#readme",
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@testing-library/dom": "^10.4.
|
|
41
|
+
"@testing-library/dom": "^10.4.1",
|
|
42
42
|
"@testing-library/react": "^16.3.0",
|
|
43
43
|
"@testing-library/user-event": "^14.6.1",
|
|
44
|
-
"@types/react": "^19.
|
|
45
|
-
"@vitejs/plugin-react": "^
|
|
46
|
-
"babel-plugin-react-compiler": "
|
|
47
|
-
"happy-dom": "^
|
|
48
|
-
"react": "^19.
|
|
49
|
-
"react-dom": "^19.
|
|
50
|
-
"typescript": "5.
|
|
51
|
-
"unplugin-dts": "^1.0.0-beta.
|
|
52
|
-
"vite": "^7.
|
|
44
|
+
"@types/react": "^19.2.2",
|
|
45
|
+
"@vitejs/plugin-react": "^5.0.4",
|
|
46
|
+
"babel-plugin-react-compiler": "^1",
|
|
47
|
+
"happy-dom": "^20.0.10",
|
|
48
|
+
"react": "^19.2.0",
|
|
49
|
+
"react-dom": "^19.2.0",
|
|
50
|
+
"typescript": "5.9.2",
|
|
51
|
+
"unplugin-dts": "^1.0.0-beta.6",
|
|
52
|
+
"vite": "^7.1.10",
|
|
53
53
|
"vitest": "^3.2.4"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@acusti/css-values": "^1.2.0",
|
|
57
|
-
"@acusti/input-text": "^2.
|
|
57
|
+
"@acusti/input-text": "^2.2.1",
|
|
58
58
|
"clsx": "^2"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|