@startupjs-ui/object-input 0.1.3

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/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
+
6
+ ## [0.1.3](https://github.com/startupjs/startupjs-ui/compare/v0.1.2...v0.1.3) (2025-12-29)
7
+
8
+ **Note:** Version bump only for package @startupjs-ui/object-input
9
+
10
+
11
+
12
+
13
+
14
+ ## [0.1.2](https://github.com/startupjs/startupjs-ui/compare/v0.1.1...v0.1.2) (2025-12-29)
15
+
16
+
17
+ ### Features
18
+
19
+ * add mdx and docs packages. Refactor docs to get rid of any @startupjs/ui usage and use startupjs-ui instead ([703c926](https://github.com/startupjs/startupjs-ui/commit/703c92636efb0421ffd11783f692fc892b74018f))
20
+ * **object-input:** refactor ObjectInput component ([f21693c](https://github.com/startupjs/startupjs-ui/commit/f21693c7f2a31198f445ec3656fb780feb2269bd))
package/README.mdx ADDED
@@ -0,0 +1,236 @@
1
+ import { useMemo } from 'react'
2
+ import { $ } from 'startupjs'
3
+ import ObjectInput, { _PropsJsonSchema as ObjectInputPropsJsonSchema } from './index'
4
+ import { Sandbox } from '@startupjs-ui/docs'
5
+
6
+ # ObjectInput
7
+
8
+ ObjectInput lets you build dynamic forms using its declarative API.
9
+
10
+ ObjectInput accepts an inputs metadata object with a required `input` key to specify what type of input to display in the `properties` property.
11
+
12
+ Possible types are: [array](/docs/forms/Array), [checkbox](/docs/forms/Checkbox), [date](/docs/forms/DateTimePicker), [datetime](/docs/forms/DateTimePicker), [multiselect](/docs/forms/Multiselect), [number](/docs/forms/NumberInput), [object](/docs/forms/ObjectInput), [password](/docs/forms/PasswordInput), [radio](/docs/forms/Radio), [select](/docs/forms/Select), [time](/docs/forms/DateTimePicker), [text](/docs/forms/TextInput).
13
+
14
+ ```jsx
15
+ import { ObjectInput } from 'startupjs-ui'
16
+ ```
17
+
18
+ ## Simple example
19
+
20
+ It's important to have `properties` object defined as a constant either outside of your component or cached with `useMemo()`, otherwise component is going to re-render the whole form tree whenever you are editing one of the field.
21
+
22
+ ```jsx example
23
+ const $value = $()
24
+ const properties = useMemo(() => ({
25
+ email: {
26
+ input: 'text',
27
+ label: 'Email'
28
+ },
29
+ password: {
30
+ input: 'text',
31
+ label: 'Password',
32
+ description: "Make sure it's at least 15 characters OR at least 8 characters including a number and a lowercase letter"
33
+ }
34
+ }), [])
35
+
36
+ return (
37
+ <ObjectInput
38
+ $value={$value}
39
+ properties={properties}
40
+ />
41
+ )
42
+ ```
43
+
44
+ ## Row
45
+
46
+ You can show nested inputs in a row by passing the `row` flag.
47
+
48
+ ```jsx example
49
+ const $value = $()
50
+ const properties = {
51
+ email: {
52
+ input: 'text',
53
+ label: 'Email'
54
+ },
55
+ password: {
56
+ input: 'text',
57
+ label: 'Password',
58
+ description: "Make sure it's at least 15 characters OR at least 8 characters including a number and a lowercase letter"
59
+ }
60
+ }
61
+
62
+ return (
63
+ <ObjectInput
64
+ $value={$value}
65
+ properties={properties}
66
+ row
67
+ />
68
+ )
69
+ ```
70
+
71
+ ## Inputs order
72
+
73
+ ObjectInput accepts an array in the `order` property to specify in what order should the inputs be displayed.
74
+
75
+ ```jsx example
76
+ const $value = $()
77
+
78
+ return (
79
+ <ObjectInput
80
+ $value={$value}
81
+ order={['password', 'email']}
82
+ properties={{
83
+ email: {
84
+ input: 'text',
85
+ label: 'Email'
86
+ },
87
+ password: {
88
+ input: 'text',
89
+ label: 'Password',
90
+ description: "Make sure it's at least 15 characters OR at least 8 characters including a number and a lowercase letter"
91
+ }
92
+ }}
93
+ />
94
+ )
95
+ ```
96
+
97
+ ## Displaying errors
98
+
99
+ The `errors` property accepts an object with error texts for inputs, where the key is the input key from the `properties` object and its value is the error text.
100
+
101
+ ```jsx example
102
+ const $value = $()
103
+
104
+ return (
105
+ <ObjectInput
106
+ $value={$value}
107
+ errors={{
108
+ email: 'Email input error',
109
+ password: 'Password input error'
110
+ }}
111
+ properties={{
112
+ email: {
113
+ input: 'text',
114
+ label: 'Email'
115
+ },
116
+ password: {
117
+ input: 'text',
118
+ label: 'Password',
119
+ description: "Make sure it's at least 15 characters OR at least 8 characters including a number and a lowercase letter"
120
+ }
121
+ }}
122
+ />
123
+ )
124
+ ```
125
+
126
+ ## Disabled
127
+
128
+ ```jsx example
129
+ const $value = $()
130
+
131
+ return (
132
+ <ObjectInput
133
+ $value={$value}
134
+ properties={{
135
+ email: {
136
+ input: 'text',
137
+ label: 'Email'
138
+ },
139
+ password: {
140
+ input: 'text',
141
+ label: 'Password',
142
+ description: "Make sure it's at least 15 characters OR at least 8 characters including a number and a lowercase letter"
143
+ }
144
+ }}
145
+ disabled
146
+ />
147
+ )
148
+ ```
149
+
150
+ ## Readonly
151
+
152
+ ```jsx example
153
+ const $value = $({ email: 'startupjs@gmail.com', password: '123456' })
154
+
155
+ return (
156
+ <ObjectInput
157
+ $value={$value}
158
+ properties={{
159
+ email: {
160
+ input: 'text',
161
+ label: 'Email'
162
+ },
163
+ password: {
164
+ input: 'text',
165
+ label: 'Password',
166
+ description: "Make sure it's at least 15 characters OR at least 8 characters including a number and a lowercase letter"
167
+ }
168
+ }}
169
+ readonly
170
+ />
171
+ )
172
+ ```
173
+
174
+ ## Advanced usage
175
+
176
+ ObjectInput supports `dependsOn` and `dependsValue` properties for each input object in `properties` to dynamically display inputs.
177
+
178
+ - `dependsOn` is used to specify the object key which the current input depends on
179
+ - `dependsValue` is used to specify at what value of the input with key `dependsOn` should the current input be shown
180
+
181
+ In the example below the dependent `password` input will only be shown if the `email` input is not empty.
182
+
183
+ **Caution**: When the `dependsOn` field changes and dependent field is no longer visible its previous value is preserved.
184
+
185
+ ```jsx example
186
+ const $value = $()
187
+
188
+ return (
189
+ <ObjectInput
190
+ $value={$value}
191
+ properties={{
192
+ email: {
193
+ input: 'text',
194
+ label: 'Email'
195
+ },
196
+ password: {
197
+ input: 'text',
198
+ label: 'Password',
199
+ dependsOn: 'email',
200
+ description: "Make sure it's at least 15 characters OR at least 8 characters including a number and a lowercase letter"
201
+ }
202
+ }}
203
+ />
204
+ )
205
+ ```
206
+
207
+ ## Sandbox
208
+
209
+ export function SandboxWrapper () {
210
+ const $value = $()
211
+ const properties = useMemo(() => ({
212
+ email: {
213
+ input: 'text',
214
+ label: 'Email'
215
+ },
216
+ password: {
217
+ input: 'text',
218
+ label: 'Password',
219
+ description: "Make sure it's at least 15 characters OR at least 8 characters including a number and a lowercase letter"
220
+ }
221
+ }), [])
222
+
223
+ return (
224
+ <Sandbox
225
+ Component={ObjectInput}
226
+ propsJsonSchema={ObjectInputPropsJsonSchema}
227
+ props={{
228
+ $value,
229
+ properties
230
+ }}
231
+ block
232
+ />
233
+ )
234
+ }
235
+
236
+ <SandboxWrapper />
@@ -0,0 +1,8 @@
1
+ .input
2
+ &.row
3
+ flex 1
4
+ &.push
5
+ &.column
6
+ margin-top 2u
7
+ &.row
8
+ margin-left 2u
package/index.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ /* eslint-disable */
2
+ // DO NOT MODIFY THIS FILE - IT IS AUTOMATICALLY GENERATED ON COMMITS.
3
+
4
+ import { type ReactNode } from 'react';
5
+ import { type StyleProp, type ViewStyle } from 'react-native';
6
+ import './index.cssx.styl';
7
+ declare const _default: import("react").ComponentType<ObjectInputProps>;
8
+ export default _default;
9
+ export declare const _PropsJsonSchema: {};
10
+ export interface ObjectInputProps {
11
+ /** Custom styles for the wrapper */
12
+ style?: StyleProp<ViewStyle>;
13
+ /** Custom styles for the inner input container */
14
+ inputStyle?: StyleProp<ViewStyle>;
15
+ /** Model binding for object values */
16
+ $value: any;
17
+ /** Error messages keyed by property name @default {} */
18
+ errors?: Record<string, any>;
19
+ /** Input metadata keyed by property name */
20
+ properties: Record<string, any>;
21
+ /** Order of rendered inputs */
22
+ order?: string[];
23
+ /** Render inputs in a row */
24
+ row?: boolean;
25
+ /** Disable interactions */
26
+ disabled?: boolean;
27
+ /** Render as read-only */
28
+ readonly?: boolean;
29
+ /** Custom wrapper renderer (used by Input layout wrappers) */
30
+ _renderWrapper?: (params: {
31
+ style: StyleProp<ViewStyle> | undefined;
32
+ }, children: ReactNode) => ReactNode;
33
+ }
package/index.tsx ADDED
@@ -0,0 +1,123 @@
1
+ import { type ReactNode } from 'react'
2
+ import { type StyleProp, type ViewStyle } from 'react-native'
3
+ import { pug, observer } from 'startupjs'
4
+ import { themed } from '@startupjs-ui/core'
5
+ import Div from '@startupjs-ui/div'
6
+ import Input from '@startupjs-ui/input'
7
+ import './index.cssx.styl'
8
+
9
+ export default observer(
10
+ themed('ObjectInput', ObjectInput)
11
+ )
12
+
13
+ export const _PropsJsonSchema = {/* ObjectInputProps */}
14
+
15
+ export interface ObjectInputProps {
16
+ /** Custom styles for the wrapper */
17
+ style?: StyleProp<ViewStyle>
18
+ /** Custom styles for the inner input container */
19
+ inputStyle?: StyleProp<ViewStyle>
20
+ /** Model binding for object values */
21
+ $value: any
22
+ /** Error messages keyed by property name @default {} */
23
+ errors?: Record<string, any>
24
+ /** Input metadata keyed by property name */
25
+ properties: Record<string, any>
26
+ /** Order of rendered inputs */
27
+ order?: string[]
28
+ /** Render inputs in a row */
29
+ row?: boolean
30
+ /** Disable interactions */
31
+ disabled?: boolean
32
+ /** Render as read-only */
33
+ readonly?: boolean
34
+ /** Custom wrapper renderer (used by Input layout wrappers) */
35
+ _renderWrapper?: (params: { style: StyleProp<ViewStyle> | undefined }, children: ReactNode) => ReactNode
36
+ }
37
+
38
+ function ObjectInput ({
39
+ style,
40
+ inputStyle,
41
+ $value,
42
+ errors = {},
43
+ properties,
44
+ order,
45
+ row,
46
+ disabled,
47
+ readonly,
48
+ _renderWrapper
49
+ }: ObjectInputProps): ReactNode {
50
+ if (!$value || !properties) {
51
+ return null
52
+ }
53
+
54
+ const value = $value.get() || {}
55
+
56
+ const resolvedOrder = getOrder(order, properties)
57
+
58
+ function getInputs () {
59
+ return resolvedOrder
60
+ .filter((key) => {
61
+ const { dependsOn, dependsValue } = properties[key]
62
+ return resolvesDeps(value, dependsOn, dependsValue)
63
+ })
64
+ .map((key) => {
65
+ const { dependsOn, dependsValue, ...inputProps } = properties[key]
66
+ return {
67
+ ...inputProps,
68
+ key,
69
+ $value: $value[key]
70
+ }
71
+ // TODO: When the dependsOn field changes and this field is no longer visible -- clear it.
72
+ }).filter(Boolean)
73
+ }
74
+
75
+ const inputs = getInputs()
76
+
77
+ if (inputs.length === 0) return null
78
+
79
+ if (!_renderWrapper) {
80
+ _renderWrapper = ({ style }, children): ReactNode => {
81
+ return pug`
82
+ Div(style=style row=row)= children
83
+ `
84
+ }
85
+ }
86
+
87
+ return _renderWrapper({
88
+ style: [style, inputStyle]
89
+ }, inputs.map(({ key, ...inputProps }, index): ReactNode => pug`
90
+ Input.input(
91
+ key=key
92
+ styleName={ push: index !== 0, row, column: !row }
93
+ error=errors[key]
94
+ disabled=disabled
95
+ readonly=readonly
96
+ ...inputProps
97
+ )
98
+ `))
99
+ }
100
+
101
+ function getOrder (order: string[] | undefined, properties: Record<string, any>): string[] {
102
+ return order ?? Object.keys(properties)
103
+ }
104
+
105
+ function resolvesDeps (
106
+ value: Record<string, any> = {},
107
+ dependsOn?: string,
108
+ dependsValue?: any
109
+ ): boolean {
110
+ if (!dependsOn) return true
111
+ const dependencyValue = value[dependsOn]
112
+ return (
113
+ (dependsValue != null && dependencyValue === dependsValue) ||
114
+ (dependsValue != null && Array.isArray(dependsValue) &&
115
+ dependsValue.includes(dependencyValue)
116
+ ) ||
117
+ (
118
+ (dependsValue == null || (typeof dependsValue === 'string' && dependsValue.trim() === '')) &&
119
+ dependencyValue != null &&
120
+ !(typeof dependencyValue === 'string' && dependencyValue.trim() === '')
121
+ )
122
+ )
123
+ }
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@startupjs-ui/object-input",
3
+ "version": "0.1.3",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "main": "index.tsx",
8
+ "types": "index.d.ts",
9
+ "type": "module",
10
+ "dependencies": {
11
+ "@startupjs-ui/core": "^0.1.3",
12
+ "@startupjs-ui/div": "^0.1.3",
13
+ "@startupjs-ui/input": "^0.1.3"
14
+ },
15
+ "peerDependencies": {
16
+ "react": "*",
17
+ "react-native": "*",
18
+ "startupjs": "*"
19
+ },
20
+ "gitHead": "fd964ebc3892d3dd0a6c85438c0af619cc50c3f0"
21
+ }