kasy-cli 1.39.1 → 1.40.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.
Files changed (23) hide show
  1. package/lib/scaffold/CHANGELOG.json +23 -0
  2. package/package.json +1 -1
  3. package/templates/firebase/.firebase/hosting.YnVpbGQvd2Vi.cache +27 -27
  4. package/templates/firebase/lib/components/components.dart +2 -0
  5. package/templates/firebase/lib/components/kasy_accordion.dart +4 -1
  6. package/templates/firebase/lib/components/kasy_alert.dart +5 -2
  7. package/templates/firebase/lib/components/kasy_bottom_sheet.dart +25 -7
  8. package/templates/firebase/lib/components/kasy_dialog.dart +3 -1
  9. package/templates/firebase/lib/components/kasy_menu.dart +926 -0
  10. package/templates/firebase/lib/components/kasy_popover.dart +267 -0
  11. package/templates/firebase/lib/components/kasy_sidebar.dart +20 -10
  12. package/templates/firebase/lib/core/navigation/kasy_route_observer.dart +8 -0
  13. package/templates/firebase/lib/features/home/home_components_page.dart +23 -4
  14. package/templates/firebase/lib/features/home/home_components_preview_registry.dart +320 -0
  15. package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +38 -94
  16. package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +32 -107
  17. package/templates/firebase/lib/features/subscriptions/ui/widgets/comparison_table.dart +21 -21
  18. package/templates/firebase/lib/features/subscriptions/ui/widgets/premium_close_button.dart +35 -27
  19. package/templates/firebase/lib/features/subscriptions/ui/widgets/selectable_col.dart +22 -17
  20. package/templates/firebase/lib/features/subscriptions/ui/widgets/selectable_row.dart +12 -7
  21. package/templates/firebase/lib/router.dart +2 -0
  22. package/templates/firebase/pubspec.yaml +1 -1
  23. package/templates/firebase/lib/features/subscriptions/ui/widgets/premium_banner.dart +0 -81
@@ -137,30 +137,30 @@ class _ComparisonTable extends StatelessWidget {
137
137
  return const SizedBox.shrink();
138
138
  }
139
139
 
140
- return Material(
140
+ // Flat bordered surface (no elevation), clipped to the rounded shape so the
141
+ // tinted header corners follow the radius. No Material wrapper: the Container
142
+ // paints the surface, border and radius itself.
143
+ return ClipRRect(
141
144
  borderRadius: KasyRadius.lgBorderRadius,
142
- child: ClipRRect(
143
- borderRadius: KasyRadius.lgBorderRadius,
144
- child: Container(
145
- decoration: BoxDecoration(
146
- color: context.colors.surface,
147
- borderRadius: KasyRadius.lgBorderRadius,
148
- border: Border.all(
149
- color: context.colors.onBackground.withValues(alpha: 0.3),
150
- ),
145
+ child: Container(
146
+ decoration: BoxDecoration(
147
+ color: context.colors.surface,
148
+ borderRadius: KasyRadius.lgBorderRadius,
149
+ border: Border.all(
150
+ color: context.colors.onBackground.withValues(alpha: 0.3),
151
151
  ),
152
- child: Column(
153
- children: [
154
- _buildHeader(context),
155
- ...rows.asMap().entries.map(
156
- (entry) => _buildRow(
157
- context,
158
- entry.value,
159
- isLast: entry.key == rows.length - 1,
160
- ),
152
+ ),
153
+ child: Column(
154
+ children: [
155
+ _buildHeader(context),
156
+ ...rows.asMap().entries.map(
157
+ (entry) => _buildRow(
158
+ context,
159
+ entry.value,
160
+ isLast: entry.key == rows.length - 1,
161
161
  ),
162
- ],
163
- ),
162
+ ),
163
+ ],
164
164
  ),
165
165
  ),
166
166
  );
@@ -1,7 +1,8 @@
1
1
  import 'package:flutter/material.dart';
2
2
  import 'package:flutter_riverpod/flutter_riverpod.dart';
3
3
  import 'package:kasy_kit/core/theme/theme.dart';
4
- import 'package:kasy_kit/core/widgets/kasy_focus_ring.dart';
4
+ import 'package:kasy_kit/core/widgets/kasy_pressable_depth.dart';
5
+ import 'package:kasy_kit/i18n/translations.g.dart';
5
6
 
6
7
  class AppCloseButtonComponent extends ConsumerWidget {
7
8
  final ValueNotifier<bool> showCloseBtn;
@@ -36,34 +37,41 @@ class AppCloseButton extends StatelessWidget {
36
37
  @override
37
38
  Widget build(BuildContext context) {
38
39
  final VoidCallback? onClose = onTap;
39
- return Material(
40
- color: Colors.transparent,
41
- child: KasyFocusRing(
42
- enabled: onClose != null,
43
- onActivate: onClose,
44
- borderRadius: BorderRadius.circular(999),
45
- child: InkWell(
46
- canRequestFocus: false,
47
- onTapUp: (_) {
48
- onClose?.call();
49
- },
50
- child: Ink(
51
- width: 32,
52
- height: 32,
53
- decoration: BoxDecoration(
54
- shape: BoxShape.circle,
55
- color: context.colors.onBackground.withValues(alpha: 0.6),
56
- ),
57
- child: Center(
58
- child: Icon(
59
- KasyIcons.close,
60
- color: context.colors.background,
61
- size: KasyIconSize.lg,
62
- ),
63
- ),
64
- ),
40
+
41
+ // Dark translucent scrim keeps the glyph readable over any hero image.
42
+ final Widget circle = Container(
43
+ width: 32,
44
+ height: 32,
45
+ decoration: BoxDecoration(
46
+ shape: BoxShape.circle,
47
+ color: context.colors.onBackground.withValues(alpha: 0.6),
48
+ ),
49
+ child: Center(
50
+ child: Icon(
51
+ KasyIcons.close,
52
+ color: context.colors.background,
53
+ size: KasyIconSize.lg,
65
54
  ),
66
55
  ),
67
56
  );
57
+
58
+ // Disabled (no handler): render the inert scrim, no interaction.
59
+ if (onClose == null) {
60
+ return circle;
61
+ }
62
+
63
+ // KasyPressableDepth gives the kit's press-in depth, a web hover highlight
64
+ // and a keyboard focus ring (no Material ripple), plus a 44x44 tap target
65
+ // around the 32px visual. A light veil lifts the dark scrim on hover/press.
66
+ final BorderRadius pill = BorderRadius.circular(999);
67
+ return KasyPressableDepth(
68
+ onPressed: onClose,
69
+ semanticLabel: t.common.close,
70
+ focusable: true,
71
+ clipBorderRadius: pill,
72
+ focusBorderRadius: pill,
73
+ pressOverlayColor: context.colors.background.withValues(alpha: 0.2),
74
+ child: circle,
75
+ );
68
76
  }
69
77
  }
@@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
4
4
  import 'package:flutter_animate/flutter_animate.dart';
5
5
  import 'package:kasy_kit/core/haptics/kasy_haptics.dart';
6
6
  import 'package:kasy_kit/core/theme/theme.dart';
7
- import 'package:kasy_kit/core/widgets/kasy_focus_ring.dart';
7
+ import 'package:kasy_kit/core/widgets/kasy_hover.dart';
8
8
  import 'package:kasy_kit/features/subscriptions/ui/widgets/selectable_row.dart';
9
9
 
10
10
 
@@ -65,24 +65,29 @@ class _SelectableColGroupState<T> extends State<SelectableColGroup<T>> {
65
65
  children: [
66
66
  for (var i = 0; i < widget.items.length; i++)
67
67
  Expanded(
68
- child: KasyFocusRing(
69
- onActivate: () => _selectIndex(i),
68
+ child: KasyHover(
69
+ onTap: () => _selectIndex(i),
70
+ focusable: true,
70
71
  borderRadius: KasyRadius.lgBorderRadius,
71
- child: GestureDetector(
72
- onTap: () => _selectIndex(i),
73
- child: Animate(
74
- effects: [
75
- FadeEffect(
76
- delay: Duration(milliseconds: 100 + 100 * i),
77
- duration: const Duration(milliseconds: 500),
78
- ),
79
- ],
80
- child: SelectableCol.fromOption(
81
- key: ValueKey('item_$i'),
82
- widget.items[i],
83
- selected: selectedIndex == i,
84
- brightness: widget.brightness,
72
+ // The card paints its own animated selection border, so suppress
73
+ // the hover/press fill: KasyHover only adds the web click cursor
74
+ // and the keyboard focus ring. The heavy selection haptic is
75
+ // fired by _selectIndex, so opt out of the default tap haptic.
76
+ hapticEnabled: false,
77
+ hoverEnabled: false,
78
+ pressEnabled: false,
79
+ child: Animate(
80
+ effects: [
81
+ FadeEffect(
82
+ delay: Duration(milliseconds: 100 + 100 * i),
83
+ duration: const Duration(milliseconds: 500),
85
84
  ),
85
+ ],
86
+ child: SelectableCol.fromOption(
87
+ key: ValueKey('item_$i'),
88
+ widget.items[i],
89
+ selected: selectedIndex == i,
90
+ brightness: widget.brightness,
86
91
  ),
87
92
  ),
88
93
  ),
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
2
2
  import 'package:flutter_animate/flutter_animate.dart';
3
3
  import 'package:kasy_kit/core/haptics/kasy_haptics.dart';
4
4
  import 'package:kasy_kit/core/theme/theme.dart';
5
- import 'package:kasy_kit/core/widgets/kasy_focus_ring.dart';
5
+ import 'package:kasy_kit/core/widgets/kasy_hover.dart';
6
6
 
7
7
  typedef OnSelectItem<T> = void Function(T data);
8
8
 
@@ -51,12 +51,18 @@ class _SelectableRowGroupState<T> extends State<SelectableRowGroup<T>> {
51
51
  return Column(
52
52
  children: [
53
53
  for (var i = 0; i < widget.items.length; i++)
54
- KasyFocusRing(
55
- onActivate: () => _selectIndex(i),
54
+ KasyHover(
55
+ onTap: () => _selectIndex(i),
56
+ focusable: true,
56
57
  borderRadius: KasyRadius.mdBorderRadius,
57
- child: GestureDetector(
58
- onTap: () => _selectIndex(i),
59
- child: Padding(
58
+ // The card paints its own animated selection border, so suppress the
59
+ // hover/press fill: KasyHover only contributes the web click cursor
60
+ // and the keyboard focus ring. The heavy selection haptic is fired
61
+ // by _selectIndex, so opt out of the default tap haptic.
62
+ hapticEnabled: false,
63
+ hoverEnabled: false,
64
+ pressEnabled: false,
65
+ child: Padding(
60
66
  padding: EdgeInsets.only(
61
67
  bottom: i == widget.items.length - 1 ? 0 : KasySpacing.sm,
62
68
  ),
@@ -75,7 +81,6 @@ class _SelectableRowGroupState<T> extends State<SelectableRowGroup<T>> {
75
81
  ),
76
82
  ),
77
83
  ),
78
- ),
79
84
  ),
80
85
  ],
81
86
  );
@@ -9,6 +9,7 @@ import 'package:kasy_kit/core/data/api/analytics_api.dart';
9
9
  import 'package:kasy_kit/core/data/models/user.dart';
10
10
  import 'package:kasy_kit/core/navigation/kasy_navigation_config.dart';
11
11
  import 'package:kasy_kit/core/navigation/kasy_page_transition.dart';
12
+ import 'package:kasy_kit/core/navigation/kasy_route_observer.dart';
12
13
  import 'package:kasy_kit/core/security/biometric_guard.dart';
13
14
  import 'package:kasy_kit/core/shared_preferences/shared_preferences.dart';
14
15
  import 'package:kasy_kit/core/states/user_state_notifier.dart';
@@ -156,6 +157,7 @@ GoRouter generateRouter({
156
157
  observers: [
157
158
  AnalyticsObserver(analyticsApi: MixpanelAnalyticsApi.instance()),
158
159
  KasyChromeVisibilityObserver(),
160
+ kasyRouteObserver,
159
161
 
160
162
  ...?observers,
161
163
  ],
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
16
16
  # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
17
17
  # In Windows, build-name is used as the major, minor, and patch parts
18
18
  # of the product and file versions while build-number is used as the build suffix.
19
- version: 1.0.0+67
19
+ version: 1.0.0+70
20
20
 
21
21
  environment:
22
22
  sdk: ^3.11.0
@@ -1,81 +0,0 @@
1
- import 'package:flutter/material.dart';
2
- import 'package:kasy_kit/core/theme/theme.dart';
3
- import 'package:kasy_kit/core/widgets/kasy_focus_ring.dart';
4
-
5
- typedef PremiumWideBannerOnTap = void Function();
6
-
7
- class PremiumWideBanner extends StatelessWidget {
8
- final Color bgColor;
9
- final String imagePath;
10
- final String smallText;
11
- final String title;
12
- final PremiumWideBannerOnTap onTap;
13
-
14
- const PremiumWideBanner({
15
- super.key,
16
- required this.bgColor,
17
- required this.imagePath,
18
- required this.smallText,
19
- required this.title,
20
- required this.onTap,
21
- });
22
-
23
- @override
24
- Widget build(BuildContext context) {
25
- return LayoutBuilder(builder: (context, constraints) {
26
- return Material(
27
- color: Colors.transparent,
28
- child: Ink(
29
- // height: 116,
30
- width: constraints.maxWidth,
31
- decoration: BoxDecoration(
32
- color: bgColor,
33
- // border: Border.all(color: borderColor),
34
- ),
35
- child: KasyFocusRing(
36
- onActivate: () => onTap(),
37
- borderRadius: BorderRadius.zero,
38
- child: InkWell(
39
- canRequestFocus: false,
40
- onTap: () => onTap(),
41
- child: Row(
42
- children: [
43
- Image.asset(
44
- imagePath,
45
- width: constraints.maxWidth * 0.32,
46
- // height: 116,
47
- fit: BoxFit.cover,
48
- ),
49
- const SizedBox(width: KasySpacing.lg),
50
- Expanded(
51
- child: Column(
52
- crossAxisAlignment: CrossAxisAlignment.start,
53
- mainAxisAlignment: MainAxisAlignment.center,
54
- children: [
55
- Text(
56
- smallText.toUpperCase(),
57
- style: context.kasyTextTheme.sectionLabel.copyWith(
58
- color: context.colors.premiumBannerText,
59
- ),
60
- ),
61
- const SizedBox(height: KasySpacing.sm),
62
- Text(
63
- title,
64
- style: context.textTheme.titleLarge?.copyWith(
65
- color: context.colors.onPrimary,
66
- ),
67
- overflow: TextOverflow.clip,
68
- ),
69
- ],
70
- ),
71
- ),
72
- const SizedBox(width: KasySpacing.lg),
73
- ],
74
- ),
75
- ),
76
- ),
77
- ),
78
- );
79
- });
80
- }
81
- }