@academix-admin/forms 0.1.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/index.cjs +324 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +229 -0
- package/dist/index.d.ts +229 -0
- package/dist/index.js +319 -0
- package/dist/index.js.map +1 -0
- package/package.json +71 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TextInput — a Flutter-idiom text field for academix-web (Workstream F-next).
|
|
5
|
+
*
|
|
6
|
+
* Dogfood stage: built app-side first (hot-reload), to be EXTRACTED to
|
|
7
|
+
* `@academix-admin/text-input` once proven across the auth/edit/quiz/footer routes.
|
|
8
|
+
*
|
|
9
|
+
* Model (Flutter `TextField` + `InputDecoration`): decoration props (label, hint,
|
|
10
|
+
* prefix, suffix, helperText, obscureText, keyboardType, counter, …) describe the
|
|
11
|
+
* field; a `classNames` slot-map + `styles` let a route pin its EXACT existing look.
|
|
12
|
+
*
|
|
13
|
+
* Styling is HYBRID:
|
|
14
|
+
* - Per slot, a caller-supplied class in `classNames` REPLACES the built-in default
|
|
15
|
+
* for that slot (so a route passes its own CSS-module classes and reproduces its
|
|
16
|
+
* current pixels exactly — including descendant-scoped theme rules like
|
|
17
|
+
* `.container_light .input`, which keep working because the <input> stays inside
|
|
18
|
+
* the route's themed root).
|
|
19
|
+
* - Slots with no caller class fall back to a sensible built-in default, so brand
|
|
20
|
+
* new pages get a usable field for free.
|
|
21
|
+
*
|
|
22
|
+
* Coordination: a plain native <input> already drives the keyboard/nav via the
|
|
23
|
+
* document-level `focusin` listener in navigation-stack@0.6.2 (`data-ax-keyboard`).
|
|
24
|
+
* The structured `FocusCoordinator` (region-aware) is the next step and will be wired
|
|
25
|
+
* here without changing this public API.
|
|
26
|
+
*/
|
|
27
|
+
type TextInputStatus = 'default' | 'error' | 'valid' | 'info';
|
|
28
|
+
type TextInputKeyboard = 'text' | 'numeric' | 'decimal' | 'tel' | 'email' | 'url' | 'search';
|
|
29
|
+
interface TextInputClassNames {
|
|
30
|
+
/** outer wrapper (Flutter: the field's column). Default: formGroup-like. */
|
|
31
|
+
root?: string;
|
|
32
|
+
label?: string;
|
|
33
|
+
/** wrapper around input + adornments; only rendered when an adornment/counter exists. */
|
|
34
|
+
field?: string;
|
|
35
|
+
input?: string;
|
|
36
|
+
prefix?: string;
|
|
37
|
+
suffix?: string;
|
|
38
|
+
/** the secure (eye) toggle button. */
|
|
39
|
+
toggle?: string;
|
|
40
|
+
/** helper / error / valid text below the field. */
|
|
41
|
+
helper?: string;
|
|
42
|
+
counter?: string;
|
|
43
|
+
}
|
|
44
|
+
interface TextInputProps {
|
|
45
|
+
value?: string;
|
|
46
|
+
defaultValue?: string;
|
|
47
|
+
/** Flutter `onChanged(String)` — receives the new value (and the raw event). */
|
|
48
|
+
onChange?: (value: string, event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
49
|
+
label?: React.ReactNode;
|
|
50
|
+
/** placeholder (Flutter `hintText`). */
|
|
51
|
+
hint?: string;
|
|
52
|
+
/** leading adornment inside the box (e.g. `@`, `+`). */
|
|
53
|
+
prefix?: React.ReactNode;
|
|
54
|
+
/** trailing adornment (rendered before the secure toggle if both present). */
|
|
55
|
+
suffix?: React.ReactNode;
|
|
56
|
+
/** helper / error / valid message below the field. */
|
|
57
|
+
helperText?: React.ReactNode;
|
|
58
|
+
/** drives helper color + `aria-invalid`. */
|
|
59
|
+
status?: TextInputStatus;
|
|
60
|
+
/** show a `len/maxLength` counter; or a custom renderer. */
|
|
61
|
+
counter?: boolean | ((len: number, maxLength?: number) => React.ReactNode);
|
|
62
|
+
type?: React.HTMLInputTypeAttribute;
|
|
63
|
+
/** password field (Flutter `obscureText`). */
|
|
64
|
+
obscureText?: boolean;
|
|
65
|
+
/** render an eye button to toggle obscure on/off. Implies obscureText default. */
|
|
66
|
+
secureToggle?: boolean;
|
|
67
|
+
/** called when the secure (eye) toggle flips; receives the new revealed state. */
|
|
68
|
+
onSecureToggle?: (revealed: boolean) => void;
|
|
69
|
+
/** maps to `inputMode` (Flutter `keyboardType`). */
|
|
70
|
+
keyboardType?: TextInputKeyboard;
|
|
71
|
+
/** transform typed text (e.g. code fields). */
|
|
72
|
+
transform?: 'none' | 'uppercase' | 'lowercase';
|
|
73
|
+
maxLength?: number;
|
|
74
|
+
pattern?: string;
|
|
75
|
+
autoFocus?: boolean;
|
|
76
|
+
disabled?: boolean;
|
|
77
|
+
required?: boolean;
|
|
78
|
+
readOnly?: boolean;
|
|
79
|
+
name?: string;
|
|
80
|
+
id?: string;
|
|
81
|
+
autoComplete?: string;
|
|
82
|
+
autoCapitalize?: string;
|
|
83
|
+
enterKeyHint?: React.HTMLAttributes<HTMLInputElement>['enterKeyHint'];
|
|
84
|
+
inputMode?: React.HTMLAttributes<HTMLInputElement>['inputMode'];
|
|
85
|
+
classNames?: TextInputClassNames;
|
|
86
|
+
/** shortcut for `classNames.root`. */
|
|
87
|
+
className?: string;
|
|
88
|
+
style?: React.CSSProperties;
|
|
89
|
+
/**
|
|
90
|
+
* Theme for the BUILT-IN default styles only. When set, the default class for a slot
|
|
91
|
+
* is composed as `slot` + `slot_theme` (e.g. `input` + `input_dark`) — the same
|
|
92
|
+
* convention as an `applyTheme` helper, but self-contained: this component depends on
|
|
93
|
+
* nothing but React and its own CSS. Ignored for any slot the caller styles via
|
|
94
|
+
* `classNames` (those already carry their own theming). Feed it from whatever theme
|
|
95
|
+
* source you use — in academix that's `useTheme()`; any other developer passes a plain
|
|
96
|
+
* `'light' | 'dark'`, or omits it and styles via `classNames`.
|
|
97
|
+
*/
|
|
98
|
+
theme?: 'light' | 'dark';
|
|
99
|
+
onFocus?: (e: React.FocusEvent<HTMLInputElement>) => void;
|
|
100
|
+
onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
|
|
101
|
+
onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
|
|
102
|
+
onKeyPress?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
|
|
103
|
+
/** spread onto the underlying <input> for anything not covered above. */
|
|
104
|
+
inputProps?: Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'defaultValue' | 'onChange' | 'className'>;
|
|
105
|
+
}
|
|
106
|
+
declare const TextInput: React.ForwardRefExoticComponent<TextInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* SelectField — a Flutter `DropdownButtonFormField`-idiom trigger (Workstream F-next
|
|
110
|
+
* form family). It renders only the FIELD (label + optional description + a button that
|
|
111
|
+
* shows the selected value or a placeholder); the actual picker (a SelectionViewer /
|
|
112
|
+
* bottom-sheet) stays owned by the page and is opened via `onOpen`.
|
|
113
|
+
*
|
|
114
|
+
* Same contract as TextInput: HYBRID styling (a `classNames` slot class REPLACES the
|
|
115
|
+
* built-in default for that slot) + a self-contained `theme` prop (no app coupling).
|
|
116
|
+
*/
|
|
117
|
+
type SelectFieldStatus = 'default' | 'error' | 'valid' | 'info';
|
|
118
|
+
interface SelectFieldClassNames {
|
|
119
|
+
root?: string;
|
|
120
|
+
label?: string;
|
|
121
|
+
description?: string;
|
|
122
|
+
/** the trigger button. */
|
|
123
|
+
control?: string;
|
|
124
|
+
/** trailing adornment wrapper (e.g. a chevron). */
|
|
125
|
+
trailing?: string;
|
|
126
|
+
helper?: string;
|
|
127
|
+
}
|
|
128
|
+
interface SelectFieldProps {
|
|
129
|
+
label?: React.ReactNode;
|
|
130
|
+
description?: React.ReactNode;
|
|
131
|
+
/** the display value; when empty the `placeholder` shows. */
|
|
132
|
+
value?: React.ReactNode;
|
|
133
|
+
placeholder?: React.ReactNode;
|
|
134
|
+
/** open the picker (SelectionViewer). */
|
|
135
|
+
onOpen?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
136
|
+
/** trailing content inside the control (e.g. a chevron icon). */
|
|
137
|
+
trailing?: React.ReactNode;
|
|
138
|
+
disabled?: boolean;
|
|
139
|
+
helperText?: React.ReactNode;
|
|
140
|
+
status?: SelectFieldStatus;
|
|
141
|
+
id?: string;
|
|
142
|
+
name?: string;
|
|
143
|
+
classNames?: SelectFieldClassNames;
|
|
144
|
+
className?: string;
|
|
145
|
+
style?: React.CSSProperties;
|
|
146
|
+
theme?: 'light' | 'dark';
|
|
147
|
+
}
|
|
148
|
+
declare function SelectField({ label, description, value, placeholder, onOpen, trailing, disabled, helperText, status, id, name, classNames, className, style, theme, }: SelectFieldProps): React.JSX.Element;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Checkbox — a Flutter `Checkbox`/`CheckboxListTile`-idiom control (form family). A
|
|
152
|
+
* hidden native <input type=checkbox> drives a custom box + check icon and a text label,
|
|
153
|
+
* so it stays accessible and form-submittable while looking however the page wants.
|
|
154
|
+
*
|
|
155
|
+
* HYBRID styling + self-contained `theme` prop, same contract as TextInput/SelectField.
|
|
156
|
+
*/
|
|
157
|
+
interface CheckboxClassNames {
|
|
158
|
+
/** the wrapping <label>. */
|
|
159
|
+
label?: string;
|
|
160
|
+
/** the (visually-hidden) native input. */
|
|
161
|
+
input?: string;
|
|
162
|
+
/** the custom box that shows the check. */
|
|
163
|
+
box?: string;
|
|
164
|
+
/** the check icon inside the box. */
|
|
165
|
+
icon?: string;
|
|
166
|
+
/** the text beside the box. */
|
|
167
|
+
text?: string;
|
|
168
|
+
}
|
|
169
|
+
interface CheckboxProps {
|
|
170
|
+
checked?: boolean;
|
|
171
|
+
defaultChecked?: boolean;
|
|
172
|
+
/** Flutter `onChanged(bool)`. */
|
|
173
|
+
onChange?: (checked: boolean, event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
174
|
+
/** label content beside the box. */
|
|
175
|
+
children?: React.ReactNode;
|
|
176
|
+
disabled?: boolean;
|
|
177
|
+
required?: boolean;
|
|
178
|
+
name?: string;
|
|
179
|
+
id?: string;
|
|
180
|
+
value?: string;
|
|
181
|
+
/** custom check icon (defaults to a checkmark). */
|
|
182
|
+
icon?: React.ReactNode;
|
|
183
|
+
classNames?: CheckboxClassNames;
|
|
184
|
+
className?: string;
|
|
185
|
+
style?: React.CSSProperties;
|
|
186
|
+
theme?: 'light' | 'dark';
|
|
187
|
+
}
|
|
188
|
+
declare function Checkbox({ checked, defaultChecked, onChange, children, disabled, required, name, id, value, icon, classNames, className, style, theme, }: CheckboxProps): React.JSX.Element;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* RadioGroup — a Flutter `Radio`/`RadioListTile` group (form family). Renders a set of
|
|
192
|
+
* selectable "cards": each is a <label> wrapping a native <input type=radio> plus content,
|
|
193
|
+
* with a `selected` class on the active one. Accessible + form-submittable.
|
|
194
|
+
*
|
|
195
|
+
* HYBRID styling + self-contained `theme` prop, same contract as the rest of the family.
|
|
196
|
+
*/
|
|
197
|
+
interface RadioOption<V extends string = string> {
|
|
198
|
+
value: V;
|
|
199
|
+
label: React.ReactNode;
|
|
200
|
+
disabled?: boolean;
|
|
201
|
+
}
|
|
202
|
+
interface RadioGroupClassNames {
|
|
203
|
+
/** wrapper around all items. */
|
|
204
|
+
group?: string;
|
|
205
|
+
/** each item <label>. */
|
|
206
|
+
item?: string;
|
|
207
|
+
/** appended to the selected item's <label>. */
|
|
208
|
+
itemSelected?: string;
|
|
209
|
+
/** the native radio input. */
|
|
210
|
+
input?: string;
|
|
211
|
+
/** wrapper around an item's label content. */
|
|
212
|
+
content?: string;
|
|
213
|
+
/** the item's text. */
|
|
214
|
+
text?: string;
|
|
215
|
+
}
|
|
216
|
+
interface RadioGroupProps<V extends string = string> {
|
|
217
|
+
name: string;
|
|
218
|
+
value?: V;
|
|
219
|
+
onChange?: (value: V) => void;
|
|
220
|
+
options: RadioOption<V>[];
|
|
221
|
+
disabled?: boolean;
|
|
222
|
+
classNames?: RadioGroupClassNames;
|
|
223
|
+
className?: string;
|
|
224
|
+
style?: React.CSSProperties;
|
|
225
|
+
theme?: 'light' | 'dark';
|
|
226
|
+
}
|
|
227
|
+
declare function RadioGroup<V extends string = string>({ name, value, onChange, options, disabled, classNames, className, style, theme, }: RadioGroupProps<V>): React.JSX.Element;
|
|
228
|
+
|
|
229
|
+
export { Checkbox, type CheckboxClassNames, type CheckboxProps, RadioGroup, type RadioGroupClassNames, type RadioGroupProps, type RadioOption, SelectField, type SelectFieldClassNames, type SelectFieldProps, type SelectFieldStatus, TextInput, type TextInputClassNames, type TextInputKeyboard, type TextInputProps, type TextInputStatus };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { forwardRef, useId, useState, useCallback } from 'react';
|
|
3
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
// src/TextInput/TextInput.tsx
|
|
6
|
+
var defaults = {};
|
|
7
|
+
var KEYBOARD_TO_INPUTMODE = {
|
|
8
|
+
text: "text",
|
|
9
|
+
numeric: "numeric",
|
|
10
|
+
decimal: "decimal",
|
|
11
|
+
tel: "tel",
|
|
12
|
+
email: "email",
|
|
13
|
+
url: "url",
|
|
14
|
+
search: "search"
|
|
15
|
+
};
|
|
16
|
+
function pickSlot(caller, name, theme) {
|
|
17
|
+
if (caller != null) return caller;
|
|
18
|
+
const base = defaults[name];
|
|
19
|
+
if (!base) return void 0;
|
|
20
|
+
const variant = theme ? defaults[`${name}_${theme}`] : void 0;
|
|
21
|
+
return variant ? `${base} ${variant}` : base;
|
|
22
|
+
}
|
|
23
|
+
var TextInput = forwardRef(function TextInput2({
|
|
24
|
+
value,
|
|
25
|
+
defaultValue,
|
|
26
|
+
onChange,
|
|
27
|
+
label,
|
|
28
|
+
hint,
|
|
29
|
+
prefix,
|
|
30
|
+
suffix,
|
|
31
|
+
helperText,
|
|
32
|
+
status = "default",
|
|
33
|
+
counter,
|
|
34
|
+
type = "text",
|
|
35
|
+
obscureText,
|
|
36
|
+
secureToggle,
|
|
37
|
+
onSecureToggle,
|
|
38
|
+
keyboardType,
|
|
39
|
+
transform = "none",
|
|
40
|
+
maxLength,
|
|
41
|
+
pattern,
|
|
42
|
+
autoFocus,
|
|
43
|
+
disabled,
|
|
44
|
+
required,
|
|
45
|
+
readOnly,
|
|
46
|
+
name,
|
|
47
|
+
id,
|
|
48
|
+
autoComplete,
|
|
49
|
+
autoCapitalize,
|
|
50
|
+
enterKeyHint,
|
|
51
|
+
inputMode,
|
|
52
|
+
classNames = {},
|
|
53
|
+
className,
|
|
54
|
+
style,
|
|
55
|
+
theme,
|
|
56
|
+
onFocus,
|
|
57
|
+
onBlur,
|
|
58
|
+
onKeyDown,
|
|
59
|
+
onKeyPress,
|
|
60
|
+
inputProps
|
|
61
|
+
}, ref) {
|
|
62
|
+
const reactId = useId();
|
|
63
|
+
const fieldId = id ?? `txt-${reactId}`;
|
|
64
|
+
const [reveal, setReveal] = useState(false);
|
|
65
|
+
const secure = obscureText ?? !!secureToggle;
|
|
66
|
+
const resolvedType = secure ? reveal ? "text" : "password" : type;
|
|
67
|
+
const handleChange = useCallback(
|
|
68
|
+
(e) => {
|
|
69
|
+
let v = e.target.value;
|
|
70
|
+
if (transform === "uppercase") v = v.toUpperCase();
|
|
71
|
+
else if (transform === "lowercase") v = v.toLowerCase();
|
|
72
|
+
if (v !== e.target.value) e.target.value = v;
|
|
73
|
+
onChange?.(v, e);
|
|
74
|
+
},
|
|
75
|
+
[transform, onChange]
|
|
76
|
+
);
|
|
77
|
+
const len = value?.length ?? 0;
|
|
78
|
+
const hasAdornment = !!prefix || !!suffix || secure && secureToggle || !!counter;
|
|
79
|
+
const statusClass = status === "error" ? defaults.statusError : status === "valid" ? defaults.statusValid : status === "info" ? defaults.statusInfo : void 0;
|
|
80
|
+
const inputEl = /* @__PURE__ */ jsx(
|
|
81
|
+
"input",
|
|
82
|
+
{
|
|
83
|
+
...inputProps,
|
|
84
|
+
ref,
|
|
85
|
+
id: fieldId,
|
|
86
|
+
name,
|
|
87
|
+
type: resolvedType,
|
|
88
|
+
value,
|
|
89
|
+
defaultValue,
|
|
90
|
+
onChange: handleChange,
|
|
91
|
+
placeholder: hint,
|
|
92
|
+
className: pickSlot(classNames.input, "input", theme),
|
|
93
|
+
disabled,
|
|
94
|
+
required,
|
|
95
|
+
readOnly,
|
|
96
|
+
autoFocus,
|
|
97
|
+
maxLength,
|
|
98
|
+
pattern,
|
|
99
|
+
autoComplete,
|
|
100
|
+
autoCapitalize,
|
|
101
|
+
enterKeyHint,
|
|
102
|
+
inputMode: inputMode ?? (keyboardType ? KEYBOARD_TO_INPUTMODE[keyboardType] : void 0),
|
|
103
|
+
"aria-invalid": status === "error" || inputProps?.["aria-invalid"] || void 0,
|
|
104
|
+
onFocus,
|
|
105
|
+
onBlur,
|
|
106
|
+
onKeyDown,
|
|
107
|
+
onKeyPress
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
return /* @__PURE__ */ jsxs("div", { className: pickSlot(className ?? classNames.root, "root", theme), style, children: [
|
|
111
|
+
label != null && /* @__PURE__ */ jsx("label", { htmlFor: fieldId, className: pickSlot(classNames.label, "label", theme), children: label }),
|
|
112
|
+
hasAdornment ? /* @__PURE__ */ jsxs("div", { className: pickSlot(classNames.field, "field", theme), children: [
|
|
113
|
+
prefix != null && /* @__PURE__ */ jsx("span", { className: pickSlot(classNames.prefix, "prefix", theme), children: prefix }),
|
|
114
|
+
inputEl,
|
|
115
|
+
suffix != null && /* @__PURE__ */ jsx("span", { className: pickSlot(classNames.suffix, "suffix", theme), children: suffix }),
|
|
116
|
+
secure && secureToggle && /* @__PURE__ */ jsx(
|
|
117
|
+
"button",
|
|
118
|
+
{
|
|
119
|
+
type: "button",
|
|
120
|
+
className: pickSlot(classNames.toggle, "toggle", theme),
|
|
121
|
+
onClick: () => {
|
|
122
|
+
setReveal((r) => !r);
|
|
123
|
+
onSecureToggle?.(!reveal);
|
|
124
|
+
},
|
|
125
|
+
"aria-label": reveal ? "Hide password" : "Show password",
|
|
126
|
+
tabIndex: -1,
|
|
127
|
+
children: reveal ? /* @__PURE__ */ jsx(EyeOpen, {}) : /* @__PURE__ */ jsx(EyeClosed, {})
|
|
128
|
+
}
|
|
129
|
+
),
|
|
130
|
+
counter && /* @__PURE__ */ jsx("div", { className: pickSlot(classNames.counter, "counter", theme), children: typeof counter === "function" ? counter(len, maxLength) : `${len}${maxLength ? `/${maxLength}` : ""}` })
|
|
131
|
+
] }) : inputEl,
|
|
132
|
+
helperText != null && /* @__PURE__ */ jsx(
|
|
133
|
+
"p",
|
|
134
|
+
{
|
|
135
|
+
className: (
|
|
136
|
+
// When a route supplies its own helper class it already carries the status
|
|
137
|
+
// color; only fall back to the default helper + status color otherwise.
|
|
138
|
+
classNames.helper ? classNames.helper : `${pickSlot(void 0, "helper", theme)}${statusClass ? ` ${statusClass}` : ""}`
|
|
139
|
+
),
|
|
140
|
+
children: helperText
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
] });
|
|
144
|
+
});
|
|
145
|
+
function EyeOpen() {
|
|
146
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": true, children: [
|
|
147
|
+
/* @__PURE__ */ jsx("path", { d: "M1 12C1 12 5 4 12 4C19 4 23 12 23 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
148
|
+
/* @__PURE__ */ jsx("path", { d: "M1 12C1 12 5 20 12 20C19 20 23 12 23 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
149
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
150
|
+
] });
|
|
151
|
+
}
|
|
152
|
+
function EyeClosed() {
|
|
153
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": true, children: [
|
|
154
|
+
/* @__PURE__ */ jsx("path", { d: "M10.5858 10.5858C10.2107 10.9609 10 11.4696 10 12C10 13.1046 10.8954 14 12 14C12.5304 14 13.0391 13.7893 13.4142 13.4142", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
155
|
+
/* @__PURE__ */ jsx("path", { d: "M17.6112 17.6112C16.0556 18.979 14.1364 19.7493 12.0001 19.7493C5.63647 19.7493 2.25011 12.3743 2.25011 12.3743C3.47011 10.1443 5.27761 8.35577 7.38911 7.13965", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
156
|
+
/* @__PURE__ */ jsx("path", { d: "M20.8892 6.00928C21.8292 6.78928 22.6732 7.70428 23.3892 8.72428C23.7502 9.23428 23.7502 9.91428 23.3892 10.4243C22.6732 11.4443 21.8292 12.3593 20.8892 13.1393", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
157
|
+
/* @__PURE__ */ jsx("path", { d: "M14.9318 6.00928C13.6618 5.38928 12.2818 5.02928 10.8188 5.00928C9.35585 4.98928 7.93185 5.30928 6.61185 5.88928", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
158
|
+
/* @__PURE__ */ jsx("path", { d: "M21 3L3 21", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
159
|
+
] });
|
|
160
|
+
}
|
|
161
|
+
var defaults2 = {};
|
|
162
|
+
function pickSlot2(caller, name, theme) {
|
|
163
|
+
if (caller != null) return caller;
|
|
164
|
+
const base = defaults2[name];
|
|
165
|
+
if (!base) return void 0;
|
|
166
|
+
const variant = theme ? defaults2[`${name}_${theme}`] : void 0;
|
|
167
|
+
return variant ? `${base} ${variant}` : base;
|
|
168
|
+
}
|
|
169
|
+
function SelectField({
|
|
170
|
+
label,
|
|
171
|
+
description,
|
|
172
|
+
value,
|
|
173
|
+
placeholder = "Select",
|
|
174
|
+
onOpen,
|
|
175
|
+
trailing,
|
|
176
|
+
disabled,
|
|
177
|
+
helperText,
|
|
178
|
+
status = "default",
|
|
179
|
+
id,
|
|
180
|
+
name,
|
|
181
|
+
classNames = {},
|
|
182
|
+
className,
|
|
183
|
+
style,
|
|
184
|
+
theme
|
|
185
|
+
}) {
|
|
186
|
+
const statusClass = status === "error" ? defaults2.statusError : status === "valid" ? defaults2.statusValid : status === "info" ? defaults2.statusInfo : void 0;
|
|
187
|
+
const hasValue = value != null && value !== "";
|
|
188
|
+
return /* @__PURE__ */ jsxs("div", { className: pickSlot2(className ?? classNames.root, "root", theme), style, children: [
|
|
189
|
+
label != null && /* @__PURE__ */ jsx("label", { htmlFor: id, className: pickSlot2(classNames.label, "label", theme), children: label }),
|
|
190
|
+
description != null && /* @__PURE__ */ jsx("p", { className: pickSlot2(classNames.description, "description", theme), children: description }),
|
|
191
|
+
/* @__PURE__ */ jsxs(
|
|
192
|
+
"button",
|
|
193
|
+
{
|
|
194
|
+
type: "button",
|
|
195
|
+
id,
|
|
196
|
+
name,
|
|
197
|
+
onClick: onOpen,
|
|
198
|
+
disabled,
|
|
199
|
+
"aria-invalid": status === "error" || void 0,
|
|
200
|
+
className: pickSlot2(classNames.control, "control", theme),
|
|
201
|
+
children: [
|
|
202
|
+
hasValue ? value : placeholder,
|
|
203
|
+
trailing != null && /* @__PURE__ */ jsx("span", { className: pickSlot2(classNames.trailing, "trailing", theme), children: trailing })
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
),
|
|
207
|
+
helperText != null && /* @__PURE__ */ jsx(
|
|
208
|
+
"p",
|
|
209
|
+
{
|
|
210
|
+
className: classNames.helper ? classNames.helper : `${pickSlot2(void 0, "helper", theme)}${statusClass ? ` ${statusClass}` : ""}`,
|
|
211
|
+
children: helperText
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
] });
|
|
215
|
+
}
|
|
216
|
+
var defaults3 = {};
|
|
217
|
+
function pickSlot3(caller, name, theme) {
|
|
218
|
+
if (caller != null) return caller;
|
|
219
|
+
const base = defaults3[name];
|
|
220
|
+
if (!base) return void 0;
|
|
221
|
+
const variant = theme ? defaults3[`${name}_${theme}`] : void 0;
|
|
222
|
+
return variant ? `${base} ${variant}` : base;
|
|
223
|
+
}
|
|
224
|
+
function Checkbox({
|
|
225
|
+
checked,
|
|
226
|
+
defaultChecked,
|
|
227
|
+
onChange,
|
|
228
|
+
children,
|
|
229
|
+
disabled,
|
|
230
|
+
required,
|
|
231
|
+
name,
|
|
232
|
+
id,
|
|
233
|
+
value,
|
|
234
|
+
icon,
|
|
235
|
+
classNames = {},
|
|
236
|
+
className,
|
|
237
|
+
style,
|
|
238
|
+
theme
|
|
239
|
+
}) {
|
|
240
|
+
const reactId = useId();
|
|
241
|
+
const fieldId = id ?? `chk-${reactId}`;
|
|
242
|
+
const isChecked = checked;
|
|
243
|
+
return /* @__PURE__ */ jsxs("label", { htmlFor: fieldId, className: pickSlot3(className ?? classNames.label, "label", theme), style, children: [
|
|
244
|
+
/* @__PURE__ */ jsx(
|
|
245
|
+
"input",
|
|
246
|
+
{
|
|
247
|
+
id: fieldId,
|
|
248
|
+
type: "checkbox",
|
|
249
|
+
name,
|
|
250
|
+
value,
|
|
251
|
+
checked,
|
|
252
|
+
defaultChecked,
|
|
253
|
+
onChange: (e) => onChange?.(e.target.checked, e),
|
|
254
|
+
disabled,
|
|
255
|
+
required,
|
|
256
|
+
className: pickSlot3(classNames.input, "input", theme)
|
|
257
|
+
}
|
|
258
|
+
),
|
|
259
|
+
/* @__PURE__ */ jsx("span", { className: pickSlot3(classNames.box, "box", theme), "aria-hidden": true, children: isChecked && (icon ?? /* @__PURE__ */ jsx("svg", { className: pickSlot3(classNames.icon, "icon", theme), viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx(
|
|
260
|
+
"path",
|
|
261
|
+
{
|
|
262
|
+
d: "M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z",
|
|
263
|
+
fill: "currentColor"
|
|
264
|
+
}
|
|
265
|
+
) })) }),
|
|
266
|
+
children != null && /* @__PURE__ */ jsx("span", { className: pickSlot3(classNames.text, "text", theme), children })
|
|
267
|
+
] });
|
|
268
|
+
}
|
|
269
|
+
var defaults4 = {};
|
|
270
|
+
function pickSlot4(caller, name, theme) {
|
|
271
|
+
if (caller != null) return caller;
|
|
272
|
+
const base = defaults4[name];
|
|
273
|
+
if (!base) return void 0;
|
|
274
|
+
const variant = theme ? defaults4[`${name}_${theme}`] : void 0;
|
|
275
|
+
return variant ? `${base} ${variant}` : base;
|
|
276
|
+
}
|
|
277
|
+
function RadioGroup({
|
|
278
|
+
name,
|
|
279
|
+
value,
|
|
280
|
+
onChange,
|
|
281
|
+
options,
|
|
282
|
+
disabled,
|
|
283
|
+
classNames = {},
|
|
284
|
+
className,
|
|
285
|
+
style,
|
|
286
|
+
theme
|
|
287
|
+
}) {
|
|
288
|
+
const itemBase = pickSlot4(classNames.item, "item", theme);
|
|
289
|
+
const itemSelected = pickSlot4(classNames.itemSelected, "itemSelected", theme);
|
|
290
|
+
return /* @__PURE__ */ jsx("div", { className: pickSlot4(className ?? classNames.group, "group", theme), style, role: "radiogroup", children: options.map((opt) => {
|
|
291
|
+
const selected = value === opt.value;
|
|
292
|
+
return /* @__PURE__ */ jsxs(
|
|
293
|
+
"label",
|
|
294
|
+
{
|
|
295
|
+
className: `${itemBase ?? ""}${selected && itemSelected ? ` ${itemSelected}` : ""}`.trim() || void 0,
|
|
296
|
+
children: [
|
|
297
|
+
/* @__PURE__ */ jsx(
|
|
298
|
+
"input",
|
|
299
|
+
{
|
|
300
|
+
type: "radio",
|
|
301
|
+
name,
|
|
302
|
+
value: opt.value,
|
|
303
|
+
checked: selected,
|
|
304
|
+
onChange: () => onChange?.(opt.value),
|
|
305
|
+
disabled: disabled || opt.disabled,
|
|
306
|
+
className: pickSlot4(classNames.input, "input", theme)
|
|
307
|
+
}
|
|
308
|
+
),
|
|
309
|
+
/* @__PURE__ */ jsx("div", { className: pickSlot4(classNames.content, "content", theme), children: /* @__PURE__ */ jsx("span", { className: pickSlot4(classNames.text, "text", theme), children: opt.label }) })
|
|
310
|
+
]
|
|
311
|
+
},
|
|
312
|
+
opt.value
|
|
313
|
+
);
|
|
314
|
+
}) });
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export { Checkbox, RadioGroup, SelectField, TextInput };
|
|
318
|
+
//# sourceMappingURL=index.js.map
|
|
319
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/TextInput/TextInput.tsx","../src/SelectField/SelectField.tsx","../src/Checkbox/Checkbox.tsx","../src/RadioGroup/RadioGroup.tsx"],"names":["TextInput","defaults","pickSlot","jsxs","jsx","useId"],"mappings":"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","file":"index.js","sourcesContent":["'use client';\n\nimport React, { forwardRef, useCallback, useId, useState } from 'react';\n// Headless: no bundled default styles — style every slot via `classNames`.\nconst defaults: Record<string, string> = {};\n\n/**\n * TextInput — a Flutter-idiom text field for academix-web (Workstream F-next).\n *\n * Dogfood stage: built app-side first (hot-reload), to be EXTRACTED to\n * `@academix-admin/text-input` once proven across the auth/edit/quiz/footer routes.\n *\n * Model (Flutter `TextField` + `InputDecoration`): decoration props (label, hint,\n * prefix, suffix, helperText, obscureText, keyboardType, counter, …) describe the\n * field; a `classNames` slot-map + `styles` let a route pin its EXACT existing look.\n *\n * Styling is HYBRID:\n * - Per slot, a caller-supplied class in `classNames` REPLACES the built-in default\n * for that slot (so a route passes its own CSS-module classes and reproduces its\n * current pixels exactly — including descendant-scoped theme rules like\n * `.container_light .input`, which keep working because the <input> stays inside\n * the route's themed root).\n * - Slots with no caller class fall back to a sensible built-in default, so brand\n * new pages get a usable field for free.\n *\n * Coordination: a plain native <input> already drives the keyboard/nav via the\n * document-level `focusin` listener in navigation-stack@0.6.2 (`data-ax-keyboard`).\n * The structured `FocusCoordinator` (region-aware) is the next step and will be wired\n * here without changing this public API.\n */\n\nexport type TextInputStatus = 'default' | 'error' | 'valid' | 'info';\n\nexport type TextInputKeyboard =\n | 'text' | 'numeric' | 'decimal' | 'tel' | 'email' | 'url' | 'search';\n\nconst KEYBOARD_TO_INPUTMODE: Record<TextInputKeyboard, React.HTMLAttributes<HTMLInputElement>['inputMode']> = {\n text: 'text', numeric: 'numeric', decimal: 'decimal',\n tel: 'tel', email: 'email', url: 'url', search: 'search',\n};\n\nexport interface TextInputClassNames {\n /** outer wrapper (Flutter: the field's column). Default: formGroup-like. */\n root?: string;\n label?: string;\n /** wrapper around input + adornments; only rendered when an adornment/counter exists. */\n field?: string;\n input?: string;\n prefix?: string;\n suffix?: string;\n /** the secure (eye) toggle button. */\n toggle?: string;\n /** helper / error / valid text below the field. */\n helper?: string;\n counter?: string;\n}\n\nexport interface TextInputProps {\n // ---- value (controlled or uncontrolled) ----\n value?: string;\n defaultValue?: string;\n /** Flutter `onChanged(String)` — receives the new value (and the raw event). */\n onChange?: (value: string, event: React.ChangeEvent<HTMLInputElement>) => void;\n\n // ---- decoration ----\n label?: React.ReactNode;\n /** placeholder (Flutter `hintText`). */\n hint?: string;\n /** leading adornment inside the box (e.g. `@`, `+`). */\n prefix?: React.ReactNode;\n /** trailing adornment (rendered before the secure toggle if both present). */\n suffix?: React.ReactNode;\n /** helper / error / valid message below the field. */\n helperText?: React.ReactNode;\n /** drives helper color + `aria-invalid`. */\n status?: TextInputStatus;\n /** show a `len/maxLength` counter; or a custom renderer. */\n counter?: boolean | ((len: number, maxLength?: number) => React.ReactNode);\n\n // ---- behavior ----\n type?: React.HTMLInputTypeAttribute;\n /** password field (Flutter `obscureText`). */\n obscureText?: boolean;\n /** render an eye button to toggle obscure on/off. Implies obscureText default. */\n secureToggle?: boolean;\n /** called when the secure (eye) toggle flips; receives the new revealed state. */\n onSecureToggle?: (revealed: boolean) => void;\n /** maps to `inputMode` (Flutter `keyboardType`). */\n keyboardType?: TextInputKeyboard;\n /** transform typed text (e.g. code fields). */\n transform?: 'none' | 'uppercase' | 'lowercase';\n maxLength?: number;\n pattern?: string;\n autoFocus?: boolean;\n disabled?: boolean;\n required?: boolean;\n readOnly?: boolean;\n name?: string;\n id?: string;\n autoComplete?: string;\n autoCapitalize?: string;\n enterKeyHint?: React.HTMLAttributes<HTMLInputElement>['enterKeyHint'];\n inputMode?: React.HTMLAttributes<HTMLInputElement>['inputMode'];\n\n // ---- styling ----\n classNames?: TextInputClassNames;\n /** shortcut for `classNames.root`. */\n className?: string;\n style?: React.CSSProperties;\n /**\n * Theme for the BUILT-IN default styles only. When set, the default class for a slot\n * is composed as `slot` + `slot_theme` (e.g. `input` + `input_dark`) — the same\n * convention as an `applyTheme` helper, but self-contained: this component depends on\n * nothing but React and its own CSS. Ignored for any slot the caller styles via\n * `classNames` (those already carry their own theming). Feed it from whatever theme\n * source you use — in academix that's `useTheme()`; any other developer passes a plain\n * `'light' | 'dark'`, or omits it and styles via `classNames`.\n */\n theme?: 'light' | 'dark';\n\n // ---- events / escape hatch ----\n onFocus?: (e: React.FocusEvent<HTMLInputElement>) => void;\n onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;\n onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;\n onKeyPress?: (e: React.KeyboardEvent<HTMLInputElement>) => void;\n /** spread onto the underlying <input> for anything not covered above. */\n inputProps?: Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n 'value' | 'defaultValue' | 'onChange' | 'className'\n >;\n}\n\n/**\n * Pick the class for a slot (hybrid rule): a caller-supplied class REPLACES the\n * built-in default. When falling back to the default and a `theme` is given, compose\n * `slot` + `slot_theme` exactly like ThemeContext's `applyTheme` — so a new page that\n * passes no classes still gets a themed field, consistent with the rest of the site.\n */\nfunction pickSlot(\n caller: string | undefined,\n name: keyof typeof defaults,\n theme?: 'light' | 'dark',\n): string | undefined {\n if (caller != null) return caller;\n const base = defaults[name];\n if (!base) return undefined;\n const variant = theme ? defaults[`${name}_${theme}` as keyof typeof defaults] : undefined;\n return variant ? `${base} ${variant}` : base;\n}\n\nexport const TextInput = forwardRef<HTMLInputElement, TextInputProps>(function TextInput(\n {\n value, defaultValue, onChange,\n label, hint, prefix, suffix, helperText, status = 'default', counter,\n type = 'text', obscureText, secureToggle, onSecureToggle, keyboardType, transform = 'none',\n maxLength, pattern, autoFocus, disabled, required, readOnly, name, id,\n autoComplete, autoCapitalize, enterKeyHint, inputMode,\n classNames = {}, className, style, theme,\n onFocus, onBlur, onKeyDown, onKeyPress, inputProps,\n },\n ref,\n) {\n const reactId = useId();\n const fieldId = id ?? `txt-${reactId}`;\n const [reveal, setReveal] = useState(false);\n\n const secure = obscureText ?? !!secureToggle;\n const resolvedType = secure ? (reveal ? 'text' : 'password') : type;\n\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n let v = e.target.value;\n if (transform === 'uppercase') v = v.toUpperCase();\n else if (transform === 'lowercase') v = v.toLowerCase();\n // reflect the transform back onto the controlled value\n if (v !== e.target.value) e.target.value = v;\n onChange?.(v, e);\n },\n [transform, onChange],\n );\n\n const len = value?.length ?? 0;\n const hasAdornment = !!prefix || !!suffix || (secure && secureToggle) || !!counter;\n const statusClass =\n status === 'error' ? defaults.statusError\n : status === 'valid' ? defaults.statusValid\n : status === 'info' ? defaults.statusInfo\n : undefined;\n\n const inputEl = (\n <input\n {...inputProps}\n ref={ref}\n id={fieldId}\n name={name}\n type={resolvedType}\n value={value}\n defaultValue={defaultValue}\n onChange={handleChange}\n placeholder={hint}\n className={pickSlot(classNames.input, 'input', theme)}\n disabled={disabled}\n required={required}\n readOnly={readOnly}\n autoFocus={autoFocus}\n maxLength={maxLength}\n pattern={pattern}\n autoComplete={autoComplete}\n autoCapitalize={autoCapitalize}\n enterKeyHint={enterKeyHint}\n inputMode={inputMode ?? (keyboardType ? KEYBOARD_TO_INPUTMODE[keyboardType] : undefined)}\n aria-invalid={status === 'error' || (inputProps?.['aria-invalid'] as boolean | undefined) || undefined}\n onFocus={onFocus}\n onBlur={onBlur}\n onKeyDown={onKeyDown}\n onKeyPress={onKeyPress}\n />\n );\n\n return (\n <div className={pickSlot(className ?? classNames.root, 'root', theme)} style={style}>\n {label != null && (\n <label htmlFor={fieldId} className={pickSlot(classNames.label, 'label', theme)}>\n {label}\n </label>\n )}\n\n {hasAdornment ? (\n <div className={pickSlot(classNames.field, 'field', theme)}>\n {prefix != null && (\n <span className={pickSlot(classNames.prefix, 'prefix', theme)}>{prefix}</span>\n )}\n {inputEl}\n {suffix != null && (\n <span className={pickSlot(classNames.suffix, 'suffix', theme)}>{suffix}</span>\n )}\n {secure && secureToggle && (\n <button\n type=\"button\"\n className={pickSlot(classNames.toggle, 'toggle', theme)}\n onClick={() => { setReveal((r) => !r); onSecureToggle?.(!reveal); }}\n aria-label={reveal ? 'Hide password' : 'Show password'}\n tabIndex={-1}\n >\n {reveal ? <EyeOpen /> : <EyeClosed />}\n </button>\n )}\n {counter && (\n <div className={pickSlot(classNames.counter, 'counter', theme)}>\n {typeof counter === 'function' ? counter(len, maxLength) : `${len}${maxLength ? `/${maxLength}` : ''}`}\n </div>\n )}\n </div>\n ) : (\n inputEl\n )}\n\n {helperText != null && (\n <p\n className={\n // When a route supplies its own helper class it already carries the status\n // color; only fall back to the default helper + status color otherwise.\n classNames.helper\n ? classNames.helper\n : `${pickSlot(undefined, 'helper', theme)}${statusClass ? ` ${statusClass}` : ''}`\n }\n >\n {helperText}\n </p>\n )}\n </div>\n );\n});\n\n/* Default eye icons (match login's SVGs closely; overridable by not using secureToggle\n and passing your own suffix). */\nfunction EyeOpen() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden>\n <path d=\"M1 12C1 12 5 4 12 4C19 4 23 12 23 12\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <path d=\"M1 12C1 12 5 20 12 20C19 20 23 12 23 12\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\nfunction EyeClosed() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden>\n <path d=\"M10.5858 10.5858C10.2107 10.9609 10 11.4696 10 12C10 13.1046 10.8954 14 12 14C12.5304 14 13.0391 13.7893 13.4142 13.4142\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <path d=\"M17.6112 17.6112C16.0556 18.979 14.1364 19.7493 12.0001 19.7493C5.63647 19.7493 2.25011 12.3743 2.25011 12.3743C3.47011 10.1443 5.27761 8.35577 7.38911 7.13965\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <path d=\"M20.8892 6.00928C21.8292 6.78928 22.6732 7.70428 23.3892 8.72428C23.7502 9.23428 23.7502 9.91428 23.3892 10.4243C22.6732 11.4443 21.8292 12.3593 20.8892 13.1393\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <path d=\"M14.9318 6.00928C13.6618 5.38928 12.2818 5.02928 10.8188 5.00928C9.35585 4.98928 7.93185 5.30928 6.61185 5.88928\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <path d=\"M21 3L3 21\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n","'use client';\n\nimport React from 'react';\n// Headless: no bundled default styles — style every slot via `classNames`.\nconst defaults: Record<string, string> = {};\n\n/**\n * SelectField — a Flutter `DropdownButtonFormField`-idiom trigger (Workstream F-next\n * form family). It renders only the FIELD (label + optional description + a button that\n * shows the selected value or a placeholder); the actual picker (a SelectionViewer /\n * bottom-sheet) stays owned by the page and is opened via `onOpen`.\n *\n * Same contract as TextInput: HYBRID styling (a `classNames` slot class REPLACES the\n * built-in default for that slot) + a self-contained `theme` prop (no app coupling).\n */\n\nexport type SelectFieldStatus = 'default' | 'error' | 'valid' | 'info';\n\nexport interface SelectFieldClassNames {\n root?: string;\n label?: string;\n description?: string;\n /** the trigger button. */\n control?: string;\n /** trailing adornment wrapper (e.g. a chevron). */\n trailing?: string;\n helper?: string;\n}\n\nexport interface SelectFieldProps {\n label?: React.ReactNode;\n description?: React.ReactNode;\n /** the display value; when empty the `placeholder` shows. */\n value?: React.ReactNode;\n placeholder?: React.ReactNode;\n /** open the picker (SelectionViewer). */\n onOpen?: (e: React.MouseEvent<HTMLButtonElement>) => void;\n /** trailing content inside the control (e.g. a chevron icon). */\n trailing?: React.ReactNode;\n disabled?: boolean;\n helperText?: React.ReactNode;\n status?: SelectFieldStatus;\n id?: string;\n name?: string;\n classNames?: SelectFieldClassNames;\n className?: string;\n style?: React.CSSProperties;\n theme?: 'light' | 'dark';\n}\n\nfunction pickSlot(\n caller: string | undefined,\n name: keyof typeof defaults,\n theme?: 'light' | 'dark',\n): string | undefined {\n if (caller != null) return caller;\n const base = defaults[name];\n if (!base) return undefined;\n const variant = theme ? defaults[`${name}_${theme}` as keyof typeof defaults] : undefined;\n return variant ? `${base} ${variant}` : base;\n}\n\nexport function SelectField({\n label, description, value, placeholder = 'Select', onOpen, trailing,\n disabled, helperText, status = 'default', id, name,\n classNames = {}, className, style, theme,\n}: SelectFieldProps) {\n const statusClass =\n status === 'error' ? defaults.statusError\n : status === 'valid' ? defaults.statusValid\n : status === 'info' ? defaults.statusInfo\n : undefined;\n\n const hasValue = value != null && value !== '';\n\n return (\n <div className={pickSlot(className ?? classNames.root, 'root', theme)} style={style}>\n {label != null && (\n <label htmlFor={id} className={pickSlot(classNames.label, 'label', theme)}>{label}</label>\n )}\n {description != null && (\n <p className={pickSlot(classNames.description, 'description', theme)}>{description}</p>\n )}\n <button\n type=\"button\"\n id={id}\n name={name}\n onClick={onOpen}\n disabled={disabled}\n aria-invalid={status === 'error' || undefined}\n className={pickSlot(classNames.control, 'control', theme)}\n >\n {hasValue ? value : placeholder}\n {trailing != null && (\n <span className={pickSlot(classNames.trailing, 'trailing', theme)}>{trailing}</span>\n )}\n </button>\n {helperText != null && (\n <p\n className={\n classNames.helper\n ? classNames.helper\n : `${pickSlot(undefined, 'helper', theme)}${statusClass ? ` ${statusClass}` : ''}`\n }\n >\n {helperText}\n </p>\n )}\n </div>\n );\n}\n","'use client';\n\nimport React, { useId } from 'react';\n// Headless: no bundled default styles — style every slot via `classNames`.\nconst defaults: Record<string, string> = {};\n\n/**\n * Checkbox — a Flutter `Checkbox`/`CheckboxListTile`-idiom control (form family). A\n * hidden native <input type=checkbox> drives a custom box + check icon and a text label,\n * so it stays accessible and form-submittable while looking however the page wants.\n *\n * HYBRID styling + self-contained `theme` prop, same contract as TextInput/SelectField.\n */\n\nexport interface CheckboxClassNames {\n /** the wrapping <label>. */\n label?: string;\n /** the (visually-hidden) native input. */\n input?: string;\n /** the custom box that shows the check. */\n box?: string;\n /** the check icon inside the box. */\n icon?: string;\n /** the text beside the box. */\n text?: string;\n}\n\nexport interface CheckboxProps {\n checked?: boolean;\n defaultChecked?: boolean;\n /** Flutter `onChanged(bool)`. */\n onChange?: (checked: boolean, event: React.ChangeEvent<HTMLInputElement>) => void;\n /** label content beside the box. */\n children?: React.ReactNode;\n disabled?: boolean;\n required?: boolean;\n name?: string;\n id?: string;\n value?: string;\n /** custom check icon (defaults to a checkmark). */\n icon?: React.ReactNode;\n classNames?: CheckboxClassNames;\n className?: string;\n style?: React.CSSProperties;\n theme?: 'light' | 'dark';\n}\n\nfunction pickSlot(\n caller: string | undefined,\n name: keyof typeof defaults,\n theme?: 'light' | 'dark',\n): string | undefined {\n if (caller != null) return caller;\n const base = defaults[name];\n if (!base) return undefined;\n const variant = theme ? defaults[`${name}_${theme}` as keyof typeof defaults] : undefined;\n return variant ? `${base} ${variant}` : base;\n}\n\nexport function Checkbox({\n checked, defaultChecked, onChange, children, disabled, required, name, id, value,\n icon, classNames = {}, className, style, theme,\n}: CheckboxProps) {\n const reactId = useId();\n const fieldId = id ?? `chk-${reactId}`;\n const isChecked = checked;\n\n return (\n <label htmlFor={fieldId} className={pickSlot(className ?? classNames.label, 'label', theme)} style={style}>\n <input\n id={fieldId}\n type=\"checkbox\"\n name={name}\n value={value}\n checked={checked}\n defaultChecked={defaultChecked}\n onChange={(e) => onChange?.(e.target.checked, e)}\n disabled={disabled}\n required={required}\n className={pickSlot(classNames.input, 'input', theme)}\n />\n <span className={pickSlot(classNames.box, 'box', theme)} aria-hidden>\n {isChecked && (icon ?? (\n <svg className={pickSlot(classNames.icon, 'icon', theme)} viewBox=\"0 0 16 16\" fill=\"none\">\n <path\n d=\"M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z\"\n fill=\"currentColor\"\n />\n </svg>\n ))}\n </span>\n {children != null && (\n <span className={pickSlot(classNames.text, 'text', theme)}>{children}</span>\n )}\n </label>\n );\n}\n","'use client';\n\nimport React from 'react';\n// Headless: no bundled default styles — style every slot via `classNames`.\nconst defaults: Record<string, string> = {};\n\n/**\n * RadioGroup — a Flutter `Radio`/`RadioListTile` group (form family). Renders a set of\n * selectable \"cards\": each is a <label> wrapping a native <input type=radio> plus content,\n * with a `selected` class on the active one. Accessible + form-submittable.\n *\n * HYBRID styling + self-contained `theme` prop, same contract as the rest of the family.\n */\n\nexport interface RadioOption<V extends string = string> {\n value: V;\n label: React.ReactNode;\n disabled?: boolean;\n}\n\nexport interface RadioGroupClassNames {\n /** wrapper around all items. */\n group?: string;\n /** each item <label>. */\n item?: string;\n /** appended to the selected item's <label>. */\n itemSelected?: string;\n /** the native radio input. */\n input?: string;\n /** wrapper around an item's label content. */\n content?: string;\n /** the item's text. */\n text?: string;\n}\n\nexport interface RadioGroupProps<V extends string = string> {\n name: string;\n value?: V;\n onChange?: (value: V) => void;\n options: RadioOption<V>[];\n disabled?: boolean;\n classNames?: RadioGroupClassNames;\n className?: string;\n style?: React.CSSProperties;\n theme?: 'light' | 'dark';\n}\n\nfunction pickSlot(\n caller: string | undefined,\n name: keyof typeof defaults,\n theme?: 'light' | 'dark',\n): string | undefined {\n if (caller != null) return caller;\n const base = defaults[name];\n if (!base) return undefined;\n const variant = theme ? defaults[`${name}_${theme}` as keyof typeof defaults] : undefined;\n return variant ? `${base} ${variant}` : base;\n}\n\nexport function RadioGroup<V extends string = string>({\n name, value, onChange, options, disabled,\n classNames = {}, className, style, theme,\n}: RadioGroupProps<V>) {\n const itemBase = pickSlot(classNames.item, 'item', theme);\n const itemSelected = pickSlot(classNames.itemSelected, 'itemSelected', theme);\n\n return (\n <div className={pickSlot(className ?? classNames.group, 'group', theme)} style={style} role=\"radiogroup\">\n {options.map((opt) => {\n const selected = value === opt.value;\n return (\n <label\n key={opt.value}\n className={`${itemBase ?? ''}${selected && itemSelected ? ` ${itemSelected}` : ''}`.trim() || undefined}\n >\n <input\n type=\"radio\"\n name={name}\n value={opt.value}\n checked={selected}\n onChange={() => onChange?.(opt.value)}\n disabled={disabled || opt.disabled}\n className={pickSlot(classNames.input, 'input', theme)}\n />\n <div className={pickSlot(classNames.content, 'content', theme)}>\n <span className={pickSlot(classNames.text, 'text', theme)}>{opt.label}</span>\n </div>\n </label>\n );\n })}\n </div>\n );\n}\n"]}
|