@alexandregme/zljs 1.11.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -1
- package/dist/index.d.ts +28 -6
- package/dist/index.js +37 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,62 @@ A change that needs action in an app has a **Migration** note. When updating, re
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [1.13.0] - 2026-11-02
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `Radio` takes `aria-label`, `required`, `onBlur`, `onKeyDown` and `ref`, as `Input` does, so it works with `register` from `react-hook-form`
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- `Radio` requires `label` or `aria-label` in its type, instead of always `label`
|
|
20
|
+
- `Radio` input now sits inside its `<label>`, as on `Checkbox`, so the gap between the circle and the text is clickable too
|
|
21
|
+
- `Radio` draws a real `focus-visible` outline in the theme's `primary` color instead of a `focus:ring`, so focus stays visible in forced colors mode
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- A checked `Radio` uses the theme's `primary` color. It had `text-blue-600` and `border-gray-300`, which do nothing on a native radio, so it kept the browser's own color
|
|
26
|
+
- The label of a disabled `Radio` shows the `not-allowed` cursor instead of `pointer`
|
|
27
|
+
|
|
28
|
+
#### Migration
|
|
29
|
+
|
|
30
|
+
Only code that extends `RadioProps`, and tests or selectors that rely on the old structure or classes, need a change. To find them, run the commands below from the app's root. The second also finds other uses of these classes, and those stay as they are:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
grep -rnE "\bRadioProps\b" . --include='*.ts' --include='*.tsx' --exclude-dir=node_modules --exclude-dir=.next
|
|
34
|
+
grep -rnE "text-blue-600|focus:ring-blue-500|nextSibling|previousSibling" . --include='*.test.*' --exclude-dir=node_modules --exclude-dir=.next
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Replace `interface X extends RadioProps` with `type X = RadioProps & { ... }`, because `RadioProps` is now a union. In tests, replace those classes with `accent-zl-primary` and `focus-visible:outline-zl-primary`, and find the radio by role instead of by its position next to the label.
|
|
38
|
+
|
|
39
|
+
## [1.12.0] - 2026-11-02
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- `TextArea` takes `aria-label`, `name`, `autoComplete`, `required`, `onBlur`, `onKeyDown` and `ref`, as `Input` and `Select` do, so it works with `register` from `react-hook-form`
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- `TextArea` requires `label` or `aria-label` in its type, instead of always `label`
|
|
48
|
+
- `TextArea` draws a real `focus-visible` outline in the theme's `primary` color instead of a `focus:ring`, so focus stays visible in forced colors mode, and an app's own color reaches it
|
|
49
|
+
- `TextArea` border is now `gray-500` instead of `gray-300`, for 3:1 contrast, and its placeholder is `gray-500`, for 4.5:1
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- `TextArea` with `error` sets `aria-invalid` and points `aria-describedby` to the message, so a screen reader announces both
|
|
54
|
+
- `TextArea` uses `resize-y`. It had a class that doesn't exist in Tailwind, so it only resized vertically in apps with Tailwind's base styles
|
|
55
|
+
|
|
56
|
+
#### Migration
|
|
57
|
+
|
|
58
|
+
Only code that extends `TextAreaProps` and tests that assert its old classes need a change. To find them, run the commands below from the app's root:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
grep -rnE "\bTextAreaProps\b" . --include='*.ts' --include='*.tsx' --exclude-dir=node_modules --exclude-dir=.next
|
|
62
|
+
grep -rnE "border-gray-300|focus:ring-blue-500|focus:border-transparent|resize-vertical" . --include='*.test.*' --exclude-dir=node_modules --exclude-dir=.next
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Replace `interface X extends TextAreaProps` with `type X = TextAreaProps & { ... }`, because `TextAreaProps` is now a union. In tests, replace those classes with `focus-visible:outline-zl-primary`, `border-gray-500` and `resize-y`.
|
|
66
|
+
|
|
11
67
|
## [1.11.0] - 2026-11-02
|
|
12
68
|
|
|
13
69
|
### Added
|
|
@@ -106,7 +162,9 @@ Replace the shade with the class of the `color` being tested, for example `/(^|\
|
|
|
106
162
|
|
|
107
163
|
The message of `Error` is `text-zl-danger`. `none` and `ghost` didn't change.
|
|
108
164
|
|
|
109
|
-
[Unreleased]: https://github.com/alexandregme/zljs/compare/v1.
|
|
165
|
+
[Unreleased]: https://github.com/alexandregme/zljs/compare/v1.13.0...HEAD
|
|
166
|
+
[1.13.0]: https://github.com/alexandregme/zljs/compare/v1.12.0...v1.13.0
|
|
167
|
+
[1.12.0]: https://github.com/alexandregme/zljs/compare/v1.11.0...v1.12.0
|
|
110
168
|
[1.11.0]: https://github.com/alexandregme/zljs/compare/v1.10.0...v1.11.0
|
|
111
169
|
[1.10.0]: https://github.com/alexandregme/zljs/compare/v1.9.0...v1.10.0
|
|
112
170
|
[1.9.0]: https://github.com/alexandregme/zljs/compare/v1.8.0...v1.9.0
|
package/dist/index.d.ts
CHANGED
|
@@ -121,16 +121,26 @@ type InputProps = InputName & {
|
|
|
121
121
|
|
|
122
122
|
declare const Input: react.ForwardRefExoticComponent<InputProps & react.RefAttributes<HTMLInputElement>>;
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
type RadioName = {
|
|
125
125
|
label: string;
|
|
126
|
+
"aria-label"?: string;
|
|
127
|
+
} | {
|
|
128
|
+
label?: undefined;
|
|
129
|
+
"aria-label": string;
|
|
130
|
+
};
|
|
131
|
+
type RadioProps = RadioName & {
|
|
126
132
|
name: string;
|
|
127
133
|
value: string;
|
|
128
134
|
checked?: boolean;
|
|
129
135
|
disabled?: boolean;
|
|
136
|
+
required?: boolean;
|
|
130
137
|
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
131
|
-
|
|
138
|
+
onBlur?: (event: FocusEvent<HTMLInputElement>) => void;
|
|
139
|
+
onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
|
|
140
|
+
ref?: Ref<HTMLInputElement>;
|
|
141
|
+
};
|
|
132
142
|
|
|
133
|
-
declare const Radio: ({ label: labelText, name, value, checked, disabled, onChange, }: RadioProps) => react.JSX.Element;
|
|
143
|
+
declare const Radio: ({ label: labelText, "aria-label": ariaLabel, name, value, checked, disabled, required, onChange, onBlur, onKeyDown, ref, }: RadioProps) => react.JSX.Element;
|
|
134
144
|
|
|
135
145
|
interface SelectOption {
|
|
136
146
|
value: string;
|
|
@@ -160,17 +170,29 @@ type SelectProps = SelectName & {
|
|
|
160
170
|
|
|
161
171
|
declare const Select: ({ label: labelText, "aria-label": ariaLabel, name, options, placeholder, autoComplete, value, error, disabled, required, onChange, onBlur, onKeyDown, ref, }: SelectProps) => react.JSX.Element;
|
|
162
172
|
|
|
163
|
-
|
|
173
|
+
type TextAreaName = {
|
|
164
174
|
label: string;
|
|
175
|
+
"aria-label"?: string;
|
|
176
|
+
} | {
|
|
177
|
+
label?: undefined;
|
|
178
|
+
"aria-label": string;
|
|
179
|
+
};
|
|
180
|
+
type TextAreaProps = TextAreaName & {
|
|
181
|
+
name?: string;
|
|
165
182
|
placeholder?: string;
|
|
183
|
+
autoComplete?: HTMLInputAutoCompleteAttribute;
|
|
166
184
|
value?: string;
|
|
167
185
|
rows?: number;
|
|
168
186
|
error?: string;
|
|
169
187
|
disabled?: boolean;
|
|
188
|
+
required?: boolean;
|
|
170
189
|
onChange?: (event: ChangeEvent<HTMLTextAreaElement>) => void;
|
|
171
|
-
|
|
190
|
+
onBlur?: (event: FocusEvent<HTMLTextAreaElement>) => void;
|
|
191
|
+
onKeyDown?: (event: KeyboardEvent<HTMLTextAreaElement>) => void;
|
|
192
|
+
ref?: Ref<HTMLTextAreaElement>;
|
|
193
|
+
};
|
|
172
194
|
|
|
173
|
-
declare const TextArea: ({ label: labelText, placeholder, value, rows, error, disabled, onChange, }: TextAreaProps) => react.JSX.Element;
|
|
195
|
+
declare const TextArea: ({ label: labelText, "aria-label": ariaLabel, name, placeholder, autoComplete, value, rows, error, disabled, required, onChange, onBlur, onKeyDown, ref, }: TextAreaProps) => react.JSX.Element;
|
|
174
196
|
|
|
175
197
|
declare const Icon: ({ name, size, "aria-label": ariaLabel, }: IconProps) => react.JSX.Element;
|
|
176
198
|
|
package/dist/index.js
CHANGED
|
@@ -306,41 +306,51 @@ import { useId as useId2 } from "react";
|
|
|
306
306
|
// core/forms/radio/radio.styles.ts
|
|
307
307
|
import { tv as tv4 } from "tailwind-variants";
|
|
308
308
|
var wrapper2 = tv4({
|
|
309
|
-
base: "flex items-center gap-2"
|
|
309
|
+
base: "flex w-fit items-center gap-2 cursor-pointer has-disabled:cursor-not-allowed"
|
|
310
310
|
});
|
|
311
311
|
var radio = tv4({
|
|
312
|
-
base: "w-4 h-4
|
|
312
|
+
base: "w-4 h-4 shrink-0 accent-zl-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zl-primary disabled:cursor-not-allowed"
|
|
313
313
|
});
|
|
314
314
|
var label2 = tv4({
|
|
315
|
-
base: "text-sm text-gray-700 select-none
|
|
315
|
+
base: "text-sm text-gray-700 select-none"
|
|
316
316
|
});
|
|
317
317
|
|
|
318
318
|
// core/forms/radio/radio.tsx
|
|
319
319
|
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
320
320
|
var Radio = ({
|
|
321
321
|
label: labelText,
|
|
322
|
+
"aria-label": ariaLabel,
|
|
322
323
|
name,
|
|
323
324
|
value,
|
|
324
325
|
checked,
|
|
325
326
|
disabled,
|
|
326
|
-
|
|
327
|
+
required,
|
|
328
|
+
onChange,
|
|
329
|
+
onBlur,
|
|
330
|
+
onKeyDown,
|
|
331
|
+
ref
|
|
327
332
|
}) => {
|
|
328
333
|
const id = useId2();
|
|
329
|
-
return /* @__PURE__ */ jsxs3("
|
|
334
|
+
return /* @__PURE__ */ jsxs3("label", { htmlFor: id, className: wrapper2(), children: [
|
|
330
335
|
/* @__PURE__ */ jsx5(
|
|
331
336
|
"input",
|
|
332
337
|
{
|
|
338
|
+
ref,
|
|
333
339
|
id,
|
|
334
340
|
type: "radio",
|
|
335
341
|
name,
|
|
336
342
|
value,
|
|
337
343
|
checked,
|
|
338
344
|
disabled,
|
|
345
|
+
required,
|
|
339
346
|
onChange,
|
|
347
|
+
onBlur,
|
|
348
|
+
onKeyDown,
|
|
349
|
+
"aria-label": ariaLabel,
|
|
340
350
|
className: radio()
|
|
341
351
|
}
|
|
342
352
|
),
|
|
343
|
-
/* @__PURE__ */ jsx5("
|
|
353
|
+
labelText && /* @__PURE__ */ jsx5("span", { className: label2(), children: labelText })
|
|
344
354
|
] });
|
|
345
355
|
};
|
|
346
356
|
|
|
@@ -421,36 +431,53 @@ var label4 = tv6({
|
|
|
421
431
|
base: "text-sm font-medium text-gray-700"
|
|
422
432
|
});
|
|
423
433
|
var textarea = tv6({
|
|
424
|
-
base: "px-3 py-2 border border-gray-
|
|
434
|
+
base: "px-3 py-2 border border-gray-500 rounded-md text-sm placeholder:text-gray-500 resize-y focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zl-primary disabled:bg-gray-100 disabled:cursor-not-allowed"
|
|
425
435
|
});
|
|
426
436
|
|
|
427
437
|
// core/forms/text-area/text-area.tsx
|
|
428
438
|
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
429
439
|
var TextArea = ({
|
|
430
440
|
label: labelText,
|
|
441
|
+
"aria-label": ariaLabel,
|
|
442
|
+
name,
|
|
431
443
|
placeholder,
|
|
444
|
+
autoComplete,
|
|
432
445
|
value,
|
|
433
446
|
rows = 3,
|
|
434
447
|
error,
|
|
435
448
|
disabled,
|
|
436
|
-
|
|
449
|
+
required,
|
|
450
|
+
onChange,
|
|
451
|
+
onBlur,
|
|
452
|
+
onKeyDown,
|
|
453
|
+
ref
|
|
437
454
|
}) => {
|
|
438
455
|
const id = useId4();
|
|
456
|
+
const errorId = `${id}-error`;
|
|
439
457
|
return /* @__PURE__ */ jsxs5("div", { className: wrapper4(), children: [
|
|
440
|
-
/* @__PURE__ */ jsx7("label", { htmlFor: id, className: label4(), children: labelText }),
|
|
458
|
+
labelText && /* @__PURE__ */ jsx7("label", { htmlFor: id, className: label4(), children: labelText }),
|
|
441
459
|
/* @__PURE__ */ jsx7(
|
|
442
460
|
"textarea",
|
|
443
461
|
{
|
|
462
|
+
ref,
|
|
444
463
|
id,
|
|
464
|
+
name,
|
|
445
465
|
placeholder,
|
|
466
|
+
autoComplete,
|
|
446
467
|
value,
|
|
447
468
|
rows,
|
|
448
469
|
disabled,
|
|
470
|
+
required,
|
|
449
471
|
onChange,
|
|
472
|
+
onBlur,
|
|
473
|
+
onKeyDown,
|
|
474
|
+
"aria-label": ariaLabel,
|
|
475
|
+
"aria-invalid": error ? true : void 0,
|
|
476
|
+
"aria-describedby": error ? errorId : void 0,
|
|
450
477
|
className: textarea()
|
|
451
478
|
}
|
|
452
479
|
),
|
|
453
|
-
error && /* @__PURE__ */ jsx7(Error, { message: error })
|
|
480
|
+
error && /* @__PURE__ */ jsx7(Error, { id: errorId, message: error })
|
|
454
481
|
] });
|
|
455
482
|
};
|
|
456
483
|
|