view-contracts 0.1.0 → 0.2.0
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/dist/view-contracts.bundle.mjs +148 -153
- package/dist/view-contracts.bundle.mjs.map +4 -4
- package/package.json +10 -4
- package/renderers/react/README.md +19 -3
- package/renderers/react/element-config.meta.json +23 -0
- package/renderers/react/elementMap.ts +55 -0
- package/renderers/react/elements.yaml +87 -0
- package/renderers/react/index.ts +2 -0
- package/renderers/react/lowering/lowerToJsx.ts +397 -0
- package/renderers/react/paths.ts +10 -0
- package/renderers/react/renderComponents.ts +65 -0
- package/renderers/react/renderRuntime.ts +134 -0
- package/renderers/react/routes.yaml +3 -2
- package/{src/renderer → renderers}/react/runtime/actionRuntime.ts +3 -3
- package/renderers/react/runtime/dispatch.ts +21 -0
- package/renderers/react/runtime/index.ts +5 -0
- package/{src/renderer → renderers}/react/runtime/style.ts +35 -8
- package/renderers/react/runtime/types.ts +63 -0
- package/renderers/react/templates/components-index.ts.hbs +5 -0
- package/renderers/react/templates/dispatch.ts.hbs +22 -0
- package/renderers/react/templates/handlers.ts.hbs +4 -0
- package/renderers/react/templates/runtime-types.ts.hbs +5 -0
- package/renderers/react/templates/style-helpers.ts.hbs +2 -0
- package/renderers/react/templates/theme.css.hbs +2 -0
- package/src/generated/schema/lowering-style-properties.ts +36 -0
- package/src/generated/schema/react-renderer-element-config.ts +26 -0
- package/src/renderer/react/runtime/components.tsx +0 -216
- package/src/renderer/react/runtime/index.ts +0 -4
- package/src/renderer/react/runtime/shared.tsx +0 -53
- package/src/renderer/react/runtime/types.ts +0 -46
- /package/{src/renderer → renderers}/react/runtime/theme.css +0 -0
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import type { ElementType, FormEvent, ReactElement, ReactNode } from 'react';
|
|
2
|
-
import { dispatchPreviewAction } from './actionRuntime.js';
|
|
3
|
-
import { commonStyle, isHidden, toneClass } from './style.js';
|
|
4
|
-
import type { Action, Align, CommonProps, FieldSize, Justify, Option } from './types.js';
|
|
5
|
-
|
|
6
|
-
function cx(...names: Array<string | false | undefined>): string {
|
|
7
|
-
return names.filter(Boolean).join(' ');
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function mapAlign(value?: Align): string | undefined {
|
|
11
|
-
if (!value) return undefined;
|
|
12
|
-
if (value === 'start') return 'flex-start';
|
|
13
|
-
if (value === 'end') return 'flex-end';
|
|
14
|
-
return value;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function mapJustify(value?: Justify): string | undefined {
|
|
18
|
-
if (!value) return undefined;
|
|
19
|
-
if (value === 'start') return 'flex-start';
|
|
20
|
-
if (value === 'end') return 'flex-end';
|
|
21
|
-
if (value === 'between') return 'space-between';
|
|
22
|
-
if (value === 'around') return 'space-around';
|
|
23
|
-
if (value === 'spaceBetween') return 'space-between';
|
|
24
|
-
if (value === 'spaceAround') return 'space-around';
|
|
25
|
-
if (value === 'spaceEvenly') return 'space-evenly';
|
|
26
|
-
return value;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function runAction(action?: Action): void {
|
|
30
|
-
void dispatchPreviewAction(action);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function Page(props: CommonProps & { title?: string }): ReactElement | null {
|
|
34
|
-
if (isHidden(props)) return null;
|
|
35
|
-
return (
|
|
36
|
-
<main id={props.id} role={props.role} aria-label={props.ariaLabel ?? props.label} className="ui-page" style={commonStyle(props)}>
|
|
37
|
-
{props.children}
|
|
38
|
-
</main>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function Box(props: CommonProps): ReactElement | null {
|
|
43
|
-
if (isHidden(props)) return null;
|
|
44
|
-
return (
|
|
45
|
-
<div id={props.id} role={props.role} aria-label={props.ariaLabel ?? props.label} className="ui-box" style={commonStyle(props)}>
|
|
46
|
-
{props.children}
|
|
47
|
-
</div>
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function Column(props: CommonProps & { align?: Align; justify?: Justify }): ReactElement | null {
|
|
52
|
-
if (isHidden(props)) return null;
|
|
53
|
-
return (
|
|
54
|
-
<div
|
|
55
|
-
id={props.id}
|
|
56
|
-
role={props.role}
|
|
57
|
-
aria-label={props.ariaLabel ?? props.label}
|
|
58
|
-
className="ui-column"
|
|
59
|
-
style={{ ...commonStyle(props), display: 'flex', flexDirection: 'column', alignItems: mapAlign(props.align), justifyContent: mapJustify(props.justify) }}
|
|
60
|
-
>
|
|
61
|
-
{props.children}
|
|
62
|
-
</div>
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function Stack(props: CommonProps & { contentAlign?: CommonProps['contentAlign'] }): ReactElement | null {
|
|
67
|
-
if (isHidden(props)) return null;
|
|
68
|
-
return (
|
|
69
|
-
<div id={props.id} role={props.role} aria-label={props.ariaLabel ?? props.label} className="ui-stack" style={commonStyle(props)}>
|
|
70
|
-
{props.children}
|
|
71
|
-
</div>
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function Row(props: CommonProps & { align?: Align; justify?: Justify; wrap?: boolean }): ReactElement | null {
|
|
76
|
-
if (isHidden(props)) return null;
|
|
77
|
-
return (
|
|
78
|
-
<div
|
|
79
|
-
id={props.id}
|
|
80
|
-
role={props.role}
|
|
81
|
-
aria-label={props.ariaLabel ?? props.label}
|
|
82
|
-
className={cx('ui-row', props.wrap && 'ui-wrap')}
|
|
83
|
-
style={{ ...commonStyle(props), display: 'flex', flexDirection: 'row', alignItems: mapAlign(props.align), justifyContent: mapJustify(props.justify) }}
|
|
84
|
-
>
|
|
85
|
-
{props.children}
|
|
86
|
-
</div>
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function Spacer(props: CommonProps): ReactElement {
|
|
91
|
-
return <div aria-hidden className="ui-spacer" style={{ ...commonStyle(props), flexGrow: props.weight ?? 1 }} />;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function Divider(props: CommonProps): ReactElement | null {
|
|
95
|
-
if (isHidden(props)) return null;
|
|
96
|
-
return <hr className="ui-divider" style={commonStyle(props)} />;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function Heading(props: CommonProps & { level?: 1 | 2 | 3 | 4 | 5 | 6; children: ReactNode }): ReactElement | null {
|
|
100
|
-
if (isHidden(props)) return null;
|
|
101
|
-
const level = Math.min(6, Math.max(1, props.level ?? 2)) as 1 | 2 | 3 | 4 | 5 | 6;
|
|
102
|
-
const Tag = `h${level}` as ElementType;
|
|
103
|
-
return (
|
|
104
|
-
<Tag className={cx('ui-heading', `ui-heading-${level}`)} style={commonStyle(props)}>
|
|
105
|
-
{props.children}
|
|
106
|
-
</Tag>
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function Text(props: CommonProps & { children: ReactNode }): ReactElement | null {
|
|
111
|
-
if (isHidden(props)) return null;
|
|
112
|
-
return (
|
|
113
|
-
<p className="ui-text" style={commonStyle(props)}>
|
|
114
|
-
{props.children}
|
|
115
|
-
</p>
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export function Link(props: CommonProps & { href?: string; onClick?: Action; children: ReactNode }): ReactElement | null {
|
|
120
|
-
if (isHidden(props)) return null;
|
|
121
|
-
return (
|
|
122
|
-
<a className="ui-link" style={commonStyle(props)} href={props.href} onClick={() => runAction(props.action ?? props.onClick)}>
|
|
123
|
-
{props.children}
|
|
124
|
-
</a>
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export function Button(props: CommonProps & { type?: 'button' | 'submit'; children: ReactNode }): ReactElement | null {
|
|
129
|
-
if (isHidden(props)) return null;
|
|
130
|
-
return (
|
|
131
|
-
<button
|
|
132
|
-
id={props.id}
|
|
133
|
-
aria-label={props.ariaLabel ?? props.label}
|
|
134
|
-
className={cx('ui-button', `ui-button-${props.variant ?? 'secondary'}`)}
|
|
135
|
-
style={commonStyle(props)}
|
|
136
|
-
type={props.type ?? 'button'}
|
|
137
|
-
disabled={props.enabled === false}
|
|
138
|
-
onClick={() => runAction(props.action)}
|
|
139
|
-
>
|
|
140
|
-
{props.children}
|
|
141
|
-
</button>
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function Form(props: CommonProps & { action?: Action; children: ReactNode }): ReactElement | null {
|
|
146
|
-
if (isHidden(props)) return null;
|
|
147
|
-
function onSubmit(event: FormEvent<HTMLFormElement>) {
|
|
148
|
-
event.preventDefault();
|
|
149
|
-
runAction(props.action);
|
|
150
|
-
}
|
|
151
|
-
return (
|
|
152
|
-
<form id={props.id} aria-label={props.ariaLabel ?? props.label} className="ui-form" style={commonStyle(props)} onSubmit={onSubmit}>
|
|
153
|
-
{props.children}
|
|
154
|
-
</form>
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function FieldFrame(props: { label?: string; help?: string; error?: string; children: ReactNode }): ReactElement {
|
|
159
|
-
return (
|
|
160
|
-
<label className="ui-field">
|
|
161
|
-
{props.label ? <span className="ui-field-label">{props.label}</span> : null}
|
|
162
|
-
{props.children}
|
|
163
|
-
{props.help ? <span className="ui-field-help">{props.help}</span> : null}
|
|
164
|
-
{props.error ? <span className="ui-field-error">{props.error}</span> : null}
|
|
165
|
-
</label>
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export function TextInput(props: CommonProps & { name: string; label?: string; placeholder?: string; type?: string; required?: boolean; size?: FieldSize }): ReactElement | null {
|
|
170
|
-
if (isHidden(props)) return null;
|
|
171
|
-
return (
|
|
172
|
-
<FieldFrame label={props.label}>
|
|
173
|
-
<input className={cx('ui-input', `ui-field-${props.size ?? 'md'}`)} style={commonStyle(props)} name={props.name} placeholder={props.placeholder} type={props.type ?? 'text'} required={props.required} readOnly={props.readonly} disabled={props.enabled === false} />
|
|
174
|
-
</FieldFrame>
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export function Select(props: CommonProps & { name: string; label?: string; options: Option[]; size?: FieldSize }): ReactElement | null {
|
|
179
|
-
if (isHidden(props)) return null;
|
|
180
|
-
return (
|
|
181
|
-
<FieldFrame label={props.label} help={props.help} error={props.error}>
|
|
182
|
-
<select className={cx('ui-select', `ui-field-${props.size ?? 'md'}`)} style={commonStyle(props)} name={props.name} disabled={props.enabled === false}>
|
|
183
|
-
{props.options.map((option) => <option key={option.value} value={option.value}>{option.label}</option>)}
|
|
184
|
-
</select>
|
|
185
|
-
</FieldFrame>
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export function TextArea(props: CommonProps & { name: string; label?: string; placeholder?: string; rows?: number }): ReactElement | null {
|
|
190
|
-
if (isHidden(props)) return null;
|
|
191
|
-
return (
|
|
192
|
-
<FieldFrame label={props.label} help={props.help} error={props.error}>
|
|
193
|
-
<textarea className="ui-textarea" style={commonStyle(props)} name={props.name} placeholder={props.placeholder} rows={props.rows ?? 4} readOnly={props.readonly} disabled={props.enabled === false} />
|
|
194
|
-
</FieldFrame>
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export function Checkbox(props: CommonProps & { name: string; label: string }): ReactElement | null {
|
|
199
|
-
if (isHidden(props)) return null;
|
|
200
|
-
return (
|
|
201
|
-
<label className="ui-check">
|
|
202
|
-
<input type="checkbox" name={props.name} defaultChecked={props.checked} disabled={props.enabled === false} />
|
|
203
|
-
<span>{props.label}</span>
|
|
204
|
-
</label>
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
export function List(props: CommonProps & { children: ReactNode }): ReactElement | null {
|
|
209
|
-
if (isHidden(props)) return null;
|
|
210
|
-
return <ul role={props.role ?? 'list'} className="ui-list" style={commonStyle(props)}>{props.children}</ul>;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export function ListItem(props: CommonProps & { children: ReactNode }): ReactElement | null {
|
|
214
|
-
if (isHidden(props)) return null;
|
|
215
|
-
return <li role={props.role ?? 'listitem'} className="ui-list-item" style={commonStyle(props)}>{props.children}</li>;
|
|
216
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { FormEvent, ReactElement, ReactNode } from 'react';
|
|
2
|
-
import { dispatchPreviewAction } from './actionRuntime.js';
|
|
3
|
-
import { commonStyle, isHidden, toneClass } from './style.js';
|
|
4
|
-
import type { Action, Align, CommonProps, Justify } from './types.js';
|
|
5
|
-
|
|
6
|
-
export function cx(...names: Array<string | false | undefined>): string {
|
|
7
|
-
return names.filter(Boolean).join(' ');
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function mapAlign(value?: Align): string | undefined {
|
|
11
|
-
if (!value) return undefined;
|
|
12
|
-
if (value === 'start') return 'flex-start';
|
|
13
|
-
if (value === 'end') return 'flex-end';
|
|
14
|
-
return value;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function mapJustify(value?: Justify): string | undefined {
|
|
18
|
-
if (!value) return undefined;
|
|
19
|
-
if (value === 'start') return 'flex-start';
|
|
20
|
-
if (value === 'end') return 'flex-end';
|
|
21
|
-
if (value === 'between' || value === 'spaceBetween') return 'space-between';
|
|
22
|
-
if (value === 'around' || value === 'spaceAround') return 'space-around';
|
|
23
|
-
if (value === 'spaceEvenly') return 'space-evenly';
|
|
24
|
-
return value;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function runAction(action?: Action): void {
|
|
28
|
-
void dispatchPreviewAction(action);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function shellProps(props: CommonProps): Pick<CommonProps, 'id' | 'testId' | 'ariaLabel' | 'role'> {
|
|
32
|
-
return { id: props.id, testId: props.testId, ariaLabel: props.ariaLabel ?? props.label, role: props.role };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function FieldFrame(props: { label?: string; help?: string; error?: string; children: ReactNode }): ReactElement {
|
|
36
|
-
return (
|
|
37
|
-
<div className="ui-field">
|
|
38
|
-
{props.label ? <span className="ui-field-label">{props.label}</span> : null}
|
|
39
|
-
{props.children}
|
|
40
|
-
{props.error ? <span className="ui-field-error">{props.error}</span> : null}
|
|
41
|
-
{!props.error && props.help ? <span className="ui-field-help">{props.help}</span> : null}
|
|
42
|
-
</div>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function onFormSubmit(action?: Action) {
|
|
47
|
-
return (event: FormEvent<HTMLFormElement>) => {
|
|
48
|
-
event.preventDefault();
|
|
49
|
-
runAction(action);
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export { commonStyle, isHidden, toneClass };
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
import type { ViewAction } from '@view-contracts/core';
|
|
3
|
-
import type { DslProperties } from '../../../generated/schema/dsl-properties.js';
|
|
4
|
-
|
|
5
|
-
export type {
|
|
6
|
-
Align,
|
|
7
|
-
Axis,
|
|
8
|
-
Border,
|
|
9
|
-
Breakpoint,
|
|
10
|
-
Clip,
|
|
11
|
-
ContentAlign,
|
|
12
|
-
Direction,
|
|
13
|
-
FontWeight,
|
|
14
|
-
HeightSize,
|
|
15
|
-
ImageFit,
|
|
16
|
-
Justify,
|
|
17
|
-
KeyboardType,
|
|
18
|
-
LineLimit,
|
|
19
|
-
Overflow,
|
|
20
|
-
Role,
|
|
21
|
-
Scroll,
|
|
22
|
-
Shadow,
|
|
23
|
-
TextAlign,
|
|
24
|
-
TextOverflow,
|
|
25
|
-
WidthSize,
|
|
26
|
-
} from '../../../generated/schema/tokens.js';
|
|
27
|
-
|
|
28
|
-
export type { DslProperties };
|
|
29
|
-
|
|
30
|
-
/** view-contracts view TSX action binding (schema property: onClick / onChange). */
|
|
31
|
-
export type Action = ViewAction;
|
|
32
|
-
|
|
33
|
-
/** React preview props: schema catalog + JSX children + view action alias. */
|
|
34
|
-
export interface CommonProps extends DslProperties {
|
|
35
|
-
children?: ReactNode;
|
|
36
|
-
/** View-layer alias for onClick in interactive components. */
|
|
37
|
-
action?: ViewAction;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface Option {
|
|
41
|
-
label: string;
|
|
42
|
-
value: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Preview-only field density (not in ui-dsl.schema.json). */
|
|
46
|
-
export type FieldSize = 'sm' | 'md' | 'lg';
|
|
File without changes
|