@stonecrop/aform 0.11.8 → 0.11.10

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 ADDED
@@ -0,0 +1,134 @@
1
+ # @stonecrop/aform
2
+
3
+ Schema-driven form components for the Stonecrop framework. Renders a `SchemaTypes[]` array into a form, wiring field values to a `data` object via `v-model:data`.
4
+
5
+ ## Components
6
+
7
+ | Component | Description |
8
+ |---|---|
9
+ | `AForm` | Root form renderer — iterates schema, renders child components, handles nested forms |
10
+ | `ACheckbox` | Boolean toggle |
11
+ | `AComboBox` | Editable combo box with option list |
12
+ | `ADate` | Date text input |
13
+ | `ADatePicker` | Date picker with calendar UI |
14
+ | `ADropdown` | Single-select dropdown for string enum fields |
15
+ | `AFieldset` | Collapsible grouping container for other fields |
16
+ | `AFileAttach` | File upload and attachment |
17
+ | `AFormLink` | Linked document selector with search dropdown and navigation arrow |
18
+ | `ANumericInput` | Numeric input with type-specific formatting |
19
+ | `ATextInput` | Single-line text input |
20
+
21
+ ## Installation
22
+
23
+ ```typescript
24
+ import { install } from '@stonecrop/aform'
25
+
26
+ app.use(install)
27
+ ```
28
+
29
+ This registers all components globally. They can also be imported individually.
30
+
31
+ ---
32
+
33
+ ## AFormLink
34
+
35
+ A form input for selecting and navigating to linked documents (`fieldtype: 'Link'`). Combines a searchable text input, an optional dropdown of results, and a navigation arrow button.
36
+
37
+ ### Value shape
38
+
39
+ ```typescript
40
+ interface AFormLinkValue {
41
+ id: string | number // the linked record's ID; id: 0 is valid
42
+ displayText?: string // shown in the input; falls back to String(id)
43
+ [extra: string]: any // extra fields available to formatter
44
+ }
45
+ ```
46
+
47
+ When `id` is falsy, the component shows a `—` placeholder and hides the navigation arrow.
48
+
49
+ ### Props
50
+
51
+ ```typescript
52
+ {
53
+ modelValue: AFormLinkValue
54
+ label?: string
55
+ mode?: 'edit' | 'read' | 'display'
56
+ doctype?: string // target doctype slug — used by the navigation arrow
57
+ filterFunction?: (search: string) => AFormLinkValue[] | Promise<AFormLinkValue[]>
58
+ isAsync?: boolean // show loading indicator while filterFunction resolves
59
+ formatter?: (value: AFormLinkValue) => string // custom display text transform
60
+ icon?: 'arrow-right' | 'chevron-right' // navigation arrow icon
61
+ disabled?: boolean
62
+ }
63
+ ```
64
+
65
+ ### Modes
66
+
67
+ | Mode | Input | Arrow | Dropdown |
68
+ |-----------|----------|----------------------|---------------------|
69
+ | `edit` | Enabled | Visible (if has id) | Opens on focus/type |
70
+ | `read` | Disabled | Visible (if has id) | Never opens |
71
+ | `display` | Hidden | Hidden | — |
72
+
73
+ ### Filter function
74
+
75
+ Provide `filterFunction` to enable the search dropdown. The function receives the current input text and must return `AFormLinkValue[]` or a `Promise<AFormLinkValue[]>`.
76
+
77
+ ```typescript
78
+ // Sync
79
+ const filterFunction = (search: string): AFormLinkValue[] =>
80
+ records
81
+ .filter(r => r.name.toLowerCase().includes(search.toLowerCase()))
82
+ .map(r => ({ id: r.id, displayText: r.name }))
83
+
84
+ // Async — set isAsync: true for loading indicator
85
+ const filterFunction = async (search: string): Promise<AFormLinkValue[]> => {
86
+ const results = await api.search(search)
87
+ return results.map(r => ({ id: r.id, displayText: r.name }))
88
+ }
89
+ ```
90
+
91
+ ### Navigation
92
+
93
+ AFormLink injects `aformLinkNavigator` from the app layer rather than depending on vue-router directly. Provide it once in your app plugin:
94
+
95
+ ```typescript
96
+ import type { AFormLinkNavigator } from '@stonecrop/aform'
97
+
98
+ app.provide('aformLinkNavigator', {
99
+ navigate(doctype: string, id: string | number) {
100
+ router.push(`/${doctype}/${id}`)
101
+ },
102
+ } satisfies AFormLinkNavigator)
103
+ ```
104
+
105
+ ```typescript
106
+ interface AFormLinkNavigator {
107
+ navigate(doctype: string, id: string | number): void
108
+ }
109
+ ```
110
+
111
+ If no navigator is provided, the arrow button is still rendered but navigation clicks are silent no-ops.
112
+
113
+ ### Via resolveSchema
114
+
115
+ For `fieldtype: 'Link'` fields with no matching `links` declaration, `Registry.resolveSchema()` automatically assigns `component: 'AFormLink'` and sets `doctype` from `field.options`. No manual wiring required:
116
+
117
+ ```typescript
118
+ const config: DoctypeConfig = {
119
+ slug: 'sales-order',
120
+ fields: [
121
+ { fieldname: 'order_number', fieldtype: 'Data', component: 'ATextInput', label: 'Order Number' },
122
+ { fieldname: 'territory', fieldtype: 'Link', options: 'territory', label: 'Territory' },
123
+ // no 'links' entry for territory
124
+ ],
125
+ }
126
+
127
+ registry.addDoctype(Doctype.fromObject(config))
128
+ const resolved = registry.resolveSchema(registry.registry['sales-order'])
129
+ // resolved[1] === { fieldname: 'territory', component: 'AFormLink', doctype: 'territory', label: 'Territory' }
130
+
131
+ // Pass to AForm as normal — the territory field renders as AFormLink automatically
132
+ ```
133
+
134
+ Declared links (those with a `links` entry and a registered target doctype) are unaffected — they continue to resolve as embedded `AForm` (1:1) or `ATable` (1:many) entries.
package/dist/aform.d.ts CHANGED
@@ -6,6 +6,7 @@ import ADropdown from './components/form/ADropdown.vue';
6
6
  import AFieldset from './components/form/AFieldset.vue';
7
7
  import AFileAttach from './components/form/AFileAttach.vue';
8
8
  import AForm from './components/AForm.vue';
9
+ import AFormLink from './components/form/AFormLink.vue';
9
10
  import ANumericInput from './components/form/ANumericInput.vue';
10
11
  import type { App } from 'vue';
11
12
  import ATextInput from './components/form/ATextInput.vue';
@@ -30,6 +31,29 @@ export { AFileAttach }
30
31
 
31
32
  export { AForm }
32
33
 
34
+ export { AFormLink }
35
+
36
+ /**
37
+ * Navigation contract for AFormLink. Provide via `provide('aformLinkNavigator', ...)` in the app plugin.
38
+ * @public
39
+ */
40
+ export declare interface AFormLinkNavigator {
41
+ /** Navigate to the linked document. Implementation is app-defined. */
42
+ navigate(doctype: string, id: string | number): void;
43
+ }
44
+
45
+ /**
46
+ * The value shape for AFormLink — a linked document reference with optional display text
47
+ * @public
48
+ */
49
+ export declare interface AFormLinkValue {
50
+ /** The FK/linked document ID. `id: 0` is a valid ID. */
51
+ id: string | number;
52
+ /** Display text shown in the input. Falls back to `String(id)` if omitted. */
53
+ displayText?: string;
54
+ [extra: string]: any;
55
+ }
56
+
33
57
  export { ANumericInput }
34
58
 
35
59
  export { ATextInput }
@@ -59,6 +83,12 @@ export declare type BaseSchema = {
59
83
  * @public
60
84
  */
61
85
  mode?: FormMode;
86
+ /**
87
+ * Hide the field from the form UI while keeping it in the data model.
88
+ * Consumed by AForm — not passed down to field components.
89
+ * @public
90
+ */
91
+ hidden?: boolean;
62
92
  };
63
93
 
64
94
  /**
@@ -113,6 +143,22 @@ export declare type ComponentProps = {
113
143
  };
114
144
  };
115
145
 
146
+ /**
147
+ * Deserializes a stringified function expression into a typed callable.
148
+ *
149
+ * Throws if the string cannot be parsed as a function (SyntaxError) or if the
150
+ * resulting expression is not callable (TypeError), or if the expression references
151
+ * an undefined variable (ReferenceError). Callers are responsible for try/catch.
152
+ *
153
+ * @example
154
+ * ```ts
155
+ * const fn = deserializeFunction<(x: number) => number>('(x) => x * 2')
156
+ * fn(5) // 10
157
+ * ```
158
+ * @public
159
+ */
160
+ export declare function deserializeFunction<T extends (...args: any[]) => any>(source: string): T;
161
+
116
162
  /**
117
163
  * Schema structure for defining fieldsets inside AForm
118
164
  * @public