@estiva-app/ui 0.3.0 → 0.4.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/dist/Field.d.ts +32 -5
- package/dist/Field.d.ts.map +1 -1
- package/dist/TextInput.d.ts.map +1 -1
- package/dist/Textarea.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -13
- package/dist/index.js.map +3 -3
- package/package.json +5 -1
- package/src/Field.test.tsx +117 -0
- package/src/Field.tsx +61 -7
- package/src/TextInput.tsx +5 -1
- package/src/Textarea.tsx +5 -1
- package/src/index.ts +1 -1
package/dist/Field.d.ts
CHANGED
|
@@ -1,13 +1,40 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* The id a surrounding `Field` wants this control to have, falling back to the
|
|
4
|
+
* caller's own outside one.
|
|
5
|
+
*
|
|
6
|
+
* **Inside a Field, the Field wins**, which is the opposite of what I wrote
|
|
7
|
+
* first and the test caught within the minute. Letting a control's own `id`
|
|
8
|
+
* take precedence leaves the label's `htmlFor` pointing at the id the Field
|
|
9
|
+
* generated and the control answering to a different one — which is this
|
|
10
|
+
* ticket's defect exactly, reproduced by the fix for it, and it fails with the
|
|
11
|
+
* same message: *"Found a label with the text of: Title, however no form
|
|
12
|
+
* control was found associated to that label."*
|
|
13
|
+
*
|
|
14
|
+
* A caller who needs to choose the id names it on the Field (`htmlFor`), which
|
|
15
|
+
* is the one place that can set both halves. Outside a Field there is nothing
|
|
16
|
+
* to disagree with, so the caller's id is used.
|
|
17
|
+
*/
|
|
18
|
+
export declare function useFieldControlId(ownId?: string): string | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Peek's Field (2026-08-28): a label over a control, 8px apart, with a red
|
|
21
|
+
* asterisk when required. The label is the `input-label` type token as a plain
|
|
22
|
+
* class, never merged.
|
|
23
|
+
*
|
|
24
|
+
* The label names its control (SHA-17). It did not, and the control was a
|
|
25
|
+
* *sibling* of the label with no `htmlFor`, so there was neither an explicit
|
|
26
|
+
* nor an implicit association: a screen reader announced an unlabelled edit
|
|
27
|
+
* box and clicking the label focused nothing.
|
|
6
28
|
*/
|
|
7
29
|
export interface FieldProps {
|
|
8
30
|
label: string;
|
|
9
31
|
required?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Override the generated id. Only needed when something outside has to name
|
|
34
|
+
* the control — an `aria-describedby` elsewhere, or a form library.
|
|
35
|
+
*/
|
|
36
|
+
htmlFor?: string;
|
|
10
37
|
children: ReactNode;
|
|
11
38
|
}
|
|
12
|
-
export declare function Field({ label, required, children }: FieldProps): import("react").JSX.Element;
|
|
39
|
+
export declare function Field({ label, required, htmlFor, children }: FieldProps): import("react").JSX.Element;
|
|
13
40
|
//# sourceMappingURL=Field.d.ts.map
|
package/dist/Field.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Field.d.ts","sourceRoot":"","sources":["../src/Field.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"Field.d.ts","sourceRoot":"","sources":["../src/Field.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAoBxE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGpE;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED,wBAAgB,KAAK,CAAC,EAAE,KAAK,EAAE,QAAgB,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,UAAU,+BAe/E"}
|
package/dist/TextInput.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextInput.d.ts","sourceRoot":"","sources":["../src/TextInput.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"TextInput.d.ts","sourceRoot":"","sources":["../src/TextInput.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAI5D;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,mBAAmB,CAAC,gBAAgB,CAAC,CAAA;AAElE,eAAO,MAAM,SAAS,6GAmBpB,CAAA"}
|
package/dist/Textarea.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Textarea.d.ts","sourceRoot":"","sources":["../src/Textarea.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,sBAAsB,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"Textarea.d.ts","sourceRoot":"","sources":["../src/Textarea.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,sBAAsB,EAAE,MAAM,OAAO,CAAA;AAI/D;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,sBAAsB,CAAC,mBAAmB,CAAC,CAAA;AAEvE,eAAO,MAAM,QAAQ,+GAkBnB,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export { ChipInput, InputChip, type ChipInputOption, type ChipInputProps, type I
|
|
|
20
20
|
export { IconButton, type IconButtonProps, type IconButtonVariant } from './IconButton';
|
|
21
21
|
export { IdentityMenu, IdentityPanel, type Identity, type IdentityMenuProps, type IdentityPanelProps } from './IdentityMenu';
|
|
22
22
|
export { Tooltip, WithTooltip, type TooltipProps, type WithTooltipProps } from './Tooltip';
|
|
23
|
-
export { Field, type FieldProps } from './Field';
|
|
23
|
+
export { Field, useFieldControlId, type FieldProps } from './Field';
|
|
24
24
|
export { Select, type SelectOption, type SelectProps } from './Select';
|
|
25
25
|
export { TextInput, type TextInputProps } from './TextInput';
|
|
26
26
|
export { Textarea, type TextareaProps } from './Textarea';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AACxE,OAAO,EAAE,WAAW,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC1F,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAA;AACpE,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAA;AACxF,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAA;AACzD,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAA;AAClH,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAA;AACvF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAC5H,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAC1F,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -748,20 +748,35 @@ function Note({ children }) {
|
|
|
748
748
|
}
|
|
749
749
|
|
|
750
750
|
// src/Field.tsx
|
|
751
|
+
import { createContext, useContext, useId } from "react";
|
|
751
752
|
import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
752
|
-
|
|
753
|
+
var FieldControlIdContext = createContext(void 0);
|
|
754
|
+
function useFieldControlId(ownId) {
|
|
755
|
+
const fromField = useContext(FieldControlIdContext);
|
|
756
|
+
return fromField ?? ownId;
|
|
757
|
+
}
|
|
758
|
+
function Field({ label, required = false, htmlFor, children }) {
|
|
759
|
+
const generated = useId();
|
|
760
|
+
const id = htmlFor ?? generated;
|
|
753
761
|
return /* @__PURE__ */ jsxs11("div", { className: "flex flex-col gap-2", children: [
|
|
754
|
-
/* @__PURE__ */ jsxs11(
|
|
755
|
-
label,
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
762
|
+
/* @__PURE__ */ jsxs11(
|
|
763
|
+
"label",
|
|
764
|
+
{
|
|
765
|
+
htmlFor: id,
|
|
766
|
+
className: `text-input-label text-text-primary${required ? " flex items-center" : ""}`,
|
|
767
|
+
children: [
|
|
768
|
+
label,
|
|
769
|
+
required && /* @__PURE__ */ jsx17("span", { className: "text-error-default ml-0.5", children: "*" })
|
|
770
|
+
]
|
|
771
|
+
}
|
|
772
|
+
),
|
|
773
|
+
/* @__PURE__ */ jsx17(FieldControlIdContext.Provider, { value: id, children })
|
|
759
774
|
] });
|
|
760
775
|
}
|
|
761
776
|
|
|
762
777
|
// src/Select.tsx
|
|
763
778
|
import { IconCheck as IconCheck2, IconChevronDown as IconChevronDown2 } from "@tabler/icons-react";
|
|
764
|
-
import { useCallback as useCallback2, useEffect as useEffect3, useId, useLayoutEffect as useLayoutEffect3, useMemo as useMemo2, useRef as useRef4, useState as useState5 } from "react";
|
|
779
|
+
import { useCallback as useCallback2, useEffect as useEffect3, useId as useId2, useLayoutEffect as useLayoutEffect3, useMemo as useMemo2, useRef as useRef4, useState as useState5 } from "react";
|
|
765
780
|
import { createPortal as createPortal4 } from "react-dom";
|
|
766
781
|
import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
767
782
|
function fitMenu({
|
|
@@ -780,7 +795,7 @@ function fitMenu({
|
|
|
780
795
|
return openUp ? { left, bottom: viewport.height - anchor.top + GAP2, maxHeight } : { left, top: anchor.bottom + GAP2, maxHeight };
|
|
781
796
|
}
|
|
782
797
|
function Select({ value, onChange, options, size = "default", ariaLabel, placeholder = "Select\u2026", disabled, className }) {
|
|
783
|
-
const id =
|
|
798
|
+
const id = useId2();
|
|
784
799
|
const triggerRef = useRef4(null);
|
|
785
800
|
const menuRef = useRef4(null);
|
|
786
801
|
const [rect, setRect] = useState5(null);
|
|
@@ -974,11 +989,13 @@ function Select({ value, onChange, options, size = "default", ariaLabel, placeho
|
|
|
974
989
|
// src/TextInput.tsx
|
|
975
990
|
import { forwardRef } from "react";
|
|
976
991
|
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
977
|
-
var TextInput = forwardRef(function TextInput2({ className, type = "text", ...props }, ref) {
|
|
992
|
+
var TextInput = forwardRef(function TextInput2({ className, type = "text", id, ...props }, ref) {
|
|
993
|
+
const controlId = useFieldControlId(id);
|
|
978
994
|
return /* @__PURE__ */ jsx19(
|
|
979
995
|
"input",
|
|
980
996
|
{
|
|
981
997
|
ref,
|
|
998
|
+
id: controlId,
|
|
982
999
|
type,
|
|
983
1000
|
className: cn(
|
|
984
1001
|
"bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2",
|
|
@@ -996,11 +1013,13 @@ var TextInput = forwardRef(function TextInput2({ className, type = "text", ...pr
|
|
|
996
1013
|
// src/Textarea.tsx
|
|
997
1014
|
import { forwardRef as forwardRef2 } from "react";
|
|
998
1015
|
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
999
|
-
var Textarea = forwardRef2(function Textarea2({ className, ...props }, ref) {
|
|
1016
|
+
var Textarea = forwardRef2(function Textarea2({ className, id, ...props }, ref) {
|
|
1017
|
+
const controlId = useFieldControlId(id);
|
|
1000
1018
|
return /* @__PURE__ */ jsx20(
|
|
1001
1019
|
"textarea",
|
|
1002
1020
|
{
|
|
1003
1021
|
ref,
|
|
1022
|
+
id: controlId,
|
|
1004
1023
|
className: cn(
|
|
1005
1024
|
"bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2",
|
|
1006
1025
|
"text-[14px] leading-[1.4] font-normal text-text-primary placeholder:text-text-muted",
|
|
@@ -1479,7 +1498,7 @@ function Tabs({ tabs, active, onChange, size = "default", className }) {
|
|
|
1479
1498
|
}
|
|
1480
1499
|
|
|
1481
1500
|
// src/Toast.tsx
|
|
1482
|
-
import { createContext, useCallback as useCallback4, useContext, useEffect as useEffect6, useMemo as useMemo3, useState as useState10 } from "react";
|
|
1501
|
+
import { createContext as createContext2, useCallback as useCallback4, useContext as useContext2, useEffect as useEffect6, useMemo as useMemo3, useState as useState10 } from "react";
|
|
1483
1502
|
import { createPortal as createPortal6 } from "react-dom";
|
|
1484
1503
|
import { IconCircleCheck } from "@tabler/icons-react";
|
|
1485
1504
|
import { jsx as jsx35, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
@@ -1533,7 +1552,7 @@ function Toast({ label, type = "neutral", leadingIcon = true, actionLabel, onAct
|
|
|
1533
1552
|
}
|
|
1534
1553
|
);
|
|
1535
1554
|
}
|
|
1536
|
-
var ToastContext =
|
|
1555
|
+
var ToastContext = createContext2(null);
|
|
1537
1556
|
var toastSeq = 0;
|
|
1538
1557
|
function ToastProvider({ children }) {
|
|
1539
1558
|
const [toast, setToast] = useState10(null);
|
|
@@ -1575,7 +1594,7 @@ function ToastProvider({ children }) {
|
|
|
1575
1594
|
] });
|
|
1576
1595
|
}
|
|
1577
1596
|
function useToast() {
|
|
1578
|
-
const ctx =
|
|
1597
|
+
const ctx = useContext2(ToastContext);
|
|
1579
1598
|
if (!ctx) throw new Error("useToast must be used within ToastProvider");
|
|
1580
1599
|
return ctx;
|
|
1581
1600
|
}
|
|
@@ -1629,6 +1648,7 @@ export {
|
|
|
1629
1648
|
cn,
|
|
1630
1649
|
hueFor,
|
|
1631
1650
|
initialsFor,
|
|
1651
|
+
useFieldControlId,
|
|
1632
1652
|
useToast
|
|
1633
1653
|
};
|
|
1634
1654
|
//# sourceMappingURL=index.js.map
|