From af3b153f032ebac9cfd35a3ae2a846f568e2750f Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Wed, 11 Feb 2026 19:38:46 +0800 Subject: [PATCH 01/17] feat(shadcn-ui): add missing ShadInput config fields to FlutterShadcnInput Expose 10 additional ShadInput properties: textalign, autocapitalize, autocorrect, enablesuggestions, enterkeyhint, maxlines, minlines, cursorcolor, selectioncolor, and obscuringcharacter. Also add onSubmitted support via a new 'submit' event. Co-Authored-By: Claude Opus 4.6 --- .../lib/src/components/input.d.ts | 61 +++++ .../lib/src/components/input.dart | 251 ++++++++++++++++++ .../components/input_bindings_generated.dart | 120 +++++++++ 3 files changed, 432 insertions(+) diff --git a/native_uis/webf_shadcn_ui/lib/src/components/input.d.ts b/native_uis/webf_shadcn_ui/lib/src/components/input.d.ts index ed171b4aa1..7830b7450e 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/input.d.ts +++ b/native_uis/webf_shadcn_ui/lib/src/components/input.d.ts @@ -69,6 +69,64 @@ interface FlutterShadcnInputProperties { * Autofocus on mount. */ autofocus?: boolean; + + /** + * Text alignment within the input. + * Options: 'start', 'end', 'left', 'right', 'center' + * Default: 'start' + */ + textalign?: string; + + /** + * Controls automatic text capitalization behavior. + * Options: 'none', 'sentences', 'words', 'characters' + * Default: 'none' + */ + autocapitalize?: string; + + /** + * Whether to enable autocorrect. + * Default: true + */ + autocorrect?: boolean; + + /** + * Whether to show input suggestions. + * Default: true + */ + enablesuggestions?: boolean; + + /** + * Hint for the keyboard action button. + * Options: 'done', 'go', 'next', 'search', 'send', 'previous', 'newline' + */ + enterkeyhint?: string; + + /** + * Maximum number of lines for the input. Use for multi-line input. + */ + maxlines?: string; + + /** + * Minimum number of lines for the input. + */ + minlines?: string; + + /** + * Color of the cursor (e.g., '#FF0000', 'red'). + */ + cursorcolor?: string; + + /** + * Color of the text selection highlight (e.g., '#0000FF', 'blue'). + */ + selectioncolor?: string; + + /** + * Character used to obscure text in password mode. + * Default: '•' + */ + obscuringcharacter?: string; } /** @@ -86,4 +144,7 @@ interface FlutterShadcnInputEvents { /** Fired when input loses focus. */ blur: Event; + + /** Fired when the user submits the input (e.g., presses Enter/Done). */ + submit: Event; } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/input.dart b/native_uis/webf_shadcn_ui/lib/src/components/input.dart index ff6c040af7..1a5580c0d5 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/input.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/input.dart @@ -26,6 +26,16 @@ class FlutterShadcnInput extends FlutterShadcnInputBindings { String? _pattern; bool _required = false; bool _autofocus = false; + TextAlign _textAlign = TextAlign.start; + TextCapitalization _textCapitalization = TextCapitalization.none; + bool _autocorrect = true; + bool _enableSuggestions = true; + TextInputAction? _textInputAction; + int? _maxLines = 1; + int? _minLines; + Color? _cursorColor; + Color? _selectionColor; + String _obscuringCharacter = '•'; @override String get value => _value; @@ -144,6 +154,233 @@ class FlutterShadcnInput extends FlutterShadcnInputBindings { } } + @override + String get textalign => _textAlign.name; + + @override + set textalign(value) { + final newValue = _parseTextAlign(value?.toString() ?? 'start'); + if (newValue != _textAlign) { + _textAlign = newValue; + state?.requestUpdateState(() {}); + } + } + + @override + String get autocapitalize => _textCapitalizationToString(_textCapitalization); + + @override + set autocapitalize(value) { + final newValue = _parseTextCapitalization(value?.toString() ?? 'none'); + if (newValue != _textCapitalization) { + _textCapitalization = newValue; + state?.requestUpdateState(() {}); + } + } + + @override + bool get autocorrect => _autocorrect; + + @override + set autocorrect(value) { + final newValue = value != false; + if (newValue != _autocorrect) { + _autocorrect = newValue; + state?.requestUpdateState(() {}); + } + } + + @override + bool get enablesuggestions => _enableSuggestions; + + @override + set enablesuggestions(value) { + final newValue = value != false; + if (newValue != _enableSuggestions) { + _enableSuggestions = newValue; + state?.requestUpdateState(() {}); + } + } + + @override + String? get enterkeyhint => + _textInputAction != null ? _textInputActionToString(_textInputAction!) : null; + + @override + set enterkeyhint(value) { + final newValue = value != null ? _parseTextInputAction(value.toString()) : null; + if (newValue != _textInputAction) { + _textInputAction = newValue; + state?.requestUpdateState(() {}); + } + } + + @override + String? get maxlines => _maxLines?.toString(); + + @override + set maxlines(value) { + final newValue = value != null ? int.tryParse(value.toString()) : null; + if (newValue != _maxLines) { + _maxLines = newValue; + state?.requestUpdateState(() {}); + } + } + + @override + String? get minlines => _minLines?.toString(); + + @override + set minlines(value) { + final newValue = value != null ? int.tryParse(value.toString()) : null; + if (newValue != _minLines) { + _minLines = newValue; + state?.requestUpdateState(() {}); + } + } + + @override + String? get cursorcolor => _cursorColor != null + ? '#${_cursorColor!.value.toRadixString(16).padLeft(8, '0')}' + : null; + + @override + set cursorcolor(value) { + final newValue = value != null ? _parseColor(value.toString()) : null; + if (newValue != _cursorColor) { + _cursorColor = newValue; + state?.requestUpdateState(() {}); + } + } + + @override + String? get selectioncolor => _selectionColor != null + ? '#${_selectionColor!.value.toRadixString(16).padLeft(8, '0')}' + : null; + + @override + set selectioncolor(value) { + final newValue = value != null ? _parseColor(value.toString()) : null; + if (newValue != _selectionColor) { + _selectionColor = newValue; + state?.requestUpdateState(() {}); + } + } + + @override + String get obscuringcharacter => _obscuringCharacter; + + @override + set obscuringcharacter(value) { + final newValue = value?.toString() ?? '•'; + if (newValue.isNotEmpty && newValue != _obscuringCharacter) { + _obscuringCharacter = newValue.characters.first; + state?.requestUpdateState(() {}); + } + } + + // --- Parsing helpers --- + + static TextAlign _parseTextAlign(String value) { + switch (value.toLowerCase()) { + case 'left': + return TextAlign.left; + case 'right': + return TextAlign.right; + case 'center': + return TextAlign.center; + case 'end': + return TextAlign.end; + case 'start': + default: + return TextAlign.start; + } + } + + static TextCapitalization _parseTextCapitalization(String value) { + switch (value.toLowerCase()) { + case 'sentences': + return TextCapitalization.sentences; + case 'words': + return TextCapitalization.words; + case 'characters': + return TextCapitalization.characters; + case 'none': + default: + return TextCapitalization.none; + } + } + + static String _textCapitalizationToString(TextCapitalization value) { + switch (value) { + case TextCapitalization.sentences: + return 'sentences'; + case TextCapitalization.words: + return 'words'; + case TextCapitalization.characters: + return 'characters'; + case TextCapitalization.none: + return 'none'; + } + } + + static TextInputAction _parseTextInputAction(String value) { + switch (value.toLowerCase()) { + case 'done': + return TextInputAction.done; + case 'go': + return TextInputAction.go; + case 'next': + return TextInputAction.next; + case 'search': + return TextInputAction.search; + case 'send': + return TextInputAction.send; + case 'previous': + return TextInputAction.previous; + case 'newline': + return TextInputAction.newline; + default: + return TextInputAction.done; + } + } + + static String _textInputActionToString(TextInputAction value) { + switch (value) { + case TextInputAction.done: + return 'done'; + case TextInputAction.go: + return 'go'; + case TextInputAction.next: + return 'next'; + case TextInputAction.search: + return 'search'; + case TextInputAction.send: + return 'send'; + case TextInputAction.previous: + return 'previous'; + case TextInputAction.newline: + return 'newline'; + default: + return 'done'; + } + } + + static Color? _parseColor(String value) { + final trimmed = value.trim().toLowerCase(); + if (trimmed.startsWith('#')) { + final hex = trimmed.substring(1); + if (hex.length == 6) { + final intValue = int.tryParse(hex, radix: 16); + if (intValue != null) return Color(0xFF000000 | intValue); + } else if (hex.length == 8) { + final intValue = int.tryParse(hex, radix: 16); + if (intValue != null) return Color(intValue); + } + } + return null; + } + TextInputType get keyboardType { switch (_type.toLowerCase()) { case 'email': @@ -224,13 +461,27 @@ class FlutterShadcnInputState extends WebFWidgetElementState { enabled: !widgetElement.disabled, readOnly: widgetElement.readonly, obscureText: widgetElement.obscureText, + obscuringCharacter: widgetElement._obscuringCharacter, keyboardType: widgetElement.keyboardType, + textInputAction: widgetElement._textInputAction, + textCapitalization: widgetElement._textCapitalization, + textAlign: widgetElement._textAlign, autofocus: widgetElement.autofocus, + autocorrect: widgetElement._autocorrect, + enableSuggestions: widgetElement._enableSuggestions, + maxLines: widgetElement._maxLines, + minLines: widgetElement._minLines, + cursorColor: widgetElement._cursorColor, + selectionColor: widgetElement._selectionColor, inputFormatters: inputFormatters, onChanged: (value) { widgetElement._value = value; widgetElement.dispatchEvent(Event('input')); }, + onSubmitted: (value) { + widgetElement._value = value; + widgetElement.dispatchEvent(Event('submit')); + }, ); } } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/input_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/input_bindings_generated.dart index 1108f93151..bce8ebc9b1 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/input_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/input_bindings_generated.dart @@ -30,6 +30,26 @@ abstract class FlutterShadcnInputBindings extends WidgetElement { set required(value); bool get autofocus; set autofocus(value); + String? get textalign; + set textalign(value); + String? get autocapitalize; + set autocapitalize(value); + bool get autocorrect; + set autocorrect(value); + bool get enablesuggestions; + set enablesuggestions(value); + String? get enterkeyhint; + set enterkeyhint(value); + String? get maxlines; + set maxlines(value); + String? get minlines; + set minlines(value); + String? get cursorcolor; + set cursorcolor(value); + String? get selectioncolor; + set selectioncolor(value); + String? get obscuringcharacter; + set obscuringcharacter(value); @override void initializeAttributes(Map attributes) { super.initializeAttributes(attributes); @@ -83,6 +103,56 @@ abstract class FlutterShadcnInputBindings extends WidgetElement { setter: (value) => this.autofocus = value == 'true' || value == '', deleter: () => autofocus = false ); + attributes['textalign'] = ElementAttributeProperty( + getter: () => textalign?.toString(), + setter: (value) => this.textalign = value, + deleter: () => textalign = null + ); + attributes['autocapitalize'] = ElementAttributeProperty( + getter: () => autocapitalize?.toString(), + setter: (value) => this.autocapitalize = value, + deleter: () => autocapitalize = null + ); + attributes['autocorrect'] = ElementAttributeProperty( + getter: () => autocorrect.toString(), + setter: (value) => this.autocorrect = value == 'true' || value == '', + deleter: () => autocorrect = true + ); + attributes['enablesuggestions'] = ElementAttributeProperty( + getter: () => enablesuggestions.toString(), + setter: (value) => this.enablesuggestions = value == 'true' || value == '', + deleter: () => enablesuggestions = true + ); + attributes['enterkeyhint'] = ElementAttributeProperty( + getter: () => enterkeyhint?.toString(), + setter: (value) => this.enterkeyhint = value, + deleter: () => enterkeyhint = null + ); + attributes['maxlines'] = ElementAttributeProperty( + getter: () => maxlines?.toString(), + setter: (value) => this.maxlines = value, + deleter: () => maxlines = null + ); + attributes['minlines'] = ElementAttributeProperty( + getter: () => minlines?.toString(), + setter: (value) => this.minlines = value, + deleter: () => minlines = null + ); + attributes['cursorcolor'] = ElementAttributeProperty( + getter: () => cursorcolor?.toString(), + setter: (value) => this.cursorcolor = value, + deleter: () => cursorcolor = null + ); + attributes['selectioncolor'] = ElementAttributeProperty( + getter: () => selectioncolor?.toString(), + setter: (value) => this.selectioncolor = value, + deleter: () => selectioncolor = null + ); + attributes['obscuringcharacter'] = ElementAttributeProperty( + getter: () => obscuringcharacter?.toString(), + setter: (value) => this.obscuringcharacter = value, + deleter: () => obscuringcharacter = null + ); } static StaticDefinedBindingPropertyMap flutterShadcnInputProperties = { 'value': StaticDefinedBindingProperty( @@ -135,6 +205,56 @@ abstract class FlutterShadcnInputBindings extends WidgetElement { setter: (element, value) => castToType(element).autofocus = value, ), + 'textalign': StaticDefinedBindingProperty( + getter: (element) => castToType(element).textalign, + setter: (element, value) => + castToType(element).textalign = value, + ), + 'autocapitalize': StaticDefinedBindingProperty( + getter: (element) => castToType(element).autocapitalize, + setter: (element, value) => + castToType(element).autocapitalize = value, + ), + 'autocorrect': StaticDefinedBindingProperty( + getter: (element) => castToType(element).autocorrect, + setter: (element, value) => + castToType(element).autocorrect = value, + ), + 'enablesuggestions': StaticDefinedBindingProperty( + getter: (element) => castToType(element).enablesuggestions, + setter: (element, value) => + castToType(element).enablesuggestions = value, + ), + 'enterkeyhint': StaticDefinedBindingProperty( + getter: (element) => castToType(element).enterkeyhint, + setter: (element, value) => + castToType(element).enterkeyhint = value, + ), + 'maxlines': StaticDefinedBindingProperty( + getter: (element) => castToType(element).maxlines, + setter: (element, value) => + castToType(element).maxlines = value, + ), + 'minlines': StaticDefinedBindingProperty( + getter: (element) => castToType(element).minlines, + setter: (element, value) => + castToType(element).minlines = value, + ), + 'cursorcolor': StaticDefinedBindingProperty( + getter: (element) => castToType(element).cursorcolor, + setter: (element, value) => + castToType(element).cursorcolor = value, + ), + 'selectioncolor': StaticDefinedBindingProperty( + getter: (element) => castToType(element).selectioncolor, + setter: (element, value) => + castToType(element).selectioncolor = value, + ), + 'obscuringcharacter': StaticDefinedBindingProperty( + getter: (element) => castToType(element).obscuringcharacter, + setter: (element, value) => + castToType(element).obscuringcharacter = value, + ), }; @override List get properties => [ From 119f76a14f5e2bcfec2f03a88e9b97b2054271d1 Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Wed, 11 Feb 2026 19:52:42 +0800 Subject: [PATCH 02/17] feat(shadcn-ui): regenerate bindings and add input demo page Run `webf codegen` to regenerate all Dart bindings and produce the React package at packages/react-shadcn-ui with the new input properties. Update ShadcnInputPage with demos for text alignment, autocapitalize, autocorrect, enterkeyhint, multi-line, cursor/selection colors, and obscuring character. Co-Authored-By: Claude Opus 4.6 --- .../accordion_bindings_generated.dart | 6 +- .../components/alert_bindings_generated.dart | 4 +- .../components/avatar_bindings_generated.dart | 8 +- .../components/badge_bindings_generated.dart | 2 +- .../breadcrumb_bindings_generated.dart | 4 +- .../components/button_bindings_generated.dart | 10 +- .../calendar_bindings_generated.dart | 26 +- .../checkbox_bindings_generated.dart | 6 +- .../collapsible_bindings_generated.dart | 4 +- .../combobox_bindings_generated.dart | 12 +- .../context_menu_bindings_generated.dart | 2 +- .../date_picker_bindings_generated.dart | 8 +- .../components/dialog_bindings_generated.dart | 4 +- .../dropdown_menu_bindings_generated.dart | 2 +- .../components/form_bindings_generated.dart | 49 +- .../icon_button_bindings_generated.dart | 10 +- .../components/image_bindings_generated.dart | 10 +- .../components/input_bindings_generated.dart | 44 +- .../popover_bindings_generated.dart | 6 +- .../progress_bindings_generated.dart | 6 +- .../components/radio_bindings_generated.dart | 6 +- .../scroll_area_bindings_generated.dart | 2 +- .../components/select_bindings_generated.dart | 16 +- .../separator_bindings_generated.dart | 2 +- .../components/sheet_bindings_generated.dart | 6 +- .../skeleton_bindings_generated.dart | 6 +- .../components/slider_bindings_generated.dart | 12 +- .../components/switch_bindings_generated.dart | 4 +- .../components/tabs_bindings_generated.dart | 4 +- .../textarea_bindings_generated.dart | 16 +- .../time_picker_bindings_generated.dart | 8 +- .../components/toast_bindings_generated.dart | 10 +- .../tooltip_bindings_generated.dart | 8 +- .../src/theme/theme_bindings_generated.dart | 6 +- packages/react-shadcn-ui/.gitignore | 9 + packages/react-shadcn-ui/README.md | 206 +++ packages/react-shadcn-ui/global.d.ts | 2 + packages/react-shadcn-ui/package-lock.json | 1244 +++++++++++++++++ packages/react-shadcn-ui/package.json | 41 + packages/react-shadcn-ui/src/index.ts | 78 ++ packages/react-shadcn-ui/src/types.ts | 2 + packages/react-shadcn-ui/tsconfig.json | 23 + packages/react-shadcn-ui/tsdown.config.ts | 8 + .../src/pages/shadcn/ShadcnInputPage.tsx | 128 +- 44 files changed, 1876 insertions(+), 194 deletions(-) create mode 100644 packages/react-shadcn-ui/.gitignore create mode 100644 packages/react-shadcn-ui/README.md create mode 100644 packages/react-shadcn-ui/global.d.ts create mode 100644 packages/react-shadcn-ui/package-lock.json create mode 100644 packages/react-shadcn-ui/package.json create mode 100644 packages/react-shadcn-ui/src/index.ts create mode 100644 packages/react-shadcn-ui/src/types.ts create mode 100644 packages/react-shadcn-ui/tsconfig.json create mode 100644 packages/react-shadcn-ui/tsdown.config.ts diff --git a/native_uis/webf_shadcn_ui/lib/src/components/accordion_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/accordion_bindings_generated.dart index 2b709c349e..6e918b5639 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/accordion_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/accordion_bindings_generated.dart @@ -21,17 +21,17 @@ abstract class FlutterShadcnAccordionBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['type'] = ElementAttributeProperty( getter: () => type?.toString(), - setter: (value) => this.type = value, + setter: (value) => type = value, deleter: () => type = null ); attributes['value'] = ElementAttributeProperty( getter: () => value?.toString(), - setter: (value) => this.value = value, + setter: (value) => value = value, deleter: () => value = null ); attributes['collapsible'] = ElementAttributeProperty( getter: () => collapsible.toString(), - setter: (value) => this.collapsible = value == 'true' || value == '', + setter: (value) => collapsible = value == 'true' || value == '', deleter: () => collapsible = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/alert_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/alert_bindings_generated.dart index 083d946088..cc19ef5650 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/alert_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/alert_bindings_generated.dart @@ -19,12 +19,12 @@ abstract class FlutterShadcnAlertBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['variant'] = ElementAttributeProperty( getter: () => variant?.toString(), - setter: (value) => this.variant = value, + setter: (value) => variant = value, deleter: () => variant = null ); attributes['icon'] = ElementAttributeProperty( getter: () => icon?.toString(), - setter: (value) => this.icon = value, + setter: (value) => icon = value, deleter: () => icon = null ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/avatar_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/avatar_bindings_generated.dart index 3e89eb61b2..b7f915709e 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/avatar_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/avatar_bindings_generated.dart @@ -23,22 +23,22 @@ abstract class FlutterShadcnAvatarBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['src'] = ElementAttributeProperty( getter: () => src?.toString(), - setter: (value) => this.src = value, + setter: (value) => src = value, deleter: () => src = null ); attributes['alt'] = ElementAttributeProperty( getter: () => alt?.toString(), - setter: (value) => this.alt = value, + setter: (value) => alt = value, deleter: () => alt = null ); attributes['fallback'] = ElementAttributeProperty( getter: () => fallback?.toString(), - setter: (value) => this.fallback = value, + setter: (value) => fallback = value, deleter: () => fallback = null ); attributes['size'] = ElementAttributeProperty( getter: () => size?.toString(), - setter: (value) => this.size = value, + setter: (value) => size = value, deleter: () => size = null ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/badge_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/badge_bindings_generated.dart index 4497f88010..03ab361024 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/badge_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/badge_bindings_generated.dart @@ -17,7 +17,7 @@ abstract class FlutterShadcnBadgeBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['variant'] = ElementAttributeProperty( getter: () => variant?.toString(), - setter: (value) => this.variant = value, + setter: (value) => variant = value, deleter: () => variant = null ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/breadcrumb_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/breadcrumb_bindings_generated.dart index 7262103791..520842caa4 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/breadcrumb_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/breadcrumb_bindings_generated.dart @@ -37,12 +37,12 @@ abstract class FlutterShadcnBreadcrumbBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['spacing'] = ElementAttributeProperty( getter: () => spacing?.toString(), - setter: (value) => this.spacing = double.tryParse(value) ?? 0.0, + setter: (value) => spacing = double.tryParse(value) ?? 0.0, deleter: () => spacing = 0.0 ); attributes['separator'] = ElementAttributeProperty( getter: () => separator?.value, - setter: (value) => this.separator = FlutterShadcnBreadcrumbSeparator.parse(value), + setter: (value) => separator = FlutterShadcnBreadcrumbSeparator.parse(value), deleter: () => separator = null ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/button_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/button_bindings_generated.dart index 6184a0a4d8..514c72f8d3 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/button_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/button_bindings_generated.dart @@ -25,27 +25,27 @@ abstract class FlutterShadcnButtonBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['variant'] = ElementAttributeProperty( getter: () => variant?.toString(), - setter: (value) => this.variant = value, + setter: (value) => variant = value, deleter: () => variant = null ); attributes['size'] = ElementAttributeProperty( getter: () => size?.toString(), - setter: (value) => this.size = value, + setter: (value) => size = value, deleter: () => size = null ); attributes['disabled'] = ElementAttributeProperty( getter: () => disabled.toString(), - setter: (value) => this.disabled = value == 'true' || value == '', + setter: (value) => disabled = value == 'true' || value == '', deleter: () => disabled = false ); attributes['loading'] = ElementAttributeProperty( getter: () => loading.toString(), - setter: (value) => this.loading = value == 'true' || value == '', + setter: (value) => loading = value == 'true' || value == '', deleter: () => loading = false ); attributes['icon'] = ElementAttributeProperty( getter: () => icon?.toString(), - setter: (value) => this.icon = value, + setter: (value) => icon = value, deleter: () => icon = null ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/calendar_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/calendar_bindings_generated.dart index 9bc3124bd7..78dc042e48 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/calendar_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/calendar_bindings_generated.dart @@ -74,67 +74,67 @@ abstract class FlutterShadcnCalendarBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['mode'] = ElementAttributeProperty( getter: () => mode?.value, - setter: (value) => this.mode = FlutterShadcnCalendarMode.parse(value), + setter: (value) => mode = FlutterShadcnCalendarMode.parse(value), deleter: () => mode = null ); attributes['value'] = ElementAttributeProperty( getter: () => value?.toString(), - setter: (value) => this.value = value, + setter: (value) => value = value, deleter: () => value = null ); attributes['disabled'] = ElementAttributeProperty( getter: () => disabled.toString(), - setter: (value) => this.disabled = value == 'true' || value == '', + setter: (value) => disabled = value == 'true' || value == '', deleter: () => disabled = false ); attributes['min'] = ElementAttributeProperty( getter: () => min?.toString(), - setter: (value) => this.min = value, + setter: (value) => min = value, deleter: () => min = null ); attributes['max'] = ElementAttributeProperty( getter: () => max?.toString(), - setter: (value) => this.max = value, + setter: (value) => max = value, deleter: () => max = null ); attributes['caption-layout'] = ElementAttributeProperty( getter: () => captionLayout?.value, - setter: (value) => this.captionLayout = FlutterShadcnCalendarCaptionLayout.parse(value), + setter: (value) => captionLayout = FlutterShadcnCalendarCaptionLayout.parse(value), deleter: () => captionLayout = null ); attributes['hide-navigation'] = ElementAttributeProperty( getter: () => hideNavigation.toString(), - setter: (value) => this.hideNavigation = value == 'true' || value == '', + setter: (value) => hideNavigation = value == 'true' || value == '', deleter: () => hideNavigation = false ); attributes['show-week-numbers'] = ElementAttributeProperty( getter: () => showWeekNumbers.toString(), - setter: (value) => this.showWeekNumbers = value == 'true' || value == '', + setter: (value) => showWeekNumbers = value == 'true' || value == '', deleter: () => showWeekNumbers = false ); attributes['show-outside-days'] = ElementAttributeProperty( getter: () => showOutsideDays.toString(), - setter: (value) => this.showOutsideDays = value == 'true' || value == '', + setter: (value) => showOutsideDays = value == 'true' || value == '', deleter: () => showOutsideDays = false ); attributes['fixed-weeks'] = ElementAttributeProperty( getter: () => fixedWeeks.toString(), - setter: (value) => this.fixedWeeks = value == 'true' || value == '', + setter: (value) => fixedWeeks = value == 'true' || value == '', deleter: () => fixedWeeks = false ); attributes['hide-weekday-names'] = ElementAttributeProperty( getter: () => hideWeekdayNames.toString(), - setter: (value) => this.hideWeekdayNames = value == 'true' || value == '', + setter: (value) => hideWeekdayNames = value == 'true' || value == '', deleter: () => hideWeekdayNames = false ); attributes['number-of-months'] = ElementAttributeProperty( getter: () => numberOfMonths?.toString(), - setter: (value) => this.numberOfMonths = double.tryParse(value) ?? 0.0, + setter: (value) => numberOfMonths = double.tryParse(value) ?? 0.0, deleter: () => numberOfMonths = 0.0 ); attributes['allow-deselection'] = ElementAttributeProperty( getter: () => allowDeselection.toString(), - setter: (value) => this.allowDeselection = value == 'true' || value == '', + setter: (value) => allowDeselection = value == 'true' || value == '', deleter: () => allowDeselection = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/checkbox_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/checkbox_bindings_generated.dart index 9ec18afde8..10a9765d80 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/checkbox_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/checkbox_bindings_generated.dart @@ -21,17 +21,17 @@ abstract class FlutterShadcnCheckboxBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['checked'] = ElementAttributeProperty( getter: () => checked.toString(), - setter: (value) => this.checked = value == 'true' || value == '', + setter: (value) => checked = value == 'true' || value == '', deleter: () => checked = false ); attributes['disabled'] = ElementAttributeProperty( getter: () => disabled.toString(), - setter: (value) => this.disabled = value == 'true' || value == '', + setter: (value) => disabled = value == 'true' || value == '', deleter: () => disabled = false ); attributes['indeterminate'] = ElementAttributeProperty( getter: () => indeterminate.toString(), - setter: (value) => this.indeterminate = value == 'true' || value == '', + setter: (value) => indeterminate = value == 'true' || value == '', deleter: () => indeterminate = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/collapsible_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/collapsible_bindings_generated.dart index c60aa8957a..17e9128e55 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/collapsible_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/collapsible_bindings_generated.dart @@ -19,12 +19,12 @@ abstract class FlutterShadcnCollapsibleBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['open'] = ElementAttributeProperty( getter: () => open.toString(), - setter: (value) => this.open = value == 'true' || value == '', + setter: (value) => open = value == 'true' || value == '', deleter: () => open = false ); attributes['disabled'] = ElementAttributeProperty( getter: () => disabled.toString(), - setter: (value) => this.disabled = value == 'true' || value == '', + setter: (value) => disabled = value == 'true' || value == '', deleter: () => disabled = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/combobox_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/combobox_bindings_generated.dart index 667c557b77..8062af8c57 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/combobox_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/combobox_bindings_generated.dart @@ -27,32 +27,32 @@ abstract class FlutterShadcnComboboxBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['value'] = ElementAttributeProperty( getter: () => value?.toString(), - setter: (value) => this.value = value, + setter: (value) => value = value, deleter: () => value = null ); attributes['placeholder'] = ElementAttributeProperty( getter: () => placeholder?.toString(), - setter: (value) => this.placeholder = value, + setter: (value) => placeholder = value, deleter: () => placeholder = null ); attributes['search-placeholder'] = ElementAttributeProperty( getter: () => searchPlaceholder?.toString(), - setter: (value) => this.searchPlaceholder = value, + setter: (value) => searchPlaceholder = value, deleter: () => searchPlaceholder = null ); attributes['empty-text'] = ElementAttributeProperty( getter: () => emptyText?.toString(), - setter: (value) => this.emptyText = value, + setter: (value) => emptyText = value, deleter: () => emptyText = null ); attributes['disabled'] = ElementAttributeProperty( getter: () => disabled.toString(), - setter: (value) => this.disabled = value == 'true' || value == '', + setter: (value) => disabled = value == 'true' || value == '', deleter: () => disabled = false ); attributes['clearable'] = ElementAttributeProperty( getter: () => clearable.toString(), - setter: (value) => this.clearable = value == 'true' || value == '', + setter: (value) => clearable = value == 'true' || value == '', deleter: () => clearable = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/context_menu_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/context_menu_bindings_generated.dart index 3fa578d47f..67f1823396 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/context_menu_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/context_menu_bindings_generated.dart @@ -17,7 +17,7 @@ abstract class FlutterShadcnContextMenuBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['open'] = ElementAttributeProperty( getter: () => open.toString(), - setter: (value) => this.open = value == 'true' || value == '', + setter: (value) => open = value == 'true' || value == '', deleter: () => open = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/date_picker_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/date_picker_bindings_generated.dart index ea847cc407..f2a1488aa7 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/date_picker_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/date_picker_bindings_generated.dart @@ -23,22 +23,22 @@ abstract class FlutterShadcnDatePickerBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['value'] = ElementAttributeProperty( getter: () => value?.toString(), - setter: (value) => this.value = value, + setter: (value) => value = value, deleter: () => value = null ); attributes['placeholder'] = ElementAttributeProperty( getter: () => placeholder?.toString(), - setter: (value) => this.placeholder = value, + setter: (value) => placeholder = value, deleter: () => placeholder = null ); attributes['disabled'] = ElementAttributeProperty( getter: () => disabled.toString(), - setter: (value) => this.disabled = value == 'true' || value == '', + setter: (value) => disabled = value == 'true' || value == '', deleter: () => disabled = false ); attributes['format'] = ElementAttributeProperty( getter: () => format?.toString(), - setter: (value) => this.format = value, + setter: (value) => format = value, deleter: () => format = null ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/dialog_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/dialog_bindings_generated.dart index adcbf779b8..a9203b5eb4 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/dialog_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/dialog_bindings_generated.dart @@ -19,12 +19,12 @@ abstract class FlutterShadcnDialogBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['open'] = ElementAttributeProperty( getter: () => open.toString(), - setter: (value) => this.open = value == 'true' || value == '', + setter: (value) => open = value == 'true' || value == '', deleter: () => open = false ); attributes['close-on-outside-click'] = ElementAttributeProperty( getter: () => closeOnOutsideClick.toString(), - setter: (value) => this.closeOnOutsideClick = value == 'true' || value == '', + setter: (value) => closeOnOutsideClick = value == 'true' || value == '', deleter: () => closeOnOutsideClick = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/dropdown_menu_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/dropdown_menu_bindings_generated.dart index 84e3e2a934..d3e66cdfb8 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/dropdown_menu_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/dropdown_menu_bindings_generated.dart @@ -17,7 +17,7 @@ abstract class FlutterShadcnDropdownMenuBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['open'] = ElementAttributeProperty( getter: () => open.toString(), - setter: (value) => this.open = value == 'true' || value == '', + setter: (value) => open = value == 'true' || value == '', deleter: () => open = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/form_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/form_bindings_generated.dart index 0180492924..2af252a3d2 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/form_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/form_bindings_generated.dart @@ -38,17 +38,17 @@ abstract class FlutterShadcnFormBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['disabled'] = ElementAttributeProperty( getter: () => disabled.toString(), - setter: (value) => this.disabled = value == 'true' || value == '', + setter: (value) => disabled = value == 'true' || value == '', deleter: () => disabled = false ); attributes['auto-validate-mode'] = ElementAttributeProperty( getter: () => autoValidateMode?.value, - setter: (value) => this.autoValidateMode = FlutterShadcnFormAutoValidateMode.parse(value), + setter: (value) => autoValidateMode = FlutterShadcnFormAutoValidateMode.parse(value), deleter: () => autoValidateMode = null ); attributes['value'] = ElementAttributeProperty( getter: () => value?.toString(), - setter: (value) => this.value = value, + setter: (value) => value = value, deleter: () => value = null ); } @@ -74,47 +74,4 @@ abstract class FlutterShadcnFormBindings extends WidgetElement { ...super.properties, flutterShadcnFormProperties, ]; - bool validate(List args); - bool submit(List args); - void reset(List args); - dynamic getFieldValue(List args); - void setFieldValue(List args); - void setFieldError(List args); - static StaticDefinedSyncBindingObjectMethodMap flutterShadcnFormMethods = { - 'validate': StaticDefinedSyncBindingObjectMethod( - call: (element, args) { - return castToType(element).validate(args); - }, - ), - 'submit': StaticDefinedSyncBindingObjectMethod( - call: (element, args) { - return castToType(element).submit(args); - }, - ), - 'reset': StaticDefinedSyncBindingObjectMethod( - call: (element, args) { - return castToType(element).reset(args); - }, - ), - 'getFieldValue': StaticDefinedSyncBindingObjectMethod( - call: (element, args) { - return castToType(element).getFieldValue(args); - }, - ), - 'setFieldValue': StaticDefinedSyncBindingObjectMethod( - call: (element, args) { - return castToType(element).setFieldValue(args); - }, - ), - 'setFieldError': StaticDefinedSyncBindingObjectMethod( - call: (element, args) { - return castToType(element).setFieldError(args); - }, - ), - }; - @override - List get methods => [ - ...super.methods, - flutterShadcnFormMethods, - ]; } \ No newline at end of file diff --git a/native_uis/webf_shadcn_ui/lib/src/components/icon_button_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/icon_button_bindings_generated.dart index 67e702a816..d55e32d88e 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/icon_button_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/icon_button_bindings_generated.dart @@ -43,27 +43,27 @@ abstract class FlutterShadcnIconButtonBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['variant'] = ElementAttributeProperty( getter: () => variant?.value, - setter: (value) => this.variant = FlutterShadcnIconButtonVariant.parse(value), + setter: (value) => variant = FlutterShadcnIconButtonVariant.parse(value), deleter: () => variant = null ); attributes['icon'] = ElementAttributeProperty( getter: () => icon?.toString(), - setter: (value) => this.icon = value, + setter: (value) => icon = value, deleter: () => icon = null ); attributes['icon-size'] = ElementAttributeProperty( getter: () => iconSize?.toString(), - setter: (value) => this.iconSize = double.tryParse(value) ?? 0.0, + setter: (value) => iconSize = double.tryParse(value) ?? 0.0, deleter: () => iconSize = 0.0 ); attributes['disabled'] = ElementAttributeProperty( getter: () => disabled.toString(), - setter: (value) => this.disabled = value == 'true' || value == '', + setter: (value) => disabled = value == 'true' || value == '', deleter: () => disabled = false ); attributes['loading'] = ElementAttributeProperty( getter: () => loading.toString(), - setter: (value) => this.loading = value == 'true' || value == '', + setter: (value) => loading = value == 'true' || value == '', deleter: () => loading = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/image_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/image_bindings_generated.dart index 0d4ea2c0a3..03e821342e 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/image_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/image_bindings_generated.dart @@ -25,27 +25,27 @@ abstract class FlutterShadcnImageBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['src'] = ElementAttributeProperty( getter: () => src?.toString(), - setter: (value) => this.src = value, + setter: (value) => src = value, deleter: () => src = null ); attributes['alt'] = ElementAttributeProperty( getter: () => alt?.toString(), - setter: (value) => this.alt = value, + setter: (value) => alt = value, deleter: () => alt = null ); attributes['width'] = ElementAttributeProperty( getter: () => width?.toString(), - setter: (value) => this.width = value, + setter: (value) => width = value, deleter: () => width = null ); attributes['height'] = ElementAttributeProperty( getter: () => height?.toString(), - setter: (value) => this.height = value, + setter: (value) => height = value, deleter: () => height = null ); attributes['fit'] = ElementAttributeProperty( getter: () => fit?.toString(), - setter: (value) => this.fit = value, + setter: (value) => fit = value, deleter: () => fit = null ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/input_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/input_bindings_generated.dart index bce8ebc9b1..3526cc29c7 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/input_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/input_bindings_generated.dart @@ -55,102 +55,102 @@ abstract class FlutterShadcnInputBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['value'] = ElementAttributeProperty( getter: () => value?.toString(), - setter: (value) => this.value = value, + setter: (value) => value = value, deleter: () => value = null ); attributes['placeholder'] = ElementAttributeProperty( getter: () => placeholder?.toString(), - setter: (value) => this.placeholder = value, + setter: (value) => placeholder = value, deleter: () => placeholder = null ); attributes['type'] = ElementAttributeProperty( getter: () => type?.toString(), - setter: (value) => this.type = value, + setter: (value) => type = value, deleter: () => type = null ); attributes['disabled'] = ElementAttributeProperty( getter: () => disabled.toString(), - setter: (value) => this.disabled = value == 'true' || value == '', + setter: (value) => disabled = value == 'true' || value == '', deleter: () => disabled = false ); attributes['readonly'] = ElementAttributeProperty( getter: () => readonly.toString(), - setter: (value) => this.readonly = value == 'true' || value == '', + setter: (value) => readonly = value == 'true' || value == '', deleter: () => readonly = false ); attributes['maxlength'] = ElementAttributeProperty( getter: () => maxlength?.toString(), - setter: (value) => this.maxlength = value, + setter: (value) => maxlength = value, deleter: () => maxlength = null ); attributes['minlength'] = ElementAttributeProperty( getter: () => minlength?.toString(), - setter: (value) => this.minlength = value, + setter: (value) => minlength = value, deleter: () => minlength = null ); attributes['pattern'] = ElementAttributeProperty( getter: () => pattern?.toString(), - setter: (value) => this.pattern = value, + setter: (value) => pattern = value, deleter: () => pattern = null ); attributes['required'] = ElementAttributeProperty( getter: () => required.toString(), - setter: (value) => this.required = value == 'true' || value == '', + setter: (value) => required = value == 'true' || value == '', deleter: () => required = false ); attributes['autofocus'] = ElementAttributeProperty( getter: () => autofocus.toString(), - setter: (value) => this.autofocus = value == 'true' || value == '', + setter: (value) => autofocus = value == 'true' || value == '', deleter: () => autofocus = false ); attributes['textalign'] = ElementAttributeProperty( getter: () => textalign?.toString(), - setter: (value) => this.textalign = value, + setter: (value) => textalign = value, deleter: () => textalign = null ); attributes['autocapitalize'] = ElementAttributeProperty( getter: () => autocapitalize?.toString(), - setter: (value) => this.autocapitalize = value, + setter: (value) => autocapitalize = value, deleter: () => autocapitalize = null ); attributes['autocorrect'] = ElementAttributeProperty( getter: () => autocorrect.toString(), - setter: (value) => this.autocorrect = value == 'true' || value == '', - deleter: () => autocorrect = true + setter: (value) => autocorrect = value == 'true' || value == '', + deleter: () => autocorrect = false ); attributes['enablesuggestions'] = ElementAttributeProperty( getter: () => enablesuggestions.toString(), - setter: (value) => this.enablesuggestions = value == 'true' || value == '', - deleter: () => enablesuggestions = true + setter: (value) => enablesuggestions = value == 'true' || value == '', + deleter: () => enablesuggestions = false ); attributes['enterkeyhint'] = ElementAttributeProperty( getter: () => enterkeyhint?.toString(), - setter: (value) => this.enterkeyhint = value, + setter: (value) => enterkeyhint = value, deleter: () => enterkeyhint = null ); attributes['maxlines'] = ElementAttributeProperty( getter: () => maxlines?.toString(), - setter: (value) => this.maxlines = value, + setter: (value) => maxlines = value, deleter: () => maxlines = null ); attributes['minlines'] = ElementAttributeProperty( getter: () => minlines?.toString(), - setter: (value) => this.minlines = value, + setter: (value) => minlines = value, deleter: () => minlines = null ); attributes['cursorcolor'] = ElementAttributeProperty( getter: () => cursorcolor?.toString(), - setter: (value) => this.cursorcolor = value, + setter: (value) => cursorcolor = value, deleter: () => cursorcolor = null ); attributes['selectioncolor'] = ElementAttributeProperty( getter: () => selectioncolor?.toString(), - setter: (value) => this.selectioncolor = value, + setter: (value) => selectioncolor = value, deleter: () => selectioncolor = null ); attributes['obscuringcharacter'] = ElementAttributeProperty( getter: () => obscuringcharacter?.toString(), - setter: (value) => this.obscuringcharacter = value, + setter: (value) => obscuringcharacter = value, deleter: () => obscuringcharacter = null ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/popover_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/popover_bindings_generated.dart index 1060bb8ecc..c7517f1f0f 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/popover_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/popover_bindings_generated.dart @@ -21,17 +21,17 @@ abstract class FlutterShadcnPopoverBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['open'] = ElementAttributeProperty( getter: () => open.toString(), - setter: (value) => this.open = value == 'true' || value == '', + setter: (value) => open = value == 'true' || value == '', deleter: () => open = false ); attributes['placement'] = ElementAttributeProperty( getter: () => placement?.toString(), - setter: (value) => this.placement = value, + setter: (value) => placement = value, deleter: () => placement = null ); attributes['close-on-outside-click'] = ElementAttributeProperty( getter: () => closeOnOutsideClick.toString(), - setter: (value) => this.closeOnOutsideClick = value == 'true' || value == '', + setter: (value) => closeOnOutsideClick = value == 'true' || value == '', deleter: () => closeOnOutsideClick = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/progress_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/progress_bindings_generated.dart index 403eb8ac0e..a2097a0316 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/progress_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/progress_bindings_generated.dart @@ -21,17 +21,17 @@ abstract class FlutterShadcnProgressBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['value'] = ElementAttributeProperty( getter: () => value?.toString(), - setter: (value) => this.value = value, + setter: (value) => value = value, deleter: () => value = null ); attributes['max'] = ElementAttributeProperty( getter: () => max?.toString(), - setter: (value) => this.max = value, + setter: (value) => max = value, deleter: () => max = null ); attributes['variant'] = ElementAttributeProperty( getter: () => variant?.toString(), - setter: (value) => this.variant = value, + setter: (value) => variant = value, deleter: () => variant = null ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/radio_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/radio_bindings_generated.dart index 64ba73ceff..8abce3eedd 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/radio_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/radio_bindings_generated.dart @@ -21,17 +21,17 @@ abstract class FlutterShadcnRadioBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['value'] = ElementAttributeProperty( getter: () => value?.toString(), - setter: (value) => this.value = value, + setter: (value) => value = value, deleter: () => value = null ); attributes['disabled'] = ElementAttributeProperty( getter: () => disabled.toString(), - setter: (value) => this.disabled = value == 'true' || value == '', + setter: (value) => disabled = value == 'true' || value == '', deleter: () => disabled = false ); attributes['orientation'] = ElementAttributeProperty( getter: () => orientation?.toString(), - setter: (value) => this.orientation = value, + setter: (value) => orientation = value, deleter: () => orientation = null ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/scroll_area_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/scroll_area_bindings_generated.dart index a51bc97c4e..f035ccfd8a 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/scroll_area_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/scroll_area_bindings_generated.dart @@ -17,7 +17,7 @@ abstract class FlutterShadcnScrollAreaBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['orientation'] = ElementAttributeProperty( getter: () => orientation?.toString(), - setter: (value) => this.orientation = value, + setter: (value) => orientation = value, deleter: () => orientation = null ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/select_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/select_bindings_generated.dart index 5cfb73dfa1..33c1e7d70e 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/select_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/select_bindings_generated.dart @@ -31,42 +31,42 @@ abstract class FlutterShadcnSelectBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['value'] = ElementAttributeProperty( getter: () => value?.toString(), - setter: (value) => this.value = value, + setter: (value) => value = value, deleter: () => value = null ); attributes['placeholder'] = ElementAttributeProperty( getter: () => placeholder?.toString(), - setter: (value) => this.placeholder = value, + setter: (value) => placeholder = value, deleter: () => placeholder = null ); attributes['disabled'] = ElementAttributeProperty( getter: () => disabled.toString(), - setter: (value) => this.disabled = value == 'true' || value == '', + setter: (value) => disabled = value == 'true' || value == '', deleter: () => disabled = false ); attributes['multiple'] = ElementAttributeProperty( getter: () => multiple.toString(), - setter: (value) => this.multiple = value == 'true' || value == '', + setter: (value) => multiple = value == 'true' || value == '', deleter: () => multiple = false ); attributes['searchable'] = ElementAttributeProperty( getter: () => searchable.toString(), - setter: (value) => this.searchable = value == 'true' || value == '', + setter: (value) => searchable = value == 'true' || value == '', deleter: () => searchable = false ); attributes['search-placeholder'] = ElementAttributeProperty( getter: () => searchPlaceholder?.toString(), - setter: (value) => this.searchPlaceholder = value, + setter: (value) => searchPlaceholder = value, deleter: () => searchPlaceholder = null ); attributes['allow-deselection'] = ElementAttributeProperty( getter: () => allowDeselection.toString(), - setter: (value) => this.allowDeselection = value == 'true' || value == '', + setter: (value) => allowDeselection = value == 'true' || value == '', deleter: () => allowDeselection = false ); attributes['close-on-select'] = ElementAttributeProperty( getter: () => closeOnSelect.toString(), - setter: (value) => this.closeOnSelect = value == 'true' || value == '', + setter: (value) => closeOnSelect = value == 'true' || value == '', deleter: () => closeOnSelect = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/separator_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/separator_bindings_generated.dart index 1a96ad0b35..013cc913c8 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/separator_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/separator_bindings_generated.dart @@ -17,7 +17,7 @@ abstract class FlutterShadcnSeparatorBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['orientation'] = ElementAttributeProperty( getter: () => orientation?.toString(), - setter: (value) => this.orientation = value, + setter: (value) => orientation = value, deleter: () => orientation = null ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/sheet_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/sheet_bindings_generated.dart index 231618aeec..58a1acb87b 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/sheet_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/sheet_bindings_generated.dart @@ -21,17 +21,17 @@ abstract class FlutterShadcnSheetBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['open'] = ElementAttributeProperty( getter: () => open.toString(), - setter: (value) => this.open = value == 'true' || value == '', + setter: (value) => open = value == 'true' || value == '', deleter: () => open = false ); attributes['side'] = ElementAttributeProperty( getter: () => side?.toString(), - setter: (value) => this.side = value, + setter: (value) => side = value, deleter: () => side = null ); attributes['close-on-outside-click'] = ElementAttributeProperty( getter: () => closeOnOutsideClick.toString(), - setter: (value) => this.closeOnOutsideClick = value == 'true' || value == '', + setter: (value) => closeOnOutsideClick = value == 'true' || value == '', deleter: () => closeOnOutsideClick = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/skeleton_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/skeleton_bindings_generated.dart index 5faa5e6fc5..885bb9f176 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/skeleton_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/skeleton_bindings_generated.dart @@ -21,17 +21,17 @@ abstract class FlutterShadcnSkeletonBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['width'] = ElementAttributeProperty( getter: () => width?.toString(), - setter: (value) => this.width = value, + setter: (value) => width = value, deleter: () => width = null ); attributes['height'] = ElementAttributeProperty( getter: () => height?.toString(), - setter: (value) => this.height = value, + setter: (value) => height = value, deleter: () => height = null ); attributes['circle'] = ElementAttributeProperty( getter: () => circle.toString(), - setter: (value) => this.circle = value == 'true' || value == '', + setter: (value) => circle = value == 'true' || value == '', deleter: () => circle = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/slider_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/slider_bindings_generated.dart index 7e558ef89e..895f7bfca3 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/slider_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/slider_bindings_generated.dart @@ -27,32 +27,32 @@ abstract class FlutterShadcnSliderBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['value'] = ElementAttributeProperty( getter: () => value?.toString(), - setter: (value) => this.value = value, + setter: (value) => value = value, deleter: () => value = null ); attributes['min'] = ElementAttributeProperty( getter: () => min?.toString(), - setter: (value) => this.min = value, + setter: (value) => min = value, deleter: () => min = null ); attributes['max'] = ElementAttributeProperty( getter: () => max?.toString(), - setter: (value) => this.max = value, + setter: (value) => max = value, deleter: () => max = null ); attributes['step'] = ElementAttributeProperty( getter: () => step?.toString(), - setter: (value) => this.step = value, + setter: (value) => step = value, deleter: () => step = null ); attributes['disabled'] = ElementAttributeProperty( getter: () => disabled.toString(), - setter: (value) => this.disabled = value == 'true' || value == '', + setter: (value) => disabled = value == 'true' || value == '', deleter: () => disabled = false ); attributes['orientation'] = ElementAttributeProperty( getter: () => orientation?.toString(), - setter: (value) => this.orientation = value, + setter: (value) => orientation = value, deleter: () => orientation = null ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/switch_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/switch_bindings_generated.dart index b168635a01..6dbd9143bd 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/switch_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/switch_bindings_generated.dart @@ -19,12 +19,12 @@ abstract class FlutterShadcnSwitchBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['checked'] = ElementAttributeProperty( getter: () => checked.toString(), - setter: (value) => this.checked = value == 'true' || value == '', + setter: (value) => checked = value == 'true' || value == '', deleter: () => checked = false ); attributes['disabled'] = ElementAttributeProperty( getter: () => disabled.toString(), - setter: (value) => this.disabled = value == 'true' || value == '', + setter: (value) => disabled = value == 'true' || value == '', deleter: () => disabled = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/tabs_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/tabs_bindings_generated.dart index 2239d012e0..65f0d6c82f 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/tabs_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/tabs_bindings_generated.dart @@ -19,12 +19,12 @@ abstract class FlutterShadcnTabsBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['value'] = ElementAttributeProperty( getter: () => value?.toString(), - setter: (value) => this.value = value, + setter: (value) => value = value, deleter: () => value = null ); attributes['default-value'] = ElementAttributeProperty( getter: () => defaultValue?.toString(), - setter: (value) => this.defaultValue = value, + setter: (value) => defaultValue = value, deleter: () => defaultValue = null ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/textarea_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/textarea_bindings_generated.dart index 48af42d1a6..2575203941 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/textarea_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/textarea_bindings_generated.dart @@ -31,42 +31,42 @@ abstract class FlutterShadcnTextareaBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['value'] = ElementAttributeProperty( getter: () => value?.toString(), - setter: (value) => this.value = value, + setter: (value) => value = value, deleter: () => value = null ); attributes['placeholder'] = ElementAttributeProperty( getter: () => placeholder?.toString(), - setter: (value) => this.placeholder = value, + setter: (value) => placeholder = value, deleter: () => placeholder = null ); attributes['rows'] = ElementAttributeProperty( getter: () => rows?.toString(), - setter: (value) => this.rows = value, + setter: (value) => rows = value, deleter: () => rows = null ); attributes['disabled'] = ElementAttributeProperty( getter: () => disabled.toString(), - setter: (value) => this.disabled = value == 'true' || value == '', + setter: (value) => disabled = value == 'true' || value == '', deleter: () => disabled = false ); attributes['readonly'] = ElementAttributeProperty( getter: () => readonly.toString(), - setter: (value) => this.readonly = value == 'true' || value == '', + setter: (value) => readonly = value == 'true' || value == '', deleter: () => readonly = false ); attributes['maxlength'] = ElementAttributeProperty( getter: () => maxlength?.toString(), - setter: (value) => this.maxlength = value, + setter: (value) => maxlength = value, deleter: () => maxlength = null ); attributes['required'] = ElementAttributeProperty( getter: () => required.toString(), - setter: (value) => this.required = value == 'true' || value == '', + setter: (value) => required = value == 'true' || value == '', deleter: () => required = false ); attributes['autofocus'] = ElementAttributeProperty( getter: () => autofocus.toString(), - setter: (value) => this.autofocus = value == 'true' || value == '', + setter: (value) => autofocus = value == 'true' || value == '', deleter: () => autofocus = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/time_picker_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/time_picker_bindings_generated.dart index 81aaeb0a35..b8cdd80dbc 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/time_picker_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/time_picker_bindings_generated.dart @@ -23,22 +23,22 @@ abstract class FlutterShadcnTimePickerBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['value'] = ElementAttributeProperty( getter: () => value?.toString(), - setter: (value) => this.value = value, + setter: (value) => value = value, deleter: () => value = null ); attributes['placeholder'] = ElementAttributeProperty( getter: () => placeholder?.toString(), - setter: (value) => this.placeholder = value, + setter: (value) => placeholder = value, deleter: () => placeholder = null ); attributes['disabled'] = ElementAttributeProperty( getter: () => disabled.toString(), - setter: (value) => this.disabled = value == 'true' || value == '', + setter: (value) => disabled = value == 'true' || value == '', deleter: () => disabled = false ); attributes['use-24-hour'] = ElementAttributeProperty( getter: () => use24Hour.toString(), - setter: (value) => this.use24Hour = value == 'true' || value == '', + setter: (value) => use24Hour = value == 'true' || value == '', deleter: () => use24Hour = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/toast_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/toast_bindings_generated.dart index ff3e321b6c..2bd02a881f 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/toast_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/toast_bindings_generated.dart @@ -25,27 +25,27 @@ abstract class FlutterShadcnToastBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['variant'] = ElementAttributeProperty( getter: () => variant?.toString(), - setter: (value) => this.variant = value, + setter: (value) => variant = value, deleter: () => variant = null ); attributes['title'] = ElementAttributeProperty( getter: () => title?.toString(), - setter: (value) => this.title = value, + setter: (value) => title = value, deleter: () => title = null ); attributes['description'] = ElementAttributeProperty( getter: () => description?.toString(), - setter: (value) => this.description = value, + setter: (value) => description = value, deleter: () => description = null ); attributes['duration'] = ElementAttributeProperty( getter: () => duration?.toString(), - setter: (value) => this.duration = value, + setter: (value) => duration = value, deleter: () => duration = null ); attributes['closable'] = ElementAttributeProperty( getter: () => closable.toString(), - setter: (value) => this.closable = value == 'true' || value == '', + setter: (value) => closable = value == 'true' || value == '', deleter: () => closable = false ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/tooltip_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/tooltip_bindings_generated.dart index 5894caaeae..8eccd442e6 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/tooltip_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/tooltip_bindings_generated.dart @@ -23,22 +23,22 @@ abstract class FlutterShadcnTooltipBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['content'] = ElementAttributeProperty( getter: () => content?.toString(), - setter: (value) => this.content = value, + setter: (value) => content = value, deleter: () => content = null ); attributes['show-delay'] = ElementAttributeProperty( getter: () => showDelay?.toString(), - setter: (value) => this.showDelay = value, + setter: (value) => showDelay = value, deleter: () => showDelay = null ); attributes['hide-delay'] = ElementAttributeProperty( getter: () => hideDelay?.toString(), - setter: (value) => this.hideDelay = value, + setter: (value) => hideDelay = value, deleter: () => hideDelay = null ); attributes['placement'] = ElementAttributeProperty( getter: () => placement?.toString(), - setter: (value) => this.placement = value, + setter: (value) => placement = value, deleter: () => placement = null ); } diff --git a/native_uis/webf_shadcn_ui/lib/src/theme/theme_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/theme/theme_bindings_generated.dart index f9e7dcba7b..0f7407a6c2 100644 --- a/native_uis/webf_shadcn_ui/lib/src/theme/theme_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/theme/theme_bindings_generated.dart @@ -21,17 +21,17 @@ abstract class FlutterShadcnThemeBindings extends WidgetElement { super.initializeAttributes(attributes); attributes['color-scheme'] = ElementAttributeProperty( getter: () => colorScheme?.toString(), - setter: (value) => this.colorScheme = value, + setter: (value) => colorScheme = value, deleter: () => colorScheme = null ); attributes['brightness'] = ElementAttributeProperty( getter: () => brightness?.toString(), - setter: (value) => this.brightness = value, + setter: (value) => brightness = value, deleter: () => brightness = null ); attributes['radius'] = ElementAttributeProperty( getter: () => radius?.toString(), - setter: (value) => this.radius = value, + setter: (value) => radius = value, deleter: () => radius = null ); } diff --git a/packages/react-shadcn-ui/.gitignore b/packages/react-shadcn-ui/.gitignore new file mode 100644 index 0000000000..8c63a6a0a9 --- /dev/null +++ b/packages/react-shadcn-ui/.gitignore @@ -0,0 +1,9 @@ +node_modules/ +lib/ +dist/ +.DS_Store +*.log +*.tsbuildinfo +.env +.env.local +.env.*.local \ No newline at end of file diff --git a/packages/react-shadcn-ui/README.md b/packages/react-shadcn-ui/README.md new file mode 100644 index 0000000000..68b77673ce --- /dev/null +++ b/packages/react-shadcn-ui/README.md @@ -0,0 +1,206 @@ +# webf_shadcn_ui + +shadcn/ui style components for WebF applications. This package provides [shadcn_ui](https://pub.dev/packages/shadcn_ui) Flutter widgets wrapped as HTML custom elements for use in WebF. + +## Features + +- 40+ UI components matching shadcn/ui design +- Full theming support with 12 color schemes +- Light/Dark mode support +- TypeScript definitions for React/Vue code generation +- Seamless integration with WebF applications + +## Installation + +Add this to your package's `pubspec.yaml` file: + +```yaml +dependencies: + webf_shadcn_ui: ^0.1.0 +``` + +## Quick Start + +1. Install the custom elements in your main function: + +```dart +import 'package:webf_shadcn_ui/webf_shadcn_ui.dart'; + +void main() { + installWebFShadcnUI(); + runApp(MyApp()); +} +``` + +2. Use the components in your HTML: + +```html + + + + Welcome + Get started with shadcn components + + + Click me + + + +``` + +## Available Components + +### Theming +- `` - Theme provider with color scheme and brightness support + +### Form Controls +- `` - Button with variants (default, secondary, destructive, outline, ghost, link) +- `` - Text input field +- `` - Multi-line text input +- `` - Checkbox control +- `` - Radio button group +- `` - Toggle switch +- `` - Dropdown select +- `` - Range slider +- `` - Searchable select +- `` - Form container with field components + +### Display Components +- `` - Card container with header, content, footer slots +- `` - Alert message with title and description +- `` - Badge/label component +- `` - User avatar with fallback +- `` - Toast notification +- `` - Hover tooltip +- `` - Progress bar +- `` - Visual separator + +### Navigation/Layout +- `` - Tabbed interface +- `` - Modal dialog +- `` - Slide-out panel +- `` - Floating content container +- `` - Breadcrumb navigation +- `` - Dropdown menu +- `` - Right-click context menu + +### Data Display +- `` - Data table +- `` - Collapsible sections +- `` - Date calendar +- `` - Date picker with popover +- `` - Time picker +- `` - Image with loading states + +### Advanced +- `` - Scrollable container +- `` - Loading placeholder +- `` - Collapsible section + +## CSS Customization + +### Button Text Styling + +The `` component supports CSS customization for text styles while automatically handling the text color based on the button variant and theme. + +**What CSS properties work on button children:** +- `font-size` / `font-weight` / `font-family` +- `letter-spacing` / `word-spacing` +- `text-decoration` / `font-style` +- Other text styling properties + +**What's controlled by the button theme:** +- Text color (automatically set based on button variant) + +**Example with inline styles:** + +```html + + Large Bold Text + +``` + +**Example with React and Tailwind:** + +```jsx + + Styled Text + +``` + +This approach ensures: +- CSS styles from your HTML/JSX are preserved (font-size, font-weight, etc.) +- Text color automatically matches the button's theme variant (primary has white text, outline has dark text, etc.) + +### Button Gradient and Shadow + +The button supports CSS `background-image` for gradients and `box-shadow` for shadows, which are passed directly to the underlying Flutter `ShadButton`. + +**Gradient example:** + +```html + + Gradient Button + +``` + +**Shadow example:** + +```html + + Shadow Button + +``` + +**Combined gradient and shadow:** + +```jsx + + Gradient + Shadow + +``` + +**Supported gradient syntax:** +- `linear-gradient(direction, color1, color2, ...)` +- `linear-gradient(angle, color1 stop1, color2 stop2, ...)` + +**Supported shadow syntax:** +- `box-shadow: offset-x offset-y blur-radius spread-radius color` + +## Color Schemes + +The following color schemes are available: +- blue, gray, green, neutral, orange, red, rose, slate, stone, violet, yellow, zinc + +Set the color scheme on the theme provider: + +```html + + + +``` + +## Generating React/Vue Components + +Use the WebF CLI to generate framework-specific packages: + +```bash +# React components +webf codegen webf-shadcn-react \ + --flutter-package-src=./native_uis/webf_shadcn_ui \ + --framework=react + +# Vue components +webf codegen webf-shadcn-vue \ + --flutter-package-src=./native_uis/webf_shadcn_ui \ + --framework=vue +``` + +## License + +Apache License 2.0 diff --git a/packages/react-shadcn-ui/global.d.ts b/packages/react-shadcn-ui/global.d.ts new file mode 100644 index 0000000000..bf744d7620 --- /dev/null +++ b/packages/react-shadcn-ui/global.d.ts @@ -0,0 +1,2 @@ +type double = number; +type int = number; diff --git a/packages/react-shadcn-ui/package-lock.json b/packages/react-shadcn-ui/package-lock.json new file mode 100644 index 0000000000..c7039fe872 --- /dev/null +++ b/packages/react-shadcn-ui/package-lock.json @@ -0,0 +1,1244 @@ +{ + "name": "@openwebf/react-shadcn-ui", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@openwebf/react-shadcn-ui", + "version": "0.1.0", + "license": "ISC", + "devDependencies": { + "@openwebf/react-core-ui": "^0.24.1", + "@types/react": "^19.1.0", + "@types/react-dom": "^19.1.2", + "picomatch": "^4.0.2", + "tsdown": "^0.19.0", + "typescript": "^5.8.3" + }, + "peerDependencies": { + "@openwebf/react-core-ui": "^0.24.1", + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", + "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@openwebf/react-core-ui": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@openwebf/react-core-ui/-/react-core-ui-0.24.2.tgz", + "integrity": "sha512-CBh1Gg/Fb5FfpNg/zYp3eRmL2gR/CwwoUKrGn+WSCFA76IQdWrghubTlVIOeCyu22WcBUYVF/QSA0NyqKu9Pkg==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.107.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.107.0.tgz", + "integrity": "sha512-QFDRbYfV2LVx8tyqtyiah3jQPUj1mK2+RYwxyFWyGoys6XJnwTdlzO6rdNNHOPorHAu5Uo34oWRKcvNpbJarmQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@quansync/fs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@quansync/fs/-/fs-1.0.0.tgz", + "integrity": "sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-beta.59", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.59.tgz", + "integrity": "sha512-6yLLgyswYwiCfls9+hoNFY9F8TQdwo15hpXDHzlAR0X/GojeKF+AuNcXjYNbOJ4zjl/5D6lliE8CbpB5t1OWIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-beta.59", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-beta.59.tgz", + "integrity": "sha512-hqGXRc162qCCIOAcHN2Cw4eXiVTwYsMFLOhAy1IG2CxY+dwc/l4Ga+dLPkLor3Ikqy5WDn+7kxHbbh6EmshEpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-beta.59", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.59.tgz", + "integrity": "sha512-ezvvGuhteE15JmMhJW0wS7BaXmhwLy1YHeEwievYaPC1PgGD86wgBKfOpHr9tSKllAXbCe0BeeMvasscWLhKdA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-beta.59", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.59.tgz", + "integrity": "sha512-4fhKVJiEYVd5n6no/mrL3LZ9kByfCGwmONOrdtvx8DJGDQhehH/q3RfhG3V/4jGKhpXgbDjpIjkkFdybCTcgew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-beta.59", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.59.tgz", + "integrity": "sha512-T3Y52sW6JAhvIqArBw+wtjNU1Ieaz4g0NBxyjSJoW971nZJBZygNlSYx78G4cwkCmo1dYTciTPDOnQygLV23pA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-beta.59", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.59.tgz", + "integrity": "sha512-NIW40jQDSQap2KDdmm9z3B/4OzWJ6trf8dwx3FD74kcQb3v34ThsBFTtzE5KjDuxnxgUlV+DkAu+XgSMKrgufw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-beta.59", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.59.tgz", + "integrity": "sha512-CCKEk+H+8c0WGe/8n1E20n85Tq4Pv+HNAbjP1KfUXW+01aCWSMjU56ChNrM2tvHnXicfm7QRNoZyfY8cWh7jLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-beta.59", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.59.tgz", + "integrity": "sha512-VlfwJ/HCskPmQi8R0JuAFndySKVFX7yPhE658o27cjSDWWbXVtGkSbwaxstii7Q+3Rz87ZXN+HLnb1kd4R9Img==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-beta.59", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.59.tgz", + "integrity": "sha512-kuO92hTRyGy0Ts3Nsqll0rfO8eFsEJe9dGQGktkQnZ2hrJrDVN0y419dMgKy/gB2S2o7F2dpWhpfQOBehZPwVA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-beta.59", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-beta.59.tgz", + "integrity": "sha512-PXAebvNL4sYfCqi8LdY4qyFRacrRoiPZLo3NoUmiTxm7MPtYYR8CNtBGNokqDmMuZIQIecRaD/jbmFAIDz7DxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-beta.59", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.59.tgz", + "integrity": "sha512-yJoklQg7XIZq8nAg0bbkEXcDK6sfpjxQGxpg2Nd6ERNtvg+eOaEBRgPww0BVTrYFQzje1pB5qPwC2VnJHT3koQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-beta.59", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.59.tgz", + "integrity": "sha512-ljZ4+McmCbIuZwEBaoGtiG8Rq2nJjaXEnLEIx+usWetXn1ECjXY0LAhkELxOV6ytv4ensEmoJJ8nXg47hRMjlw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-beta.59", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.59.tgz", + "integrity": "sha512-bMY4tTIwbdZljW+xe/ln1hvs0SRitahQSXfWtvgAtIzgSX9Ar7KqJzU7lRm33YTRFIHLULRi53yNjw9nJGd6uQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.59", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.59.tgz", + "integrity": "sha512-aoh6LAJRyhtazs98ydgpNOYstxUlsOV1KJXcpf/0c0vFcUA8uyd/hwKRhqE/AAPNqAho9RliGsvitCoOzREoVA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/ansis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/ast-kit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-2.2.0.tgz", + "integrity": "sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "pathe": "^2.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/birpc": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-4.0.0.tgz", + "integrity": "sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/dts-resolver": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/dts-resolver/-/dts-resolver-2.1.3.tgz", + "integrity": "sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "oxc-resolver": ">=11.0.0" + }, + "peerDependenciesMeta": { + "oxc-resolver": { + "optional": true + } + } + }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/hookable": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-6.0.1.tgz", + "integrity": "sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-without-cache": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/import-without-cache/-/import-without-cache-0.2.5.tgz", + "integrity": "sha512-B6Lc2s6yApwnD2/pMzFh/d5AVjdsDXjgkeJ766FmFuJELIGHNycKRj+l3A39yZPM4CchqNCB4RITEAYB1KUM6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/quansync": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-1.0.0.tgz", + "integrity": "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-beta.59", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.59.tgz", + "integrity": "sha512-Slm000Gd8/AO9z4Kxl4r8mp/iakrbAuJ1L+7ddpkNxgQ+Vf37WPvY63l3oeyZcfuPD1DRrUYBsRPIXSOhvOsmw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@oxc-project/types": "=0.107.0", + "@rolldown/pluginutils": "1.0.0-beta.59" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-beta.59", + "@rolldown/binding-darwin-arm64": "1.0.0-beta.59", + "@rolldown/binding-darwin-x64": "1.0.0-beta.59", + "@rolldown/binding-freebsd-x64": "1.0.0-beta.59", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.59", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.59", + "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.59", + "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.59", + "@rolldown/binding-linux-x64-musl": "1.0.0-beta.59", + "@rolldown/binding-openharmony-arm64": "1.0.0-beta.59", + "@rolldown/binding-wasm32-wasi": "1.0.0-beta.59", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.59", + "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.59" + } + }, + "node_modules/rolldown-plugin-dts": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/rolldown-plugin-dts/-/rolldown-plugin-dts-0.20.0.tgz", + "integrity": "sha512-cLAY1kN2ilTYMfZcFlGWbXnu6Nb+8uwUBsi+Mjbh4uIx7IN8uMOmJ7RxrrRgPsO4H7eSz3E+JwGoL1gyugiyUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.28.5", + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "ast-kit": "^2.2.0", + "birpc": "^4.0.0", + "dts-resolver": "^2.1.3", + "get-tsconfig": "^4.13.0", + "obug": "^2.1.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@ts-macro/tsc": "^0.3.6", + "@typescript/native-preview": ">=7.0.0-dev.20250601.1", + "rolldown": "^1.0.0-beta.57", + "typescript": "^5.0.0", + "vue-tsc": "~3.2.0" + }, + "peerDependenciesMeta": { + "@ts-macro/tsc": { + "optional": true + }, + "@typescript/native-preview": { + "optional": true + }, + "typescript": { + "optional": true + }, + "vue-tsc": { + "optional": true + } + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tsdown": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/tsdown/-/tsdown-0.19.0.tgz", + "integrity": "sha512-uqg8yzlS7GemFWcM6aCp/sptF4bJiJbWUibuHTRLLCBEsGCgJxuqxPhuVTqyHXqoEkh9ohwAdlyDKli5MEWCyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansis": "^4.2.0", + "cac": "^6.7.14", + "defu": "^6.1.4", + "empathic": "^2.0.0", + "hookable": "^6.0.1", + "import-without-cache": "^0.2.5", + "obug": "^2.1.1", + "picomatch": "^4.0.3", + "rolldown": "1.0.0-beta.59", + "rolldown-plugin-dts": "^0.20.0", + "semver": "^7.7.3", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tree-kill": "^1.2.2", + "unconfig-core": "^7.4.2", + "unrun": "^0.2.24" + }, + "bin": { + "tsdown": "dist/run.mjs" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@arethetypeswrong/core": "^0.18.1", + "@vitejs/devtools": "*", + "publint": "^0.3.0", + "typescript": "^5.0.0", + "unplugin-lightningcss": "^0.4.0", + "unplugin-unused": "^0.5.0" + }, + "peerDependenciesMeta": { + "@arethetypeswrong/core": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "publint": { + "optional": true + }, + "typescript": { + "optional": true + }, + "unplugin-lightningcss": { + "optional": true + }, + "unplugin-unused": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unconfig-core": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/unconfig-core/-/unconfig-core-7.4.2.tgz", + "integrity": "sha512-VgPCvLWugINbXvMQDf8Jh0mlbvNjNC6eSUziHsBCMpxR05OPrNrvDnyatdMjRgcHaaNsCqz+wjNXxNw1kRLHUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@quansync/fs": "^1.0.0", + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/unrun": { + "version": "0.2.27", + "resolved": "https://registry.npmjs.org/unrun/-/unrun-0.2.27.tgz", + "integrity": "sha512-Mmur1UJpIbfxasLOhPRvox/QS4xBiDii71hMP7smfRthGcwFL2OAmYRgduLANOAU4LUkvVamuP+02U+c90jlrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "rolldown": "1.0.0-rc.3" + }, + "bin": { + "unrun": "dist/cli.mjs" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/Gugustinette" + }, + "peerDependencies": { + "synckit": "^0.11.11" + }, + "peerDependenciesMeta": { + "synckit": { + "optional": true + } + } + }, + "node_modules/unrun/node_modules/@oxc-project/types": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.112.0.tgz", + "integrity": "sha512-m6RebKHIRsax2iCwVpYW2ErQwa4ywHJrE4sCK3/8JK8ZZAWOKXaRJFl/uP51gaVyyXlaS4+chU1nSCdzYf6QqQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/unrun/node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.3.tgz", + "integrity": "sha512-0T1k9FinuBZ/t7rZ8jN6OpUKPnUjNdYHoj/cESWrQ3ZraAJ4OMm6z7QjSfCxqj8mOp9kTKc1zHK3kGz5vMu+nQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/unrun/node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.3.tgz", + "integrity": "sha512-JWWLzvcmc/3pe7qdJqPpuPk91SoE/N+f3PcWx/6ZwuyDVyungAEJPvKm/eEldiDdwTmaEzWfIR+HORxYWrCi1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/unrun/node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.3.tgz", + "integrity": "sha512-MTakBxfx3tde5WSmbHxuqlDsIW0EzQym+PJYGF4P6lG2NmKzi128OGynoFUqoD5ryCySEY85dug4v+LWGBElIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/unrun/node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.3.tgz", + "integrity": "sha512-jje3oopyOLs7IwfvXoS6Lxnmie5JJO7vW29fdGFu5YGY1EDbVDhD+P9vDihqS5X6fFiqL3ZQZCMBg6jyHkSVww==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/unrun/node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.3.tgz", + "integrity": "sha512-A0n8P3hdLAaqzSFrQoA42p23ZKBYQOw+8EH5r15Sa9X1kD9/JXe0YT2gph2QTWvdr0CVK2BOXiK6ENfy6DXOag==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/unrun/node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.3.tgz", + "integrity": "sha512-kWXkoxxarYISBJ4bLNf5vFkEbb4JvccOwxWDxuK9yee8lg5XA7OpvlTptfRuwEvYcOZf+7VS69Uenpmpyo5Bjw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/unrun/node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.3.tgz", + "integrity": "sha512-Z03/wrqau9Bicfgb3Dbs6SYTHliELk2PM2LpG2nFd+cGupTMF5kanLEcj2vuuJLLhptNyS61rtk7SOZ+lPsTUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/unrun/node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.3.tgz", + "integrity": "sha512-iSXXZsQp08CSilff/DCTFZHSVEpEwdicV3W8idHyrByrcsRDVh9sGC3sev6d8BygSGj3vt8GvUKBPCoyMA4tgQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/unrun/node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.3.tgz", + "integrity": "sha512-qaj+MFudtdCv9xZo9znFvkgoajLdc+vwf0Kz5N44g+LU5XMe+IsACgn3UG7uTRlCCvhMAGXm1XlpEA5bZBrOcw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/unrun/node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.3.tgz", + "integrity": "sha512-U662UnMETyjT65gFmG9ma+XziENrs7BBnENi/27swZPYagubfHRirXHG2oMl+pEax2WvO7Kb9gHZmMakpYqBHQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/unrun/node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.3.tgz", + "integrity": "sha512-gekrQ3Q2HiC1T5njGyuUJoGpK/l6B/TNXKed3fZXNf9YRTJn3L5MOZsFBn4bN2+UX+8+7hgdlTcEsexX988G4g==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/unrun/node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.3.tgz", + "integrity": "sha512-85y5JifyMgs8m5K2XzR/VDsapKbiFiohl7s5lEj7nmNGO0pkTXE7q6TQScei96BNAsoK7JC3pA7ukA8WRHVJpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/unrun/node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.3.tgz", + "integrity": "sha512-a4VUQZH7LxGbUJ3qJ/TzQG8HxdHvf+jOnqf7B7oFx1TEBm+j2KNL2zr5SQ7wHkNAcaPevF6gf9tQnVBnC4mD+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/unrun/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/unrun/node_modules/rolldown": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.3.tgz", + "integrity": "sha512-Po/YZECDOqVXjIXrtC5h++a5NLvKAQNrd9ggrIG3sbDfGO5BqTUsrI6l8zdniKRp3r5Tp/2JTrXqx4GIguFCMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.112.0", + "@rolldown/pluginutils": "1.0.0-rc.3" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.3", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.3", + "@rolldown/binding-darwin-x64": "1.0.0-rc.3", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.3", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.3", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.3", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.3", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.3", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.3", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.3", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.3", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.3" + } + } + } +} diff --git a/packages/react-shadcn-ui/package.json b/packages/react-shadcn-ui/package.json new file mode 100644 index 0000000000..53207df769 --- /dev/null +++ b/packages/react-shadcn-ui/package.json @@ -0,0 +1,41 @@ +{ + "name": "@openwebf/react-shadcn-ui", + "version": "0.1.0", + "description": "shadcn/ui style components for WebF applications. This package provides shadcn_ui Flutter widgets wrapped as HTML custom elements for use in WebF.", + "main": "dist/index.cjs", + "module": "dist/index.mjs", + "types": "dist/index.d.mts", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + } + }, + "files": ["dist", "README.md"], + "scripts": { + "build": "tsdown" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0", + "@openwebf/react-core-ui": "^0.24.1" + }, + "devDependencies": { + "@openwebf/react-core-ui": "^0.24.1", + "@types/react": "^19.1.0", + "@types/react-dom": "^19.1.2", + "picomatch": "^4.0.2", + "tsdown": "^0.19.0", + "typescript": "^5.8.3" + } +} diff --git a/packages/react-shadcn-ui/src/index.ts b/packages/react-shadcn-ui/src/index.ts new file mode 100644 index 0000000000..6ee3877c17 --- /dev/null +++ b/packages/react-shadcn-ui/src/index.ts @@ -0,0 +1,78 @@ +/* + * Generated by TSDL, don't edit this file directly. + */ + + +export { FlutterShadcnTheme } from "./lib/src/theme/theme"; +export type { FlutterShadcnThemeElement } from "./lib/src/theme/theme"; +export { FlutterShadcnTooltip } from "./lib/src/components/tooltip"; +export type { FlutterShadcnTooltipElement } from "./lib/src/components/tooltip"; +export { FlutterShadcnToast } from "./lib/src/components/toast"; +export type { FlutterShadcnToastElement } from "./lib/src/components/toast"; +export { FlutterShadcnTimePicker } from "./lib/src/components/time_picker"; +export type { FlutterShadcnTimePickerElement } from "./lib/src/components/time_picker"; +export { FlutterShadcnTextarea } from "./lib/src/components/textarea"; +export type { FlutterShadcnTextareaElement } from "./lib/src/components/textarea"; +export { FlutterShadcnTabs, FlutterShadcnTabsList, FlutterShadcnTabsTrigger, FlutterShadcnTabsContent } from "./lib/src/components/tabs"; +export type { FlutterShadcnTabsElement, FlutterShadcnTabsListElement, FlutterShadcnTabsTriggerElement, FlutterShadcnTabsContentElement } from "./lib/src/components/tabs"; +export { FlutterShadcnTable, FlutterShadcnTableHeader, FlutterShadcnTableBody, FlutterShadcnTableRow, FlutterShadcnTableHead, FlutterShadcnTableCell } from "./lib/src/components/table"; +export type { FlutterShadcnTableElement, FlutterShadcnTableHeaderElement, FlutterShadcnTableBodyElement, FlutterShadcnTableRowElement, FlutterShadcnTableHeadElement, FlutterShadcnTableCellElement } from "./lib/src/components/table"; +export { FlutterShadcnSwitch } from "./lib/src/components/switch"; +export type { FlutterShadcnSwitchElement } from "./lib/src/components/switch"; +export { FlutterShadcnSlider } from "./lib/src/components/slider"; +export type { FlutterShadcnSliderElement } from "./lib/src/components/slider"; +export { FlutterShadcnSkeleton } from "./lib/src/components/skeleton"; +export type { FlutterShadcnSkeletonElement } from "./lib/src/components/skeleton"; +export { FlutterShadcnSheet, FlutterShadcnSheetHeader, FlutterShadcnSheetTitle, FlutterShadcnSheetDescription, FlutterShadcnSheetContent, FlutterShadcnSheetFooter } from "./lib/src/components/sheet"; +export type { FlutterShadcnSheetElement, FlutterShadcnSheetHeaderElement, FlutterShadcnSheetTitleElement, FlutterShadcnSheetDescriptionElement, FlutterShadcnSheetContentElement, FlutterShadcnSheetFooterElement } from "./lib/src/components/sheet"; +export { FlutterShadcnSeparator } from "./lib/src/components/separator"; +export type { FlutterShadcnSeparatorElement } from "./lib/src/components/separator"; +export { FlutterShadcnSelect, FlutterShadcnSelectTrigger, FlutterShadcnSelectContent, FlutterShadcnSelectItem, FlutterShadcnSelectGroup, FlutterShadcnSelectLabel, FlutterShadcnSelectSeparator } from "./lib/src/components/select"; +export type { FlutterShadcnSelectElement, FlutterShadcnSelectTriggerElement, FlutterShadcnSelectContentElement, FlutterShadcnSelectItemElement, FlutterShadcnSelectGroupElement, FlutterShadcnSelectLabelElement, FlutterShadcnSelectSeparatorElement } from "./lib/src/components/select"; +export { FlutterShadcnScrollArea } from "./lib/src/components/scroll_area"; +export type { FlutterShadcnScrollAreaElement } from "./lib/src/components/scroll_area"; +export { FlutterShadcnRadio, FlutterShadcnRadioItem } from "./lib/src/components/radio"; +export type { FlutterShadcnRadioElement, FlutterShadcnRadioItemElement } from "./lib/src/components/radio"; +export { FlutterShadcnProgress } from "./lib/src/components/progress"; +export type { FlutterShadcnProgressElement } from "./lib/src/components/progress"; +export { FlutterShadcnPopover, FlutterShadcnPopoverTrigger, FlutterShadcnPopoverContent } from "./lib/src/components/popover"; +export type { FlutterShadcnPopoverElement, FlutterShadcnPopoverTriggerElement, FlutterShadcnPopoverContentElement } from "./lib/src/components/popover"; +export { FlutterShadcnInput } from "./lib/src/components/input"; +export type { FlutterShadcnInputElement } from "./lib/src/components/input"; +export { FlutterShadcnImage } from "./lib/src/components/image"; +export type { FlutterShadcnImageElement } from "./lib/src/components/image"; +export { FlutterShadcnIconButton } from "./lib/src/components/icon_button"; +export type { FlutterShadcnIconButtonElement } from "./lib/src/components/icon_button"; +export { FlutterShadcnForm, FlutterShadcnFormField, FlutterShadcnFormLabel, FlutterShadcnFormDescription, FlutterShadcnFormMessage } from "./lib/src/components/form"; +export type { FlutterShadcnFormElement, FlutterShadcnFormFieldElement, FlutterShadcnFormLabelElement, FlutterShadcnFormDescriptionElement, FlutterShadcnFormMessageElement } from "./lib/src/components/form"; +export { FlutterShadcnDropdownMenu, FlutterShadcnDropdownMenuTrigger, FlutterShadcnDropdownMenuContent, FlutterShadcnDropdownMenuItem, FlutterShadcnDropdownMenuSeparator, FlutterShadcnDropdownMenuLabel } from "./lib/src/components/dropdown_menu"; +export type { FlutterShadcnDropdownMenuElement, FlutterShadcnDropdownMenuTriggerElement, FlutterShadcnDropdownMenuContentElement, FlutterShadcnDropdownMenuItemElement, FlutterShadcnDropdownMenuSeparatorElement, FlutterShadcnDropdownMenuLabelElement } from "./lib/src/components/dropdown_menu"; +export { FlutterShadcnDialog, FlutterShadcnDialogHeader, FlutterShadcnDialogTitle, FlutterShadcnDialogDescription, FlutterShadcnDialogContent, FlutterShadcnDialogFooter } from "./lib/src/components/dialog"; +export type { FlutterShadcnDialogElement, FlutterShadcnDialogHeaderElement, FlutterShadcnDialogTitleElement, FlutterShadcnDialogDescriptionElement, FlutterShadcnDialogContentElement, FlutterShadcnDialogFooterElement } from "./lib/src/components/dialog"; +export { FlutterShadcnDatePicker } from "./lib/src/components/date_picker"; +export type { FlutterShadcnDatePickerElement } from "./lib/src/components/date_picker"; +export { FlutterShadcnContextMenu, FlutterShadcnContextMenuTrigger, FlutterShadcnContextMenuContent, FlutterShadcnContextMenuItem, FlutterShadcnContextMenuSeparator, FlutterShadcnContextMenuLabel, FlutterShadcnContextMenuSub, FlutterShadcnContextMenuSubTrigger, FlutterShadcnContextMenuSubContent, FlutterShadcnContextMenuCheckboxItem, FlutterShadcnContextMenuRadioGroup, FlutterShadcnContextMenuRadioItem } from "./lib/src/components/context_menu"; +export type { FlutterShadcnContextMenuElement, FlutterShadcnContextMenuTriggerElement, FlutterShadcnContextMenuContentElement, FlutterShadcnContextMenuItemElement, FlutterShadcnContextMenuSeparatorElement, FlutterShadcnContextMenuLabelElement, FlutterShadcnContextMenuSubElement, FlutterShadcnContextMenuSubTriggerElement, FlutterShadcnContextMenuSubContentElement, FlutterShadcnContextMenuCheckboxItemElement, FlutterShadcnContextMenuRadioGroupElement, FlutterShadcnContextMenuRadioItemElement } from "./lib/src/components/context_menu"; +export { FlutterShadcnCombobox, FlutterShadcnComboboxItem } from "./lib/src/components/combobox"; +export type { FlutterShadcnComboboxElement, FlutterShadcnComboboxItemElement } from "./lib/src/components/combobox"; +export { FlutterShadcnCollapsible, FlutterShadcnCollapsibleTrigger, FlutterShadcnCollapsibleContent } from "./lib/src/components/collapsible"; +export type { FlutterShadcnCollapsibleElement, FlutterShadcnCollapsibleTriggerElement, FlutterShadcnCollapsibleContentElement } from "./lib/src/components/collapsible"; +export { FlutterShadcnCheckbox } from "./lib/src/components/checkbox"; +export type { FlutterShadcnCheckboxElement } from "./lib/src/components/checkbox"; +export { FlutterShadcnCard, FlutterShadcnCardHeader, FlutterShadcnCardTitle, FlutterShadcnCardDescription, FlutterShadcnCardContent, FlutterShadcnCardFooter } from "./lib/src/components/card"; +export type { FlutterShadcnCardElement, FlutterShadcnCardHeaderElement, FlutterShadcnCardTitleElement, FlutterShadcnCardDescriptionElement, FlutterShadcnCardContentElement, FlutterShadcnCardFooterElement } from "./lib/src/components/card"; +export { FlutterShadcnCalendar } from "./lib/src/components/calendar"; +export type { FlutterShadcnCalendarElement } from "./lib/src/components/calendar"; +export { FlutterShadcnButton } from "./lib/src/components/button"; +export type { FlutterShadcnButtonElement } from "./lib/src/components/button"; +export { FlutterShadcnBreadcrumb, FlutterShadcnBreadcrumbList, FlutterShadcnBreadcrumbItem, FlutterShadcnBreadcrumbLink, FlutterShadcnBreadcrumbPage, FlutterShadcnBreadcrumbSeparator, FlutterShadcnBreadcrumbEllipsis, FlutterShadcnBreadcrumbDropdown, FlutterShadcnBreadcrumbDropdownItem } from "./lib/src/components/breadcrumb"; +export type { FlutterShadcnBreadcrumbElement, FlutterShadcnBreadcrumbListElement, FlutterShadcnBreadcrumbItemElement, FlutterShadcnBreadcrumbLinkElement, FlutterShadcnBreadcrumbPageElement, FlutterShadcnBreadcrumbSeparatorElement, FlutterShadcnBreadcrumbEllipsisElement, FlutterShadcnBreadcrumbDropdownElement, FlutterShadcnBreadcrumbDropdownItemElement } from "./lib/src/components/breadcrumb"; +export { FlutterShadcnBadge } from "./lib/src/components/badge"; +export type { FlutterShadcnBadgeElement } from "./lib/src/components/badge"; +export { FlutterShadcnAvatar } from "./lib/src/components/avatar"; +export type { FlutterShadcnAvatarElement } from "./lib/src/components/avatar"; +export { FlutterShadcnAlert, FlutterShadcnAlertTitle, FlutterShadcnAlertDescription } from "./lib/src/components/alert"; +export type { FlutterShadcnAlertElement, FlutterShadcnAlertTitleElement, FlutterShadcnAlertDescriptionElement } from "./lib/src/components/alert"; +export { FlutterShadcnAccordion, FlutterShadcnAccordionItem, FlutterShadcnAccordionTrigger, FlutterShadcnAccordionContent } from "./lib/src/components/accordion"; +export type { FlutterShadcnAccordionElement, FlutterShadcnAccordionItemElement, FlutterShadcnAccordionTriggerElement, FlutterShadcnAccordionContentElement } from "./lib/src/components/accordion"; +export * from './types'; diff --git a/packages/react-shadcn-ui/src/types.ts b/packages/react-shadcn-ui/src/types.ts new file mode 100644 index 0000000000..6e4b18b902 --- /dev/null +++ b/packages/react-shadcn-ui/src/types.ts @@ -0,0 +1,2 @@ +/* Generated by WebF CLI - aggregated type declarations */ +export type ShadcnColorScheme = "blue" | "gray" | "green" | "neutral" | "orange" | "red" | "rose" | "slate" | "stone" | "violet" | "yellow" | "zinc"; \ No newline at end of file diff --git a/packages/react-shadcn-ui/tsconfig.json b/packages/react-shadcn-ui/tsconfig.json new file mode 100644 index 0000000000..92fa5851ee --- /dev/null +++ b/packages/react-shadcn-ui/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "jsx": "react-jsx", + "declaration": true, + "declarationDir": "dist", + "outDir": "dist", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "baseUrl": ".", + "paths": { + "react": ["./node_modules/@types/react/index.d.ts"], + "react-dom": ["./node_modules/@types/react-dom/index.d.ts"], + "react/jsx-runtime": ["./node_modules/@types/react/jsx-runtime.d.ts"], + "react/jsx-dev-runtime": ["./node_modules/@types/react/jsx-dev-runtime.d.ts"] + } + }, + "include": ["src"] +} diff --git a/packages/react-shadcn-ui/tsdown.config.ts b/packages/react-shadcn-ui/tsdown.config.ts new file mode 100644 index 0000000000..7a76bc134b --- /dev/null +++ b/packages/react-shadcn-ui/tsdown.config.ts @@ -0,0 +1,8 @@ +module.exports = { + entry: ['src/index.ts'], + format: ['esm', 'cjs'], + dts: true, + sourcemap: true, + clean: true, + external: ['react', 'react-dom', '@openwebf/react-core-ui'], +}; diff --git a/use_cases/src/pages/shadcn/ShadcnInputPage.tsx b/use_cases/src/pages/shadcn/ShadcnInputPage.tsx index 1ecfdcce76..5f38da1679 100644 --- a/use_cases/src/pages/shadcn/ShadcnInputPage.tsx +++ b/use_cases/src/pages/shadcn/ShadcnInputPage.tsx @@ -7,9 +7,14 @@ import { FlutterShadcnButton, } from '@openwebf/react-shadcn-ui'; +// New props added but not yet regenerated in the npm package types. +// Cast to allow passing them as attributes until types are regenerated. +const ShadcnInput = FlutterShadcnInput as React.FC; + export const ShadcnInputPage: React.FC = () => { const [inputValue, setInputValue] = useState(''); const [textareaValue, setTextareaValue] = useState(''); + const [submitted, setSubmitted] = useState(''); return ( @@ -22,7 +27,7 @@ export const ShadcnInputPage: React.FC = () => {
- setInputValue(e.target?.value || '')} @@ -30,18 +35,26 @@ export const ShadcnInputPage: React.FC = () => {
-
-
+
+ + +
@@ -50,21 +63,21 @@ export const ShadcnInputPage: React.FC = () => {
-
-
- @@ -72,6 +85,100 @@ export const ShadcnInputPage: React.FC = () => {
+
+

Text Alignment

+
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+

Keyboard & Input Behavior

+
+
+ + +
+
+ + +
+
+ + +
+
+ + setSubmitted('Submitted!')} + /> + {submitted && ( + {submitted} + )} +
+
+
+ +
+

Multi-line Input

+
+
+ + +
+
+
+ +
+

Cursor & Selection Colors

+
+
+ + +
+
+ + +
+
+
+

Textarea

@@ -100,11 +207,16 @@ export const ShadcnInputPage: React.FC = () => {
- +
- +
From fc50ee97ad51cc10f5e8c63bada5187b26ed8815 Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Wed, 11 Feb 2026 22:05:47 +0800 Subject: [PATCH 03/17] fix(shadcn-input): add alignment mapping for textAlign support Map textAlign to ShadInput's alignment and placeholderAlignment parameters so both typed text and placeholder render correctly for center and right alignment. Co-Authored-By: Claude Opus 4.6 --- .../lib/src/components/input.dart | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/native_uis/webf_shadcn_ui/lib/src/components/input.dart b/native_uis/webf_shadcn_ui/lib/src/components/input.dart index 1a5580c0d5..7796903020 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/input.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/input.dart @@ -452,12 +452,32 @@ class FlutterShadcnInputState extends WebFWidgetElementState { ]; } + // Map textAlign to ShadInput's alignment + placeholderAlignment. + // ShadInput uses three separate alignment controls: + // textAlign -> text within EditableText + // alignment -> positions the EditableText widget in the Stack + // placeholderAlignment -> positions the placeholder Text in the Stack + AlignmentGeometry? widgetAlignment; + switch (widgetElement._textAlign) { + case TextAlign.center: + widgetAlignment = Alignment.topCenter; + break; + case TextAlign.right: + case TextAlign.end: + widgetAlignment = Alignment.topRight; + break; + default: + widgetAlignment = null; // ShadInput defaults to topLeft + } + return ShadInput( controller: _controller, focusNode: _focusNode, placeholder: widgetElement.placeholder != null ? Text(widgetElement.placeholder!) : null, + placeholderAlignment: widgetAlignment, + alignment: widgetAlignment, enabled: !widgetElement.disabled, readOnly: widgetElement.readonly, obscureText: widgetElement.obscureText, From f1b5cc3e1e4ef341b27fdcda6ef76077c727cc91 Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Wed, 11 Feb 2026 22:07:00 +0800 Subject: [PATCH 04/17] refactor(use-cases): use FlutterShadcnInput directly in input demo Remove unnecessary React.FC cast now that local package has proper types for all new input properties. Co-Authored-By: Claude Opus 4.6 --- .../src/pages/shadcn/ShadcnInputPage.tsx | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/use_cases/src/pages/shadcn/ShadcnInputPage.tsx b/use_cases/src/pages/shadcn/ShadcnInputPage.tsx index 5f38da1679..18f804003a 100644 --- a/use_cases/src/pages/shadcn/ShadcnInputPage.tsx +++ b/use_cases/src/pages/shadcn/ShadcnInputPage.tsx @@ -7,10 +7,6 @@ import { FlutterShadcnButton, } from '@openwebf/react-shadcn-ui'; -// New props added but not yet regenerated in the npm package types. -// Cast to allow passing them as attributes until types are regenerated. -const ShadcnInput = FlutterShadcnInput as React.FC; - export const ShadcnInputPage: React.FC = () => { const [inputValue, setInputValue] = useState(''); const [textareaValue, setTextareaValue] = useState(''); @@ -27,7 +23,7 @@ export const ShadcnInputPage: React.FC = () => {
- setInputValue(e.target?.value || '')} @@ -35,21 +31,21 @@ export const ShadcnInputPage: React.FC = () => {
-
-
- {
-
-
- @@ -90,15 +86,15 @@ export const ShadcnInputPage: React.FC = () => {
- +
- +
- +
@@ -108,14 +104,14 @@ export const ShadcnInputPage: React.FC = () => {
-
- {
- { - setSubmitted('Submitted!')} @@ -150,7 +146,7 @@ export const ShadcnInputPage: React.FC = () => {
- {
-
- @@ -207,11 +203,11 @@ export const ShadcnInputPage: React.FC = () => {
- +
- Date: Wed, 11 Feb 2026 22:06:00 +0800 Subject: [PATCH 05/17] chore(use-cases): link local react-shadcn-ui package Replace npm registry dependency with link to local generated package so new props (textalign, cursorcolor, etc.) are available. Co-Authored-By: Claude Opus 4.6 --- use_cases/package.json | 2 +- use_cases/pnpm-lock.yaml | 32 +++++++++++++++++--------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/use_cases/package.json b/use_cases/package.json index 722f865a93..50b33a66ca 100644 --- a/use_cases/package.json +++ b/use_cases/package.json @@ -8,7 +8,7 @@ "@openwebf/react-cupertino-ui": "^0.3.35", "@openwebf/react-lucide-icons": "^0.1.0", "@openwebf/react-router": "1.0.0", - "@openwebf/react-shadcn-ui": "^0.1.0", + "@openwebf/react-shadcn-ui": "link:../packages/react-shadcn-ui", "@openwebf/react-ui-kit": "^0.1.0-beta.1", "@openwebf/react-video-player": "^1.0.0", "@openwebf/webf-bluetooth": "^1.0.0", diff --git a/use_cases/pnpm-lock.yaml b/use_cases/pnpm-lock.yaml index e309994f6d..f56c9291cc 100644 --- a/use_cases/pnpm-lock.yaml +++ b/use_cases/pnpm-lock.yaml @@ -24,8 +24,8 @@ importers: specifier: 1.0.0 version: 1.0.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@openwebf/react-shadcn-ui': - specifier: ^0.1.0 - version: 0.1.0(@openwebf/react-core-ui@0.24.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + specifier: link:../packages/react-shadcn-ui + version: link:../packages/react-shadcn-ui '@openwebf/react-ui-kit': specifier: ^0.1.0-beta.1 version: 0.1.0-beta.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -1253,13 +1253,6 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' - '@openwebf/react-shadcn-ui@0.1.0': - resolution: {integrity: sha512-tYiuRknaK9IoLe9W1c8jI+XzJCauZuVle9HJIzsBmzqQUghY3cXruq3bTZGS72GNbHefjK10tbOHE9Yj2fSKQg==} - peerDependencies: - '@openwebf/react-core-ui': ^0.24.1 - react: '>=16.8.0' - react-dom: '>=16.8.0' - '@openwebf/react-ui-kit@0.1.0-beta.1': resolution: {integrity: sha512-Z95UhcaI304WI6nojZjk7o15ZVD0rGX9EpLbOoryOzdyY0R5yyl7oP1cmirilL8twvwXdKHqpiKm7XFYGQUtLw==} peerDependencies: @@ -1388,56 +1381,67 @@ packages: resolution: {integrity: sha512-9tS4QyfU5NF5CdUugEi7kWbcGD7pbu6Fm8SunuePH6beeQgtcRZ9K9KVwKHEgfBHeeyrr5OvfV1qWs7PMDOf5w==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.53.0': resolution: {integrity: sha512-U+0ovxGU9bVJIHfW+oALpHd0ho1YDwhj0yHASDzIj+bOeo+VzEpNtHxcjhFab0YcHUorIMoqyxckC98+81oTJw==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.53.0': resolution: {integrity: sha512-Cp/TQ+wLjRTqTuiVwLz4XPZMo3ROl7EJYMF8HhMp8Uf+9kOOATB3/p4gGZPpuQ4BP7qEXG29ET24u9+F0ERYkQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.53.0': resolution: {integrity: sha512-SuGoAwhsSonrSTEZTiQOGC3+XZfq7rc/qAdAOBrYYIp8pu+Wh4EFFXl6+QYYNbNrHL3DnVoWACLwnfwlTa0neA==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.53.0': resolution: {integrity: sha512-EOKej1x0WoePnJWfg7ZbnUqiuiQunshzsKZSIfTHFDiCY9pnsr3Weit1GjcpGnun7H5HuRREqkT2c9CcKxNwSg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.53.0': resolution: {integrity: sha512-YAvv2aMFlfiawJ97lutomuehG2Yowd4YgsAqI85XNiMK9eBA1vEMZHt3BShg8cUvak71BM+VFRHddqc+OrRdVA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.53.0': resolution: {integrity: sha512-DxZe/sMVaqN+s5kVk3Iq619Rgyl1JCTob7xOLSNC84mbzg3NYTSheqqrtVllYjLYo4wm9YyqjVS57miuzNyXbQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.53.0': resolution: {integrity: sha512-N7+iZ0jEhwLY1FEsjbCR9lAxIZP0k+3Cghx9vSQWn+rcW8SgN8VcCmwJDoPDaGKTzWWB791U1s79BSLnEhUa0Q==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.53.0': resolution: {integrity: sha512-MA/NVneZyIskjvXdh2NR9YcPi7eHWBlQOWP2X8OymzyeUEB0JfUpmbKQZngHmOlyleV2IoR5nHIgMSRjLskOnA==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.53.0': resolution: {integrity: sha512-iYEYzYpfaSCkunVD0LOYrD9OMc357be7+rBuCxW1qvsjCGl+95iWnYAFfyEoxAm6koasNN3tFxFYze5MKl5S3A==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.53.0': resolution: {integrity: sha512-FoRekOqhRUKbJMsB5LvhQchDeFeNlS6UGUwi0p3860sxE4zE+lp07FnkuR+yQH0rSn6iLXsnr44jnorgl8mGlQ==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openharmony-arm64@4.53.0': resolution: {integrity: sha512-mEN2k1zKO5PUzW8W15hKpLh+zZI2by1onX2GfI93OekGbKN5aTjWGo7yAjwRZLjhAgs2UQcXmEWbIw0R5B4RnQ==} @@ -1561,24 +1565,28 @@ packages: engines: {node: '>=10'} cpu: [arm64] os: [linux] + libc: [glibc] '@swc/core-linux-arm64-musl@1.15.0': resolution: {integrity: sha512-Q5ldc2bzriuzYEoAuqJ9Vr3FyZhakk5hiwDbniZ8tlEXpbjBhbOleGf9/gkhLaouDnkNUEazFW9mtqwUTRdh7Q==} engines: {node: '>=10'} cpu: [arm64] os: [linux] + libc: [musl] '@swc/core-linux-x64-gnu@1.15.0': resolution: {integrity: sha512-pY4is+jEpOxlYCSnI+7N8Oxbap9TmTz5YT84tUvRTlOlTBwFAUlWFCX0FRwWJlsfP0TxbqhIe8dNNzlsEmJbXQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] + libc: [glibc] '@swc/core-linux-x64-musl@1.15.0': resolution: {integrity: sha512-zYEt5eT8y8RUpoe7t5pjpoOdGu+/gSTExj8PV86efhj6ugB3bPlj3Y85ogdW3WMVXr4NvwqvzdaYGCZfXzSyVg==} engines: {node: '>=10'} cpu: [x64] os: [linux] + libc: [musl] '@swc/core-win32-arm64-msvc@1.15.0': resolution: {integrity: sha512-zC1rmOgFH5v2BCbByOazEqs0aRNpTdLRchDExfcCfgKgeaD+IdpUOqp7i3VG1YzkcnbuZjMlXfM0ugpt+CddoA==} @@ -7512,12 +7520,6 @@ snapshots: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - '@openwebf/react-shadcn-ui@0.1.0(@openwebf/react-core-ui@0.24.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@openwebf/react-core-ui': 0.24.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - '@openwebf/react-ui-kit@0.1.0-beta.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@openwebf/react-core-ui': 0.2.13(react-dom@19.2.0(react@19.2.0))(react@19.2.0) From 35599777e7a3824dd023eb0c8328a168a128642b Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Wed, 11 Feb 2026 22:06:23 +0800 Subject: [PATCH 06/17] fix(use-cases): fix quick start cards layout with horizontal scroll Use fixed card width and overflow-x auto for consistent sizing and proper horizontal scrolling on all screen widths. Co-Authored-By: Claude Opus 4.6 --- use_cases/src/pages/HomePage.tsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/use_cases/src/pages/HomePage.tsx b/use_cases/src/pages/HomePage.tsx index c8dcddc688..c865b1d0ec 100644 --- a/use_cases/src/pages/HomePage.tsx +++ b/use_cases/src/pages/HomePage.tsx @@ -142,6 +142,13 @@ const quickStart: QuickStartItem[] = [ icon: LucideIcons.sparkles, gradient: 'linear-gradient(135deg, #f97316, #f59e0b)', }, + { + title: 'Shadcn UI', + desc: 'Popular React component library', + to: '/shadcn-showcase', + icon: LucideIcons.box, + gradient: 'linear-gradient(135deg, #8b5cf6, #ec4899)', + } ]; export const HomePage: React.FC = () => { @@ -152,10 +159,10 @@ export const HomePage: React.FC = () => { style={{ background: item.gradient, borderRadius: '16px', - padding: '20px 16px', + padding: '14px 12px', cursor: 'pointer', - flex: '1', - minWidth: '0', + width: '130px', + flexShrink: 0, display: 'flex', flexDirection: 'column', gap: '10px', @@ -175,7 +182,7 @@ export const HomePage: React.FC = () => {
{ }}>Quick Start
{quickStart.map((qs) => ( From 695f5f78983009f53d73bdaf7862738f2c327ce8 Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Wed, 11 Feb 2026 22:06:39 +0800 Subject: [PATCH 07/17] feat(use-cases): enable all shadcn component demos Uncomment previously hidden demos for Input, Select, Slider, Radio Group, Form, Progress, Skeleton, Tabs, Table, and Popover. Co-Authored-By: Claude Opus 4.6 --- use_cases/src/pages/ShadcnShowcasePage.tsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/use_cases/src/pages/ShadcnShowcasePage.tsx b/use_cases/src/pages/ShadcnShowcasePage.tsx index fe50b64da9..acf32cd4a4 100644 --- a/use_cases/src/pages/ShadcnShowcasePage.tsx +++ b/use_cases/src/pages/ShadcnShowcasePage.tsx @@ -33,12 +33,12 @@ export const ShadcnShowcasePage: React.FC = () => {
- {/**/} + - {/**/} - {/**/} - {/**/} - {/**/} + + + +

Display Components

@@ -46,13 +46,13 @@ export const ShadcnShowcasePage: React.FC = () => { - {/**/} - {/**/} + +

Navigation & Layout

- {/**/} + @@ -60,7 +60,7 @@ export const ShadcnShowcasePage: React.FC = () => {

Data & Pickers

- {/**/} +
@@ -68,7 +68,7 @@ export const ShadcnShowcasePage: React.FC = () => {
- {/**/} +
From 108dd463270737ab11a7b46962f0dfc0ca28279a Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Wed, 11 Feb 2026 22:11:51 +0800 Subject: [PATCH 08/17] fix(use-cases): allow quick start cards to expand when few items Use flex: 1 with minWidth instead of fixed width so cards fill available space when there are only 2-3 items. Co-Authored-By: Claude Opus 4.6 --- use_cases/src/pages/HomePage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/use_cases/src/pages/HomePage.tsx b/use_cases/src/pages/HomePage.tsx index c865b1d0ec..90bd3801f0 100644 --- a/use_cases/src/pages/HomePage.tsx +++ b/use_cases/src/pages/HomePage.tsx @@ -161,8 +161,8 @@ export const HomePage: React.FC = () => { borderRadius: '16px', padding: '14px 12px', cursor: 'pointer', - width: '130px', - flexShrink: 0, + flex: '1', + minWidth: '130px', display: 'flex', flexDirection: 'column', gap: '10px', From ce319fb4eb68ae913fefab392372fd558ff0a5d3 Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Wed, 11 Feb 2026 22:15:56 +0800 Subject: [PATCH 09/17] feat(use-cases): hide Shadcn UI quick start card in production Add hidden config to QuickStartItem and set it for Shadcn UI since shadcn routes are only available in dev mode. Co-Authored-By: Claude Opus 4.6 --- use_cases/src/pages/HomePage.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/use_cases/src/pages/HomePage.tsx b/use_cases/src/pages/HomePage.tsx index 90bd3801f0..51fae74d0a 100644 --- a/use_cases/src/pages/HomePage.tsx +++ b/use_cases/src/pages/HomePage.tsx @@ -125,6 +125,7 @@ type QuickStartItem = { to: string; icon: LucideIcons; gradient: string; + hidden?: boolean; }; const quickStart: QuickStartItem[] = [ @@ -148,6 +149,7 @@ const quickStart: QuickStartItem[] = [ to: '/shadcn-showcase', icon: LucideIcons.box, gradient: 'linear-gradient(135deg, #8b5cf6, #ec4899)', + hidden: !import.meta.env.DEV, } ]; @@ -358,7 +360,7 @@ export const HomePage: React.FC = () => { gap: '10px', overflowX: 'auto' as const, }}> - {quickStart.map((qs) => ( + {quickStart.filter((qs) => !qs.hidden).map((qs) => ( ))}
From 0c2ef6b669b639d583979c2ed53cab76a05e1002 Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Thu, 12 Feb 2026 20:22:56 +0800 Subject: [PATCH 10/17] fix(use-cases): add prebuild step for linked react-shadcn-ui package On CI, the linked package's dist/ doesn't exist since it's gitignored. Add a prebuild script to build it before vite build. Co-Authored-By: Claude Opus 4.6 --- use_cases/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/use_cases/package.json b/use_cases/package.json index 50b33a66ca..2bb0606855 100644 --- a/use_cases/package.json +++ b/use_cases/package.json @@ -33,6 +33,7 @@ "scripts": { "start": "vite", "dev": "vite", + "prebuild": "pnpm -C ../packages/react-shadcn-ui run build", "build": "vite build", "preview": "vite preview", "test": "react-scripts test" From c8dbfb941e204a22c95445b92090c58b324c6213 Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Thu, 12 Feb 2026 20:26:22 +0800 Subject: [PATCH 11/17] fix(use-cases): install react-shadcn-ui deps before prebuild CI doesn't have node_modules for the linked package, so pnpm install is needed before tsdown can run. Co-Authored-By: Claude Opus 4.6 --- use_cases/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/use_cases/package.json b/use_cases/package.json index 2bb0606855..52450bcfb5 100644 --- a/use_cases/package.json +++ b/use_cases/package.json @@ -33,7 +33,7 @@ "scripts": { "start": "vite", "dev": "vite", - "prebuild": "pnpm -C ../packages/react-shadcn-ui run build", + "prebuild": "pnpm -C ../packages/react-shadcn-ui install && pnpm -C ../packages/react-shadcn-ui run build", "build": "vite build", "preview": "vite preview", "test": "react-scripts test" From feee4ecb4a99a0a67481ceeee3f5594cb6c47fcc Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Thu, 12 Feb 2026 20:30:08 +0800 Subject: [PATCH 12/17] fix(react-shadcn-ui): track generated src/lib/ files for CI builds The .gitignore `lib/` rule was too broad, also excluding `src/lib/` which contains the generated .tsx component files needed for tsdown to build. Change to `/lib/` to only ignore root lib/ directory. Co-Authored-By: Claude Opus 4.6 --- packages/react-shadcn-ui/.gitignore | 2 +- .../src/lib/src/components/accordion.tsx | 267 ++++++ .../src/lib/src/components/alert.tsx | 185 ++++ .../src/lib/src/components/avatar.tsx | 89 ++ .../src/lib/src/components/badge.tsx | 72 ++ .../src/lib/src/components/breadcrumb.tsx | 572 ++++++++++++ .../src/lib/src/components/button.tsx | 119 +++ .../src/lib/src/components/calendar.tsx | 210 +++++ .../src/lib/src/components/card.tsx | 330 +++++++ .../src/lib/src/components/checkbox.tsx | 96 ++ .../src/lib/src/components/collapsible.tsx | 193 ++++ .../src/lib/src/components/combobox.tsx | 200 +++++ .../src/lib/src/components/context_menu.tsx | 832 ++++++++++++++++++ .../src/lib/src/components/date_picker.tsx | 112 +++ .../src/lib/src/components/dialog.tsx | 371 ++++++++ .../src/lib/src/components/dropdown_menu.tsx | 376 ++++++++ .../src/lib/src/components/form.tsx | 572 ++++++++++++ .../src/lib/src/components/icon_button.tsx | 154 ++++ .../src/lib/src/components/image.tsx | 123 +++ .../src/lib/src/components/input.tsx | 268 ++++++ .../src/lib/src/components/popover.tsx | 217 +++++ .../src/lib/src/components/progress.tsx | 83 ++ .../src/lib/src/components/radio.tsx | 162 ++++ .../src/lib/src/components/scroll_area.tsx | 70 ++ .../src/lib/src/components/select.tsx | 540 ++++++++++++ .../src/lib/src/components/separator.tsx | 68 ++ .../src/lib/src/components/sheet.tsx | 373 ++++++++ .../src/lib/src/components/skeleton.tsx | 81 ++ .../src/lib/src/components/slider.tsx | 134 +++ .../src/lib/src/components/switch.tsx | 89 ++ .../src/lib/src/components/table.tsx | 327 +++++++ .../src/lib/src/components/tabs.tsx | 269 ++++++ .../src/lib/src/components/textarea.tsx | 163 ++++ .../src/lib/src/components/time_picker.tsx | 104 +++ .../src/lib/src/components/toast.tsx | 110 +++ .../src/lib/src/components/tooltip.tsx | 95 ++ .../src/lib/src/theme/theme.tsx | 93 ++ 37 files changed, 8120 insertions(+), 1 deletion(-) create mode 100644 packages/react-shadcn-ui/src/lib/src/components/accordion.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/alert.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/avatar.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/badge.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/breadcrumb.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/button.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/calendar.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/card.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/checkbox.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/collapsible.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/combobox.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/context_menu.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/date_picker.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/dialog.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/dropdown_menu.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/form.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/icon_button.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/image.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/input.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/popover.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/progress.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/radio.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/scroll_area.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/select.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/separator.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/sheet.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/skeleton.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/slider.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/switch.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/table.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/tabs.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/textarea.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/time_picker.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/toast.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/components/tooltip.tsx create mode 100644 packages/react-shadcn-ui/src/lib/src/theme/theme.tsx diff --git a/packages/react-shadcn-ui/.gitignore b/packages/react-shadcn-ui/.gitignore index 8c63a6a0a9..2b60337458 100644 --- a/packages/react-shadcn-ui/.gitignore +++ b/packages/react-shadcn-ui/.gitignore @@ -1,5 +1,5 @@ node_modules/ -lib/ +/lib/ dist/ .DS_Store *.log diff --git a/packages/react-shadcn-ui/src/lib/src/components/accordion.tsx b/packages/react-shadcn-ui/src/lib/src/components/accordion.tsx new file mode 100644 index 0000000000..d69c9584ad --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/accordion.tsx @@ -0,0 +1,267 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnAccordionProps { + /** + * Selection type. + * - 'single': Only one item can be expanded + * - 'multiple': Multiple items can be expanded + * Default: 'single' + */ + type?: string; + /** + * Currently expanded item(s) value(s). + * For single type: string, for multiple type: comma-separated string + */ + value?: string; + /** + * Allow collapsing all items in single mode. + * Default: true + */ + collapsible?: boolean; + /** + * change event handler + */ + onChange?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnAccordionElement extends WebFElementWithMethods<{ +}> { + /** Selection type. */ + type?: string; + /** Currently expanded item(s) value(s). */ + value?: string; + /** Allow collapsing all items in single mode. */ + collapsible?: boolean; +} +/** + * Properties for +A collapsible accordion component. +@example +```html + + + Is it accessible? + Yes. It adheres to the WAI-ARIA design pattern. + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnAccordion = createWebFComponent({ + tagName: 'flutter-shadcn-accordion', + displayName: 'FlutterShadcnAccordion', + // Map props to attributes + attributeProps: [ + 'type', + 'value', + 'collapsible', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnAccordionItemProps { + /** + * value property + */ + value: string; + /** + * disabled property + * @default undefined + */ + disabled?: boolean; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnAccordionItemElement extends WebFElementWithMethods<{ +}> { + value: string; + disabled?: boolean; +} +/** + * FlutterShadcnAccordionItem - WebF FlutterShadcnAccordionItem component + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnAccordionItem = createWebFComponent({ + tagName: 'flutter-shadcn-accordion-item', + displayName: 'FlutterShadcnAccordionItem', + // Map props to attributes + attributeProps: [ + 'value', + 'disabled', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnAccordionTriggerProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnAccordionTriggerElement extends WebFElementWithMethods<{ +}> { +} +/** + * FlutterShadcnAccordionTrigger - WebF FlutterShadcnAccordionTrigger component + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnAccordionTrigger = createWebFComponent({ + tagName: 'flutter-shadcn-accordion-trigger', + displayName: 'FlutterShadcnAccordionTrigger', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnAccordionContentProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnAccordionContentElement extends WebFElementWithMethods<{ +}> { +} +/** + * FlutterShadcnAccordionContent - WebF FlutterShadcnAccordionContent component + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnAccordionContent = createWebFComponent({ + tagName: 'flutter-shadcn-accordion-content', + displayName: 'FlutterShadcnAccordionContent', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/alert.tsx b/packages/react-shadcn-ui/src/lib/src/components/alert.tsx new file mode 100644 index 0000000000..69729f6eb8 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/alert.tsx @@ -0,0 +1,185 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnAlertProps { + /** + * Visual variant of the alert. + * - 'default': Standard informational alert + * - 'destructive': Red destructive/error alert + * Default: 'default' + */ + variant?: string; + /** + * Icon name to display in the alert. + */ + icon?: string; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnAlertElement extends WebFElementWithMethods<{ +}> { + /** Visual variant of the alert. */ + variant?: string; + /** Icon name to display in the alert. */ + icon?: string; +} +/** + * Properties for +An alert component for displaying important messages. +@example +```html + + Heads up! + + You can add components to your app using the CLI. + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnAlert = createWebFComponent({ + tagName: 'flutter-shadcn-alert', + displayName: 'FlutterShadcnAlert', + // Map props to attributes + attributeProps: [ + 'variant', + 'icon', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnAlertTitleProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnAlertTitleElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Title slot for the alert. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnAlertTitle = createWebFComponent({ + tagName: 'flutter-shadcn-alert-title', + displayName: 'FlutterShadcnAlertTitle', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnAlertDescriptionProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnAlertDescriptionElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Description slot for the alert. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnAlertDescription = createWebFComponent({ + tagName: 'flutter-shadcn-alert-description', + displayName: 'FlutterShadcnAlertDescription', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/avatar.tsx b/packages/react-shadcn-ui/src/lib/src/components/avatar.tsx new file mode 100644 index 0000000000..ff8864483f --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/avatar.tsx @@ -0,0 +1,89 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnAvatarProps { + /** + * URL of the avatar image. + */ + src?: string; + /** + * Alt text for the image. + */ + alt?: string; + /** + * Fallback text/initials when image fails to load or is not provided. + */ + fallback?: string; + /** + * Size of the avatar in pixels. + * Default: 40 + */ + size?: string; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnAvatarElement extends WebFElementWithMethods<{ +}> { + /** URL of the avatar image. */ + src?: string; + /** Alt text for the image. */ + alt?: string; + /** Fallback text/initials when image fails to load or is not provided. */ + fallback?: string; + /** Size of the avatar in pixels. */ + size?: string; +} +/** + * Properties for +An avatar component for displaying user images or initials. +@example +```html + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnAvatar = createWebFComponent({ + tagName: 'flutter-shadcn-avatar', + displayName: 'FlutterShadcnAvatar', + // Map props to attributes + attributeProps: [ + 'src', + 'alt', + 'fallback', + 'size', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/badge.tsx b/packages/react-shadcn-ui/src/lib/src/components/badge.tsx new file mode 100644 index 0000000000..fafc0e1697 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/badge.tsx @@ -0,0 +1,72 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnBadgeProps { + /** + * Visual variant of the badge. + * - 'default': Primary filled badge + * - 'secondary': Secondary muted badge + * - 'destructive': Red destructive badge + * - 'outline': Bordered outline badge + * Default: 'default' + */ + variant?: string; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnBadgeElement extends WebFElementWithMethods<{ +}> { + /** Visual variant of the badge. */ + variant?: string; +} +/** + * Properties for +A small badge component for displaying labels or counts. +@example +```html +New +Error +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnBadge = createWebFComponent({ + tagName: 'flutter-shadcn-badge', + displayName: 'FlutterShadcnBadge', + // Map props to attributes + attributeProps: [ + 'variant', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/breadcrumb.tsx b/packages/react-shadcn-ui/src/lib/src/components/breadcrumb.tsx new file mode 100644 index 0000000000..f8214d490e --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/breadcrumb.tsx @@ -0,0 +1,572 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnBreadcrumbProps { + /** + * Spacing between breadcrumb items. + * @default 10 + */ + spacing?: number; + /** + * Custom separator between items. + * Predefined values: 'slash', '/', 'arrow', '>', 'dash', '-', 'dot', '.', 'chevron' + * Or any custom string to use as separator text. + * @default chevron icon + */ + separator?: 'slash' | 'arrow' | 'dash' | 'dot' | 'chevron' | any; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnBreadcrumbElement extends WebFElementWithMethods<{ +}> { + /** Spacing between breadcrumb items. */ + spacing?: number; + /** Custom separator between items. */ + separator?: 'slash' | 'arrow' | 'dash' | 'dot' | 'chevron' | any; +} +/** + * Properties for +A breadcrumb navigation component that displays the current page location +within a navigational hierarchy. +@example +```html + + + Home + + + Components + + + Breadcrumb + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnBreadcrumb = createWebFComponent({ + tagName: 'flutter-shadcn-breadcrumb', + displayName: 'FlutterShadcnBreadcrumb', + // Map props to attributes + attributeProps: [ + 'spacing', + 'separator', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnBreadcrumbListProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnBreadcrumbListElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Container for breadcrumb items (for backwards compatibility). + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnBreadcrumbList = createWebFComponent({ + tagName: 'flutter-shadcn-breadcrumb-list', + displayName: 'FlutterShadcnBreadcrumbList', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnBreadcrumbItemProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnBreadcrumbItemElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Individual breadcrumb item container. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnBreadcrumbItem = createWebFComponent({ + tagName: 'flutter-shadcn-breadcrumb-item', + displayName: 'FlutterShadcnBreadcrumbItem', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnBreadcrumbLinkProps { + /** + * Link destination URL. + */ + href?: string; + /** + * Fired when link is clicked. + */ + onClick?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnBreadcrumbLinkElement extends WebFElementWithMethods<{ +}> { + /** Link destination URL. */ + href?: string; +} +/** + * Properties for +Clickable breadcrumb link with hover effects. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnBreadcrumbLink = createWebFComponent({ + tagName: 'flutter-shadcn-breadcrumb-link', + displayName: 'FlutterShadcnBreadcrumbLink', + // Map props to attributes + attributeProps: [ + 'href', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onClick', + eventName: 'click', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnBreadcrumbPageProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnBreadcrumbPageElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Current page indicator (non-clickable, highlighted text). + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnBreadcrumbPage = createWebFComponent({ + tagName: 'flutter-shadcn-breadcrumb-page', + displayName: 'FlutterShadcnBreadcrumbPage', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnBreadcrumbSeparatorProps { + /** + * Size of the separator icon. + * @default 14 + */ + size?: number; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnBreadcrumbSeparatorElement extends WebFElementWithMethods<{ +}> { + /** Size of the separator icon. */ + size?: number; +} +/** + * Properties for +Separator between breadcrumb items (chevron icon by default). + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnBreadcrumbSeparator = createWebFComponent({ + tagName: 'flutter-shadcn-breadcrumb-separator', + displayName: 'FlutterShadcnBreadcrumbSeparator', + // Map props to attributes + attributeProps: [ + 'size', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnBreadcrumbEllipsisProps { + /** + * Size of the ellipsis icon. + * @default 16 + */ + size?: number; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnBreadcrumbEllipsisElement extends WebFElementWithMethods<{ +}> { + /** Size of the ellipsis icon. */ + size?: number; +} +/** + * Properties for +Ellipsis indicator for collapsed/hidden breadcrumb sections. +@example +```html + + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnBreadcrumbEllipsis = createWebFComponent({ + tagName: 'flutter-shadcn-breadcrumb-ellipsis', + displayName: 'FlutterShadcnBreadcrumbEllipsis', + // Map props to attributes + attributeProps: [ + 'size', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnBreadcrumbDropdownProps { + /** + * Whether to show the dropdown arrow icon. + * @default true + */ + showArrow?: boolean; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnBreadcrumbDropdownElement extends WebFElementWithMethods<{ +}> { + /** Whether to show the dropdown arrow icon. */ + showArrow?: boolean; +} +/** + * Properties for +Dropdown menu for showing collapsed breadcrumb items. +@example +```html + + + Documentation + Themes + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnBreadcrumbDropdown = createWebFComponent({ + tagName: 'flutter-shadcn-breadcrumb-dropdown', + displayName: 'FlutterShadcnBreadcrumbDropdown', + // Map props to attributes + attributeProps: [ + 'showArrow', + ], + // Convert prop names to attribute names if needed + attributeMap: { + showArrow: 'show-arrow', + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnBreadcrumbDropdownItemProps { + /** + * Fired when dropdown item is clicked. + */ + onClick?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnBreadcrumbDropdownItemElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Individual item in a breadcrumb dropdown menu. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnBreadcrumbDropdownItem = createWebFComponent({ + tagName: 'flutter-shadcn-breadcrumb-dropdown-item', + displayName: 'FlutterShadcnBreadcrumbDropdownItem', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onClick', + eventName: 'click', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/button.tsx b/packages/react-shadcn-ui/src/lib/src/components/button.tsx new file mode 100644 index 0000000000..611568877c --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/button.tsx @@ -0,0 +1,119 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnButtonProps { + /** + * Visual variant of the button. + * - 'default': Primary filled button + * - 'secondary': Secondary muted button + * - 'destructive': Red destructive action button + * - 'outline': Bordered outline button + * - 'ghost': Transparent with hover effect + * - 'link': Text link style button + * Default: 'default' + */ + variant?: string; + /** + * Size of the button. + * - 'default': Standard size + * - 'sm': Small size + * - 'lg': Large size + * - 'icon': Square icon-only button + * Default: 'default' + */ + size?: string; + /** + * Disable button interactions. + */ + disabled?: boolean; + /** + * Show loading spinner and disable interactions. + */ + loading?: boolean; + /** + * Icon name to show before the button text. + */ + icon?: string; + /** + * Fired when the button is pressed (not emitted when disabled or loading). + */ + onClick?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnButtonElement extends WebFElementWithMethods<{ +}> { + /** Visual variant of the button. */ + variant?: string; + /** Size of the button. */ + size?: string; + /** Disable button interactions. */ + disabled?: boolean; + /** Show loading spinner and disable interactions. */ + loading?: boolean; + /** Icon name to show before the button text. */ + icon?: string; +} +/** + * Properties for +A versatile button component with multiple variants and sizes. +@example +```html + + Click me + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnButton = createWebFComponent({ + tagName: 'flutter-shadcn-button', + displayName: 'FlutterShadcnButton', + // Map props to attributes + attributeProps: [ + 'variant', + 'size', + 'disabled', + 'loading', + 'icon', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onClick', + eventName: 'click', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/calendar.tsx b/packages/react-shadcn-ui/src/lib/src/components/calendar.tsx new file mode 100644 index 0000000000..08577dea4c --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/calendar.tsx @@ -0,0 +1,210 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +/** + * Detail for calendar change event. + */ +interface FlutterShadcnCalendarChangeEventDetail { + /** + * The selected value. + * - For single mode: ISO date string (YYYY-MM-DD) or null + * - For multiple mode: Comma-separated ISO date strings + * - For range mode: Comma-separated start and end dates + */ + value: string | null; +} +export interface FlutterShadcnCalendarProps { + /** + * Selection mode. + * - 'single': Select one date + * - 'multiple': Select multiple dates (value is comma-separated) + * - 'range': Select a date range (value is start,end) + * @default 'single' + */ + mode?: 'single' | 'multiple' | 'range'; + /** + * Selected date(s) in ISO format (YYYY-MM-DD). + * - For single: '2024-01-15' + * - For multiple: '2024-01-15,2024-01-20,2024-01-25' + * - For range: '2024-01-15,2024-01-20' (start,end) + */ + value?: string; + /** + * Disable the calendar. + */ + disabled?: boolean; + /** + * Minimum selectable date (YYYY-MM-DD). + */ + min?: string; + /** + * Maximum selectable date (YYYY-MM-DD). + */ + max?: string; + /** + * Caption layout style. + * - 'label': Default label style + * - 'dropdown': Both month and year dropdowns + * - 'dropdown-months': Month dropdown only + * - 'dropdown-years': Year dropdown only + * @default 'label' + */ + captionLayout?: 'label' | 'dropdown' | 'dropdown-months' | 'dropdown-years'; + /** + * Hide the navigation arrows (prev/next month). + * @default false + */ + hideNavigation?: boolean; + /** + * Show week numbers column. + * @default false + */ + showWeekNumbers?: boolean; + /** + * Show days from adjacent months. + * @default true + */ + showOutsideDays?: boolean; + /** + * Always display 6 weeks for consistent height. + * @default false + */ + fixedWeeks?: boolean; + /** + * Hide the weekday name headers (Mon, Tue, etc.). + * @default false + */ + hideWeekdayNames?: boolean; + /** + * Number of months to display simultaneously. + * @default 1 + */ + numberOfMonths?: number; + /** + * Allow deselecting a selected date by clicking it again. + * Only applies to 'single' selection mode. + * @default false + */ + allowDeselection?: boolean; + /** + * Fired when date selection changes. Detail contains the selected value. + */ + onChange?: (event: CustomEvent) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnCalendarElement extends WebFElementWithMethods<{ +}> { + /** Selection mode. */ + mode?: 'single' | 'multiple' | 'range'; + /** Selected date(s) in ISO format (YYYY-MM-DD). */ + value?: string; + /** Disable the calendar. */ + disabled?: boolean; + /** Minimum selectable date (YYYY-MM-DD). */ + min?: string; + /** Maximum selectable date (YYYY-MM-DD). */ + max?: string; + /** Caption layout style. */ + captionLayout?: 'label' | 'dropdown' | 'dropdown-months' | 'dropdown-years'; + /** Hide the navigation arrows (prev/next month). */ + hideNavigation?: boolean; + /** Show week numbers column. */ + showWeekNumbers?: boolean; + /** Show days from adjacent months. */ + showOutsideDays?: boolean; + /** Always display 6 weeks for consistent height. */ + fixedWeeks?: boolean; + /** Hide the weekday name headers (Mon, Tue, etc.). */ + hideWeekdayNames?: boolean; + /** Number of months to display simultaneously. */ + numberOfMonths?: number; + /** Allow deselecting a selected date by clicking it again. */ + allowDeselection?: boolean; +} +/** + * Properties for +A calendar component for date selection with support for single, multiple, and range modes. +@example +```html + + + + + + + + + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnCalendar = createWebFComponent({ + tagName: 'flutter-shadcn-calendar', + displayName: 'FlutterShadcnCalendar', + // Map props to attributes + attributeProps: [ + 'mode', + 'value', + 'disabled', + 'min', + 'max', + 'captionLayout', + 'hideNavigation', + 'showWeekNumbers', + 'showOutsideDays', + 'fixedWeeks', + 'hideWeekdayNames', + 'numberOfMonths', + 'allowDeselection', + ], + // Convert prop names to attribute names if needed + attributeMap: { + captionLayout: 'caption-layout', + hideNavigation: 'hide-navigation', + showWeekNumbers: 'show-week-numbers', + showOutsideDays: 'show-outside-days', + fixedWeeks: 'fixed-weeks', + hideWeekdayNames: 'hide-weekday-names', + numberOfMonths: 'number-of-months', + allowDeselection: 'allow-deselection', + }, + // Event handlers + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: CustomEvent) => void) => (event: Event) => { + callback(event as CustomEvent); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/card.tsx b/packages/react-shadcn-ui/src/lib/src/components/card.tsx new file mode 100644 index 0000000000..ca48b16833 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/card.tsx @@ -0,0 +1,330 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnCardProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnCardElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +A container card with optional header, content, and footer slots. +@example +```html + + + Card Title + Card description here. + + +

Card content goes here.

+
+ + Action + +
+``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnCard = createWebFComponent({ + tagName: 'flutter-shadcn-card', + displayName: 'FlutterShadcnCard', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnCardHeaderProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnCardHeaderElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Header slot for the card. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnCardHeader = createWebFComponent({ + tagName: 'flutter-shadcn-card-header', + displayName: 'FlutterShadcnCardHeader', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnCardTitleProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnCardTitleElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Title slot within card header. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnCardTitle = createWebFComponent({ + tagName: 'flutter-shadcn-card-title', + displayName: 'FlutterShadcnCardTitle', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnCardDescriptionProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnCardDescriptionElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Description slot within card header. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnCardDescription = createWebFComponent({ + tagName: 'flutter-shadcn-card-description', + displayName: 'FlutterShadcnCardDescription', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnCardContentProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnCardContentElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Main content slot for the card. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnCardContent = createWebFComponent({ + tagName: 'flutter-shadcn-card-content', + displayName: 'FlutterShadcnCardContent', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnCardFooterProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnCardFooterElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Footer slot for the card. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnCardFooter = createWebFComponent({ + tagName: 'flutter-shadcn-card-footer', + displayName: 'FlutterShadcnCardFooter', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/checkbox.tsx b/packages/react-shadcn-ui/src/lib/src/components/checkbox.tsx new file mode 100644 index 0000000000..7b23af118a --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/checkbox.tsx @@ -0,0 +1,96 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnCheckboxProps { + /** + * Whether the checkbox is checked. + */ + checked?: boolean; + /** + * Disable the checkbox. + */ + disabled?: boolean; + /** + * Show indeterminate state (neither checked nor unchecked). + */ + indeterminate?: boolean; + /** + * Fired when the checked state changes. + */ + onChange?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnCheckboxElement extends WebFElementWithMethods<{ +}> { + /** Whether the checkbox is checked. */ + checked?: boolean; + /** Disable the checkbox. */ + disabled?: boolean; + /** Show indeterminate state (neither checked nor unchecked). */ + indeterminate?: boolean; +} +/** + * Properties for +A checkbox control for boolean input. +@example +```html + + Accept terms and conditions + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnCheckbox = createWebFComponent({ + tagName: 'flutter-shadcn-checkbox', + displayName: 'FlutterShadcnCheckbox', + // Map props to attributes + attributeProps: [ + 'checked', + 'disabled', + 'indeterminate', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/collapsible.tsx b/packages/react-shadcn-ui/src/lib/src/components/collapsible.tsx new file mode 100644 index 0000000000..e66afc6cea --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/collapsible.tsx @@ -0,0 +1,193 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnCollapsibleProps { + /** + * Whether the section is expanded. + */ + open?: boolean; + /** + * Disable the collapsible. + */ + disabled?: boolean; + /** + * Fired when open state changes. + */ + onChange?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnCollapsibleElement extends WebFElementWithMethods<{ +}> { + /** Whether the section is expanded. */ + open?: boolean; + /** Disable the collapsible. */ + disabled?: boolean; +} +/** + * Properties for +A collapsible section. +@example +```html + + + Toggle + + + Hidden content here + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnCollapsible = createWebFComponent({ + tagName: 'flutter-shadcn-collapsible', + displayName: 'FlutterShadcnCollapsible', + // Map props to attributes + attributeProps: [ + 'open', + 'disabled', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnCollapsibleTriggerProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnCollapsibleTriggerElement extends WebFElementWithMethods<{ +}> { +} +/** + * FlutterShadcnCollapsibleTrigger - WebF FlutterShadcnCollapsibleTrigger component + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnCollapsibleTrigger = createWebFComponent({ + tagName: 'flutter-shadcn-collapsible-trigger', + displayName: 'FlutterShadcnCollapsibleTrigger', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnCollapsibleContentProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnCollapsibleContentElement extends WebFElementWithMethods<{ +}> { +} +/** + * FlutterShadcnCollapsibleContent - WebF FlutterShadcnCollapsibleContent component + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnCollapsibleContent = createWebFComponent({ + tagName: 'flutter-shadcn-collapsible-content', + displayName: 'FlutterShadcnCollapsibleContent', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/combobox.tsx b/packages/react-shadcn-ui/src/lib/src/components/combobox.tsx new file mode 100644 index 0000000000..d24fac8187 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/combobox.tsx @@ -0,0 +1,200 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnComboboxProps { + /** + * Currently selected value. + */ + value?: string; + /** + * Placeholder text when no value is selected. + */ + placeholder?: string; + /** + * Placeholder for the search input. + */ + searchPlaceholder?: string; + /** + * Text shown when no results match the search. + */ + emptyText?: string; + /** + * Disable the combobox. + */ + disabled?: boolean; + /** + * Allow clearing the selection. + */ + clearable?: boolean; + /** + * Fired when selection changes. + */ + onChange?: (event: Event) => void; + /** + * Fired when search query changes. + */ + onSearch?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnComboboxElement extends WebFElementWithMethods<{ +}> { + /** Currently selected value. */ + value?: string; + /** Placeholder text when no value is selected. */ + placeholder?: string; + /** Placeholder for the search input. */ + searchPlaceholder?: string; + /** Text shown when no results match the search. */ + emptyText?: string; + /** Disable the combobox. */ + disabled?: boolean; + /** Allow clearing the selection. */ + clearable?: boolean; +} +/** + * Properties for +A searchable dropdown with autocomplete functionality. +@example +```html + + React + Vue + Angular + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnCombobox = createWebFComponent({ + tagName: 'flutter-shadcn-combobox', + displayName: 'FlutterShadcnCombobox', + // Map props to attributes + attributeProps: [ + 'value', + 'placeholder', + 'searchPlaceholder', + 'emptyText', + 'disabled', + 'clearable', + ], + // Convert prop names to attribute names if needed + attributeMap: { + searchPlaceholder: 'search-placeholder', + emptyText: 'empty-text', + }, + // Event handlers + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onSearch', + eventName: 'search', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnComboboxItemProps { + /** + * Value of this option. + */ + value: string; + /** + * Disable this specific option. + */ + disabled?: boolean; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnComboboxItemElement extends WebFElementWithMethods<{ +}> { + /** Value of this option. */ + value: string; + /** Disable this specific option. */ + disabled?: boolean; +} +/** + * Properties for +Individual combobox option. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnComboboxItem = createWebFComponent({ + tagName: 'flutter-shadcn-combobox-item', + displayName: 'FlutterShadcnComboboxItem', + // Map props to attributes + attributeProps: [ + 'value', + 'disabled', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/context_menu.tsx b/packages/react-shadcn-ui/src/lib/src/components/context_menu.tsx new file mode 100644 index 0000000000..b3d9a4bada --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/context_menu.tsx @@ -0,0 +1,832 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +interface FlutterShadcnContextMenuRadioGroupChangeEventDetail { + value: string | null; +} +export interface FlutterShadcnContextMenuProps { + /** + * Whether the menu is open. + * + * This value is updated automatically when the native menu opens/closes. + * Setting it to `false` will close the menu. + */ + open?: boolean; + /** + * Fired when menu opens. + */ + onOpen?: (event: Event) => void; + /** + * Fired when menu closes. + */ + onClose?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnContextMenuElement extends WebFElementWithMethods<{ +}> { + /** Whether the menu is open. */ + open?: boolean; +} +/** + * Properties for +A context menu that appears on right-click. +@example +```html + + +
Right click here
+
+ + Cut + Copy + Paste + +
+``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnContextMenu = createWebFComponent({ + tagName: 'flutter-shadcn-context-menu', + displayName: 'FlutterShadcnContextMenu', + // Map props to attributes + attributeProps: [ + 'open', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onOpen', + eventName: 'open', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onClose', + eventName: 'close', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnContextMenuTriggerProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnContextMenuTriggerElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnContextMenuTrigger = createWebFComponent({ + tagName: 'flutter-shadcn-context-menu-trigger', + displayName: 'FlutterShadcnContextMenuTrigger', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnContextMenuContentProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnContextMenuContentElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnContextMenuContent = createWebFComponent({ + tagName: 'flutter-shadcn-context-menu-content', + displayName: 'FlutterShadcnContextMenuContent', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnContextMenuItemProps { + /** + * Whether the item is disabled. + */ + disabled?: boolean; + /** + * Keyboard shortcut to display (e.g., "⌘Z", "Ctrl+C"). + * + * When the context menu is open, matching key presses will trigger the + * item's `click` handler. + */ + shortcut?: string; + /** + * Whether to use inset styling (adds left padding for alignment with checkbox/radio items). + */ + inset?: boolean; + /** + * Fired when the item is clicked. + */ + onClick?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnContextMenuItemElement extends WebFElementWithMethods<{ +}> { + /** Whether the item is disabled. */ + disabled?: boolean; + /** Keyboard shortcut to display (e.g., "⌘Z", "Ctrl+C"). */ + shortcut?: string; + /** Whether to use inset styling (adds left padding for alignment with checkbox/radio items). */ + inset?: boolean; +} +/** + * Properties for + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnContextMenuItem = createWebFComponent({ + tagName: 'flutter-shadcn-context-menu-item', + displayName: 'FlutterShadcnContextMenuItem', + // Map props to attributes + attributeProps: [ + 'disabled', + 'shortcut', + 'inset', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onClick', + eventName: 'click', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnContextMenuSeparatorProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnContextMenuSeparatorElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnContextMenuSeparator = createWebFComponent({ + tagName: 'flutter-shadcn-context-menu-separator', + displayName: 'FlutterShadcnContextMenuSeparator', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnContextMenuLabelProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnContextMenuLabelElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +A label/header for grouping context menu items. +@example +```html +People +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnContextMenuLabel = createWebFComponent({ + tagName: 'flutter-shadcn-context-menu-label', + displayName: 'FlutterShadcnContextMenuLabel', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnContextMenuSubProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnContextMenuSubElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +A container for nested submenu items. +Submenus open on hover (mouse) and can be toggled by tap/click. +@example +```html + + More Tools + + Save Page As... + Create Shortcut... + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnContextMenuSub = createWebFComponent({ + tagName: 'flutter-shadcn-context-menu-sub', + displayName: 'FlutterShadcnContextMenuSub', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnContextMenuSubTriggerProps { + /** + * Whether the submenu trigger is disabled. + */ + disabled?: boolean; + /** + * Whether to use inset styling (adds left padding for alignment). + */ + inset?: boolean; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnContextMenuSubTriggerElement extends WebFElementWithMethods<{ +}> { + /** Whether the submenu trigger is disabled. */ + disabled?: boolean; + /** Whether to use inset styling (adds left padding for alignment). */ + inset?: boolean; +} +/** + * Properties for +The trigger element for a submenu. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnContextMenuSubTrigger = createWebFComponent({ + tagName: 'flutter-shadcn-context-menu-sub-trigger', + displayName: 'FlutterShadcnContextMenuSubTrigger', + // Map props to attributes + attributeProps: [ + 'disabled', + 'inset', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnContextMenuSubContentProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnContextMenuSubContentElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Container for submenu items. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnContextMenuSubContent = createWebFComponent({ + tagName: 'flutter-shadcn-context-menu-sub-content', + displayName: 'FlutterShadcnContextMenuSubContent', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnContextMenuCheckboxItemProps { + /** + * Whether the item is disabled. + */ + disabled?: boolean; + /** + * Whether the checkbox is checked. + */ + checked?: boolean; + /** + * Keyboard shortcut to display (e.g., "⌘B"). + */ + shortcut?: string; + /** + * Fired when the checked state changes. + */ + onChange?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnContextMenuCheckboxItemElement extends WebFElementWithMethods<{ +}> { + /** Whether the item is disabled. */ + disabled?: boolean; + /** Whether the checkbox is checked. */ + checked?: boolean; + /** Keyboard shortcut to display (e.g., "⌘B"). */ + shortcut?: string; +} +/** + * Properties for +A menu item with a checkbox indicator. +@example +```html + + Show Bookmarks Bar + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnContextMenuCheckboxItem = createWebFComponent({ + tagName: 'flutter-shadcn-context-menu-checkbox-item', + displayName: 'FlutterShadcnContextMenuCheckboxItem', + // Map props to attributes + attributeProps: [ + 'disabled', + 'checked', + 'shortcut', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnContextMenuRadioGroupProps { + /** + * The value of the currently selected radio item. + */ + value?: string; + /** + * Fired when the selected value changes. + */ + onChange?: (event: CustomEvent) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnContextMenuRadioGroupElement extends WebFElementWithMethods<{ +}> { + /** The value of the currently selected radio item. */ + value?: string; +} +/** + * Properties for +A group of radio items where only one can be selected. +@example +```html + + People + + Pedro Duarte + Colm Tuite + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnContextMenuRadioGroup = createWebFComponent({ + tagName: 'flutter-shadcn-context-menu-radio-group', + displayName: 'FlutterShadcnContextMenuRadioGroup', + // Map props to attributes + attributeProps: [ + 'value', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: CustomEvent) => void) => (event: Event) => { + callback(event as CustomEvent); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnContextMenuRadioItemProps { + /** + * Whether the item is disabled. + */ + disabled?: boolean; + /** + * The value of this radio item. + */ + value?: string; + /** + * Keyboard shortcut to display. + */ + shortcut?: string; + /** + * Fired when the item is clicked. + */ + onClick?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnContextMenuRadioItemElement extends WebFElementWithMethods<{ +}> { + /** Whether the item is disabled. */ + disabled?: boolean; + /** The value of this radio item. */ + value?: string; + /** Keyboard shortcut to display. */ + shortcut?: string; +} +/** + * Properties for +A radio-style menu item within a radio group. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnContextMenuRadioItem = createWebFComponent({ + tagName: 'flutter-shadcn-context-menu-radio-item', + displayName: 'FlutterShadcnContextMenuRadioItem', + // Map props to attributes + attributeProps: [ + 'disabled', + 'value', + 'shortcut', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onClick', + eventName: 'click', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/date_picker.tsx b/packages/react-shadcn-ui/src/lib/src/components/date_picker.tsx new file mode 100644 index 0000000000..64acd6ce13 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/date_picker.tsx @@ -0,0 +1,112 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +/** + * Detail for date picker change event. + */ +interface FlutterShadcnDatePickerChangeEventDetail { + /** + * The selected date in ISO format (YYYY-MM-DD). + */ + value: string; +} +export interface FlutterShadcnDatePickerProps { + /** + * Selected date in ISO format (YYYY-MM-DD). + */ + value?: string; + /** + * Placeholder text when no date is selected. + */ + placeholder?: string; + /** + * Disable the picker. + */ + disabled?: boolean; + /** + * Date format for display. + * Default: 'yyyy-MM-dd' + */ + format?: string; + /** + * Fired when date selection changes. Detail contains the selected value. + */ + onChange?: (event: CustomEvent) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnDatePickerElement extends WebFElementWithMethods<{ +}> { + /** Selected date in ISO format (YYYY-MM-DD). */ + value?: string; + /** Placeholder text when no date is selected. */ + placeholder?: string; + /** Disable the picker. */ + disabled?: boolean; + /** Date format for display. */ + format?: string; +} +/** + * Properties for +A date picker with a popover calendar. +@example +```html + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnDatePicker = createWebFComponent({ + tagName: 'flutter-shadcn-date-picker', + displayName: 'FlutterShadcnDatePicker', + // Map props to attributes + attributeProps: [ + 'value', + 'placeholder', + 'disabled', + 'format', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: CustomEvent) => void) => (event: Event) => { + callback(event as CustomEvent); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/dialog.tsx b/packages/react-shadcn-ui/src/lib/src/components/dialog.tsx new file mode 100644 index 0000000000..4654d2f30a --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/dialog.tsx @@ -0,0 +1,371 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnDialogProps { + /** + * Whether the dialog is open. + */ + open?: boolean; + /** + * Close when clicking outside the dialog. + * Default: true + */ + closeOnOutsideClick?: boolean; + /** + * Fired when dialog opens. + */ + onOpen?: (event: Event) => void; + /** + * Fired when dialog closes. + */ + onClose?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnDialogElement extends WebFElementWithMethods<{ +}> { + /** Whether the dialog is open. */ + open?: boolean; + /** Close when clicking outside the dialog. */ + closeOnOutsideClick?: boolean; +} +/** + * Properties for +A modal dialog component. +@example +```html + + + Are you sure? + + This action cannot be undone. + + + + Dialog content here. + + + Cancel + Continue + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnDialog = createWebFComponent({ + tagName: 'flutter-shadcn-dialog', + displayName: 'FlutterShadcnDialog', + // Map props to attributes + attributeProps: [ + 'open', + 'closeOnOutsideClick', + ], + // Convert prop names to attribute names if needed + attributeMap: { + closeOnOutsideClick: 'close-on-outside-click', + }, + // Event handlers + events: [ + { + propName: 'onOpen', + eventName: 'open', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onClose', + eventName: 'close', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnDialogHeaderProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnDialogHeaderElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Header slot for dialog. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnDialogHeader = createWebFComponent({ + tagName: 'flutter-shadcn-dialog-header', + displayName: 'FlutterShadcnDialogHeader', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnDialogTitleProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnDialogTitleElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Title within dialog header. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnDialogTitle = createWebFComponent({ + tagName: 'flutter-shadcn-dialog-title', + displayName: 'FlutterShadcnDialogTitle', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnDialogDescriptionProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnDialogDescriptionElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Description within dialog header. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnDialogDescription = createWebFComponent({ + tagName: 'flutter-shadcn-dialog-description', + displayName: 'FlutterShadcnDialogDescription', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnDialogContentProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnDialogContentElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Main content slot for dialog. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnDialogContent = createWebFComponent({ + tagName: 'flutter-shadcn-dialog-content', + displayName: 'FlutterShadcnDialogContent', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnDialogFooterProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnDialogFooterElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Footer slot for dialog actions. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnDialogFooter = createWebFComponent({ + tagName: 'flutter-shadcn-dialog-footer', + displayName: 'FlutterShadcnDialogFooter', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/dropdown_menu.tsx b/packages/react-shadcn-ui/src/lib/src/components/dropdown_menu.tsx new file mode 100644 index 0000000000..9e0cd374e3 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/dropdown_menu.tsx @@ -0,0 +1,376 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnDropdownMenuProps { + /** + * Whether the menu is open. + */ + open?: boolean; + /** + * Fired when menu opens. + */ + onOpen?: (event: Event) => void; + /** + * Fired when menu closes. + */ + onClose?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnDropdownMenuElement extends WebFElementWithMethods<{ +}> { + /** Whether the menu is open. */ + open?: boolean; +} +/** + * Properties for +A dropdown menu component. +@example +```html + + + Open Menu + + + Profile + Settings + + Logout + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnDropdownMenu = createWebFComponent({ + tagName: 'flutter-shadcn-dropdown-menu', + displayName: 'FlutterShadcnDropdownMenu', + // Map props to attributes + attributeProps: [ + 'open', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onOpen', + eventName: 'open', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onClose', + eventName: 'close', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnDropdownMenuTriggerProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnDropdownMenuTriggerElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Trigger element for the menu. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnDropdownMenuTrigger = createWebFComponent({ + tagName: 'flutter-shadcn-dropdown-menu-trigger', + displayName: 'FlutterShadcnDropdownMenuTrigger', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnDropdownMenuContentProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnDropdownMenuContentElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Container for menu items. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnDropdownMenuContent = createWebFComponent({ + tagName: 'flutter-shadcn-dropdown-menu-content', + displayName: 'FlutterShadcnDropdownMenuContent', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnDropdownMenuItemProps { + /** + * Disable this menu item. + */ + disabled?: boolean; + /** + * Fired when item is selected. + */ + onSelect?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnDropdownMenuItemElement extends WebFElementWithMethods<{ +}> { + /** Disable this menu item. */ + disabled?: boolean; +} +/** + * Properties for +Individual menu item. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnDropdownMenuItem = createWebFComponent({ + tagName: 'flutter-shadcn-dropdown-menu-item', + displayName: 'FlutterShadcnDropdownMenuItem', + // Map props to attributes + attributeProps: [ + 'disabled', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onSelect', + eventName: 'select', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnDropdownMenuSeparatorProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnDropdownMenuSeparatorElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Visual separator between items. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnDropdownMenuSeparator = createWebFComponent({ + tagName: 'flutter-shadcn-dropdown-menu-separator', + displayName: 'FlutterShadcnDropdownMenuSeparator', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnDropdownMenuLabelProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnDropdownMenuLabelElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Label/header for a group of items. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnDropdownMenuLabel = createWebFComponent({ + tagName: 'flutter-shadcn-dropdown-menu-label', + displayName: 'FlutterShadcnDropdownMenuLabel', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/form.tsx b/packages/react-shadcn-ui/src/lib/src/components/form.tsx new file mode 100644 index 0000000000..44b9fcc275 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/form.tsx @@ -0,0 +1,572 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +/** + * Methods available on + */ +interface FlutterShadcnFormMethods { + /** + * Validate the form without saving. + * + * @returns true if the form is valid, false otherwise + */ + validate(): boolean; + /** + * Save and validate the form. Dispatches 'submit' event if valid. + * + * @returns true if the form is valid and submitted, false otherwise + */ + submit(): boolean; + /** + * Reset the form to its initial values. Dispatches 'reset' event. + */ + reset(): void; + /** + * Get the value of a specific form field. + * + * @param fieldId - The id of the field + * @returns The field's current value + */ + getFieldValue(fieldId: string): any; + /** + * Set the value of a specific form field. + * + * @param fieldId - The id of the field + * @param value - The value to set + */ + setFieldValue(fieldId: string, value: any): void; + /** + * Set an error message for a specific form field. + * + * @param fieldId - The id of the field + * @param error - The error message, or null to clear the error + */ + setFieldError(fieldId: string, error: string | null): void; +} +export interface FlutterShadcnFormProps { + /** + * Disable all form fields. + */ + disabled?: boolean; + /** + * Auto-validation mode for the form. + * + * Options: + * - 'disabled': No auto validation + * - 'always': Always validate + * - 'onUserInteraction': Validate after user interaction + * - 'alwaysAfterFirstValidation': Validate always after first validation (default) + */ + autoValidateMode?: 'disabled' | 'always' | 'onUserInteraction' | 'alwaysAfterFirstValidation'; + /** + * Current form values as a JSON string. + * + * Get: Returns all form field values as a JSON string. + * Set: Accepts a JSON string or object to set form values. + */ + value?: string; + /** + * Fired when form is successfully submitted (after validation passes). + * The event detail contains the form values. + */ + onSubmit?: (event: CustomEvent) => void; + /** + * Fired when form is reset. + */ + onReset?: (event: Event) => void; + /** + * Fired when any form field value changes. + */ + onChange?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +/** + * Element interface with methods/properties accessible via ref + * @example + * ```tsx + * const ref = useRef(null); + * // Call methods on the element + * ref.current?.finishRefresh('success'); + * // Access properties + * console.log(ref.current?.disabled); + * ``` + */ +export interface FlutterShadcnFormElement extends WebFElementWithMethods<{ + /** + * Validate the form without saving. + * + * @returns true if the form is valid, false otherwise + */ + validate(): boolean; + /** + * Save and validate the form. Dispatches 'submit' event if valid. + * + * @returns true if the form is valid and submitted, false otherwise + */ + submit(): boolean; + /** + * Reset the form to its initial values. Dispatches 'reset' event. + */ + reset(): void; + /** + * Get the value of a specific form field. + * + * @param fieldId - The id of the field + * @returns The field's current value + */ + getFieldValue(fieldId: string): any; + /** + * Set the value of a specific form field. + * + * @param fieldId - The id of the field + * @param value - The value to set + */ + setFieldValue(fieldId: string, value: any): void; + /** + * Set an error message for a specific form field. + * + * @param fieldId - The id of the field + * @param error - The error message, or null to clear the error + */ + setFieldError(fieldId: string, error: string | null): void; +}> { + /** Disable all form fields. */ + disabled?: boolean; + /** Auto-validation mode for the form. */ + autoValidateMode?: 'disabled' | 'always' | 'onUserInteraction' | 'alwaysAfterFirstValidation'; + /** Current form values as a JSON string. */ + value?: string; +} +/** + * Properties for +A form container that manages form state and validation using shadcn_ui's ShadForm. +@example +```html + + + + Submit + +``` +@example +```javascript +const form = document.getElementById('myForm'); +// Submit form and get values +if (form.submit()) { + const values = JSON.parse(form.value); + console.log('Form values:', values); +} +// Set form values +form.value = JSON.stringify({ username: 'john', email: 'john@example.com' }); +// Get/set individual field values +form.setFieldValue('username', 'jane'); +console.log(form.getFieldValue('username')); +// Set field errors +form.setFieldError('email', 'Invalid email format'); +// Reset form +form.reset(); +``` + * + * @example + * ```tsx + * const ref = useRef(null); + * + * + * Content + * + * + * // Call methods on the element + * ref.current?.finishRefresh('success'); + * ``` + */ +export const FlutterShadcnForm = createWebFComponent({ + tagName: 'flutter-shadcn-form', + displayName: 'FlutterShadcnForm', + // Map props to attributes + attributeProps: [ + 'disabled', + 'autoValidateMode', + 'value', + ], + // Convert prop names to attribute names if needed + attributeMap: { + autoValidateMode: 'auto-validate-mode', + }, + // Event handlers + events: [ + { + propName: 'onSubmit', + eventName: 'submit', + handler: (callback: (event: CustomEvent) => void) => (event: Event) => { + callback(event as CustomEvent); + }, + }, + { + propName: 'onReset', + eventName: 'reset', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnFormFieldProps { + /** + * Unique identifier for the field within the form. + * Used to access field values via form.getFieldValue(fieldId). + */ + fieldId?: string; + /** + * Label text displayed above the field. + */ + label?: string; + /** + * Description text shown below the field. + */ + description?: string; + /** + * Error message to display. Overrides validation errors. + */ + error?: string; + /** + * Whether this field is required. + * Adds a "*" indicator and validates that the field is not empty. + */ + required?: boolean; + /** + * Input type (used when no children are provided). + * Default: 'text' + */ + type?: string; + /** + * Placeholder text for the input field. + */ + placeholder?: string; + /** + * Initial value for the field. + */ + initialValue?: string; + /** + * Fired when the field value changes. + * The event detail contains the new value. + */ + onChange?: (event: CustomEvent) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnFormFieldElement extends WebFElementWithMethods<{ +}> { + /** Unique identifier for the field within the form. */ + fieldId?: string; + /** Label text displayed above the field. */ + label?: string; + /** Description text shown below the field. */ + description?: string; + /** Error message to display. Overrides validation errors. */ + error?: string; + /** Whether this field is required. */ + required?: boolean; + /** Input type (used when no children are provided). */ + type?: string; + /** Placeholder text for the input field. */ + placeholder?: string; + /** Initial value for the field. */ + initialValue?: string; +} +/** + * Properties for +A form field that automatically integrates with the parent form. +When used without children, renders a ShadInputFormField. +When used with children, wraps them with label, description, and error display. +@example +```html + + + + + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnFormField = createWebFComponent({ + tagName: 'flutter-shadcn-form-field', + displayName: 'FlutterShadcnFormField', + // Map props to attributes + attributeProps: [ + 'fieldId', + 'label', + 'description', + 'error', + 'required', + 'type', + 'placeholder', + 'initialValue', + ], + // Convert prop names to attribute names if needed + attributeMap: { + fieldId: 'field-id', + initialValue: 'initial-value', + }, + // Event handlers + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: CustomEvent) => void) => (event: Event) => { + callback(event as CustomEvent); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnFormLabelProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnFormLabelElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Label element for form fields. +Typically used inside a custom form field layout. +@example +```html +Username +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnFormLabel = createWebFComponent({ + tagName: 'flutter-shadcn-form-label', + displayName: 'FlutterShadcnFormLabel', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnFormDescriptionProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnFormDescriptionElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Description text for form fields. +Provides additional context or instructions. +@example +```html + + This is your public display name. + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnFormDescription = createWebFComponent({ + tagName: 'flutter-shadcn-form-description', + displayName: 'FlutterShadcnFormDescription', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnFormMessageProps { + /** + * Type of message which determines the styling. + * + * Options: + * - 'error': Red text for validation errors (default) + * - 'success': Green text for success messages + * - 'info': Primary color for informational messages + */ + type?: 'error' | 'success' | 'info'; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnFormMessageElement extends WebFElementWithMethods<{ +}> { + /** Type of message which determines the styling. */ + type?: 'error' | 'success' | 'info'; +} +/** + * Properties for +Validation or status message for form fields. +@example +```html + + This field is required + + + Email is available + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnFormMessage = createWebFComponent({ + tagName: 'flutter-shadcn-form-message', + displayName: 'FlutterShadcnFormMessage', + // Map props to attributes + attributeProps: [ + 'type', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/icon_button.tsx b/packages/react-shadcn-ui/src/lib/src/components/icon_button.tsx new file mode 100644 index 0000000000..124e14a312 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/icon_button.tsx @@ -0,0 +1,154 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnIconButtonProps { + /** + * Visual variant of the icon button. + * - 'primary': Primary filled button (default) + * - 'secondary': Secondary muted button + * - 'destructive': Red destructive action button + * - 'outline': Bordered outline button + * - 'ghost': Transparent with hover effect + * + * Note: Unlike regular buttons, icon buttons do not support the 'link' variant. + * + * Default: 'primary' + */ + variant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'ghost'; + /** + * Name of the Lucide icon to display. + * + * Common icons include: + * - Navigation: 'chevron-right', 'chevron-left', 'arrow-right', 'arrow-left' + * - Actions: 'plus', 'minus', 'x', 'check', 'search', 'edit', 'trash', 'copy', 'share' + * - Objects: 'rocket', 'heart', 'star', 'home', 'user', 'mail', 'calendar', 'file' + * - UI: 'menu', 'ellipsis', 'more-vertical', 'grid', 'list', 'filter' + * - Status: 'info', 'alert-circle', 'help', 'check-circle', 'x-circle' + * - Formatting: 'bold', 'italic', 'underline', 'align-left', 'align-center', 'align-right' + * + * @example "rocket" + * @example "plus" + * @example "chevron-right" + */ + icon?: string; + /** + * Size of the icon in pixels. + * + * Default: 16 + */ + iconSize?: number; + /** + * Disable button interactions. + * + * When disabled, the button will not respond to clicks and will have a muted appearance. + */ + disabled?: boolean; + /** + * Show loading spinner and disable interactions. + * + * When loading, the icon is replaced with a circular progress indicator. + */ + loading?: boolean; + /** + * Fired when the button is pressed. + * Not emitted when disabled or loading. + */ + onClick?: (event: Event) => void; + /** + * Fired when the button is long-pressed. + * Not emitted when disabled or loading. + */ + onLongpress?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnIconButtonElement extends WebFElementWithMethods<{ +}> { + /** Visual variant of the icon button. */ + variant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'ghost'; + /** Name of the Lucide icon to display. */ + icon?: string; + /** Size of the icon in pixels. */ + iconSize?: number; + /** Disable button interactions. */ + disabled?: boolean; + /** Show loading spinner and disable interactions. */ + loading?: boolean; +} +/** + * Properties for +An icon-only button component with multiple variants. +Unlike the regular button, this is specifically designed for icon-only use cases. +@example +```html + +``` +@example With gradient and shadow (via CSS) +```html + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnIconButton = createWebFComponent({ + tagName: 'flutter-shadcn-icon-button', + displayName: 'FlutterShadcnIconButton', + // Map props to attributes + attributeProps: [ + 'variant', + 'icon', + 'iconSize', + 'disabled', + 'loading', + ], + // Convert prop names to attribute names if needed + attributeMap: { + iconSize: 'icon-size', + }, + // Event handlers + events: [ + { + propName: 'onClick', + eventName: 'click', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onLongpress', + eventName: 'longpress', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/image.tsx b/packages/react-shadcn-ui/src/lib/src/components/image.tsx new file mode 100644 index 0000000000..b16e7082bd --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/image.tsx @@ -0,0 +1,123 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnImageProps { + /** + * Image source URL. + */ + src?: string; + /** + * Alt text for accessibility. + */ + alt?: string; + /** + * Image width in pixels. + */ + width?: string; + /** + * Image height in pixels. + */ + height?: string; + /** + * How to fit the image. + * Options: 'contain', 'cover', 'fill', 'none', 'scaleDown' + * Default: 'cover' + */ + fit?: string; + /** + * Fired when image loads successfully. + */ + onLoad?: (event: Event) => void; + /** + * Fired when image fails to load. + */ + onError?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnImageElement extends WebFElementWithMethods<{ +}> { + /** Image source URL. */ + src?: string; + /** Alt text for accessibility. */ + alt?: string; + /** Image width in pixels. */ + width?: string; + /** Image height in pixels. */ + height?: string; + /** How to fit the image. */ + fit?: string; +} +/** + * Properties for +An image component with loading and error states. +@example +```html + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnImage = createWebFComponent({ + tagName: 'flutter-shadcn-image', + displayName: 'FlutterShadcnImage', + // Map props to attributes + attributeProps: [ + 'src', + 'alt', + 'width', + 'height', + 'fit', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onLoad', + eventName: 'load', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onError', + eventName: 'error', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/input.tsx b/packages/react-shadcn-ui/src/lib/src/components/input.tsx new file mode 100644 index 0000000000..5be84070d2 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/input.tsx @@ -0,0 +1,268 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnInputProps { + /** + * Current value of the input. + */ + value?: string; + /** + * Placeholder text shown when empty. + */ + placeholder?: string; + /** + * Input type. + * Options: 'text', 'password', 'email', 'number', 'tel', 'url', 'search' + * Default: 'text' + */ + type?: string; + /** + * Disable the input. + */ + disabled?: boolean; + /** + * Make the input read-only. + */ + readonly?: boolean; + /** + * Maximum length of the input value. + */ + maxlength?: string; + /** + * Minimum length of the input value. + */ + minlength?: string; + /** + * Pattern for validation (regex). + */ + pattern?: string; + /** + * Whether the input is required. + */ + required?: boolean; + /** + * Autofocus on mount. + */ + autofocus?: boolean; + /** + * Text alignment within the input. + * Options: 'start', 'end', 'left', 'right', 'center' + * Default: 'start' + */ + textalign?: string; + /** + * Controls automatic text capitalization behavior. + * Options: 'none', 'sentences', 'words', 'characters' + * Default: 'none' + */ + autocapitalize?: string; + /** + * Whether to enable autocorrect. + * Default: true + */ + autocorrect?: boolean; + /** + * Whether to show input suggestions. + * Default: true + */ + enablesuggestions?: boolean; + /** + * Hint for the keyboard action button. + * Options: 'done', 'go', 'next', 'search', 'send', 'previous', 'newline' + */ + enterkeyhint?: string; + /** + * Maximum number of lines for the input. Use for multi-line input. + */ + maxlines?: string; + /** + * Minimum number of lines for the input. + */ + minlines?: string; + /** + * Color of the cursor (e.g., '#FF0000', 'red'). + */ + cursorcolor?: string; + /** + * Color of the text selection highlight (e.g., '#0000FF', 'blue'). + */ + selectioncolor?: string; + /** + * Character used to obscure text in password mode. + * Default: '•' + */ + obscuringcharacter?: string; + /** + * Fired on every input change. + */ + onInput?: (event: Event) => void; + /** + * Fired when input loses focus. + */ + onChange?: (event: Event) => void; + /** + * Fired when input gains focus. + */ + onFocus?: (event: Event) => void; + /** + * Fired when input loses focus. + */ + onBlur?: (event: Event) => void; + /** + * Fired when the user submits the input (e.g., presses Enter/Done). + */ + onSubmit?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnInputElement extends WebFElementWithMethods<{ +}> { + /** Current value of the input. */ + value?: string; + /** Placeholder text shown when empty. */ + placeholder?: string; + /** Input type. */ + type?: string; + /** Disable the input. */ + disabled?: boolean; + /** Make the input read-only. */ + readonly?: boolean; + /** Maximum length of the input value. */ + maxlength?: string; + /** Minimum length of the input value. */ + minlength?: string; + /** Pattern for validation (regex). */ + pattern?: string; + /** Whether the input is required. */ + required?: boolean; + /** Autofocus on mount. */ + autofocus?: boolean; + /** Text alignment within the input. */ + textalign?: string; + /** Controls automatic text capitalization behavior. */ + autocapitalize?: string; + /** Whether to enable autocorrect. */ + autocorrect?: boolean; + /** Whether to show input suggestions. */ + enablesuggestions?: boolean; + /** Hint for the keyboard action button. */ + enterkeyhint?: string; + /** Maximum number of lines for the input. Use for multi-line input. */ + maxlines?: string; + /** Minimum number of lines for the input. */ + minlines?: string; + /** Color of the cursor (e.g., '#FF0000', 'red'). */ + cursorcolor?: string; + /** Color of the text selection highlight (e.g., '#0000FF', 'blue'). */ + selectioncolor?: string; + /** Character used to obscure text in password mode. */ + obscuringcharacter?: string; +} +/** + * Properties for +A styled text input field. +@example +```html + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnInput = createWebFComponent({ + tagName: 'flutter-shadcn-input', + displayName: 'FlutterShadcnInput', + // Map props to attributes + attributeProps: [ + 'value', + 'placeholder', + 'type', + 'disabled', + 'readonly', + 'maxlength', + 'minlength', + 'pattern', + 'required', + 'autofocus', + 'textalign', + 'autocapitalize', + 'autocorrect', + 'enablesuggestions', + 'enterkeyhint', + 'maxlines', + 'minlines', + 'cursorcolor', + 'selectioncolor', + 'obscuringcharacter', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onInput', + eventName: 'input', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onFocus', + eventName: 'focus', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onBlur', + eventName: 'blur', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onSubmit', + eventName: 'submit', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/popover.tsx b/packages/react-shadcn-ui/src/lib/src/components/popover.tsx new file mode 100644 index 0000000000..733efda2de --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/popover.tsx @@ -0,0 +1,217 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnPopoverProps { + /** + * Whether the popover is open. + */ + open?: boolean; + /** + * Placement of the popover. + * Options: 'top', 'bottom', 'left', 'right' + * Default: 'bottom' + */ + placement?: string; + /** + * Close when clicking outside. + * Default: true + */ + closeOnOutsideClick?: boolean; + /** + * Fired when popover opens. + */ + onOpen?: (event: Event) => void; + /** + * Fired when popover closes. + */ + onClose?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnPopoverElement extends WebFElementWithMethods<{ +}> { + /** Whether the popover is open. */ + open?: boolean; + /** Placement of the popover. */ + placement?: string; + /** Close when clicking outside. */ + closeOnOutsideClick?: boolean; +} +/** + * Properties for +A floating content container that appears on trigger. +@example +```html + + + Open + + + Popover content here. + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnPopover = createWebFComponent({ + tagName: 'flutter-shadcn-popover', + displayName: 'FlutterShadcnPopover', + // Map props to attributes + attributeProps: [ + 'open', + 'placement', + 'closeOnOutsideClick', + ], + // Convert prop names to attribute names if needed + attributeMap: { + closeOnOutsideClick: 'close-on-outside-click', + }, + // Event handlers + events: [ + { + propName: 'onOpen', + eventName: 'open', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onClose', + eventName: 'close', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnPopoverTriggerProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnPopoverTriggerElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Trigger element for the popover. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnPopoverTrigger = createWebFComponent({ + tagName: 'flutter-shadcn-popover-trigger', + displayName: 'FlutterShadcnPopoverTrigger', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnPopoverContentProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnPopoverContentElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Content of the popover. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnPopoverContent = createWebFComponent({ + tagName: 'flutter-shadcn-popover-content', + displayName: 'FlutterShadcnPopoverContent', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/progress.tsx b/packages/react-shadcn-ui/src/lib/src/components/progress.tsx new file mode 100644 index 0000000000..7e7da004b1 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/progress.tsx @@ -0,0 +1,83 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnProgressProps { + /** + * Current progress value. + */ + value?: string; + /** + * Maximum value. + * Default: 100 + */ + max?: string; + /** + * Visual variant. + * Options: 'default', 'indeterminate' + * Default: 'default' + */ + variant?: string; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnProgressElement extends WebFElementWithMethods<{ +}> { + /** Current progress value. */ + value?: string; + /** Maximum value. */ + max?: string; + /** Visual variant. */ + variant?: string; +} +/** + * Properties for +A progress indicator component. +@example +```html + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnProgress = createWebFComponent({ + tagName: 'flutter-shadcn-progress', + displayName: 'FlutterShadcnProgress', + // Map props to attributes + attributeProps: [ + 'value', + 'max', + 'variant', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/radio.tsx b/packages/react-shadcn-ui/src/lib/src/components/radio.tsx new file mode 100644 index 0000000000..f8f3ab01ff --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/radio.tsx @@ -0,0 +1,162 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnRadioProps { + /** + * Currently selected value. + */ + value?: string; + /** + * Disable all radio items. + */ + disabled?: boolean; + /** + * Orientation of the radio group. + * Options: 'horizontal', 'vertical' + * Default: 'vertical' + */ + orientation?: string; + /** + * Fired when selection changes. + */ + onChange?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnRadioElement extends WebFElementWithMethods<{ +}> { + /** Currently selected value. */ + value?: string; + /** Disable all radio items. */ + disabled?: boolean; + /** Orientation of the radio group. */ + orientation?: string; +} +/** + * Properties for +Container for radio button groups. +@example +```html + + Option 1 + Option 2 + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnRadio = createWebFComponent({ + tagName: 'flutter-shadcn-radio', + displayName: 'FlutterShadcnRadio', + // Map props to attributes + attributeProps: [ + 'value', + 'disabled', + 'orientation', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnRadioItemProps { + /** + * Value of this radio option. + */ + value: string; + /** + * Disable this specific radio item. + */ + disabled?: boolean; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnRadioItemElement extends WebFElementWithMethods<{ +}> { + /** Value of this radio option. */ + value: string; + /** Disable this specific radio item. */ + disabled?: boolean; +} +/** + * Properties for +Individual radio button option. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnRadioItem = createWebFComponent({ + tagName: 'flutter-shadcn-radio-item', + displayName: 'FlutterShadcnRadioItem', + // Map props to attributes + attributeProps: [ + 'value', + 'disabled', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/scroll_area.tsx b/packages/react-shadcn-ui/src/lib/src/components/scroll_area.tsx new file mode 100644 index 0000000000..4d5ea6310f --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/scroll_area.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnScrollAreaProps { + /** + * Orientation of the scroll area. + * Options: 'vertical', 'horizontal', 'both' + * Default: 'vertical' + */ + orientation?: string; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnScrollAreaElement extends WebFElementWithMethods<{ +}> { + /** Orientation of the scroll area. */ + orientation?: string; +} +/** + * Properties for +A scrollable area with styled scrollbars. +@example +```html + +
Scrollable content...
+
+``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnScrollArea = createWebFComponent({ + tagName: 'flutter-shadcn-scroll-area', + displayName: 'FlutterShadcnScrollArea', + // Map props to attributes + attributeProps: [ + 'orientation', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/select.tsx b/packages/react-shadcn-ui/src/lib/src/components/select.tsx new file mode 100644 index 0000000000..3d7533cd05 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/select.tsx @@ -0,0 +1,540 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnSelectProps { + /** + * Currently selected value. + */ + value?: string; + /** + * Placeholder text when no value is selected. + */ + placeholder?: string; + /** + * Disable the select. + */ + disabled?: boolean; + /** + * Allow multiple selection. + */ + multiple?: boolean; + /** + * Allow searching/filtering options. + */ + searchable?: boolean; + /** + * Placeholder for search input when searchable. + */ + searchPlaceholder?: string; + /** + * Allow deselecting the current selection. + */ + allowDeselection?: boolean; + /** + * Whether to close the popover after selecting an option. + * Default: true for single select, configurable for multiple. + */ + closeOnSelect?: boolean; + /** + * Fired when selection changes. + * Event detail contains { value: string }. + */ + onChange?: (event: CustomEvent<{ value: string }>) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSelectElement extends WebFElementWithMethods<{ +}> { + /** Currently selected value. */ + value?: string; + /** Placeholder text when no value is selected. */ + placeholder?: string; + /** Disable the select. */ + disabled?: boolean; + /** Allow multiple selection. */ + multiple?: boolean; + /** Allow searching/filtering options. */ + searchable?: boolean; + /** Placeholder for search input when searchable. */ + searchPlaceholder?: string; + /** Allow deselecting the current selection. */ + allowDeselection?: boolean; + /** Whether to close the popover after selecting an option. */ + closeOnSelect?: boolean; +} +/** + * Properties for +A dropdown select component with support for single/multiple selection and search. +@example +```html + + + Apple + Banana + Orange + + + + + + Apple + Banana + + + + + + Eastern Standard Time + Central Standard Time + + + Greenwich Mean Time + Central European Time + + + + + React + Vue + Angular + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSelect = createWebFComponent({ + tagName: 'flutter-shadcn-select', + displayName: 'FlutterShadcnSelect', + // Map props to attributes + attributeProps: [ + 'value', + 'placeholder', + 'disabled', + 'multiple', + 'searchable', + 'searchPlaceholder', + 'allowDeselection', + 'closeOnSelect', + ], + // Convert prop names to attribute names if needed + attributeMap: { + searchPlaceholder: 'search-placeholder', + allowDeselection: 'allow-deselection', + closeOnSelect: 'close-on-select', + }, + // Event handlers + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: CustomEvent<{ value: string }>) => void) => (event: Event) => { + callback(event as CustomEvent<{ value: string }>); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnSelectTriggerProps { + /** + * Placeholder text when no value is selected. + */ + placeholder?: string; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSelectTriggerElement extends WebFElementWithMethods<{ +}> { + /** Placeholder text when no value is selected. */ + placeholder?: string; +} +/** + * Properties for +The trigger element that displays the selected value and opens the dropdown. +@example +```html + + + + ... + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSelectTrigger = createWebFComponent({ + tagName: 'flutter-shadcn-select-trigger', + displayName: 'FlutterShadcnSelectTrigger', + // Map props to attributes + attributeProps: [ + 'placeholder', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnSelectContentProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSelectContentElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Container for the dropdown content/options. +@example +```html + + Option 1 + Option 2 + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSelectContent = createWebFComponent({ + tagName: 'flutter-shadcn-select-content', + displayName: 'FlutterShadcnSelectContent', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnSelectItemProps { + /** + * Value of this option. + */ + value: string; + /** + * Disable this specific option. + */ + disabled?: boolean; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSelectItemElement extends WebFElementWithMethods<{ +}> { + /** Value of this option. */ + value: string; + /** Disable this specific option. */ + disabled?: boolean; +} +/** + * Properties for +Individual select option. +@example +```html +Apple +Banana (out of stock) +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSelectItem = createWebFComponent({ + tagName: 'flutter-shadcn-select-item', + displayName: 'FlutterShadcnSelectItem', + // Map props to attributes + attributeProps: [ + 'value', + 'disabled', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnSelectGroupProps { + /** + * Label for this option group. + */ + label?: string; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSelectGroupElement extends WebFElementWithMethods<{ +}> { + /** Label for this option group. */ + label?: string; +} +/** + * Properties for +Group of select options with optional label. +@example +```html + + Apple + Banana + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSelectGroup = createWebFComponent({ + tagName: 'flutter-shadcn-select-group', + displayName: 'FlutterShadcnSelectGroup', + // Map props to attributes + attributeProps: [ + 'label', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnSelectLabelProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSelectLabelElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +A label/header for a section of options (not inside a group). +@example +```html +Fruits +Apple +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSelectLabel = createWebFComponent({ + tagName: 'flutter-shadcn-select-label', + displayName: 'FlutterShadcnSelectLabel', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnSelectSeparatorProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSelectSeparatorElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Visual separator between options. +@example +```html +Option 1 + +Option 2 +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSelectSeparator = createWebFComponent({ + tagName: 'flutter-shadcn-select-separator', + displayName: 'FlutterShadcnSelectSeparator', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/separator.tsx b/packages/react-shadcn-ui/src/lib/src/components/separator.tsx new file mode 100644 index 0000000000..aa1e1b10d1 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/separator.tsx @@ -0,0 +1,68 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnSeparatorProps { + /** + * Orientation of the separator. + * Options: 'horizontal', 'vertical' + * Default: 'horizontal' + */ + orientation?: string; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSeparatorElement extends WebFElementWithMethods<{ +}> { + /** Orientation of the separator. */ + orientation?: string; +} +/** + * Properties for +A visual separator/divider component. +@example +```html + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSeparator = createWebFComponent({ + tagName: 'flutter-shadcn-separator', + displayName: 'FlutterShadcnSeparator', + // Map props to attributes + attributeProps: [ + 'orientation', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/sheet.tsx b/packages/react-shadcn-ui/src/lib/src/components/sheet.tsx new file mode 100644 index 0000000000..b9142e8bac --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/sheet.tsx @@ -0,0 +1,373 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnSheetProps { + /** + * Whether the sheet is open. + */ + open?: boolean; + /** + * Side from which the sheet appears. + * Options: 'top', 'bottom', 'left', 'right' + * Default: 'right' + */ + side?: string; + /** + * Close when clicking outside. + * Default: true + */ + closeOnOutsideClick?: boolean; + /** + * Fired when sheet opens. + */ + onOpen?: (event: Event) => void; + /** + * Fired when sheet closes. + */ + onClose?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSheetElement extends WebFElementWithMethods<{ +}> { + /** Whether the sheet is open. */ + open?: boolean; + /** Side from which the sheet appears. */ + side?: string; + /** Close when clicking outside. */ + closeOnOutsideClick?: boolean; +} +/** + * Properties for +A slide-out panel component. +@example +```html + + + Settings + + + Sheet content here. + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSheet = createWebFComponent({ + tagName: 'flutter-shadcn-sheet', + displayName: 'FlutterShadcnSheet', + // Map props to attributes + attributeProps: [ + 'open', + 'side', + 'closeOnOutsideClick', + ], + // Convert prop names to attribute names if needed + attributeMap: { + closeOnOutsideClick: 'close-on-outside-click', + }, + // Event handlers + events: [ + { + propName: 'onOpen', + eventName: 'open', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onClose', + eventName: 'close', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnSheetHeaderProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSheetHeaderElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Header slot for sheet. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSheetHeader = createWebFComponent({ + tagName: 'flutter-shadcn-sheet-header', + displayName: 'FlutterShadcnSheetHeader', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnSheetTitleProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSheetTitleElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Title within sheet header. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSheetTitle = createWebFComponent({ + tagName: 'flutter-shadcn-sheet-title', + displayName: 'FlutterShadcnSheetTitle', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnSheetDescriptionProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSheetDescriptionElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Description within sheet header. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSheetDescription = createWebFComponent({ + tagName: 'flutter-shadcn-sheet-description', + displayName: 'FlutterShadcnSheetDescription', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnSheetContentProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSheetContentElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Main content slot for sheet. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSheetContent = createWebFComponent({ + tagName: 'flutter-shadcn-sheet-content', + displayName: 'FlutterShadcnSheetContent', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnSheetFooterProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSheetFooterElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Footer slot for sheet. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSheetFooter = createWebFComponent({ + tagName: 'flutter-shadcn-sheet-footer', + displayName: 'FlutterShadcnSheetFooter', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/skeleton.tsx b/packages/react-shadcn-ui/src/lib/src/components/skeleton.tsx new file mode 100644 index 0000000000..e4bc0cdef2 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/skeleton.tsx @@ -0,0 +1,81 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnSkeletonProps { + /** + * Width of the skeleton in pixels. + */ + width?: string; + /** + * Height of the skeleton in pixels. + */ + height?: string; + /** + * Whether the skeleton is circular. + */ + circle?: boolean; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSkeletonElement extends WebFElementWithMethods<{ +}> { + /** Width of the skeleton in pixels. */ + width?: string; + /** Height of the skeleton in pixels. */ + height?: string; + /** Whether the skeleton is circular. */ + circle?: boolean; +} +/** + * Properties for +A skeleton loading placeholder. +@example +```html + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSkeleton = createWebFComponent({ + tagName: 'flutter-shadcn-skeleton', + displayName: 'FlutterShadcnSkeleton', + // Map props to attributes + attributeProps: [ + 'width', + 'height', + 'circle', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/slider.tsx b/packages/react-shadcn-ui/src/lib/src/components/slider.tsx new file mode 100644 index 0000000000..84a8d0f422 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/slider.tsx @@ -0,0 +1,134 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnSliderProps { + /** + * Current value of the slider. + */ + value?: string; + /** + * Minimum value. + * Default: 0 + */ + min?: string; + /** + * Maximum value. + * Default: 100 + */ + max?: string; + /** + * Step increment. + * Default: 1 + */ + step?: string; + /** + * Disable the slider. + */ + disabled?: boolean; + /** + * Orientation of the slider. + * Options: 'horizontal', 'vertical' + * Default: 'horizontal' + */ + orientation?: string; + /** + * Fired continuously while sliding. + */ + onInput?: (event: Event) => void; + /** + * Fired when sliding ends. + */ + onChange?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSliderElement extends WebFElementWithMethods<{ +}> { + /** Current value of the slider. */ + value?: string; + /** Minimum value. */ + min?: string; + /** Maximum value. */ + max?: string; + /** Step increment. */ + step?: string; + /** Disable the slider. */ + disabled?: boolean; + /** Orientation of the slider. */ + orientation?: string; +} +/** + * Properties for +A slider control for selecting a value from a range. +@example +```html + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSlider = createWebFComponent({ + tagName: 'flutter-shadcn-slider', + displayName: 'FlutterShadcnSlider', + // Map props to attributes + attributeProps: [ + 'value', + 'min', + 'max', + 'step', + 'disabled', + 'orientation', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onInput', + eventName: 'input', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/switch.tsx b/packages/react-shadcn-ui/src/lib/src/components/switch.tsx new file mode 100644 index 0000000000..21684c7685 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/switch.tsx @@ -0,0 +1,89 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnSwitchProps { + /** + * Whether the switch is on. + */ + checked?: boolean; + /** + * Disable the switch. + */ + disabled?: boolean; + /** + * Fired when the switch state changes. + */ + onChange?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnSwitchElement extends WebFElementWithMethods<{ +}> { + /** Whether the switch is on. */ + checked?: boolean; + /** Disable the switch. */ + disabled?: boolean; +} +/** + * Properties for +A toggle switch control. +@example +```html + + Enable notifications + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnSwitch = createWebFComponent({ + tagName: 'flutter-shadcn-switch', + displayName: 'FlutterShadcnSwitch', + // Map props to attributes + attributeProps: [ + 'checked', + 'disabled', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/table.tsx b/packages/react-shadcn-ui/src/lib/src/components/table.tsx new file mode 100644 index 0000000000..d6d388a962 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/table.tsx @@ -0,0 +1,327 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnTableProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnTableElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +A data table component. +@example +```html + + + + Name + Status + + + + + John + Active + + + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnTable = createWebFComponent({ + tagName: 'flutter-shadcn-table', + displayName: 'FlutterShadcnTable', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnTableHeaderProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnTableHeaderElement extends WebFElementWithMethods<{ +}> { +} +/** + * FlutterShadcnTableHeader - WebF FlutterShadcnTableHeader component + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnTableHeader = createWebFComponent({ + tagName: 'flutter-shadcn-table-header', + displayName: 'FlutterShadcnTableHeader', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnTableBodyProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnTableBodyElement extends WebFElementWithMethods<{ +}> { +} +/** + * FlutterShadcnTableBody - WebF FlutterShadcnTableBody component + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnTableBody = createWebFComponent({ + tagName: 'flutter-shadcn-table-body', + displayName: 'FlutterShadcnTableBody', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnTableRowProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnTableRowElement extends WebFElementWithMethods<{ +}> { +} +/** + * FlutterShadcnTableRow - WebF FlutterShadcnTableRow component + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnTableRow = createWebFComponent({ + tagName: 'flutter-shadcn-table-row', + displayName: 'FlutterShadcnTableRow', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnTableHeadProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnTableHeadElement extends WebFElementWithMethods<{ +}> { +} +/** + * FlutterShadcnTableHead - WebF FlutterShadcnTableHead component + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnTableHead = createWebFComponent({ + tagName: 'flutter-shadcn-table-head', + displayName: 'FlutterShadcnTableHead', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnTableCellProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnTableCellElement extends WebFElementWithMethods<{ +}> { +} +/** + * FlutterShadcnTableCell - WebF FlutterShadcnTableCell component + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnTableCell = createWebFComponent({ + tagName: 'flutter-shadcn-table-cell', + displayName: 'FlutterShadcnTableCell', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/tabs.tsx b/packages/react-shadcn-ui/src/lib/src/components/tabs.tsx new file mode 100644 index 0000000000..9c8c870b57 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/tabs.tsx @@ -0,0 +1,269 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnTabsProps { + /** + * Currently active tab value. + */ + value?: string; + /** + * Default tab value (uncontrolled mode). + */ + defaultValue?: string; + /** + * Fired when active tab changes. + */ + onChange?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnTabsElement extends WebFElementWithMethods<{ +}> { + /** Currently active tab value. */ + value?: string; + /** Default tab value (uncontrolled mode). */ + defaultValue?: string; +} +/** + * Properties for +A tabbed interface component. +@example +```html + + + Account + Password + + Account content here. + Password content here. + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnTabs = createWebFComponent({ + tagName: 'flutter-shadcn-tabs', + displayName: 'FlutterShadcnTabs', + // Map props to attributes + attributeProps: [ + 'value', + 'defaultValue', + ], + // Convert prop names to attribute names if needed + attributeMap: { + defaultValue: 'default-value', + }, + // Event handlers + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnTabsListProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnTabsListElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for +Container for tab triggers. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnTabsList = createWebFComponent({ + tagName: 'flutter-shadcn-tabs-list', + displayName: 'FlutterShadcnTabsList', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnTabsTriggerProps { + /** + * Value identifier for this tab. + */ + value: string; + /** + * Disable this tab. + */ + disabled?: boolean; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnTabsTriggerElement extends WebFElementWithMethods<{ +}> { + /** Value identifier for this tab. */ + value: string; + /** Disable this tab. */ + disabled?: boolean; +} +/** + * Properties for +Individual tab trigger button. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnTabsTrigger = createWebFComponent({ + tagName: 'flutter-shadcn-tabs-trigger', + displayName: 'FlutterShadcnTabsTrigger', + // Map props to attributes + attributeProps: [ + 'value', + 'disabled', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); +export interface FlutterShadcnTabsContentProps { + /** + * Value identifier matching a trigger. + */ + value: string; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnTabsContentElement extends WebFElementWithMethods<{ +}> { + /** Value identifier matching a trigger. */ + value: string; +} +/** + * Properties for +Content panel for a tab. + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnTabsContent = createWebFComponent({ + tagName: 'flutter-shadcn-tabs-content', + displayName: 'FlutterShadcnTabsContent', + // Map props to attributes + attributeProps: [ + 'value', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/textarea.tsx b/packages/react-shadcn-ui/src/lib/src/components/textarea.tsx new file mode 100644 index 0000000000..f9e3014a65 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/textarea.tsx @@ -0,0 +1,163 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnTextareaProps { + /** + * Current value of the textarea. + */ + value?: string; + /** + * Placeholder text shown when empty. + */ + placeholder?: string; + /** + * Number of visible text rows. + * Default: 3 + */ + rows?: string; + /** + * Disable the textarea. + */ + disabled?: boolean; + /** + * Make the textarea read-only. + */ + readonly?: boolean; + /** + * Maximum length of the input value. + */ + maxlength?: string; + /** + * Whether the textarea is required. + */ + required?: boolean; + /** + * Autofocus on mount. + */ + autofocus?: boolean; + /** + * Fired on every input change. + */ + onInput?: (event: Event) => void; + /** + * Fired when textarea loses focus. + */ + onChange?: (event: Event) => void; + /** + * Fired when textarea gains focus. + */ + onFocus?: (event: Event) => void; + /** + * Fired when textarea loses focus. + */ + onBlur?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnTextareaElement extends WebFElementWithMethods<{ +}> { + /** Current value of the textarea. */ + value?: string; + /** Placeholder text shown when empty. */ + placeholder?: string; + /** Number of visible text rows. */ + rows?: string; + /** Disable the textarea. */ + disabled?: boolean; + /** Make the textarea read-only. */ + readonly?: boolean; + /** Maximum length of the input value. */ + maxlength?: string; + /** Whether the textarea is required. */ + required?: boolean; + /** Autofocus on mount. */ + autofocus?: boolean; +} +/** + * Properties for +A multi-line text input field. +@example +```html + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnTextarea = createWebFComponent({ + tagName: 'flutter-shadcn-textarea', + displayName: 'FlutterShadcnTextarea', + // Map props to attributes + attributeProps: [ + 'value', + 'placeholder', + 'rows', + 'disabled', + 'readonly', + 'maxlength', + 'required', + 'autofocus', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onInput', + eventName: 'input', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onFocus', + eventName: 'focus', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onBlur', + eventName: 'blur', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/time_picker.tsx b/packages/react-shadcn-ui/src/lib/src/components/time_picker.tsx new file mode 100644 index 0000000000..a1087bbe12 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/time_picker.tsx @@ -0,0 +1,104 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnTimePickerProps { + /** + * Selected time in HH:mm format. + */ + value?: string; + /** + * Placeholder text when no time is selected. + */ + placeholder?: string; + /** + * Disable the picker. + */ + disabled?: boolean; + /** + * Use 24-hour format. + * Default: true + */ + use24Hour?: boolean; + /** + * Fired when time selection changes. + */ + onChange?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnTimePickerElement extends WebFElementWithMethods<{ +}> { + /** Selected time in HH:mm format. */ + value?: string; + /** Placeholder text when no time is selected. */ + placeholder?: string; + /** Disable the picker. */ + disabled?: boolean; + /** Use 24-hour format. */ + use24Hour?: boolean; +} +/** + * Properties for +A time picker component. +@example +```html + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnTimePicker = createWebFComponent({ + tagName: 'flutter-shadcn-time-picker', + displayName: 'FlutterShadcnTimePicker', + // Map props to attributes + attributeProps: [ + 'value', + 'placeholder', + 'disabled', + 'use24Hour', + ], + // Convert prop names to attribute names if needed + attributeMap: { + use24Hour: 'use-24-hour', + }, + // Event handlers + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/toast.tsx b/packages/react-shadcn-ui/src/lib/src/components/toast.tsx new file mode 100644 index 0000000000..2dd6c75bd8 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/toast.tsx @@ -0,0 +1,110 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnToastProps { + /** + * Visual variant of the toast. + * - 'default': Standard toast + * - 'destructive': Red error toast + * Default: 'default' + */ + variant?: string; + /** + * Title of the toast. + */ + title?: string; + /** + * Description text. + */ + description?: string; + /** + * Duration in milliseconds before auto-dismiss. + * Set to 0 to disable auto-dismiss. + * Default: 5000 + */ + duration?: string; + /** + * Show close button. + */ + closable?: boolean; + /** + * Fired when toast is dismissed. + */ + onClose?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnToastElement extends WebFElementWithMethods<{ +}> { + /** Visual variant of the toast. */ + variant?: string; + /** Title of the toast. */ + title?: string; + /** Description text. */ + description?: string; + /** Duration in milliseconds before auto-dismiss. */ + duration?: string; + /** Show close button. */ + closable?: boolean; +} +/** + * Properties for +A toast notification component. +@example +```html + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnToast = createWebFComponent({ + tagName: 'flutter-shadcn-toast', + displayName: 'FlutterShadcnToast', + // Map props to attributes + attributeProps: [ + 'variant', + 'title', + 'description', + 'duration', + 'closable', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onClose', + eventName: 'close', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/components/tooltip.tsx b/packages/react-shadcn-ui/src/lib/src/components/tooltip.tsx new file mode 100644 index 0000000000..7761c0bada --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/tooltip.tsx @@ -0,0 +1,95 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnTooltipProps { + /** + * Text content of the tooltip. + */ + content?: string; + /** + * Delay before showing tooltip in milliseconds. + * Default: 200 + */ + showDelay?: string; + /** + * Delay before hiding tooltip in milliseconds. + * Default: 0 + */ + hideDelay?: string; + /** + * Placement of the tooltip relative to the trigger. + * Options: 'top', 'bottom', 'left', 'right' + * Default: 'top' + */ + placement?: string; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnTooltipElement extends WebFElementWithMethods<{ +}> { + /** Text content of the tooltip. */ + content?: string; + /** Delay before showing tooltip in milliseconds. */ + showDelay?: string; + /** Delay before hiding tooltip in milliseconds. */ + hideDelay?: string; + /** Placement of the tooltip relative to the trigger. */ + placement?: string; +} +/** + * Properties for +A tooltip component that shows additional information on hover. +@example +```html + + Hover me + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnTooltip = createWebFComponent({ + tagName: 'flutter-shadcn-tooltip', + displayName: 'FlutterShadcnTooltip', + // Map props to attributes + attributeProps: [ + 'content', + 'showDelay', + 'hideDelay', + 'placement', + ], + // Convert prop names to attribute names if needed + attributeMap: { + showDelay: 'show-delay', + hideDelay: 'hide-delay', + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file diff --git a/packages/react-shadcn-ui/src/lib/src/theme/theme.tsx b/packages/react-shadcn-ui/src/lib/src/theme/theme.tsx new file mode 100644 index 0000000000..2ad6b8fe0d --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/theme/theme.tsx @@ -0,0 +1,93 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnThemeProps { + /** + * The color scheme to use for theming. + * Available options: 'blue', 'gray', 'green', 'neutral', 'orange', + * 'red', 'rose', 'slate', 'stone', 'violet', 'yellow', 'zinc'. + * Default: 'zinc' + */ + colorScheme?: string; + /** + * The brightness mode for the theme. + * - 'light': Light mode + * - 'dark': Dark mode + * - 'system': Follow system preference + * Default: 'system' + */ + brightness?: string; + /** + * Radius multiplier for border radius values. + * Default: 0.5 + */ + radius?: string; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnThemeElement extends WebFElementWithMethods<{ +}> { + /** The color scheme to use for theming. */ + colorScheme?: string; + /** The brightness mode for the theme. */ + brightness?: string; + /** Radius multiplier for border radius values. */ + radius?: string; +} +/** + * Properties for +Theme provider element that wraps content with shadcn_ui theming. +Use this as a root element to provide consistent theming to all +shadcn components within. +@example +```html + + Click me + +``` + * + * @example + * ```tsx + * + * + * Content + * + * ``` + */ +export const FlutterShadcnTheme = createWebFComponent({ + tagName: 'flutter-shadcn-theme', + displayName: 'FlutterShadcnTheme', + // Map props to attributes + attributeProps: [ + 'colorScheme', + 'brightness', + 'radius', + ], + // Convert prop names to attribute names if needed + attributeMap: { + colorScheme: 'color-scheme', + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + // Add default values here + }, +}); \ No newline at end of file From 9af77a488cda956082c366384a331a3ef8af2364 Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Sat, 14 Feb 2026 11:51:19 +0800 Subject: [PATCH 13/17] feat(react-shadcn-ui): add InputOTP component Add ShadInputOTP wrapper with group, slot, and separator child elements following the existing parent-child pattern (radio). Includes TypeScript definitions, Dart implementation, generated bindings, React wrappers, and a use_cases demo page. Co-Authored-By: Claude Opus 4.6 --- .../lib/src/components/input_otp.d.ts | 80 ++++++ .../lib/src/components/input_otp.dart | 206 ++++++++++++++ .../input_otp_bindings_generated.dart | 60 +++++ .../webf_shadcn_ui/lib/webf_shadcn_ui.dart | 10 + packages/react-shadcn-ui/src/index.ts | 2 + .../src/lib/src/components/input_otp.tsx | 253 ++++++++++++++++++ use_cases/src/App.tsx | 2 + use_cases/src/pages/ShadcnShowcasePage.tsx | 1 + .../src/pages/shadcn/ShadcnInputOtpPage.tsx | 105 ++++++++ 9 files changed, 719 insertions(+) create mode 100644 native_uis/webf_shadcn_ui/lib/src/components/input_otp.d.ts create mode 100644 native_uis/webf_shadcn_ui/lib/src/components/input_otp.dart create mode 100644 native_uis/webf_shadcn_ui/lib/src/components/input_otp_bindings_generated.dart create mode 100644 packages/react-shadcn-ui/src/lib/src/components/input_otp.tsx create mode 100644 use_cases/src/pages/shadcn/ShadcnInputOtpPage.tsx diff --git a/native_uis/webf_shadcn_ui/lib/src/components/input_otp.d.ts b/native_uis/webf_shadcn_ui/lib/src/components/input_otp.d.ts new file mode 100644 index 0000000000..48580194f7 --- /dev/null +++ b/native_uis/webf_shadcn_ui/lib/src/components/input_otp.d.ts @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2024-present The OpenWebF Company. All rights reserved. + * Licensed under the Apache License, Version 2.0. + */ + +/** + * Properties for + * + * Accessible one-time password input with individual character slots. + * + * @example + * ```html + * + * + * + * + * + * + * + * + * + * + * + * + * + * ``` + */ +interface FlutterShadcnInputOtpProperties { + /** + * Maximum number of characters (required). + */ + maxlength: string; + + /** + * Current OTP value. + */ + value?: string; + + /** + * Disable the input. + */ + disabled?: boolean; +} + +/** + * Events emitted by + */ +interface FlutterShadcnInputOtpEvents { + /** Fired when the OTP value changes. */ + change: Event; + /** Fired when all slots are filled. */ + complete: Event; +} + +/** + * Properties for + * + * Groups OTP slots together visually. + */ +interface FlutterShadcnInputOtpGroupProperties {} + +interface FlutterShadcnInputOtpGroupEvents {} + +/** + * Properties for + * + * Individual character slot managed by the parent input. + */ +interface FlutterShadcnInputOtpSlotProperties {} + +interface FlutterShadcnInputOtpSlotEvents {} + +/** + * Properties for + * + * Visual separator between OTP groups. + */ +interface FlutterShadcnInputOtpSeparatorProperties {} + +interface FlutterShadcnInputOtpSeparatorEvents {} diff --git a/native_uis/webf_shadcn_ui/lib/src/components/input_otp.dart b/native_uis/webf_shadcn_ui/lib/src/components/input_otp.dart new file mode 100644 index 0000000000..b78c52040f --- /dev/null +++ b/native_uis/webf_shadcn_ui/lib/src/components/input_otp.dart @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2024-present The OpenWebF Company. All rights reserved. + * Licensed under the Apache License, Version 2.0. + */ + +import 'package:flutter/material.dart'; +import 'package:shadcn_ui/shadcn_ui.dart'; +import 'package:webf/webf.dart'; + +import 'input_otp_bindings_generated.dart'; + +/// WebF custom element that wraps shadcn_ui [ShadInputOTP]. +/// +/// Exposed as `` in the DOM. +class FlutterShadcnInputOtp extends FlutterShadcnInputOtpBindings { + FlutterShadcnInputOtp(super.context); + + String? _maxlength; + String? _value; + bool _disabled = false; + + @override + String? get maxlength => _maxlength; + + @override + set maxlength(value) { + final String? v = value?.toString(); + if (v != _maxlength) { + _maxlength = v; + state?.requestUpdateState(() {}); + } + } + + @override + String? get value => _value; + + @override + set value(value) { + final String? v = value?.toString(); + if (v != _value) { + _value = v; + state?.requestUpdateState(() {}); + } + } + + @override + bool get disabled => _disabled; + + @override + set disabled(value) { + final bool v = value == true || value == 'true' || value == ''; + if (v != _disabled) { + _disabled = v; + state?.requestUpdateState(() {}); + } + } + + int get maxLength => int.tryParse(_maxlength ?? '') ?? 6; + + @override + WebFWidgetElementState createState() => FlutterShadcnInputOtpState(this); +} + +class FlutterShadcnInputOtpState extends WebFWidgetElementState { + FlutterShadcnInputOtpState(super.widgetElement); + + @override + FlutterShadcnInputOtp get widgetElement => + super.widgetElement as FlutterShadcnInputOtp; + + List _buildChildren() { + final List children = []; + + for (final child in widgetElement.childNodes) { + if (child is FlutterShadcnInputOtpGroup) { + final slotCount = + child.childNodes.whereType().length; + children.add( + ShadInputOTPGroup( + children: List.generate(slotCount, (_) => const ShadInputOTPSlot()), + ), + ); + } else if (child is FlutterShadcnInputOtpSeparator) { + children.add(const Icon(Icons.remove, size: 16)); + } + } + + return children; + } + + @override + Widget build(BuildContext context) { + return ShadInputOTP( + maxLength: widgetElement.maxLength, + enabled: !widgetElement.disabled, + initialValue: widgetElement._value, + onChanged: (value) { + widgetElement._value = value; + widgetElement.dispatchEvent(Event('change')); + if (value.length == widgetElement.maxLength) { + widgetElement.dispatchEvent(Event('complete')); + } + }, + children: _buildChildren(), + ); + } +} + +/// WebF custom element for grouping OTP slots. +/// +/// Exposed as `` in the DOM. +class FlutterShadcnInputOtpGroup extends WidgetElement { + FlutterShadcnInputOtpGroup(super.context); + + void _notifyParent() { + final parent = parentNode; + if (parent is FlutterShadcnInputOtp) { + parent.state?.requestUpdateState(() {}); + } + } + + @override + void connectedCallback() { + super.connectedCallback(); + _notifyParent(); + } + + @override + WebFWidgetElementState createState() => + FlutterShadcnInputOtpGroupState(this); +} + +class FlutterShadcnInputOtpGroupState extends WebFWidgetElementState { + FlutterShadcnInputOtpGroupState(super.widgetElement); + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} + +/// WebF custom element for individual OTP character slots. +/// +/// Exposed as `` in the DOM. +class FlutterShadcnInputOtpSlot extends WidgetElement { + FlutterShadcnInputOtpSlot(super.context); + + void _notifyParent() { + final group = parentNode; + if (group is FlutterShadcnInputOtpGroup) { + group._notifyParent(); + } + } + + @override + void connectedCallback() { + super.connectedCallback(); + _notifyParent(); + } + + @override + WebFWidgetElementState createState() => + FlutterShadcnInputOtpSlotState(this); +} + +class FlutterShadcnInputOtpSlotState extends WebFWidgetElementState { + FlutterShadcnInputOtpSlotState(super.widgetElement); + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} + +/// WebF custom element for visual separator between OTP groups. +/// +/// Exposed as `` in the DOM. +class FlutterShadcnInputOtpSeparator extends WidgetElement { + FlutterShadcnInputOtpSeparator(super.context); + + void _notifyParent() { + final parent = parentNode; + if (parent is FlutterShadcnInputOtp) { + parent.state?.requestUpdateState(() {}); + } + } + + @override + void connectedCallback() { + super.connectedCallback(); + _notifyParent(); + } + + @override + WebFWidgetElementState createState() => + FlutterShadcnInputOtpSeparatorState(this); +} + +class FlutterShadcnInputOtpSeparatorState extends WebFWidgetElementState { + FlutterShadcnInputOtpSeparatorState(super.widgetElement); + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} diff --git a/native_uis/webf_shadcn_ui/lib/src/components/input_otp_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/input_otp_bindings_generated.dart new file mode 100644 index 0000000000..38fce5add4 --- /dev/null +++ b/native_uis/webf_shadcn_ui/lib/src/components/input_otp_bindings_generated.dart @@ -0,0 +1,60 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `webf codegen` +// ignore_for_file: avoid_unused_constructor_parameters +// ignore_for_file: non_constant_identifier_names +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: implementation_imports +// ignore_for_file: library_private_types_in_public_api +// ignore_for_file: prefer_void_to_null +import 'package:webf/webf.dart'; +abstract class FlutterShadcnInputOtpBindings extends WidgetElement { + FlutterShadcnInputOtpBindings(super.context); + String? get maxlength; + set maxlength(value); + String? get value; + set value(value); + bool get disabled; + set disabled(value); + @override + void initializeAttributes(Map attributes) { + super.initializeAttributes(attributes); + attributes['maxlength'] = ElementAttributeProperty( + getter: () => maxlength?.toString(), + setter: (value) => maxlength = value, + deleter: () => maxlength = null + ); + attributes['value'] = ElementAttributeProperty( + getter: () => value?.toString(), + setter: (value) => value = value, + deleter: () => value = null + ); + attributes['disabled'] = ElementAttributeProperty( + getter: () => disabled.toString(), + setter: (value) => disabled = value == 'true' || value == '', + deleter: () => disabled = false + ); + } + static StaticDefinedBindingPropertyMap flutterShadcnInputOtpProperties = { + 'maxlength': StaticDefinedBindingProperty( + getter: (element) => castToType(element).maxlength, + setter: (element, value) => + castToType(element).maxlength = value, + ), + 'value': StaticDefinedBindingProperty( + getter: (element) => castToType(element).value, + setter: (element, value) => + castToType(element).value = value, + ), + 'disabled': StaticDefinedBindingProperty( + getter: (element) => castToType(element).disabled, + setter: (element, value) => + castToType(element).disabled = value, + ), + }; + @override + List get properties => [ + ...super.properties, + flutterShadcnInputOtpProperties, + ]; +} diff --git a/native_uis/webf_shadcn_ui/lib/webf_shadcn_ui.dart b/native_uis/webf_shadcn_ui/lib/webf_shadcn_ui.dart index 313081ca67..0a9edfc788 100644 --- a/native_uis/webf_shadcn_ui/lib/webf_shadcn_ui.dart +++ b/native_uis/webf_shadcn_ui/lib/webf_shadcn_ui.dart @@ -51,6 +51,7 @@ export 'src/components/select.dart'; export 'src/components/slider.dart'; export 'src/components/combobox.dart'; export 'src/components/form.dart'; +export 'src/components/input_otp.dart'; // Display Components export 'src/components/card.dart'; @@ -97,6 +98,7 @@ import 'src/components/select.dart'; import 'src/components/slider.dart'; import 'src/components/combobox.dart'; import 'src/components/form.dart'; +import 'src/components/input_otp.dart'; import 'src/components/card.dart'; import 'src/components/alert.dart'; import 'src/components/badge.dart'; @@ -180,6 +182,14 @@ void installWebFShadcnUI() { 'flutter-shadcn-form-description', (context) => FlutterShadcnFormDescription(context)); WebF.defineCustomElement( 'flutter-shadcn-form-message', (context) => FlutterShadcnFormMessage(context)); + WebF.defineCustomElement( + 'flutter-shadcn-input-otp', (context) => FlutterShadcnInputOtp(context)); + WebF.defineCustomElement( + 'flutter-shadcn-input-otp-group', (context) => FlutterShadcnInputOtpGroup(context)); + WebF.defineCustomElement( + 'flutter-shadcn-input-otp-slot', (context) => FlutterShadcnInputOtpSlot(context)); + WebF.defineCustomElement( + 'flutter-shadcn-input-otp-separator', (context) => FlutterShadcnInputOtpSeparator(context)); // Display Components WebF.defineCustomElement( diff --git a/packages/react-shadcn-ui/src/index.ts b/packages/react-shadcn-ui/src/index.ts index 6ee3877c17..d36d551006 100644 --- a/packages/react-shadcn-ui/src/index.ts +++ b/packages/react-shadcn-ui/src/index.ts @@ -37,6 +37,8 @@ export { FlutterShadcnProgress } from "./lib/src/components/progress"; export type { FlutterShadcnProgressElement } from "./lib/src/components/progress"; export { FlutterShadcnPopover, FlutterShadcnPopoverTrigger, FlutterShadcnPopoverContent } from "./lib/src/components/popover"; export type { FlutterShadcnPopoverElement, FlutterShadcnPopoverTriggerElement, FlutterShadcnPopoverContentElement } from "./lib/src/components/popover"; +export { FlutterShadcnInputOtp, FlutterShadcnInputOtpGroup, FlutterShadcnInputOtpSlot, FlutterShadcnInputOtpSeparator } from "./lib/src/components/input_otp"; +export type { FlutterShadcnInputOtpElement, FlutterShadcnInputOtpGroupElement, FlutterShadcnInputOtpSlotElement, FlutterShadcnInputOtpSeparatorElement } from "./lib/src/components/input_otp"; export { FlutterShadcnInput } from "./lib/src/components/input"; export type { FlutterShadcnInputElement } from "./lib/src/components/input"; export { FlutterShadcnImage } from "./lib/src/components/image"; diff --git a/packages/react-shadcn-ui/src/lib/src/components/input_otp.tsx b/packages/react-shadcn-ui/src/lib/src/components/input_otp.tsx new file mode 100644 index 0000000000..2a37657233 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/input_otp.tsx @@ -0,0 +1,253 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +export interface FlutterShadcnInputOtpProps { + /** + * Maximum number of characters (required). + */ + maxlength: string; + /** + * Current OTP value. + */ + value?: string; + /** + * Disable the input. + */ + disabled?: boolean; + /** + * Fired when the OTP value changes. + */ + onChange?: (event: Event) => void; + /** + * Fired when all slots are filled. + */ + onComplete?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnInputOtpElement extends WebFElementWithMethods<{ +}> { + /** Maximum number of characters. */ + maxlength: string; + /** Current OTP value. */ + value?: string; + /** Disable the input. */ + disabled?: boolean; +} +/** + * Properties for + * Accessible one-time password input with individual character slots. + * + * @example + * ```tsx + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * ``` + */ +export const FlutterShadcnInputOtp = createWebFComponent({ + tagName: 'flutter-shadcn-input-otp', + displayName: 'FlutterShadcnInputOtp', + // Map props to attributes + attributeProps: [ + 'maxlength', + 'value', + 'disabled', + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + { + propName: 'onComplete', + eventName: 'complete', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + // Default prop values + defaultProps: { + }, +}); +export interface FlutterShadcnInputOtpGroupProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnInputOtpGroupElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for + * Groups OTP slots together visually. + * + * @example + * ```tsx + * + * + * + * + * + * + * ``` + */ +export const FlutterShadcnInputOtpGroup = createWebFComponent({ + tagName: 'flutter-shadcn-input-otp-group', + displayName: 'FlutterShadcnInputOtpGroup', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + }, +}); +export interface FlutterShadcnInputOtpSlotProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnInputOtpSlotElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for + * Individual character slot managed by the parent input. + * + * @example + * ```tsx + * + * + * ``` + */ +export const FlutterShadcnInputOtpSlot = createWebFComponent({ + tagName: 'flutter-shadcn-input-otp-slot', + displayName: 'FlutterShadcnInputOtpSlot', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + }, +}); +export interface FlutterShadcnInputOtpSeparatorProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnInputOtpSeparatorElement extends WebFElementWithMethods<{ +}> { +} +/** + * Properties for + * Visual separator between OTP groups. + * + * @example + * ```tsx + * + * + * ``` + */ +export const FlutterShadcnInputOtpSeparator = createWebFComponent({ + tagName: 'flutter-shadcn-input-otp-separator', + displayName: 'FlutterShadcnInputOtpSeparator', + // Map props to attributes + attributeProps: [ + ], + // Convert prop names to attribute names if needed + attributeMap: { + }, + // Event handlers + events: [ + ], + // Default prop values + defaultProps: { + }, +}); diff --git a/use_cases/src/App.tsx b/use_cases/src/App.tsx index a522ceb3da..bd50d47143 100644 --- a/use_cases/src/App.tsx +++ b/use_cases/src/App.tsx @@ -140,6 +140,7 @@ import { ShadcnContextMenuPage } from './pages/shadcn/ShadcnContextMenuPage'; import { ShadcnPopoverPage } from './pages/shadcn/ShadcnPopoverPage'; import { ShadcnFormPage } from './pages/shadcn/ShadcnFormPage'; import { ShadcnIconButtonPage } from './pages/shadcn/ShadcnIconButtonPage'; +import { ShadcnInputOtpPage } from './pages/shadcn/ShadcnInputOtpPage'; // Lucide Icons imports import { LucideShowcasePage } from './pages/LucideShowcasePage'; import { LucideIconsPage } from './pages/lucide/LucideIconsPage'; @@ -256,6 +257,7 @@ function App() { } /> } /> } /> + } /> )} diff --git a/use_cases/src/pages/ShadcnShowcasePage.tsx b/use_cases/src/pages/ShadcnShowcasePage.tsx index acf32cd4a4..dcae48536d 100644 --- a/use_cases/src/pages/ShadcnShowcasePage.tsx +++ b/use_cases/src/pages/ShadcnShowcasePage.tsx @@ -38,6 +38,7 @@ export const ShadcnShowcasePage: React.FC = () => { +
diff --git a/use_cases/src/pages/shadcn/ShadcnInputOtpPage.tsx b/use_cases/src/pages/shadcn/ShadcnInputOtpPage.tsx new file mode 100644 index 0000000000..d22967393b --- /dev/null +++ b/use_cases/src/pages/shadcn/ShadcnInputOtpPage.tsx @@ -0,0 +1,105 @@ +import React, { useState } from 'react'; +import { WebFListView } from '@openwebf/react-core-ui'; +import { + FlutterShadcnTheme, + FlutterShadcnInputOtp, + FlutterShadcnInputOtpGroup, + FlutterShadcnInputOtpSlot, + FlutterShadcnInputOtpSeparator, +} from '@openwebf/react-shadcn-ui'; + +export const ShadcnInputOtpPage: React.FC = () => { + const [otpValue, setOtpValue] = useState(''); + const [completed, setCompleted] = useState(false); + + return ( + +
+ +

Input OTP

+ +
+

Basic OTP Input

+

Enter a 6-digit verification code.

+ { + setCompleted(false); + }} + onComplete={() => { + setCompleted(true); + }} + > + + + + + + + + + + + + + {completed && ( +

OTP complete!

+ )} +
+ +
+

4-Digit PIN

+

A shorter 4-digit code without separator.

+ + + + + + + + +
+ +
+

Disabled State

+

OTP input in disabled state.

+ + + + + + + + + + + + + +
+ +
+

Multiple Groups

+

6-character code split into groups of 2.

+ + + + + + + + + + + + + + + + +
+
+
+
+ ); +}; From 711ee517e15baafdaef148ebf6d06babd5781bbf Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Sat, 14 Feb 2026 12:26:01 +0800 Subject: [PATCH 14/17] feat: add Shadcn Menubar and Cupertino Menu components Add Shadcn UI Menubar component wrapping ShadMenubar/ShadMenubarItem Flutter widgets with full support for menu items, keyboard shortcuts, submenus, checkbox items, radio groups, labels, and separators. Also includes the Cupertino popup menu component from previous work. Co-Authored-By: Claude Opus 4.6 --- .../webf_cupertino_ui/lib/src/menu.d.ts | 53 + .../webf_cupertino_ui/lib/src/menu.dart | 208 +++ .../lib/src/menu_bindings_generated.dart | 36 + .../lib/webf_cupertino_ui.dart | 5 + .../lib/src/components/menubar.dart | 1242 +++++++++++++++++ .../webf_shadcn_ui/lib/webf_shadcn_ui.dart | 28 + packages/react-shadcn-ui/src/index.ts | 2 + .../src/lib/src/components/menubar.tsx | 570 ++++++++ use_cases/src/App.tsx | 2 + use_cases/src/pages/ShadcnShowcasePage.tsx | 1 + .../src/pages/shadcn/ShadcnMenubarPage.tsx | 264 ++++ 11 files changed, 2411 insertions(+) create mode 100644 native_uis/webf_cupertino_ui/lib/src/menu.d.ts create mode 100644 native_uis/webf_cupertino_ui/lib/src/menu.dart create mode 100644 native_uis/webf_cupertino_ui/lib/src/menu_bindings_generated.dart create mode 100644 native_uis/webf_shadcn_ui/lib/src/components/menubar.dart create mode 100644 packages/react-shadcn-ui/src/lib/src/components/menubar.tsx create mode 100644 use_cases/src/pages/shadcn/ShadcnMenubarPage.tsx diff --git a/native_uis/webf_cupertino_ui/lib/src/menu.d.ts b/native_uis/webf_cupertino_ui/lib/src/menu.d.ts new file mode 100644 index 0000000000..83f98a0e7f --- /dev/null +++ b/native_uis/webf_cupertino_ui/lib/src/menu.d.ts @@ -0,0 +1,53 @@ +/** + * Properties for . + * A tap-triggered popup menu wrapping Flutter's showMenu with Cupertino styling. + */ + +interface MenuAction { + /** Button label text. */ + text: string; + /** Optional trailing icon name (Cupertino icon key). */ + icon?: string; + /** Marks this action as destructive (red). */ + destructive?: boolean; + /** + * Optional event name associated with this action. + * If omitted, a name is derived from the text. + */ + event?: string; +} + +interface FlutterCupertinoMenuProperties { + /** + * Whether the menu trigger is disabled. + * When disabled, tapping the child will not open the menu. + * @default false + */ + disabled?: boolean; +} + +interface FlutterCupertinoMenuMethods { + /** + * Set the list of actions displayed in the popup menu. + */ + setActions(actions: MenuAction[]): void; +} + +interface FlutterCupertinoMenuSelectDetail { + /** Zero-based index of the selected action. */ + index: number; + /** Action text. */ + text: string; + /** Event name for this action. */ + event: string; + /** Whether the action is destructive. */ + destructive: boolean; +} + +interface FlutterCupertinoMenuEvents { + /** + * Fired when an action is selected. + * detail contains metadata about the selected action. + */ + select: CustomEvent; +} diff --git a/native_uis/webf_cupertino_ui/lib/src/menu.dart b/native_uis/webf_cupertino_ui/lib/src/menu.dart new file mode 100644 index 0000000000..0941dc9163 --- /dev/null +++ b/native_uis/webf_cupertino_ui/lib/src/menu.dart @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2024-present The OpenWebF Company. All rights reserved. + * Licensed under the Apache License, Version 2.0. + */ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:webf/rendering.dart'; +import 'package:webf/webf.dart'; +import 'package:webf/dom.dart' as dom; + +import 'icon.dart'; +import 'menu_bindings_generated.dart'; +import 'logger.dart'; + +/// WebF custom element that provides a tap-triggered popup menu +/// with Cupertino styling. +/// +/// Exposed as `` in the DOM. +class FlutterCupertinoMenu extends FlutterCupertinoMenuBindings { + FlutterCupertinoMenu(super.context) { + _actions = >[]; + } + + bool _disabled = false; + List> _actions = >[]; + + @override + bool get disabled => _disabled; + + @override + set disabled(value) { + final bool next = value == true; + if (next != _disabled) { + _disabled = next; + state?.requestUpdateState(() {}); + } + } + + @override + bool get allowsInfiniteHeight => true; + + @override + bool get allowsInfiniteWidth => true; + + static StaticDefinedSyncBindingObjectMethodMap menuMethods = { + 'setActions': StaticDefinedSyncBindingObjectMethod( + call: (element, args) { + final menu = castToType(element); + + if (args.isNotEmpty) { + final actionsData = args[0]; + + if (actionsData is List) { + final List> newActions = >[]; + for (final item in actionsData) { + if (item is Map) { + newActions.add( + Map.from( + item.map((key, value) => MapEntry(key.toString(), value)), + ), + ); + } else { + logger.w('Skipping non-map item in actions list: $item'); + } + } + menu._actions = newActions; + } else { + menu._actions = >[]; + } + menu.state?.requestUpdateState(() {}); + } else { + menu._actions = >[]; + menu.state?.requestUpdateState(() {}); + } + }, + ), + }; + + @override + List get methods => + [ + ...super.methods, + menuMethods, + ]; + + IconData? _getIconData(String iconName) { + final IconData? icon = FlutterCupertinoIcon.getIconType(iconName); + if (icon == null) { + logger.w('Icon not found for name: $iconName'); + } + return icon; + } + + @override + FlutterCupertinoMenuState? get state => + super.state as FlutterCupertinoMenuState?; + + @override + WebFWidgetElementState createState() { + return FlutterCupertinoMenuState(this); + } +} + +class FlutterCupertinoMenuState extends WebFWidgetElementState { + FlutterCupertinoMenuState(super.widgetElement); + + @override + FlutterCupertinoMenu get widgetElement => + super.widgetElement as FlutterCupertinoMenu; + + Widget? _findChild() { + for (final child in widgetElement.childNodes) { + if (child is dom.Element) { + return WebFWidgetElementChild(child: child.toWidget()); + } + } + return null; + } + + Future _showMenu() async { + if (widgetElement._disabled || widgetElement._actions.isEmpty) return; + + final RenderBox renderBox = context.findRenderObject() as RenderBox; + final Offset offset = renderBox.localToGlobal(Offset.zero); + final Size size = renderBox.size; + + final List> items = >[]; + for (int i = 0; i < widgetElement._actions.length; i++) { + final Map action = widgetElement._actions[i]; + final bool isDestructive = action['destructive'] == true; + final String text = action['text'] as String? ?? ''; + final String? iconName = action['icon'] as String?; + + items.add( + PopupMenuItem( + value: i, + child: Row( + children: [ + Expanded( + child: Text( + text, + style: TextStyle( + color: isDestructive ? CupertinoColors.destructiveRed : null, + fontSize: 17, + ), + ), + ), + if (iconName != null) + Padding( + padding: const EdgeInsets.only(left: 8), + child: Icon( + widgetElement._getIconData(iconName), + size: 20, + color: isDestructive ? CupertinoColors.destructiveRed : null, + ), + ), + ], + ), + ), + ); + } + + final int? selected = await showMenu( + context: context, + position: RelativeRect.fromLTRB( + offset.dx, + offset.dy + size.height, + offset.dx + size.width, + offset.dy + size.height, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14), + ), + items: items, + ); + + if (selected != null && selected < widgetElement._actions.length) { + final Map action = widgetElement._actions[selected]; + final String text = action['text'] as String? ?? ''; + final bool isDestructive = action['destructive'] == true; + final String eventName = action['event'] as String? ?? + text.toLowerCase().replaceAll(' ', '_'); + + final Map detail = { + 'index': selected, + 'text': text, + 'event': eventName, + 'destructive': isDestructive, + }; + + widgetElement.dispatchEvent(CustomEvent('select', detail: detail)); + } + } + + @override + Widget build(BuildContext context) { + final Widget? child = _findChild(); + + if (child == null) { + return const SizedBox.shrink(); + } + + return GestureDetector( + onTap: widgetElement._disabled ? null : _showMenu, + child: child, + ); + } +} diff --git a/native_uis/webf_cupertino_ui/lib/src/menu_bindings_generated.dart b/native_uis/webf_cupertino_ui/lib/src/menu_bindings_generated.dart new file mode 100644 index 0000000000..617339d8b8 --- /dev/null +++ b/native_uis/webf_cupertino_ui/lib/src/menu_bindings_generated.dart @@ -0,0 +1,36 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `webf codegen` +// ignore_for_file: avoid_unused_constructor_parameters +// ignore_for_file: non_constant_identifier_names +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: implementation_imports +// ignore_for_file: library_private_types_in_public_api +// ignore_for_file: prefer_void_to_null +import 'package:webf/webf.dart'; +abstract class FlutterCupertinoMenuBindings extends WidgetElement { + FlutterCupertinoMenuBindings(super.context); + bool get disabled; + set disabled(value); + @override + void initializeAttributes(Map attributes) { + super.initializeAttributes(attributes); + attributes['disabled'] = ElementAttributeProperty( + getter: () => disabled.toString(), + setter: (value) => disabled = value == 'true' || value == '', + deleter: () => disabled = false + ); + } + static StaticDefinedBindingPropertyMap flutterCupertinoMenuProperties = { + 'disabled': StaticDefinedBindingProperty( + getter: (element) => castToType(element).disabled, + setter: (element, value) => + castToType(element).disabled = value, + ), + }; + @override + List get properties => [ + ...super.properties, + flutterCupertinoMenuProperties, + ]; +} diff --git a/native_uis/webf_cupertino_ui/lib/webf_cupertino_ui.dart b/native_uis/webf_cupertino_ui/lib/webf_cupertino_ui.dart index b830dee6fa..afc1d858ac 100644 --- a/native_uis/webf_cupertino_ui/lib/webf_cupertino_ui.dart +++ b/native_uis/webf_cupertino_ui/lib/webf_cupertino_ui.dart @@ -20,6 +20,7 @@ export 'src/form_section.dart'; export 'src/input.dart'; export 'src/search_text_field.dart'; export 'src/date_picker.dart'; +export 'src/menu.dart'; import 'src/alert.dart'; import 'src/action_sheet.dart'; import 'src/context_menu.dart'; @@ -41,6 +42,7 @@ import 'src/tab_view.dart'; import 'src/sliding_segmented_control.dart'; import 'src/list_tile.dart'; import 'src/search_text_field.dart'; +import 'src/menu.dart'; /// Installs all Cupertino UI custom elements for WebF. /// @@ -106,6 +108,9 @@ void installWebFCupertinoUI() { WebF.defineCustomElement( 'flutter-cupertino-date-picker', (context) => FlutterCupertinoDatePicker(context)); + WebF.defineCustomElement( + 'flutter-cupertino-menu', + (context) => FlutterCupertinoMenu(context)); // WebF.defineCustomElement('flutter-cupertino-input', (context) => FlutterCupertinoInput(context)); diff --git a/native_uis/webf_shadcn_ui/lib/src/components/menubar.dart b/native_uis/webf_shadcn_ui/lib/src/components/menubar.dart new file mode 100644 index 0000000000..132cc4430b --- /dev/null +++ b/native_uis/webf_shadcn_ui/lib/src/components/menubar.dart @@ -0,0 +1,1242 @@ +/* + * Copyright (C) 2024-present The OpenWebF Company. All rights reserved. + * Licensed under the Apache License, Version 2.0. + */ + +import 'dart:async'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:shadcn_ui/shadcn_ui.dart'; +import 'package:webf/css.dart'; +import 'package:webf/webf.dart'; + +const Map _menubarDefaultStyle = { + DISPLAY: INLINE_FLEX, +}; + +/// Helper to extract text content from nodes recursively. +String _extractTextContent(Iterable nodes) { + final buffer = StringBuffer(); + for (final node in nodes) { + if (node is TextNode) { + buffer.write(node.data); + } else if (node.childNodes.isNotEmpty) { + buffer.write(_extractTextContent(node.childNodes)); + } + } + return buffer.toString().trim(); +} + +class _ParsedShortcut { + const _ParsedShortcut({ + required this.meta, + required this.control, + required this.alt, + required this.shift, + this.logicalKey, + this.keyLabel, + }); + + final bool meta; + final bool control; + final bool alt; + final bool shift; + final LogicalKeyboardKey? logicalKey; + final String? keyLabel; + + bool matches(KeyDownEvent event) { + final keyboard = HardwareKeyboard.instance; + if (keyboard.isMetaPressed != meta) return false; + if (keyboard.isControlPressed != control) return false; + if (keyboard.isAltPressed != alt) return false; + if (keyboard.isShiftPressed != shift) return false; + if (logicalKey != null) return event.logicalKey == logicalKey; + final expectedLabel = keyLabel?.toUpperCase().trim(); + if (expectedLabel == null || expectedLabel.isEmpty) return false; + return event.logicalKey.keyLabel.toUpperCase().trim() == expectedLabel; + } +} + +_ParsedShortcut? _parseShortcut(String? rawShortcut) { + var shortcut = rawShortcut?.trim(); + if (shortcut == null || shortcut.isEmpty) return null; + + var meta = false; + var control = false; + var alt = false; + var shift = false; + String? keyToken; + + if (shortcut.contains('+')) { + final parts = shortcut + .split('+') + .map((p) => p.trim()) + .where((p) => p.isNotEmpty); + for (final part in parts) { + final normalized = part.toLowerCase(); + if (normalized == 'cmd' || + normalized == 'command' || + normalized == 'meta' || + normalized == 'win' || + normalized == 'super') { + meta = true; + } else if (normalized == 'ctrl' || normalized == 'control') { + control = true; + } else if (normalized == 'alt' || normalized == 'option') { + alt = true; + } else if (normalized == 'shift') { + shift = true; + } else { + keyToken = part; + } + } + } else { + if (shortcut.contains('⌘')) { + meta = true; + shortcut = shortcut.replaceAll('⌘', ''); + } + if (shortcut.contains('⌃')) { + control = true; + shortcut = shortcut.replaceAll('⌃', ''); + } + if (shortcut.contains('⌥')) { + alt = true; + shortcut = shortcut.replaceAll('⌥', ''); + } + if (shortcut.contains('⇧')) { + shift = true; + shortcut = shortcut.replaceAll('⇧', ''); + } + keyToken = shortcut.trim(); + } + + final isApplePlatform = defaultTargetPlatform == TargetPlatform.macOS || + defaultTargetPlatform == TargetPlatform.iOS; + if (meta && !isApplePlatform) { + meta = false; + control = true; + } + + final key = keyToken?.trim(); + if (key == null || key.isEmpty) return null; + + final normalizedKey = key.toUpperCase(); + final LogicalKeyboardKey? logicalKey = switch (normalizedKey) { + '⌫' || 'BACKSPACE' => LogicalKeyboardKey.backspace, + 'DEL' || 'DELETE' => LogicalKeyboardKey.delete, + 'ESC' || 'ESCAPE' => LogicalKeyboardKey.escape, + 'ENTER' || 'RETURN' || '⏎' => LogicalKeyboardKey.enter, + 'TAB' => LogicalKeyboardKey.tab, + 'SPACE' || '␣' => LogicalKeyboardKey.space, + _ => null, + }; + + return _ParsedShortcut( + meta: meta, + control: control, + alt: alt, + shift: shift, + logicalKey: logicalKey, + keyLabel: logicalKey == null ? normalizedKey : null, + ); +} + +class _ShortcutBinding { + const _ShortcutBinding(this.shortcut, this.item); + final _ParsedShortcut shortcut; + final FlutterShadcnMenubarItem item; +} + +class _MenubarSubmenuTrigger extends StatefulWidget { + const _MenubarSubmenuTrigger({ + super.key, + required this.label, + required this.items, + required this.inset, + required this.enabled, + required this.isNestedSubmenu, + }); + + final String label; + final List items; + final bool inset; + final bool enabled; + final bool isNestedSubmenu; + + @override + State<_MenubarSubmenuTrigger> createState() => _MenubarSubmenuTriggerState(); +} + +class _MenubarSubmenuTriggerState extends State<_MenubarSubmenuTrigger> { + final ShadContextMenuController _controller = ShadContextMenuController(); + final Object _groupId = Object(); + bool _insideTrigger = false; + bool _insidePopover = false; + Timer? _closeTimer; + + @override + void dispose() { + _closeTimer?.cancel(); + _controller.dispose(); + super.dispose(); + } + + void _scheduleCloseIfNeeded() { + _closeTimer?.cancel(); + if (!_controller.isOpen) return; + if (_insideTrigger || _insidePopover) return; + _closeTimer = Timer(const Duration(milliseconds: 80), () { + if (!mounted) return; + if (_insideTrigger || _insidePopover) return; + _controller.hide(); + }); + } + + void _onTriggerHoverChanged(bool inside) { + _insideTrigger = inside; + if (!widget.enabled) return; + if (inside) { + _controller.show(); + } else { + _scheduleCloseIfNeeded(); + } + } + + void _onPopoverHoverChanged(bool inside) { + _insidePopover = inside; + if (!widget.enabled) return; + if (inside) { + _controller.show(); + } else { + _scheduleCloseIfNeeded(); + } + } + + void _toggleOpen() { + if (!widget.enabled) return; + if (_controller.isOpen) { + _controller.hide(); + } else { + _controller.show(); + } + } + + @override + Widget build(BuildContext context) { + final theme = ShadTheme.of(context); + + final effectiveItemPadding = theme.contextMenuTheme.itemPadding ?? + const EdgeInsets.symmetric(horizontal: 4); + + final defaultInsetPadding = widget.inset + ? const EdgeInsetsDirectional.only(start: 32, end: 8) + : const EdgeInsets.symmetric(horizontal: 8); + + final effectiveInsetPadding = + theme.contextMenuTheme.insetPadding ?? defaultInsetPadding; + + final effectiveTrailingPadding = theme.contextMenuTheme.trailingPadding ?? + const EdgeInsetsDirectional.only(start: 8); + + final effectiveHeight = theme.contextMenuTheme.height ?? 32; + + final effectiveButtonVariant = + theme.contextMenuTheme.buttonVariant ?? ShadButtonVariant.ghost; + + final effectiveDecoration = const ShadDecoration( + secondaryBorder: ShadBorder.none, + secondaryFocusedBorder: ShadBorder.none, + ).merge(theme.contextMenuTheme.itemDecoration); + + final effectiveTextStyle = + (theme.contextMenuTheme.textStyle ?? + theme.textTheme.small.copyWith(fontWeight: FontWeight.normal)) + .fallback(color: theme.colorScheme.foreground); + + final effectiveSelectedBackgroundColor = + theme.contextMenuTheme.selectedBackgroundColor ?? theme.colorScheme.accent; + final effectiveBackgroundColor = theme.contextMenuTheme.backgroundColor; + + final effectiveAnchor = theme.contextMenuTheme.anchor ?? + ShadAnchorAuto( + offset: Offset(-8, widget.isNestedSubmenu ? -5 : -3), + targetAnchor: Alignment.topRight, + followerAnchor: Alignment.bottomRight, + ); + + final popoverContent = MouseRegion( + onEnter: (_) => _onPopoverHoverChanged(true), + onExit: (_) => _onPopoverHoverChanged(false), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: widget.items, + ), + ); + + return ListenableBuilder( + listenable: _controller, + builder: (context, _) { + final selected = _insideTrigger || _insidePopover || _controller.isOpen; + return ShadContextMenu( + controller: _controller, + anchor: effectiveAnchor, + groupId: _groupId, + onHoverArea: (hovered) => + hovered ? _onTriggerHoverChanged(true) : _onTriggerHoverChanged(false), + items: [popoverContent], + child: MouseRegion( + onEnter: (_) => _onTriggerHoverChanged(true), + onExit: (_) => _onTriggerHoverChanged(false), + child: Padding( + padding: effectiveItemPadding, + child: ShadButton.raw( + height: effectiveHeight, + enabled: widget.enabled, + variant: effectiveButtonVariant, + decoration: effectiveDecoration, + width: double.infinity, + padding: effectiveInsetPadding, + backgroundColor: selected + ? effectiveSelectedBackgroundColor + : effectiveBackgroundColor, + hoverBackgroundColor: effectiveSelectedBackgroundColor, + onPressed: _toggleOpen, + child: Expanded( + child: Row( + children: [ + Expanded( + child: DefaultTextStyle( + style: effectiveTextStyle, + child: Text( + widget.label, + overflow: TextOverflow.ellipsis, + softWrap: false, + ), + ), + ), + Padding( + padding: effectiveTrailingPadding, + child: const Icon(LucideIcons.chevronRight, size: 16), + ), + ], + ), + ), + ), + ), + ), + ); + }, + ); + } +} + +// --------------------------------------------------------------------------- +// Main Menubar element +// --------------------------------------------------------------------------- + +/// WebF custom element for the menubar container. +/// +/// Exposed as `` in the DOM. +class FlutterShadcnMenubar extends WidgetElement { + FlutterShadcnMenubar(super.context); + + @override + Map get defaultStyle => _menubarDefaultStyle; + + @override + WebFWidgetElementState createState() => FlutterShadcnMenubarState(this); +} + +class FlutterShadcnMenubarState extends WebFWidgetElementState { + FlutterShadcnMenubarState(super.widgetElement); + + @override + FlutterShadcnMenubar get widgetElement => + super.widgetElement as FlutterShadcnMenubar; + + List _buildMenuItems( + Iterable nodes, + List<_ShortcutBinding> shortcutBindings, { + String? radioGroupValue, + FlutterShadcnMenubarRadioGroup? radioGroup, + bool isSubMenu = false, + }) { + final items = []; + for (final node in nodes) { + if (node is FlutterShadcnMenubarItem) { + final text = _extractTextContent(node.childNodes); + final shortcut = node.shortcut ?? node.getAttribute('shortcut'); + final isInset = node.inset || + node.getAttribute('inset') == 'true' || + node.getAttribute('inset') == ''; + + Widget? trailingWidget; + if (shortcut != null && shortcut.isNotEmpty) { + trailingWidget = Text(shortcut); + } + + final parsedShortcut = _parseShortcut(shortcut); + if (parsedShortcut != null) { + shortcutBindings.add(_ShortcutBinding(parsedShortcut, node)); + } + + if (isInset) { + items.add( + ShadContextMenuItem.inset( + enabled: !node.disabled, + trailing: trailingWidget, + onPressed: () { + node.dispatchEvent(Event('click')); + }, + child: Text(text), + ), + ); + } else { + items.add( + ShadContextMenuItem( + enabled: !node.disabled, + trailing: trailingWidget, + onPressed: () { + node.dispatchEvent(Event('click')); + }, + child: Text(text), + ), + ); + } + } else if (node is FlutterShadcnMenubarSeparator) { + items.add(const ShadSeparator.horizontal( + margin: EdgeInsets.symmetric(vertical: 4), + )); + } else if (node is FlutterShadcnMenubarLabel) { + final text = _extractTextContent(node.childNodes); + items.add( + Padding( + padding: const EdgeInsets.fromLTRB(36, 8, 8, 8), + child: Builder( + builder: (context) { + final theme = ShadTheme.of(context); + return Text(text, style: theme.textTheme.small); + }, + ), + ), + ); + } else if (node is FlutterShadcnMenubarSub) { + FlutterShadcnMenubarSubTrigger? trigger; + FlutterShadcnMenubarSubContent? subContent; + + for (final child in node.childNodes) { + if (child is FlutterShadcnMenubarSubTrigger) { + trigger = child; + } else if (child is FlutterShadcnMenubarSubContent) { + subContent = child; + } + } + + if (trigger != null) { + final text = _extractTextContent(trigger.childNodes); + final isInset = trigger.inset || + trigger.getAttribute('inset') == 'true' || + trigger.getAttribute('inset') == ''; + final subItems = subContent != null + ? _buildMenuItems( + subContent.childNodes, + shortcutBindings, + isSubMenu: true, + ) + : []; + + items.add( + _MenubarSubmenuTrigger( + key: ObjectKey(node), + label: text, + inset: isInset, + enabled: !trigger.disabled, + isNestedSubmenu: isSubMenu, + items: subItems, + ), + ); + } + } else if (node is FlutterShadcnMenubarCheckboxItem) { + final text = _extractTextContent(node.childNodes); + final shortcut = node.shortcut ?? node.getAttribute('shortcut'); + + Widget? trailingWidget; + if (shortcut != null && shortcut.isNotEmpty) { + trailingWidget = Text(shortcut); + } + + items.add( + ShadContextMenuItem( + enabled: !node.disabled, + leading: node.checked + ? const Icon(LucideIcons.check, size: 16) + : const SizedBox(width: 16), + trailing: trailingWidget, + onPressed: () { + node._checked = !node._checked; + node.dispatchEvent(Event('change')); + node._notifyMenuNeedsRebuild(); + }, + child: Text(text), + ), + ); + } else if (node is FlutterShadcnMenubarRadioGroup) { + final groupItems = _buildMenuItems( + node.childNodes, + shortcutBindings, + radioGroupValue: node.value, + radioGroup: node, + isSubMenu: isSubMenu, + ); + items.addAll(groupItems); + } else if (node is FlutterShadcnMenubarRadioItem) { + final text = _extractTextContent(node.childNodes); + final shortcut = node.shortcut ?? node.getAttribute('shortcut'); + final isSelected = + radioGroupValue != null && node.value == radioGroupValue; + + Widget? trailingWidget; + if (shortcut != null && shortcut.isNotEmpty) { + trailingWidget = Text(shortcut); + } + + items.add( + ShadContextMenuItem( + enabled: !node.disabled, + leading: isSelected + ? SizedBox.square( + dimension: 16, + child: Center( + child: Container( + width: 8, + height: 8, + decoration: const BoxDecoration( + color: Colors.black, + shape: BoxShape.circle, + ), + ), + ), + ) + : const SizedBox(width: 16), + trailing: trailingWidget, + onPressed: () { + if (radioGroup != null && node.value != null) { + radioGroup._value = node.value; + radioGroup.dispatchEvent( + CustomEvent('change', detail: {'value': node.value})); + radioGroup._notifyMenuNeedsRebuild(); + } + node.dispatchEvent(Event('click')); + }, + child: Text(text), + ), + ); + } + } + return items; + } + + @override + Widget build(BuildContext context) { + final menuWidgets = []; + + for (final child in widgetElement.childNodes) { + if (child is FlutterShadcnMenubarMenu) { + FlutterShadcnMenubarTrigger? trigger; + FlutterShadcnMenubarContent? content; + + for (final menuChild in child.childNodes) { + if (menuChild is FlutterShadcnMenubarTrigger) { + trigger = menuChild; + } else if (menuChild is FlutterShadcnMenubarContent) { + content = menuChild; + } + } + + if (trigger != null) { + final triggerText = _extractTextContent(trigger.childNodes); + final shortcutBindings = <_ShortcutBinding>[]; + final items = content != null + ? _buildMenuItems(content.childNodes, shortcutBindings) + : []; + + menuWidgets.add( + ShadMenubarItem( + items: items, + child: Text(triggerText), + ), + ); + } + } + } + + return ShadMenubar(items: menuWidgets); + } +} + +// --------------------------------------------------------------------------- +// Menubar Menu (groups trigger + content) +// --------------------------------------------------------------------------- + +/// WebF custom element for a single menu in the menubar. +class FlutterShadcnMenubarMenu extends WidgetElement { + FlutterShadcnMenubarMenu(super.context); + + @override + WebFWidgetElementState createState() => + FlutterShadcnMenubarMenuState(this); +} + +class FlutterShadcnMenubarMenuState extends WebFWidgetElementState { + FlutterShadcnMenubarMenuState(super.widgetElement); + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} + +// --------------------------------------------------------------------------- +// Menubar Trigger +// --------------------------------------------------------------------------- + +/// WebF custom element for the menubar trigger label. +class FlutterShadcnMenubarTrigger extends WidgetElement { + FlutterShadcnMenubarTrigger(super.context); + + @override + WebFWidgetElementState createState() => + FlutterShadcnMenubarTriggerState(this); +} + +class FlutterShadcnMenubarTriggerState extends WebFWidgetElementState { + FlutterShadcnMenubarTriggerState(super.widgetElement); + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} + +// --------------------------------------------------------------------------- +// Menubar Content +// --------------------------------------------------------------------------- + +/// WebF custom element for the menubar dropdown content. +class FlutterShadcnMenubarContent extends WidgetElement { + FlutterShadcnMenubarContent(super.context); + + @override + WebFWidgetElementState createState() => + FlutterShadcnMenubarContentState(this); +} + +class FlutterShadcnMenubarContentState extends WebFWidgetElementState { + FlutterShadcnMenubarContentState(super.widgetElement); + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} + +// --------------------------------------------------------------------------- +// Menubar Item +// --------------------------------------------------------------------------- + +/// WebF custom element for a menubar menu item. +class FlutterShadcnMenubarItem extends WidgetElement { + FlutterShadcnMenubarItem(super.context); + + bool _disabled = false; + String? _shortcut; + bool _inset = false; + + void _notifyMenuNeedsRebuild() { + Node? current = parentNode; + while (current != null) { + if (current is FlutterShadcnMenubar) { + current.state?.requestUpdateState(() {}); + return; + } + current = current.parentNode; + } + } + + bool get disabled => _disabled; + + set disabled(value) { + final bool v = value == true; + if (v != _disabled) { + _disabled = v; + _notifyMenuNeedsRebuild(); + state?.requestUpdateState(() {}); + } + } + + String? get shortcut => _shortcut; + + set shortcut(value) { + final String? v = value?.toString(); + if (v != _shortcut) { + _shortcut = v; + _notifyMenuNeedsRebuild(); + state?.requestUpdateState(() {}); + } + } + + bool get inset => _inset; + + set inset(value) { + final bool v = value == true; + if (v != _inset) { + _inset = v; + _notifyMenuNeedsRebuild(); + state?.requestUpdateState(() {}); + } + } + + @override + void initializeAttributes(Map attributes) { + super.initializeAttributes(attributes); + attributes['disabled'] = ElementAttributeProperty( + getter: () => disabled.toString(), + setter: (val) => disabled = val == 'true' || val == '', + deleter: () => disabled = false); + attributes['shortcut'] = ElementAttributeProperty( + getter: () => shortcut, + setter: (val) => shortcut = val, + deleter: () => shortcut = null); + attributes['inset'] = ElementAttributeProperty( + getter: () => inset.toString(), + setter: (val) => inset = val == 'true' || val == '', + deleter: () => inset = false); + } + + static StaticDefinedBindingPropertyMap + flutterShadcnMenubarItemProperties = { + 'disabled': StaticDefinedBindingProperty( + getter: (element) => + castToType(element).disabled, + setter: (element, value) => + castToType(element).disabled = value, + ), + 'shortcut': StaticDefinedBindingProperty( + getter: (element) => + castToType(element).shortcut, + setter: (element, value) => + castToType(element).shortcut = value, + ), + 'inset': StaticDefinedBindingProperty( + getter: (element) => + castToType(element).inset, + setter: (element, value) => + castToType(element).inset = value, + ), + }; + + @override + List get properties => [ + ...super.properties, + flutterShadcnMenubarItemProperties, + ]; + + @override + WebFWidgetElementState createState() => + FlutterShadcnMenubarItemState(this); +} + +class FlutterShadcnMenubarItemState extends WebFWidgetElementState { + FlutterShadcnMenubarItemState(super.widgetElement); + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} + +// --------------------------------------------------------------------------- +// Menubar Separator +// --------------------------------------------------------------------------- + +/// WebF custom element for a menubar separator. +class FlutterShadcnMenubarSeparator extends WidgetElement { + FlutterShadcnMenubarSeparator(super.context); + + @override + WebFWidgetElementState createState() => + FlutterShadcnMenubarSeparatorState(this); +} + +class FlutterShadcnMenubarSeparatorState extends WebFWidgetElementState { + FlutterShadcnMenubarSeparatorState(super.widgetElement); + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} + +// --------------------------------------------------------------------------- +// Menubar Label +// --------------------------------------------------------------------------- + +/// WebF custom element for a menubar label (group header). +class FlutterShadcnMenubarLabel extends WidgetElement { + FlutterShadcnMenubarLabel(super.context); + + @override + WebFWidgetElementState createState() => + FlutterShadcnMenubarLabelState(this); +} + +class FlutterShadcnMenubarLabelState extends WebFWidgetElementState { + FlutterShadcnMenubarLabelState(super.widgetElement); + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} + +// --------------------------------------------------------------------------- +// Menubar Sub (submenu container) +// --------------------------------------------------------------------------- + +/// WebF custom element for a menubar sub-menu. +class FlutterShadcnMenubarSub extends WidgetElement { + FlutterShadcnMenubarSub(super.context); + + @override + WebFWidgetElementState createState() => + FlutterShadcnMenubarSubState(this); +} + +class FlutterShadcnMenubarSubState extends WebFWidgetElementState { + FlutterShadcnMenubarSubState(super.widgetElement); + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} + +// --------------------------------------------------------------------------- +// Menubar Sub Trigger +// --------------------------------------------------------------------------- + +/// WebF custom element for a menubar sub-menu trigger. +class FlutterShadcnMenubarSubTrigger extends WidgetElement { + FlutterShadcnMenubarSubTrigger(super.context); + + bool _disabled = false; + bool _inset = false; + + bool get disabled => _disabled; + + set disabled(value) { + final bool v = value == true; + if (v != _disabled) { + _disabled = v; + _notifyMenuNeedsRebuild(); + state?.requestUpdateState(() {}); + } + } + + bool get inset => _inset; + + set inset(value) { + final bool v = value == true; + if (v != _inset) { + _inset = v; + _notifyMenuNeedsRebuild(); + state?.requestUpdateState(() {}); + } + } + + void _notifyMenuNeedsRebuild() { + Node? current = parentNode; + while (current != null) { + if (current is FlutterShadcnMenubar) { + current.state?.requestUpdateState(() {}); + return; + } + current = current.parentNode; + } + } + + @override + void initializeAttributes(Map attributes) { + super.initializeAttributes(attributes); + attributes['disabled'] = ElementAttributeProperty( + getter: () => disabled.toString(), + setter: (val) => disabled = val == 'true' || val == '', + deleter: () => disabled = false); + attributes['inset'] = ElementAttributeProperty( + getter: () => inset.toString(), + setter: (val) => inset = val == 'true' || val == '', + deleter: () => inset = false); + } + + static StaticDefinedBindingPropertyMap + flutterShadcnMenubarSubTriggerProperties = { + 'disabled': StaticDefinedBindingProperty( + getter: (element) => + castToType(element).disabled, + setter: (element, value) => + castToType(element).disabled = value, + ), + 'inset': StaticDefinedBindingProperty( + getter: (element) => + castToType(element).inset, + setter: (element, value) => + castToType(element).inset = value, + ), + }; + + @override + List get properties => [ + ...super.properties, + flutterShadcnMenubarSubTriggerProperties, + ]; + + @override + WebFWidgetElementState createState() => + FlutterShadcnMenubarSubTriggerState(this); +} + +class FlutterShadcnMenubarSubTriggerState extends WebFWidgetElementState { + FlutterShadcnMenubarSubTriggerState(super.widgetElement); + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} + +// --------------------------------------------------------------------------- +// Menubar Sub Content +// --------------------------------------------------------------------------- + +/// WebF custom element for menubar sub-menu content. +class FlutterShadcnMenubarSubContent extends WidgetElement { + FlutterShadcnMenubarSubContent(super.context); + + @override + WebFWidgetElementState createState() => + FlutterShadcnMenubarSubContentState(this); +} + +class FlutterShadcnMenubarSubContentState extends WebFWidgetElementState { + FlutterShadcnMenubarSubContentState(super.widgetElement); + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} + +// --------------------------------------------------------------------------- +// Menubar Checkbox Item +// --------------------------------------------------------------------------- + +/// WebF custom element for a menubar checkbox item. +class FlutterShadcnMenubarCheckboxItem extends WidgetElement { + FlutterShadcnMenubarCheckboxItem(super.context); + + bool _disabled = false; + bool _checked = false; + String? _shortcut; + + void _notifyMenuNeedsRebuild() { + Node? current = parentNode; + while (current != null) { + if (current is FlutterShadcnMenubar) { + current.state?.requestUpdateState(() {}); + return; + } + current = current.parentNode; + } + } + + bool get disabled => _disabled; + + set disabled(value) { + final bool v = value == true; + if (v != _disabled) { + _disabled = v; + _notifyMenuNeedsRebuild(); + state?.requestUpdateState(() {}); + } + } + + bool get checked => _checked; + + set checked(value) { + final bool v = value == true; + if (v != _checked) { + _checked = v; + _notifyMenuNeedsRebuild(); + state?.requestUpdateState(() {}); + } + } + + String? get shortcut => _shortcut; + + set shortcut(value) { + final String? v = value?.toString(); + if (v != _shortcut) { + _shortcut = v; + _notifyMenuNeedsRebuild(); + state?.requestUpdateState(() {}); + } + } + + @override + void initializeAttributes(Map attributes) { + super.initializeAttributes(attributes); + attributes['disabled'] = ElementAttributeProperty( + getter: () => disabled.toString(), + setter: (val) => disabled = val == 'true' || val == '', + deleter: () => disabled = false); + attributes['checked'] = ElementAttributeProperty( + getter: () => checked.toString(), + setter: (val) => checked = val == 'true' || val == '', + deleter: () => checked = false); + attributes['shortcut'] = ElementAttributeProperty( + getter: () => shortcut, + setter: (val) => shortcut = val, + deleter: () => shortcut = null); + } + + static StaticDefinedBindingPropertyMap + flutterShadcnMenubarCheckboxItemProperties = { + 'disabled': StaticDefinedBindingProperty( + getter: (element) => + castToType(element).disabled, + setter: (element, value) => + castToType(element).disabled = + value, + ), + 'checked': StaticDefinedBindingProperty( + getter: (element) => + castToType(element).checked, + setter: (element, value) => + castToType(element).checked = value, + ), + 'shortcut': StaticDefinedBindingProperty( + getter: (element) => + castToType(element).shortcut, + setter: (element, value) => + castToType(element).shortcut = + value, + ), + }; + + @override + List get properties => [ + ...super.properties, + flutterShadcnMenubarCheckboxItemProperties, + ]; + + @override + WebFWidgetElementState createState() => + FlutterShadcnMenubarCheckboxItemState(this); +} + +class FlutterShadcnMenubarCheckboxItemState extends WebFWidgetElementState { + FlutterShadcnMenubarCheckboxItemState(super.widgetElement); + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} + +// --------------------------------------------------------------------------- +// Menubar Radio Group +// --------------------------------------------------------------------------- + +/// WebF custom element for a menubar radio group. +class FlutterShadcnMenubarRadioGroup extends WidgetElement { + FlutterShadcnMenubarRadioGroup(super.context); + + String? _value; + + void _notifyMenuNeedsRebuild() { + Node? current = parentNode; + while (current != null) { + if (current is FlutterShadcnMenubar) { + current.state?.requestUpdateState(() {}); + return; + } + current = current.parentNode; + } + } + + String? get value => _value; + + set value(val) { + final String? v = val?.toString(); + if (v != _value) { + _value = v; + _notifyMenuNeedsRebuild(); + state?.requestUpdateState(() {}); + } + } + + @override + void initializeAttributes(Map attributes) { + super.initializeAttributes(attributes); + attributes['value'] = ElementAttributeProperty( + getter: () => value, + setter: (val) => value = val, + deleter: () => value = null); + } + + static StaticDefinedBindingPropertyMap + flutterShadcnMenubarRadioGroupProperties = { + 'value': StaticDefinedBindingProperty( + getter: (element) => + castToType(element).value, + setter: (element, value) => + castToType(element).value = value, + ), + }; + + @override + List get properties => [ + ...super.properties, + flutterShadcnMenubarRadioGroupProperties, + ]; + + @override + WebFWidgetElementState createState() => + FlutterShadcnMenubarRadioGroupState(this); +} + +class FlutterShadcnMenubarRadioGroupState extends WebFWidgetElementState { + FlutterShadcnMenubarRadioGroupState(super.widgetElement); + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} + +// --------------------------------------------------------------------------- +// Menubar Radio Item +// --------------------------------------------------------------------------- + +/// WebF custom element for a menubar radio item. +class FlutterShadcnMenubarRadioItem extends WidgetElement { + FlutterShadcnMenubarRadioItem(super.context); + + bool _disabled = false; + String? _value; + String? _shortcut; + + void _notifyMenuNeedsRebuild() { + Node? current = parentNode; + while (current != null) { + if (current is FlutterShadcnMenubar) { + current.state?.requestUpdateState(() {}); + return; + } + current = current.parentNode; + } + } + + bool get disabled => _disabled; + + set disabled(value) { + final bool v = value == true; + if (v != _disabled) { + _disabled = v; + _notifyMenuNeedsRebuild(); + state?.requestUpdateState(() {}); + } + } + + String? get value => _value; + + set value(val) { + final String? v = val?.toString(); + if (v != _value) { + _value = v; + _notifyMenuNeedsRebuild(); + state?.requestUpdateState(() {}); + } + } + + String? get shortcut => _shortcut; + + set shortcut(value) { + final String? v = value?.toString(); + if (v != _shortcut) { + _shortcut = v; + _notifyMenuNeedsRebuild(); + state?.requestUpdateState(() {}); + } + } + + @override + void initializeAttributes(Map attributes) { + super.initializeAttributes(attributes); + attributes['disabled'] = ElementAttributeProperty( + getter: () => disabled.toString(), + setter: (val) => disabled = val == 'true' || val == '', + deleter: () => disabled = false); + attributes['value'] = ElementAttributeProperty( + getter: () => value, + setter: (val) => value = val, + deleter: () => value = null); + attributes['shortcut'] = ElementAttributeProperty( + getter: () => shortcut, + setter: (val) => shortcut = val, + deleter: () => shortcut = null); + } + + static StaticDefinedBindingPropertyMap + flutterShadcnMenubarRadioItemProperties = { + 'disabled': StaticDefinedBindingProperty( + getter: (element) => + castToType(element).disabled, + setter: (element, value) => + castToType(element).disabled = value, + ), + 'value': StaticDefinedBindingProperty( + getter: (element) => + castToType(element).value, + setter: (element, value) => + castToType(element).value = value, + ), + 'shortcut': StaticDefinedBindingProperty( + getter: (element) => + castToType(element).shortcut, + setter: (element, value) => + castToType(element).shortcut = value, + ), + }; + + @override + List get properties => [ + ...super.properties, + flutterShadcnMenubarRadioItemProperties, + ]; + + @override + WebFWidgetElementState createState() => + FlutterShadcnMenubarRadioItemState(this); +} + +class FlutterShadcnMenubarRadioItemState extends WebFWidgetElementState { + FlutterShadcnMenubarRadioItemState(super.widgetElement); + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} diff --git a/native_uis/webf_shadcn_ui/lib/webf_shadcn_ui.dart b/native_uis/webf_shadcn_ui/lib/webf_shadcn_ui.dart index 0a9edfc788..6f5719fe20 100644 --- a/native_uis/webf_shadcn_ui/lib/webf_shadcn_ui.dart +++ b/native_uis/webf_shadcn_ui/lib/webf_shadcn_ui.dart @@ -71,6 +71,7 @@ export 'src/components/popover.dart'; export 'src/components/breadcrumb.dart'; export 'src/components/dropdown_menu.dart'; export 'src/components/context_menu.dart'; +export 'src/components/menubar.dart'; // Data Display export 'src/components/table.dart'; @@ -114,6 +115,7 @@ import 'src/components/popover.dart'; import 'src/components/breadcrumb.dart'; import 'src/components/dropdown_menu.dart'; import 'src/components/context_menu.dart'; +import 'src/components/menubar.dart'; import 'src/components/table.dart'; import 'src/components/accordion.dart'; import 'src/components/calendar.dart'; @@ -316,6 +318,32 @@ void installWebFShadcnUI() { 'flutter-shadcn-context-menu-radio-group', (context) => FlutterShadcnContextMenuRadioGroup(context)); WebF.defineCustomElement( 'flutter-shadcn-context-menu-radio-item', (context) => FlutterShadcnContextMenuRadioItem(context)); + WebF.defineCustomElement( + 'flutter-shadcn-menubar', (context) => FlutterShadcnMenubar(context)); + WebF.defineCustomElement( + 'flutter-shadcn-menubar-menu', (context) => FlutterShadcnMenubarMenu(context)); + WebF.defineCustomElement( + 'flutter-shadcn-menubar-trigger', (context) => FlutterShadcnMenubarTrigger(context)); + WebF.defineCustomElement( + 'flutter-shadcn-menubar-content', (context) => FlutterShadcnMenubarContent(context)); + WebF.defineCustomElement( + 'flutter-shadcn-menubar-item', (context) => FlutterShadcnMenubarItem(context)); + WebF.defineCustomElement( + 'flutter-shadcn-menubar-separator', (context) => FlutterShadcnMenubarSeparator(context)); + WebF.defineCustomElement( + 'flutter-shadcn-menubar-label', (context) => FlutterShadcnMenubarLabel(context)); + WebF.defineCustomElement( + 'flutter-shadcn-menubar-sub', (context) => FlutterShadcnMenubarSub(context)); + WebF.defineCustomElement( + 'flutter-shadcn-menubar-sub-trigger', (context) => FlutterShadcnMenubarSubTrigger(context)); + WebF.defineCustomElement( + 'flutter-shadcn-menubar-sub-content', (context) => FlutterShadcnMenubarSubContent(context)); + WebF.defineCustomElement( + 'flutter-shadcn-menubar-checkbox-item', (context) => FlutterShadcnMenubarCheckboxItem(context)); + WebF.defineCustomElement( + 'flutter-shadcn-menubar-radio-group', (context) => FlutterShadcnMenubarRadioGroup(context)); + WebF.defineCustomElement( + 'flutter-shadcn-menubar-radio-item', (context) => FlutterShadcnMenubarRadioItem(context)); // Data Display WebF.defineCustomElement( diff --git a/packages/react-shadcn-ui/src/index.ts b/packages/react-shadcn-ui/src/index.ts index d36d551006..710523f29b 100644 --- a/packages/react-shadcn-ui/src/index.ts +++ b/packages/react-shadcn-ui/src/index.ts @@ -55,6 +55,8 @@ export { FlutterShadcnDatePicker } from "./lib/src/components/date_picker"; export type { FlutterShadcnDatePickerElement } from "./lib/src/components/date_picker"; export { FlutterShadcnContextMenu, FlutterShadcnContextMenuTrigger, FlutterShadcnContextMenuContent, FlutterShadcnContextMenuItem, FlutterShadcnContextMenuSeparator, FlutterShadcnContextMenuLabel, FlutterShadcnContextMenuSub, FlutterShadcnContextMenuSubTrigger, FlutterShadcnContextMenuSubContent, FlutterShadcnContextMenuCheckboxItem, FlutterShadcnContextMenuRadioGroup, FlutterShadcnContextMenuRadioItem } from "./lib/src/components/context_menu"; export type { FlutterShadcnContextMenuElement, FlutterShadcnContextMenuTriggerElement, FlutterShadcnContextMenuContentElement, FlutterShadcnContextMenuItemElement, FlutterShadcnContextMenuSeparatorElement, FlutterShadcnContextMenuLabelElement, FlutterShadcnContextMenuSubElement, FlutterShadcnContextMenuSubTriggerElement, FlutterShadcnContextMenuSubContentElement, FlutterShadcnContextMenuCheckboxItemElement, FlutterShadcnContextMenuRadioGroupElement, FlutterShadcnContextMenuRadioItemElement } from "./lib/src/components/context_menu"; +export { FlutterShadcnMenubar, FlutterShadcnMenubarMenu, FlutterShadcnMenubarTrigger, FlutterShadcnMenubarContent, FlutterShadcnMenubarItem, FlutterShadcnMenubarSeparator, FlutterShadcnMenubarLabel, FlutterShadcnMenubarSub, FlutterShadcnMenubarSubTrigger, FlutterShadcnMenubarSubContent, FlutterShadcnMenubarCheckboxItem, FlutterShadcnMenubarRadioGroup, FlutterShadcnMenubarRadioItem } from "./lib/src/components/menubar"; +export type { FlutterShadcnMenubarElement, FlutterShadcnMenubarMenuElement, FlutterShadcnMenubarTriggerElement, FlutterShadcnMenubarContentElement, FlutterShadcnMenubarItemElement, FlutterShadcnMenubarSeparatorElement, FlutterShadcnMenubarLabelElement, FlutterShadcnMenubarSubElement, FlutterShadcnMenubarSubTriggerElement, FlutterShadcnMenubarSubContentElement, FlutterShadcnMenubarCheckboxItemElement, FlutterShadcnMenubarRadioGroupElement, FlutterShadcnMenubarRadioItemElement } from "./lib/src/components/menubar"; export { FlutterShadcnCombobox, FlutterShadcnComboboxItem } from "./lib/src/components/combobox"; export type { FlutterShadcnComboboxElement, FlutterShadcnComboboxItemElement } from "./lib/src/components/combobox"; export { FlutterShadcnCollapsible, FlutterShadcnCollapsibleTrigger, FlutterShadcnCollapsibleContent } from "./lib/src/components/collapsible"; diff --git a/packages/react-shadcn-ui/src/lib/src/components/menubar.tsx b/packages/react-shadcn-ui/src/lib/src/components/menubar.tsx new file mode 100644 index 0000000000..60ea1fea23 --- /dev/null +++ b/packages/react-shadcn-ui/src/lib/src/components/menubar.tsx @@ -0,0 +1,570 @@ +import React from "react"; +import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; +import * as __webfTypes from "../../../types"; +interface FlutterShadcnMenubarRadioGroupChangeEventDetail { + value: string | null; +} +export interface FlutterShadcnMenubarProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnMenubarElement extends WebFElementWithMethods<{ +}> { +} +/** + * A horizontal menubar that contains a list of menus. + * @example + * ```tsx + * + * + * File + * + * New Tab + * + * + * + * ``` + */ +export const FlutterShadcnMenubar = createWebFComponent({ + tagName: 'flutter-shadcn-menubar', + displayName: 'FlutterShadcnMenubar', + attributeProps: [], + attributeMap: {}, + events: [], + defaultProps: {}, +}); +export interface FlutterShadcnMenubarMenuProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnMenubarMenuElement extends WebFElementWithMethods<{ +}> { +} +/** + * A single menu in the menubar, containing a trigger and content. + */ +export const FlutterShadcnMenubarMenu = createWebFComponent({ + tagName: 'flutter-shadcn-menubar-menu', + displayName: 'FlutterShadcnMenubarMenu', + attributeProps: [], + attributeMap: {}, + events: [], + defaultProps: {}, +}); +export interface FlutterShadcnMenubarTriggerProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnMenubarTriggerElement extends WebFElementWithMethods<{ +}> { +} +/** + * The trigger label for a menubar menu. + */ +export const FlutterShadcnMenubarTrigger = createWebFComponent({ + tagName: 'flutter-shadcn-menubar-trigger', + displayName: 'FlutterShadcnMenubarTrigger', + attributeProps: [], + attributeMap: {}, + events: [], + defaultProps: {}, +}); +export interface FlutterShadcnMenubarContentProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnMenubarContentElement extends WebFElementWithMethods<{ +}> { +} +/** + * Container for the dropdown menu items within a menubar menu. + */ +export const FlutterShadcnMenubarContent = createWebFComponent({ + tagName: 'flutter-shadcn-menubar-content', + displayName: 'FlutterShadcnMenubarContent', + attributeProps: [], + attributeMap: {}, + events: [], + defaultProps: {}, +}); +export interface FlutterShadcnMenubarItemProps { + /** + * Whether the item is disabled. + */ + disabled?: boolean; + /** + * Keyboard shortcut to display (e.g., "⌘T", "Ctrl+N"). + */ + shortcut?: string; + /** + * Whether to use inset styling (adds left padding for alignment with checkbox/radio items). + */ + inset?: boolean; + /** + * Fired when the item is clicked. + */ + onClick?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnMenubarItemElement extends WebFElementWithMethods<{ +}> { + /** Whether the item is disabled. */ + disabled?: boolean; + /** Keyboard shortcut to display. */ + shortcut?: string; + /** Whether to use inset styling. */ + inset?: boolean; +} +/** + * A menu item within a menubar menu. + */ +export const FlutterShadcnMenubarItem = createWebFComponent({ + tagName: 'flutter-shadcn-menubar-item', + displayName: 'FlutterShadcnMenubarItem', + attributeProps: [ + 'disabled', + 'shortcut', + 'inset', + ], + attributeMap: {}, + events: [ + { + propName: 'onClick', + eventName: 'click', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + defaultProps: {}, +}); +export interface FlutterShadcnMenubarSeparatorProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnMenubarSeparatorElement extends WebFElementWithMethods<{ +}> { +} +/** + * A separator line within a menubar menu. + */ +export const FlutterShadcnMenubarSeparator = createWebFComponent({ + tagName: 'flutter-shadcn-menubar-separator', + displayName: 'FlutterShadcnMenubarSeparator', + attributeProps: [], + attributeMap: {}, + events: [], + defaultProps: {}, +}); +export interface FlutterShadcnMenubarLabelProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnMenubarLabelElement extends WebFElementWithMethods<{ +}> { +} +/** + * A label/header for grouping menubar items. + */ +export const FlutterShadcnMenubarLabel = createWebFComponent({ + tagName: 'flutter-shadcn-menubar-label', + displayName: 'FlutterShadcnMenubarLabel', + attributeProps: [], + attributeMap: {}, + events: [], + defaultProps: {}, +}); +export interface FlutterShadcnMenubarSubProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnMenubarSubElement extends WebFElementWithMethods<{ +}> { +} +/** + * A container for nested submenu items within a menubar menu. + */ +export const FlutterShadcnMenubarSub = createWebFComponent({ + tagName: 'flutter-shadcn-menubar-sub', + displayName: 'FlutterShadcnMenubarSub', + attributeProps: [], + attributeMap: {}, + events: [], + defaultProps: {}, +}); +export interface FlutterShadcnMenubarSubTriggerProps { + /** + * Whether the submenu trigger is disabled. + */ + disabled?: boolean; + /** + * Whether to use inset styling. + */ + inset?: boolean; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnMenubarSubTriggerElement extends WebFElementWithMethods<{ +}> { + /** Whether the submenu trigger is disabled. */ + disabled?: boolean; + /** Whether to use inset styling. */ + inset?: boolean; +} +/** + * The trigger element for a menubar submenu. + */ +export const FlutterShadcnMenubarSubTrigger = createWebFComponent({ + tagName: 'flutter-shadcn-menubar-sub-trigger', + displayName: 'FlutterShadcnMenubarSubTrigger', + attributeProps: [ + 'disabled', + 'inset', + ], + attributeMap: {}, + events: [], + defaultProps: {}, +}); +export interface FlutterShadcnMenubarSubContentProps { + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnMenubarSubContentElement extends WebFElementWithMethods<{ +}> { +} +/** + * Container for submenu items within a menubar. + */ +export const FlutterShadcnMenubarSubContent = createWebFComponent({ + tagName: 'flutter-shadcn-menubar-sub-content', + displayName: 'FlutterShadcnMenubarSubContent', + attributeProps: [], + attributeMap: {}, + events: [], + defaultProps: {}, +}); +export interface FlutterShadcnMenubarCheckboxItemProps { + /** + * Whether the item is disabled. + */ + disabled?: boolean; + /** + * Whether the checkbox is checked. + */ + checked?: boolean; + /** + * Keyboard shortcut to display (e.g., "⌘B"). + */ + shortcut?: string; + /** + * Fired when the checked state changes. + */ + onChange?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnMenubarCheckboxItemElement extends WebFElementWithMethods<{ +}> { + /** Whether the item is disabled. */ + disabled?: boolean; + /** Whether the checkbox is checked. */ + checked?: boolean; + /** Keyboard shortcut to display. */ + shortcut?: string; +} +/** + * A menu item with a checkbox indicator in a menubar menu. + */ +export const FlutterShadcnMenubarCheckboxItem = createWebFComponent({ + tagName: 'flutter-shadcn-menubar-checkbox-item', + displayName: 'FlutterShadcnMenubarCheckboxItem', + attributeProps: [ + 'disabled', + 'checked', + 'shortcut', + ], + attributeMap: {}, + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + defaultProps: {}, +}); +export interface FlutterShadcnMenubarRadioGroupProps { + /** + * The value of the currently selected radio item. + */ + value?: string; + /** + * Fired when the selected value changes. + */ + onChange?: (event: CustomEvent) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnMenubarRadioGroupElement extends WebFElementWithMethods<{ +}> { + /** The value of the currently selected radio item. */ + value?: string; +} +/** + * A group of radio items where only one can be selected. + */ +export const FlutterShadcnMenubarRadioGroup = createWebFComponent({ + tagName: 'flutter-shadcn-menubar-radio-group', + displayName: 'FlutterShadcnMenubarRadioGroup', + attributeProps: [ + 'value', + ], + attributeMap: {}, + events: [ + { + propName: 'onChange', + eventName: 'change', + handler: (callback: (event: CustomEvent) => void) => (event: Event) => { + callback(event as CustomEvent); + }, + }, + ], + defaultProps: {}, +}); +export interface FlutterShadcnMenubarRadioItemProps { + /** + * Whether the item is disabled. + */ + disabled?: boolean; + /** + * The value of this radio item. + */ + value?: string; + /** + * Keyboard shortcut to display. + */ + shortcut?: string; + /** + * Fired when the item is clicked. + */ + onClick?: (event: Event) => void; + /** + * HTML id attribute + */ + id?: string; + /** + * Additional CSS styles + */ + style?: React.CSSProperties; + /** + * Children elements + */ + children?: React.ReactNode; + /** + * Additional CSS class names + */ + className?: string; +} +export interface FlutterShadcnMenubarRadioItemElement extends WebFElementWithMethods<{ +}> { + /** Whether the item is disabled. */ + disabled?: boolean; + /** The value of this radio item. */ + value?: string; + /** Keyboard shortcut to display. */ + shortcut?: string; +} +/** + * A radio-style menu item within a menubar radio group. + */ +export const FlutterShadcnMenubarRadioItem = createWebFComponent({ + tagName: 'flutter-shadcn-menubar-radio-item', + displayName: 'FlutterShadcnMenubarRadioItem', + attributeProps: [ + 'disabled', + 'value', + 'shortcut', + ], + attributeMap: {}, + events: [ + { + propName: 'onClick', + eventName: 'click', + handler: (callback: (event: Event) => void) => (event: Event) => { + callback(event as Event); + }, + }, + ], + defaultProps: {}, +}); diff --git a/use_cases/src/App.tsx b/use_cases/src/App.tsx index bd50d47143..5032a8a2e8 100644 --- a/use_cases/src/App.tsx +++ b/use_cases/src/App.tsx @@ -141,6 +141,7 @@ import { ShadcnPopoverPage } from './pages/shadcn/ShadcnPopoverPage'; import { ShadcnFormPage } from './pages/shadcn/ShadcnFormPage'; import { ShadcnIconButtonPage } from './pages/shadcn/ShadcnIconButtonPage'; import { ShadcnInputOtpPage } from './pages/shadcn/ShadcnInputOtpPage'; +import { ShadcnMenubarPage } from './pages/shadcn/ShadcnMenubarPage'; // Lucide Icons imports import { LucideShowcasePage } from './pages/LucideShowcasePage'; import { LucideIconsPage } from './pages/lucide/LucideIconsPage'; @@ -258,6 +259,7 @@ function App() { } /> } /> } /> + } /> )} diff --git a/use_cases/src/pages/ShadcnShowcasePage.tsx b/use_cases/src/pages/ShadcnShowcasePage.tsx index dcae48536d..63733dcf94 100644 --- a/use_cases/src/pages/ShadcnShowcasePage.tsx +++ b/use_cases/src/pages/ShadcnShowcasePage.tsx @@ -69,6 +69,7 @@ export const ShadcnShowcasePage: React.FC = () => {
+
diff --git a/use_cases/src/pages/shadcn/ShadcnMenubarPage.tsx b/use_cases/src/pages/shadcn/ShadcnMenubarPage.tsx new file mode 100644 index 0000000000..ba06b45342 --- /dev/null +++ b/use_cases/src/pages/shadcn/ShadcnMenubarPage.tsx @@ -0,0 +1,264 @@ +import React, { useState } from 'react'; +import { WebFListView } from '@openwebf/react-core-ui'; +import { + FlutterShadcnTheme, + FlutterShadcnMenubar, + FlutterShadcnMenubarMenu, + FlutterShadcnMenubarTrigger, + FlutterShadcnMenubarContent, + FlutterShadcnMenubarItem, + FlutterShadcnMenubarSeparator, + FlutterShadcnMenubarLabel, + FlutterShadcnMenubarSub, + FlutterShadcnMenubarSubTrigger, + FlutterShadcnMenubarSubContent, + FlutterShadcnMenubarCheckboxItem, + FlutterShadcnMenubarRadioGroup, + FlutterShadcnMenubarRadioItem, +} from '@openwebf/react-shadcn-ui'; + +export const ShadcnMenubarPage: React.FC = () => { + const [showBookmarksBar, setShowBookmarksBar] = useState(true); + const [showFullUrls, setShowFullUrls] = useState(false); + const [showStatusBar, setShowStatusBar] = useState(true); + const [selectedPerson, setSelectedPerson] = useState('benoit'); + + const handleAction = (action: string) => { + console.log(`Menubar action: ${action}`); + }; + + return ( + +
+ +

Menubar

+ +
+

Complete Menubar

+

+ A full-featured menubar with items, shortcuts, submenus, checkboxes, and radio groups. +

+ + {/* File Menu */} + + File + + handleAction('newTab')}> + New Tab + + handleAction('newWindow')}> + New Window + + + New Incognito Window + + + + Share + + handleAction('emailLink')}> + Email link + + handleAction('messages')}> + Messages + + handleAction('notes')}> + Notes + + + + + handleAction('print')}> + Print... + + + + + {/* Edit Menu */} + + Edit + + handleAction('undo')}> + Undo + + handleAction('redo')}> + Redo + + + + Find + + handleAction('searchWeb')}> + Search the web + + + handleAction('find')}> + Find... + + handleAction('findNext')}> + Find Next + + handleAction('findPrev')}> + Find Previous + + + + + handleAction('cut')}> + Cut + + handleAction('copy')}> + Copy + + handleAction('paste')}> + Paste + + + + + {/* View Menu */} + + View + + setShowBookmarksBar(!showBookmarksBar)} + > + Always Show Bookmarks Bar + + setShowFullUrls(!showFullUrls)} + > + Always Show Full URLs + + + handleAction('reload')}> + Reload + + + Force Reload + + + handleAction('toggleFullscreen')}> + Toggle Fullscreen + + + handleAction('hideSidebar')}> + Hide Sidebar + + + + + {/* Profiles Menu */} + + Profiles + + ) => { + if (e.detail?.value) { + setSelectedPerson(e.detail.value); + } + }} + > + + Andy + + + Benoit + + + Luis + + + + handleAction('editProfiles')}> + Edit... + + + handleAction('addProfile')}> + Add Profile... + + + + +
+ +
+

Simple Menubar

+

+ A minimal menubar with basic items and shortcuts. +

+ + + File + + handleAction('new')}> + New + + handleAction('open')}> + Open + + handleAction('save')}> + Save + + + handleAction('exit')}> + Exit + + + + + Help + + handleAction('about')}> + About + + + + +
+ +
+

Menubar with Status Bar Toggle

+

+ Demonstrates checkbox items to toggle visibility of UI elements. +

+ + + View + + setShowStatusBar(!showStatusBar)} + > + Status Bar + + setShowBookmarksBar(!showBookmarksBar)} + > + Bookmarks Bar + + setShowFullUrls(!showFullUrls)} + > + Full URLs + + + + + {showStatusBar && ( +
+

Status bar is visible

+
+ )} +
+
+
+
+ ); +}; From e4ca9152d8f9a8b7a98285e1cae6e6523a54af91 Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Sat, 14 Feb 2026 13:00:40 +0800 Subject: [PATCH 15/17] fix(react-shadcn-ui): enhance popover component and fix gesture conflicts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Popover Property Enhancements Wire up previously declared but unused properties on the popover component: - **placement**: Add `_placementToAnchor()` conversion that maps placement strings (top/bottom/left/right and compound variants like top-start) to `ShadAnchorAuto` anchors with correct follower/target alignment - **align**: Add new `align` property (start/center/end) for fine-grained positioning within the placement direction, with attribute binding and JS property binding support - **closeOnOutsideClick**: Pass through to `ShadPopover.closeOnTapOutside` (was declared but never wired up) - **Controller sync**: Add `ShadPopoverController` listener to sync native open/close state back to JS when popover is dismissed externally (e.g. outside tap) - **Safety**: Add `mounted` checks in postFrameCallback for controller ops - **Deprecation**: Fix `Colors.black.withOpacity()` → `.withValues(alpha:)` ## Gesture Arena Fix (popover, dropdown_menu, collapsible) Replace `GestureDetector` with `Listener` for trigger tap handling in three components: popover, dropdown_menu, and collapsible. **Root cause**: When a `GestureDetector` wraps a WebF `WidgetElement` containing interactive Flutter widgets (e.g. `ShadButton`), the inner widget's `TapGestureRecognizer` wins the Flutter gesture arena, preventing the outer `GestureDetector.onTap` from ever firing. This made popover triggers completely non-functional when using button children. **Fix**: `Listener` receives raw pointer events at the pointer level without participating in the gesture arena, so both the outer Listener AND the inner button receive events. A pointer-distance check (<20px) distinguishes taps from scroll gestures to avoid false triggers inside scrollable containers. ## React Wrapper Updates - Add `align` prop with documentation to `FlutterShadcnPopoverProps` - Update `placement` docs with all supported compound values - Add `align` to element interface and `attributeProps` ## Use Cases Updates Add three new demo sections to ShadcnPopoverPage: - Popover Placement (top/bottom/left/right) - Popover Alignment (start/center/end) - Controlled Popover (closeOnOutsideClick={false}) Co-Authored-By: Claude Opus 4.6 --- .../lib/src/components/collapsible.dart | 28 ++- .../lib/src/components/dropdown_menu.dart | 22 +- .../lib/src/components/popover.dart | 193 +++++++++++++++++- .../src/lib/src/components/popover.tsx | 23 ++- .../src/pages/shadcn/ShadcnPopoverPage.tsx | 94 +++++++++ 5 files changed, 341 insertions(+), 19 deletions(-) diff --git a/native_uis/webf_shadcn_ui/lib/src/components/collapsible.dart b/native_uis/webf_shadcn_ui/lib/src/components/collapsible.dart index 6c7e4b5ae7..07064f4ca0 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/collapsible.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/collapsible.dart @@ -51,6 +51,8 @@ class FlutterShadcnCollapsible extends FlutterShadcnCollapsibleBindings { class FlutterShadcnCollapsibleState extends WebFWidgetElementState { FlutterShadcnCollapsibleState(super.widgetElement); + Offset? _tapDownPosition; + @override FlutterShadcnCollapsible get widgetElement => super.widgetElement as FlutterShadcnCollapsible; @@ -74,12 +76,30 @@ class FlutterShadcnCollapsibleState extends WebFWidgetElementState { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (trigger != null) - GestureDetector( - onTap: widgetElement.disabled + // Use Listener instead of GestureDetector to avoid gesture arena + // conflicts with interactive child widgets (e.g. ShadButton). + Listener( + behavior: HitTestBehavior.opaque, + onPointerDown: widgetElement.disabled + ? null + : (event) { + _tapDownPosition = event.position; + }, + onPointerUp: widgetElement.disabled ? null - : () { - widgetElement.open = !widgetElement.open; + : (event) { + if (_tapDownPosition != null) { + final distance = + (event.position - _tapDownPosition!).distance; + _tapDownPosition = null; + if (distance < 20) { + widgetElement.open = !widgetElement.open; + } + } }, + onPointerCancel: (_) { + _tapDownPosition = null; + }, child: trigger, ), if (widgetElement.open && content != null) content, diff --git a/native_uis/webf_shadcn_ui/lib/src/components/dropdown_menu.dart b/native_uis/webf_shadcn_ui/lib/src/components/dropdown_menu.dart index 6a12d313e0..49e697451b 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/dropdown_menu.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/dropdown_menu.dart @@ -44,6 +44,7 @@ class FlutterShadcnDropdownMenuState extends WebFWidgetElementState { FlutterShadcnDropdownMenuState(super.widgetElement); final _popoverController = ShadPopoverController(); + Offset? _tapDownPosition; @override FlutterShadcnDropdownMenu get widgetElement => @@ -87,9 +88,24 @@ class FlutterShadcnDropdownMenuState extends WebFWidgetElementState { return ShadPopover( controller: _popoverController, popover: (context) => content ?? const SizedBox.shrink(), - child: GestureDetector( - onTap: () { - widgetElement.open = !widgetElement.open; + // Use Listener instead of GestureDetector to avoid gesture arena + // conflicts with interactive child widgets (e.g. ShadButton). + child: Listener( + behavior: HitTestBehavior.opaque, + onPointerDown: (event) { + _tapDownPosition = event.position; + }, + onPointerUp: (event) { + if (_tapDownPosition != null) { + final distance = (event.position - _tapDownPosition!).distance; + _tapDownPosition = null; + if (distance < 20) { + widgetElement.open = !widgetElement.open; + } + } + }, + onPointerCancel: (_) { + _tapDownPosition = null; }, child: trigger, ), diff --git a/native_uis/webf_shadcn_ui/lib/src/components/popover.dart b/native_uis/webf_shadcn_ui/lib/src/components/popover.dart index 8c0c5efeb7..bb45fa9a66 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/popover.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/popover.dart @@ -11,6 +11,111 @@ import 'package:webf/webf.dart'; import 'popover_bindings_generated.dart'; +/// Converts a placement string (e.g. 'top', 'bottom-start') to a +/// [ShadAnchorAuto] anchor for the popover. +ShadAnchorBase _placementToAnchor(String placement, String align) { + // Parse combined placement like "top-start", "bottom-end" + String side = placement; + String resolvedAlign = align; + if (placement.contains('-')) { + final parts = placement.split('-'); + side = parts[0]; + if (resolvedAlign == 'center') { + resolvedAlign = parts[1]; + } + } + + switch (side) { + case 'top': + return ShadAnchorAuto( + offset: const Offset(0, -4), + followerAnchor: _alignToFollower('top', resolvedAlign), + targetAnchor: _alignToTarget('top', resolvedAlign), + ); + case 'left': + return ShadAnchorAuto( + offset: const Offset(-4, 0), + followerAnchor: _alignToFollower('left', resolvedAlign), + targetAnchor: _alignToTarget('left', resolvedAlign), + ); + case 'right': + return ShadAnchorAuto( + offset: const Offset(4, 0), + followerAnchor: _alignToFollower('right', resolvedAlign), + targetAnchor: _alignToTarget('right', resolvedAlign), + ); + case 'bottom': + default: + return ShadAnchorAuto( + offset: const Offset(0, 4), + followerAnchor: _alignToFollower('bottom', resolvedAlign), + targetAnchor: _alignToTarget('bottom', resolvedAlign), + ); + } +} + +Alignment _alignToFollower(String side, String align) { + switch (side) { + case 'top': + return switch (align) { + 'start' => Alignment.topLeft, + 'end' => Alignment.topRight, + _ => Alignment.topCenter, + }; + case 'bottom': + return switch (align) { + 'start' => Alignment.bottomLeft, + 'end' => Alignment.bottomRight, + _ => Alignment.bottomCenter, + }; + case 'left': + return switch (align) { + 'start' => Alignment.topLeft, + 'end' => Alignment.bottomLeft, + _ => Alignment.centerLeft, + }; + case 'right': + return switch (align) { + 'start' => Alignment.topRight, + 'end' => Alignment.bottomRight, + _ => Alignment.centerRight, + }; + default: + return Alignment.bottomCenter; + } +} + +Alignment _alignToTarget(String side, String align) { + switch (side) { + case 'top': + return switch (align) { + 'start' => Alignment.topLeft, + 'end' => Alignment.topRight, + _ => Alignment.topCenter, + }; + case 'bottom': + return switch (align) { + 'start' => Alignment.bottomLeft, + 'end' => Alignment.bottomRight, + _ => Alignment.bottomCenter, + }; + case 'left': + return switch (align) { + 'start' => Alignment.topLeft, + 'end' => Alignment.bottomLeft, + _ => Alignment.centerLeft, + }; + case 'right': + return switch (align) { + 'start' => Alignment.topRight, + 'end' => Alignment.bottomRight, + _ => Alignment.centerRight, + }; + default: + return Alignment.bottomCenter; + } +} + /// WebF custom element that wraps shadcn_ui [ShadPopover]. /// /// Exposed as `` in the DOM. @@ -19,6 +124,7 @@ class FlutterShadcnPopover extends FlutterShadcnPopoverBindings { bool _open = false; String _placement = 'bottom'; + String _align = 'center'; bool _closeOnOutsideClick = true; @override @@ -50,6 +156,16 @@ class FlutterShadcnPopover extends FlutterShadcnPopoverBindings { } } + String get align => _align; + + set align(value) { + final String newValue = value?.toString() ?? 'center'; + if (newValue != _align) { + _align = newValue; + state?.requestUpdateState(() {}); + } + } + @override bool get closeOnOutsideClick => _closeOnOutsideClick; @@ -58,9 +174,35 @@ class FlutterShadcnPopover extends FlutterShadcnPopoverBindings { final bool v = value == true; if (v != _closeOnOutsideClick) { _closeOnOutsideClick = v; + state?.requestUpdateState(() {}); } } + @override + void initializeAttributes(Map attributes) { + super.initializeAttributes(attributes); + attributes['align'] = ElementAttributeProperty( + getter: () => align, + setter: (val) => align = val, + deleter: () => align = 'center', + ); + } + + static StaticDefinedBindingPropertyMap flutterShadcnPopoverExtraProperties = { + 'align': StaticDefinedBindingProperty( + getter: (element) => + castToType(element).align, + setter: (element, value) => + castToType(element).align = value, + ), + }; + + @override + List get properties => [ + ...super.properties, + flutterShadcnPopoverExtraProperties, + ]; + @override WebFWidgetElementState createState() => FlutterShadcnPopoverState(this); } @@ -69,13 +211,29 @@ class FlutterShadcnPopoverState extends WebFWidgetElementState { FlutterShadcnPopoverState(super.widgetElement); final _popoverController = ShadPopoverController(); + Offset? _tapDownPosition; @override FlutterShadcnPopover get widgetElement => super.widgetElement as FlutterShadcnPopover; + void _onControllerChanged() { + final isOpen = _popoverController.isOpen; + // Sync native open/close state back to JS and emit events. + if (widgetElement.open != isOpen) { + widgetElement.open = isOpen; + } + } + + @override + void initState() { + super.initState(); + _popoverController.addListener(_onControllerChanged); + } + @override void dispose() { + _popoverController.removeListener(_onControllerChanged); _popoverController.dispose(); super.dispose(); } @@ -101,20 +259,43 @@ class FlutterShadcnPopoverState extends WebFWidgetElementState { // Sync controller with open state if (widgetElement.open && !_popoverController.isOpen) { WidgetsBinding.instance.addPostFrameCallback((_) { - _popoverController.show(); + if (mounted) _popoverController.show(); }); } else if (!widgetElement.open && _popoverController.isOpen) { WidgetsBinding.instance.addPostFrameCallback((_) { - _popoverController.hide(); + if (mounted) _popoverController.hide(); }); } + final anchor = _placementToAnchor( + widgetElement.placement, + widgetElement.align, + ); + return ShadPopover( controller: _popoverController, + anchor: anchor, + closeOnTapOutside: widgetElement.closeOnOutsideClick, popover: (context) => content ?? const SizedBox.shrink(), - child: GestureDetector( - onTap: () { - widgetElement.open = !widgetElement.open; + // Use Listener instead of GestureDetector to avoid gesture arena + // conflicts with interactive child widgets (e.g. ShadButton). + // Listener receives raw pointer events without competing in the arena. + child: Listener( + behavior: HitTestBehavior.opaque, + onPointerDown: (event) { + _tapDownPosition = event.position; + }, + onPointerUp: (event) { + if (_tapDownPosition != null) { + final distance = (event.position - _tapDownPosition!).distance; + _tapDownPosition = null; + if (distance < 20) { + widgetElement.open = !widgetElement.open; + } + } + }, + onPointerCancel: (_) { + _tapDownPosition = null; }, child: trigger, ), @@ -171,7 +352,7 @@ class FlutterShadcnPopoverContentState extends WebFWidgetElementState { border: Border.all(color: theme.colorScheme.border), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.1), + color: Colors.black.withValues(alpha: 0.1), blurRadius: 10, ), ], diff --git a/packages/react-shadcn-ui/src/lib/src/components/popover.tsx b/packages/react-shadcn-ui/src/lib/src/components/popover.tsx index 733efda2de..90e9458cd1 100644 --- a/packages/react-shadcn-ui/src/lib/src/components/popover.tsx +++ b/packages/react-shadcn-ui/src/lib/src/components/popover.tsx @@ -7,14 +7,22 @@ export interface FlutterShadcnPopoverProps { */ open?: boolean; /** - * Placement of the popover. - * Options: 'top', 'bottom', 'left', 'right' - * Default: 'bottom' + * Placement of the popover relative to the trigger. + * Supported values: 'top', 'bottom', 'left', 'right', + * 'top-start', 'top-end', 'bottom-start', 'bottom-end', + * 'left-start', 'left-end', 'right-start', 'right-end'. + * @default 'bottom' */ placement?: string; /** - * Close when clicking outside. - * Default: true + * Alignment of the popover within its placement direction. + * Supported values: 'start', 'center', 'end'. + * @default 'center' + */ + align?: string; + /** + * Whether to close the popover when clicking outside. + * @default true */ closeOnOutsideClick?: boolean; /** @@ -48,7 +56,9 @@ export interface FlutterShadcnPopoverElement extends WebFElementWithMethods<{ open?: boolean; /** Placement of the popover. */ placement?: string; - /** Close when clicking outside. */ + /** Alignment within placement direction. */ + align?: string; + /** Whether to close when clicking outside. */ closeOnOutsideClick?: boolean; } /** @@ -83,6 +93,7 @@ export const FlutterShadcnPopover = createWebFComponent {
+
+

Popover Placement

+

+ Use the placement prop to control where the popover appears. +

+
+ + + Top + + +

Placed on top

+
+
+ + + Bottom + + +

Placed on bottom

+
+
+ + + Left + + +

Placed on left

+
+
+ + + Right + + +

Placed on right

+
+
+
+
+ +
+

Popover Alignment

+

+ Use the align prop to fine-tune positioning within the placement direction. +

+
+ + + Bottom Start + + +

Aligned to start

+
+
+ + + Bottom Center + + +

Aligned to center

+
+
+ + + Bottom End + + +

Aligned to end

+
+
+
+
+ +
+

Controlled Popover

+

+ Use closeOnOutsideClick={false} to prevent closing when clicking outside. +

+ + + Sticky Popover + + +
+

Sticky popover

+

+ This won't close when clicking outside. Click the trigger again to close. +

+
+
+
+
+

Popover with Actions

From 2bfa99517f307cb483bf2a84f17c18134747cadd Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Sat, 14 Feb 2026 13:22:02 +0800 Subject: [PATCH 16/17] feat(react-shadcn-ui): enhance progress component with color, height, and radius props Add backgroundColor, color, minHeight, and borderRadius properties to the progress component. Fix the indeterminate variant which was defined but never wired up in the build method. Update use_cases demo to cover all new properties. Also fix input_otp maxlength type mismatch after codegen regeneration. Co-Authored-By: Claude Opus 4.6 --- .../lib/src/components/input_otp.dart | 8 +- .../input_otp_bindings_generated.dart | 8 +- .../lib/src/components/progress.d.ts | 25 ++++ .../lib/src/components/progress.dart | 94 +++++++++++++- .../progress_bindings_generated.dart | 48 +++++++ .../src/lib/src/components/input_otp.tsx | 83 +++++++----- .../src/lib/src/components/popover.tsx | 23 +--- .../src/lib/src/components/progress.tsx | 36 ++++++ .../src/pages/shadcn/ShadcnProgressPage.tsx | 118 ++++++++++++++++++ 9 files changed, 381 insertions(+), 62 deletions(-) diff --git a/native_uis/webf_shadcn_ui/lib/src/components/input_otp.dart b/native_uis/webf_shadcn_ui/lib/src/components/input_otp.dart index b78c52040f..ae27c778ef 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/input_otp.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/input_otp.dart @@ -15,16 +15,16 @@ import 'input_otp_bindings_generated.dart'; class FlutterShadcnInputOtp extends FlutterShadcnInputOtpBindings { FlutterShadcnInputOtp(super.context); - String? _maxlength; + String _maxlength = ''; String? _value; bool _disabled = false; @override - String? get maxlength => _maxlength; + String get maxlength => _maxlength; @override set maxlength(value) { - final String? v = value?.toString(); + final String v = value?.toString() ?? ''; if (v != _maxlength) { _maxlength = v; state?.requestUpdateState(() {}); @@ -55,7 +55,7 @@ class FlutterShadcnInputOtp extends FlutterShadcnInputOtpBindings { } } - int get maxLength => int.tryParse(_maxlength ?? '') ?? 6; + int get maxLength => int.tryParse(_maxlength) ?? 6; @override WebFWidgetElementState createState() => FlutterShadcnInputOtpState(this); diff --git a/native_uis/webf_shadcn_ui/lib/src/components/input_otp_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/input_otp_bindings_generated.dart index 38fce5add4..0dcf6546b1 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/input_otp_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/input_otp_bindings_generated.dart @@ -10,7 +10,7 @@ import 'package:webf/webf.dart'; abstract class FlutterShadcnInputOtpBindings extends WidgetElement { FlutterShadcnInputOtpBindings(super.context); - String? get maxlength; + String get maxlength; set maxlength(value); String? get value; set value(value); @@ -20,9 +20,9 @@ abstract class FlutterShadcnInputOtpBindings extends WidgetElement { void initializeAttributes(Map attributes) { super.initializeAttributes(attributes); attributes['maxlength'] = ElementAttributeProperty( - getter: () => maxlength?.toString(), + getter: () => maxlength.toString(), setter: (value) => maxlength = value, - deleter: () => maxlength = null + deleter: () => maxlength = '' ); attributes['value'] = ElementAttributeProperty( getter: () => value?.toString(), @@ -57,4 +57,4 @@ abstract class FlutterShadcnInputOtpBindings extends WidgetElement { ...super.properties, flutterShadcnInputOtpProperties, ]; -} +} \ No newline at end of file diff --git a/native_uis/webf_shadcn_ui/lib/src/components/progress.d.ts b/native_uis/webf_shadcn_ui/lib/src/components/progress.d.ts index 09a6ffe671..9bd7197c53 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/progress.d.ts +++ b/native_uis/webf_shadcn_ui/lib/src/components/progress.d.ts @@ -31,6 +31,31 @@ interface FlutterShadcnProgressProperties { * Default: 'default' */ variant?: string; + + /** + * Background color of the progress track. + * Accepts hex color string (e.g. '#e0e0e0', '#FF808080'). + */ + backgroundColor?: string; + + /** + * Color of the progress indicator. + * Accepts hex color string (e.g. '#3b82f6', '#FF0000FF'). + */ + color?: string; + + /** + * Minimum height of the progress bar in logical pixels. + * Default: 16 + */ + minHeight?: string; + + /** + * Border radius of the progress bar in logical pixels. + * Applied uniformly to all corners. + * Default: 16 + */ + borderRadius?: string; } interface FlutterShadcnProgressEvents {} diff --git a/native_uis/webf_shadcn_ui/lib/src/components/progress.dart b/native_uis/webf_shadcn_ui/lib/src/components/progress.dart index ad3850376a..4800b6f5ad 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/progress.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/progress.dart @@ -18,6 +18,10 @@ class FlutterShadcnProgress extends FlutterShadcnProgressBindings { double _value = 0; double _max = 100; String _variant = 'default'; + Color? _backgroundColor; + Color? _color; + double? _minHeight; + double? _borderRadius; @override String? get value => _value.toString(); @@ -58,6 +62,73 @@ class FlutterShadcnProgress extends FlutterShadcnProgressBindings { } } + @override + String? get backgroundColor => _backgroundColor != null + ? '#${_backgroundColor!.value.toRadixString(16).padLeft(8, '0')}' + : null; + + @override + set backgroundColor(value) { + final newValue = value != null ? _parseColor(value.toString()) : null; + if (newValue != _backgroundColor) { + _backgroundColor = newValue; + state?.requestUpdateState(() {}); + } + } + + @override + String? get color => _color != null + ? '#${_color!.value.toRadixString(16).padLeft(8, '0')}' + : null; + + @override + set color(value) { + final newValue = value != null ? _parseColor(value.toString()) : null; + if (newValue != _color) { + _color = newValue; + state?.requestUpdateState(() {}); + } + } + + @override + String? get minHeight => _minHeight?.toString(); + + @override + set minHeight(value) { + final newValue = value != null ? double.tryParse(value.toString()) : null; + if (newValue != _minHeight) { + _minHeight = newValue; + state?.requestUpdateState(() {}); + } + } + + @override + String? get borderRadius => _borderRadius?.toString(); + + @override + set borderRadius(value) { + final newValue = value != null ? double.tryParse(value.toString()) : null; + if (newValue != _borderRadius) { + _borderRadius = newValue; + state?.requestUpdateState(() {}); + } + } + + static Color? _parseColor(String value) { + final trimmed = value.trim().toLowerCase(); + if (trimmed.startsWith('#')) { + final hex = trimmed.substring(1); + if (hex.length == 6) { + final intValue = int.tryParse(hex, radix: 16); + if (intValue != null) return Color(0xFF000000 | intValue); + } else if (hex.length == 8) { + final intValue = int.tryParse(hex, radix: 16); + if (intValue != null) return Color(intValue); + } + } + return null; + } + @override WebFWidgetElementState createState() => FlutterShadcnProgressState(this); } @@ -71,10 +142,25 @@ class FlutterShadcnProgressState extends WebFWidgetElementState { @override Widget build(BuildContext context) { - final progress = widgetElement._max > 0 - ? widgetElement._value / widgetElement._max - : 0.0; + final isIndeterminate = widgetElement._variant == 'indeterminate'; + + final double? progressValue; + if (isIndeterminate) { + progressValue = null; + } else { + progressValue = widgetElement._max > 0 + ? widgetElement._value / widgetElement._max + : 0.0; + } - return ShadProgress(value: progress); + return ShadProgress( + value: progressValue, + backgroundColor: widgetElement._backgroundColor, + color: widgetElement._color, + minHeight: widgetElement._minHeight, + borderRadius: widgetElement._borderRadius != null + ? BorderRadius.circular(widgetElement._borderRadius!) + : null, + ); } } diff --git a/native_uis/webf_shadcn_ui/lib/src/components/progress_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/progress_bindings_generated.dart index a2097a0316..de5b88b19c 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/progress_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/progress_bindings_generated.dart @@ -16,6 +16,14 @@ abstract class FlutterShadcnProgressBindings extends WidgetElement { set max(value); String? get variant; set variant(value); + String? get backgroundColor; + set backgroundColor(value); + String? get color; + set color(value); + String? get minHeight; + set minHeight(value); + String? get borderRadius; + set borderRadius(value); @override void initializeAttributes(Map attributes) { super.initializeAttributes(attributes); @@ -34,6 +42,26 @@ abstract class FlutterShadcnProgressBindings extends WidgetElement { setter: (value) => variant = value, deleter: () => variant = null ); + attributes['background-color'] = ElementAttributeProperty( + getter: () => backgroundColor?.toString(), + setter: (value) => backgroundColor = value, + deleter: () => backgroundColor = null + ); + attributes['color'] = ElementAttributeProperty( + getter: () => color?.toString(), + setter: (value) => color = value, + deleter: () => color = null + ); + attributes['min-height'] = ElementAttributeProperty( + getter: () => minHeight?.toString(), + setter: (value) => minHeight = value, + deleter: () => minHeight = null + ); + attributes['border-radius'] = ElementAttributeProperty( + getter: () => borderRadius?.toString(), + setter: (value) => borderRadius = value, + deleter: () => borderRadius = null + ); } static StaticDefinedBindingPropertyMap flutterShadcnProgressProperties = { 'value': StaticDefinedBindingProperty( @@ -51,6 +79,26 @@ abstract class FlutterShadcnProgressBindings extends WidgetElement { setter: (element, value) => castToType(element).variant = value, ), + 'backgroundColor': StaticDefinedBindingProperty( + getter: (element) => castToType(element).backgroundColor, + setter: (element, value) => + castToType(element).backgroundColor = value, + ), + 'color': StaticDefinedBindingProperty( + getter: (element) => castToType(element).color, + setter: (element, value) => + castToType(element).color = value, + ), + 'minHeight': StaticDefinedBindingProperty( + getter: (element) => castToType(element).minHeight, + setter: (element, value) => + castToType(element).minHeight = value, + ), + 'borderRadius': StaticDefinedBindingProperty( + getter: (element) => castToType(element).borderRadius, + setter: (element, value) => + castToType(element).borderRadius = value, + ), }; @override List get properties => [ diff --git a/packages/react-shadcn-ui/src/lib/src/components/input_otp.tsx b/packages/react-shadcn-ui/src/lib/src/components/input_otp.tsx index 2a37657233..d11647bdaf 100644 --- a/packages/react-shadcn-ui/src/lib/src/components/input_otp.tsx +++ b/packages/react-shadcn-ui/src/lib/src/components/input_otp.tsx @@ -41,7 +41,7 @@ export interface FlutterShadcnInputOtpProps { } export interface FlutterShadcnInputOtpElement extends WebFElementWithMethods<{ }> { - /** Maximum number of characters. */ + /** Maximum number of characters (required). */ maxlength: string; /** Current OTP value. */ value?: string; @@ -50,26 +50,31 @@ export interface FlutterShadcnInputOtpElement extends WebFElementWithMethods<{ } /** * Properties for - * Accessible one-time password input with individual character slots. - * +Accessible one-time password input with individual character slots. +@example +```html + + + + + + + + + + + + + +``` + * * @example * ```tsx - * + * * - * - * - * - * - * - * - * - * - * - * - * + * Content * * ``` */ @@ -104,6 +109,7 @@ export const FlutterShadcnInputOtp = createWebFComponent - * Groups OTP slots together visually. - * +Groups OTP slots together visually. + * * @example * ```tsx - * - * - * - * - * + * + * + * Content * * ``` */ @@ -155,6 +161,7 @@ export const FlutterShadcnInputOtpGroup = createWebFComponent - * Individual character slot managed by the parent input. - * +Individual character slot managed by the parent input. + * * @example * ```tsx - * - * + * + * + * Content + * * ``` */ export const FlutterShadcnInputOtpSlot = createWebFComponent({ @@ -202,6 +213,7 @@ export const FlutterShadcnInputOtpSlot = createWebFComponent - * Visual separator between OTP groups. - * +Visual separator between OTP groups. + * * @example * ```tsx - * - * + * + * + * Content + * * ``` */ export const FlutterShadcnInputOtpSeparator = createWebFComponent({ @@ -249,5 +265,6 @@ export const FlutterShadcnInputOtpSeparator = createWebFComponent @@ -69,9 +98,16 @@ export const FlutterShadcnProgress = createWebFComponent {
+
+

Indeterminate

+
+
+

Default indeterminate animation

+ +
+
+

Indeterminate with custom color

+ +
+
+
+ +
+

Custom Colors

+
+
+

Blue indicator

+ +
+
+

Green indicator

+ +
+
+

Red indicator

+ +
+
+

Purple indicator with custom background

+ +
+
+

Orange indicator with dark background

+ +
+
+
+ +
+

Custom Height

+
+
+

Thin (4px)

+ +
+
+

Default (16px)

+ +
+
+

Thick (24px)

+ +
+
+

Extra thick (32px)

+ +
+
+
+ +
+

Custom Border Radius

+
+
+

Square corners (0)

+ +
+
+

Slightly rounded (4)

+ +
+
+

Default rounded (16)

+ +
+
+
+ +
+

Combined Customization

+
+
+

Download Progress

+

2.4 GB / 4.0 GB

+ +
+
+

Storage Used

+

85% of 100 GB

+ +
+
+

Battery Level

+

72% remaining

+ +
+
+
+

Multi-step Progress

From d8a1c1ce14bc0d5498ebfc4d062111a9fcc7702b Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Sat, 14 Feb 2026 16:00:49 +0800 Subject: [PATCH 17/17] feat(webf-shadcn-ui): rewrite RadioGroup component with proper naming and manual rendering Rename radio component from `flutter-shadcn-radio`/`flutter-shadcn-radio-item` to `flutter-shadcn-radio-group`/`flutter-shadcn-radio-group-item` to match the shadcn/ui naming convention. Rewrite the implementation to render radio items manually rather than using ShadRadioGroup/ShadRadio, because ShadRadio relies on ShadProvider (InheritedWidget) which does not work correctly in WebF's widget tree. Key changes: - **Naming**: Renamed all Dart, TypeScript, and React files from `radio` to `radio_group`, updated element registrations, exports, and documentation. - **Manual radio rendering**: Build radio circles manually using theme values from `ShadTheme.of(context).radioTheme` to match the visual style of ShadRadio (outer ring border + filled inner circle when selected). - **Text extraction pattern**: Extract label text from child nodes recursively (matching the accordion component pattern) instead of using `toWidget()` which creates child render objects that interfere with gesture detection in WebF's widget tree. - **Hit testing**: Use `GestureDetector` with `HitTestBehavior.opaque` to ensure taps register on the entire radio row, since DecoratedBox/SizedBox don't participate in hit testing by default. - **CustomEvent dispatch**: Dispatch `CustomEvent('change', detail: {'value': itemValue})` instead of plain `Event('change')` so React handlers can read `e.detail.value` (consistent with select/calendar components). - **Recursive item search**: `_getRadioItems()` recursively walks child nodes to find `FlutterShadcnRadioGroupItem` descendants, supporting arbitrary DOM nesting between parent and items. - **Fallback value resolution**: Added `_resolveItemValue()` that tries `_itemValue`, then `getAttribute('value')`, with a final fallback to lowercase label text when no explicit value is set. - **Use case updates**: Simplified ShadcnRadioPage.tsx to use the new component names with labels inside items. Updated BasicFormElementsPage with input event fallback for native radio controls. Co-Authored-By: Claude Opus 4.6 --- .../webf_shadcn_ui/IMPLEMENTATION_STATUS.md | 4 +- native_uis/webf_shadcn_ui/README.md | 2 +- .../lib/src/components/radio.d.ts | 63 ---- .../lib/src/components/radio.dart | 172 ---------- .../lib/src/components/radio_group.d.ts | 63 ++++ .../lib/src/components/radio_group.dart | 322 ++++++++++++++++++ ...rt => radio_group_bindings_generated.dart} | 22 +- .../webf_shadcn_ui/lib/webf_shadcn_ui.dart | 8 +- packages/react-shadcn-ui/README.md | 2 +- packages/react-shadcn-ui/src/index.ts | 4 +- .../components/{radio.tsx => radio_group.tsx} | 60 ++-- use_cases/src/pages/BasicFormElementsPage.tsx | 34 +- .../src/pages/shadcn/ShadcnRadioPage.tsx | 137 ++++---- 13 files changed, 525 insertions(+), 368 deletions(-) delete mode 100644 native_uis/webf_shadcn_ui/lib/src/components/radio.d.ts delete mode 100644 native_uis/webf_shadcn_ui/lib/src/components/radio.dart create mode 100644 native_uis/webf_shadcn_ui/lib/src/components/radio_group.d.ts create mode 100644 native_uis/webf_shadcn_ui/lib/src/components/radio_group.dart rename native_uis/webf_shadcn_ui/lib/src/components/{radio_bindings_generated.dart => radio_group_bindings_generated.dart} (68%) rename packages/react-shadcn-ui/src/lib/src/components/{radio.tsx => radio_group.tsx} (63%) diff --git a/native_uis/webf_shadcn_ui/IMPLEMENTATION_STATUS.md b/native_uis/webf_shadcn_ui/IMPLEMENTATION_STATUS.md index fa16f55885..1e762db91d 100644 --- a/native_uis/webf_shadcn_ui/IMPLEMENTATION_STATUS.md +++ b/native_uis/webf_shadcn_ui/IMPLEMENTATION_STATUS.md @@ -29,7 +29,7 @@ native_uis/webf_shadcn_ui/ | Input | `` | input.dart | input.d.ts | Done | | Textarea | `` | textarea.dart | textarea.d.ts | Done | | Checkbox | `` | checkbox.dart | checkbox.d.ts | Done | -| Radio | `` | radio.dart | radio.d.ts | Done | +| RadioGroup | `` | radio_group.dart | radio_group.d.ts | Done | | Switch | `` | switch.dart | switch.d.ts | Done | | Select | `` | select.dart | select.d.ts | Done | | Slider | `` | slider.dart | slider.d.ts | Done | @@ -144,7 +144,7 @@ The following slot elements are implemented for compositional components: - `` ### Other Slots -- `` +- `` - `` - `` - `` diff --git a/native_uis/webf_shadcn_ui/README.md b/native_uis/webf_shadcn_ui/README.md index 68b77673ce..3457799b8b 100644 --- a/native_uis/webf_shadcn_ui/README.md +++ b/native_uis/webf_shadcn_ui/README.md @@ -58,7 +58,7 @@ void main() { - `` - Text input field - `` - Multi-line text input - `` - Checkbox control -- `` - Radio button group +- `` - Radio button group - `` - Toggle switch - `` - Dropdown select - `` - Range slider diff --git a/native_uis/webf_shadcn_ui/lib/src/components/radio.d.ts b/native_uis/webf_shadcn_ui/lib/src/components/radio.d.ts deleted file mode 100644 index 4b55b019f0..0000000000 --- a/native_uis/webf_shadcn_ui/lib/src/components/radio.d.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2024-present The OpenWebF Company. All rights reserved. - * Licensed under the Apache License, Version 2.0. - */ - -/** - * Properties for - * - * Container for radio button groups. - * - * @example - * ```html - * - * Option 1 - * Option 2 - * - * ``` - */ -interface FlutterShadcnRadioProperties { - /** - * Currently selected value. - */ - value?: string; - - /** - * Disable all radio items. - */ - disabled?: boolean; - - /** - * Orientation of the radio group. - * Options: 'horizontal', 'vertical' - * Default: 'vertical' - */ - orientation?: string; -} - -/** - * Events emitted by - */ -interface FlutterShadcnRadioEvents { - /** Fired when selection changes. */ - change: Event; -} - -/** - * Properties for - * - * Individual radio button option. - */ -interface FlutterShadcnRadioItemProperties { - /** - * Value of this radio option. - */ - value: string; - - /** - * Disable this specific radio item. - */ - disabled?: boolean; -} - -interface FlutterShadcnRadioItemEvents {} diff --git a/native_uis/webf_shadcn_ui/lib/src/components/radio.dart b/native_uis/webf_shadcn_ui/lib/src/components/radio.dart deleted file mode 100644 index 879fb39dc7..0000000000 --- a/native_uis/webf_shadcn_ui/lib/src/components/radio.dart +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2024-present The OpenWebF Company. All rights reserved. - * Licensed under the Apache License, Version 2.0. - */ - -import 'package:flutter/material.dart'; -import 'package:shadcn_ui/shadcn_ui.dart'; -import 'package:webf/rendering.dart'; -import 'package:webf/webf.dart'; - -import 'radio_bindings_generated.dart'; - -/// WebF custom element that wraps shadcn_ui [ShadRadioGroup]. -/// -/// Exposed as `` in the DOM. -class FlutterShadcnRadio extends FlutterShadcnRadioBindings { - FlutterShadcnRadio(super.context); - - String? _value; - bool _disabled = false; - String _orientation = 'vertical'; - - @override - String? get value => _value; - - @override - set value(value) { - final String? v = value?.toString(); - if (v != _value) { - _value = v; - state?.requestUpdateState(() {}); - } - } - - @override - bool get disabled => _disabled; - - @override - set disabled(value) { - final bool v = value == true || value == 'true' || value == ''; - if (v != _disabled) { - _disabled = v; - state?.requestUpdateState(() {}); - } - } - - @override - String? get orientation => _orientation; - - @override - set orientation(value) { - final String newValue = value?.toString() ?? 'vertical'; - if (newValue != _orientation) { - _orientation = newValue; - state?.requestUpdateState(() {}); - } - } - - bool get isHorizontal => _orientation.toLowerCase() == 'horizontal'; - - @override - WebFWidgetElementState createState() => FlutterShadcnRadioState(this); -} - -class FlutterShadcnRadioState extends WebFWidgetElementState { - FlutterShadcnRadioState(super.widgetElement); - - @override - FlutterShadcnRadio get widgetElement => - super.widgetElement as FlutterShadcnRadio; - - List _getRadioItems() { - return widgetElement.childNodes - .whereType() - .toList(); - } - - @override - Widget build(BuildContext context) { - final items = _getRadioItems(); - - final radioItems = items.map((item) { - Widget? labelWidget; - if (item.childNodes.isNotEmpty) { - labelWidget = WebFWidgetElementChild( - child: item.childNodes.first.toWidget(), - ); - } - - return ShadRadio( - value: item._itemValue ?? '', - enabled: !widgetElement.disabled && !item._itemDisabled, - label: labelWidget, - ); - }).toList(); - - return ShadRadioGroup( - initialValue: widgetElement.value, - enabled: !widgetElement.disabled, - onChanged: (value) { - widgetElement._value = value; - widgetElement.dispatchEvent(Event('change')); - }, - items: radioItems, - ); - } -} - -/// WebF custom element for individual radio items. -/// -/// Exposed as `` in the DOM. -class FlutterShadcnRadioItem extends WidgetElement { - FlutterShadcnRadioItem(super.context); - - String? _itemValue; - bool _itemDisabled = false; - - String? get value => _itemValue; - - set value(value) { - final String? v = value?.toString(); - if (v != _itemValue) { - _itemValue = v; - _notifyParent(); - } - } - - bool get disabled => _itemDisabled; - - set disabled(value) { - final bool v = value == true || value == 'true' || value == ''; - if (v != _itemDisabled) { - _itemDisabled = v; - _notifyParent(); - } - } - - void _notifyParent() { - final parent = parentNode; - if (parent is FlutterShadcnRadio) { - parent.state?.requestUpdateState(() {}); - } - } - - @override - void initializeAttributes(Map attributes) { - super.initializeAttributes(attributes); - attributes['value'] = ElementAttributeProperty( - getter: () => value?.toString(), - setter: (v) => value = v, - deleter: () => value = null, - ); - attributes['disabled'] = ElementAttributeProperty( - getter: () => disabled.toString(), - setter: (value) => disabled = value == 'true' || value == '', - deleter: () => disabled = false, - ); - } - - @override - WebFWidgetElementState createState() => FlutterShadcnRadioItemState(this); -} - -class FlutterShadcnRadioItemState extends WebFWidgetElementState { - FlutterShadcnRadioItemState(super.widgetElement); - - @override - Widget build(BuildContext context) { - // This widget is built by the parent FlutterShadcnRadio - return const SizedBox.shrink(); - } -} diff --git a/native_uis/webf_shadcn_ui/lib/src/components/radio_group.d.ts b/native_uis/webf_shadcn_ui/lib/src/components/radio_group.d.ts new file mode 100644 index 0000000000..bfd9a819ba --- /dev/null +++ b/native_uis/webf_shadcn_ui/lib/src/components/radio_group.d.ts @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024-present The OpenWebF Company. All rights reserved. + * Licensed under the Apache License, Version 2.0. + */ + +/** + * Properties for + * + * Container for radio button groups. + * + * @example + * ```html + * + * Option 1 + * Option 2 + * + * ``` + */ +interface FlutterShadcnRadioGroupProperties { + /** + * Currently selected value. + */ + value?: string; + + /** + * Disable all radio items. + */ + disabled?: boolean; + + /** + * Orientation of the radio group. + * Options: 'horizontal', 'vertical' + * Default: 'vertical' + */ + orientation?: string; +} + +/** + * Events emitted by + */ +interface FlutterShadcnRadioGroupEvents { + /** Fired when selection changes. detail.value contains the selected value. */ + change: CustomEvent<{ value: string }>; +} + +/** + * Properties for + * + * Individual radio button option. + */ +interface FlutterShadcnRadioGroupItemProperties { + /** + * Value of this radio option. + */ + value: string; + + /** + * Disable this specific radio item. + */ + disabled?: boolean; +} + +interface FlutterShadcnRadioGroupItemEvents {} diff --git a/native_uis/webf_shadcn_ui/lib/src/components/radio_group.dart b/native_uis/webf_shadcn_ui/lib/src/components/radio_group.dart new file mode 100644 index 0000000000..a4903baed4 --- /dev/null +++ b/native_uis/webf_shadcn_ui/lib/src/components/radio_group.dart @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2024-present The OpenWebF Company. All rights reserved. + * Licensed under the Apache License, Version 2.0. + */ + +import 'package:flutter/material.dart'; +import 'package:shadcn_ui/shadcn_ui.dart'; +import 'package:webf/webf.dart'; + +import 'radio_group_bindings_generated.dart'; + +/// WebF custom element that wraps shadcn_ui [ShadRadioGroup]. +/// +/// Exposed as `` in the DOM. +/// +/// Note: We render radio items manually rather than using [ShadRadioGroup] +/// because [ShadRadio] relies on [ShadProvider] (InheritedWidget) which +/// does not work correctly in WebF's widget tree. +class FlutterShadcnRadioGroup extends FlutterShadcnRadioGroupBindings { + FlutterShadcnRadioGroup(super.context); + + String? _value; + bool _disabled = false; + String _orientation = 'vertical'; + + @override + String? get value => _value; + + @override + set value(value) { + final String? v = value?.toString(); + if (v != _value) { + _value = v; + state?.requestUpdateState(() {}); + } + } + + @override + bool get disabled => _disabled; + + @override + set disabled(value) { + final bool v = value == true || value == 'true' || value == ''; + if (v != _disabled) { + _disabled = v; + state?.requestUpdateState(() {}); + } + } + + @override + String? get orientation => _orientation; + + @override + set orientation(value) { + final String newValue = value?.toString() ?? 'vertical'; + if (newValue != _orientation) { + _orientation = newValue; + state?.requestUpdateState(() {}); + } + } + + bool get isHorizontal => _orientation.toLowerCase() == 'horizontal'; + + @override + WebFWidgetElementState createState() => FlutterShadcnRadioGroupState(this); +} + +class FlutterShadcnRadioGroupState extends WebFWidgetElementState { + FlutterShadcnRadioGroupState(super.widgetElement); + + @override + FlutterShadcnRadioGroup get widgetElement => + super.widgetElement as FlutterShadcnRadioGroup; + + /// Recursively find all [FlutterShadcnRadioGroupItem] descendants. + List _getRadioItems() { + return _findItemsRecursive(widgetElement.childNodes); + } + + List _findItemsRecursive( + Iterable nodes) { + final items = []; + for (final node in nodes) { + if (node is FlutterShadcnRadioGroupItem) { + items.add(node); + } else if (node.childNodes.isNotEmpty) { + items.addAll(_findItemsRecursive(node.childNodes)); + } + } + return items; + } + + /// Extract text content from a list of nodes recursively. + String _extractTextContent(Iterable nodes) { + final buffer = StringBuffer(); + for (final node in nodes) { + if (node is TextNode) { + buffer.write(node.data); + } else if (node.childNodes.isNotEmpty) { + buffer.write(_extractTextContent(node.childNodes)); + } + } + return buffer.toString().trim(); + } + + void _selectValue(String? itemValue) { + if (widgetElement.disabled) return; + if (itemValue != null && itemValue != widgetElement._value) { + widgetElement._value = itemValue; + widgetElement + .dispatchEvent(CustomEvent('change', detail: {'value': itemValue})); + setState(() {}); + } + } + + String? _resolveItemValue(FlutterShadcnRadioGroupItem item) { + final String? internalValue = item._itemValue; + if (internalValue != null && internalValue.isNotEmpty) { + return internalValue; + } + + final String? attrValue = item.getAttribute('value'); + if (attrValue != null && attrValue.isNotEmpty) { + return attrValue; + } + + return null; + } + + @override + Widget build(BuildContext context) { + final theme = ShadTheme.of(context); + final items = _getRadioItems(); + + final radioWidgets = + items.map((item) => _buildRadioItem(item, theme)).toList(); + + if (widgetElement.isHorizontal) { + return Wrap( + spacing: theme.radioTheme.spacing ?? 4, + runSpacing: theme.radioTheme.runSpacing ?? 0, + children: radioWidgets, + ); + } + + return Wrap( + direction: Axis.vertical, + spacing: theme.radioTheme.spacing ?? 4, + runSpacing: theme.radioTheme.runSpacing ?? 0, + children: radioWidgets, + ); + } + + /// Build a single radio item matching [ShadRadio] visual style. + Widget _buildRadioItem( + FlutterShadcnRadioGroupItem item, + ShadThemeData theme, + ) { + var itemValue = _resolveItemValue(item); + final isEnabled = !widgetElement.disabled && !item._itemDisabled; + + // Read theme values matching ShadRadio defaults + final effectiveSize = theme.radioTheme.size ?? 16.0; + final effectiveCircleSize = theme.radioTheme.circleSize ?? 10.0; + final effectiveColor = + theme.radioTheme.color ?? theme.colorScheme.primary; + final effectivePadding = theme.radioTheme.padding ?? + const EdgeInsetsDirectional.only(start: 8); + final effectiveRadioPadding = + theme.radioTheme.radioPadding ?? const EdgeInsets.only(top: 1); + + // Extract label text from child nodes (like accordion pattern) + final labelText = _extractTextContent(item.childNodes); + + // Defensive fallback when value binding is not available. + if (itemValue == null && labelText.isNotEmpty) { + itemValue = labelText.toLowerCase(); + } + + final isSelected = itemValue != null && widgetElement.value == itemValue; + + // Radio circle: outer ring + inner fill when selected + final radioCircle = SizedBox.square( + dimension: effectiveSize, + child: DecoratedBox( + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: effectiveColor, + width: 1, + ), + ), + child: isSelected + ? Center( + child: SizedBox.square( + dimension: effectiveCircleSize, + child: DecoratedBox( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: effectiveColor, + ), + ), + ), + ) + : null, + ), + ); + + Widget row = Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: effectiveRadioPadding, + child: radioCircle, + ), + if (labelText.isNotEmpty) + Flexible( + child: Padding( + padding: effectivePadding, + child: Text( + labelText, + style: theme.textTheme.muted.copyWith( + fontWeight: FontWeight.w500, + color: theme.colorScheme.foreground, + ), + ), + ), + ), + ], + ), + ); + + if (!isEnabled) { + return AbsorbPointer( + absorbing: true, + child: Opacity( + opacity: theme.disabledOpacity, + child: row, + ), + ); + } + + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => _selectValue(itemValue), + child: row, + ); + } +} + +/// WebF custom element for individual radio items. +/// +/// Exposed as `` in the DOM. +class FlutterShadcnRadioGroupItem extends WidgetElement { + FlutterShadcnRadioGroupItem(super.context); + + String? _itemValue; + bool _itemDisabled = false; + + String? get value => _itemValue; + + set value(value) { + final String? v = value?.toString(); + if (v != _itemValue) { + _itemValue = v; + _notifyParentGroup(); + } + } + + bool get disabled => _itemDisabled; + + set disabled(value) { + final bool v = value == true || value == 'true' || value == ''; + if (v != _itemDisabled) { + _itemDisabled = v; + _notifyParentGroup(); + } + } + + /// Walk up the tree to find the parent [FlutterShadcnRadioGroup]. + void _notifyParentGroup() { + Node? current = parentNode; + while (current != null) { + if (current is FlutterShadcnRadioGroup) { + current.state?.requestUpdateState(() {}); + return; + } + current = current.parentNode; + } + } + + @override + void initializeAttributes(Map attributes) { + super.initializeAttributes(attributes); + attributes['value'] = ElementAttributeProperty( + getter: () => value?.toString(), + setter: (v) => value = v, + deleter: () => value = null, + ); + attributes['disabled'] = ElementAttributeProperty( + getter: () => disabled.toString(), + setter: (value) => disabled = value == 'true' || value == '', + deleter: () => disabled = false, + ); + } + + @override + WebFWidgetElementState createState() => + FlutterShadcnRadioGroupItemState(this); +} + +class FlutterShadcnRadioGroupItemState extends WebFWidgetElementState { + FlutterShadcnRadioGroupItemState(super.widgetElement); + + @override + Widget build(BuildContext context) { + // This widget is built by the parent FlutterShadcnRadioGroup + return const SizedBox.shrink(); + } +} diff --git a/native_uis/webf_shadcn_ui/lib/src/components/radio_bindings_generated.dart b/native_uis/webf_shadcn_ui/lib/src/components/radio_group_bindings_generated.dart similarity index 68% rename from native_uis/webf_shadcn_ui/lib/src/components/radio_bindings_generated.dart rename to native_uis/webf_shadcn_ui/lib/src/components/radio_group_bindings_generated.dart index 8abce3eedd..086db74dde 100644 --- a/native_uis/webf_shadcn_ui/lib/src/components/radio_bindings_generated.dart +++ b/native_uis/webf_shadcn_ui/lib/src/components/radio_group_bindings_generated.dart @@ -8,8 +8,8 @@ // ignore_for_file: library_private_types_in_public_api // ignore_for_file: prefer_void_to_null import 'package:webf/webf.dart'; -abstract class FlutterShadcnRadioBindings extends WidgetElement { - FlutterShadcnRadioBindings(super.context); +abstract class FlutterShadcnRadioGroupBindings extends WidgetElement { + FlutterShadcnRadioGroupBindings(super.context); String? get value; set value(value); bool get disabled; @@ -35,26 +35,26 @@ abstract class FlutterShadcnRadioBindings extends WidgetElement { deleter: () => orientation = null ); } - static StaticDefinedBindingPropertyMap flutterShadcnRadioProperties = { + static StaticDefinedBindingPropertyMap flutterShadcnRadioGroupProperties = { 'value': StaticDefinedBindingProperty( - getter: (element) => castToType(element).value, + getter: (element) => castToType(element).value, setter: (element, value) => - castToType(element).value = value, + castToType(element).value = value, ), 'disabled': StaticDefinedBindingProperty( - getter: (element) => castToType(element).disabled, + getter: (element) => castToType(element).disabled, setter: (element, value) => - castToType(element).disabled = value, + castToType(element).disabled = value, ), 'orientation': StaticDefinedBindingProperty( - getter: (element) => castToType(element).orientation, + getter: (element) => castToType(element).orientation, setter: (element, value) => - castToType(element).orientation = value, + castToType(element).orientation = value, ), }; @override List get properties => [ ...super.properties, - flutterShadcnRadioProperties, + flutterShadcnRadioGroupProperties, ]; -} \ No newline at end of file +} diff --git a/native_uis/webf_shadcn_ui/lib/webf_shadcn_ui.dart b/native_uis/webf_shadcn_ui/lib/webf_shadcn_ui.dart index 6f5719fe20..2b375662b6 100644 --- a/native_uis/webf_shadcn_ui/lib/webf_shadcn_ui.dart +++ b/native_uis/webf_shadcn_ui/lib/webf_shadcn_ui.dart @@ -45,7 +45,7 @@ export 'src/components/icon_button.dart'; export 'src/components/input.dart'; export 'src/components/textarea.dart'; export 'src/components/checkbox.dart'; -export 'src/components/radio.dart'; +export 'src/components/radio_group.dart'; export 'src/components/switch.dart'; export 'src/components/select.dart'; export 'src/components/slider.dart'; @@ -93,7 +93,7 @@ import 'src/components/icon_button.dart'; import 'src/components/input.dart'; import 'src/components/textarea.dart'; import 'src/components/checkbox.dart'; -import 'src/components/radio.dart'; +import 'src/components/radio_group.dart'; import 'src/components/switch.dart'; import 'src/components/select.dart'; import 'src/components/slider.dart'; @@ -155,9 +155,9 @@ void installWebFShadcnUI() { WebF.defineCustomElement( 'flutter-shadcn-checkbox', (context) => FlutterShadcnCheckbox(context)); WebF.defineCustomElement( - 'flutter-shadcn-radio', (context) => FlutterShadcnRadio(context)); + 'flutter-shadcn-radio-group', (context) => FlutterShadcnRadioGroup(context)); WebF.defineCustomElement( - 'flutter-shadcn-radio-item', (context) => FlutterShadcnRadioItem(context)); + 'flutter-shadcn-radio-group-item', (context) => FlutterShadcnRadioGroupItem(context)); WebF.defineCustomElement( 'flutter-shadcn-switch', (context) => FlutterShadcnSwitch(context)); WebF.defineCustomElement( diff --git a/packages/react-shadcn-ui/README.md b/packages/react-shadcn-ui/README.md index 68b77673ce..3457799b8b 100644 --- a/packages/react-shadcn-ui/README.md +++ b/packages/react-shadcn-ui/README.md @@ -58,7 +58,7 @@ void main() { - `` - Text input field - `` - Multi-line text input - `` - Checkbox control -- `` - Radio button group +- `` - Radio button group - `` - Toggle switch - `` - Dropdown select - `` - Range slider diff --git a/packages/react-shadcn-ui/src/index.ts b/packages/react-shadcn-ui/src/index.ts index 710523f29b..a09714db41 100644 --- a/packages/react-shadcn-ui/src/index.ts +++ b/packages/react-shadcn-ui/src/index.ts @@ -31,8 +31,8 @@ export { FlutterShadcnSelect, FlutterShadcnSelectTrigger, FlutterShadcnSelectCon export type { FlutterShadcnSelectElement, FlutterShadcnSelectTriggerElement, FlutterShadcnSelectContentElement, FlutterShadcnSelectItemElement, FlutterShadcnSelectGroupElement, FlutterShadcnSelectLabelElement, FlutterShadcnSelectSeparatorElement } from "./lib/src/components/select"; export { FlutterShadcnScrollArea } from "./lib/src/components/scroll_area"; export type { FlutterShadcnScrollAreaElement } from "./lib/src/components/scroll_area"; -export { FlutterShadcnRadio, FlutterShadcnRadioItem } from "./lib/src/components/radio"; -export type { FlutterShadcnRadioElement, FlutterShadcnRadioItemElement } from "./lib/src/components/radio"; +export { FlutterShadcnRadioGroup, FlutterShadcnRadioGroupItem } from "./lib/src/components/radio_group"; +export type { FlutterShadcnRadioGroupElement, FlutterShadcnRadioGroupItemElement } from "./lib/src/components/radio_group"; export { FlutterShadcnProgress } from "./lib/src/components/progress"; export type { FlutterShadcnProgressElement } from "./lib/src/components/progress"; export { FlutterShadcnPopover, FlutterShadcnPopoverTrigger, FlutterShadcnPopoverContent } from "./lib/src/components/popover"; diff --git a/packages/react-shadcn-ui/src/lib/src/components/radio.tsx b/packages/react-shadcn-ui/src/lib/src/components/radio_group.tsx similarity index 63% rename from packages/react-shadcn-ui/src/lib/src/components/radio.tsx rename to packages/react-shadcn-ui/src/lib/src/components/radio_group.tsx index f8f3ab01ff..4f7c72b9ec 100644 --- a/packages/react-shadcn-ui/src/lib/src/components/radio.tsx +++ b/packages/react-shadcn-ui/src/lib/src/components/radio_group.tsx @@ -1,7 +1,7 @@ import React from "react"; import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui"; import * as __webfTypes from "../../../types"; -export interface FlutterShadcnRadioProps { +export interface FlutterShadcnRadioGroupProps { /** * Currently selected value. */ @@ -37,7 +37,7 @@ export interface FlutterShadcnRadioProps { */ className?: string; } -export interface FlutterShadcnRadioElement extends WebFElementWithMethods<{ +export interface FlutterShadcnRadioGroupElement extends WebFElementWithMethods<{ }> { /** Currently selected value. */ value?: string; @@ -47,29 +47,29 @@ export interface FlutterShadcnRadioElement extends WebFElementWithMethods<{ orientation?: string; } /** - * Properties for -Container for radio button groups. -@example -```html - - Option 1 - Option 2 - -``` - * + * Properties for + * Container for radio button groups. + * @example + * ```html + * + * Option 1 + * Option 2 + * + * ``` + * * @example * ```tsx - * - * * Content - * + * * ``` */ -export const FlutterShadcnRadio = createWebFComponent({ - tagName: 'flutter-shadcn-radio', - displayName: 'FlutterShadcnRadio', +export const FlutterShadcnRadioGroup = createWebFComponent({ + tagName: 'flutter-shadcn-radio-group', + displayName: 'FlutterShadcnRadioGroup', // Map props to attributes attributeProps: [ 'value', @@ -94,7 +94,7 @@ export const FlutterShadcnRadio = createWebFComponent { /** Value of this radio option. */ value: string; @@ -128,22 +128,22 @@ export interface FlutterShadcnRadioItemElement extends WebFElementWithMethods<{ disabled?: boolean; } /** - * Properties for -Individual radio button option. - * + * Properties for + * Individual radio button option. + * * @example * ```tsx - * - * * Content - * + * * ``` */ -export const FlutterShadcnRadioItem = createWebFComponent({ - tagName: 'flutter-shadcn-radio-item', - displayName: 'FlutterShadcnRadioItem', +export const FlutterShadcnRadioGroupItem = createWebFComponent({ + tagName: 'flutter-shadcn-radio-group-item', + displayName: 'FlutterShadcnRadioGroupItem', // Map props to attributes attributeProps: [ 'value', @@ -159,4 +159,4 @@ export const FlutterShadcnRadioItem = createWebFComponent { setTextValue(e.target.value); }; + const selectRadioValue = (value: string) => { + console.log('selectRadioValue', value); + setRadioValue(value); + }; + const handleRadioChange = (e: React.ChangeEvent) => { console.log('handleRadioChange', e.target.value); - setRadioValue(e.target.value); + selectRadioValue(e.target.value); + }; + + const handleRadioInput = (e: React.FormEvent) => { + // WebF radio controls dispatch native "input" reliably; keep this as a fallback + // so selection still updates when React's radio onChange path is not triggered. + const value = e.currentTarget.value; + console.log('handleRadioInput', value); + selectRadioValue(value); }; const handleCheckboxChange = (e: React.ChangeEvent) => { @@ -35,7 +48,7 @@ export const BasicFormElementsPage: React.FC = () => { // Test functions to change states programmatically const testRadioChange = (value: string) => { console.log('Programmatically changing radio from', radioValue, 'to:', value); - setRadioValue(value); + selectRadioValue(value); }; const testCheckboxToggle = (value: string) => { @@ -79,42 +92,45 @@ export const BasicFormElementsPage: React.FC = () => {
Choose your favorite color:
-
Selected: "{radioValue}"
- + {/* Test buttons for radio */}

Test Radio Programmatically:

@@ -122,7 +138,7 @@ export const BasicFormElementsPage: React.FC = () => { Set Red