kasy-cli 1.38.0 → 1.39.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/scaffold/CHANGELOG.json +23 -0
- package/lib/scaffold/backends/api/patch/README.md +15 -0
- package/lib/scaffold/backends/api/patch/lib/features/notifications/api/device_api.dart +11 -6
- package/lib/scaffold/backends/api/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +9 -1
- package/lib/scaffold/backends/api/pubspec.yaml.tpl +1 -0
- package/lib/scaffold/backends/patch-base-hashes.json +6 -6
- package/lib/scaffold/backends/supabase/edge-functions/admin-list-users/index.ts +3 -1
- package/lib/scaffold/backends/supabase/edge-functions/send-push-notification/index.ts +3 -0
- package/lib/scaffold/backends/supabase/migrations/20240101000012_welcome_decouple_from_push.sql +62 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/notifications/api/device_api.dart +11 -6
- package/lib/scaffold/backends/supabase/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +7 -0
- package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +1 -0
- package/lib/scaffold/shared/generator-utils.js +12 -6
- package/package.json +1 -1
- package/templates/firebase/.firebase/hosting.YnVpbGQvd2Vi.cache +23 -23
- package/templates/firebase/AGENTS.md +2 -2
- package/templates/firebase/DESIGN_SYSTEM.md +23 -8
- package/templates/firebase/assets/icons/apple_black.svg +3 -0
- package/templates/firebase/assets/icons/apple_white.svg +4 -0
- package/templates/firebase/assets/icons/facebook.svg +49 -0
- package/templates/firebase/assets/icons/google.svg +1 -0
- package/templates/firebase/functions/src/admin/functions.ts +2 -0
- package/templates/firebase/functions/src/authentication/functions.ts +13 -7
- package/templates/firebase/functions/src/notifications/triggers.ts +6 -2
- package/templates/firebase/lib/components/components.dart +5 -2
- package/templates/firebase/lib/components/kasy_app_bar.dart +325 -15
- package/templates/firebase/lib/components/kasy_card.dart +4 -0
- package/templates/firebase/lib/components/kasy_drop_down.dart +584 -0
- package/templates/firebase/lib/components/kasy_sidebar.dart +18 -6
- package/templates/firebase/lib/components/kasy_tabs.dart +31 -10
- package/templates/firebase/lib/components/kasy_text_field.dart +29 -7
- package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +27 -18
- package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +34 -16
- package/templates/firebase/lib/core/chrome/app_bar_config.dart +214 -0
- package/templates/firebase/lib/core/chrome/app_bar_scope.dart +102 -0
- package/templates/firebase/lib/core/data/api/user_api.dart +11 -0
- package/templates/firebase/lib/core/dev_inspector/dev_inspector_service.dart +55 -15
- package/templates/firebase/lib/core/rating/widgets/review_popup.dart +95 -30
- package/templates/firebase/lib/core/shared_preferences/shared_preferences.dart +11 -0
- package/templates/firebase/lib/core/states/logout_action.dart +11 -1
- package/templates/firebase/lib/core/states/user_state_notifier.dart +28 -1
- package/templates/firebase/lib/core/theme/texts.dart +21 -6
- package/templates/firebase/lib/core/theme/type_scale.dart +34 -15
- package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +51 -19
- package/templates/firebase/lib/core/web_viewport_scale.dart +66 -36
- package/templates/firebase/lib/core/widgets/kasy_pressable_depth.dart +14 -3
- package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_chat_composer.dart +1 -1
- package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_chat_conversation_view.dart +52 -35
- package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_list.dart +1 -1
- package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_tile.dart +18 -8
- package/templates/firebase/lib/features/authentication/ui/signin_page.dart +11 -61
- package/templates/firebase/lib/features/authentication/ui/signup_page.dart +11 -61
- package/templates/firebase/lib/features/authentication/ui/widgets/auth_card_scaffold.dart +7 -5
- package/templates/firebase/lib/features/authentication/ui/widgets/social_auth_tile.dart +83 -0
- package/templates/firebase/lib/features/feedbacks/ui/widgets/feature_card.dart +4 -4
- package/templates/firebase/lib/features/home/home_components_page.dart +253 -125
- package/templates/firebase/lib/features/home/home_components_preview_registry.dart +263 -59
- package/templates/firebase/lib/features/home/home_feed.dart +2 -2
- package/templates/firebase/lib/features/home/home_image_grid.dart +3 -3
- package/templates/firebase/lib/features/local_reminders/providers/reminder_notifier.dart +8 -1
- package/templates/firebase/lib/features/local_reminders/ui/reminder_page.dart +111 -57
- package/templates/firebase/lib/features/notifications/api/device_api.dart +11 -3
- package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +16 -4
- package/templates/firebase/lib/features/notifications/ui/widgets/empty_notifications.dart +7 -56
- package/templates/firebase/lib/features/notifications/ui/widgets/notification_tile.dart +5 -5
- package/templates/firebase/lib/features/notifications/ui/widgets/push_permission_banner.dart +163 -0
- package/templates/firebase/lib/features/notifications/ui/widgets/web_notifications_bell.dart +2 -2
- package/templates/firebase/lib/features/onboarding/providers/onboarding_model.dart +9 -0
- package/templates/firebase/lib/features/onboarding/providers/onboarding_provider.dart +28 -0
- package/templates/firebase/lib/features/onboarding/ui/onboarding_page.dart +51 -12
- package/templates/firebase/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +90 -32
- package/templates/firebase/lib/features/settings/settings_page.dart +53 -32
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_home_widgets.dart +4 -0
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_page.dart +895 -111
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_api.dart +7 -0
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_tab.dart +445 -233
- package/templates/firebase/lib/features/settings/ui/components/admin/send_push_notification_page.dart +171 -41
- package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +1 -1
- package/templates/firebase/lib/features/settings/ui/components/delete_user_component.dart +9 -1
- package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +48 -47
- package/templates/firebase/lib/features/settings/ui/widgets/settings_bottom_sheet_option_tile.dart +21 -18
- package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +25 -10
- package/templates/firebase/lib/i18n/en.i18n.json +753 -712
- package/templates/firebase/lib/i18n/es.i18n.json +753 -712
- package/templates/firebase/lib/i18n/pt.i18n.json +753 -712
- package/templates/firebase/lib/main.dart +20 -7
- package/templates/firebase/lib/router.dart +32 -26
- package/templates/firebase/pubspec.yaml +2 -1
- package/templates/firebase/test/admin_shell_chrome_test.dart +11 -5
- package/templates/firebase/test/app_bar_config_test.dart +70 -0
- package/templates/firebase/test/components/kasy_text_field_height_test.dart +77 -0
- package/templates/firebase/test/core/web_viewport_scale_test.dart +23 -16
- package/templates/firebase/tool/design_check.dart +9 -0
- package/templates/firebase/assets/icons/apple.png +0 -0
- package/templates/firebase/assets/icons/facebook.png +0 -0
- package/templates/firebase/assets/icons/google.png +0 -0
- package/templates/firebase/assets/icons/google_play_games.png +0 -0
- package/templates/firebase/lib/components/kasy_web_header.dart +0 -218
- package/templates/firebase/lib/core/chrome/web_header_scope.dart +0 -20
- package/templates/firebase/lib/features/authentication/ui/components/apple_signin.dart +0 -19
- package/templates/firebase/lib/features/authentication/ui/components/google_signin.dart +0 -32
- package/templates/firebase/lib/features/authentication/ui/widgets/round_signin.dart +0 -73
- package/templates/firebase/lib/features/feedbacks/ui/widgets/add_feature_button.dart +0 -66
- package/templates/firebase/lib/features/notifications/ui/components/notification_settings_sheet.dart +0 -179
- package/templates/firebase/lib/features/notifications/ui/components/push_notification_switcher.dart +0 -106
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
import 'package:flutter_svg/flutter_svg.dart';
|
|
3
|
+
import 'package:kasy_kit/core/haptics/kasy_haptics.dart';
|
|
4
|
+
import 'package:kasy_kit/core/theme/theme.dart';
|
|
5
|
+
import 'package:kasy_kit/core/widgets/kasy_hover.dart';
|
|
6
|
+
|
|
7
|
+
/// Outlined social sign-in button (Google / Apple / Facebook), shared by the
|
|
8
|
+
/// sign-in and sign-up screens.
|
|
9
|
+
///
|
|
10
|
+
/// Each tile fills its slot in the social row (the parent wraps it in an
|
|
11
|
+
/// [Expanded]) and centers the provider glyph. It uses [KasyHover] so it has a
|
|
12
|
+
/// real hover highlight on web/desktop and a press fill on every platform — no
|
|
13
|
+
/// Material ripple, matching the rest of the kit's interactive controls. While a
|
|
14
|
+
/// request is in flight ([onPressed] null) it dims and stops responding to
|
|
15
|
+
/// pointer and keyboard.
|
|
16
|
+
class SocialAuthButton extends StatelessWidget {
|
|
17
|
+
const SocialAuthButton({
|
|
18
|
+
super.key,
|
|
19
|
+
required this.label,
|
|
20
|
+
required this.iconAsset,
|
|
21
|
+
required this.onPressed,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/// Accessibility label (the provider name) announced to screen readers.
|
|
25
|
+
final String label;
|
|
26
|
+
|
|
27
|
+
/// Path to the provider's official brand SVG bundled under `assets/icons/`
|
|
28
|
+
/// (e.g. `assets/icons/google.svg`). Rendered at full brand colour and at a
|
|
29
|
+
/// shared [KasyIconSize.lg] box so every provider stays the same size. For
|
|
30
|
+
/// Apple, the caller picks the black/white variant for the current theme.
|
|
31
|
+
final String iconAsset;
|
|
32
|
+
|
|
33
|
+
/// Tap handler. When null the button is disabled (dimmed, inert).
|
|
34
|
+
final VoidCallback? onPressed;
|
|
35
|
+
|
|
36
|
+
@override
|
|
37
|
+
Widget build(BuildContext context) {
|
|
38
|
+
final bool enabled = onPressed != null;
|
|
39
|
+
final BorderRadius radius = BorderRadius.circular(KasyRadius.sm);
|
|
40
|
+
|
|
41
|
+
// No background fill: the KasyHover overlay sits behind the transparent
|
|
42
|
+
// interior to provide the hover/press tint, and on the surface auth card a
|
|
43
|
+
// fill would be invisible anyway. The hairline border gives the outlined
|
|
44
|
+
// shape, consistent with KasyButton's outlined variant.
|
|
45
|
+
final Widget surface = Container(
|
|
46
|
+
height: 44,
|
|
47
|
+
decoration: BoxDecoration(
|
|
48
|
+
borderRadius: radius,
|
|
49
|
+
border: Border.all(
|
|
50
|
+
color: context.colors.outline.withValues(alpha: 0.38),
|
|
51
|
+
),
|
|
52
|
+
),
|
|
53
|
+
child: Center(
|
|
54
|
+
// Brand logos have different aspect ratios (Apple is tall, Google is
|
|
55
|
+
// square); SvgPicture defaults to BoxFit.contain, which keeps each one
|
|
56
|
+
// inside the shared box without distortion so the row stays balanced.
|
|
57
|
+
child: SvgPicture.asset(
|
|
58
|
+
iconAsset,
|
|
59
|
+
width: KasyIconSize.lg,
|
|
60
|
+
height: KasyIconSize.lg,
|
|
61
|
+
),
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (!enabled) {
|
|
66
|
+
return Opacity(opacity: 0.5, child: surface);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return KasyHover(
|
|
70
|
+
onTap: () {
|
|
71
|
+
KasyHaptics.medium(context);
|
|
72
|
+
onPressed!();
|
|
73
|
+
},
|
|
74
|
+
// KasyHover fires a light selection haptic by default; a social sign-in is
|
|
75
|
+
// a significant action, so opt out and fire the heavier "medium" above.
|
|
76
|
+
hapticEnabled: false,
|
|
77
|
+
focusable: true,
|
|
78
|
+
borderRadius: radius,
|
|
79
|
+
semanticLabel: label,
|
|
80
|
+
child: surface,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -59,15 +59,15 @@ class _FeatureCardState extends State<FeatureCard> {
|
|
|
59
59
|
children: [
|
|
60
60
|
Text(
|
|
61
61
|
widget.title,
|
|
62
|
-
style: context.
|
|
63
|
-
|
|
62
|
+
style: context.kasyTextTheme.listRowTitle.copyWith(
|
|
63
|
+
color: context.colors.onSurface,
|
|
64
|
+
fontWeight: FontWeight.w600,
|
|
64
65
|
),
|
|
65
66
|
),
|
|
66
67
|
const SizedBox(height: 2),
|
|
67
68
|
Text(
|
|
68
69
|
widget.description,
|
|
69
|
-
style: context.textTheme.
|
|
70
|
-
fontWeight: FontWeight.w400,
|
|
70
|
+
style: context.textTheme.bodyMedium?.copyWith(
|
|
71
71
|
color: context.colors.muted,
|
|
72
72
|
),
|
|
73
73
|
),
|
|
@@ -6,6 +6,7 @@ import 'package:kasy_kit/core/theme/theme.dart';
|
|
|
6
6
|
import 'package:kasy_kit/core/widgets/kasy_hover.dart';
|
|
7
7
|
import 'package:kasy_kit/core/widgets/kasy_scroll_behavior.dart';
|
|
8
8
|
import 'package:kasy_kit/features/home/home_components_preview_registry.dart';
|
|
9
|
+
import 'package:kasy_kit/i18n/translations.g.dart';
|
|
9
10
|
|
|
10
11
|
/// UI-kit catalog, reachable from the home dashboard.
|
|
11
12
|
///
|
|
@@ -38,140 +39,267 @@ class HomeComponentsPage extends StatelessWidget {
|
|
|
38
39
|
/// The UI-kit catalog list on its own (no app bar / scaffold), so it can be the
|
|
39
40
|
/// body of [HomeComponentsPage] AND a section inside the admin console (which
|
|
40
41
|
/// supplies its own chrome). Fills the space it is given.
|
|
41
|
-
class HomeComponentsCatalog extends
|
|
42
|
+
class HomeComponentsCatalog extends StatefulWidget {
|
|
42
43
|
const HomeComponentsCatalog({super.key});
|
|
43
44
|
|
|
45
|
+
@override
|
|
46
|
+
State<HomeComponentsCatalog> createState() => _HomeComponentsCatalogState();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class _HomeComponentsCatalogState extends State<HomeComponentsCatalog> {
|
|
50
|
+
final TextEditingController _searchCtrl = TextEditingController();
|
|
51
|
+
String _query = '';
|
|
52
|
+
|
|
53
|
+
@override
|
|
54
|
+
void dispose() {
|
|
55
|
+
_searchCtrl.dispose();
|
|
56
|
+
super.dispose();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// Reset the filter when leaving for a component, so coming back shows the
|
|
60
|
+
/// full catalog instead of the stale search term.
|
|
61
|
+
void _clearSearch() {
|
|
62
|
+
if (!mounted) return;
|
|
63
|
+
if (_query.isEmpty && _searchCtrl.text.isEmpty) return;
|
|
64
|
+
_searchCtrl.clear();
|
|
65
|
+
setState(() => _query = '');
|
|
66
|
+
}
|
|
67
|
+
|
|
44
68
|
@override
|
|
45
69
|
Widget build(BuildContext context) {
|
|
70
|
+
final String q = _query.trim().toLowerCase();
|
|
71
|
+
final List<_CatalogRow> rows = q.isEmpty
|
|
72
|
+
? _kCatalog
|
|
73
|
+
: _kCatalog
|
|
74
|
+
.where((r) => r.name.toLowerCase().contains(q))
|
|
75
|
+
.toList(growable: false);
|
|
76
|
+
|
|
46
77
|
return Padding(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
endIndent: KasySpacing.md,
|
|
70
|
-
color: context.colors.outline.withValues(alpha: 0.33),
|
|
78
|
+
padding: EdgeInsets.fromLTRB(
|
|
79
|
+
KasySpacing.pageHorizontalGutter,
|
|
80
|
+
KasySpacing.belowChromeContentGap,
|
|
81
|
+
KasySpacing.pageHorizontalGutter,
|
|
82
|
+
MediaQuery.paddingOf(context).bottom + KasySpacing.belowChromeContentGap,
|
|
83
|
+
),
|
|
84
|
+
child: Column(
|
|
85
|
+
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
86
|
+
children: [
|
|
87
|
+
_ComponentsSearchField(
|
|
88
|
+
controller: _searchCtrl,
|
|
89
|
+
onChanged: (v) => setState(() => _query = v),
|
|
90
|
+
),
|
|
91
|
+
const SizedBox(height: KasySpacing.smd),
|
|
92
|
+
Expanded(
|
|
93
|
+
child: _componentsCatalogInset(
|
|
94
|
+
context,
|
|
95
|
+
child: rows.isEmpty
|
|
96
|
+
? const _ComponentsEmptyResults()
|
|
97
|
+
: Padding(
|
|
98
|
+
padding: const EdgeInsets.symmetric(
|
|
99
|
+
horizontal: KasySpacing.xs,
|
|
71
100
|
),
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
// Skip rows without a preview yet (registry returns
|
|
86
|
-
// null); otherwise open /components/:name, which the
|
|
87
|
-
// router resolves back to this component's preview.
|
|
88
|
-
if (getComponentPreviewDefinition(row.name) == null) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
context.push(
|
|
92
|
-
'/components/${Uri.encodeComponent(row.name)}',
|
|
93
|
-
);
|
|
94
|
-
},
|
|
95
|
-
child: Padding(
|
|
96
|
-
padding: const EdgeInsets.symmetric(
|
|
97
|
-
horizontal: KasySpacing.smd,
|
|
98
|
-
vertical: 16,
|
|
99
|
-
),
|
|
100
|
-
child: Row(
|
|
101
|
-
children: [
|
|
102
|
-
Expanded(
|
|
103
|
-
child: Row(
|
|
104
|
-
children: [
|
|
105
|
-
Flexible(
|
|
106
|
-
child: Text(
|
|
107
|
-
row.name,
|
|
108
|
-
// Lighter weight (w500) to match the
|
|
109
|
-
// app's row/list scale instead of the
|
|
110
|
-
// heavier heading weight.
|
|
111
|
-
style: context.kasyTextTheme.rowTitle
|
|
112
|
-
.copyWith(
|
|
113
|
-
color:
|
|
114
|
-
context.colors.onSurface,
|
|
115
|
-
),
|
|
116
|
-
),
|
|
117
|
-
),
|
|
118
|
-
if (_kReadyComponents.contains(
|
|
119
|
-
row.name,
|
|
120
|
-
)) ...[
|
|
121
|
-
const SizedBox(width: KasySpacing.sm),
|
|
122
|
-
Text(
|
|
123
|
-
'Pronto',
|
|
124
|
-
style: context.textTheme.bodySmall
|
|
125
|
-
?.copyWith(
|
|
126
|
-
color: context.colors.success,
|
|
127
|
-
fontWeight: FontWeight.w600,
|
|
128
|
-
),
|
|
129
|
-
),
|
|
130
|
-
],
|
|
131
|
-
if (_kWebReadyComponents.contains(
|
|
132
|
-
row.name,
|
|
133
|
-
)) ...[
|
|
134
|
-
const SizedBox(width: KasySpacing.xs),
|
|
135
|
-
Text(
|
|
136
|
-
'Web',
|
|
137
|
-
style: context.textTheme.bodySmall
|
|
138
|
-
?.copyWith(
|
|
139
|
-
color: context.colors.primary,
|
|
140
|
-
fontWeight: FontWeight.w600,
|
|
141
|
-
),
|
|
142
|
-
),
|
|
143
|
-
],
|
|
144
|
-
if (_kUrgentComponents.contains(
|
|
145
|
-
row.name,
|
|
146
|
-
)) ...[
|
|
147
|
-
const SizedBox(width: KasySpacing.sm),
|
|
148
|
-
Text(
|
|
149
|
-
'Prioridade: Urgente',
|
|
150
|
-
style: context.textTheme.bodySmall
|
|
151
|
-
?.copyWith(
|
|
152
|
-
color: context.colors.warning,
|
|
153
|
-
fontWeight: FontWeight.w700,
|
|
154
|
-
),
|
|
155
|
-
),
|
|
156
|
-
],
|
|
157
|
-
],
|
|
158
|
-
),
|
|
159
|
-
),
|
|
160
|
-
Icon(
|
|
161
|
-
KasyIcons.chevronRight,
|
|
162
|
-
color: context.colors.muted,
|
|
163
|
-
size: 22,
|
|
164
|
-
),
|
|
165
|
-
],
|
|
101
|
+
child: ScrollConfiguration(
|
|
102
|
+
behavior: const KasyKitScrollBehavior(),
|
|
103
|
+
child: ListView.separated(
|
|
104
|
+
padding: EdgeInsets.zero,
|
|
105
|
+
itemCount: rows.length + 1,
|
|
106
|
+
separatorBuilder: (context, index) => Divider(
|
|
107
|
+
height: 1,
|
|
108
|
+
thickness: 1,
|
|
109
|
+
indent: KasySpacing.md,
|
|
110
|
+
endIndent: KasySpacing.md,
|
|
111
|
+
color: context.colors.outline.withValues(
|
|
112
|
+
alpha: 0.33,
|
|
166
113
|
),
|
|
167
114
|
),
|
|
168
|
-
|
|
115
|
+
itemBuilder: (context, index) {
|
|
116
|
+
if (index >= rows.length) {
|
|
117
|
+
return const SizedBox(height: KasySpacing.sm);
|
|
118
|
+
}
|
|
119
|
+
return _catalogRow(context, rows[index]);
|
|
120
|
+
},
|
|
121
|
+
),
|
|
122
|
+
),
|
|
123
|
+
),
|
|
124
|
+
),
|
|
125
|
+
),
|
|
126
|
+
],
|
|
127
|
+
),
|
|
128
|
+
);
|
|
129
|
+
}
|
|
169
130
|
|
|
170
|
-
|
|
131
|
+
Widget _catalogRow(BuildContext context, _CatalogRow row) {
|
|
132
|
+
return KasyHover(
|
|
133
|
+
hapticEnabled: false,
|
|
134
|
+
onTap: () {
|
|
135
|
+
KasyHaptics.selection(context);
|
|
136
|
+
if (row.name == 'Design System') {
|
|
137
|
+
context.push('/design-system').whenComplete(_clearSearch);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
// Skip rows without a preview yet (registry returns null); otherwise
|
|
141
|
+
// open /components/:name, which the router resolves back to this
|
|
142
|
+
// component's preview.
|
|
143
|
+
if (getComponentPreviewDefinition(row.name) == null) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
context
|
|
147
|
+
.push('/components/${Uri.encodeComponent(row.name)}')
|
|
148
|
+
.whenComplete(_clearSearch);
|
|
149
|
+
},
|
|
150
|
+
child: Padding(
|
|
151
|
+
padding: const EdgeInsets.symmetric(
|
|
152
|
+
horizontal: KasySpacing.smd,
|
|
153
|
+
vertical: 16,
|
|
154
|
+
),
|
|
155
|
+
child: Row(
|
|
156
|
+
children: [
|
|
157
|
+
Expanded(
|
|
158
|
+
child: Row(
|
|
159
|
+
children: [
|
|
160
|
+
Flexible(
|
|
161
|
+
child: Text(
|
|
162
|
+
row.name,
|
|
163
|
+
// Lighter weight (w500) to match the app's row/list scale
|
|
164
|
+
// instead of the heavier heading weight.
|
|
165
|
+
style: context.kasyTextTheme.rowTitle.copyWith(
|
|
166
|
+
color: context.colors.onSurface,
|
|
167
|
+
),
|
|
171
168
|
),
|
|
172
169
|
),
|
|
173
|
-
|
|
170
|
+
if (_kReadyComponents.contains(row.name)) ...[
|
|
171
|
+
const SizedBox(width: KasySpacing.sm),
|
|
172
|
+
Text(
|
|
173
|
+
'Pronto',
|
|
174
|
+
style: context.textTheme.bodySmall?.copyWith(
|
|
175
|
+
color: context.colors.success,
|
|
176
|
+
fontWeight: FontWeight.w600,
|
|
177
|
+
),
|
|
178
|
+
),
|
|
179
|
+
],
|
|
180
|
+
if (_kWebReadyComponents.contains(row.name)) ...[
|
|
181
|
+
const SizedBox(width: KasySpacing.xs),
|
|
182
|
+
Text(
|
|
183
|
+
'Web',
|
|
184
|
+
style: context.textTheme.bodySmall?.copyWith(
|
|
185
|
+
color: context.colors.primary,
|
|
186
|
+
fontWeight: FontWeight.w600,
|
|
187
|
+
),
|
|
188
|
+
),
|
|
189
|
+
],
|
|
190
|
+
if (_kUrgentComponents.contains(row.name)) ...[
|
|
191
|
+
const SizedBox(width: KasySpacing.sm),
|
|
192
|
+
Text(
|
|
193
|
+
'Prioridade: Urgente',
|
|
194
|
+
style: context.textTheme.bodySmall?.copyWith(
|
|
195
|
+
color: context.colors.warning,
|
|
196
|
+
fontWeight: FontWeight.w700,
|
|
197
|
+
),
|
|
198
|
+
),
|
|
199
|
+
],
|
|
200
|
+
],
|
|
201
|
+
),
|
|
202
|
+
),
|
|
203
|
+
Icon(
|
|
204
|
+
KasyIcons.chevronRight,
|
|
205
|
+
color: context.colors.muted,
|
|
206
|
+
size: 22,
|
|
207
|
+
),
|
|
208
|
+
],
|
|
209
|
+
),
|
|
210
|
+
),
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/// Compact search field above the catalog — filters the list by component name.
|
|
216
|
+
class _ComponentsSearchField extends StatelessWidget {
|
|
217
|
+
final TextEditingController controller;
|
|
218
|
+
final ValueChanged<String> onChanged;
|
|
219
|
+
const _ComponentsSearchField({
|
|
220
|
+
required this.controller,
|
|
221
|
+
required this.onChanged,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
@override
|
|
225
|
+
Widget build(BuildContext context) {
|
|
226
|
+
return SizedBox(
|
|
227
|
+
height: 40,
|
|
228
|
+
child: TextField(
|
|
229
|
+
controller: controller,
|
|
230
|
+
onChanged: onChanged,
|
|
231
|
+
style: context.textTheme.bodyMedium,
|
|
232
|
+
decoration: InputDecoration(
|
|
233
|
+
hintText: t.home.dashboard.search_hint,
|
|
234
|
+
hintStyle: context.textTheme.bodyMedium?.copyWith(
|
|
235
|
+
color: context.colors.muted,
|
|
236
|
+
),
|
|
237
|
+
prefixIcon: Icon(
|
|
238
|
+
Icons.search_rounded,
|
|
239
|
+
size: 18,
|
|
240
|
+
color: context.colors.muted,
|
|
241
|
+
),
|
|
242
|
+
prefixIconConstraints: const BoxConstraints(
|
|
243
|
+
minWidth: 40,
|
|
244
|
+
minHeight: 40,
|
|
245
|
+
),
|
|
246
|
+
isDense: true,
|
|
247
|
+
contentPadding: const EdgeInsets.symmetric(
|
|
248
|
+
horizontal: 12,
|
|
249
|
+
vertical: 10,
|
|
250
|
+
),
|
|
251
|
+
filled: true,
|
|
252
|
+
fillColor: context.colors.surface,
|
|
253
|
+
enabledBorder: OutlineInputBorder(
|
|
254
|
+
borderRadius: BorderRadius.circular(KasyRadius.sm),
|
|
255
|
+
borderSide: BorderSide(
|
|
256
|
+
color: context.colors.outline.withValues(alpha: 0.5),
|
|
257
|
+
),
|
|
258
|
+
),
|
|
259
|
+
focusedBorder: OutlineInputBorder(
|
|
260
|
+
borderRadius: BorderRadius.circular(KasyRadius.sm),
|
|
261
|
+
borderSide: BorderSide(color: context.colors.primary, width: 1.4),
|
|
262
|
+
),
|
|
263
|
+
border: OutlineInputBorder(
|
|
264
|
+
borderRadius: BorderRadius.circular(KasyRadius.sm),
|
|
265
|
+
borderSide: BorderSide(
|
|
266
|
+
color: context.colors.outline.withValues(alpha: 0.5),
|
|
267
|
+
),
|
|
268
|
+
),
|
|
269
|
+
),
|
|
270
|
+
),
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/// Shown inside the catalog surface when a search matches nothing.
|
|
276
|
+
class _ComponentsEmptyResults extends StatelessWidget {
|
|
277
|
+
const _ComponentsEmptyResults();
|
|
278
|
+
|
|
279
|
+
@override
|
|
280
|
+
Widget build(BuildContext context) {
|
|
281
|
+
return Center(
|
|
282
|
+
child: Padding(
|
|
283
|
+
padding: const EdgeInsets.all(KasySpacing.lg),
|
|
284
|
+
child: Column(
|
|
285
|
+
mainAxisSize: MainAxisSize.min,
|
|
286
|
+
children: [
|
|
287
|
+
Icon(
|
|
288
|
+
Icons.search_off_rounded,
|
|
289
|
+
size: 30,
|
|
290
|
+
color: context.colors.muted,
|
|
291
|
+
),
|
|
292
|
+
const SizedBox(height: KasySpacing.sm),
|
|
293
|
+
Text(
|
|
294
|
+
t.home.dashboard.search_empty,
|
|
295
|
+
textAlign: TextAlign.center,
|
|
296
|
+
style: context.textTheme.bodySmall?.copyWith(
|
|
297
|
+
color: context.colors.muted,
|
|
174
298
|
),
|
|
299
|
+
),
|
|
300
|
+
],
|
|
301
|
+
),
|
|
302
|
+
),
|
|
175
303
|
);
|
|
176
304
|
}
|
|
177
305
|
}
|
|
@@ -195,6 +323,7 @@ const Set<String> _kReadyComponents = <String>{
|
|
|
195
323
|
'DatePicker',
|
|
196
324
|
'Design System',
|
|
197
325
|
'Dialog',
|
|
326
|
+
'DropDown',
|
|
198
327
|
'Hover',
|
|
199
328
|
'Tabs',
|
|
200
329
|
'TextArea',
|
|
@@ -205,7 +334,6 @@ const Set<String> _kReadyComponents = <String>{
|
|
|
205
334
|
'SwipeAction',
|
|
206
335
|
'TextFieldOTP',
|
|
207
336
|
'Toast',
|
|
208
|
-
'Web Header',
|
|
209
337
|
};
|
|
210
338
|
|
|
211
339
|
/// Components that have been adapted for web (cursor, hover, shadow, border).
|
|
@@ -222,6 +350,7 @@ const Set<String> _kWebReadyComponents = <String>{
|
|
|
222
350
|
'DatePicker',
|
|
223
351
|
'Design System',
|
|
224
352
|
'Dialog',
|
|
353
|
+
'DropDown',
|
|
225
354
|
'Hover',
|
|
226
355
|
'Sidebar',
|
|
227
356
|
'Skeleton',
|
|
@@ -231,7 +360,6 @@ const Set<String> _kWebReadyComponents = <String>{
|
|
|
231
360
|
'TextField',
|
|
232
361
|
'TextFieldOTP',
|
|
233
362
|
'Toast',
|
|
234
|
-
'Web Header',
|
|
235
363
|
};
|
|
236
364
|
|
|
237
365
|
const Set<String> _kUrgentComponents = <String>{
|
|
@@ -254,6 +382,7 @@ const List<_CatalogRow> _kCatalog = [
|
|
|
254
382
|
_CatalogRow('Checkbox'),
|
|
255
383
|
_CatalogRow('DatePicker'),
|
|
256
384
|
_CatalogRow('Dialog'),
|
|
385
|
+
_CatalogRow('DropDown'),
|
|
257
386
|
_CatalogRow('Hover'),
|
|
258
387
|
_CatalogRow('Sidebar'),
|
|
259
388
|
_CatalogRow('Skeleton'),
|
|
@@ -263,7 +392,6 @@ const List<_CatalogRow> _kCatalog = [
|
|
|
263
392
|
_CatalogRow('TextField'),
|
|
264
393
|
_CatalogRow('TextFieldOTP'),
|
|
265
394
|
_CatalogRow('Toast'),
|
|
266
|
-
_CatalogRow('Web Header'),
|
|
267
395
|
// Prioridade: Urgente (_kUrgentComponents), A–Z
|
|
268
396
|
_CatalogRow('Radio Group'),
|
|
269
397
|
_CatalogRow('Switch'),
|