sveltacular 1.1.0 → 1.1.1

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.
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { untrack } from 'svelte';
3
- import type { ReferenceItem, ComponentSize } from '../../types/form.js';
3
+ import type { ReferenceItem, ComponentSize, FieldNameMapping } from '../../types/form.js';
4
+ import { createFieldMapper } from '../../types/form.js';
4
5
  import FormField from '../form-field/form-field.svelte';
5
6
  import CheckBox from './check-box.svelte';
6
7
  import { uniqueId } from '../../helpers/unique-id.js';
@@ -8,40 +9,62 @@
8
9
  const id = uniqueId();
9
10
 
10
11
  let {
11
- group = $bindable([] as string[]),
12
+ group = $bindable([] as (string | number)[]),
12
13
  items = [],
14
+ fieldNames = undefined as FieldNameMapping | undefined,
13
15
  size = 'md' as ComponentSize,
14
16
  disabled = false,
15
17
  required = false,
16
18
  onChange,
17
19
  label
18
20
  }: {
19
- group?: string[];
20
- items?: ReferenceItem[];
21
+ group?: (string | number)[];
22
+ items?: any[];
23
+ /**
24
+ * Maps database field names to ReferenceItem properties.
25
+ * Use this when your data uses different field names (e.g., 'name' instead of 'label').
26
+ *
27
+ * @example
28
+ * // Basic usage
29
+ * fieldNames={{ label: 'name', value: 'id' }}
30
+ *
31
+ * @example
32
+ * // With description field
33
+ * fieldNames={{ label: 'title', value: 'id', description: 'subtitle' }}
34
+ */
35
+ fieldNames?: FieldNameMapping | undefined;
21
36
  size?: ComponentSize;
22
37
  disabled?: boolean;
23
38
  required?: boolean;
24
- onChange?: ((selected: string[]) => void) | undefined;
39
+ onChange?: ((selected: (string | number)[]) => void) | undefined;
25
40
  label?: string;
26
41
  } = $props();
27
42
 
43
+ // Create field mapper
44
+ const mapper = $derived(createFieldMapper<any>(fieldNames));
45
+
46
+ // Transform items for internal use (always work with ReferenceItem internally)
47
+ const referenceItems = $derived(
48
+ fieldNames ? items.map(item => mapper.toReferenceItem(item)) : items as ReferenceItem[]
49
+ );
50
+
28
51
  // Create reactive items with checked state, synced with group
29
52
  let itemsWithState = $state<Array<ReferenceItem & { isChecked: boolean }>>([]);
30
53
 
31
54
  // Sync itemsWithState when items or group changes (one-way: items/group -> itemsWithState)
32
55
  // Reassign the entire array to avoid reading itemsWithState in the effect
33
56
  $effect(() => {
34
- // Track items and group as dependencies
35
- const currentItems = items;
57
+ // Track referenceItems and group as dependencies
58
+ const currentItems = referenceItems;
36
59
  const currentGroup = group;
37
60
 
38
61
  // Use untrack to prevent writing to itemsWithState from triggering this effect again
39
62
  untrack(() => {
40
- // Rebuild itemsWithState from items, using group to determine checked state
63
+ // Rebuild itemsWithState from referenceItems, using group to determine checked state
41
64
  // Reassign instead of mutate to avoid circular dependency
42
65
  const newItems = currentItems.map((item) => ({
43
66
  ...item,
44
- isChecked: currentGroup.includes(item.value != null ? String(item.value) : '')
67
+ isChecked: currentGroup.includes(item.value as any)
45
68
  }));
46
69
  itemsWithState = newItems;
47
70
  });
@@ -1,11 +1,24 @@
1
- import type { ReferenceItem, ComponentSize } from '../../types/form.js';
1
+ import type { ComponentSize, FieldNameMapping } from '../../types/form.js';
2
2
  type $$ComponentProps = {
3
- group?: string[];
4
- items?: ReferenceItem[];
3
+ group?: (string | number)[];
4
+ items?: any[];
5
+ /**
6
+ * Maps database field names to ReferenceItem properties.
7
+ * Use this when your data uses different field names (e.g., 'name' instead of 'label').
8
+ *
9
+ * @example
10
+ * // Basic usage
11
+ * fieldNames={{ label: 'name', value: 'id' }}
12
+ *
13
+ * @example
14
+ * // With description field
15
+ * fieldNames={{ label: 'title', value: 'id', description: 'subtitle' }}
16
+ */
17
+ fieldNames?: FieldNameMapping | undefined;
5
18
  size?: ComponentSize;
6
19
  disabled?: boolean;
7
20
  required?: boolean;
8
- onChange?: ((selected: string[]) => void) | undefined;
21
+ onChange?: ((selected: (string | number)[]) => void) | undefined;
9
22
  label?: string;
10
23
  };
11
24
  declare const CheckBoxGroup: import("svelte").Component<$$ComponentProps, {}, "group">;
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
- import type { ReferenceItem, ComponentSize } from '../../types/form.js';
2
+ import type { ReferenceItem, ComponentSize, FieldNameMapping } from '../../types/form.js';
3
+ import { createFieldMapper } from '../../types/form.js';
3
4
  import FormField, { type FormFieldFeedback } from '../form-field/form-field.svelte';
4
5
  import { uniqueId } from '../../helpers/unique-id.js';
5
6
  import Menu from '../../generic/menu/menu.svelte';
@@ -12,8 +13,9 @@
12
13
  import type { CreateNewFunction, SearchFunction } from '../../types/form.js';
13
14
 
14
15
  let {
15
- value = $bindable(null as string | null),
16
- items = [] as ReferenceItem[],
16
+ value = $bindable(null as string | number | null),
17
+ items = [] as any[],
18
+ fieldNames = undefined as FieldNameMapping | undefined,
17
19
  size = 'md',
18
20
  disabled = false,
19
21
  required = false,
@@ -32,8 +34,21 @@
32
34
  createNew = undefined,
33
35
  resourceName = undefined
34
36
  }: {
35
- value?: string | null;
36
- items?: ReferenceItem[];
37
+ value?: string | number | null;
38
+ items?: any[];
39
+ /**
40
+ * Maps database field names to ReferenceItem properties.
41
+ * Use this when your data uses different field names (e.g., 'name' instead of 'label').
42
+ *
43
+ * @example
44
+ * // Basic usage
45
+ * fieldNames={{ label: 'name', value: 'id' }}
46
+ *
47
+ * @example
48
+ * // With description field
49
+ * fieldNames={{ label: 'title', value: 'id', description: 'subtitle' }}
50
+ */
51
+ fieldNames?: FieldNameMapping | undefined;
37
52
  size?: ComponentSize;
38
53
  disabled?: boolean;
39
54
  required?: boolean;
@@ -41,7 +56,7 @@
41
56
  searchable?: boolean;
42
57
  search?: SearchFunction | undefined;
43
58
  placeholder?: string;
44
- onChange?: ((value: string | null) => void) | undefined;
59
+ onChange?: ((value: string | number | null) => void) | undefined;
45
60
  onFocus?: ((e: FocusEvent) => void) | undefined;
46
61
  onBlur?: ((e: FocusEvent) => void) | undefined;
47
62
  label?: string;
@@ -56,9 +71,17 @@
56
71
  const id = uniqueId();
57
72
  const listboxId = `${id}-listbox`;
58
73
 
59
- // Use local items state when search function is provided, otherwise use prop
74
+ // Create field mapper
75
+ const mapper = $derived(createFieldMapper<any>(fieldNames));
76
+
77
+ // Transform items for internal use (always work with ReferenceItem internally)
78
+ const referenceItems = $derived(
79
+ fieldNames ? items.map(item => mapper.toReferenceItem(item)) : items as ReferenceItem[]
80
+ );
81
+
82
+ // Use local items state when search function is provided, otherwise use transformed items
60
83
  let localItems = $state<ReferenceItem[]>([]);
61
- let currentItems = $derived(search ? localItems : items);
84
+ let currentItems = $derived(search ? localItems : referenceItems);
62
85
 
63
86
  const getText = () => currentItems.find((item) => item.value == value)?.label || '';
64
87
 
@@ -78,10 +101,18 @@
78
101
  // Initialize localItems when items prop changes (only when no search function)
79
102
  $effect(() => {
80
103
  if (!search) {
81
- localItems = [...items];
104
+ localItems = [...referenceItems];
82
105
  }
83
106
  });
84
107
 
108
+ // Internal value for Menu component (always string | null)
109
+ let internalValue = $state<string | null>(value != null ? String(value) : null);
110
+
111
+ // Sync internal value from external value
112
+ $effect(() => {
113
+ internalValue = value != null ? String(value) : null;
114
+ });
115
+
85
116
  // Initialize text from value on mount and when value/items change (but not when user is typing)
86
117
  $effect(() => {
87
118
  // Track value and currentItems to update text when they change
@@ -141,7 +172,8 @@
141
172
  // When an item is selected from the dropdown menu
142
173
  const onSelect = (item: ReferenceItem) => {
143
174
  isUserTyping = false;
144
- value = item.value != null ? String(item.value) : null;
175
+ // Keep value as-is (string | number | null)
176
+ value = item.value;
145
177
  onChange?.(value);
146
178
  text = getText();
147
179
  isMenuOpen = false;
@@ -337,11 +369,12 @@
337
369
  const result = await createNew(name);
338
370
 
339
371
  if (result) {
340
- items = [...items, result];
372
+ // Note: items prop is read-only, so we can't update it
373
+ // Just add to localItems for display
341
374
  localItems = [...localItems, result];
342
375
 
343
376
  // Select the newly created item
344
- value = result.value != null ? String(result.value) : null;
377
+ value = result.value;
345
378
  onChange?.(value);
346
379
  text = result.label;
347
380
  isMenuOpen = false;
@@ -475,7 +508,7 @@
475
508
  searchText={isSearchable ? text : ''}
476
509
  {onSelect}
477
510
  bind:highlightIndex
478
- bind:value
511
+ bind:value={internalValue}
479
512
  {listboxId}
480
513
  {virtualScroll}
481
514
  {itemHeight}
@@ -1,9 +1,22 @@
1
- import type { ReferenceItem, ComponentSize } from '../../types/form.js';
1
+ import type { ReferenceItem, ComponentSize, FieldNameMapping } from '../../types/form.js';
2
2
  import { type FormFieldFeedback } from '../form-field/form-field.svelte';
3
3
  import type { CreateNewFunction, SearchFunction } from '../../types/form.js';
4
4
  type $$ComponentProps = {
5
- value?: string | null;
6
- items?: ReferenceItem[];
5
+ value?: string | number | null;
6
+ items?: any[];
7
+ /**
8
+ * Maps database field names to ReferenceItem properties.
9
+ * Use this when your data uses different field names (e.g., 'name' instead of 'label').
10
+ *
11
+ * @example
12
+ * // Basic usage
13
+ * fieldNames={{ label: 'name', value: 'id' }}
14
+ *
15
+ * @example
16
+ * // With description field
17
+ * fieldNames={{ label: 'title', value: 'id', description: 'subtitle' }}
18
+ */
19
+ fieldNames?: FieldNameMapping | undefined;
7
20
  size?: ComponentSize;
8
21
  disabled?: boolean;
9
22
  required?: boolean;
@@ -11,7 +24,7 @@ type $$ComponentProps = {
11
24
  searchable?: boolean;
12
25
  search?: SearchFunction | undefined;
13
26
  placeholder?: string;
14
- onChange?: ((value: string | null) => void) | undefined;
27
+ onChange?: ((value: string | number | null) => void) | undefined;
15
28
  onFocus?: ((e: FocusEvent) => void) | undefined;
16
29
  onBlur?: ((e: FocusEvent) => void) | undefined;
17
30
  label?: string;
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
- import type { ReferenceItem, ComponentSize } from '../../types/form.js';
2
+ import type { ReferenceItem, ComponentSize, FieldNameMapping } from '../../types/form.js';
3
+ import { createFieldMapper } from '../../types/form.js';
3
4
  import FormField, { type FormFieldFeedback } from '../form-field/form-field.svelte';
4
5
  import { uniqueId } from '../../helpers/unique-id.js';
5
6
  import RadioBox from './radio-box.svelte';
@@ -7,8 +8,9 @@
7
8
  const id = uniqueId();
8
9
 
9
10
  let {
10
- group = '',
11
+ group = $bindable(''),
11
12
  items = [],
13
+ fieldNames = undefined as FieldNameMapping | undefined,
12
14
  size = 'md' as ComponentSize,
13
15
  disabled = false,
14
16
  required = false,
@@ -17,22 +19,63 @@
17
19
  feedback = undefined,
18
20
  onChange = undefined
19
21
  }: {
20
- group?: string;
21
- items?: ReferenceItem[];
22
+ group?: string | number;
23
+ items?: any[];
24
+ /**
25
+ * Maps database field names to ReferenceItem properties.
26
+ * Use this when your data uses different field names (e.g., 'name' instead of 'label').
27
+ *
28
+ * @example
29
+ * // Basic usage
30
+ * fieldNames={{ label: 'name', value: 'id' }}
31
+ *
32
+ * @example
33
+ * // With description field
34
+ * fieldNames={{ label: 'title', value: 'id', description: 'subtitle' }}
35
+ */
36
+ fieldNames?: FieldNameMapping | undefined;
22
37
  size?: ComponentSize;
23
38
  disabled?: boolean;
24
39
  required?: boolean;
25
40
  label?: string;
26
41
  helperText?: string;
27
42
  feedback?: FormFieldFeedback;
28
- onChange?: ((value: string) => void) | undefined;
43
+ onChange?: ((value: string | number) => void) | undefined;
29
44
  } = $props();
45
+
46
+ // Create field mapper
47
+ const mapper = $derived(createFieldMapper<any>(fieldNames));
48
+
49
+ // Transform items for internal use (always work with ReferenceItem internally)
50
+ const referenceItems = $derived(
51
+ fieldNames ? items.map(item => mapper.toReferenceItem(item)) : items as ReferenceItem[]
52
+ );
53
+
54
+ // Internal string group for binding to RadioBox components
55
+ let internalGroup = $state<string>(String(group ?? ''));
56
+
57
+ // Sync internal group from external group
58
+ $effect(() => {
59
+ internalGroup = String(group ?? '');
60
+ });
61
+
62
+ // Handle change to convert back to original type
63
+ function handleChange(value: string) {
64
+ // Try to convert back to number if the original was a number
65
+ const numValue = Number(value);
66
+ if (!isNaN(numValue) && String(numValue) === value) {
67
+ group = numValue;
68
+ } else {
69
+ group = value;
70
+ }
71
+ onChange?.(group);
72
+ }
30
73
  </script>
31
74
 
32
75
  <FormField {size} {label} {id} {required} {disabled} {helperText} {feedback}>
33
76
  <div>
34
- {#each items as item}
35
- <RadioBox bind:group {disabled} value={item.value} {onChange}>{item.label}</RadioBox>
77
+ {#each referenceItems as item}
78
+ <RadioBox bind:group={internalGroup} {disabled} value={item.value} onChange={handleChange}>{item.label}</RadioBox>
36
79
  {/each}
37
80
  </div>
38
81
  </FormField>
@@ -1,16 +1,29 @@
1
- import type { ReferenceItem, ComponentSize } from '../../types/form.js';
1
+ import type { ComponentSize, FieldNameMapping } from '../../types/form.js';
2
2
  import { type FormFieldFeedback } from '../form-field/form-field.svelte';
3
3
  type $$ComponentProps = {
4
- group?: string;
5
- items?: ReferenceItem[];
4
+ group?: string | number;
5
+ items?: any[];
6
+ /**
7
+ * Maps database field names to ReferenceItem properties.
8
+ * Use this when your data uses different field names (e.g., 'name' instead of 'label').
9
+ *
10
+ * @example
11
+ * // Basic usage
12
+ * fieldNames={{ label: 'name', value: 'id' }}
13
+ *
14
+ * @example
15
+ * // With description field
16
+ * fieldNames={{ label: 'title', value: 'id', description: 'subtitle' }}
17
+ */
18
+ fieldNames?: FieldNameMapping | undefined;
6
19
  size?: ComponentSize;
7
20
  disabled?: boolean;
8
21
  required?: boolean;
9
22
  label?: string;
10
23
  helperText?: string;
11
24
  feedback?: FormFieldFeedback;
12
- onChange?: ((value: string) => void) | undefined;
25
+ onChange?: ((value: string | number) => void) | undefined;
13
26
  };
14
- declare const RadioGroup: import("svelte").Component<$$ComponentProps, {}, "">;
27
+ declare const RadioGroup: import("svelte").Component<$$ComponentProps, {}, "group">;
15
28
  type RadioGroup = ReturnType<typeof RadioGroup>;
16
29
  export default RadioGroup;
@@ -1,4 +1,4 @@
1
- <script lang="ts">
1
+ <script lang="ts" generics="T = ReferenceItem">
2
2
  import MultiSelectBase, {
3
3
  type MultiSelectAdapter
4
4
  } from '../multi-select-base/multi-select-base.svelte';
@@ -8,15 +8,18 @@
8
8
  ReferenceItem,
9
9
  SearchFunction,
10
10
  CreateNewFunction,
11
- LinkBuilderFunction
11
+ LinkBuilderFunction,
12
+ FieldNameMapping
12
13
  } from '../../types/form.js';
14
+ import { createFieldMapper } from '../../types/form.js';
13
15
  import Prompt from '../../modals/prompt.svelte';
14
16
  import { ucfirst } from '../../helpers/ucfirst.js';
15
17
  import Icon from '../../icons/icon.svelte';
16
18
 
17
19
  let {
18
- value = $bindable([] as ReferenceItem[]),
19
- items = [] as ReferenceItem[],
20
+ value = $bindable([] as T[]),
21
+ items = [] as T[],
22
+ fieldNames = undefined as FieldNameMapping | undefined,
20
23
  search = undefined,
21
24
  createNew = undefined,
22
25
  linkBuilder = undefined as LinkBuilderFunction | undefined,
@@ -29,10 +32,23 @@
29
32
  helperText = undefined as string | undefined,
30
33
  feedback = undefined as FormFieldFeedback | undefined,
31
34
  maxItems = undefined as number | undefined,
32
- onChange = undefined as ((value: ReferenceItem[]) => void) | undefined
35
+ onChange = undefined as ((value: T[]) => void) | undefined
33
36
  }: {
34
- value?: ReferenceItem[];
35
- items?: ReferenceItem[];
37
+ value?: T[];
38
+ items?: T[];
39
+ /**
40
+ * Maps database field names to ReferenceItem properties.
41
+ * Use this when your data uses different field names (e.g., 'name' instead of 'label').
42
+ *
43
+ * @example
44
+ * // Basic usage
45
+ * fieldNames={{ label: 'name', value: 'id' }}
46
+ *
47
+ * @example
48
+ * // With description field
49
+ * fieldNames={{ label: 'title', value: 'id', description: 'subtitle' }}
50
+ */
51
+ fieldNames?: FieldNameMapping | undefined;
36
52
  search?: SearchFunction | undefined;
37
53
  createNew?: CreateNewFunction<ReferenceItem> | undefined;
38
54
  linkBuilder?: LinkBuilderFunction | undefined;
@@ -45,7 +61,7 @@
45
61
  helperText?: string;
46
62
  feedback?: FormFieldFeedback | undefined;
47
63
  maxItems?: number | undefined;
48
- onChange?: ((value: ReferenceItem[]) => void) | undefined;
64
+ onChange?: ((value: T[]) => void) | undefined;
49
65
  } = $props();
50
66
 
51
67
  let baseComponent: MultiSelectBase<ReferenceItem> | null = $state(null);
@@ -57,10 +73,40 @@
57
73
  let promptKey = $state(0);
58
74
  let currentSearchText = $state('');
59
75
 
60
- // Use local items when search function is provided, otherwise use static items
61
- let currentItems = $derived(search ? localItems : items);
76
+ // Create field mapper
77
+ const mapper = $derived(createFieldMapper<T>(fieldNames));
62
78
 
63
- // Convert ReferenceItem[] to menu options format
79
+ // Transform items for internal use (always work with ReferenceItem internally)
80
+ const referenceItems = $derived(
81
+ fieldNames ? items.map(item => mapper.toReferenceItem(item)) : items as unknown as ReferenceItem[]
82
+ );
83
+
84
+ // Internal value state (always ReferenceItem[])
85
+ let internalValue = $state<ReferenceItem[]>([]);
86
+
87
+ // Sync internal value from external value
88
+ $effect(() => {
89
+ if (fieldNames) {
90
+ internalValue = value.map(item => mapper.toReferenceItem(item));
91
+ } else {
92
+ internalValue = value as unknown as ReferenceItem[];
93
+ }
94
+ });
95
+
96
+ // Update external value from internal value
97
+ function updateExternalValue(newInternalValue: ReferenceItem[]) {
98
+ if (fieldNames) {
99
+ value = newInternalValue.map(ref => mapper.fromReferenceItem(ref));
100
+ } else {
101
+ value = newInternalValue as unknown as T[];
102
+ }
103
+ onChange?.(value);
104
+ }
105
+
106
+ // Use local items when search function is provided, otherwise use transformed items
107
+ let currentItems = $derived(search ? localItems : referenceItems);
108
+
109
+ // Convert to menu options format
64
110
  let menuOptions = $derived(currentItems);
65
111
 
66
112
  // Adapter to work with ReferenceItems
@@ -69,7 +115,15 @@
69
115
  getKey: (item: ReferenceItem) => String(item.value ?? item.label),
70
116
  equals: (a: ReferenceItem, b: ReferenceItem) => a.value === b.value,
71
117
  fromMenuOption: (option: ReferenceItem) => option,
72
- getLink: linkBuilder ? (item: ReferenceItem) => linkBuilder(item) : undefined,
118
+ getLink: linkBuilder
119
+ ? (item: ReferenceItem) => {
120
+ // Find original item to pass to linkBuilder
121
+ const original = fieldNames
122
+ ? value.find(v => mapper.toReferenceItem(v).value === item.value)
123
+ : item;
124
+ return linkBuilder(original as any);
125
+ }
126
+ : undefined,
73
127
  getTooltip: (item: ReferenceItem) => item.description
74
128
  });
75
129
 
@@ -122,9 +176,10 @@
122
176
  localItems = [...localItems, result];
123
177
  }
124
178
 
125
- // Add the newly created item to value
126
- value = [...value, result];
127
- onChange?.(value);
179
+ // Add the newly created item to internal value, then sync to external
180
+ const newInternalValue = [...internalValue, result];
181
+ internalValue = newInternalValue;
182
+ updateExternalValue(newInternalValue);
128
183
  showPrompt = false;
129
184
  } else {
130
185
  createError = 'Failed to create new item';
@@ -158,7 +213,7 @@
158
213
 
159
214
  <MultiSelectBase
160
215
  bind:this={baseComponent}
161
- bind:value
216
+ bind:value={internalValue}
162
217
  adapter={referenceAdapter}
163
218
  {placeholder}
164
219
  {required}
@@ -169,7 +224,7 @@
169
224
  {helperText}
170
225
  {feedback}
171
226
  {maxItems}
172
- {onChange}
227
+ onChange={updateExternalValue}
173
228
  onInputChange={handleInputChange}
174
229
  {isLoading}
175
230
  filterSuggestions={!search}
@@ -1,22 +1,55 @@
1
1
  import type { FormFieldFeedback } from '../form-field/form-field.svelte';
2
- import type { ComponentSize, ReferenceItem, SearchFunction, CreateNewFunction, LinkBuilderFunction } from '../../types/form.js';
3
- type $$ComponentProps = {
4
- value?: ReferenceItem[];
5
- items?: ReferenceItem[];
6
- search?: SearchFunction | undefined;
7
- createNew?: CreateNewFunction<ReferenceItem> | undefined;
8
- linkBuilder?: LinkBuilderFunction | undefined;
9
- resourceName?: string | undefined;
10
- placeholder?: string;
11
- required?: boolean;
12
- disabled?: boolean;
13
- size?: ComponentSize;
14
- label?: string;
15
- helperText?: string;
16
- feedback?: FormFieldFeedback | undefined;
17
- maxItems?: number | undefined;
18
- onChange?: ((value: ReferenceItem[]) => void) | undefined;
2
+ import type { ComponentSize, ReferenceItem, SearchFunction, CreateNewFunction, LinkBuilderFunction, FieldNameMapping } from '../../types/form.js';
3
+ declare function $$render<T = ReferenceItem>(): {
4
+ props: {
5
+ value?: T[];
6
+ items?: T[];
7
+ /**
8
+ * Maps database field names to ReferenceItem properties.
9
+ * Use this when your data uses different field names (e.g., 'name' instead of 'label').
10
+ *
11
+ * @example
12
+ * // Basic usage
13
+ * fieldNames={{ label: 'name', value: 'id' }}
14
+ *
15
+ * @example
16
+ * // With description field
17
+ * fieldNames={{ label: 'title', value: 'id', description: 'subtitle' }}
18
+ */
19
+ fieldNames?: FieldNameMapping | undefined;
20
+ search?: SearchFunction | undefined;
21
+ createNew?: CreateNewFunction<ReferenceItem> | undefined;
22
+ linkBuilder?: LinkBuilderFunction | undefined;
23
+ resourceName?: string | undefined;
24
+ placeholder?: string;
25
+ required?: boolean;
26
+ disabled?: boolean;
27
+ size?: ComponentSize;
28
+ label?: string;
29
+ helperText?: string;
30
+ feedback?: FormFieldFeedback | undefined;
31
+ maxItems?: number | undefined;
32
+ onChange?: ((value: T[]) => void) | undefined;
33
+ };
34
+ exports: {};
35
+ bindings: "value";
36
+ slots: {};
37
+ events: {};
19
38
  };
20
- declare const ReferenceBox: import("svelte").Component<$$ComponentProps, {}, "value">;
21
- type ReferenceBox = ReturnType<typeof ReferenceBox>;
39
+ declare class __sveltets_Render<T = ReferenceItem> {
40
+ props(): ReturnType<typeof $$render<T>>['props'];
41
+ events(): ReturnType<typeof $$render<T>>['events'];
42
+ slots(): ReturnType<typeof $$render<T>>['slots'];
43
+ bindings(): "value";
44
+ exports(): {};
45
+ }
46
+ interface $$IsomorphicComponent {
47
+ new <T = ReferenceItem>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
48
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
49
+ } & ReturnType<__sveltets_Render<T>['exports']>;
50
+ <T = ReferenceItem>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
51
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
52
+ }
53
+ declare const ReferenceBox: $$IsomorphicComponent;
54
+ type ReferenceBox<T = ReferenceItem> = InstanceType<typeof ReferenceBox<T>>;
22
55
  export default ReferenceBox;
@@ -33,3 +33,78 @@ export type CreateNewFunction<ReferenceItem> = (inputName: string) => Promise<Re
33
33
  * Link builder function type
34
34
  */
35
35
  export type LinkBuilderFunction = (item: ReferenceItem) => string | undefined;
36
+ /**
37
+ * Maps database object field names to ReferenceItem properties.
38
+ * Allows components to work directly with your database objects.
39
+ *
40
+ * @example
41
+ * // Basic usage - map database fields to display format
42
+ * fieldNames={{ label: 'name', value: 'id' }}
43
+ *
44
+ * @example
45
+ * // With optional fields
46
+ * fieldNames={{
47
+ * label: 'title',
48
+ * value: 'id',
49
+ * description: 'subtitle',
50
+ * disabled: 'isInactive'
51
+ * }}
52
+ */
53
+ export type FieldNameMapping = {
54
+ /**
55
+ * Database field name to use for display label
56
+ * @default 'label'
57
+ */
58
+ label?: string;
59
+ /**
60
+ * Database field name to use for unique value/ID
61
+ * @default 'value'
62
+ */
63
+ value?: string;
64
+ /**
65
+ * Database field name to use for description/subtitle text
66
+ * @default 'description'
67
+ */
68
+ description?: string;
69
+ /**
70
+ * Database field name to use for disabled state
71
+ * @default 'disabled'
72
+ */
73
+ disabled?: string;
74
+ };
75
+ /**
76
+ * Creates field mapping transformation functions.
77
+ * Used internally by components to convert between user types and ReferenceItem.
78
+ *
79
+ * @internal
80
+ */
81
+ export declare function createFieldMapper<T>(fieldNames?: FieldNameMapping): {
82
+ /**
83
+ * Converts user's database object to ReferenceItem for internal use
84
+ */
85
+ toReferenceItem: (item: T) => ReferenceItem;
86
+ /**
87
+ * Converts ReferenceItem back to user's database object type
88
+ * Note: Only reconstructs mapped fields, additional fields are lost
89
+ */
90
+ fromReferenceItem: (ref: ReferenceItem) => T;
91
+ /**
92
+ * Extracts just the value field from user's object
93
+ */
94
+ extractValue: (item: T) => string | number | null;
95
+ /**
96
+ * Finds an item in array by matching value field
97
+ */
98
+ findByValue: (items: T[], value: string | number | null) => T | undefined;
99
+ };
100
+ /**
101
+ * Helper function to create field name mappings with less syntax.
102
+ *
103
+ * @example
104
+ * // Instead of:
105
+ * fieldNames={{ label: 'name', value: 'id' }}
106
+ *
107
+ * // You can write:
108
+ * fieldNames={mapFields('name', 'id')}
109
+ */
110
+ export declare function mapFields(label: string, value: string, description?: string, disabled?: string): FieldNameMapping;
@@ -1 +1,73 @@
1
- export {};
1
+ /**
2
+ * Creates field mapping transformation functions.
3
+ * Used internally by components to convert between user types and ReferenceItem.
4
+ *
5
+ * @internal
6
+ */
7
+ export function createFieldMapper(fieldNames) {
8
+ const labelField = fieldNames?.label ?? 'label';
9
+ const valueField = fieldNames?.value ?? 'value';
10
+ const descriptionField = fieldNames?.description ?? 'description';
11
+ const disabledField = fieldNames?.disabled ?? 'disabled';
12
+ return {
13
+ /**
14
+ * Converts user's database object to ReferenceItem for internal use
15
+ */
16
+ toReferenceItem: (item) => {
17
+ const obj = item;
18
+ return {
19
+ label: String(obj[labelField] ?? ''),
20
+ value: obj[valueField] ?? null,
21
+ description: obj[descriptionField],
22
+ disabled: obj[disabledField]
23
+ };
24
+ },
25
+ /**
26
+ * Converts ReferenceItem back to user's database object type
27
+ * Note: Only reconstructs mapped fields, additional fields are lost
28
+ */
29
+ fromReferenceItem: (ref) => {
30
+ const obj = {
31
+ [labelField]: ref.label,
32
+ [valueField]: ref.value
33
+ };
34
+ if (ref.description !== undefined) {
35
+ obj[descriptionField] = ref.description;
36
+ }
37
+ if (ref.disabled !== undefined) {
38
+ obj[disabledField] = ref.disabled;
39
+ }
40
+ return obj;
41
+ },
42
+ /**
43
+ * Extracts just the value field from user's object
44
+ */
45
+ extractValue: (item) => {
46
+ return item[valueField] ?? null;
47
+ },
48
+ /**
49
+ * Finds an item in array by matching value field
50
+ */
51
+ findByValue: (items, value) => {
52
+ return items.find(item => item[valueField] === value);
53
+ }
54
+ };
55
+ }
56
+ /**
57
+ * Helper function to create field name mappings with less syntax.
58
+ *
59
+ * @example
60
+ * // Instead of:
61
+ * fieldNames={{ label: 'name', value: 'id' }}
62
+ *
63
+ * // You can write:
64
+ * fieldNames={mapFields('name', 'id')}
65
+ */
66
+ export function mapFields(label, value, description, disabled) {
67
+ return {
68
+ label,
69
+ value,
70
+ ...(description && { description }),
71
+ ...(disabled && { disabled })
72
+ };
73
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sveltacular",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "A Svelte component library",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",