view-contracts 0.5.2 → 0.5.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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Contract-first UI design toolchain. Compiles restricted view-contract TSX to language-neutral View IR and renders platform targets (React, SwiftUI, Compose).
4
4
 
5
- **Version:** 0.5.2
5
+ **Version:** 0.5.3
6
6
 
7
7
  ## Table of Contents
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "view-contracts",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "Contract-first UI design toolchain — restricted TSX to View IR to platform renderers",
5
5
  "type": "module",
6
6
  "bin": {
@@ -109,19 +109,19 @@ function actionHandler(props: Record<string, IrValue>, event: 'onClick' | 'onSub
109
109
  return `onClick={() => { void dispatchAction(${expr}); }}`;
110
110
  }
111
111
 
112
- function onChangeHandler(props: Record<string, IrValue>): string | undefined {
112
+ function onChangeHandler(props: Record<string, IrValue>, targetProp = 'e.target.value'): string | undefined {
113
113
  const onChange = props.onChange;
114
114
  if (!onChange || typeof onChange !== 'object' || !('kind' in onChange) || onChange.kind !== 'action') {
115
115
  return undefined;
116
116
  }
117
117
  const action = onChange as IrAction;
118
118
  if (!action.params || Object.keys(action.params).length === 0) {
119
- return `onChange={(e) => { void dispatchAction({ name: ${JSON.stringify(action.name)}, params: { value: e.target.value } } as ActionDescriptor); }}`;
119
+ return `onChange={(e) => { void dispatchAction({ name: ${JSON.stringify(action.name)}, params: { value: ${targetProp} } } as ActionDescriptor); }}`;
120
120
  }
121
121
  const existingParams = Object.entries(action.params)
122
122
  .map(([key, val]) => `${key}: ${renderValue(val, 0)}`)
123
123
  .join(', ');
124
- return `onChange={(e) => { void dispatchAction({ name: ${JSON.stringify(action.name)}, params: { ${existingParams}, value: e.target.value } } as ActionDescriptor); }}`;
124
+ return `onChange={(e) => { void dispatchAction({ name: ${JSON.stringify(action.name)}, params: { ${existingParams}, value: ${targetProp} } } as ActionDescriptor); }}`;
125
125
  }
126
126
 
127
127
  function componentToKebab(name: string): string {
@@ -197,10 +197,13 @@ function emitFieldWrap(inner: string, props: Record<string, IrValue>, level: num
197
197
  function emitCheckbox(props: Record<string, IrValue>, level: number): string {
198
198
  const name = propExpr(props, 'name') ?? '""';
199
199
  const label = propExpr(props, 'label') ?? '""';
200
- const checked = props.checked ? `defaultChecked={${propExpr(props, 'checked')}}` : '';
201
200
  const disabled = props.enabled ? `disabled={${propExpr(props, 'enabled')} === false}` : '';
201
+ const changeAttr = onChangeHandler(props, 'e.target.checked') ?? '';
202
+ const checkedAttr = props.checked
203
+ ? (changeAttr ? `checked={${propExpr(props, 'checked')}}` : `defaultChecked={${propExpr(props, 'checked')}}`)
204
+ : '';
202
205
  return `${indent(level)}<label className="vc-checkbox">
203
- ${indent(level + 1)}<input type="checkbox" name={${name}} ${checked} ${disabled} />
206
+ ${indent(level + 1)}<input type="checkbox" name={${name}} ${checkedAttr} ${disabled} ${changeAttr} />
204
207
  ${indent(level + 1)}<span>{${label}}</span>
205
208
  ${indent(level)}</label>`;
206
209
  }
@@ -209,10 +212,13 @@ function emitRadio(props: Record<string, IrValue>, level: number): string {
209
212
  const name = propExpr(props, 'binding') ?? propExpr(props, 'name') ?? '""';
210
213
  const value = propExpr(props, 'value') ?? '""';
211
214
  const label = propExpr(props, 'label') ?? '""';
212
- const checked = props.checked ? `defaultChecked={${propExpr(props, 'checked')}}` : '';
213
215
  const disabled = props.enabled ? `disabled={${propExpr(props, 'enabled')} === false}` : '';
216
+ const changeAttr = onChangeHandler(props, 'e.target.value') ?? '';
217
+ const checkedAttr = props.checked
218
+ ? (changeAttr ? `checked={${propExpr(props, 'checked')}}` : `defaultChecked={${propExpr(props, 'checked')}}`)
219
+ : '';
214
220
  return `${indent(level)}<label className="vc-radio">
215
- ${indent(level + 1)}<input type="radio" name={${name}} value={${value}} ${checked} ${disabled} />
221
+ ${indent(level + 1)}<input type="radio" name={${name}} value={${value}} ${checkedAttr} ${disabled} ${changeAttr} />
216
222
  ${indent(level + 1)}<span>{${label}}</span>
217
223
  ${indent(level)}</label>`;
218
224
  }
@@ -220,10 +226,13 @@ ${indent(level)}</label>`;
220
226
  function emitSwitch(props: Record<string, IrValue>, level: number): string {
221
227
  const name = propExpr(props, 'binding') ?? propExpr(props, 'name') ?? '""';
222
228
  const label = propExpr(props, 'label') ?? '""';
223
- const checked = props.checked ? `defaultChecked={${propExpr(props, 'checked')}}` : '';
224
229
  const disabled = props.enabled ? `disabled={${propExpr(props, 'enabled')} === false}` : '';
230
+ const changeAttr = onChangeHandler(props, 'e.target.checked') ?? '';
231
+ const checkedAttr = props.checked
232
+ ? (changeAttr ? `checked={${propExpr(props, 'checked')}}` : `defaultChecked={${propExpr(props, 'checked')}}`)
233
+ : '';
225
234
  return `${indent(level)}<label className="vc-switch">
226
- ${indent(level + 1)}<input type="checkbox" role="switch" name={${name}} ${checked} ${disabled} />
235
+ ${indent(level + 1)}<input type="checkbox" role="switch" name={${name}} ${checkedAttr} ${disabled} ${changeAttr} />
227
236
  ${indent(level + 1)}<span>{${label}}</span>
228
237
  ${indent(level)}</label>`;
229
238
  }
@@ -282,7 +291,7 @@ function emitVoidInput(
282
291
  }
283
292
  if (props.name) attrs.push(`name={${propExpr(props, 'name')}}`);
284
293
  if (props.placeholder) attrs.push(`placeholder={${propExpr(props, 'placeholder')}}`);
285
- if (entry.inputType) attrs.push(`type={${propExpr(props, 'type') ?? JSON.stringify(entry.inputType)}}`);
294
+ if (entry.inputType) attrs.push(`type={${propExpr(props, 'inputType') ?? JSON.stringify(entry.inputType)}}`);
286
295
  if (props.required) attrs.push(`required={${propExpr(props, 'required')}}`);
287
296
  if (props.readonly) attrs.push(`readOnly={${propExpr(props, 'readonly')}}`);
288
297
  if (props.enabled) attrs.push(`disabled={${propExpr(props, 'enabled')} === false}`);
@@ -320,7 +329,10 @@ function emitOpenTag(
320
329
  const handler = actionHandler(props, entry.actionEvent);
321
330
  if (handler) attrs.push(handler);
322
331
  }
332
+ if (props.name) attrs.push(`name={${propExpr(props, 'name')}}`);
323
333
  if (entry.tag === 'select' || entry.tag === 'textarea' || entry.tag === 'input') {
334
+ if (props.required) attrs.push(`required={${propExpr(props, 'required')}}`);
335
+ if (props.enabled) attrs.push(`disabled={${propExpr(props, 'enabled')} === false}`);
324
336
  const changeHandler = onChangeHandler(props);
325
337
  if (changeHandler) {
326
338
  if (props.value) attrs.push(`value={${propExpr(props, 'value')}}`);