@k3-universe/react-kit 0.0.13 → 0.0.14

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.
Files changed (41) hide show
  1. package/dist/index.js +413 -403
  2. package/dist/kit/builder/data-table/types.d.ts +1 -1
  3. package/dist/kit/builder/data-table/types.d.ts.map +1 -1
  4. package/dist/kit/builder/form/components/FormBuilder.d.ts +3 -172
  5. package/dist/kit/builder/form/components/FormBuilder.d.ts.map +1 -1
  6. package/dist/kit/builder/form/components/FormBuilderField.d.ts +8 -8
  7. package/dist/kit/builder/form/components/FormBuilderField.d.ts.map +1 -1
  8. package/dist/kit/builder/form/components/fields/types.d.ts +3 -3
  9. package/dist/kit/builder/form/components/fields/types.d.ts.map +1 -1
  10. package/dist/kit/builder/form/index.d.ts +1 -0
  11. package/dist/kit/builder/form/index.d.ts.map +1 -1
  12. package/dist/kit/builder/form/types.d.ts +175 -0
  13. package/dist/kit/builder/form/types.d.ts.map +1 -0
  14. package/dist/kit/builder/form/utils/common-forms.d.ts +1 -1
  15. package/dist/kit/builder/form/utils/common-forms.d.ts.map +1 -1
  16. package/dist/kit/builder/form/utils/field-factories.d.ts +3 -3
  17. package/dist/kit/builder/form/utils/field-factories.d.ts.map +1 -1
  18. package/dist/kit/builder/form/utils/section-factories.d.ts +4 -4
  19. package/dist/kit/builder/form/utils/section-factories.d.ts.map +1 -1
  20. package/dist/kit/builder/stack-dialog/provider.d.ts.map +1 -1
  21. package/dist/kit/builder/stack-dialog/renderer.d.ts.map +1 -1
  22. package/dist/kit/components/autocomplete/Autocomplete.d.ts +8 -8
  23. package/dist/kit/components/autocomplete/Autocomplete.d.ts.map +1 -1
  24. package/dist/kit/components/autocomplete/types.d.ts +6 -4
  25. package/dist/kit/components/autocomplete/types.d.ts.map +1 -1
  26. package/package.json +1 -1
  27. package/src/kit/builder/data-table/components/DataTable.tsx +1 -1
  28. package/src/kit/builder/data-table/types.ts +1 -1
  29. package/src/kit/builder/form/components/FormBuilder.tsx +43 -237
  30. package/src/kit/builder/form/components/FormBuilderField.tsx +42 -34
  31. package/src/kit/builder/form/components/fields/AutocompleteField.tsx +2 -2
  32. package/src/kit/builder/form/components/fields/types.ts +3 -3
  33. package/src/kit/builder/form/index.ts +1 -0
  34. package/src/kit/builder/form/types.ts +198 -0
  35. package/src/kit/builder/form/utils/common-forms.ts +1 -1
  36. package/src/kit/builder/form/utils/field-factories.ts +5 -5
  37. package/src/kit/builder/form/utils/section-factories.ts +10 -10
  38. package/src/kit/builder/stack-dialog/provider.tsx +2 -1
  39. package/src/kit/builder/stack-dialog/renderer.tsx +6 -7
  40. package/src/kit/components/autocomplete/Autocomplete.tsx +33 -25
  41. package/src/kit/components/autocomplete/types.ts +7 -5
@@ -32,10 +32,10 @@ import type {
32
32
  } from "./types";
33
33
  import { useDebounce } from "use-debounce";
34
34
 
35
- export type AutocompleteProps = {
35
+ export type AutocompleteProps<T = unknown> = {
36
36
  mode: AutocompleteMode;
37
- options?: AutocompleteOption[];
38
- fetcher?: AutocompleteFetcher;
37
+ options?: AutocompleteOption<T>[];
38
+ fetcher?: AutocompleteFetcher<T>;
39
39
  pageSize?: number;
40
40
  /**
41
41
  * Value can be a single primitive or an array when `multiple` is true
@@ -46,7 +46,8 @@ export type AutocompleteProps = {
46
46
  */
47
47
  onChange?: (
48
48
  value: string | number | null | Array<string | number>,
49
- option: AutocompleteOption | AutocompleteOption[] | null,
49
+ option: AutocompleteOption<T> | AutocompleteOption<T>[] | null,
50
+ raw?: T | T[] | null,
50
51
  ) => void;
51
52
  /** Enable selecting multiple values (shows chips) */
52
53
  multiple?: boolean;
@@ -56,7 +57,7 @@ export type AutocompleteProps = {
56
57
  className?: string;
57
58
  emptyText?: string;
58
59
  renderOption?: (
59
- option: AutocompleteOption,
60
+ option: AutocompleteOption<T>,
60
61
  selected: boolean,
61
62
  ) => React.ReactNode;
62
63
  searchPlaceholder?: string;
@@ -79,21 +80,21 @@ export type AutocompleteProps = {
79
80
  * Optional: seed selected labels for edit pages. These options are only used to populate the
80
81
  * internal label map so labels render correctly when values are prefilled.
81
82
  */
82
- initialSelectedOptions?: AutocompleteOption | AutocompleteOption[] | null;
83
+ initialSelectedOptions?: AutocompleteOption<T> | AutocompleteOption<T>[] | null;
83
84
  /**
84
85
  * Optional: load labels/options for a list of values whose labels are unknown.
85
86
  * Useful for edit pages in server mode when only values are available.
86
87
  */
87
- loadSelected?: (values: Array<string | number>) => Promise<AutocompleteOption[]>;
88
+ loadSelected?: (values: Array<string | number>) => Promise<AutocompleteOption<T>[]>;
88
89
  };
89
90
 
90
91
  const DEFAULT_PAGE_SIZE = 20;
91
92
 
92
93
  const EMPTY_OPTIONS: AutocompleteOption[] = [];
93
94
 
94
- export function Autocomplete({
95
+ export function Autocomplete<T = unknown>({
95
96
  mode,
96
- options = EMPTY_OPTIONS,
97
+ options = EMPTY_OPTIONS as AutocompleteOption<T>[],
97
98
  fetcher,
98
99
  pageSize = DEFAULT_PAGE_SIZE,
99
100
  value: controlledValue,
@@ -113,7 +114,7 @@ export function Autocomplete({
113
114
  clearable = true,
114
115
  initialSelectedOptions,
115
116
  loadSelected,
116
- }: AutocompleteProps) {
117
+ }: AutocompleteProps<T>) {
117
118
  const [open, setOpen] = useState<boolean>(!!defaultOpen);
118
119
  const [search, setSearch] = useState("");
119
120
  const [debouncedSearch] = useDebounce(search, 250);
@@ -135,8 +136,12 @@ export function Autocomplete({
135
136
  // Keep a map of value -> label to ensure we can render chips/labels even if the option
136
137
  // is not present in the current page (especially in server mode or for custom values)
137
138
  const labelMapRef = useRef<Map<string | number, string>>(new Map());
138
- const addToLabelMap = useCallback((opt: AutocompleteOption) => {
139
+ const rawMapRef = useRef<Map<string | number, T>>(new Map());
140
+ const addToLabelMap = useCallback((opt: AutocompleteOption<T>) => {
139
141
  labelMapRef.current.set(opt.value, opt.label);
142
+ if (Object.prototype.hasOwnProperty.call(opt, "raw") && (opt as AutocompleteOption<T>).raw !== undefined) {
143
+ rawMapRef.current.set(opt.value, (opt as AutocompleteOption<T>).raw as T);
144
+ }
140
145
  }, []);
141
146
  // Tick to force re-render when labels hydrate via async
142
147
  const [labelTick, setLabelTick] = useState(0);
@@ -149,7 +154,7 @@ export function Autocomplete({
149
154
  );
150
155
 
151
156
  const handleSelect = useCallback(
152
- (next: AutocompleteOption) => {
157
+ (next: AutocompleteOption<T>) => {
153
158
  addToLabelMap(next);
154
159
  if (isMultiple) {
155
160
  const prevValues = Array.isArray(value) ? value : [];
@@ -159,16 +164,18 @@ export function Autocomplete({
159
164
  : [...prevValues, next.value];
160
165
 
161
166
  if (controlledValue === undefined) setValue(newValues);
162
- const selectedOptions: AutocompleteOption[] = newValues.map((v) => ({
167
+ const selectedOptions: AutocompleteOption<T>[] = newValues.map((v) => ({
163
168
  value: v,
164
169
  label: getLabel(v),
170
+ raw: rawMapRef.current.get(v),
165
171
  }));
166
- onChange?.(newValues, selectedOptions);
172
+ const raws = selectedOptions.map((o) => o.raw as T);
173
+ onChange?.(newValues, selectedOptions, raws);
167
174
  // Keep open for multi-select
168
175
  } else {
169
176
  const newValue = next.value;
170
177
  if (controlledValue === undefined) setValue(newValue);
171
- onChange?.(newValue, next);
178
+ onChange?.(newValue, next, (next as AutocompleteOption<T>).raw as T | undefined ?? null);
172
179
  setOpen(false);
173
180
  }
174
181
  },
@@ -176,7 +183,7 @@ export function Autocomplete({
176
183
  );
177
184
 
178
185
  // Data state (shared for both modes)
179
- const [items, setItems] = useState<AutocompleteOption[]>([]);
186
+ const [items, setItems] = useState<AutocompleteOption<T>[]>([]);
180
187
  const [loading, setLoading] = useState(false);
181
188
  const [hasMore, setHasMore] = useState(false);
182
189
  const [nextCursor, setNextCursor] = useState<
@@ -197,7 +204,7 @@ export function Autocomplete({
197
204
  if (!fetcher) return;
198
205
  setLoading(true);
199
206
  try {
200
- const res: AutocompleteFetchResult = await fetcher({
207
+ const res: AutocompleteFetchResult<T> = await fetcher({
201
208
  search: debouncedSearch,
202
209
  cursor: nextCursor ?? null,
203
210
  page,
@@ -334,24 +341,23 @@ export function Autocomplete({
334
341
  return placeholder;
335
342
  }, [isMultiple, mode, options, selectedOption, value, placeholder, labelTick]);
336
343
 
337
-
338
344
  const selectedValues: Array<string | number> = useMemo(
339
345
  () => (isMultiple && Array.isArray(value) ? value : []),
340
346
  [isMultiple, value],
341
347
  );
342
348
  // biome-ignore lint/correctness/useExhaustiveDependencies: labelTick intentionally triggers recompute when labelMap hydrates
343
- const selectedOptionsMulti: AutocompleteOption[] = useMemo(
344
- () => selectedValues.map((v) => ({ value: v, label: getLabel(v) })),
349
+ const selectedOptionsMulti: AutocompleteOption<T>[] = useMemo(
350
+ () => selectedValues.map((v) => ({ value: v, label: getLabel(v), raw: rawMapRef.current.get(v) })),
345
351
  [getLabel, selectedValues, labelTick],
346
352
  );
347
353
 
348
354
  const handleClear = useCallback(() => {
349
355
  if (isMultiple) {
350
356
  if (controlledValue === undefined) setValue([]);
351
- onChange?.([], []);
357
+ onChange?.([], [], []);
352
358
  } else {
353
359
  if (controlledValue === undefined) setValue(null);
354
- onChange?.(null, null);
360
+ onChange?.(null, null, null);
355
361
  }
356
362
  }, [controlledValue, isMultiple, onChange]);
357
363
 
@@ -360,7 +366,7 @@ export function Autocomplete({
360
366
  (text: string) => {
361
367
  const t = text.trim();
362
368
  if (!t) return;
363
- const created: AutocompleteOption = { value: t, label: t };
369
+ const created: AutocompleteOption<T> = { value: t, label: t };
364
370
  addToLabelMap(created);
365
371
  if (isMultiple) {
366
372
  const prevValues = Array.isArray(value) ? value : [];
@@ -370,12 +376,14 @@ export function Autocomplete({
370
376
  const newOptions = newValues.map((v) => ({
371
377
  value: v,
372
378
  label: getLabel(v),
379
+ raw: rawMapRef.current.get(v),
373
380
  }));
374
- onChange?.(newValues, newOptions);
381
+ const raws = newOptions.map((o) => o.raw as T);
382
+ onChange?.(newValues, newOptions, raws);
375
383
  setSearch("");
376
384
  } else {
377
385
  if (controlledValue === undefined) setValue(created.value);
378
- onChange?.(created.value, created);
386
+ onChange?.(created.value, created, (created.raw as T | undefined) ?? null);
379
387
  setSearch("");
380
388
  setOpen(false);
381
389
  }
@@ -1,6 +1,8 @@
1
- export type AutocompleteOption = {
1
+ export type AutocompleteOption<T = unknown> = {
2
2
  value: string | number
3
3
  label: string
4
+ /** Optional original object returned by the fetcher/static source */
5
+ raw?: T
4
6
  }
5
7
 
6
8
  export type AutocompleteFetchParams = {
@@ -10,15 +12,15 @@ export type AutocompleteFetchParams = {
10
12
  pageSize: number
11
13
  }
12
14
 
13
- export type AutocompleteFetchResult = {
14
- items: AutocompleteOption[]
15
+ export type AutocompleteFetchResult<T = unknown> = {
16
+ items: AutocompleteOption<T>[]
15
17
  nextCursor?: string | number | null
16
18
  hasMore: boolean
17
19
  total?: number
18
20
  }
19
21
 
20
- export type AutocompleteFetcher = (
22
+ export type AutocompleteFetcher<T = unknown> = (
21
23
  params: AutocompleteFetchParams,
22
- ) => Promise<AutocompleteFetchResult>
24
+ ) => Promise<AutocompleteFetchResult<T>>
23
25
 
24
26
  export type AutocompleteMode = 'client' | 'server'