@openwebf/webf 0.22.0 → 0.22.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.
@@ -82,3 +82,8 @@ export class ClassObject {
82
82
  export class FunctionObject {
83
83
  declare: FunctionDeclaration
84
84
  }
85
+
86
+ export class TypeAliasObject {
87
+ name: string;
88
+ type: string;
89
+ }
package/src/generator.ts CHANGED
@@ -64,6 +64,8 @@ interface GenerateOptions {
64
64
  source: string;
65
65
  target: string;
66
66
  command: string;
67
+ exclude?: string[];
68
+ packageName?: string;
67
69
  }
68
70
 
69
71
  // Batch processing for file operations
@@ -99,11 +101,14 @@ function validatePaths(source: string, target: string): { source: string; target
99
101
  return { source: normalizedSource, target: normalizedTarget };
100
102
  }
101
103
 
102
- function getTypeFiles(source: string): string[] {
104
+ function getTypeFiles(source: string, excludePatterns?: string[]): string[] {
103
105
  try {
106
+ const defaultIgnore = ['**/node_modules/**', '**/dist/**', '**/build/**', '**/example/**'];
107
+ const ignore = excludePatterns ? [...defaultIgnore, ...excludePatterns] : defaultIgnore;
108
+
104
109
  const files = glob.globSync("**/*.d.ts", {
105
110
  cwd: source,
106
- ignore: ['**/node_modules/**', '**/dist/**', '**/build/**']
111
+ ignore: ignore
107
112
  });
108
113
 
109
114
  return files.filter(file => !file.includes('global.d.ts'));
@@ -139,7 +144,7 @@ function createBlobs(typeFiles: string[], source: string, target: string): IDLBl
139
144
  });
140
145
  }
141
146
 
142
- export async function dartGen({ source, target, command }: GenerateOptions) {
147
+ export async function dartGen({ source, target, command, exclude }: GenerateOptions) {
143
148
  group('Dart Code Generation');
144
149
  time('dartGen');
145
150
 
@@ -154,7 +159,7 @@ export async function dartGen({ source, target, command }: GenerateOptions) {
154
159
  }
155
160
 
156
161
  // Get type files
157
- const typeFiles = getTypeFiles(normalizedSource);
162
+ const typeFiles = getTypeFiles(normalizedSource, exclude);
158
163
  info(`Found ${typeFiles.length} type definition files`);
159
164
 
160
165
  if (typeFiles.length === 0) {
@@ -232,7 +237,7 @@ export async function dartGen({ source, target, command }: GenerateOptions) {
232
237
  info(`Output directory: ${normalizedTarget}`);
233
238
  }
234
239
 
235
- export async function reactGen({ source, target }: GenerateOptions) {
240
+ export async function reactGen({ source, target, exclude, packageName }: GenerateOptions) {
236
241
  group('React Code Generation');
237
242
  time('reactGen');
238
243
 
@@ -247,7 +252,7 @@ export async function reactGen({ source, target }: GenerateOptions) {
247
252
  }
248
253
 
249
254
  // Get type files
250
- const typeFiles = getTypeFiles(normalizedSource);
255
+ const typeFiles = getTypeFiles(normalizedSource, exclude);
251
256
  info(`Found ${typeFiles.length} type definition files`);
252
257
 
253
258
  if (typeFiles.length === 0) {
@@ -288,9 +293,16 @@ export async function reactGen({ source, target }: GenerateOptions) {
288
293
 
289
294
  await processFilesInBatch(blobs, 5, async (blob) => {
290
295
  try {
291
- const result = generateReactComponent(blob);
296
+ const result = generateReactComponent(blob, packageName, blob.relativeDir);
297
+
298
+ // Skip if no content was generated
299
+ if (!result || result.trim().length === 0) {
300
+ debug(`Skipped ${blob.filename} - no components found`);
301
+ return;
302
+ }
292
303
 
293
304
  // Maintain the same directory structure as the .d.ts file
305
+ // Always put files under src/ directory
294
306
  const outputDir = path.join(normalizedTarget, 'src', blob.relativeDir);
295
307
  // Ensure the directory exists
296
308
  if (!fs.existsSync(outputDir)) {
@@ -309,12 +321,68 @@ export async function reactGen({ source, target }: GenerateOptions) {
309
321
  }
310
322
  });
311
323
 
312
- // Generate index file
313
- const indexContent = generateReactIndex(blobs);
324
+ // Generate or update index file
314
325
  const indexFilePath = path.join(normalizedTarget, 'src', 'index.ts');
315
- if (writeFileIfChanged(indexFilePath, indexContent)) {
316
- filesChanged++;
317
- debug(`Generated: index.ts`);
326
+ const newExports = generateReactIndex(blobs);
327
+
328
+ if (fs.existsSync(indexFilePath)) {
329
+ // Read existing index file
330
+ const existingContent = fs.readFileSync(indexFilePath, 'utf-8');
331
+ const existingLines = existingContent.split('\n');
332
+
333
+ // Parse new exports
334
+ const newExportLines = newExports.split('\n').filter(line => line.startsWith('export '));
335
+
336
+ // Find which exports are missing
337
+ const missingExports: string[] = [];
338
+ for (const newExport of newExportLines) {
339
+ // Extract the export statement to check if it exists
340
+ const exportMatch = newExport.match(/export\s*{\s*([^}]+)\s*}\s*from\s*["']([^"']+)["']/);
341
+ if (exportMatch) {
342
+ const [, exportNames, modulePath] = exportMatch;
343
+ const exportedItems = exportNames.split(',').map(s => s.trim());
344
+
345
+ // Check if this exact export exists
346
+ const exists = existingLines.some(line => {
347
+ if (!line.startsWith('export ')) return false;
348
+ const lineMatch = line.match(/export\s*{\s*([^}]+)\s*}\s*from\s*["']([^"']+)["']/);
349
+ if (!lineMatch) return false;
350
+ const [, lineExportNames, lineModulePath] = lineMatch;
351
+ const lineExportedItems = lineExportNames.split(',').map(s => s.trim());
352
+
353
+ // Check if same module and same exports
354
+ return lineModulePath === modulePath &&
355
+ exportedItems.every(item => lineExportedItems.includes(item)) &&
356
+ lineExportedItems.every(item => exportedItems.includes(item));
357
+ });
358
+
359
+ if (!exists) {
360
+ missingExports.push(newExport);
361
+ }
362
+ }
363
+ }
364
+
365
+ // If there are missing exports, append them
366
+ if (missingExports.length > 0) {
367
+ let updatedContent = existingContent.trimRight();
368
+ if (!updatedContent.endsWith('\n')) {
369
+ updatedContent += '\n';
370
+ }
371
+ updatedContent += missingExports.join('\n') + '\n';
372
+
373
+ if (writeFileIfChanged(indexFilePath, updatedContent)) {
374
+ filesChanged++;
375
+ debug(`Updated: index.ts (added ${missingExports.length} exports)`);
376
+ }
377
+ } else {
378
+ debug(`Skipped: index.ts (all exports already exist)`);
379
+ }
380
+ } else {
381
+ // File doesn't exist, create it
382
+ if (writeFileIfChanged(indexFilePath, newExports)) {
383
+ filesChanged++;
384
+ debug(`Generated: index.ts`);
385
+ }
318
386
  }
319
387
 
320
388
  timeEnd('reactGen');
@@ -323,7 +391,7 @@ export async function reactGen({ source, target }: GenerateOptions) {
323
391
  info('You can now import these components in your React project.');
324
392
  }
325
393
 
326
- export async function vueGen({ source, target }: GenerateOptions) {
394
+ export async function vueGen({ source, target, exclude }: GenerateOptions) {
327
395
  group('Vue Typings Generation');
328
396
  time('vueGen');
329
397
 
@@ -338,7 +406,7 @@ export async function vueGen({ source, target }: GenerateOptions) {
338
406
  }
339
407
 
340
408
  // Get type files
341
- const typeFiles = getTypeFiles(normalizedSource);
409
+ const typeFiles = getTypeFiles(normalizedSource, exclude);
342
410
  info(`Found ${typeFiles.length} type definition files`);
343
411
 
344
412
  if (typeFiles.length === 0) {
package/src/react.ts CHANGED
@@ -2,7 +2,7 @@ import _ from "lodash";
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
4
  import {ParameterType} from "./analyzer";
5
- import {ClassObject, FunctionArgumentType, FunctionDeclaration} from "./declaration";
5
+ import {ClassObject, FunctionArgumentType, FunctionDeclaration, TypeAliasObject} from "./declaration";
6
6
  import {IDLBlob} from "./IDLBlob";
7
7
  import {getPointerType, isPointerType} from "./utils";
8
8
 
@@ -53,6 +53,21 @@ function generateEventHandlerType(type: ParameterType) {
53
53
  throw new Error('Unknown event type: ' + pointerType);
54
54
  }
55
55
 
56
+ function getEventType(type: ParameterType) {
57
+ if (!isPointerType(type)) {
58
+ return 'Event';
59
+ }
60
+ const pointerType = getPointerType(type);
61
+ if (pointerType === 'CustomEvent') {
62
+ return 'CustomEvent';
63
+ }
64
+ // For specific event types like MouseEvent, TouchEvent, etc.
65
+ if (pointerType.endsWith('Event')) {
66
+ return pointerType;
67
+ }
68
+ return 'Event';
69
+ }
70
+
56
71
  function generateMethodDeclaration(method: FunctionDeclaration) {
57
72
  var methodName = method.name;
58
73
  var args = method.args.map(arg => {
@@ -69,8 +84,25 @@ function toReactEventName(name: string) {
69
84
  return _.camelCase(eventName);
70
85
  }
71
86
 
72
- export function generateReactComponent(blob: IDLBlob) {
73
- const classObjects = blob.objects as ClassObject[];
87
+ export function toWebFTagName(className: string): string {
88
+ // Special handling for WebF prefix - treat it as a single unit
89
+ if (className.startsWith('WebF')) {
90
+ // Replace WebF with webf- and then kebab-case the rest
91
+ const withoutPrefix = className.substring(4);
92
+ return 'webf-' + _.kebabCase(withoutPrefix);
93
+ } else if (className.startsWith('Flutter')) {
94
+ // Handle Flutter prefix similarly
95
+ const withoutPrefix = className.substring(7);
96
+ return 'flutter-' + _.kebabCase(withoutPrefix);
97
+ }
98
+ // Default kebab-case for other components
99
+ return _.kebabCase(className);
100
+ }
101
+
102
+ export function generateReactComponent(blob: IDLBlob, packageName?: string, relativeDir?: string) {
103
+ const classObjects = blob.objects.filter(obj => obj instanceof ClassObject) as ClassObject[];
104
+ const typeAliases = blob.objects.filter(obj => obj instanceof TypeAliasObject) as TypeAliasObject[];
105
+
74
106
  const classObjectDictionary = Object.fromEntries(
75
107
  classObjects.map(object => {
76
108
  return [object.name, object];
@@ -89,66 +121,163 @@ export function generateReactComponent(blob: IDLBlob) {
89
121
  && !object.name.endsWith('Events');
90
122
  });
91
123
 
92
- const dependencies = others.map(object => {
93
- const props = object.props.map(prop => {
94
- if (prop.optional) {
95
- return `${prop.name}?: ${generateReturnType(prop.type)};`;
96
- }
97
- return `${prop.name}: ${generateReturnType(prop.type)};`;
98
- }).join('\n ');
124
+ // Include type aliases
125
+ const typeAliasDeclarations = typeAliases.map(typeAlias => {
126
+ return `type ${typeAlias.name} = ${typeAlias.type};`;
127
+ }).join('\n');
128
+
129
+ const dependencies = [
130
+ typeAliasDeclarations,
131
+ others.map(object => {
132
+ const props = object.props.map(prop => {
133
+ if (prop.optional) {
134
+ return `${prop.name}?: ${generateReturnType(prop.type)};`;
135
+ }
136
+ return `${prop.name}: ${generateReturnType(prop.type)};`;
137
+ }).join('\n ');
99
138
 
100
- return `
139
+ return `
101
140
  interface ${object.name} {
102
141
  ${props}
103
142
  }`;
104
- }).join('\n\n');
143
+ }).join('\n\n')
144
+ ].filter(Boolean).join('\n\n');
105
145
 
106
- const componentProperties = properties.length > 0 ? properties[0] : undefined;
107
- const componentEvents = events.length > 0 ? events[0] : undefined;
108
- const className = (() => {
109
- if (componentProperties) {
110
- return componentProperties.name.replace(/Properties$/, '');
146
+ // Generate all components from this file
147
+ const components: string[] = [];
148
+
149
+ // Create a map of component names to their properties and events
150
+ const componentMap = new Map<string, { properties?: ClassObject, events?: ClassObject }>();
151
+
152
+ // Process all Properties interfaces
153
+ properties.forEach(prop => {
154
+ const componentName = prop.name.replace(/Properties$/, '');
155
+ if (!componentMap.has(componentName)) {
156
+ componentMap.set(componentName, {});
111
157
  }
112
- if (componentEvents) {
113
- return componentEvents.name.replace(/Events$/, '');
158
+ componentMap.get(componentName)!.properties = prop;
159
+ });
160
+
161
+ // Process all Events interfaces
162
+ events.forEach(event => {
163
+ const componentName = event.name.replace(/Events$/, '');
164
+ if (!componentMap.has(componentName)) {
165
+ componentMap.set(componentName, {});
114
166
  }
115
- return '';
116
- })();
117
-
118
- if (!className) {
167
+ componentMap.get(componentName)!.events = event;
168
+ });
169
+
170
+ // If we have multiple components, we need to generate a combined file
171
+ const componentEntries = Array.from(componentMap.entries());
172
+
173
+ if (componentEntries.length === 0) {
119
174
  return '';
120
175
  }
176
+
177
+ if (componentEntries.length === 1) {
178
+ // Single component - use existing template
179
+ const [className, component] = componentEntries[0];
180
+
181
+ // Determine the import path for createWebFComponent
182
+ const isReactCoreUI = packageName === '@openwebf/react-core-ui';
183
+ let createWebFComponentImport: string;
184
+
185
+ if (isReactCoreUI && relativeDir) {
186
+ // Calculate relative path from current file to utils/createWebFComponent
187
+ // Files are generated in src/<relativeDir>/ and need to import from src/utils/
188
+ const depth = relativeDir.split('/').filter(p => p).length;
189
+ const upPath = '../'.repeat(depth);
190
+ createWebFComponentImport = `import { createWebFComponent, WebFElementWithMethods } from "${upPath}utils/createWebFComponent";`;
191
+ } else {
192
+ createWebFComponentImport = `import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui";`;
193
+ }
194
+
195
+ const templateContent = readTemplate('react.component.tsx')
196
+ .replace(
197
+ 'import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui";',
198
+ createWebFComponentImport
199
+ );
200
+
201
+ const content = _.template(templateContent)({
202
+ className: className,
203
+ properties: component.properties,
204
+ events: component.events,
205
+ classObjectDictionary,
206
+ dependencies,
207
+ blob,
208
+ toReactEventName,
209
+ toWebFTagName,
210
+ generateReturnType,
211
+ generateMethodDeclaration,
212
+ generateEventHandlerType,
213
+ getEventType,
214
+ });
121
215
 
122
- // Calculate the relative path from component location to utils
123
- const componentDepth = blob.relativeDir ? blob.relativeDir.split(path.sep).length : 0;
124
- const utilsPath = componentDepth > 0
125
- ? '../'.repeat(componentDepth) + 'utils/createComponent'
126
- : './utils/createComponent';
127
-
128
- const content = _.template(readTemplate('react.component.tsx'))({
129
- className: className,
130
- properties: componentProperties,
131
- events: componentEvents,
132
- classObjectDictionary,
133
- dependencies,
134
- blob,
135
- utilsPath,
136
- toReactEventName,
137
- generateReturnType,
138
- generateMethodDeclaration,
139
- generateEventHandlerType,
216
+ return content.split('\n').filter(str => {
217
+ return str.trim().length > 0;
218
+ }).join('\n');
219
+ }
220
+
221
+ // Multiple components - generate with shared imports
222
+ const componentDefinitions: string[] = [];
223
+
224
+ // Determine the import path for createWebFComponent
225
+ const isReactCoreUI = packageName === '@openwebf/react-core-ui';
226
+ let createWebFComponentImport: string;
227
+
228
+ if (isReactCoreUI && relativeDir) {
229
+ // Calculate relative path from current file to utils/createWebFComponent
230
+ // Files are generated in src/<relativeDir>/ and need to import from src/utils/
231
+ const depth = relativeDir.split('/').filter(p => p).length;
232
+ const upPath = '../'.repeat(depth);
233
+ createWebFComponentImport = `import { createWebFComponent, WebFElementWithMethods } from "${upPath}utils/createWebFComponent";`;
234
+ } else {
235
+ createWebFComponentImport = `import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui";`;
236
+ }
237
+
238
+ componentEntries.forEach(([className, component]) => {
239
+ const content = _.template(readTemplate('react.component.tsx'))({
240
+ className: className,
241
+ properties: component.properties,
242
+ events: component.events,
243
+ classObjectDictionary,
244
+ dependencies: '', // Dependencies will be at the top
245
+ blob,
246
+ toReactEventName,
247
+ toWebFTagName,
248
+ generateReturnType,
249
+ generateMethodDeclaration,
250
+ generateEventHandlerType,
251
+ getEventType,
252
+ });
253
+
254
+ // Remove the import statements from all but the first component
255
+ const lines = content.split('\n');
256
+ const withoutImports = lines.filter(line => {
257
+ return !line.startsWith('import ');
258
+ }).join('\n');
259
+
260
+ componentDefinitions.push(withoutImports);
140
261
  });
141
-
142
- const result = content.split('\n').filter(str => {
262
+
263
+ // Combine with shared imports at the top
264
+ const result = [
265
+ 'import React from "react";',
266
+ createWebFComponentImport,
267
+ '',
268
+ dependencies,
269
+ '',
270
+ ...componentDefinitions
271
+ ].filter(line => line !== undefined).join('\n');
272
+
273
+ return result.split('\n').filter(str => {
143
274
  return str.trim().length > 0;
144
275
  }).join('\n');
145
-
146
- return result;
147
276
  }
148
277
 
149
278
  export function generateReactIndex(blobs: IDLBlob[]) {
150
- const components = blobs.map(blob => {
151
- const classObjects = blob.objects as ClassObject[];
279
+ const components = blobs.flatMap(blob => {
280
+ const classObjects = blob.objects.filter(obj => obj instanceof ClassObject) as ClassObject[];
152
281
 
153
282
  const properties = classObjects.filter(object => {
154
283
  return object.name.endsWith('Properties');
@@ -157,25 +286,31 @@ export function generateReactIndex(blobs: IDLBlob[]) {
157
286
  return object.name.endsWith('Events');
158
287
  });
159
288
 
160
- const componentProperties = properties.length > 0 ? properties[0] : undefined;
161
- const componentEvents = events.length > 0 ? events[0] : undefined;
162
- const className = (() => {
163
- if (componentProperties) {
164
- return componentProperties.name.replace(/Properties$/, '');
165
- }
166
- if (componentEvents) {
167
- return componentEvents.name.replace(/Events$/, '');
168
- }
169
- return '';
170
- })();
171
- return {
289
+ // Create a map of component names
290
+ const componentMap = new Map<string, boolean>();
291
+
292
+ // Add all components from Properties interfaces
293
+ properties.forEach(prop => {
294
+ const componentName = prop.name.replace(/Properties$/, '');
295
+ componentMap.set(componentName, true);
296
+ });
297
+
298
+ // Add all components from Events interfaces
299
+ events.forEach(event => {
300
+ const componentName = event.name.replace(/Events$/, '');
301
+ componentMap.set(componentName, true);
302
+ });
303
+
304
+ // Return an array of all components from this file
305
+ return Array.from(componentMap.keys()).map(className => ({
172
306
  className: className,
173
307
  fileName: blob.filename,
174
308
  relativeDir: blob.relativeDir,
175
- }
176
- }).filter(name => {
177
- return name.className.length > 0;
309
+ }));
310
+ }).filter(component => {
311
+ return component.className.length > 0;
178
312
  });
313
+
179
314
  const content = _.template(readTemplate('react.index.ts'))({
180
315
  components,
181
316
  });
@@ -33,8 +33,9 @@ abstract class <%= className %>Bindings extends WidgetElement {
33
33
  <% var attributeName = _.kebabCase(prop.name); %>
34
34
  <% var propName = _.camelCase(prop.name); %>
35
35
  attributes['<%= attributeName %>'] = ElementAttributeProperty(
36
- getter: () => <%= propName %>.toString(),
37
- setter: (value) => <%= generateAttributeSetter(propName, prop.type) %>
36
+ getter: () => <%= generateAttributeGetter(propName, prop.type, prop.optional) %>,
37
+ setter: (value) => <%= generateAttributeSetter(propName, prop.type) %>,
38
+ deleter: () => <%= generateAttributeDeleter(propName, prop.type, prop.optional) %>
38
39
  );
39
40
  <% }); %>
40
41
  }
@@ -1,2 +1,9 @@
1
- dist/
2
1
  node_modules/
2
+ lib/
3
+ dist/
4
+ .DS_Store
5
+ *.log
6
+ *.tsbuildinfo
7
+ .env
8
+ .env.local
9
+ .env.*.local
@@ -1,53 +1,105 @@
1
- /*
2
- * Generated by TSDL, don't edit this file directly.
3
- */
4
-
5
- <% if (events) { %>
6
- import React, { EventHandler, SyntheticEvent } from "react";
7
- <% } %>
8
- import { createComponent } from "<%= utilsPath %>";
1
+ import React from "react";
2
+ import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui";
9
3
 
10
4
  <%= dependencies %>
11
5
 
12
- interface <%= className %>Props {
6
+ export interface <%= className %>Props {
13
7
  <% _.forEach(properties?.props, function(prop, index) { %>
14
8
  <% var propName = _.camelCase(prop.name); %>
9
+ <% var attributeName = _.kebabCase(prop.name); %>
10
+ /**
11
+ * <%= propName %> property
12
+ <% if (prop.optional) { %>
13
+ * @default undefined
14
+ <% } %>
15
+ */
15
16
  <% if (prop.optional) { %>
16
17
  <%= propName %>?: <%= generateReturnType(prop.type) %>;
17
18
  <% } else { %>
18
19
  <%= propName %>: <%= generateReturnType(prop.type) %>;
19
20
  <% } %>
21
+
20
22
  <% }); %>
21
23
  <% _.forEach(events?.props, function(prop, index) { %>
22
24
  <% var propName = toReactEventName(prop.name); %>
23
- <%= propName %>?: <%= generateEventHandlerType(prop.type) %>;
25
+ /**
26
+ * <%= prop.name %> event handler
27
+ */
28
+ <%= propName %>?: (event: <%= getEventType(prop.type) %>) => void;
29
+
24
30
  <% }); %>
31
+ /**
32
+ * Additional CSS styles
33
+ */
34
+ style?: React.CSSProperties;
35
+
36
+ /**
37
+ * Children elements
38
+ */
25
39
  children?: React.ReactNode;
40
+
41
+ /**
42
+ * Additional CSS class names
43
+ */
44
+ className?: string;
26
45
  }
27
46
 
28
- export interface <%= className %>Element extends HTMLElement {
29
- <% _.forEach(properties?.props, function(prop, index) { %>
30
- <% var propName = _.camelCase(prop.name); %>
31
- <% if (prop.optional) { %>
32
- <%= propName %>?: <%= generateReturnType(prop.type) %>;
33
- <% } else { %>
34
- <%= propName %>: <%= generateReturnType(prop.type) %>;
35
- <% } %>
36
- <% }); %>
47
+ export interface <%= className %>Element extends WebFElementWithMethods<{
37
48
  <% _.forEach(properties?.methods, function(method, index) { %>
38
49
  <%= generateMethodDeclaration(method) %>
39
50
  <% }); %>
40
- }
51
+ }> {}
41
52
 
42
- export const <%= className %> = createComponent({
43
- tagName: '<%= _.kebabCase(className) %>',
53
+ /**
54
+ * <%= className %> - WebF <%= className %> component
55
+ *
56
+ * @example
57
+ * ```tsx
58
+ * <<%= className %>
59
+ * // Add example props here
60
+ * >
61
+ * Content
62
+ * </<%= className %>>
63
+ * ```
64
+ */
65
+ export const <%= className %> = createWebFComponent<<%= className %>Element, <%= className %>Props>({
66
+ tagName: '<%= toWebFTagName(className) %>',
44
67
  displayName: '<%= className %>',
68
+
69
+ // Map props to attributes
70
+ attributeProps: [<% _.forEach(properties?.props, function(prop, index) { %>
71
+ <% var propName = _.camelCase(prop.name); %>'<%= propName %>',<% }); %>
72
+ ],
73
+
74
+ // Convert prop names to attribute names if needed
75
+ attributeMap: {
76
+ <% _.forEach(properties?.props, function(prop, index) { %>
77
+ <% var propName = _.camelCase(prop.name); %>
78
+ <% var attributeName = _.kebabCase(prop.name); %>
79
+ <% if (propName !== attributeName) { %>
80
+ <%= propName %>: '<%= attributeName %>',
81
+ <% } %>
82
+ <% }); %>
83
+ },
84
+
45
85
  <% if (events) { %>
46
- events: {
86
+ // Event handlers
87
+ events: [
47
88
  <% _.forEach(events?.props, function(prop, index) { %>
48
89
  <% var propName = toReactEventName(prop.name); %>
49
- <%= propName %>: '<%= prop.name %>',
90
+ {
91
+ propName: '<%= propName %>',
92
+ eventName: '<%= prop.name %>',
93
+ handler: (callback) => (event) => {
94
+ callback((event as <%= getEventType(prop.type) %>));
95
+ },
96
+ },
50
97
  <% }); %>
51
- }
98
+ ],
52
99
  <% } %>
53
- }) as React.ComponentType<<%= className %>Props & { ref?: React.Ref<HTMLUnknownElement> }>
100
+
101
+ // Default prop values
102
+ defaultProps: {
103
+ // Add default values here
104
+ },
105
+ });
@@ -5,4 +5,3 @@
5
5
  <% components.forEach(component => { %>
6
6
  export { <%= component.className %>, <%= component.className %>Element } from "./<%= component.relativeDir ? component.relativeDir + '/' : '' %><%= component.fileName %>";
7
7
  <% }); %>
8
- export { createComponent } from "./utils/createComponent";
@@ -17,6 +17,9 @@
17
17
  "react": ">=16.8.0",
18
18
  "react-dom": ">=16.8.0"
19
19
  },
20
+ "dependencies": {
21
+ "@openwebf/webf-react-core-ui": "^0.1.1"
22
+ },
20
23
  "devDependencies": {
21
24
  "@types/react": "^19.1.0",
22
25
  "@types/react-dom": "^19.1.2",