@eifi1/ui-kit 0.5.0 → 0.6.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/README.md +50 -1
- package/dist/components/amount-input.d.ts +7 -0
- package/dist/components/autocomplete.d.ts +101 -0
- package/dist/components/autocomplete.js +260 -0
- package/dist/components/autocomplete.js.map +1 -0
- package/dist/components/calculator.d.ts +7 -0
- package/dist/components/chip.d.ts +22 -7
- package/dist/components/chip.js +14 -1
- package/dist/components/chip.js.map +1 -1
- package/dist/components/choice-card.d.ts +100 -0
- package/dist/components/choice-card.js +170 -0
- package/dist/components/choice-card.js.map +1 -0
- package/dist/components/combobox-core.d.ts +76 -6
- package/dist/components/combobox-core.js +119 -49
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.d.ts +12 -2
- package/dist/components/combobox.js +42 -17
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/currency-select.js +10 -2
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/danger-confirm.d.ts +91 -0
- package/dist/components/danger-confirm.js +181 -0
- package/dist/components/danger-confirm.js.map +1 -0
- package/dist/components/date-picker.js +19 -7
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/dialog-frame.d.ts +84 -0
- package/dist/components/dialog-frame.js +86 -0
- package/dist/components/dialog-frame.js.map +1 -0
- package/dist/components/disclosure.d.ts +108 -0
- package/dist/components/disclosure.js +127 -0
- package/dist/components/disclosure.js.map +1 -0
- package/dist/components/entity-combobox.d.ts +17 -3
- package/dist/components/entity-combobox.js +25 -5
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/file-button.d.ts +161 -0
- package/dist/components/file-button.js +225 -0
- package/dist/components/file-button.js.map +1 -0
- package/dist/components/file-dropzone.d.ts +72 -23
- package/dist/components/file-dropzone.js +219 -94
- package/dist/components/file-dropzone.js.map +1 -1
- package/dist/components/icon-picker.d.ts +72 -0
- package/dist/components/icon-picker.js +104 -0
- package/dist/components/icon-picker.js.map +1 -0
- package/dist/components/mini-calendar.d.ts +3 -0
- package/dist/components/mini-calendar.js +4 -3
- package/dist/components/mini-calendar.js.map +1 -1
- package/dist/components/modal.d.ts +8 -1
- package/dist/components/modal.js +4 -2
- package/dist/components/modal.js.map +1 -1
- package/dist/components/month-picker.js +16 -5
- package/dist/components/month-picker.js.map +1 -1
- package/dist/components/multi-entity-combobox.d.ts +16 -3
- package/dist/components/multi-entity-combobox.js +25 -5
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/number-field.d.ts +41 -1
- package/dist/components/number-field.js +42 -10
- package/dist/components/number-field.js.map +1 -1
- package/dist/components/number-input.d.ts +35 -2
- package/dist/components/number-input.js +35 -4
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.d.ts +7 -0
- package/dist/components/popover.js +4 -1
- package/dist/components/popover.js.map +1 -1
- package/dist/components/search-field.d.ts +16 -0
- package/dist/components/search-field.js +29 -7
- package/dist/components/search-field.js.map +1 -1
- package/dist/components/signature-pad.d.ts +43 -1
- package/dist/components/signature-pad.js +74 -2
- package/dist/components/signature-pad.js.map +1 -1
- package/dist/components/swatch-picker.d.ts +69 -0
- package/dist/components/swatch-picker.js +75 -0
- package/dist/components/swatch-picker.js.map +1 -0
- package/dist/components/tile-radio.d.ts +50 -0
- package/dist/components/tile-radio.js +140 -0
- package/dist/components/tile-radio.js.map +1 -0
- package/dist/components/toggle-group.d.ts +27 -5
- package/dist/components/toggle-group.js +22 -15
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/trigger-aria.d.ts +18 -0
- package/dist/components/trigger-aria.js +25 -0
- package/dist/components/trigger-aria.js.map +1 -0
- package/dist/components/ui.d.ts +164 -18
- package/dist/components/ui.js +207 -34
- package/dist/components/ui.js.map +1 -1
- package/dist/i18n/defaults.d.ts +7 -0
- package/dist/i18n/defaults.js +13 -2
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +38 -5
- package/dist/i18n/kit-labels.js +12 -4
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/index.d.ts +16 -7
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/table-text.d.ts +127 -0
- package/dist/lib/table-text.js +82 -0
- package/dist/lib/table-text.js.map +1 -0
- package/dist/rhf/form.d.ts +79 -0
- package/dist/rhf/form.js +143 -0
- package/dist/rhf/form.js.map +1 -0
- package/dist/rhf.d.ts +4 -0
- package/dist/rhf.js +3 -0
- package/dist/rhf.js.map +1 -0
- package/dist/shell/app-shell.js +3 -1
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/table-text.d.ts +1 -0
- package/dist/table-text.js +3 -0
- package/dist/table-text.js.map +1 -0
- package/package.json +14 -1
- package/src/components/autocomplete.tsx +425 -0
- package/src/components/chip.tsx +43 -7
- package/src/components/choice-card.tsx +305 -0
- package/src/components/combobox-core.tsx +228 -58
- package/src/components/combobox.tsx +58 -21
- package/src/components/currency-select.tsx +16 -2
- package/src/components/danger-confirm.tsx +286 -0
- package/src/components/date-picker.tsx +31 -6
- package/src/components/dialog-frame.tsx +179 -0
- package/src/components/disclosure.tsx +259 -0
- package/src/components/entity-combobox.tsx +41 -6
- package/src/components/file-button.tsx +431 -0
- package/src/components/file-dropzone.tsx +323 -117
- package/src/components/icon-picker.tsx +181 -0
- package/src/components/mini-calendar.tsx +7 -3
- package/src/components/modal.tsx +10 -2
- package/src/components/month-picker.tsx +22 -4
- package/src/components/multi-entity-combobox.tsx +40 -6
- package/src/components/number-field.tsx +86 -10
- package/src/components/number-input.tsx +79 -2
- package/src/components/popover.tsx +4 -1
- package/src/components/search-field.tsx +49 -6
- package/src/components/signature-pad.tsx +112 -0
- package/src/components/swatch-picker.tsx +141 -0
- package/src/components/tile-radio.tsx +228 -0
- package/src/components/toggle-group.tsx +54 -18
- package/src/components/trigger-aria.ts +42 -0
- package/src/components/ui.tsx +427 -40
- package/src/i18n/defaults.ts +12 -1
- package/src/i18n/kit-labels.tsx +45 -5
- package/src/index.ts +19 -0
- package/src/lib/table-text.ts +265 -0
- package/src/rhf/form.tsx +300 -0
- package/src/rhf.ts +9 -0
- package/src/shell/app-shell.tsx +3 -1
- package/src/table-text.ts +8 -0
package/src/rhf/form.tsx
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The react-hook-form adapter: the five parts every form is built from, and the hook
|
|
3
|
+
* they share. `@eifi1/ui-kit/rhf`.
|
|
4
|
+
*
|
|
5
|
+
* ```tsx
|
|
6
|
+
* <Form {...form}>
|
|
7
|
+
* <FormField control={form.control} name="amount" render={({ field }) => (
|
|
8
|
+
* <FormItem>
|
|
9
|
+
* <FormLabel required>Amount</FormLabel>
|
|
10
|
+
* <FormControl>
|
|
11
|
+
* <NumberField value={field.value} onCommit={field.onChange} />
|
|
12
|
+
* </FormControl>
|
|
13
|
+
* <FormMessage />
|
|
14
|
+
* </FormItem>
|
|
15
|
+
* )} />
|
|
16
|
+
* </Form>
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* The shape is shadcn's `form.tsx`, on purpose: Kastlan builds every form from that
|
|
20
|
+
* file, so moving onto this one is an import line rather than a rewrite. What differs
|
|
21
|
+
* is what the shadcn file gets wrong:
|
|
22
|
+
*
|
|
23
|
+
* - **`aria-describedby` names only what is on screen.** shadcn always points the
|
|
24
|
+
* control at a description id, rendered or not, and at the message id whenever the
|
|
25
|
+
* field has an error, whether a message was rendered for it or not. A reference to
|
|
26
|
+
* a node that does not exist describes the field as nothing. Here the description
|
|
27
|
+
* and the message say when they are mounted, and the control points at exactly
|
|
28
|
+
* those.
|
|
29
|
+
* - **Merged, never replaced.** A control that already carries its own
|
|
30
|
+
* `aria-describedby` keeps it, first — the same rule as the kit's `error` prop
|
|
31
|
+
* (see `useFieldError` in components/ui.tsx).
|
|
32
|
+
* - **No Radix.** `FormControl` is a one-child `cloneElement`, not `Slot`, and the
|
|
33
|
+
* label is the kit's own `Label` — the kit carries no Radix dependency and this
|
|
34
|
+
* entry is not the place to start.
|
|
35
|
+
*
|
|
36
|
+
* ## Why this is its own entry
|
|
37
|
+
*
|
|
38
|
+
* The main barrel once shipped a react-hook-form hook (`use-rhf-wizard-step`) and
|
|
39
|
+
* removed it: it made react-hook-form an install every consumer's typecheck paid for,
|
|
40
|
+
* for a hook nobody called (see the wizard section of src/index.ts). This is the
|
|
41
|
+
* answer to that, not a reversal of it. Nothing outside `src/rhf*` imports
|
|
42
|
+
* react-hook-form — `__tests__/packaging-contract.test.ts` holds that — and the peer is
|
|
43
|
+
* optional, so an app that does not use it installs nothing and imports nothing.
|
|
44
|
+
*
|
|
45
|
+
* ## Painting the field
|
|
46
|
+
*
|
|
47
|
+
* `FormControl` sets `aria-invalid` on its child. `NumberField` and the pickers paint
|
|
48
|
+
* from that attribute; `Input`, `Select` and `Textarea` paint only from their
|
|
49
|
+
* `invalid` prop (see its note in components/ui.tsx: a bare attribute announces but
|
|
50
|
+
* does not paint), so pass it from the render prop —
|
|
51
|
+
* `<Input {...field} invalid={fieldState.invalid} />`. `FormControl` does not inject
|
|
52
|
+
* `invalid` itself because its child may be a native element, which would print it
|
|
53
|
+
* into the DOM as an unknown attribute.
|
|
54
|
+
*/
|
|
55
|
+
import {
|
|
56
|
+
Children,
|
|
57
|
+
cloneElement,
|
|
58
|
+
createContext,
|
|
59
|
+
isValidElement,
|
|
60
|
+
useCallback,
|
|
61
|
+
useContext,
|
|
62
|
+
useId,
|
|
63
|
+
useLayoutEffect,
|
|
64
|
+
useMemo,
|
|
65
|
+
useState,
|
|
66
|
+
type ComponentProps,
|
|
67
|
+
type ReactElement,
|
|
68
|
+
} from "react";
|
|
69
|
+
import {
|
|
70
|
+
Controller,
|
|
71
|
+
FormProvider,
|
|
72
|
+
useFormContext,
|
|
73
|
+
useFormState,
|
|
74
|
+
type ControllerProps,
|
|
75
|
+
type FieldPath,
|
|
76
|
+
type FieldValues,
|
|
77
|
+
} from "react-hook-form";
|
|
78
|
+
import { Label, type LabelProps } from "../components/ui";
|
|
79
|
+
import { cn } from "../lib/cn";
|
|
80
|
+
|
|
81
|
+
/** The form's `useForm()` return, spread in: `<Form {...form}>`. react-hook-form's own
|
|
82
|
+
* `FormProvider`, under the name the shadcn file gave it. */
|
|
83
|
+
export const Form = FormProvider;
|
|
84
|
+
|
|
85
|
+
// ── contexts ─────────────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
const FormFieldContext = createContext<{ name: string } | null>(null);
|
|
88
|
+
|
|
89
|
+
/** The two optional nodes a control can be described by. */
|
|
90
|
+
type Described = "description" | "message";
|
|
91
|
+
|
|
92
|
+
interface FormItemContextValue {
|
|
93
|
+
id: string;
|
|
94
|
+
mounted: Record<Described, boolean>;
|
|
95
|
+
mark: (slot: Described, on: boolean) => void;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const FormItemContext = createContext<FormItemContextValue | null>(null);
|
|
99
|
+
|
|
100
|
+
// ── FormField ────────────────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* react-hook-form's `Controller`, with the field's name put in context for the parts
|
|
104
|
+
* below it. Takes exactly `Controller`'s props.
|
|
105
|
+
*/
|
|
106
|
+
export function FormField<
|
|
107
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
108
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
109
|
+
TTransformedValues = TFieldValues,
|
|
110
|
+
>(props: ControllerProps<TFieldValues, TName, TTransformedValues>) {
|
|
111
|
+
const value = useMemo(() => ({ name: props.name as string }), [props.name]);
|
|
112
|
+
return (
|
|
113
|
+
<FormFieldContext.Provider value={value}>
|
|
114
|
+
<Controller {...props} />
|
|
115
|
+
</FormFieldContext.Provider>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── useFormField ─────────────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The ids and the state of the field around the caller: for a part of your own that
|
|
123
|
+
* sits beside the kit's (a character counter that belongs in `aria-describedby`, a
|
|
124
|
+
* label with a different look).
|
|
125
|
+
*
|
|
126
|
+
* Throws outside a `FormField` + `FormItem` pair. shadcn's version answered there with
|
|
127
|
+
* ids like `undefined-form-item`, which label nothing and fail nowhere.
|
|
128
|
+
*/
|
|
129
|
+
export function useFormField() {
|
|
130
|
+
const field = useContext(FormFieldContext);
|
|
131
|
+
const item = useContext(FormItemContext);
|
|
132
|
+
if (!field) throw new Error("useFormField must be used inside <FormField>.");
|
|
133
|
+
if (!item) throw new Error("useFormField must be used inside <FormItem>.");
|
|
134
|
+
const { getFieldState } = useFormContext();
|
|
135
|
+
// Subscribes this part to the field's state, so a label or message re-renders when
|
|
136
|
+
// the field's error changes without the whole form doing so.
|
|
137
|
+
const formState = useFormState({ name: field.name });
|
|
138
|
+
const state = getFieldState(field.name, formState);
|
|
139
|
+
const formDescriptionId = `${item.id}-description`;
|
|
140
|
+
const formMessageId = `${item.id}-message`;
|
|
141
|
+
const describedBy = [
|
|
142
|
+
item.mounted.description && formDescriptionId,
|
|
143
|
+
item.mounted.message && formMessageId,
|
|
144
|
+
]
|
|
145
|
+
.filter(Boolean)
|
|
146
|
+
.join(" ");
|
|
147
|
+
return {
|
|
148
|
+
id: item.id,
|
|
149
|
+
name: field.name,
|
|
150
|
+
formItemId: `${item.id}-control`,
|
|
151
|
+
formDescriptionId,
|
|
152
|
+
formMessageId,
|
|
153
|
+
/** The ids of the description and message that are actually rendered, or
|
|
154
|
+
* `undefined` when neither is. */
|
|
155
|
+
describedBy: describedBy || undefined,
|
|
156
|
+
...state,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ── FormItem ─────────────────────────────────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
export type FormItemProps = ComponentProps<"div">;
|
|
163
|
+
|
|
164
|
+
/** One field's box: mints the id its label, control, description and message share. */
|
|
165
|
+
export function FormItem({ className, ...rest }: FormItemProps) {
|
|
166
|
+
const id = useId();
|
|
167
|
+
const [mounted, setMounted] = useState<Record<Described, boolean>>({
|
|
168
|
+
description: false,
|
|
169
|
+
message: false,
|
|
170
|
+
});
|
|
171
|
+
const mark = useCallback(
|
|
172
|
+
(slot: Described, on: boolean) =>
|
|
173
|
+
setMounted((prev) => (prev[slot] === on ? prev : { ...prev, [slot]: on })),
|
|
174
|
+
[],
|
|
175
|
+
);
|
|
176
|
+
const value = useMemo(() => ({ id, mounted, mark }), [id, mounted, mark]);
|
|
177
|
+
return (
|
|
178
|
+
<FormItemContext.Provider value={value}>
|
|
179
|
+
<div data-slot="form-item" {...rest} className={cn("grid gap-1", className)} />
|
|
180
|
+
</FormItemContext.Provider>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Report a describing node's presence to the item, so the control points at it only
|
|
185
|
+
* while it is on screen. A layout effect, so the reference lands before paint. */
|
|
186
|
+
function useDescribes(slot: Described, on: boolean) {
|
|
187
|
+
const item = useContext(FormItemContext);
|
|
188
|
+
const mark = item?.mark;
|
|
189
|
+
useLayoutEffect(() => {
|
|
190
|
+
if (!on || !mark) return;
|
|
191
|
+
mark(slot, true);
|
|
192
|
+
return () => mark(slot, false);
|
|
193
|
+
}, [slot, on, mark]);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ── FormLabel ────────────────────────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
export type FormLabelProps = LabelProps;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* The kit's {@link Label}, bound to the control by `htmlFor` and turned to the danger
|
|
202
|
+
* colour while the field is invalid. `required` draws the kit's `aria-hidden` mark:
|
|
203
|
+
* whether the field is required is the control's to announce.
|
|
204
|
+
*/
|
|
205
|
+
export function FormLabel({ className, ...rest }: FormLabelProps) {
|
|
206
|
+
const { error, formItemId } = useFormField();
|
|
207
|
+
return (
|
|
208
|
+
<Label
|
|
209
|
+
data-slot="form-label"
|
|
210
|
+
data-error={error ? true : undefined}
|
|
211
|
+
htmlFor={formItemId}
|
|
212
|
+
{...rest}
|
|
213
|
+
className={cn("data-[error=true]:text-[var(--danger)]", className)}
|
|
214
|
+
/>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ── FormControl ──────────────────────────────────────────────────────────────
|
|
219
|
+
|
|
220
|
+
export interface FormControlProps {
|
|
221
|
+
/** Exactly one element: the kit field, or a native one. It receives `id`,
|
|
222
|
+
* `aria-describedby` and `aria-invalid`. */
|
|
223
|
+
children: ReactElement;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
type AriaProps = {
|
|
227
|
+
id?: string;
|
|
228
|
+
"aria-describedby"?: string;
|
|
229
|
+
"aria-invalid"?: ComponentProps<"input">["aria-invalid"];
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Wires its one child to the item: the `id` the label points at, `aria-describedby`
|
|
234
|
+
* naming the rendered description and message, and `aria-invalid` while the field has
|
|
235
|
+
* an error.
|
|
236
|
+
*
|
|
237
|
+
* The child's own values win where it has them (an explicit `id`, a set
|
|
238
|
+
* `aria-invalid`), as they did under Radix `Slot`; its own `aria-describedby` is
|
|
239
|
+
* merged in front of the item's rather than replaced.
|
|
240
|
+
*/
|
|
241
|
+
export function FormControl({ children }: FormControlProps) {
|
|
242
|
+
const { invalid, formItemId, describedBy } = useFormField();
|
|
243
|
+
const child = Children.only(children);
|
|
244
|
+
if (!isValidElement<AriaProps>(child)) return child;
|
|
245
|
+
const own = child.props;
|
|
246
|
+
const merged = [own["aria-describedby"], describedBy].filter(Boolean).join(" ");
|
|
247
|
+
return cloneElement(child, {
|
|
248
|
+
id: own.id ?? formItemId,
|
|
249
|
+
"aria-describedby": merged || undefined,
|
|
250
|
+
"aria-invalid": own["aria-invalid"] ?? (invalid || undefined),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ── FormDescription / FormMessage ────────────────────────────────────────────
|
|
255
|
+
|
|
256
|
+
export type FormDescriptionProps = ComponentProps<"p">;
|
|
257
|
+
|
|
258
|
+
/** Standing advice under the field ("at least twelve characters"). */
|
|
259
|
+
export function FormDescription({ className, ...rest }: FormDescriptionProps) {
|
|
260
|
+
const { formDescriptionId } = useFormField();
|
|
261
|
+
useDescribes("description", true);
|
|
262
|
+
return (
|
|
263
|
+
<p
|
|
264
|
+
data-slot="form-description"
|
|
265
|
+
{...rest}
|
|
266
|
+
id={formDescriptionId}
|
|
267
|
+
className={cn("text-[11px] leading-tight text-[var(--text-muted)]", className)}
|
|
268
|
+
/>
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export type FormMessageProps = ComponentProps<"p">;
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* The field's error message, from the form — the kit renders no string of its own
|
|
276
|
+
* here; the schema or the `rules` carry the words. Without an error it renders its
|
|
277
|
+
* `children`, if any, and otherwise nothing.
|
|
278
|
+
*
|
|
279
|
+
* Deliberately NOT `role="alert"`, for the reason the kit's `error` prop gives: the
|
|
280
|
+
* message is read when focus reaches the control, which is where a field's own error
|
|
281
|
+
* is wanted, and an alert would interrupt on every keystroke of a form that
|
|
282
|
+
* re-validates as you type.
|
|
283
|
+
*/
|
|
284
|
+
export function FormMessage({ className, children, ...rest }: FormMessageProps) {
|
|
285
|
+
const { error, formMessageId } = useFormField();
|
|
286
|
+
const body = error ? String(error.message ?? "") : children;
|
|
287
|
+
const shown = body !== undefined && body !== null && body !== false && body !== "";
|
|
288
|
+
useDescribes("message", shown);
|
|
289
|
+
if (!shown) return null;
|
|
290
|
+
return (
|
|
291
|
+
<p
|
|
292
|
+
data-slot="form-message"
|
|
293
|
+
{...rest}
|
|
294
|
+
id={formMessageId}
|
|
295
|
+
className={cn("text-[11px] leading-tight text-[var(--danger)]", className)}
|
|
296
|
+
>
|
|
297
|
+
{body}
|
|
298
|
+
</p>
|
|
299
|
+
);
|
|
300
|
+
}
|
package/src/rhf.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// `@eifi1/ui-kit/rhf` — the react-hook-form adapter: Form, FormField, FormItem,
|
|
2
|
+
// FormLabel, FormControl, FormDescription, FormMessage and useFormField.
|
|
3
|
+
//
|
|
4
|
+
// The only entry point in the package that requires `react-hook-form`, and an optional
|
|
5
|
+
// peer for that reason: an app without a form library installs nothing for it. NOT
|
|
6
|
+
// re-exported from the main barrel, which would make the peer everyone's — the reason
|
|
7
|
+
// the barrel dropped its last react-hook-form import (see the wizard section of
|
|
8
|
+
// src/index.ts). The packaging contract test holds that line.
|
|
9
|
+
export * from "./rhf/form";
|
package/src/shell/app-shell.tsx
CHANGED
|
@@ -600,7 +600,9 @@ function SidebarGroup({
|
|
|
600
600
|
aria-hidden
|
|
601
601
|
className={cn(
|
|
602
602
|
"size-3.5 transition-transform duration-200 ease-out motion-reduce:transition-none rtl:-scale-x-100",
|
|
603
|
-
|
|
603
|
+
// Under dir="rtl" the chevron is MIRRORED first (scale applies before rotate), so
|
|
604
|
+
// it starts pointing left and a +90° turn would point it UP — hence −90° there.
|
|
605
|
+
open && "rotate-90 rtl:-rotate-90",
|
|
604
606
|
)}
|
|
605
607
|
/>
|
|
606
608
|
</button>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// `@eifi1/ui-kit/table-text` — the lexer for a table pasted out of a spreadsheet or read
|
|
2
|
+
// from a CSV, and the three named rules for the comma. See the note at the top of
|
|
3
|
+
// lib/table-text.ts.
|
|
4
|
+
//
|
|
5
|
+
// Pure functions over strings: no React, no DOM, nothing to install. It is its own
|
|
6
|
+
// entry so an app that imports a CSV on a server, in a worker or in a script can take
|
|
7
|
+
// it without reaching through a barrel of components.
|
|
8
|
+
export * from "./lib/table-text";
|